diff --git a/assets/shaders/circle_renderer.frag b/assets/shaders/circle_renderer.frag new file mode 100644 index 0000000..baf7b86 --- /dev/null +++ b/assets/shaders/circle_renderer.frag @@ -0,0 +1,33 @@ +#if __VERSION__ >= 330 +in vec3 localPos; +out vec4 FragColor; +#else +varying vec3 localPos; +#endif + +uniform vec4 color; + +float circleAlpha(vec3 lPos) +{ + const float RADIUS = 1.0; + const float MULTIPLIER = 2.1; // Quad is -0.5 to 0.5, scale to radius 1.0 + + vec3 samplePos = lPos * MULTIPLIER; + + float dist = length(samplePos); + float edgeWidth = fwidth(dist); + float alpha = 1.0 - smoothstep(RADIUS - edgeWidth, RADIUS + edgeWidth, dist); + + return alpha; +} + +void main() +{ + float alpha = circleAlpha(localPos); + +#if __VERSION__ >= 330 + FragColor = vec4(color.rgb, color.a * alpha); +#else + gl_FragColor = vec4(color.rgb, color.a * alpha); +#endif +} diff --git a/assets/shaders/circle_renderer.vert b/assets/shaders/circle_renderer.vert new file mode 100644 index 0000000..950331f --- /dev/null +++ b/assets/shaders/circle_renderer.vert @@ -0,0 +1,27 @@ +#if __VERSION__ >= 330 +layout (location = 0) in vec3 aPos; +out vec3 localPos; +#else +attribute vec3 aPos; +varying vec3 localPos; +#endif + +uniform mat4 model; +#if GAMECOE_HAS_UBO +layout(std140) uniform CameraMatrices +{ + mat4 projection; + mat4 view; + vec3 cameraPosition; +}; +#else +uniform mat4 view; +uniform mat4 projection; +uniform vec3 cameraPosition; +#endif + +void main() +{ + localPos = aPos; + gl_Position = projection * view * model * vec4(aPos, 1.0); +} diff --git a/assets/shaders/shape_renderer.frag b/assets/shaders/shape_renderer.frag index fbf3db5..99e6560 100644 --- a/assets/shaders/shape_renderer.frag +++ b/assets/shaders/shape_renderer.frag @@ -1,9 +1,9 @@ -uniform vec4 color; - #if __VERSION__ >= 330 out vec4 FragColor; #endif +uniform vec4 color; + void main() { #if __VERSION__ >= 330 diff --git a/assets/shaders/shape_renderer.vert b/assets/shaders/shape_renderer.vert index bbcf78f..23e6418 100644 --- a/assets/shaders/shape_renderer.vert +++ b/assets/shaders/shape_renderer.vert @@ -3,7 +3,6 @@ layout (location = 0) in vec3 aPos; #else attribute vec3 aPos; #endif -// texture, etc... in the future uniform mat4 model; #if GAMECOE_HAS_UBO @@ -11,10 +10,12 @@ layout(std140) uniform CameraMatrices { mat4 projection; mat4 view; + vec3 cameraPosition; }; #else uniform mat4 view; uniform mat4 projection; +uniform vec3 cameraPosition; #endif void main() diff --git a/assets/shaders/sphere_renderer.frag b/assets/shaders/sphere_renderer.frag new file mode 100644 index 0000000..05bd522 --- /dev/null +++ b/assets/shaders/sphere_renderer.frag @@ -0,0 +1,73 @@ +#if __VERSION__ >= 330 +in vec3 worldPos; +out vec4 FragColor; +#else +varying vec3 worldPos; +#endif + +uniform mat4 model; +uniform vec4 color; +#if GAMECOE_HAS_UBO +layout(std140) uniform CameraMatrices +{ + mat4 projection; + mat4 view; + vec3 cameraPosition; +}; +#else +uniform mat4 view; +uniform mat4 projection; +uniform vec3 cameraPosition; +#endif +uniform vec3 sphereCenter; +uniform vec3 sphereScale; +uniform mat3 sphereRotation; + +vec3 sphereIntersection(vec3 rayOrigin, vec3 wPos) +{ + const float RADIUS = 1.0; + const float BOX_RADIUS = 0.5; + + mat3 sphereRotationT = transpose(sphereRotation); + + vec3 effectiveScale = sphereScale * BOX_RADIUS; + vec3 rayDirection = vec3(sphereRotationT * normalize(wPos - rayOrigin)) / effectiveScale; + vec3 sphereCenterToRayOrigin = (sphereRotationT * vec3(rayOrigin - sphereCenter)) / effectiveScale; + + float a = dot(rayDirection, rayDirection); + float b = 2.0 * dot(sphereCenterToRayOrigin, rayDirection); + float c = dot(sphereCenterToRayOrigin, sphereCenterToRayOrigin) - (RADIUS * RADIUS); + + float discriminant = b * b - 4.0 * a * c; + + if (discriminant < 0.0) + { + return vec3(0.0); + } + + float t1 = (-b + sqrt(discriminant)) / (2.0 * a); + float t2 = (-b - sqrt(discriminant)) / (2.0 * a); + float t = t2 > 0.0 ? t2 : t1; + vec3 intersection = rayOrigin + (sphereRotation * (vec3(t * rayDirection) * effectiveScale)); + + return intersection; +} + +void main() +{ + vec3 spherePoint = sphereIntersection(cameraPosition, worldPos); + if (spherePoint == vec3(0.0)) + { + discard; + } + + vec4 shadingPos = projection * view * model * vec4(spherePoint, 1.0); + shadingPos /= shadingPos.w; + gl_FragDepth = shadingPos.z; // need version gating? + +#if __VERSION__ >= 330 + FragColor = color; +#else + gl_FragColor = color; +#endif +} \ No newline at end of file diff --git a/assets/shaders/sphere_renderer.vert b/assets/shaders/sphere_renderer.vert new file mode 100644 index 0000000..6f5da86 --- /dev/null +++ b/assets/shaders/sphere_renderer.vert @@ -0,0 +1,28 @@ +#if __VERSION__ >= 330 +layout (location = 0) in vec3 aPos; +out vec3 worldPos; +#else +attribute vec3 aPos; +varying vec3 worldPos; +#endif + +uniform mat4 model; +#if GAMECOE_HAS_UBO +layout(std140) uniform CameraMatrices +{ + mat4 projection; + mat4 view; + vec3 cameraPosition; +}; +#else +uniform mat4 view; +uniform mat4 projection; +uniform vec3 cameraPosition; +#endif + +void main() +{ + vec4 wPos = model * vec4(aPos, 1.0); + worldPos = vec3(wPos); + gl_Position = projection * view * wPos; +} diff --git a/include/gamecoe/entity/renderer/shape_renderer.hpp b/include/gamecoe/entity/renderer/shape_renderer.hpp index f5740bc..4d303f0 100644 --- a/include/gamecoe/entity/renderer/shape_renderer.hpp +++ b/include/gamecoe/entity/renderer/shape_renderer.hpp @@ -16,21 +16,24 @@ namespace gamecoe Triangle, Rectangle, Box, + Circle, + Sphere, // TODO: Support more primitive shapes }; class ShapeRenderer : public Renderer { static std::atomic s_counter; - static std::optional s_shader; -#if GAMECOE_HAS_UBO - static std::uint32_t s_cameraUniformBlockIndex; -#endif + static std::optional s_shapeShader; + static std::optional s_circleShader; + static std::optional s_sphereShader; Shape m_shape; Color m_color; const VertexArray &m_vertexArray; + std::optional &shapeToShader(gamecoe::Shape shape) const; + ShapeRenderer(GameObject &owner, Shape shape, const Color &color, std::int8_t layer = 0); public: @@ -55,6 +58,8 @@ namespace gamecoe static std::unique_ptr triangle(GameObject &owner, const Color &color, std::int8_t layer = 0); static std::unique_ptr rectangle(GameObject &owner, const Color &color, std::int8_t layer = 0); static std::unique_ptr box(GameObject &owner, const Color &color, std::int8_t layer = 0); + static std::unique_ptr circle(GameObject &owner, const Color &color, std::int8_t layer = 0); + static std::unique_ptr sphere(GameObject &owner, const Color &color, std::int8_t layer = 0); }; } // namespace gamecoe \ No newline at end of file diff --git a/src/gamecoe/core/game.cpp b/src/gamecoe/core/game.cpp index 8b0da9b..74fb624 100644 --- a/src/gamecoe/core/game.cpp +++ b/src/gamecoe/core/game.cpp @@ -59,6 +59,11 @@ namespace gamecoe #if GAMECOE_USE_OPENGL if(!gladLoadGL(glfwGetProcAddress)) detail::throwError("Game::Game(): Failed to initialize glad"); + + glEnable(GL_DEPTH_TEST); + glEnable(GL_MULTISAMPLE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); #endif m_internalScene.emplace(*this, "gamecoe::Internal"); diff --git a/src/gamecoe/core/window.cpp b/src/gamecoe/core/window.cpp index a04186f..8ca3b2e 100644 --- a/src/gamecoe/core/window.cpp +++ b/src/gamecoe/core/window.cpp @@ -38,6 +38,7 @@ namespace gamecoe glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); #endif #endif + glfwWindowHint(GLFW_SAMPLES, 4); // which opengl version? work only on opengl? GLFWwindow *current = glfwGetCurrentContext(); GLFWwindow *window = glfwCreateWindow(m_width, m_height, m_title.c_str(), nullptr, current); diff --git a/src/gamecoe/entity/camera.cpp b/src/gamecoe/entity/camera.cpp index 0b3ab06..de103e8 100644 --- a/src/gamecoe/entity/camera.cpp +++ b/src/gamecoe/entity/camera.cpp @@ -37,9 +37,11 @@ namespace gamecoe { glm::mat4 m_projection; glm::mat4 m_view; + glm::vec3 m_cameraPosition; } data; data.m_projection = projectionMatrix(); data.m_view = viewMatrix(); + data.m_cameraPosition = m_owner.transform().position(); m_uniformBuffer->uploadData(&data, sizeof(data)); #endif diff --git a/src/gamecoe/entity/renderer/shape_renderer.cpp b/src/gamecoe/entity/renderer/shape_renderer.cpp index faaa22f..e80c906 100644 --- a/src/gamecoe/entity/renderer/shape_renderer.cpp +++ b/src/gamecoe/entity/renderer/shape_renderer.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #if GAMECOE_USE_OPENGL @@ -22,13 +23,28 @@ namespace case gamecoe::Shape::Triangle: return gamecoe::VertexArray::triangle(); case gamecoe::Shape::Rectangle: + case gamecoe::Shape::Circle: return gamecoe::VertexArray::rectangle(); case gamecoe::Shape::Box: + case gamecoe::Shape::Sphere: + default: return gamecoe::VertexArray::box(); - + } + } + + std::pair shapeToShaderPaths(gamecoe::Shape shape) + { + switch (shape) + { + case gamecoe::Shape::Circle: + return { "gamecoe/shaders/circle_renderer.vert", "gamecoe/shaders/circle_renderer.frag" }; + case gamecoe::Shape::Sphere: + return { "gamecoe/shaders/sphere_renderer.vert", "gamecoe/shaders/sphere_renderer.frag" }; + case gamecoe::Shape::Triangle: + case gamecoe::Shape::Rectangle: + case gamecoe::Shape::Box: default: - assert(false && "Need to add support for a new Shape"); - break; + return { "gamecoe/shaders/shape_renderer.vert", "gamecoe/shaders/shape_renderer.frag" }; } } } @@ -36,10 +52,25 @@ namespace namespace gamecoe { std::atomic ShapeRenderer::s_counter = 0; - std::optional ShapeRenderer::s_shader = std::nullopt; -#if GAMECOE_HAS_UBO - std::uint32_t ShapeRenderer::s_cameraUniformBlockIndex = 0; -#endif + std::optional ShapeRenderer::s_shapeShader = std::nullopt; + std::optional ShapeRenderer::s_circleShader = std::nullopt; + std::optional ShapeRenderer::s_sphereShader = std::nullopt; + + std::optional &ShapeRenderer::shapeToShader(gamecoe::Shape shape) const + { + switch (shape) + { + case Shape::Circle: + return s_circleShader; + case Shape::Sphere: + return s_sphereShader; + case Shape::Triangle: + case Shape::Rectangle: + case Shape::Box: + default: + return s_shapeShader; + } + } ShapeRenderer::ShapeRenderer(GameObject &owner, Shape shape, const Color &color, std::int8_t layer) : Renderer(owner, layer), @@ -55,7 +86,9 @@ namespace gamecoe --s_counter; if (s_counter == 0) { - s_shader.reset(); + s_shapeShader.reset(); + s_circleShader.reset(); + s_sphereShader.reset(); VertexArray::destroyShapeVAs(); } } @@ -64,27 +97,34 @@ namespace gamecoe { if (!m_active) return; - if (!s_shader) + std::optional &shader = shapeToShader(m_shape); + if (!shader) { - s_shader.emplace( - resolvePath("gamecoe/shaders/shape_renderer.vert"), - resolvePath("gamecoe/shaders/shape_renderer.frag") - ); + auto shaderPaths = shapeToShaderPaths(m_shape); + shader.emplace(resolvePath(shaderPaths.first), resolvePath(shaderPaths.second)); #if GAMECOE_HAS_UBO - s_cameraUniformBlockIndex = glGetUniformBlockIndex(s_shader->id(), "CameraMatrices"); - glUniformBlockBinding(s_shader->id(), s_cameraUniformBlockIndex, constcoe::CAMERA_UBO_BINDING_POINT); + std::uint32_t cameraUniformBlockIndex = glGetUniformBlockIndex(shader->id(), "CameraMatrices"); + glUniformBlockBinding(shader->id(), cameraUniformBlockIndex, constcoe::CAMERA_UBO_BINDING_POINT); #endif } - s_shader->use(); + shader->use(); #if !GAMECOE_HAS_UBO auto &camera = owner().game().mainCamera(); - s_shader->set("view", camera.viewMatrix()); - s_shader->set("projection", camera.projectionMatrix()); + shader->set("view", camera.viewMatrix()); + shader->set("projection", camera.projectionMatrix()); + shader->set("cameraPosition", camera.owner().transform().position()); #endif - s_shader->set("model", owner().transform().modelMatrix()); - s_shader->set("color", m_color.normalized()); + auto &transform = owner().transform(); + shader->set("model", transform.modelMatrix()); + shader->set("color", m_color.normalized()); + if(m_shape == Shape::Sphere) + { + shader->set("sphereCenter", transform.position()); + shader->set("sphereScale", transform.scale()); + shader->set("sphereRotation", glm::mat3(transform.rotationMatrix())); // how should I handle it mat3-mat4? maybe change to mat4 in the frag shader? + } m_vertexArray.bind(); @@ -117,4 +157,14 @@ namespace gamecoe { return std::unique_ptr(new ShapeRenderer(owner, Shape::Box, color, layer)); } + + std::unique_ptr ShapeRenderer::circle(GameObject &owner, const Color &color, std::int8_t layer) + { + return std::unique_ptr(new ShapeRenderer(owner, Shape::Circle, color, layer)); + } + + std::unique_ptr ShapeRenderer::sphere(GameObject &owner, const Color &color, std::int8_t layer) + { + return std::unique_ptr(new ShapeRenderer(owner, Shape::Sphere, color, layer)); + } } // namespace gamecoe