Add test project; work on gfx
@ -1,6 +1,9 @@
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
|
||||
# options
|
||||
option(ENGINE_BUILD_TEST "Compile the test program" ON)
|
||||
option(ENGINE_BUILD_VULKAN "Use Vulkan 1.3 for graphics" ON)
|
||||
option(ENGINE_BUILD_OPENGL "Use OpenGL 4.5 for graphics" OFF)
|
||||
|
||||
project(engine LANGUAGES CXX
|
||||
VERSION "0.1.0"
|
||||
@ -106,7 +109,20 @@ target_include_directories(${PROJECT_NAME} PRIVATE src)
|
||||
configure_file(config.h.in config.h)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE "ENGINE_BUILD_VULKAN")
|
||||
# figure out what graphics api to use
|
||||
if (ENGINE_BUILD_VULKAN)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE "ENGINE_BUILD_VULKAN")
|
||||
elseif(ENGINE_BUILD_OPENGL)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE "ENGINE_BUILD_OPENGL")
|
||||
else()
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE "ENGINE_BUILD_NULL")
|
||||
endif()
|
||||
|
||||
# Build the test
|
||||
if (ENGINE_BUILD_TEST)
|
||||
add_subdirectory(test)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY $<TARGET_FILE_DIR:enginetest>)
|
||||
endif()
|
||||
|
||||
# external libraries:
|
||||
|
||||
@ -126,7 +142,6 @@ endif()
|
||||
set(GLAD_SPEC "gl" CACHE INTERNAL "" FORCE)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
add_subdirectory(dependencies/glad)
|
||||
set_property(TARGET glad PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC glad)
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC dependencies/glad/include)
|
||||
|
||||
|
@ -32,7 +32,7 @@ namespace engine {
|
||||
gfx::Pipeline* createPipeline(const char* vertShaderPath, const char* fragShaderPath, const gfx::VertexFormat& vertexFormat, uint64_t uniformBufferSize);
|
||||
void destroyPipeline(const gfx::Pipeline* pipeline);
|
||||
|
||||
void updateUniformBuffer(const gfx::Pipeline* pipeline, void* data);
|
||||
void updateUniformBuffer(const gfx::Pipeline* pipeline, void* data, size_t size, uint32_t offset);
|
||||
|
||||
gfx::Buffer* createBuffer(gfx::BufferType type, uint64_t size, const void* data);
|
||||
void destroyBuffer(const gfx::Buffer* buffer);
|
||||
|
@ -22,8 +22,8 @@ public:
|
||||
~Shader() override;
|
||||
|
||||
struct UniformBuffer {
|
||||
glm::mat4 v;
|
||||
glm::mat4 p;
|
||||
glm::vec4 color;
|
||||
};
|
||||
|
||||
gfx::Pipeline* getPipeline()
|
||||
|
@ -38,13 +38,9 @@ void Camera::updateCam(glm::mat4 transform, glm::mat4* viewMatOut)
|
||||
|
||||
glm::mat4 viewMatrix = glm::inverse(transform);
|
||||
|
||||
struct {
|
||||
glm::mat4 view;
|
||||
glm::mat4 proj;
|
||||
} uniformData{};
|
||||
resources::Shader::UniformBuffer uniformData{};
|
||||
|
||||
uniformData.view = viewMatrix;
|
||||
uniformData.proj = m_projMatrix;
|
||||
uniformData.p = m_projMatrix;
|
||||
|
||||
using namespace resources;
|
||||
|
||||
@ -54,7 +50,7 @@ void Camera::updateCam(glm::mat4 transform, glm::mat4* viewMatOut)
|
||||
auto lockedPtr = resPtr.lock();
|
||||
auto shader = dynamic_cast<Shader*>(lockedPtr.get());
|
||||
// SET VIEW TRANSFORM HERE
|
||||
gfxdev->updateUniformBuffer(shader->getPipeline(), &uniformData);
|
||||
gfxdev->updateUniformBuffer(shader->getPipeline(), &uniformData.p, sizeof(uniformData.p), offsetof(resources::Shader::UniformBuffer, p));
|
||||
}
|
||||
|
||||
*viewMatOut = viewMatrix;
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include "components/mesh_renderer.hpp"
|
||||
|
||||
#include "resources/shader.hpp"
|
||||
|
||||
#include "object.hpp"
|
||||
|
||||
#include "resource_manager.hpp"
|
||||
@ -25,6 +27,10 @@ Renderer::~Renderer()
|
||||
|
||||
void Renderer::render(glm::mat4 transform, glm::mat4 view)
|
||||
{
|
||||
resources::Shader::UniformBuffer uniformData{};
|
||||
uniformData.color = glm::vec4{ m_color.r, m_color.g, m_color.b, 1.0 };
|
||||
gfxdev->updateUniformBuffer(m_shader->getPipeline(), &uniformData.color, sizeof(uniformData.color), offsetof(resources::Shader::UniformBuffer, color));
|
||||
|
||||
glm::mat4 pushConsts[] = { transform, view };
|
||||
gfxdev->draw(m_shader->getPipeline(), m_mesh->vb, m_mesh->ib, m_mesh->m_vertices.size(), pushConsts, sizeof(glm::mat4) * 2);
|
||||
}
|
||||
|
@ -1,29 +1,70 @@
|
||||
// The implementation of the graphics layer using OpenGL 4.5
|
||||
// This uses SDL specific code
|
||||
|
||||
//#undef ENGINE_BUILD_OPENGL
|
||||
#ifdef ENGINE_BUILD_OPENGL
|
||||
|
||||
#include "gfx_device.hpp"
|
||||
#include "util.hpp"
|
||||
#include "config.h"
|
||||
|
||||
#include "log.hpp"
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL_video.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <unordered_set>
|
||||
#include <array>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
|
||||
namespace engine {
|
||||
|
||||
// EXTERNED GLOBAL VARIABLE
|
||||
GFXDevice* gfxdev = nullptr;
|
||||
|
||||
// structures and enums
|
||||
|
||||
|
||||
|
||||
// handles
|
||||
|
||||
struct gfx::Buffer {
|
||||
gfx::BufferType type;
|
||||
// TODO
|
||||
};
|
||||
|
||||
struct gfx::Pipeline {
|
||||
// TODO
|
||||
};
|
||||
|
||||
|
||||
|
||||
// enum converters
|
||||
|
||||
namespace vkinternal {
|
||||
/*
|
||||
static GLenum getVertexAttribFormat(gfx::VertexAttribFormat fmt)
|
||||
{
|
||||
switch (fmt) {
|
||||
case gfx::VertexAttribFormat::VEC2:
|
||||
return VK_FORMAT_R32G32_SFLOAT;
|
||||
case gfx::VertexAttribFormat::VEC3:
|
||||
return VK_FORMAT_R32G32B32_SFLOAT;
|
||||
}
|
||||
throw std::runtime_error("Unknown vertex attribute format");
|
||||
}
|
||||
|
||||
static VkBufferUsageFlagBits getBufferUsageFlag(gfx::BufferType type)
|
||||
{
|
||||
switch (type) {
|
||||
case gfx::BufferType::VERTEX:
|
||||
return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
|
||||
case gfx::BufferType::INDEX:
|
||||
return VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
|
||||
}
|
||||
throw std::runtime_error("Unknown buffer type");
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
// functions
|
||||
|
||||
static std::vector<char> readFile(const std::string& filename)
|
||||
{
|
||||
std::ifstream file(filename, std::ios::ate | std::ios::binary);
|
||||
@ -37,21 +78,37 @@ namespace engine {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// class definitions
|
||||
|
||||
struct GFXDevice::Impl {
|
||||
|
||||
SDL_GLContext context = nullptr;
|
||||
SDL_Window* window = nullptr;
|
||||
|
||||
uint64_t FRAMECOUNT = 0;
|
||||
|
||||
SDL_GLContext context;
|
||||
|
||||
};
|
||||
|
||||
GFXDevice::GFXDevice(const char* appName, const char* appVersion, SDL_Window* window)
|
||||
{
|
||||
if (gfxdev != nullptr) {
|
||||
throw std::runtime_error("There can only be one graphics device");
|
||||
}
|
||||
gfxdev = this;
|
||||
|
||||
pimpl = std::make_unique<Impl>();
|
||||
|
||||
pimpl->window = window;
|
||||
|
||||
pimpl->context = SDL_GL_CreateContext(window);
|
||||
if (pimpl->context == NULL) {
|
||||
throw std::runtime_error("Unable to create OpenGL context: " + std::string(SDL_GetError()));
|
||||
}
|
||||
|
||||
if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) {
|
||||
throw std::runtime_error("Unable to initialise GLAD");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -62,42 +119,66 @@ namespace engine {
|
||||
SDL_GL_DeleteContext(pimpl->context);
|
||||
}
|
||||
|
||||
void GFXDevice::drawBuffer(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, uint32_t count)
|
||||
void GFXDevice::getViewportSize(uint32_t* w, uint32_t* h)
|
||||
{
|
||||
int width, height;
|
||||
SDL_GL_GetDrawableSize(pimpl->window, &width, &height);
|
||||
*w = (uint32_t)width;
|
||||
*h = (uint32_t)height;
|
||||
}
|
||||
|
||||
void GFXDevice::drawIndexed(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, const gfx::Buffer* indexBuffer, uint32_t indexCount)
|
||||
void GFXDevice::draw(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, const gfx::Buffer* indexBuffer, uint32_t count, const void* pushConstantData, size_t pushConstantSize)
|
||||
{
|
||||
assert(vertexBuffer->type == gfx::BufferType::VERTEX);
|
||||
assert(vertexBuffer != nullptr);
|
||||
assert(indexBuffer == nullptr || indexBuffer->type == gfx::BufferType::INDEX);
|
||||
|
||||
}
|
||||
|
||||
void GFXDevice::renderFrame()
|
||||
{
|
||||
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
SDL_GL_SwapWindow(pimpl->window);
|
||||
|
||||
pimpl->FRAMECOUNT++;
|
||||
}
|
||||
|
||||
gfx::Pipeline* GFXDevice::createPipeline(const char* vertShaderPath, const char* fragShaderPath, const gfx::VertexFormat& vertexFormat)
|
||||
gfx::Pipeline* GFXDevice::createPipeline(const char* vertShaderPath, const char* fragShaderPath, const gfx::VertexFormat& vertexFormat, uint64_t uniformBufferSize)
|
||||
{
|
||||
return nullptr;
|
||||
gfx::Pipeline* pipeline = new gfx::Pipeline{};
|
||||
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
void GFXDevice::destroyPipeline(const gfx::Pipeline* pipeline)
|
||||
{
|
||||
delete pipeline;
|
||||
}
|
||||
|
||||
void GFXDevice::updateUniformBuffer(const gfx::Pipeline* pipeline, void* data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
gfx::Buffer* GFXDevice::createBuffer(gfx::BufferType type, uint64_t size, const void* data)
|
||||
{
|
||||
return nullptr;
|
||||
auto out = new gfx::Buffer{};
|
||||
|
||||
out->type = type;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void GFXDevice::destroyBuffer(const gfx::Buffer* buffer)
|
||||
{
|
||||
|
||||
delete buffer;
|
||||
}
|
||||
|
||||
void GFXDevice::waitIdle()
|
||||
{
|
||||
glFinish();
|
||||
//glFinish();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1175,8 +1175,10 @@ namespace engine {
|
||||
|
||||
void GFXDevice::getViewportSize(uint32_t *w, uint32_t *h)
|
||||
{
|
||||
*w = pimpl->swapchain.extent.width;
|
||||
*h = pimpl->swapchain.extent.height;
|
||||
int width, height;
|
||||
SDL_Vulkan_GetDrawableSize(pimpl->window, &width, &height);
|
||||
*w = (uint32_t)width;
|
||||
*h = (uint32_t)height;
|
||||
}
|
||||
|
||||
void GFXDevice::draw(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, const gfx::Buffer* indexBuffer, uint32_t count, const void* pushConstantData, size_t pushConstantSize)
|
||||
@ -1600,15 +1602,17 @@ namespace engine {
|
||||
delete pipeline;
|
||||
}
|
||||
|
||||
void GFXDevice::updateUniformBuffer(const gfx::Pipeline* pipeline, void* data)
|
||||
void GFXDevice::updateUniformBuffer(const gfx::Pipeline* pipeline, void* data, size_t size, uint32_t offset)
|
||||
{
|
||||
assert(size <= pipeline->uniformBuffers[0]->size);
|
||||
|
||||
VkResult res;
|
||||
|
||||
void* uniformDest;
|
||||
for (gfx::Buffer* buffer : pipeline->uniformBuffers) {
|
||||
void* uniformDest;
|
||||
res = vmaMapMemory(pimpl->allocator, buffer->allocation, &uniformDest);
|
||||
assert(res == VK_SUCCESS);
|
||||
memcpy(uniformDest, data, buffer->size);
|
||||
memcpy((uint8_t*)uniformDest + offset, data, size);
|
||||
vmaUnmapMemory(pimpl->allocator, buffer->allocation);
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,9 @@ namespace engine {
|
||||
#ifdef ENGINE_BUILD_VULKAN
|
||||
windowFlags |= SDL_WINDOW_VULKAN;
|
||||
#endif
|
||||
#ifdef ENGINE_BUILD_OPENGL
|
||||
windowFlags |= SDL_WINDOW_OPENGL;
|
||||
#endif
|
||||
|
||||
if (m_resizable) {
|
||||
windowFlags |= SDL_WINDOW_RESIZABLE;
|
||||
@ -58,21 +61,6 @@ namespace engine {
|
||||
const int WINDOWED_MIN_HEIGHT = 480;
|
||||
SDL_SetWindowMinimumSize(m_handle, WINDOWED_MIN_WIDTH, WINDOWED_MIN_HEIGHT);
|
||||
|
||||
/*
|
||||
m_glContext = SDL_GL_CreateContext(m_handle);
|
||||
if (m_glContext == NULL) {
|
||||
SDL_DestroyWindow(m_handle);
|
||||
SDL_Quit();
|
||||
throw std::runtime_error("Unable to create OpenGL context: " + std::string(SDL_GetError()));
|
||||
}
|
||||
|
||||
if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) {
|
||||
SDL_DestroyWindow(m_handle);
|
||||
SDL_Quit();
|
||||
throw std::runtime_error("Unable to initialise GLAD");
|
||||
}
|
||||
*/
|
||||
|
||||
// onResize(m_winSize.x, m_winSize.y);
|
||||
|
||||
}
|
||||
|
53
test/CMakeLists.txt
Normal file
@ -0,0 +1,53 @@
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
|
||||
# options
|
||||
|
||||
project(enginetest LANGUAGES CXX
|
||||
VERSION "0.1.0"
|
||||
)
|
||||
|
||||
set(GAME_SOURCES
|
||||
|
||||
src/main.cpp
|
||||
|
||||
src/game.cpp
|
||||
src/game.hpp
|
||||
src/meshgen.cpp
|
||||
src/meshgen.hpp
|
||||
src/terrain.cpp
|
||||
src/terrain.hpp
|
||||
src/camera_controller.cpp
|
||||
src/camera_controller.hpp
|
||||
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
add_executable(${PROJECT_NAME} WIN32 ${GAME_SOURCES} "game.rc")
|
||||
else()
|
||||
add_executable(${PROJECT_NAME} ${GAME_SOURCES})
|
||||
endif()
|
||||
|
||||
# compiling options:
|
||||
|
||||
|
||||
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 20)
|
||||
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if (MSVC)
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE /W3)
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE /MP)
|
||||
endif()
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE src)
|
||||
|
||||
# Pass some project information into the source code
|
||||
configure_file(config.h.in config.h)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE engine)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE ../include)
|
||||
|
||||
add_custom_command(
|
||||
TARGET ${PROJECT_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink
|
||||
${PROJECT_SOURCE_DIR}/res $<TARGET_FILE_DIR:enginetest>/res)
|
1
test/README
Normal file
@ -0,0 +1 @@
|
||||
A random game made using my game engine
|
4
test/config.h.in
Normal file
@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#define PROJECT_VERSION "@PROJECT_VERSION@"
|
||||
#define PROJECT_NAME "@PROJECT_NAME@"
|
1
test/game.rc
Normal file
@ -0,0 +1 @@
|
||||
IDI_ICON1 ICON DISCARDABLE "icon1.ico"
|
BIN
test/icon1.ico
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
test/res/fonts/LiberationMono-Regular.ttf
Normal file
BIN
test/res/meshes/cube.mesh
Normal file
BIN
test/res/meshes/donut.mesh
Normal file
BIN
test/res/meshes/gun.mesh
Normal file
BIN
test/res/meshes/monke.mesh
Normal file
36
test/res/shader.frag
Normal file
@ -0,0 +1,36 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec3 fragPos;
|
||||
layout(location = 1) in vec3 fragNorm;
|
||||
layout(location = 2) in vec2 fragUV;
|
||||
layout(location = 3) in vec3 fragLightPos;
|
||||
layout(location = 4) in vec3 fragColor;
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
|
||||
// constants
|
||||
vec3 lightColor = vec3(1.0, 1.0, 1.0);
|
||||
vec3 ambientColor = vec3(1.0, 1.0, 1.0);
|
||||
float ambientStrength = 0.1;
|
||||
vec3 baseColor = fragColor;
|
||||
vec3 emission = vec3(0.0, 0.0, 0.0);
|
||||
|
||||
// code
|
||||
|
||||
vec3 norm = normalize(fragNorm);
|
||||
vec3 lightDir = normalize(fragLightPos - fragPos);
|
||||
|
||||
vec3 diffuse = max(dot(norm, lightDir), 0.0) * lightColor;
|
||||
vec3 ambient = ambientColor * ambientStrength;
|
||||
|
||||
vec3 viewDir = normalize(-fragPos);
|
||||
vec3 reflectDir = reflect(-lightDir, norm);
|
||||
|
||||
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
|
||||
vec3 specular = 0.5 * spec * lightColor;
|
||||
|
||||
vec3 lighting = min(diffuse + ambient + specular, 1.0);
|
||||
outColor = min( ( vec4(baseColor, 1.0) ) * vec4(lighting + emission, 1.0), vec4(1.0));
|
||||
}
|
BIN
test/res/shader.frag.spv
Normal file
31
test/res/shader.vert
Normal file
@ -0,0 +1,31 @@
|
||||
#version 450
|
||||
|
||||
layout(binding = 0) uniform UBO {
|
||||
mat4 proj;
|
||||
vec4 color;
|
||||
} ubo;
|
||||
|
||||
layout( push_constant ) uniform Constants {
|
||||
mat4 model;
|
||||
mat4 view;
|
||||
} constants;
|
||||
|
||||
layout(location = 0) in vec3 inPosition;
|
||||
layout(location = 1) in vec3 inNorm;
|
||||
layout(location = 2) in vec2 inUV;
|
||||
|
||||
layout(location = 0) out vec3 fragPos;
|
||||
layout(location = 1) out vec3 fragNorm;
|
||||
layout(location = 2) out vec2 fragUV;
|
||||
layout(location = 3) out vec3 fragLightPos;
|
||||
layout(location = 4) out vec3 fragColor;
|
||||
|
||||
void main() {
|
||||
gl_Position = ubo.proj * constants.view * constants.model * vec4(inPosition, 1.0);
|
||||
fragPos = vec3(constants.view * constants.model * vec4(inPosition, 1.0));
|
||||
fragNorm = mat3(transpose(inverse(constants.view * constants.model))) * inNorm;
|
||||
fragUV = inUV;
|
||||
vec3 lightPos = vec3(-5.0, 20.0, 5.0);
|
||||
fragLightPos = vec3(constants.view * vec4(lightPos, 1.0));
|
||||
fragColor = ubo.color.rgb;
|
||||
}
|
BIN
test/res/shader.vert.spv
Normal file
37
test/res/shaders/basic.frag
Normal file
@ -0,0 +1,37 @@
|
||||
#version 330
|
||||
|
||||
uniform float ambientStrength;
|
||||
uniform vec3 ambientColor;
|
||||
|
||||
uniform vec3 lightColor;
|
||||
uniform vec3 emission;
|
||||
|
||||
uniform vec3 baseColor;
|
||||
uniform sampler2D tex;
|
||||
|
||||
in vec3 f_Pos;
|
||||
in vec3 f_Norm;
|
||||
in vec2 f_UV;
|
||||
|
||||
in vec3 f_lightPos;
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
void main() {
|
||||
|
||||
vec3 norm = normalize(f_Norm);
|
||||
vec3 lightDir = normalize(f_lightPos - f_Pos);
|
||||
|
||||
vec3 diffuse = max(dot(norm, lightDir), 0.0) * lightColor;
|
||||
vec3 ambient = ambientColor * ambientStrength;
|
||||
|
||||
vec3 viewDir = normalize(-f_Pos);
|
||||
vec3 reflectDir = reflect(-lightDir, norm);
|
||||
|
||||
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
|
||||
vec3 specular = 0.5 * spec * lightColor;
|
||||
|
||||
vec3 lighting = min(diffuse + ambient + specular, 1.0);
|
||||
FragColor = min( ( vec4(baseColor, 1.0) * texture(tex, f_UV) ) * vec4(lighting + emission, 1.0), vec4(1.0));
|
||||
|
||||
}
|
26
test/res/shaders/basic.vert
Normal file
@ -0,0 +1,26 @@
|
||||
#version 330
|
||||
|
||||
layout (location = 0) in vec3 v_Position;
|
||||
layout (location = 1) in vec3 v_Norm;
|
||||
layout (location = 2) in vec2 v_UV;
|
||||
|
||||
uniform mat4 modelMat;
|
||||
uniform mat4 viewMat;
|
||||
uniform mat4 projMat;
|
||||
|
||||
uniform vec3 lightPos;
|
||||
|
||||
out vec3 f_Pos;
|
||||
out vec3 f_Norm;
|
||||
out vec2 f_UV;
|
||||
|
||||
out vec3 f_lightPos;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = projMat * viewMat * modelMat * vec4(v_Position, 1.0);
|
||||
f_Pos = vec3(viewMat * modelMat * vec4(v_Position, 1.0));
|
||||
f_Norm = mat3(transpose(inverse(viewMat * modelMat))) * v_Norm;
|
||||
f_UV = v_UV;
|
||||
f_lightPos = vec3(viewMat * vec4(lightPos, 1.0));
|
||||
}
|
14
test/res/shaders/font.frag
Normal file
@ -0,0 +1,14 @@
|
||||
#version 330
|
||||
|
||||
uniform vec3 textColor;
|
||||
|
||||
uniform sampler2D tex;
|
||||
|
||||
in vec2 f_UV;
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
FragColor = vec4(textColor, texture(tex, f_UV).r);
|
||||
}
|
25
test/res/shaders/font.vert
Normal file
@ -0,0 +1,25 @@
|
||||
#version 330
|
||||
|
||||
uniform vec2 windowSize;
|
||||
uniform bool textScaling; // true means keep text aspect ratio
|
||||
|
||||
layout (location = 0) in vec3 v_Position;
|
||||
layout (location = 2) in vec2 v_UV;
|
||||
|
||||
uniform mat4 modelMat;
|
||||
|
||||
out vec2 f_UV;
|
||||
|
||||
void main()
|
||||
{
|
||||
float aspect = windowSize.y / windowSize.x;
|
||||
|
||||
vec3 pos = v_Position;
|
||||
|
||||
if (textScaling) {
|
||||
pos.x *= aspect;
|
||||
}
|
||||
|
||||
gl_Position = modelMat * vec4(pos, 1.0);
|
||||
f_UV = v_UV;
|
||||
}
|
BIN
test/res/textures/MonkeTexture.png
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
test/res/textures/cobble_stone.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
test/res/textures/grass_block_top.png
Normal file
After Width: | Height: | Size: 6.5 KiB |
BIN
test/res/textures/gun.png
Normal file
After Width: | Height: | Size: 6.6 MiB |
BIN
test/res/textures/missing.png
Normal file
After Width: | Height: | Size: 125 B |
BIN
test/res/textures/stone_bricks.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
test/res/textures/ten_feet.png
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
test/res/textures/white.png
Normal file
After Width: | Height: | Size: 195 B |
118
test/src/camera_controller.cpp
Normal file
@ -0,0 +1,118 @@
|
||||
#include "camera_controller.hpp"
|
||||
|
||||
#include "object.hpp"
|
||||
|
||||
#include "window.hpp"
|
||||
#include "input.hpp"
|
||||
|
||||
#include <glm/trigonometric.hpp>
|
||||
#include <glm/gtc/constants.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <glm/gtx/rotate_vector.hpp>
|
||||
|
||||
#include <log.hpp>
|
||||
|
||||
CameraController::CameraController(engine::Object* parent) :
|
||||
CustomComponent(parent)
|
||||
{
|
||||
standingHeight = parent->transform.position.y;
|
||||
m_yaw = glm::half_pi<float>();
|
||||
}
|
||||
|
||||
void CameraController::onUpdate(glm::mat4 t)
|
||||
{
|
||||
|
||||
// calculate new position
|
||||
|
||||
// use one unit per meter
|
||||
|
||||
const float dt = win.dt();
|
||||
|
||||
// jumping
|
||||
constexpr float G = 9.8f;
|
||||
constexpr float JUMPHEIGHT = 16.0f * 25.4f / 1000.0f; // 16 inches
|
||||
constexpr float JUMPVEL = (float)2.82231110971133017648; //std::sqrt(2 * G * JUMPHEIGHT);
|
||||
//constexpr float JUMPDURATION = 0.5f;
|
||||
//constexpr float JUMPVEL = G * JUMPDURATION / 2.0f;
|
||||
|
||||
if (inp.getButton("jump") && isJumping == false) {
|
||||
isJumping = true;
|
||||
dy = JUMPVEL;
|
||||
//standingHeight = tcomp->position.y;
|
||||
|
||||
}
|
||||
|
||||
if (isJumping) {
|
||||
dy -= G * dt;
|
||||
parent.transform.position.y += dy * dt;
|
||||
if (parent.transform.position.y < standingHeight) {
|
||||
isJumping = false;
|
||||
dy = 0.0f;
|
||||
parent.transform.position.y = standingHeight;
|
||||
}
|
||||
}
|
||||
|
||||
if (win.getButton(engine::inputs::MouseButton::M_LEFT)) {
|
||||
//standingHeight = tcomp->position.y;
|
||||
dy += dt * thrust;
|
||||
isJumping = true;
|
||||
}
|
||||
|
||||
// in metres per second
|
||||
//constexpr float SPEED = 1.5f;
|
||||
float SPEED = walk_speed;
|
||||
if (win.getKey(engine::inputs::Key::LSHIFT)) SPEED *= 10.0f;
|
||||
|
||||
const float dx = inp.getAxis("movex") * SPEED;
|
||||
const float dz = (-inp.getAxis("movey")) * SPEED;
|
||||
|
||||
// calculate new pitch and yaw
|
||||
|
||||
constexpr float MAX_PITCH = glm::half_pi<float>();
|
||||
constexpr float MIN_PITCH = -MAX_PITCH;
|
||||
|
||||
float dPitch = inp.getAxis("looky") * -1.0f * m_cameraSensitivity;
|
||||
m_pitch += dPitch;
|
||||
if (m_pitch <= MIN_PITCH || m_pitch >= MAX_PITCH) {
|
||||
m_pitch -= dPitch;
|
||||
}
|
||||
m_yaw += inp.getAxis("lookx") * -1.0f * m_cameraSensitivity;
|
||||
|
||||
// update position relative to camera direction in xz plane
|
||||
const glm::vec3 d2xRotated = glm::rotateY(glm::vec3{ dx, 0.0f, 0.0f }, m_yaw);
|
||||
const glm::vec3 d2zRotated = glm::rotateY(glm::vec3{ 0.0f, 0.0f, dz }, m_yaw);
|
||||
parent.transform.position += (d2xRotated + d2zRotated) * dt;
|
||||
parent.transform.position.y += dy * dt;
|
||||
|
||||
// pitch quaternion
|
||||
const float halfPitch = m_pitch / 2.0f;
|
||||
glm::quat pitchQuat{};
|
||||
pitchQuat.x = glm::sin(halfPitch);
|
||||
pitchQuat.y = 0.0f;
|
||||
pitchQuat.z = 0.0f;
|
||||
pitchQuat.w = glm::cos(halfPitch);
|
||||
|
||||
// yaw quaternion
|
||||
const float halfYaw = m_yaw / 2.0f;
|
||||
glm::quat yawQuat{};
|
||||
yawQuat.x = 0.0f;
|
||||
yawQuat.y = glm::sin(halfYaw);
|
||||
yawQuat.z = 0.0f;
|
||||
yawQuat.w = glm::cos(halfYaw);
|
||||
|
||||
// update rotation
|
||||
parent.transform.rotation = yawQuat * pitchQuat;
|
||||
|
||||
if (win.getKeyPress(engine::inputs::Key::P)) {
|
||||
std::string pos_string{
|
||||
"x: " + std::to_string(parent.transform.position.x) +
|
||||
" y: " + std::to_string(parent.transform.position.y) +
|
||||
" z: " + std::to_string(parent.transform.position.z)
|
||||
};
|
||||
#ifdef NDEBUG
|
||||
win.infoBox("POSITION", pos_string);
|
||||
#endif
|
||||
INFO("position: " + pos_string);
|
||||
}
|
||||
|
||||
}
|
24
test/src/camera_controller.hpp
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "components/custom.hpp"
|
||||
|
||||
class CameraController : public engine::components::CustomComponent {
|
||||
|
||||
public:
|
||||
CameraController(engine::Object* parent);
|
||||
void onUpdate(glm::mat4 t) override;
|
||||
|
||||
float m_cameraSensitivity = 0.007f;
|
||||
|
||||
private:
|
||||
float m_yaw = 0.0f;
|
||||
float m_pitch = 0.0f;
|
||||
|
||||
float walk_speed = 4.0f;
|
||||
|
||||
bool isJumping = false;
|
||||
float dy = 0.0f;
|
||||
float standingHeight = 0.0f;
|
||||
float thrust = 25.0f;
|
||||
|
||||
};
|
122
test/src/game.cpp
Normal file
@ -0,0 +1,122 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "engine.hpp"
|
||||
|
||||
#include "window.hpp"
|
||||
#include "input.hpp"
|
||||
#include "sceneroot.hpp"
|
||||
|
||||
#include "components/camera.hpp"
|
||||
#include "components/mesh_renderer.hpp"
|
||||
|
||||
#include "camera_controller.hpp"
|
||||
#include "meshgen.hpp"
|
||||
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
#include <log.hpp>
|
||||
|
||||
void playGame()
|
||||
{
|
||||
engine::Application app(PROJECT_NAME, PROJECT_VERSION);
|
||||
|
||||
// configure window
|
||||
app.window()->setRelativeMouseMode(true);
|
||||
|
||||
|
||||
|
||||
// input config
|
||||
|
||||
// game buttons
|
||||
app.input()->addInputButton("fire", engine::inputs::MouseButton::M_LEFT);
|
||||
app.input()->addInputButton("aim", engine::inputs::MouseButton::M_RIGHT);
|
||||
app.input()->addInputButton("jump", engine::inputs::Key::SPACE);
|
||||
app.input()->addInputButton("sneak", engine::inputs::Key::LSHIFT);
|
||||
// game movement
|
||||
app.input()->addInputButtonAsAxis("movex", engine::inputs::Key::D, engine::inputs::Key::A);
|
||||
app.input()->addInputButtonAsAxis("movey", engine::inputs::Key::W, engine::inputs::Key::S);
|
||||
// looking around
|
||||
app.input()->addInputAxis("lookx", engine::inputs::MouseAxis::X);
|
||||
app.input()->addInputAxis("looky", engine::inputs::MouseAxis::Y);
|
||||
|
||||
|
||||
|
||||
// create the scene
|
||||
|
||||
auto cam = app.scene()->createChild("cam");
|
||||
constexpr float HEIGHT_INCHES = 6.0f * 12.0f;
|
||||
// eye level is about 4 1/2 inches below height
|
||||
constexpr float EYE_LEVEL = (HEIGHT_INCHES - 4.5f) * 25.4f / 1000.0f;
|
||||
cam->transform.position = { 0.0f, EYE_LEVEL, 0.0f };
|
||||
auto camCamera = cam->createComponent<engine::components::Camera>();
|
||||
camCamera->usePerspective(70.0f);
|
||||
cam->createComponent<CameraController>();
|
||||
cam->createComponent<engine::components::Renderer>()->m_mesh = genSphereMesh(0.2f, 20);
|
||||
cam->getComponent<engine::components::Renderer>()->setTexture("textures/cobble_stone.png");
|
||||
|
||||
auto gun = cam->createChild("gun");
|
||||
gun->transform.position = glm::vec3{ 0.2f, -0.1f, -0.15f };
|
||||
gun->transform.rotation = glm::angleAxis(glm::pi<float>(), glm::vec3{ 0.0f, 1.0f, 0.0f });
|
||||
float GUN_SCALE = 9.0f / 560.0f;
|
||||
GUN_SCALE *= 1.0f;
|
||||
gun->transform.scale *= GUN_SCALE;
|
||||
auto gunRenderer = gun->createComponent<engine::components::Renderer>();
|
||||
gunRenderer->setMesh("meshes/gun.mesh");
|
||||
gunRenderer->setTexture("textures/gun.png");
|
||||
gunRenderer->m_color = { 0.2f, 0.3f, 0.2f };
|
||||
|
||||
// FLOOR
|
||||
|
||||
constexpr float GRASS_DENSITY = 128.0f * 20.0f;
|
||||
auto floor = app.scene()->createChild("floor");
|
||||
auto floorRenderer = floor->createComponent<engine::components::Renderer>();
|
||||
floor->transform.position = glm::vec3{ 0.0f, 0.0f, 0.0f };
|
||||
floorRenderer->setTexture("textures/stone_bricks.png");
|
||||
floorRenderer->m_mesh = std::make_unique<engine::resources::Mesh>(std::vector<Vertex>{
|
||||
{ { -16.0f, 0.0f, 16.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, GRASS_DENSITY } },
|
||||
{ { 16.0f, 0.0f, -16.0f }, { 0.0f, 1.0f, 0.0f }, { GRASS_DENSITY, 0.0f } },
|
||||
{ { -16.0f, 0.0f, -16.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f } },
|
||||
{ { 16.0f, 0.0f, 16.0f }, { 0.0f, 1.0f, 0.0f }, { GRASS_DENSITY, GRASS_DENSITY } },
|
||||
{ { 16.0f, 0.0f, -16.0f }, { 0.0f, 1.0f, 0.0f }, { GRASS_DENSITY, 0.0f } },
|
||||
{ { -16.0f, 0.0f, 16.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, GRASS_DENSITY } }
|
||||
});
|
||||
floor->transform.scale = { 100.0f, 1.0f, 100.0f };
|
||||
floorRenderer->m_color = { 0.1f, 0.9f, 0.1f };
|
||||
|
||||
auto cube = app.scene()->createChild("cube");
|
||||
auto cubeRen = cube->createComponent<engine::components::Renderer>();
|
||||
cubeRen->setMesh("meshes/cube.mesh");
|
||||
cubeRen->m_color = { 0.8f, 0.2f, 0.05f };
|
||||
|
||||
cube->transform.position = glm::vec3{ -5.0f, 1.0f, 0.0f };
|
||||
class Spin : public engine::components::CustomComponent {
|
||||
|
||||
public:
|
||||
Spin(engine::Object* parent) : CustomComponent(parent)
|
||||
{
|
||||
|
||||
}
|
||||
void onUpdate(glm::mat4 t) override
|
||||
{
|
||||
m_yaw += win.dt();
|
||||
m_yaw = glm::mod(m_yaw, glm::two_pi<float>());
|
||||
const float halfYaw = m_yaw / 2.0f;
|
||||
glm::quat yawQuat{};
|
||||
yawQuat.x = 0.0f;
|
||||
yawQuat.y = glm::sin(halfYaw);
|
||||
yawQuat.z = 0.0f;
|
||||
yawQuat.w = glm::cos(halfYaw);
|
||||
parent.transform.rotation = yawQuat;
|
||||
}
|
||||
|
||||
private:
|
||||
float m_yaw = 0.0f;
|
||||
|
||||
};
|
||||
app.scene()->getChild("cube")->createComponent<Spin>();
|
||||
|
||||
|
||||
app.scene()->printTree();
|
||||
|
||||
app.gameLoop();
|
||||
}
|
3
test/src/game.hpp
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void playGame();
|
34
test/src/main.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
#include "config.h"
|
||||
#include "game.hpp"
|
||||
|
||||
#include "logger.hpp"
|
||||
#include "window.hpp"
|
||||
|
||||
#include <exception>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
engine::setupLog(PROJECT_NAME);
|
||||
|
||||
INFO("{} v{}", PROJECT_NAME, PROJECT_VERSION);
|
||||
|
||||
try {
|
||||
playGame();
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
|
||||
CRITICAL("{}", e.what());
|
||||
|
||||
#ifdef NDEBUG
|
||||
engine::Window::errorBox(e.what());
|
||||
#else
|
||||
fputs(e.what(), stderr);
|
||||
fputc('\n', stderr);
|
||||
#endif
|
||||
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
135
test/src/meshgen.cpp
Normal file
@ -0,0 +1,135 @@
|
||||
#include "meshgen.hpp"
|
||||
|
||||
#include <glm/gtc/constants.hpp>
|
||||
#include <glm/ext.hpp>
|
||||
#include <glm/trigonometric.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <thread>
|
||||
|
||||
std::unique_ptr<engine::resources::Mesh> genSphereMesh(float r, int detail, bool windInside)
|
||||
{
|
||||
using namespace glm;
|
||||
|
||||
std::vector<Vertex> vertices{};
|
||||
|
||||
float angleStep = two_pi<float>() / (float)detail;
|
||||
|
||||
for (int i = 0; i < detail; i++) {
|
||||
// theta goes north-to-south
|
||||
float theta = i * angleStep;
|
||||
float theta2 = theta + angleStep;
|
||||
for (int j = 0; j < detail/2; j++) {
|
||||
// phi goes west-to-east
|
||||
float phi = j * angleStep;
|
||||
float phi2 = phi + angleStep;
|
||||
|
||||
vec3 top_left{ r * sin(phi) * cos(theta),
|
||||
r * cos(phi),
|
||||
r * sin(phi) * sin(theta) };
|
||||
vec3 bottom_left{ r * sin(phi) * cos(theta2),
|
||||
r * cos(phi),
|
||||
r * sin(phi) * sin(theta2) };
|
||||
vec3 top_right{ r * sin(phi2) * cos(theta),
|
||||
r * cos(phi2),
|
||||
r * sin(phi2) * sin(theta) };
|
||||
vec3 bottom_right{ r * sin(phi2) * cos(theta2),
|
||||
r * cos(phi2),
|
||||
r * sin(phi2) * sin(theta2) };
|
||||
|
||||
if (windInside == false) {
|
||||
// tris are visible from outside the sphere
|
||||
|
||||
// triangle 1
|
||||
vertices.push_back({ top_left, {}, {0.0f, 0.0f} });
|
||||
vertices.push_back({ bottom_left, {}, {0.0f, 1.0f} });
|
||||
vertices.push_back({ bottom_right, {}, {1.0f, 1.0f} });
|
||||
// triangle 2
|
||||
vertices.push_back({ top_right, {}, {1.0f, 0.0f} });
|
||||
vertices.push_back({ top_left, {}, {0.0f, 0.0f} });
|
||||
vertices.push_back({ bottom_right, {}, {1.0f, 1.0f} });
|
||||
|
||||
}
|
||||
else {
|
||||
// tris are visible from inside the sphere
|
||||
|
||||
// triangle 1
|
||||
vertices.push_back({ bottom_right, {}, {1.0f, 1.0f} });
|
||||
vertices.push_back({ bottom_left, {}, {0.0f, 1.0f} });
|
||||
vertices.push_back({ top_left, {}, {0.0f, 0.0f} });
|
||||
|
||||
// triangle 2
|
||||
vertices.push_back({ bottom_right, {}, {1.0f, 1.0f} });
|
||||
vertices.push_back({ top_left, {}, {0.0f, 0.0f} });
|
||||
vertices.push_back({ top_right, {}, {1.0f, 0.0f} });
|
||||
|
||||
}
|
||||
|
||||
glm::vec3 vector1 = (vertices.end() - 1)->pos - (vertices.end() - 2)->pos;
|
||||
glm::vec3 vector2 = (vertices.end() - 2)->pos - (vertices.end() - 3)->pos;
|
||||
glm::vec3 norm = glm::normalize(glm::cross(vector1, vector2));
|
||||
|
||||
|
||||
// TODO: FIX NORMALS
|
||||
if (!windInside)
|
||||
norm = -norm;
|
||||
|
||||
for (auto it = vertices.end() - 6; it != vertices.end(); it++) {
|
||||
it->norm = norm;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_unique<engine::resources::Mesh>(vertices);
|
||||
}
|
||||
|
||||
std::unique_ptr<engine::resources::Mesh> genCuboidMesh(float x, float y, float z)
|
||||
{
|
||||
|
||||
// x goes ->
|
||||
// y goes ^
|
||||
// z goes into the screen
|
||||
|
||||
using glm::vec3;
|
||||
|
||||
std::vector<Vertex> v{};
|
||||
|
||||
// 0 top_left_front
|
||||
v.push_back({{ 0.0f, y, 0.0f }, {}, {}});
|
||||
// 1 bottom_left_front
|
||||
v.push_back({{ 0.0f, 0.0f, 0.0f }, {}, {}});
|
||||
// 2 top_right_front
|
||||
v.push_back({{ x, y, 0.0f }, {}, {}});
|
||||
// 3 bottom_right_front
|
||||
v.push_back({{ x, 0.0f, 0.0f }, {}, {}});
|
||||
|
||||
// 4 top_left_back
|
||||
v.push_back({{ 0.0f, y, z }, {}, {}});
|
||||
// 5 bottom_left_back
|
||||
v.push_back({{ 0.0f, 0.0f, z }, {}, {}});
|
||||
// 6 top_right_back
|
||||
v.push_back({{ x, y, z }, {}, {}});
|
||||
// 7 bottom_right_back
|
||||
v.push_back({{ x, 0.0f, z }, {}, {}});
|
||||
|
||||
// front quad
|
||||
std::vector<unsigned int> indices{
|
||||
// front
|
||||
0, 1, 3, 2, 0, 3,
|
||||
// back
|
||||
4, 5, 7, 6, 4, 7,
|
||||
// bottom
|
||||
5, 1, 3, 7, 5, 3,
|
||||
// top
|
||||
4, 0, 2, 6, 4, 2,
|
||||
// left
|
||||
4, 5, 1, 0, 4, 1,
|
||||
// right
|
||||
2, 3, 7, 6, 2, 7
|
||||
};
|
||||
|
||||
return std::make_unique<engine::resources::Mesh>(v, indices);
|
||||
|
||||
}
|
7
test/src/meshgen.hpp
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "resources/mesh.hpp"
|
||||
|
||||
// generates a UV sphere
|
||||
std::unique_ptr<engine::resources::Mesh> genSphereMesh(float r, int detail, bool windInside = false);
|
||||
std::unique_ptr<engine::resources::Mesh> genCuboidMesh(float x, float y, float z);
|
21
test/src/terrain.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
#if 0
|
||||
|
||||
#include "terrain.hpp"
|
||||
|
||||
#include "resources/mesh.hpp"
|
||||
|
||||
std::unique_ptr<engine::resources::Mesh> getChunkMesh(int x, int y)
|
||||
{
|
||||
(void)x;
|
||||
(void)y;
|
||||
|
||||
std::vector<Vertex> vertices{
|
||||
{{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}},
|
||||
{{1.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}},
|
||||
{{0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}
|
||||
};
|
||||
|
||||
return std::make_unique<engine::resources::Mesh>(vertices);
|
||||
}
|
||||
|
||||
#endif
|
9
test/src/terrain.hpp
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace engine::resources {
|
||||
class Mesh;
|
||||
}
|
||||
|
||||
std::unique_ptr<engine::resources::Mesh> getChunkMesh(int x, int y);
|