diff --git a/.gitignore b/.gitignore index c482431c..42df0183 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ .* !.gitignore !.gitmodules +out \ No newline at end of file diff --git a/resources/shader/demo.frag b/resources/shader/demo.frag new file mode 100644 index 00000000..2b02c502 --- /dev/null +++ b/resources/shader/demo.frag @@ -0,0 +1,74 @@ +#version 450 + +#define LIGHT_TYPE_DIRECTIONAL 0 +#define LIGHT_TYPE_SPOT 1 +#define LIGHT_TYPE_POINT 2 + +layout(location = 0) in vec2 uv_coord_frag; +layout(location = 1) in vec3 normal_vec_frag; +layout(location = 2) in vec3 render_position_frag; + +layout(binding = 0) uniform sampler2D tx_color; +layout(binding = 1) uniform sampler2D tx_phong;// stores ambient/diffuse/specular/shinyness weights in r/g/b/a respectively + +layout(location = 0) out vec4 out_color; + +uniform vec3 camera_position; +uniform vec3 ambient_light; + +// TODO figure out what to do about multiple light sources (ideally with variable length - "Shader Storage Buffer Objects" ? ) +uniform int light_type; +uniform vec3 light_position; +uniform vec3 light_direction; +uniform float light_range; +uniform float light_spot_angle; +uniform vec3 light_color; +uniform float light_intensity; + +void main() +{ + vec4 texColor = texture(tx_color, uv_coord_frag); + vec4 phongData = texture(tx_phong, uv_coord_frag); + vec3 ambientColor = max(phongData.r * ambient_light, 0.0f); + vec3 diffuseColor; + vec3 specularColor; + + // TODO light bounces? occlusion? + if (light_type == LIGHT_TYPE_DIRECTIONAL){ + // direction, color, intensity + + float diffuseDot = max(dot(-light_direction, normal_vec_frag), 0.0f); + float specularDot = max(dot(reflect(light_direction, normal_vec_frag), normalize(camera_position - render_position_frag)), 0.0f); + diffuseColor = phongData.g * diffuseDot * light_color * light_intensity; + specularColor = phongData.b * 8 * pow(specularDot, phongData.a * 255) * light_color * light_intensity; + + } else if (light_type == LIGHT_TYPE_SPOT){ + // position, direction, range, spot_angle, color, intensity + vec3 spotLightDirection = normalize(render_position_frag - light_position); + // here we're (ab-)using the fact that the dot product of two normalized vectors is the cosine of their angle + float spotLightAngle = dot(light_direction, spotLightDirection); + float spotLightDistance = length(render_position_frag - light_position); + if (spotLightAngle > light_spot_angle && spotLightDistance <= light_range){ + float distanceFactor = 0.01f / pow(spotLightDistance / light_range, 2); + float diffuseDot = max(dot(-spotLightDirection, normal_vec_frag), 0.0f); + float specularDot = max(dot(reflect(spotLightDirection, normal_vec_frag), normalize(camera_position - render_position_frag)), 0.0f); + diffuseColor = phongData.g * diffuseDot * light_color * light_intensity * distanceFactor; + specularColor = phongData.b * 8 * pow(specularDot, phongData.a * 255) * light_color * light_intensity * distanceFactor; + } + + } else if (light_type == LIGHT_TYPE_POINT){ + // position, range, color, intensity + // light intensity should diminish proportionally to `1 / distance_squared` + vec3 pointLightDirection = normalize(render_position_frag - light_position); + float pointLightDistance = length(render_position_frag - light_position); + if (pointLightDistance <= light_range){ + float distanceFactor = 0.01f / pow(pointLightDistance / light_range, 2); + float diffuseDot = max(dot(-pointLightDirection, normal_vec_frag), 0.0f); + float specularDot = max(dot(reflect(pointLightDirection, normal_vec_frag), normalize(camera_position - render_position_frag)), 0.0f); + diffuseColor = phongData.g * diffuseDot * light_color * light_intensity * distanceFactor; + specularColor = phongData.b * 8 * pow(specularDot, phongData.a * 255) * light_color * light_intensity * distanceFactor; + } + } + + out_color = texColor * vec4(ambientColor + diffuseColor + specularColor, 1.0f); +} \ No newline at end of file diff --git a/resources/shader/demo.vert b/resources/shader/demo.vert new file mode 100644 index 00000000..28579417 --- /dev/null +++ b/resources/shader/demo.vert @@ -0,0 +1,22 @@ +#version 450 + +layout(location = 0) in vec3 in_position; +layout(location = 1) in vec2 uv_coord; +layout(location = 2) in vec3 normal_vec; + +layout(location = 0) out vec2 uv_coord_frag; +layout(location = 1) out vec3 normal_vec_frag; +layout(location = 2) out vec3 render_position_frag; + +uniform mat4 object_to_world_matrix; +uniform mat4 world_to_camera_matrix; + +void main() +{ + vec4 worldPosition = object_to_world_matrix * vec4(in_position, 1.0f); + gl_Position = world_to_camera_matrix * worldPosition; + render_position_frag = worldPosition.xyz; + + uv_coord_frag = uv_coord; + normal_vec_frag = normal_vec; +} \ No newline at end of file diff --git a/resources/textures/Planet1_phong.png b/resources/textures/Planet1_phong.png new file mode 100644 index 00000000..af32edfd Binary files /dev/null and b/resources/textures/Planet1_phong.png differ diff --git a/src/engine/graphics/camera.cpp b/src/engine/graphics/camera.cpp index 180a5e76..8354bb03 100644 --- a/src/engine/graphics/camera.cpp +++ b/src/engine/graphics/camera.cpp @@ -4,38 +4,125 @@ namespace graphics { - using namespace glm; - - Camera::Camera(float _fov, float _zNear, float zFar) - : m_projection(glm::perspective(glm::radians(_fov), Device::getAspectRatio(), _zNear, zFar)), - m_view(glm::identity()) - { - updateMatrices(); - } - - Camera::Camera(glm::vec2 _size, glm::vec2 _origin, float _zNear, float _zFar) - : m_projection(glm::ortho(-_size.x*_origin.x, _size.x*(1.f-_origin.x), - -_size.y * _origin.y, _size.y* (1.f - _origin.y), - _zNear, _zFar)), - m_view(glm::identity()) - { - updateMatrices(); - } - - vec3 Camera::toWorldSpace(const vec2& _screenSpace) const - { - vec2 clipSpace = _screenSpace / vec2(Device::getBufferSize()); - clipSpace.y = 1.f - clipSpace.y; - clipSpace = clipSpace * 2.f - vec2(1.f, 1.f); - - const vec4 worldSpace = m_viewProjectionInv * vec4(clipSpace, -1.f, 1.f); - - return vec3(worldSpace) / worldSpace.w; - } - - void Camera::updateMatrices() - { - m_viewProjection = m_projection * m_view; - m_viewProjectionInv = glm::inverse(m_viewProjection); - } + using namespace glm; + constexpr glm::vec3 RIGHT_VECTOR = glm::vec3(1.0f, 0.0f, 0.0f); + constexpr glm::vec3 UP_VECTOR = glm::vec3(0.0f, 1.0f, 0.0f); + constexpr glm::vec3 FORWARD_VECTOR = glm::vec3(0.0f, 0.0f, 1.0f); + + Camera::Camera(float _fov, float _zNear, float zFar) + : m_projection(glm::perspective(glm::radians(_fov), Device::getAspectRatio(), _zNear, zFar)), + m_view(glm::identity()) { + updateMatrices(); + } + + Camera::Camera(glm::vec2 _size, glm::vec2 _origin, float _zNear, float _zFar) + : m_projection(glm::ortho(-_size.x * _origin.x, _size.x * (1.f - _origin.x), + -_size.y * _origin.y, _size.y * (1.f - _origin.y), + _zNear, _zFar)), + m_view(glm::identity()) { + updateMatrices(); + } + + vec3 Camera::toWorldSpace(const vec2 &_screenSpace) const { + vec2 clipSpace = _screenSpace / vec2(Device::getBufferSize()); + clipSpace.y = 1.f - clipSpace.y; + clipSpace = clipSpace * 2.f - vec2(1.f, 1.f); + + const vec4 worldSpace = m_viewProjectionInv * vec4(clipSpace, -1.f, 1.f); + + return vec3(worldSpace) / worldSpace.w; + } + + void Camera::updateMatrices() { + m_viewProjection = m_projection * m_view; + m_viewProjectionInv = glm::inverse(m_viewProjection); + updateTransform(); + } + + void Camera::updateTransform() { + lookAtMatrix = glm::lookAt(cameraPosition, cameraPosition + cameraForward, cameraUp); + // fix x direction (gets inverted due to RHS / cross product) + lookAtMatrix[0][0] *= -1.0f; + lookAtMatrix[1][0] *= -1.0f; + lookAtMatrix[2][0] *= -1.0f; + lookAtMatrix[3][0] *= -1.0f; + worldToCameraMatrix = m_viewProjection * lookAtMatrix; + } + + void Camera::moveRelative(const float &right, const float &up, const float &forward) { + if (right == 0.0f && up == 0.0f && forward == 0.0f) { + return; + } + + if (right != 0.0f) { + cameraPosition += cameraRight * right; + } + if (up != 0.0f) { + cameraPosition += cameraUp * up; + } + if (forward != 0.0f) { + cameraPosition += cameraForward * forward; + } + updateTransform(); + } + + void Camera::moveAbsolute(const vec3 &direction) { + cameraPosition += direction; + updateTransform(); + } + + void Camera::setRotation(const float &yawAngle, const float &pitchAngle, const float &rollAngle) { + cameraRight = RIGHT_VECTOR; + cameraUp = UP_VECTOR; + cameraForward = FORWARD_VECTOR; + rotate(yawAngle, pitchAngle, rollAngle); + } + + void Camera::setRotation(const glm::mat3 &rotationMatrix) { + cameraRight = rotationMatrix * RIGHT_VECTOR; + cameraUp = rotationMatrix * UP_VECTOR; + cameraForward = rotationMatrix * FORWARD_VECTOR; + updateTransform(); + } + + void Camera::rotate(const float &yawAngle, const float &pitchAngle, const float &rollAngle) { + if (yawAngle == 0.0f && pitchAngle == 0.0f && rollAngle == 0.0f) { + return; + } + + if (yawAngle != 0.0f) { + auto yawRotationMatrix = glm::mat3(glm::rotate( + glm::identity(), + glm::radians(yawAngle), + cameraUp)); + cameraRight = yawRotationMatrix * cameraRight; + cameraForward = yawRotationMatrix * cameraForward; + } + if (pitchAngle != 0.0f) { + auto pitchRotationMatrix = glm::mat3(glm::rotate( + glm::identity(), + glm::radians(pitchAngle), + cameraRight)); + cameraForward = pitchRotationMatrix * cameraForward; + cameraUp = pitchRotationMatrix * cameraUp; + } + if (rollAngle != 0.0f) { + auto rollRotationMatrix = glm::mat3(glm::rotate( + glm::identity(), + glm::radians(rollAngle), + cameraForward)); + cameraRight = rollRotationMatrix * cameraRight; + cameraUp = rollRotationMatrix * cameraUp; + } + + updateTransform(); + } + + void Camera::rotate(const mat3 &rotationMatrix) { + cameraRight = rotationMatrix * cameraRight; + cameraUp = rotationMatrix * cameraUp; + cameraForward = rotationMatrix * cameraForward; + updateTransform(); + } + } \ No newline at end of file diff --git a/src/engine/graphics/camera.hpp b/src/engine/graphics/camera.hpp index f357ad3f..38f9af0f 100644 --- a/src/engine/graphics/camera.hpp +++ b/src/engine/graphics/camera.hpp @@ -4,29 +4,76 @@ namespace graphics { - class Camera - { - public: - // Perspective - // @param _fov as angle - Camera(float _fov, float _zNear, float zFar); - // Orthogonal - Camera(glm::vec2 _size, glm::vec2 _origin = glm::vec2(0.f), float _zNear = 0.f, float _zFar = 1.f); - - const glm::mat4& getView() const { return m_view; } - const glm::mat4& getProjection() const { return m_projection; } - const glm::mat4& getViewProjection() const { return m_viewProjection; } - - void setView(const glm::mat4& _view) { m_view = _view; updateMatrices(); } - - // @Return the position of the point _screenSpace on the near plane of this camera - glm::vec3 toWorldSpace(const glm::vec2& _sceenSpace) const; - private: - void updateMatrices(); - - glm::mat4 m_projection; - glm::mat4 m_view; - glm::mat4 m_viewProjection; - glm::mat4 m_viewProjectionInv; - }; + class Camera { + public: + // Perspective + // @param _fov as angle + Camera(float _fov, float _zNear, float zFar); + + // Orthogonal + Camera(glm::vec2 _size, glm::vec2 _origin = glm::vec2(0.f), float _zNear = 0.f, float _zFar = 1.f); + + [[nodiscard]] const glm::mat4 &getView() const { return m_view; } + + [[nodiscard]] const glm::mat4 &getProjection() const { return m_projection; } + + [[nodiscard]] const glm::mat4 &getViewProjection() const { return m_viewProjection; } + + void setView(const glm::mat4 &_view) { + m_view = _view; + updateMatrices(); + } + + // @Return the position of the point _screenSpace on the near plane of this camera + [[nodiscard]] glm::vec3 toWorldSpace(const glm::vec2 &_screenSpace) const; + + [[nodiscard]] const glm::vec3 &getPosition() const { return cameraPosition; } + + void setPosition(const glm::vec3 &position) { + cameraPosition = position; + updateTransform(); + } + + void moveRelative(const float &right, const float &up, const float &forward); + + void moveAbsolute(const glm::vec3 &direction); + + [[nodiscard]] const glm::vec3 &forwardVector() const { return cameraForward; } + + [[nodiscard]] const glm::vec3 &upVector() const { return cameraUp; } + + [[nodiscard]] const glm::vec3 &rightVector() const { return cameraRight; } + + // sets the camera rotation + void setRotation(const float &yawAngle, const float &pitchAngle, const float &rollAngle); + + // sets the camera rotation + void setRotation(const glm::mat3 &rotationMatrix); + + // rotates the camera based on its current rotation + void rotate(const float &yawAngle, const float &pitchAngle, const float &rollAngle); + + // rotates the camera based on its current rotation + void rotate(const glm::mat3 &rotationMatrix); + + [[nodiscard]] const glm::mat4 &getWorldToCamera() const { return worldToCameraMatrix; } + + private: + void updateMatrices(); + + void updateTransform(); + + glm::mat4 m_projection; + glm::mat4 m_view; + glm::mat4 m_viewProjection; + glm::mat4 m_viewProjectionInv; + + glm::vec3 cameraPosition = {0.0f, 0.0f, 0.0f}; + glm::vec3 cameraForward = {0.0f, 0.0f, 1.0f}; + glm::vec3 cameraUp = {0.0f, 1.0f, 0.0f}; + glm::vec3 cameraRight = {1.0f, 0.0f, 0.0f}; + glm::mat4 lookAtMatrix; + + glm::mat4 worldToCameraMatrix; + }; } \ No newline at end of file diff --git a/src/engine/graphics/core/geometrybuffer.cpp b/src/engine/graphics/core/geometrybuffer.cpp index f2ed4f67..1cd68acb 100644 --- a/src/engine/graphics/core/geometrybuffer.cpp +++ b/src/engine/graphics/core/geometrybuffer.cpp @@ -42,7 +42,9 @@ namespace graphics { m_indexSize(_indexed), m_vertexCount(0), m_indexCount(0), - m_instanceCount(0) + m_instanceCount(0), + m_vao(0), + m_vbo(0) { // Analyze vertex data (vertexsize, instancesize) for(int i = 0; i < _numAttributes; ++i) @@ -80,13 +82,13 @@ namespace graphics { glCall(glBufferData, GL_ARRAY_BUFFER, m_instanceCapacity, nullptr, GL_STATIC_DRAW); // Bind Attributes - unsigned offset = 0; + unsigned offset2 = 0; for(int i = 0; i < _numAttributes; ++i) if(_attributes[i].perInstance) { // Also set this attribute to be instanced glCall(glVertexAttribDivisor, attribIndex, 1); - enableAttribute(attribIndex++, _attributes[i], m_instanceVertexSize, offset); - offset += attributeSize(_attributes[i]); + enableAttribute(attribIndex++, _attributes[i], m_instanceVertexSize, offset2); + offset2 += attributeSize(_attributes[i]); } } diff --git a/src/engine/graphics/lightdata.cpp b/src/engine/graphics/lightdata.cpp new file mode 100644 index 00000000..c7d908b2 --- /dev/null +++ b/src/engine/graphics/lightdata.cpp @@ -0,0 +1,29 @@ +#include "lightdata.h" + +namespace graphics { + + void LightData::prepareBinding(const unsigned int &programID) { + currentProgramID = programID; + glsl_light_type = glGetUniformLocation(programID, "light_type"); + glsl_light_position = glGetUniformLocation(programID, "light_position"); + glsl_light_direction = glGetUniformLocation(programID, "light_direction"); + glsl_light_range = glGetUniformLocation(programID, "light_range"); + glsl_light_spot_angle = glGetUniformLocation(programID, "light_spot_angle"); + glsl_light_color = glGetUniformLocation(programID, "light_color"); + glsl_light_intensity = glGetUniformLocation(programID, "light_intensity"); + } + + void LightData::bindData(const unsigned int &programID) { + if (currentProgramID != programID) { + prepareBinding(programID); + } + + glUniform1i(glsl_light_type, light_type); + glUniform3fv(glsl_light_position, 1, glm::value_ptr(light_position)); + glUniform3fv(glsl_light_direction, 1, glm::value_ptr(light_direction)); + glUniform1f(glsl_light_range, light_range); + glUniform1f(glsl_light_spot_angle, light_type == LightType::spot ? glm::cos(glm::radians(light_spot_angle)) : 0.0f); + glUniform3fv(glsl_light_color, 1, glm::value_ptr(light_color)); + glUniform1f(glsl_light_intensity, light_intensity); + } +} \ No newline at end of file diff --git a/src/engine/graphics/lightdata.h b/src/engine/graphics/lightdata.h new file mode 100644 index 00000000..8e7d4f31 --- /dev/null +++ b/src/engine/graphics/lightdata.h @@ -0,0 +1,111 @@ +#ifndef ACAENGINE_LIGHTDATA_H +#define ACAENGINE_LIGHTDATA_H + +#include +#include +#include + +namespace graphics { + enum LightType : int { + directional = 0, + spot = 1, + point = 2 + }; + + struct LightData { + /** + * Directional Light: + * position - + * direction direction the light is emitted in + * range - + * spot_angle - + * color color of the emitted light + * intensity simple multiplier for emitted light + * Spot-Light: + * position position of the light source in world space + * direction direction the spot-light is facing + * range maximum range of emitted light + * spot_angle max angle the light is emitted at in degrees (e.g. 5° means light is emitted in a cone of 2.5° from the spot-light's direction) + * color color of the emitted light + * intensity resulting light-intensity at a given point is `intensity * ( 1 - ([point_to_light_distance] / [range]) )²` + * Point-Light: + * position position of the light source in world space + * direction - + * range maximum range of emitted light + * spot_angle - + * color color of the emitted light + * intensity resulting light-intensity at a given point is `intensity * ( 1 - ([point_to_light_distance] / [range]) )²` + */ + + LightType light_type; + glm::vec3 light_position; + glm::vec3 light_direction; + float light_range; + float light_spot_angle; + glm::vec3 light_color; + float light_intensity; + + static constexpr LightData directional(glm::vec3 direction, glm::vec3 lightColor, float lightIntensity) { + return LightData{ + LightType::directional, + glm::vec3(0.0f, 0.0f, 0.0f), + direction, + 0.0f, + 0.0f, + lightColor, + lightIntensity + }; + } + + static constexpr LightData spot(glm::vec3 position, glm::vec3 direction, float range, float spotAngle, glm::vec3 lightColor, float lightIntensity) { + return LightData{ + LightType::spot, + position, + direction, + range, + spotAngle, + lightColor, + lightIntensity + }; + } + + static constexpr LightData point(glm::vec3 position, float range, glm::vec3 lightColor, float lightIntensity) { + return LightData{ + LightType::point, + position, + glm::vec3(0.0f, 0.0f, 0.0f), + range, + 0.0f, + lightColor, + lightIntensity + }; + } + + void bindData(const unsigned int &programID); + + private: + constexpr LightData(LightType lightType, glm::vec3 lightPosition, glm::vec3 lightDirection, float lightRange, float lightSpotAngle, + glm::vec3 lightColor, float lightIntensity) : + light_type(lightType), + light_position(lightPosition), + light_direction(lightDirection), + light_range(lightRange), + light_spot_angle(lightSpotAngle), + light_color(lightColor), + light_intensity(lightIntensity) {} + + void prepareBinding(const unsigned int &programID); + + unsigned int currentProgramID = -1; + GLint glsl_light_type = 0; + GLint glsl_light_position = 0; + GLint glsl_light_direction = 0; + GLint glsl_light_range = 0; + GLint glsl_light_spot_angle = 0; + GLint glsl_light_color = 0; + GLint glsl_light_intensity = 0; + }; +} + + +#endif //ACAENGINE_LIGHTDATA_H diff --git a/src/engine/graphics/renderer/mesh.cpp b/src/engine/graphics/renderer/mesh.cpp index f98423c2..734c795d 100644 --- a/src/engine/graphics/renderer/mesh.cpp +++ b/src/engine/graphics/renderer/mesh.cpp @@ -1,6 +1,60 @@ #include "mesh.hpp" -#include "mesh.hpp" namespace graphics { + static graphics::GeometryBuffer *createStripBuffer(const graphics::VertexAttribute *vertexAttribute, int numAttributes) { + auto *buffer = new graphics::GeometryBuffer( + graphics::GLPrimitiveType::TRIANGLE_STRIPE, + vertexAttribute, + numAttributes, + 0 + ); + return buffer; + } + + static graphics::GeometryBuffer *createTriangleBuffer(const graphics::VertexAttribute *vertexAttribute, int numAttributes) { + auto *buffer = new graphics::GeometryBuffer( + graphics::GLPrimitiveType::TRIANGLES, + vertexAttribute, + numAttributes, + 0 + ); + return buffer; + } + + Mesh::Mesh(const utils::MeshData::Handle &_meshData) { + utils::MeshStripData::Handle meshStripData = utils::MeshStripData::createFromMeshData(_meshData); + graphics::GeometryBuffer *triBuffer = createTriangleBuffer(VERTEX_ATTRIBUTES.data(), VERTEX_ATTRIBUTES.size()); + auto *triBufferData = new VertexData[meshStripData->floatingTriangles.size() * 3]; + int dataIndex = 0; + for (const auto &floatingTriangle: meshStripData->floatingTriangles) { + for (int i = 0; i < 3; i++) { + triBufferData[dataIndex + i] = { + _meshData->positions[floatingTriangle.indices[i].positionIdx], + _meshData->textureCoordinates[floatingTriangle.indices[i].textureCoordinateIdx.value_or(0)], + _meshData->normals[floatingTriangle.indices[i].normalIdx.value_or(0)] + }; + } + dataIndex += 3; + } + triBuffer->setData(triBufferData, sizeof(VertexData) * meshStripData->floatingTriangles.size() * 3); + buffers.push_back(triBuffer); + + for (const auto &strip: meshStripData->triangleStrips) { + auto *stripBuffer = createStripBuffer(VERTEX_ATTRIBUTES.data(), VERTEX_ATTRIBUTES.size()); + unsigned int vertexCount = strip.vertexIndices.size(); + auto *stripBufferData = new VertexData[vertexCount]; + dataIndex = 0; + for (const auto &vertexIndex: strip.vertexIndices) { + stripBufferData[dataIndex] = { + _meshData->positions[vertexIndex.positionIdx], + _meshData->textureCoordinates[vertexIndex.textureCoordinateIdx.value_or(0)], + _meshData->normals[vertexIndex.normalIdx.value_or(0)] + }; + dataIndex++; + } + stripBuffer->setData(stripBufferData, sizeof(VertexData) * vertexCount); + buffers.push_back(stripBuffer); + } + } } \ No newline at end of file diff --git a/src/engine/graphics/renderer/mesh.hpp b/src/engine/graphics/renderer/mesh.hpp index 6fa50a8a..d2603424 100644 --- a/src/engine/graphics/renderer/mesh.hpp +++ b/src/engine/graphics/renderer/mesh.hpp @@ -1,12 +1,46 @@ #pragma once +#include #include "../../utils/meshloader.hpp" +#include namespace graphics { - class Mesh - { - public: - Mesh(const utils::MeshData& _meshData) {} - }; + struct VertexData { + glm::vec3 positionData; + glm::vec2 uvData; + glm::vec3 normalData; + }; + + class Mesh { + public: + Mesh(const utils::MeshData::Handle &_meshData); + + static constexpr std::array VERTEX_ATTRIBUTES = { + graphics::VertexAttribute{ + graphics::PrimitiveFormat::FLOAT, + 3, + false, + false + }, + graphics::VertexAttribute{ + graphics::PrimitiveFormat::FLOAT, + 2, + false, + false + }, + graphics::VertexAttribute{ + graphics::PrimitiveFormat::FLOAT, + 3, + false, + false + } + }; + + const std::vector &getGeometryBuffers() { return buffers; } + + private: + std::vector buffers = {}; + + }; } diff --git a/src/engine/graphics/renderer/meshrenderer.cpp b/src/engine/graphics/renderer/meshrenderer.cpp index b740e7a5..3a5d2179 100644 --- a/src/engine/graphics/renderer/meshrenderer.cpp +++ b/src/engine/graphics/renderer/meshrenderer.cpp @@ -1,8 +1,36 @@ #include "meshrenderer.hpp" -#include "../core/opengl.hpp" #include "../core/texture.hpp" namespace graphics { + void MeshRenderer::draw(const Mesh &_mesh, const Texture2D &_texture, const Texture2D &phongData, const glm::mat4 &_transform) { + auto *renderData = new MeshRenderData{ + _mesh, + _texture, + phongData, + _transform + }; + meshBuffer.push_back(renderData); + } + + void MeshRenderer::present(const unsigned int &programID) { + if (currentProgramID != programID){ + currentProgramID = programID; + glsl_object_to_world_matrix = glGetUniformLocation(programID, "object_to_world_matrix"); + } + + for (const auto &meshRenderData: meshBuffer){ + meshRenderData->textureData.bind(0); + meshRenderData->phongData.bind(1); + glUniformMatrix4fv(glsl_object_to_world_matrix, 1, false, glm::value_ptr(meshRenderData->transform)); + for (const auto &geometryBuffer : meshRenderData->meshData.getGeometryBuffers()){ + geometryBuffer->draw(); + } + } + } + + void MeshRenderer::clear() { + meshBuffer.clear(); + } } diff --git a/src/engine/graphics/renderer/meshrenderer.hpp b/src/engine/graphics/renderer/meshrenderer.hpp index 520b886a..d3583cb0 100644 --- a/src/engine/graphics/renderer/meshrenderer.hpp +++ b/src/engine/graphics/renderer/meshrenderer.hpp @@ -3,22 +3,37 @@ #include "../core/shader.hpp" #include "../camera.hpp" #include "glm/glm.hpp" +#include "mesh.hpp" #include +#include +#include namespace graphics { - class Mesh; - class Texture2D; + class Texture2D; - class MeshRenderer - { - public: - MeshRenderer() {} + class MeshRenderer { + public: + MeshRenderer() = default; - void draw(const Mesh& _mesh, const Texture2D& _texture, const glm::mat4& _transform) {} + void draw(const Mesh &_mesh, const Texture2D &_texture, const Texture2D &phongData, const glm::mat4 &_transform); - void present(const Camera& _camera) {} - void clear() {} - private: - }; + void present(const unsigned int &programID); + + void clear(); + + private: + unsigned int currentProgramID = -1; + GLint glsl_object_to_world_matrix = 0; + + struct MeshRenderData{ + Mesh meshData; + const Texture2D &textureData; + const Texture2D &phongData; + const glm::mat4 &transform; + }; + + std::vector meshBuffer = {}; + + }; } \ No newline at end of file diff --git a/src/engine/utils/meshstripper.cpp b/src/engine/utils/meshstripper.cpp new file mode 100644 index 00000000..b2c32498 --- /dev/null +++ b/src/engine/utils/meshstripper.cpp @@ -0,0 +1,202 @@ +#include +#include "meshstripper.hpp" + +using VertexIndices = utils::MeshData::FaceData::VertexIndices; +namespace utils { + TriangleStrip *TriangleStrip::create(std::array vertices) { + auto *strip = new TriangleStrip; + for (const auto &vertexIndex: vertices) { + strip->vertexIndices.push_back(vertexIndex); + } + return strip; + } + + TriangleStrip * + TriangleStrip::create(std::array vertices) { + auto *strip = new TriangleStrip; + for (const auto &vertexIndex: vertices) { + strip->vertexIndices.push_back(vertexIndex); + } + return strip; + } + + TriangleStrip::~TriangleStrip() { + vertexIndices.clear(); + } + + bool findConnectedTriangle(utils::MeshData::FaceData *firstFace, std::vector *remainingFaces, + std::array &result) { + int connectedVertexIndex; + for (auto iterator = remainingFaces->begin(); iterator != remainingFaces->end(); ++iterator) { + connectedVertexIndex = 1; // reset index + for (const auto &vertexB: (*iterator).indices) { + bool didMatch = false; + for (const auto &vertexA: firstFace->indices) { + if (vertexA.positionIdx == vertexB.positionIdx) { + didMatch = true; + result[connectedVertexIndex] = vertexA; + connectedVertexIndex++; + } + } + if (!didMatch) { + result[3] = vertexB; + } + } + + if (connectedVertexIndex > 2) { + // find unique vertex of first face + for (const auto &vertexA: firstFace->indices) { + if (vertexA.positionIdx != result[1].positionIdx && vertexA.positionIdx != result[2].positionIdx) { + result[0] = vertexA; + break; + } + } + + remainingFaces->erase(iterator); + return true; + } + } + return false; + } + + bool findConnectedTriangle(std::array stripOfTwo, std::vector *remainingFaces, + std::array &result) { + for (auto iterator = remainingFaces->begin(); iterator != remainingFaces->end(); ++iterator) { + int outerMatchIndex = -1; // marks which end of the strip the triangle connects to + int innerMatchIndex = -1; // marks which inner vertex the triangle connects to + VertexIndices uniqueVertex; // the one vertex of the triangle that doesn't connect to the strip + for (const auto &vertexB: (*iterator).indices) { + int vertexIndex = vertexB.positionIdx; + if (vertexIndex == stripOfTwo[0].positionIdx) { + outerMatchIndex = 0; + } else if (vertexIndex == stripOfTwo[3].positionIdx) { + outerMatchIndex = 3; + } else if (vertexIndex == stripOfTwo[1].positionIdx) { + innerMatchIndex = 1; + } else if (vertexIndex == stripOfTwo[2].positionIdx) { + innerMatchIndex = 2; + } else { + uniqueVertex = vertexB; + } + } + + // only consider triangles that have an outer and inner match + if (outerMatchIndex < 0 || innerMatchIndex < 0) { + continue; + } + + // make sure the inner and outer matching vertices are directly connected + if ((outerMatchIndex == 0 && innerMatchIndex == 2) + || (outerMatchIndex == 3 && innerMatchIndex == 1)) { + VertexIndices temp = stripOfTwo[2]; + stripOfTwo[2] = stripOfTwo[1]; + stripOfTwo[1] = temp; + } + + if (outerMatchIndex == 0) { + result[0] = uniqueVertex; + std::copy(stripOfTwo.begin(), stripOfTwo.begin() + 4, result.begin() + 1); + } else if (outerMatchIndex == 3) { + std::copy(stripOfTwo.begin(), stripOfTwo.begin() + 4, result.begin()); + result[4] = uniqueVertex; + } + remainingFaces->erase(iterator); + return true; + } + return false; + } + + bool areFacesConnected(VertexIndices vertex1, VertexIndices vertex2, utils::MeshData::FaceData face, VertexIndices &foundVertex) { + int matches = 0; + for (const auto &index: face.indices) { + if (index.positionIdx == vertex1.positionIdx || index.positionIdx == vertex2.positionIdx) { + matches++; + } else { + foundVertex = index; + } + } + return matches >= 2; + } + + void expandTriangleStrip(TriangleStrip *stripToExpand, std::vector *remainingFaces) { + VertexIndices vertA1 = stripToExpand->vertexIndices[0]; + VertexIndices vertA2 = stripToExpand->vertexIndices[1]; + const auto vertIteratorEnd = stripToExpand->vertexIndices.end(); + VertexIndices vertB1 = *(vertIteratorEnd - 1); + VertexIndices vertB2 = *(vertIteratorEnd - 2); + + int count = 0; + while (!remainingFaces->empty()) { + int foundConnectedFace = false; + for (auto iterator = remainingFaces->begin(); iterator != remainingFaces->end(); ++iterator) { + VertexIndices foundVertex; + if (areFacesConnected(vertA1, vertA2, *iterator, foundVertex)) { + vertA2 = vertA1; + vertA1 = foundVertex; + stripToExpand->vertexIndices.insert(stripToExpand->vertexIndices.begin(), foundVertex); + remainingFaces->erase(iterator); + foundConnectedFace = true; + count++; + break; + } + if (areFacesConnected(vertB1, vertB2, *iterator, foundVertex)) { + vertB2 = vertB1; + vertB1 = foundVertex; + stripToExpand->vertexIndices.push_back(foundVertex); + remainingFaces->erase(iterator); + foundConnectedFace = true; + count++; + break; + } + } + + if (!foundConnectedFace) { + break; + } + } + } + + MeshStripData::Handle MeshStripData::createFromMeshData(const utils::MeshData *data) { + auto *result = new MeshStripData; + + std::vector remainingFaces = data->faces; + while (!remainingFaces.empty()) { + utils::MeshData::FaceData face1 = *remainingFaces.begin(); + remainingFaces.erase(remainingFaces.begin()); + + std::array twoTriangleStrip = {}; + bool foundFace2 = findConnectedTriangle( + &face1, + &remainingFaces, + twoTriangleStrip + ); + if (!foundFace2) { + result->floatingTriangles.push_back(face1); + continue; + } + + std::array threeTriangleStrip = {}; + bool foundFace3 = findConnectedTriangle( + twoTriangleStrip, + &remainingFaces, + threeTriangleStrip + ); + + if (!foundFace3) { + result->triangleStrips.push_back(*TriangleStrip::create(twoTriangleStrip)); + continue; + } + + TriangleStrip *strip = TriangleStrip::create(threeTriangleStrip); + expandTriangleStrip(strip, &remainingFaces); + result->triangleStrips.push_back(*strip); + } + + return result; + } + + MeshStripData::~MeshStripData() { + triangleStrips.clear(); + floatingTriangles.clear(); + } +} \ No newline at end of file diff --git a/src/engine/utils/meshstripper.hpp b/src/engine/utils/meshstripper.hpp new file mode 100644 index 00000000..c729aa9f --- /dev/null +++ b/src/engine/utils/meshstripper.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include + +namespace utils { + + /** + * @brief describes a single triangle strip of a mesh + */ + struct TriangleStrip { + std::vector vertexIndices = {}; + + virtual ~TriangleStrip(); + + static TriangleStrip* create(std::array vertices); + + static TriangleStrip* + create(std::array vertices); + }; + + /** + * @brief contains the MeshData split into triangle strips + */ + struct MeshStripData { + std::vector floatingTriangles = {}; + std::vector triangleStrips = {}; + + virtual ~MeshStripData(); + + using Handle = const MeshStripData *; + + static Handle createFromMeshData(const utils::MeshData *data); + }; +} diff --git a/src/main.cpp b/src/main.cpp index 4b53e1f5..52f35a20 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,49 +1,202 @@ #include #include -#include #include #include -#include #include #include +#include +#include +#include #include // CRT's memory leak detection -#ifndef NDEBUG +#ifndef NDEBUG #if defined(_MSC_VER) #define _CRTDBG_MAP_ALLOC + #include +#include + #endif #endif using namespace std::chrono_literals; -int main(int argc, char *argv[]) -{ -#ifndef NDEBUG +int main(int argc, char *argv[]) { +#ifndef NDEBUG #if defined(_MSC_VER) - _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); - // _CrtSetBreakAlloc(2760); + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); + // _CrtSetBreakAlloc(2760); #endif #endif - graphics::Device::initialize(1366, 768,false); - GLFWwindow* window = graphics::Device::getWindow(); - input::InputManager::initialize(window); + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE); + + graphics::Device::initialize(1366, 1366, false); + GLFWwindow *window = graphics::Device::getWindow(); + input::InputManager::initialize(window); + input::InputManager::setCursorMode(input::InputManager::CursorMode::NORMAL); + + glm::vec3 ambientLightData = {0.7f, 0.7f, 0.7f}; + /*graphics::LightData lightData = graphics::LightData::directional( + glm::normalize(glm::vec3(-1.0f, -1.0f, 0.5f)), + glm::vec3(1.0f, 1.0f, 1.0f), + 2.0f);*/ + graphics::LightData lightData = graphics::LightData::point( + glm::vec3(0.0f, 0.0f, 0.0f), + 10.0f, + glm::vec3(1.0f, 1.0f, 1.0f), + 5.0f + ); + /*graphics::LightData lightData = graphics::LightData::spot( + glm::vec3(3.0f, 0.0f, 0.0f), + glm::vec3(-1.0f, 0.0f, 0.0f), + 10.0f, + 10.0f, + glm::vec3(1.0f, 1.0f, 1.0f), + 5.0f + );*/ + + graphics::Camera camera(90.0f, 0.1f, 300.0f); + camera.setPosition(glm::vec3(0.0f, 0.0f, -3.0f)); + static graphics::Sampler sampler(graphics::Sampler::Filter::LINEAR, graphics::Sampler::Filter::LINEAR, + graphics::Sampler::Filter::LINEAR, graphics::Sampler::Border::MIRROR); + auto planetTexture = graphics::Texture2DManager::get("textures/planet1.png", sampler); + auto crateTexture = graphics::Texture2DManager::get("textures/cratetex.png", sampler); + auto planetPhong = graphics::Texture2DManager::get("textures/Planet1_phong.png", sampler); + utils::MeshData::Handle meshData = utils::MeshLoader::get("models/sphere.obj"); + + auto mesh = graphics::Mesh(meshData); + auto meshRenderer = graphics::MeshRenderer(); + + graphics::Shader::Handle fragmentShader = graphics::ShaderManager::get("shader/demo.frag", graphics::ShaderType::FRAGMENT); + graphics::Shader::Handle vertexShader = graphics::ShaderManager::get("shader/demo.vert", graphics::ShaderType::VERTEX); + + graphics::Program program; + program.attach(vertexShader); + program.attach(fragmentShader); + program.link(); + program.use(); + + lightData.bindData(program.getID()); + + const float cameraStep = 0.05f; + const float cameraPitchSensitivity = 0.5f; + const float cameraYawSensitivity = 0.5f; + const float cameraRollStep = 0.05f; + float cameraPitch = 0.0f; + float cameraYaw = 0.0f; + float cameraRoll = 0.0f; + GLint worldToCameraMatrixID = glGetUniformLocation(program.getID(), "world_to_camera_matrix"); + GLint cameraPositionShaderID = glGetUniformLocation(program.getID(), "camera_position"); + GLint glsl_ambient_light = glGetUniformLocation(program.getID(), "ambient_light"); + + graphics::glCall(glEnable, GL_DEPTH_TEST); + graphics::glCall(glClearColor, 0.05f, 0.0f, 0.05f, 1.f); + + glm::vec2 prevCursorPos; + glm::vec2 currentCursorPos = input::InputManager::getCursorPos(); + + while (!glfwWindowShouldClose(window) && !input::InputManager::isKeyPressed(input::Key::ESCAPE)) { + prevCursorPos = currentCursorPos; + currentCursorPos = input::InputManager::getCursorPos(); + graphics::glCall(glClear, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + if (input::InputManager::isKeyPressed(input::Key::Num1)) { + meshRenderer.clear(); + meshRenderer.draw(mesh, *crateTexture, *planetPhong, glm::translate(glm::identity(), glm::vec3(1.5f, 0.0f, 0.0f))); + meshRenderer.draw(mesh, *planetTexture, *planetPhong, glm::translate(glm::identity(), glm::vec3(-1.5f, 0.0f, 0.0f))); + } + if (input::InputManager::isKeyPressed(input::Key::Num2)) { + meshRenderer.clear(); + meshRenderer.draw(mesh, *planetTexture, *planetPhong, glm::identity()); + } + + float rightInput = 0.0f; + float forwardInput = 0.0f; + float upInput = 0.0f; + if (input::InputManager::isKeyPressed(input::Key::W)) { + forwardInput += cameraStep; + } + if (input::InputManager::isKeyPressed(input::Key::S)) { + forwardInput -= cameraStep; + } + if (input::InputManager::isKeyPressed(input::Key::D)) { + rightInput += cameraStep; + } + if (input::InputManager::isKeyPressed(input::Key::A)) { + rightInput -= cameraStep; + } + if (input::InputManager::isKeyPressed(input::Key::E)) { + upInput += cameraStep; + } + if (input::InputManager::isKeyPressed(input::Key::Q)) { + upInput -= cameraStep; + } + camera.moveRelative(rightInput, upInput, forwardInput); + + float cursorDeltaX = currentCursorPos.x - prevCursorPos.x; + float cursorDeltaY = currentCursorPos.y - prevCursorPos.y; + if (cursorDeltaX != 0.0f) { + cameraYaw += cursorDeltaX * cameraYawSensitivity; + if (cameraYaw > 180.0f) { + cameraYaw -= 360.0f; + } else if (cameraYaw < -180.0f) { + cameraYaw += 360.0f; + } + } + if (cursorDeltaY != 0.0f) { + cameraPitch += cursorDeltaY * cameraPitchSensitivity; + if (cameraPitch > 90.0f) { + cameraPitch = 90.0f; + } else if (cameraPitch < -90.0f) { + cameraPitch = -90.0f; + } + } + camera.setRotation(cameraYaw, cameraPitch, cameraRoll); - graphics::glCall(glClearColor,0.f, 1.f, 0.f, 1.f); + bool lightChanged = false; + if (input::InputManager::isKeyPressed(input::Key::J)){ + lightData.light_position += glm::vec3(-0.05f, 0.0f, 0.0f); + lightChanged = true; + } + if (input::InputManager::isKeyPressed(input::Key::L)){ + lightData.light_position += glm::vec3(0.05f, 0.0f, 0.0f); + lightChanged = true; + } + if (input::InputManager::isKeyPressed(input::Key::I)){ + lightData.light_position += glm::vec3(0.0f, 0.05f, 0.0f); + lightChanged = true; + } + if (input::InputManager::isKeyPressed(input::Key::K)){ + lightData.light_position += glm::vec3(0.0f, -0.05f, 0.0f); + lightChanged = true; + } + if(input::InputManager::isKeyPressed(input::Key::U)){ + lightData.light_spot_angle -= 0.1f; + lightChanged = true; + } + if(input::InputManager::isKeyPressed(input::Key::O)){ + lightData.light_spot_angle += 0.1f; + lightChanged = true; + } + if(lightChanged){ + lightData.bindData(program.getID()); + } - while (!glfwWindowShouldClose(window) && !input::InputManager::isKeyPressed(input::Key::ESCAPE)) - { - graphics::glCall(glClear, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glUniform3fv(glsl_ambient_light, 1, glm::value_ptr(ambientLightData)); + glUniformMatrix4fv(worldToCameraMatrixID, 1, GL_FALSE, glm::value_ptr(camera.getWorldToCamera())); + glUniform3fv(cameraPositionShaderID, 1, glm::value_ptr(camera.getPosition())); + meshRenderer.present(program.getID()); - glfwPollEvents(); - glfwSwapBuffers(window); - std::this_thread::sleep_for(16ms); - } + glfwPollEvents(); + glfwSwapBuffers(window); + graphics::glCall(glClear, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + std::this_thread::sleep_for(16ms); + } - utils::MeshLoader::clear(); - graphics::Device::close(); - return EXIT_SUCCESS; -} + utils::MeshLoader::clear(); + graphics::Device::close(); + return EXIT_SUCCESS; +} \ No newline at end of file