#include "Game.h" #include "LayeredFramebuffer.h" #include "Texture3D.h" #include "math/Frustum.h" #include "rendering/FileTexture.h" #include "rendering/Framebuffer.h" #include "rendering/Shader.h" #include "rendering/VertexBuffer.h" #include "rendering/Window.h" #include "utils/Array.h" #include "utils/List.h" #include "utils/Random.h" #include "utils/Utils.h" #include "wrapper/GL.h" static constexpr float STEP = 1.0f / 31.5f; static Window window; static Shader cubeShader; static Shader noiseShader; static Shader particleShader; static Shader backgroundShader; static Shader blurShader; static Framebuffer<1> shadowBuffer; static Framebuffer<1> blurShadowBuffer; static LayeredFramebuffer noiceBuffer; static FileTexture bricks; static FileTexture bricksBump; static FileTexture bricksNormal; static VertexBuffer rectangleBuffer; static VertexBuffer emptyBuffer; static Frustum frustum{60.0f, 0.1f, 1000.0f, window.getSize()}; static Matrix projection; static Matrix view; static Matrix shadowView; static Matrix shadowProjectionView; static Matrix model; static Button up{GLFW_KEY_SPACE, "Up"}; static Button down{GLFW_KEY_LEFT_SHIFT, "Down"}; static Button left{GLFW_KEY_A, "left"}; static Button right{GLFW_KEY_D, "right"}; static Button front{GLFW_KEY_W, "front"}; static Button back{GLFW_KEY_S, "back"}; static Button toggle{GLFW_KEY_T, "toggle"}; static Button scaleUp{GLFW_KEY_G, "scale up"}; static Button scaleDown{GLFW_KEY_H, "scale down"}; static Button stepsUp{GLFW_KEY_Y, "steps up"}; static Button stepsDown{GLFW_KEY_U, "steps down"}; static Button fineStepsUp{GLFW_KEY_I, "fine steps up"}; static Button fineStepsDown{GLFW_KEY_O, "fine steps down"}; static Button modeToggle{GLFW_KEY_C, "mode toggle"}; static Button primaryMouse{GLFW_MOUSE_BUTTON_1, "primary click"}; static Button timeUp{GLFW_KEY_N, "time up"}; static Button timeDown{GLFW_KEY_M, "time down"}; static Vector3 oldPosition; static Vector3 position{-32.0f, 0.0f, -120.0f}; static float oldHeight = 0.0f; static float height = 0.0f; static float heightScale = 0.01f; static int steps = 1; static int fineSteps = 1; static bool mode = false; static float timeTicks = 0.0f; static Vector3 emitterPos; static float emitterAge = 999999.0f; static float timeFactor = 1.0f; static void tickTimeFactors() { if(timeUp.isDown()) { timeFactor *= 1.05f; } if(timeDown.isDown()) { timeFactor /= 1.05f; } timeTicks++; } static void tickMovement() { oldHeight = height; oldPosition = position; if(up.isDown()) { height += 1.0f; } if(down.isDown()) { height -= 1.0f; } constexpr float speed = 3.5f; if(left.isDown()) { position += Vector3(speed, 0.0f, 0.0f); } if(right.isDown()) { position -= Vector3(speed, 0.0f, 0.0f); } if(front.isDown()) { position += Vector3(0.0f, 0.0f, speed); } if(back.isDown()) { position -= Vector3(0.0f, 0.0f, speed); } } static void tickParallaxSettings() { if(scaleUp.isDown()) { heightScale += 0.005f; } if(scaleDown.isDown()) { heightScale -= 0.005f; if(heightScale < 0.0f) { heightScale = 0.0f; } } if(stepsUp.wasReleased()) { steps++; } if(stepsDown.wasReleased() && steps > 1) { steps--; } if(fineStepsUp.wasReleased()) { fineSteps++; } if(fineStepsDown.wasReleased() && fineSteps > 1) { fineSteps--; } if(modeToggle.wasReleased()) { mode = !mode; } } static void tickGame() { tickTimeFactors(); tickMovement(); tickParallaxSettings(); } static void prepareMatrices(float lag) { projection = frustum.updateProjection(); view.translateTo(Vector3(0.0f, 0.0f, 0.0f)); view.translate(Utils::interpolate(oldPosition, position, lag)); float h = -32.0f + (oldHeight - height) * lag; view.translateY(h); shadowView.translateTo(Vector3(-32.0f - 180.0f, h, -340.0f)); shadowProjectionView.translateTo(Vector3(0.0f, 0.0f, 0.0f)); shadowProjectionView.scale(0.5f); shadowProjectionView.translate(Vector3(0.5f, 0.5f, 0.5f)); shadowProjectionView *= projection; shadowProjectionView *= shadowView; } static void renderNoise() { GL::setViewport(64, 64); noiseShader.use(); noiceBuffer.bindAndClear(); noiseShader.setFloat("height", oldHeight * STEP); for(int i = 0; i < 64; i++) { noiseShader.setFloat("layer", i * STEP - 1.0f); noiceBuffer.bindLayer(i); rectangleBuffer.draw(6); } } static void renderCubes(Matrix& view) { GL::setViewport(window.getSize().width, window.getSize().height); cubeShader.use(); cubeShader.setMatrix("proj", projection.getValues()); cubeShader.setMatrix("view", view.getValues()); cubeShader.setMatrix("shadow", shadowProjectionView.getValues()); cubeShader.setFloat("height", oldHeight * STEP * 0.5f); cubeShader.setVector("viewPos", -position + Vector3(0.0f, 32.0f, 0.0f)); cubeShader.setVector("lightPos", Vector3()); cubeShader.setFloat("heightScale", heightScale); cubeShader.setInt("steps", steps); cubeShader.setInt("fineSteps", fineSteps); cubeShader.setInt("kajetan", mode); if(toggle.isDown()) { glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); } noiceBuffer.bindTextureTo(0); bricks.bindTo(1); bricksBump.bindTo(2); bricksNormal.bindTo(3); blurShadowBuffer.bindTextureTo(0, 4); shadowBuffer.bindTextureTo(0, 5); for(int i = 0; i < 3; i++) { model.translateTo(Vector3(80.0f * i, 0.0f, 0.0f)); cubeShader.setMatrix("model", model.getValues()); emptyBuffer.drawPoints(64 * 64 * 64); } glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } static void pickParticleCenter() { if(!primaryMouse.wasReleased()) { return; } noiceBuffer.bindTextureTo(0); static float buffer[64][64][64]; glGetTexImage(GL_TEXTURE_3D, 0, GL_RED, GL_FLOAT, buffer); float hWidth = window.getSize().width * 0.5f; float hHeight = window.getSize().height * 0.5f; float x = (window.buttons.getMouseX() - hWidth) / hWidth; float y = -(window.buttons.getMouseY() - hHeight) / hHeight; float aspect = hWidth / hHeight; float tan = tanf((0.5f * frustum.fieldOfView) * M_PI / 180.0f); float q = 1.0f / tan; Vector3 direction(x / (q / aspect), y / q, 1.0f); direction[2] = -direction[2]; direction.normalize(); Vector3 pos = -position; pos[1] = 32.0f; pos[1] = 32.0f; for(int i = 0; i < 150; i++) { int x = pos[0] + 0.5f; int y = pos[1] + 0.5f; int z = pos[2] + 0.5f; if(x >= 0 && x < 64 && y >= 0 && y < 64 && z >= 0 && z < 64 && buffer[x][y][z] > 0.5f) { emitterPos = pos + Vector3(0.0f, height, 0.0f); emitterAge = timeTicks; break; } pos += direction; } } static void renderParticles(float lag) { glPointSize(5.0f); particleShader.use(); particleShader.setMatrix("proj", projection.getValues()); Matrix m; m.translateTo(Utils::interpolate(oldPosition, position, lag)); m.translateY(-32.0f - (oldHeight + (height - oldHeight) * lag)); particleShader.setMatrix("view", m.getValues()); particleShader.setFloat("time", timeTicks + lag); GL::enableBlending(); glDepthMask(false); pickParticleCenter(); particleShader.setFloat("timeFactor", timeFactor); particleShader.setVector("position", emitterPos); particleShader.setFloat("age", emitterAge); particleShader.setVector("color", Vector4(1.0f, 0.0f, 0.0f, 1.0f)); particleShader.setInt("seedBase", 0); emptyBuffer.drawPoints(2000000); particleShader.setVector("position", emitterPos); particleShader.setFloat("age", emitterAge + 5); particleShader.setVector("color", Vector4(0.0f, 1.0f, 0.0f, 1.0f)); particleShader.setInt("seedBase", 1); emptyBuffer.drawPoints(10000); particleShader.setVector("position", emitterPos); particleShader.setFloat("age", emitterAge + 10); particleShader.setVector("color", Vector4(0.0f, 0.0f, 1.0f, 1.0f)); particleShader.setInt("seedBase", 2); emptyBuffer.drawPoints(5000); particleShader.setVector("position", emitterPos); particleShader.setFloat("age", emitterAge + 20); particleShader.setVector("color", Vector4(0.0f, 1.0f, 1.0f, 1.0f)); particleShader.setInt("seedBase", 2); emptyBuffer.drawPoints(5000); glDepthMask(true); GL::disableBlending(); } static void renderBackground(Matrix& view) { backgroundShader.use(); blurShadowBuffer.bindTextureTo(0, 0); backgroundShader.setMatrix("proj", frustum.updateProjection().getValues()); backgroundShader.setMatrix("view", view.getValues()); model.translateTo(Vector3(0.0f, 0.0f, 0.0f)); model.scale(Vector3(260.0f, 120.0f, 1.0f)); model.translate(Vector3(0.0f, 20.0f, -200.0f)); model.rotateX(-5.0f); backgroundShader.setMatrix("model", model.getValues()); backgroundShader.setMatrix("shadow", shadowProjectionView.getValues()); rectangleBuffer.draw(6); } static void renderGame(float lag) { GL::enableDepthTesting(); prepareMatrices(lag); renderNoise(); shadowBuffer.bindAndClear(); renderCubes(shadowView); renderBackground(shadowView); blurShader.use(); shadowBuffer.bindTextureTo(0, 0); blurShadowBuffer.bindAndClear(); rectangleBuffer.draw(6); GL::bindMainFramebuffer(); GL::clear(); renderCubes(view); renderBackground(view); renderParticles(lag); } void Game::init() { WindowOptions options(4, 0, {1024, 620}, false, "test"); Error error = window.open(options); if(error.has()) { error.message.printLine(); return; } error = cubeShader.compile("resources/cubes.vs", "resources/cubes.gs", "resources/cubes.fs"); if(error.has()) { error.message.printLine(); return; } error = noiseShader.compile("resources/noise.vs", nullptr, "resources/noise.fs"); if(error.has()) { error.message.printLine(); return; } error = particleShader.compile("resources/particles.vs", "resources/particles.gs", "resources/particles.fs"); if(error.has()) { error.message.printLine(); return; } error = backgroundShader.compile("resources/background.vs", nullptr, "resources/background.fs"); if(error.has()) { error.message.printLine(); return; } error = blurShader.compile("resources/blur.vs", nullptr, "resources/blur.fs"); if(error.has()) { error.message.printLine(); return; } error = shadowBuffer.init(window.getSize(), TextureFormat::depth32(true)); if(error.has()) { error.message.printLine(); return; } error = blurShadowBuffer.init(window.getSize(), TextureFormat::float32(2, true)); if(error.has()) { error.message.printLine(); return; } error = bricks.load("resources/bricks.png", 0); if(error.has()) { error.message.printLine(); return; } error = bricksBump.load("resources/bricks_bump.png", 0); if(error.has()) { error.message.printLine(); return; } error = bricksNormal.load("resources/bricks_normal.png", 0); if(error.has()) { error.message.printLine(); return; } noiceBuffer.init(64, 64, 64); window.buttons.add(up); window.buttons.add(down); window.buttons.add(left); window.buttons.add(right); window.buttons.add(front); window.buttons.add(back); window.buttons.add(toggle); window.buttons.add(scaleUp); window.buttons.add(scaleDown); window.buttons.add(stepsUp); window.buttons.add(stepsDown); window.buttons.add(fineStepsUp); window.buttons.add(fineStepsDown); window.buttons.add(modeToggle); window.buttons.add(timeUp); window.buttons.add(timeDown); window.buttons.addMouse(primaryMouse); bricks.setLinearFilter(); bricksBump.setLinearFilter(); bricksNormal.setLinearFilter(); rectangleBuffer.init(Attributes().addFloat(2)); float recData[6][2] = {{-1.0f, -1.0f}, {-1.0, 1.0}, {1.0, -1.0}, {1.0f, 1.0f}, {-1.0, 1.0}, {1.0, -1.0}}; rectangleBuffer.setStaticData(sizeof(recData), recData); emptyBuffer.init(Attributes()); struct Intern { void tick() { tickGame(); } void render(float lag) { renderGame(lag); } bool isRunning() const { return true; } }; Intern intern; window.run(intern, 50'000'000); }