diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml new file mode 100644 index 00000000..6efb3bbf --- /dev/null +++ b/.github/workflows/cmake-single-platform.yml @@ -0,0 +1,49 @@ +# This starter workflow is for a CMake project running on a single platform. There is a different starter workflow if you need cross-platform coverage. +# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-multi-platform.yml +name: CMake on a single platform + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies + run: | + sudo add-apt-repository universe + sudo apt-get update + sudo apt-get install -y \ + libglfw3-dev \ + libopenal-dev \ + libalut-dev \ + libassimp-dev \ + freeglut3-dev \ + libglew-dev \ + libglm-dev \ + libgl1-mesa-dev \ + libfreetype6-dev \ + nlohmann-json3-dev + + - name: Clear build directory + run: rm -rf build + + - name: Configure CMake + run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + + - name: Build + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} + + - name: Test + working-directory: ${{github.workspace}}/build + run: ctest -C ${{env.BUILD_TYPE}} \ No newline at end of file diff --git a/App.cpp b/App.cpp index f0668004..311aa2b5 100644 --- a/App.cpp +++ b/App.cpp @@ -1,353 +1,425 @@ +#include +#include #include "App.h" +#include "Renderer/Opengl/BoltRenderer.h" +#include "Renderer/Opengl/Material/StandardMaterial.h" +#include "Renderer/Opengl/Material/Uniform/TextureArrayUniform.h" +#include "Renderer/Opengl/Model/Standard/AnimationArrayMesh.h" #include "Resource/AnimLoader.h" - -#pragma clang diagnostic push -#pragma ide diagnostic ignored "NullDereference" -App::App(Camera* camera, int width, int height) : width(width), height(height), camera(camera) { - rendererManager = new RenderManager(width, height); - keyboardManager = new KeyboardManager(); - startText = new Text("Press start I, K or L..."); - tilesCounterText = new Text(""); - eat = new Eat; - skybox = new Cube(); - eatManager = nullptr; +#include "Resource/ShaderLoader.h" +#include "Resource/TextureLoader.h" + +App::App(const shared_ptr &camera, const int width, const int height) : camera(camera), width(width), height(height) { + resourceManager = make_shared(); + keyboardManager = make_unique(); + + projection = glm::perspective( + glm::radians(camera->getZoom()), + static_cast(width) / static_cast(height), + 0.1f, + 1000.0f + ); + contextState = make_shared(); + + rendererManager = make_shared(contextState, camera, resourceManager, projection, width, height); + rendererManager->setWidth(width); + rendererManager->setHeight(height); + environment = make_shared(); + mainScene = make_unique( + nullptr, + vector >{}, + vector >{}, + rendererManager, camera, projection, resourceManager, width, height + ); + //mainScene->setEnvironment(environment); } App::~App() { - delete rendererManager; - delete resourceManager; - delete keyboardManager; - delete eatManager; - delete collisionDetector; - delete eat; - delete skybox; - delete levelManager; - delete camera; alDeleteSources(1, &musicSource); alDeleteSources(1, &coinSource); alDeleteBuffers(1, &musicBuffer); alDeleteBuffers(1, &coinBuffer); } +void App::initScene() { + mainScene->init(100); + //const glm::mat4 ortho = glm::ortho(0.0f, static_cast(width), static_cast(height), 0.0f, -1.0f, 1000.0f); + + auto basicShader = resourceManager->getShader("basicShader"); + auto shadowDepthShader = resourceManager->getShader("shadowDepthShader"); + const auto planeMaterial = make_shared(StandardMaterial(basicShader, shadowDepthShader)); + const auto coinMaterial = make_shared(StandardMaterial(basicShader, shadowDepthShader)); + const auto boxMaterial = make_shared(StandardMaterial(basicShader, shadowDepthShader)); + + auto gamefieldAlbedo = resourceManager->getTexture("gamefield.bmp"); + auto gamefieldNormal = resourceManager->getTexture("gamefield_normal.jpg"); + auto gamefieldSpecular = resourceManager->getTexture("gamefield_specular.jpg"); + // auto skeletonAlbedo = resourceManager->getTexture("Skeleton_Body.png"); + // auto skeletonORM = resourceManager->getTexture("Skeleton_Body_ORM.png"); + auto shadowMap = resourceManager->getTexture("depth"); + auto coinAlbedo = resourceManager->getTexture("Coin_Gold_albedo.png"); + auto coinNormal = resourceManager->getTexture("Coin_Gold_nm.png"); + auto coinMetalness = resourceManager->getTexture("Coin_Gold_metalness.png"); + auto coinRoughness = resourceManager->getTexture("Coin_Gold_rough.png"); + auto rustedAlbedo = resourceManager->getTexture("rusted_albedo.png"); + auto rustedNormal = resourceManager->getTexture("rusted_normal.png"); + auto rustedRoughness = resourceManager->getTexture("rusted_roughness.png"); + auto aoMap = resourceManager->getTexture("ao.png"); + auto rustedMetallic = resourceManager->getTexture("rusted_metallic.png"); + auto brickWall = resourceManager->getTexture("brickwork-texture.jpg"); + auto brickWallNormal = resourceManager->getTexture("brickwork_normal-map.jpg"); + auto brickWallSpecular = resourceManager->getTexture("brickwork-bump-map.jpg"); + auto environmentMap = resourceManager->getTexture("skybox"); + + // const auto directionalLight = make_shared(); + // directionalLight->setPosition({0.0f, 7.0f, 11.0f}); + // directionalLight->setDirection({1, 1.0, -3}); + // directionalLight->setAmbient({0.7f, 0.7f, 0.7f}); + // directionalLight->setDiffuse({0.1f, 0.1f, 0.1f}); + // directionalLight->setSpecular({.091f, .091f, .091f}); + + // const auto spotLight = make_shared(); + // spotLight->setPosition({0.0f, 0.5f, 0.2f}); + // spotLight->setDirection({0.0f, 0.0f, 0.0f}); + // spotLight->setAmbient({1.0f, 1.0f, 1.0f}); + // spotLight->setDiffuse({0.0f, 0.0f, 0.0f}); + // spotLight->setSpecular({1.0f, 1.0f, 1.0f}); + // spotLight->setCutOff(12.5); + // spotLight->setOuterCutOff(17.5); + // + // const auto pointLight = make_shared(); + // pointLight->setPosition({-0.9f, 1.1f, 0.2f}); + // pointLight->setAmbient(glm::vec3(0.6f)); + // // pointLight->setDiffuse({0.198f, 0.459f, 0.94f}); + // pointLight->setDiffuse(glm::vec3(0.0f)); + // pointLight->setSpecular(glm::vec3(0.0f)); + // pointLight->setConstant(0.0005f); + // pointLight->setLinear(0.8f); + + planeMaterial->setColor({0.88, 0.05, 0.05}); + // planeMaterial->setColor({1, 1, 1}); + //planeMaterial->setAlbedo(rustedAlbedo); + //planeMaterial->setNormal(rustedNormal); + // planeMaterial->setRoughness(rustedRoughness); + // planeMaterial->setMetalness(rustedMetallic); + //planeMaterial->setAoMap(aoMap); + // planeMaterial->setAlbedo(skeletonAlbedo); + // planeMaterial->setMetalness(skeletonORM); + // planeMaterial->setRoughness(skeletonORM); + // planeMaterial->setAoMap(skeletonORM); + boxMaterial->setAlbedo(brickWall); + boxMaterial->setNormal(brickWallNormal); + boxMaterial->setSpecular(brickWallSpecular); + // planeMaterial->setAlbedo(gamefieldAlbedo); + coinMaterial->setAlbedo(coinAlbedo); + coinMaterial->setNormal(coinNormal); + coinMaterial->setSpecular(coinMetalness); + // coinMaterial->setAoMap(aoMap); + // planeMaterial->setAlbedo(brickWall); + // planeMaterial->setNormal(brickWallNormal); + // planeMaterial->setMetalness(coinMetalness); + // planeMaterial->setRoughness(coinRoughness); + // planeMaterial->setNormal(gamefieldNormal); + planeMaterial->setShadow(shadowMap); + coinMaterial->setShadow(shadowMap); + boxMaterial->setShadow(shadowMap); + // planeMaterial->setNormalEnabled(true); + coinMaterial->setNormalEnabled(true); + boxMaterial->setNormalEnabled(true); + // planeMaterial->setSpecular(gamefieldSpecular); + // planeMaterial->setDirectionalLight(directionalLight); + // coinMaterial->setDirectionalLight(directionalLight); + // boxMaterial->setDirectionalLight(directionalLight); + // planeMaterial->addSpotLight(spotLight); + // planeMaterial->addPointLight(pointLight); + // coinMaterial->addPointLight(pointLight); + // boxMaterial->addPointLight(pointLight); + // boxMaterial->addPointLight(pointLight); + // planeMaterial->setShadow(true); + // coinMaterial->setShadow(true); + // boxMaterial->setShadow(true); + // planeMaterial->setEnvironmentMap(environmentMap); + // coinMaterial->setEnvironmentMap(environmentMap); + + musicBuffer = alutCreateBufferFromFile("Assets/Sounds/snake.wav"); + coinBuffer = alutCreateBufferFromFile("Assets/Sounds/coin.wav"); + alGenSources (1, &musicSource); + alGenSources (1, &coinSource); + alSourcei (musicSource, AL_BUFFER, static_cast(musicBuffer)); + alSourcei (coinSource, AL_BUFFER, static_cast(coinBuffer)); + alSourcei (musicSource, AL_LOOPING, true); + //alSourcePlay (musicSource); + ALCenum error; + + error = alGetError(); + if (error != AL_NO_ERROR) { + cout << "Sound error" << endl; + } +} + void App::Init() { InitResourceManager(); - glm::mat4 projection = glm::perspective(glm::radians(camera->getZoom()), (float) width / (float) height, 1.5f, - 2600.0f); - glm::mat4 ortho = glm::ortho(0.0f, static_cast(width), static_cast(height), 0.0f, -1.0f, 1000.0f); - glm::mat4 view = camera->getViewMatrix(); - - resourceManager->addShader("textShader", std::make_shared( - ShaderLoader::loadShader("Assets/Shaders/text.vs", "Assets/Shaders/text.fs"))); - resourceManager->addShader("basicShader", std::make_shared( - ShaderLoader::loadShader("Assets/Shaders/basic.vs", "Assets/Shaders/basic.fs"))); - resourceManager->addShader("normalShader", std::make_shared( - ShaderLoader::loadShader("Assets/Shaders/normal_map.vs", "Assets/Shaders/normal_map.fs"))); - resourceManager->addShader("radarShader", std::make_shared( - ShaderLoader::loadShader("Assets/Shaders/radar.vs", "Assets/Shaders/radar.fs"))); - resourceManager->addShader("skyboxShader", std::make_shared( - ShaderLoader::loadShader("Assets/Shaders/skybox.vs", "Assets/Shaders/skybox.fs"))); - resourceManager->addShader("shadowShader", std::make_shared( - ShaderLoader::loadShader("Assets/Shaders/shadow_map.vs", "Assets/Shaders/shadow_map.fs"))); - resourceManager->addShader("shadowDepthShader", std::make_shared( - ShaderLoader::loadShader("Assets/Shaders/shadow_map_depth.vs", "Assets/Shaders/shadow_map_depth.fs"))); - resourceManager->addShader("debugQuadShader", std::make_shared( - ShaderLoader::loadShader("Assets/Shaders/debug_quad.vs", "Assets/Shaders/debug_quad.fs"))); - resourceManager->addShader("bloomLight", std::make_shared( - ShaderLoader::loadShader("Assets/Shaders/bloom/bloom.vs", "Assets/Shaders/bloom/light.fs"))); - resourceManager->addShader("rain", std::make_shared( - ShaderLoader::loadShader("Assets/Shaders/rain/rain.vs", "Assets/Shaders/rain/rain.fs"))); - resourceManager->addShader("rainDrop", std::make_shared( - ShaderLoader::loadShader("Assets/Shaders/basic.vs", "Assets/Shaders/rain/raindrop.fs"))); - - InitSnake(); - animRenderer = new AnimRenderer((*snake->getItems().begin()), resourceManager->getAnimationModel("pacman"), camera, projection, resourceManager); - animRenderer->addPlay("KostraAction"); - snake->getHeadTile()->setVisible(false); -// animRenderer->addPlay("Armature|Take 001|BaseLayer"); -// animRenderer->addPlay("Kostra2Action.002"); -// animRenderer->addPlay("Kostra3Action"); - bloomRenderer = new BloomRenderer(resourceManager, width, height); - depthMapRenderer = new DepthMapRenderer(camera, projection, resourceManager); - gameFieldRenderer = new GameFieldRenderer(InitGameField(), camera, projection, resourceManager); - eat = InitEat(); - ObjWall *objWall = InitObjWall(); - barriers = InitBarriers(); - radar = CreateRadar(); - InitRadar(); - - levelManager = new LevelManager(1, MAX_LIVES, barriers); - levelManager->createLevel(START_LEVEL); - - auto *eatLocationHandler = new EatLocationHandler(barriers, snake, eat, radar); - eatManager = new EatManager(eatLocationHandler); - - snakeRenderer = new SnakeRenderer(snake, camera, projection, resourceManager); - objWallRenderer = new ObjWallRenderer(objWall, camera, projection, resourceManager); - barrierRenderer = new BarrierRenderer(barriers, camera, projection, resourceManager); - eatRenderer = new EatRenderer(eat, camera, projection, resourceManager); - radarRenderer = new RadarRenderer(radar, camera, ortho, resourceManager); - textRenderer = new TextRenderer(width, height); - skyboxRenderer = new SkyboxRenderer(skybox, camera, projection, resourceManager); - rainRenderer = new RainRenderer(new BaseItem(), camera, projection, resourceManager); - rainDropRenderer = new RainDropRenderer(new BaseItem(), camera, projection, resourceManager); - auto storm = new BaseItem(); - storm->setVisible(false); - - initTexts(); - - animateEat = new Eat; - animateEat->setVisible(false); - animateEat->setPosition(eat->getPosition()); - - eatRemoveAnimateRenderer = new EatRemoveAnimateRenderer(animateEat, camera, projection, - resourceManager); + // resourceManager->addShader( + // "bloom", + // std::make_shared( + // ShaderLoader::loadShader( + // "Assets/Shaders/bloom/bloom.vs", + // "Assets/Shaders/bloom/bloom.fs" + // )) + // ); + + resourceManager->addShader("blur", + std::make_shared( + ShaderLoader::loadShader( + "Assets/Shaders/bloom/blur.vs", + "Assets/Shaders/bloom/blur.fs" + )) + ); + + resourceManager->addShader( + "bloomFinal", + std::make_shared( + ShaderLoader::loadShader( + "Assets/Shaders/bloom/bloom_final.vs", + "Assets/Shaders/bloom/bloom_final.fs" + )) + ); + + resourceManager->addShader( + "shadowShader", + std::make_shared( + ShaderLoader::loadShader( + "Assets/Shaders/shadow_map.vs", + "Assets/Shaders/shadow_map.fs" + )) + ); + resourceManager->addShader( + "shadowDepthShader", + std::make_shared( + ShaderLoader::loadShader( + "Assets/Shaders/shadow_map_depth.vs", + "Assets/Shaders/shadow_map_depth.fs" + )) + ); + resourceManager->addShader( + "basicShader", + std::make_shared( + ShaderLoader::loadShader( + "Assets/Shaders/basic.vs", + "Assets/Shaders/basic.fs" + )) + ); + resourceManager->addShader( + "arrowGizmo", + std::make_shared( + ShaderLoader::loadShader( + "Assets/Shaders/gizmo/arrow.vert", + "Assets/Shaders/gizmo/arrow.frag" + )) + ); + resourceManager->addShader( + "preloadShader", + std::make_shared( + ShaderLoader::loadShader( + "Assets/Shaders/preloader/preloader.vs", + "Assets/Shaders/preloader/preloader.fs" + )) + ); + resourceManager->addShader( + "particle_update", + std::make_shared( + ShaderLoader::loadShader( + "Assets/Shaders/particle/particle_update.vs" + )) + ); + resourceManager->addShader( + "particle_update_2d", + std::make_shared( + ShaderLoader::loadShader( + "Assets/Shaders/particle/particle_update.vs" + )) + ); + resourceManager->addShader( + "particle_3d_render", + std::make_shared( + ShaderLoader::loadShader( + "Assets/Shaders/particle/particle_3d_render.vs", + "Assets/Shaders/particle/particle_3d_render.fs" + )) + ); + resourceManager->addShader( + "particle_render_2d", + std::make_shared( + ShaderLoader::loadShader( + "Assets/Shaders/particle/particle_render_2d.vs", + "Assets/Shaders/particle/particle_render_2d.fs" + )) + ); + resourceManager->addShader( + "particle_3d_render_tex", + std::make_shared( + ShaderLoader::loadShader( + "Assets/Shaders/particle/particle_3d_render_tex.vs", + "Assets/Shaders/particle/particle_3d_render_tex.fs" + )) + ); + resourceManager->addShader( + "particle_render_2d_tex", + std::make_shared( + ShaderLoader::loadShader( + "Assets/Shaders/particle/particle_render_2d_tex.vs", + "Assets/Shaders/particle/particle_render_2d_tex.fs" + )) + ); + + rendererManager->initBloom(); + rendererManager->initShadowMapping(); + rendererManager->initReflection(); + + preloaderScene = make_shared(nullptr, + vector >{}, + vector >{}, + rendererManager, camera, projection, resourceManager, width, height); + preloaderScene->init(0); - rendererManager->setWidth(width); - rendererManager->setHeight(height); - rendererManager->addRenderer(gameFieldRenderer); - rendererManager->addRenderer(objWallRenderer); - rendererManager->addRenderer(barrierRenderer); - rendererManager->addRenderer(eatRenderer); - rendererManager->addRenderer(eatRemoveAnimateRenderer); - rendererManager->addRenderer(rainDropRenderer); - rendererManager->addRenderer(snakeRenderer); - rendererManager->addRenderer(animRenderer); - rendererManager->addRenderer(radarRenderer); - rendererManager->addRenderer(skyboxRenderer); - rendererManager->addRenderer(rainRenderer); - rendererManager->addRenderer(textRenderer); - rendererManager->setDepthMapRenderer(depthMapRenderer); - rendererManager->setBloomRenderer(bloomRenderer); - //rendererManager->enableShadows(); - camera->setStickyPoint(snake->getHeadTile()); - - auto animHead = resourceManager->getAnimationModel("pacman"); - animHead->setBaseItem(snake->getHeadTile()); - auto *snakeMoveHandler = new SnakeMoveHandler(snake, animHead); - auto *radarHandler = new RadarHandler(radar); - - collisionDetector = new CollisionDetector(); - collisionDetector->setPerimeter(objWall); - collisionDetector->setBarriers(barriers); - collisionDetector->addStaticItem(eat); - snakeMoveHandler->setCollisionDetector(collisionDetector); - snakeMoveHandler->setStartMoveCallback([this, animHead]() { - if (this->levelManager) { - animHead->setGlobalPause(false); - this->eatManager->run(Manager::EatManager::firstPlace); - this->startText->fadeOut(); - char buff[100]; - snprintf(buff, sizeof(buff), - "%s %d, %s %d, %s %d", - "Level:", - this->levelManager->getLevel(), - "Lives:", - this->levelManager->getLive(), - "Points left:", - MAX_POINT - this->levelManager->getEatCounter() - ); - std::string buffAsStdStr = buff; - this->tilesCounterText->setText(buffAsStdStr); - if (this->tilesCounterText->getAlpha() == 1.0f) { - this->tilesCounterText->setAlpha(0.0f); - this->tilesCounterText->fadeIn(); - } - } + const fs::path assets_dir{"Assets/Objects"}; + resourceManager->loadAsyncModel(assets_dir / "pacman.glb", "pacman", []() { + std::cout << "Model pacman ready!" << std::endl; }); - snakeMoveHandler->setCrashCallback([this]() { - if (this->levelManager && this->barrierRenderer) { - snake->reset(); - InitRadar(); - this->levelManager->setLive(this->levelManager->getLive() - 1); - this->levelManager->setEatCounter(0); - char buff[100]; - snprintf(buff, sizeof(buff), - "%s %d, %s %d, %s %d", - "Level:", - this->levelManager->getLevel(), - "Lives:", - this->levelManager->getLive(), - "Points left:", - MAX_POINT - this->levelManager->getEatCounter() - ); - std::string buffAsStdStr = buff; - this->tilesCounterText->setText(buffAsStdStr); - eat->setVisible(false); - if (this->levelManager->getLive() == 0) { // Game Over - this->levelManager->createLevel(1); - this->startText->setVisible(true); - this->levelManager->setLive(3); - cout << "crash callback call" << endl; - } - } + resourceManager->loadAsyncModel(assets_dir / "Coin.obj", "coin", []() { + std::cout << "Model coin ready!" << std::endl; }); - snakeMoveHandler->setEatenUpCallback([this]() { - if (this->levelManager && this->snake && this->barrierRenderer) { - alSourcePlay (coinSource); - ALCenum error; - - error = alGetError(); - if (error != AL_NO_ERROR) { - cout << "Sound error" << endl; - } - - if (this->eatRemoveAnimateRenderer && this->animateEat) { - this->animateEat->setPosition(eat->getPosition()); - this->animateEat->setVisible(true); - this->animateEat->fadeOut(); - } + resourceManager->loadAsyncModel(assets_dir / "torch.glb", "torch", []() { + std::cout << "Model torch ready!" << std::endl; + }); + resourceManager->loadAsyncModel(assets_dir / "streetlamp.glb", "streetlamp", []() { + std::cout << "Model street lamp ready!" << std::endl; + }); + resourceManager->loadAsyncModel(assets_dir / "barrel.glb", "barrel", []() { + std::cout << "Model barrel ready!" << std::endl; + }); +} - this->levelManager->setEatCounter(this->levelManager->getEatCounter() + 1); - - if (this->levelManager->getEatCounter() == MAX_POINT) { - this->startText->setVisible(true); - this->snake->reset(); - this->eat->setVisible(false); - this->levelManager->createLevel(this->levelManager->getLevel() + 1); - this->eatManager->run(Manager::EatManager::clean); - InitRadar(); - } else { - this->eatManager->run(Manager::EatManager::eatenUp); - } +void App::run() { + if (state == SceneState::LOADING) { + resourceManager->processPending(); - char buff[100]; - snprintf(buff, sizeof(buff), - "%s %d, %s %d, %s %d", - "Level:", - this->levelManager->getLevel(), - "Lives:", - this->levelManager->getLive(), - "Points left:", - MAX_POINT - this->levelManager->getEatCounter() - ); - std::string buffAsStdStr = buff; - this->tilesCounterText->setText(buffAsStdStr); + if (!scanning && resourceManager->isAllLoaded()) { + std::cout << "\rLoading DONE! " << std::endl; + state = SceneState::RUNNING; + rendererManager->reset(); + initScene(); } - }); - keyboardManager->addEventHandler(snakeMoveHandler); - keyboardManager->addEventHandler(radarHandler); - - musicBuffer = (ALint)alutCreateBufferFromFile("Assets/Sounds/snake.wav"); - coinBuffer = (ALint)alutCreateBufferFromFile("Assets/Sounds/coin.wav"); - alGenSources (1, &musicSource); - alGenSources (1, &coinSource); - alSourcei (musicSource, AL_BUFFER, (ALint)musicBuffer); - alSourcei (coinSource, AL_BUFFER, (ALint)coinBuffer); - alSourcei (musicSource, AL_LOOPING, true); - //alSourcePlay (musicSource); - ALCenum error; - - error = alGetError(); - if (error != AL_NO_ERROR) { - cout << "Sound error" << endl; } -} -void App::initTexts() { - if (textRenderer && resourceManager) { - startText->setVisible(true); - startText->setColor({0.8, 0.8, 0.8}); - startText->setFontPath("Assets/Fonts/OCRAEXT.TTF"); - startText->setFontSize(22); - startText->setPosition({(width - 360) / 2, height / 2 + 15, 0.0}); - startText->setZoom({1.0f, 0, 0}); - textRenderer->addText(startText, resourceManager->getShader("textShader")); - - tilesCounterText->setVisible(true); - tilesCounterText->setColor({0.8, 0.8, 0.8}); - tilesCounterText->setFontPath("Assets/Fonts/OCRAEXT.TTF"); - tilesCounterText->setFontSize(22); - tilesCounterText->setPosition({25.0f, 25.0f, 0.0}); - tilesCounterText->setZoom({1.0f, 0, 0}); - textRenderer->addText(tilesCounterText, resourceManager->getShader("textShader")); + if (state == SceneState::RUNNING) { + mainScene->update(); + mainScene->render(); + } else { + preloaderScene->update(); + preloaderScene->render(); } } -Eat *App::InitEat() { - eat->setVirtualX((((int) (23 - (-23)) / 2) * 32) + 16); - eat->setVirtualY((((int) (-3 - (-23)) / 2) * 32) + 16); - eat->setPosition({-69.0, -69, -70.0f}); // velikost mince je cca 6x6 - - eat->setRotate({90, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}); - eat->setVisible(false); - - return eat; -} - -GameField *App::InitGameField() { - gameField = new GameField(); - gameField->Init(); - - return gameField; -} - -Snake *App::InitSnake() { - snake = new Snake(); - snake->init(); +void App::processInput(GLFWwindow *window, const int keyCode, const int scancode, const int action, const int mods) const { + mainScene->keyboardInput(window, keyCode, scancode, action, mods); + //keyboardManager->onKeyPress(keyCode, scancode, action, mods); - return snake; -} - -Barriers *App::InitBarriers() { - barriers = new Barriers(); - - return barriers; + switch (keyCode) { + case GLFW_KEY_P: + // if (objWallRenderer) { + // objWallRenderer->toggleParallax(); + // } + break; + case GLFW_KEY_F: + // rendererManager->toggleFog(); + break; + case GLFW_KEY_W: + // if (rainRenderer && rainDropRenderer) { + // rainRenderer->toggle(); + // rainDropRenderer->setEnable(rainRenderer->isEnable()); + // } + break; + // case GLFW_KEY_U: + // camera->startUpsideDownRotate(); + // break; + case GLFW_KEY_M: + // TODO: sound + // ALint source_state; + // alGetSourcei(musicSource, AL_SOURCE_STATE, &source_state); + // + // if (source_state == AL_PLAYING) { + // alSourceStop(musicSource); + // } else { + // alSourcePlay(musicSource); + // } + break; + // case GLFW_KEY_1: // show classic red head + // snake->getHeadTile()->setVisible(true); + // animRenderer->setShow(false); + //snakeRenderer->toggleStyle(1); + // break; + // case GLFW_KEY_2: // show animated pacman head + // snake->getHeadTile()->setVisible(false); + // animRenderer->setShow(true); + //snakeRenderer->toggleStyle(2); + // break; + case GLFW_KEY_T: + // if (boltRenderer) { + // boltRenderer->triggerBolt(); + // } + break; + case GLFW_KEY_ESCAPE: + if (state == SceneState::RUNNING) { + glfwSetWindowShouldClose(window, true); + } + default: + break; + } } -void App::InitRadar() { - radar->reset(); - radar->setVisible(true); - radar->setPosition({125.0, 160.0, 0.0}); - radar->setZoom({100, 100, 1}); - radar->setWidth(176); - radar->setHeight(176); - - if (resourceManager) { - for (auto tile: snake->getItems()) { - radar->addItem(tile->tile, {0.278,1.,0.}); +void App::mouseButtonCallback(GLFWwindow *window, const int button, const int action, const int mods) const { + double xpos, ypos; + // glfwGetCursorPos(window, &xpos, &ypos); + // const glm::vec2 cursor(static_cast(xpos), static_cast(ypos)); + + // if (torchRenderer != nullptr) { + // if (button == GLFW_MOUSE_BUTTON_LEFT) { + // if (action == GLFW_PRESS) { + // torchRenderer->onMouseDown(cursor, width, height); + // } else if (action == GLFW_RELEASE) { + // torchRenderer->onMouseUp(); + // } + // } + // } + if (camera) { + if (button == GLFW_MOUSE_BUTTON_RIGHT) { + camera->onMouseDown(button, action, mods); } - for (auto block: barriers->getItems()) { - radar->addItem(block, {0.694,0.078,0.016}); - } - radar->addItem(eat, {1.,0.953,0.}); } } -Radar *App::CreateRadar() { - auto radar = new Radar(); - - return radar; +void App::mousePositionCallback(GLFWwindow *window, const double x, const double y) const { + const glm::vec2 cursor(static_cast(x), static_cast(y)); + //if (torchRenderer != nullptr) { + // torchRenderer->onMouseMove(cursor, width, height); + //} + if (state == SceneState::RUNNING && camera != nullptr) { + camera->processMouseMovement(x, y); + } } -ObjWall *App::InitObjWall() { - objWall = new ObjWall(); - objWall->init(); - - return objWall; +void App::setKeyState(const int key, const bool pressed) const { + camera->setKeyState(key, pressed); } -void App::InitResourceManager() { - resourceManager = new ResourceManager(); - - std::string path = "Assets/Textures/"; - for (fs::recursive_directory_iterator i(path), end; i != end; ++i) { - if (!is_directory(i->path())) { - std::cout << i->path().filename() << std::endl; - auto texture = std::make_shared(); - texture->addTexture(TextureLoader::loadTexture(i->path())); - resourceManager->addTexture(i->path().filename(), texture); - } - } - - const fs::path assets_dir{"Assets/Objects"}; - - resourceManager->addModel("cube", ObjModelLoader::loadObj(assets_dir / "Cube.obj")); - resourceManager->addModel("coin", ObjModelLoader::loadObj(assets_dir / "Coin.obj")); - resourceManager->addModel("tile", AnimLoader::loadObj(assets_dir / "Tile.obj")); - resourceManager->addModel("pacman", AnimLoader::loadObj(assets_dir / "pacman.glb")); //pac-man-ghosts-blue.glb +void App::cameraProcessKeyboard(GLFWwindow *window) const { + camera->processKeyboard(window, 1); +} +void App::InitResourceManager() const { vector faces; faces.emplace_back("Assets/Skybox/cloud/right.jpg"); faces.emplace_back("Assets/Skybox/cloud/left.jpg"); @@ -355,82 +427,105 @@ void App::InitResourceManager() { faces.emplace_back("Assets/Skybox/cloud/bottom.jpg"); faces.emplace_back("Assets/Skybox/cloud/front.jpg"); faces.emplace_back("Assets/Skybox/cloud/back.jpg"); - unsigned int cubeMapTexture = TextureLoader::loadSkyboxTexture(faces); - auto texture = std::make_shared(); + const unsigned int cubeMapTexture = TextureLoader::loadSkyboxTexture(faces); + const auto texture = std::make_shared(); texture->addTexture(cubeMapTexture); resourceManager->addTexture("skybox", texture); -} -void App::run() { - camera->updateStickyPoint(); - rendererManager->render(); - keyboardManager->runDefault(); - if (!startText->isVisible()) { // pokud hra bezi, tak checkneme zda je videt jidlo, pokud ne zkusime znova umisti - eatManager->run(Manager::EatManager::checkPlace); + std::ifstream manifestFile("Assets/texture_manifest.json"); + if (!manifestFile.is_open()) { + throw std::runtime_error("Cant open manifest file."); } -} -void App::processInput(int keyCode) { - keyboardManager->onKeyPress(keyCode); + nlohmann::json j; + manifestFile >> j; - switch (keyCode) { - case GLFW_KEY_V: - rendererManager->toggleShadows(); - break; - case GLFW_KEY_P: - if (objWallRenderer) { - objWallRenderer->toggleParallax(); - } - break; - case GLFW_KEY_RIGHT: - if (objWallRenderer) { - objWallRenderer->upScale(); - } - break; - case GLFW_KEY_LEFT: - if (objWallRenderer) { - objWallRenderer->downScale(); - } - break; - case GLFW_KEY_B: - rendererManager->toggleBloom(); - if (snakeRenderer) { - snakeRenderer->toggleBlur(); - } - break; - case GLFW_KEY_F: - rendererManager->toggleFog(); - break; - case GLFW_KEY_W: - rainRenderer->toggle(); - rainDropRenderer->setEnable(rainRenderer->isEnable()); - break; -// case GLFW_KEY_U: -// camera->startUpsideDownRotate(); -// break; - case GLFW_KEY_M: - ALint source_state; - alGetSourcei(musicSource, AL_SOURCE_STATE, &source_state); + std::vector textures; - if (source_state == AL_PLAYING) { - alSourceStop(musicSource); - } else { - alSourcePlay(musicSource); - } - break; - case GLFW_KEY_1: // show classic red head - snake->getHeadTile()->setVisible(true); - animRenderer->setShow(false); - snakeRenderer->toggleStyle(1); - break; - case GLFW_KEY_2: // show animated pacman head - snake->getHeadTile()->setVisible(false); - animRenderer->setShow(true); - snakeRenderer->toggleStyle(2); - break; - default: - break; + for (auto &item : j) { + textures.push_back(TextureEntry{ + item["name"].get(), + item["path"].get(), + item["category"].get() + }); + } + + for (const auto &[name, path, category] : textures) { + bool isAlbedo = (category == "Albedo"); + + resourceManager->loadAsyncTexture("Assets/Textures/" + path, name, isAlbedo, [name=name]() { + std::cout << "Texture ready: " << name << std::endl; + }); } -} -#pragma clang diagnostic pop \ No newline at end of file + resourceManager->loadAsyncShader("textShader", "Assets/Shaders/text.vs", "", "Assets/Shaders/text.fs", []() { + std::cout << "Shader textShader ready!" << std::endl; + }); + + resourceManager->loadAsyncShader("gizmoShader", + "Assets/Shaders/gizmo/gizmo.vs", + "Assets/Shaders/gizmo/gizmo.geom", + "Assets/Shaders/gizmo/gizmo.fs", + []() { + std::cout << "Shader gizmo ready!" << std::endl; + }); + + resourceManager->loadAsyncShader("colorShader", "Assets/Shaders/color.vs", "", "Assets/Shaders/color.fs", []() { + std::cout << "Shader color ready!" << std::endl; + }); + + resourceManager->loadAsyncShader("respawnShader", "Assets/Shaders/basic.vs", "", "Assets/Shaders/respawn/respawn.fs", []() {; + std::cout << "Shader respawnShader ready!" << std::endl; + }); + + resourceManager->loadAsyncShader("normalShader", "Assets/Shaders/normal_map.vs", "", "Assets/Shaders/normal_map.fs", []() { + std::cout << "Shader normalShader ready!" << std::endl; + }); + + resourceManager->loadAsyncShader("basic2d", "Assets/Shaders/basic_2d.vs", "", "Assets/Shaders/basic_2d.fs", []() {; + std::cout << "Shader basic2d ready!" << std::endl; + }); + + resourceManager->loadAsyncShader("quadCorner", "Assets/Shaders/basic_2d.vs", "", "Assets/Shaders/corner/corner.fs", []() {; + std::cout << "Shader quadCorner ready!" << std::endl; + }); + + resourceManager->loadAsyncShader("skyboxShader", "Assets/Shaders/skybox.vs", "", "Assets/Shaders/skybox.fs", []() { + std::cout << "Shader skyboxShader ready!" << std::endl; + }); + + resourceManager->loadAsyncShader("debugQuadShader", "Assets/Shaders/debug_quad.vs", "", "Assets/Shaders/debug_quad.fs", []() { + std::cout << "Shader debugQuadShader ready!" << std::endl; + }); + + resourceManager->loadAsyncShader("rain", "Assets/Shaders/rain/rain.vs", "", "Assets/Shaders/rain/rain.fs", []() { + std::cout << "Shader rain ready!" << std::endl; + }); + + resourceManager->loadAsyncShader("rainDrop", "Assets/Shaders/basic.vs", "", "Assets/Shaders/rain/raindrop.fs", []() { + std::cout << "Shader rainDrop ready!" << std::endl; + }); + + resourceManager->loadAsyncShader("fire", "Assets/Shaders/fire/fire.vs", "", "Assets/Shaders/fire/fire.fs", []() { + std::cout << "Shader fire ready!" << std::endl; + }); + + resourceManager->loadAsyncShader("smoke", "Assets/Shaders/fire/smoke.vs", "", "Assets/Shaders/fire/smoke.fs", []() { + std::cout << "Shader smoke ready!" << std::endl; + }); + + resourceManager->loadAsyncShader("boltShader", "Assets/Shaders/bolt/bolt.vs", "", "Assets/Shaders/bolt/bolt.fs", []() { + std::cout << "Shader boltShader ready!" << std::endl; + }); + + resourceManager->loadAsyncShader("flash", "Assets/Shaders/bolt/flash.vs", "", "Assets/Shaders/bolt/flash.fs", []() { + std::cout << "Shader flash ready!" << std::endl; + }); + + resourceManager->loadAsyncShader("explosion", + "Assets/Shaders/explosion/explosion.vs", + "Assets/Shaders/explosion/explosion.geom", + "Assets/Shaders/explosion/explosion.fs", []() { + std::cout << "Shader explosion ready!" << std::endl; + }); +} diff --git a/App.h b/App.h index 7fc2a8ca..8f9cde22 100644 --- a/App.h +++ b/App.h @@ -1,45 +1,25 @@ #ifndef SNAKE3_APP_H #define SNAKE3_APP_H -#include "ItemsDto/GameField.h" -#include "ItemsDto/Snake.h" -#include "ItemsDto/ObjWall.h" -#include "Resource/ObjModelLoader.h" -#include "Resource/ShaderLoader.h" #include "Manager/ResourceManager.h" #include "Manager/RenderManager.h" #include "Manager/KeyboardManager.h" -#include "Renderer/Opengl/GameFieldRenderer.h" -#include "Renderer/Opengl/SkyboxRenderer.h" -#include "Renderer/Opengl/SnakeRenderer.h" -#include "Renderer/Opengl/RadarRenderer.h" -#include "Renderer/Opengl/DepthMapRenderer.h" -#include "Handler/SnakeMoveHandler.h" -#include "Handler/RadarHandler.h" #include "Handler/EatLocationHandler.h" -#include "stdafx.h" -#include "Renderer/Opengl/EatRenderer.h" -#include "Renderer/Opengl/TextRenderer.h" -#include "ItemsDto/Eat.h" #include "Manager/EatManager.h" -#include "Renderer/Opengl/EatRemoveAnimateRenderer.h" -#include "ItemsDto/Barriers.h" -#include "Renderer/Opengl/BarrierRenderer.h" #include "Manager/LevelManager.h" -#include "Renderer/Opengl/ObjWallRenderer.h" #include "Manager/Camera.h" -#include "Renderer/Opengl/BloomRenderer.h" -#include "Renderer/Opengl/RainRenderer.h" #include "Renderer/Opengl/RainDropRenderer.h" -#include "Renderer/Opengl/AnimRenderer.h" -#include +#include "Particle/SmokeParticleSystem.h" +#include "Renderer/Opengl/BoltRenderer.h" +#include "Renderer/Opengl/FireRenderer.h" #include -#include -#include +#include +#include "Scenes/MainScene.h" +#include "Scenes/PreloaderScene.h" #define MAX_POINT 6 #define MAX_LIVES 4 -#define START_LEVEL 1 +#define START_LEVEL 2 namespace fs = std::filesystem; using namespace ItemsDto; @@ -47,61 +27,54 @@ using namespace Manager; using namespace Renderer; using namespace Handler; using namespace Resource; +using namespace Particle; +using namespace Model; +using namespace Material; class App { + enum class SceneState { + LOADING, + RUNNING + }; public: - App(Camera* camera, int width, int height); + App(const shared_ptr &camera, int width, int height); ~App(); void Init(); void run(); - void processInput(int keyCode); + void processInput(GLFWwindow *window, int keyCode, int scancode, int action, int mods) const; + void mouseButtonCallback(GLFWwindow* window, int button, int action, int mods) const; + void mousePositionCallback(GLFWwindow* window, double x, double y) const; + void setKeyState(int key, bool pressed) const; + void cameraProcessKeyboard(GLFWwindow *window) const; protected: - void InitResourceManager(); - GameField* InitGameField(); - Snake* InitSnake(); - ObjWall* InitObjWall(); // outer wall - Barriers* InitBarriers(); // inter barriers - static Radar* CreateRadar(); - void InitRadar(); - Eat *InitEat(); - void initTexts(); + void initScene(); + void InitResourceManager() const; private: - LevelManager* levelManager{}; - ResourceManager* resourceManager{}; - RenderManager* rendererManager; - GameField* gameField{}; - GameFieldRenderer* gameFieldRenderer{}; - SkyboxRenderer* skyboxRenderer{}; - Snake* snake{}; - Eat* animateEat{}; - Eat* eat; - Radar* radar{}; - ObjWall* objWall{}; - Barriers* barriers{}; - Cube* skybox{}; - SnakeRenderer* snakeRenderer{}; - ObjWallRenderer* objWallRenderer{}; - BarrierRenderer* barrierRenderer{}; - EatRenderer* eatRenderer{}; - RadarRenderer* radarRenderer{}; - TextRenderer* textRenderer{}; - DepthMapRenderer* depthMapRenderer{}; - BloomRenderer* bloomRenderer{}; - EatRemoveAnimateRenderer* eatRemoveAnimateRenderer{}; - RainRenderer* rainRenderer{}; - AnimRenderer* animRenderer{}; - RainDropRenderer* rainDropRenderer{}; - KeyboardManager* keyboardManager; - CollisionDetector* collisionDetector{}; - EatManager* eatManager; - Text* startText; - Text* tilesCounterText; - Camera* camera; + struct TextureEntry { + std::string name; + std::string path; + std::string category; + }; + shared_ptr environment; + shared_ptr resourceManager; + shared_ptr rendererManager; + shared_ptr rainDropRenderer{}; + shared_ptr fireRenderer{}; + shared_ptr boltRenderer{}; + unique_ptr keyboardManager; + shared_ptr collisionDetector; + unique_ptr eatManager; + shared_ptr camera; + glm::mat4 projection{}; int width; int height; ALuint musicSource{}, coinSource{}; ALuint coinBuffer{}, musicBuffer{}; + SceneState state = SceneState::LOADING; + std::atomic scanning = false; + shared_ptr mainScene; + shared_ptr preloaderScene; + shared_ptr contextState; }; - #endif //SNAKE3_APP_H diff --git a/Assets/Objects/Cube.mtl b/Assets/Objects/Cube.mtl deleted file mode 100644 index 85c62002..00000000 --- a/Assets/Objects/Cube.mtl +++ /dev/null @@ -1,12 +0,0 @@ -# Blender MTL File: 'Crate1.blend' -# Material Count: 1 - -newmtl Material.002 -Ns 225.000000 -Ka 1.000000 1.000000 1.000000 -Kd 0.800000 0.800000 0.800000 -Ks 0.500000 0.500000 0.500000 -Ke 0.000000 0.000000 0.000000 -Ni 1.000000 -d 1.000000 -illum 2 diff --git a/Assets/Objects/Cube.obj b/Assets/Objects/Cube.obj deleted file mode 100644 index df276007..00000000 --- a/Assets/Objects/Cube.obj +++ /dev/null @@ -1,46 +0,0 @@ -# Blender v3.0.1 OBJ File: 'Crate1.blend' -# www.blender.org -mtllib Assets/Objects/Cube.mtl -o Cube_Cube.002 -v -1.000000 -1.000000 1.000000 -v -1.000000 -1.000000 -1.000000 -v 1.000000 -1.000000 -1.000000 -v 1.000000 -1.000000 1.000000 -v -1.000000 1.000000 1.000000 -v -1.000000 1.000000 -1.000000 -v 1.000000 1.000000 -1.000000 -v 1.000000 1.000000 1.000000 -vt 0.000000 0.000000 -vt 0.000000 0.500000 -vt 0.500000 0.500000 -vt 0.500000 0.000000 -vt 0.000000 0.000000 -vt 0.000000 0.500000 -vt 0.500000 0.500000 -vt 0.500000 0.000000 -vt 0.000000 0.000000 -vt 0.000000 0.500000 -vt 0.500000 0.500000 -vt 0.500000 0.000000 -vt 0.000000 0.000000 -vt 0.000000 0.500000 -vt 0.500000 0.500000 -vt 0.500000 0.000000 -vt 0.000000 0.000000 -vt 0.000000 0.500000 -vt 0.000000 0.500000 -vt 0.500000 0.000000 -vn -1.0000 0.0000 0.0000 -vn 0.0000 0.0000 -1.0000 -vn 1.0000 0.0000 0.0000 -vn 0.0000 0.0000 1.0000 -vn 0.0000 -1.0000 0.0000 -vn 0.0000 1.0000 0.0000 -usemtl Material.002 -s off -f 5/1/1 6/2/1 2/3/1 1/4/1 -f 6/5/2 7/6/2 3/7/2 2/8/2 -f 7/9/3 8/10/3 4/11/3 3/12/3 -f 8/13/4 5/14/4 1/15/4 4/16/4 -f 1/17/5 2/18/5 3/7/5 4/16/5 -f 8/13/6 7/6/6 6/19/6 5/20/6 diff --git a/Assets/Objects/Tile.mtl b/Assets/Objects/Tile.mtl deleted file mode 100644 index 75c673e1..00000000 --- a/Assets/Objects/Tile.mtl +++ /dev/null @@ -1,12 +0,0 @@ -# Blender MTL File: 'pacman2.blend' -# Material Count: 1 - -newmtl Standard1 -Ns 225.000000 -Ka 1.000000 1.000000 1.000000 -Kd 1.000000 0.898039 0.000000 -Ks 0.500000 0.500000 0.500000 -Ke 0.000000 0.000000 0.000000 -Ni 1.000000 -d 1.000000 -illum 2 diff --git a/Assets/Objects/Tile.obj b/Assets/Objects/Tile.obj deleted file mode 100644 index 29916c8f..00000000 --- a/Assets/Objects/Tile.obj +++ /dev/null @@ -1,2071 +0,0 @@ -# Blender v3.0.1 OBJ File: 'pacman2.blend' -# www.blender.org -mtllib Tile.mtl -o Sphere.001 -v -0.032024 0.655614 -0.135141 -v -0.032024 0.615780 -0.266456 -v -0.032024 0.551093 -0.387477 -v -0.032024 0.464039 -0.493553 -v -0.032024 0.357963 -0.580607 -v -0.032024 0.236942 -0.645294 -v -0.032024 0.105627 -0.685128 -v -0.032024 -0.419835 -0.580607 -v -0.032024 -0.525911 -0.493553 -v -0.032024 -0.612965 -0.387477 -v -0.005382 0.655614 -0.132517 -v 0.020236 0.615780 -0.261309 -v 0.043846 0.551093 -0.380005 -v 0.064541 0.464039 -0.484042 -v 0.081524 0.357963 -0.569423 -v 0.094144 0.236942 -0.632867 -v 0.101915 0.105627 -0.671936 -v 0.104539 -0.030936 -0.685128 -v 0.101915 -0.167499 -0.671936 -v 0.094144 -0.298814 -0.632867 -v 0.081524 -0.419835 -0.569423 -v 0.064541 -0.525911 -0.484042 -v 0.043846 -0.612965 -0.380005 -v 0.020236 -0.677652 -0.261309 -v -0.005382 -0.717486 -0.132517 -v 0.020237 0.655614 -0.124746 -v 0.070489 0.615780 -0.246065 -v 0.116801 0.551093 -0.357874 -v 0.157395 0.464039 -0.455875 -v 0.190709 0.357963 -0.536302 -v 0.215463 0.236942 -0.596065 -v 0.230707 0.105627 -0.632867 -v 0.235854 -0.030936 -0.645294 -v 0.230707 -0.167499 -0.632867 -v 0.215463 -0.298814 -0.596065 -v 0.190709 -0.419835 -0.536302 -v 0.157395 -0.525911 -0.455875 -v 0.116801 -0.612965 -0.357874 -v 0.070489 -0.677652 -0.246065 -v 0.020236 -0.717486 -0.124746 -v 0.043846 0.655614 -0.112126 -v 0.116801 0.615780 -0.221311 -v 0.184037 0.551093 -0.321936 -v 0.242969 0.464039 -0.410134 -v 0.291334 0.357963 -0.482517 -v 0.327272 0.236942 -0.536302 -v 0.349403 0.105627 -0.569423 -v 0.356875 -0.030936 -0.580607 -v 0.349403 -0.167499 -0.569423 -v 0.327272 -0.298814 -0.536302 -v 0.291334 -0.419835 -0.482517 -v 0.242969 -0.525911 -0.410134 -v 0.184037 -0.612965 -0.321936 -v 0.116801 -0.677652 -0.221311 -v 0.043846 -0.717486 -0.112126 -v 0.064541 0.655614 -0.095143 -v 0.157395 0.615780 -0.187997 -v 0.242969 0.551093 -0.273571 -v 0.317976 0.464039 -0.348578 -v 0.379532 0.357963 -0.410134 -v 0.425273 0.236942 -0.455875 -v 0.453440 0.105627 -0.484042 -v 0.462951 -0.030936 -0.493553 -v 0.453440 -0.167499 -0.484042 -v 0.425273 -0.298814 -0.455875 -v 0.379532 -0.419835 -0.410134 -v 0.317976 -0.525911 -0.348578 -v 0.242969 -0.612965 -0.273571 -v 0.157395 -0.677652 -0.187997 -v 0.064541 -0.717486 -0.095143 -v 0.081524 0.655614 -0.074448 -v 0.190709 0.615780 -0.147403 -v 0.291334 0.551093 -0.214639 -v 0.379532 0.464039 -0.273571 -v 0.451915 0.357963 -0.321936 -v 0.505700 0.236942 -0.357874 -v 0.538821 0.105627 -0.380004 -v 0.550005 -0.030936 -0.387477 -v 0.538821 -0.167499 -0.380004 -v 0.505700 -0.298814 -0.357874 -v 0.451915 -0.419835 -0.321936 -v 0.379532 -0.525911 -0.273571 -v 0.291334 -0.612965 -0.214639 -v 0.190709 -0.677652 -0.147403 -v 0.081524 -0.717486 -0.074448 -v 0.094144 0.655614 -0.050838 -v 0.215463 0.615780 -0.101091 -v 0.327272 0.551093 -0.147403 -v 0.425273 0.464039 -0.187997 -v 0.505700 0.357963 -0.221311 -v 0.565463 0.236942 -0.246065 -v 0.602265 0.105627 -0.261309 -v 0.614692 -0.030936 -0.266456 -v 0.602265 -0.167499 -0.261309 -v 0.565463 -0.298814 -0.246065 -v 0.505700 -0.419835 -0.221311 -v 0.425273 -0.525911 -0.187997 -v 0.327272 -0.612965 -0.147403 -v 0.215463 -0.677652 -0.101091 -v 0.094144 -0.717486 -0.050838 -v 0.101915 0.655614 -0.025220 -v 0.230707 0.615780 -0.050838 -v 0.349403 0.551093 -0.074448 -v 0.453440 0.464039 -0.095143 -v 0.538821 0.357963 -0.112126 -v 0.602265 0.236942 -0.124746 -v 0.641334 0.105627 -0.132517 -v 0.654526 -0.030936 -0.135141 -v 0.641334 -0.167499 -0.132517 -v 0.602265 -0.298814 -0.124746 -v 0.538821 -0.419835 -0.112126 -v 0.453440 -0.525911 -0.095143 -v 0.349403 -0.612965 -0.074448 -v 0.230707 -0.677652 -0.050838 -v 0.101915 -0.717486 -0.025220 -v 0.104539 0.655614 0.001422 -v 0.235854 0.615780 0.001422 -v 0.356875 0.551093 0.001422 -v 0.462951 0.464039 0.001422 -v 0.550005 0.357963 0.001422 -v 0.614692 0.236942 0.001422 -v 0.654526 0.105627 0.001422 -v 0.667976 -0.030936 0.001422 -v 0.654526 -0.167499 0.001422 -v 0.614692 -0.298814 0.001422 -v 0.550005 -0.419835 0.001422 -v 0.462951 -0.525911 0.001422 -v 0.356875 -0.612965 0.001422 -v 0.235855 -0.677652 0.001422 -v 0.104539 -0.717486 0.001422 -v 0.101915 0.655614 0.028064 -v 0.230707 0.615780 0.053683 -v 0.349402 0.551093 0.077293 -v 0.453440 0.464039 0.097987 -v 0.538821 0.357963 0.114970 -v 0.602265 0.236942 0.127590 -v 0.641334 0.105627 0.135361 -v 0.654525 -0.030936 0.137985 -v 0.641334 -0.167499 0.135361 -v 0.602265 -0.298814 0.127590 -v 0.538821 -0.419835 0.114970 -v 0.453440 -0.525911 0.097987 -v 0.349402 -0.612965 0.077293 -v 0.230707 -0.677652 0.053683 -v 0.101915 -0.717486 0.028064 -v 0.094144 0.655614 0.053683 -v 0.215463 0.615780 0.103935 -v 0.327272 0.551093 0.150247 -v 0.425273 0.464039 0.190841 -v 0.505700 0.357963 0.224155 -v 0.565463 0.236942 0.248909 -v 0.602265 0.105627 0.264153 -v 0.614691 -0.030936 0.269300 -v 0.602265 -0.167499 0.264153 -v 0.565463 -0.298814 0.248909 -v 0.505700 -0.419835 0.224155 -v 0.425273 -0.525911 0.190841 -v 0.327272 -0.612965 0.150247 -v 0.215463 -0.677652 0.103935 -v 0.094144 -0.717486 0.053683 -v 0.081524 0.655614 0.077293 -v 0.190709 0.615780 0.150247 -v 0.291334 0.551093 0.217483 -v 0.379532 0.464039 0.276415 -v 0.451915 0.357963 0.324780 -v 0.505700 0.236942 0.360718 -v 0.538821 0.105627 0.382849 -v 0.550004 -0.030936 0.390321 -v 0.538821 -0.167499 0.382849 -v 0.505700 -0.298814 0.360718 -v 0.451915 -0.419835 0.324780 -v 0.379532 -0.525911 0.276415 -v 0.291334 -0.612965 0.217483 -v 0.190709 -0.677652 0.150247 -v 0.081524 -0.717486 0.077293 -v 0.064541 0.655614 0.097987 -v 0.157395 0.615780 0.190841 -v 0.242969 0.551093 0.276415 -v 0.317976 0.464039 0.351422 -v 0.379532 0.357963 0.412978 -v 0.425273 0.236942 0.458719 -v 0.453440 0.105627 0.486886 -v 0.462950 -0.030936 0.496397 -v 0.453440 -0.167499 0.486886 -v 0.425273 -0.298814 0.458719 -v 0.379532 -0.419835 0.412978 -v 0.317976 -0.525911 0.351422 -v 0.242969 -0.612965 0.276415 -v 0.157395 -0.677652 0.190841 -v 0.064541 -0.717486 0.097987 -v 0.043846 0.655614 0.114970 -v 0.116801 0.615780 0.224155 -v 0.184037 0.551093 0.324780 -v 0.242969 0.464039 0.412978 -v 0.291334 0.357963 0.485361 -v 0.327272 0.236942 0.539146 -v 0.349402 0.105627 0.572267 -v 0.356875 -0.030936 0.583451 -v 0.349402 -0.167499 0.572267 -v 0.327272 -0.298814 0.539146 -v 0.291334 -0.419835 0.485361 -v 0.242969 -0.525911 0.412978 -v 0.184037 -0.612965 0.324780 -v 0.116801 -0.677652 0.224155 -v 0.043846 -0.717486 0.114970 -v 0.020236 0.655614 0.127590 -v 0.070489 0.615780 0.248909 -v 0.116801 0.551093 0.360718 -v 0.157395 0.464039 0.458719 -v 0.190709 0.357963 0.539146 -v 0.215463 0.236942 0.598909 -v 0.230707 0.105627 0.635711 -v 0.235854 -0.030936 0.648137 -v 0.230707 -0.167499 0.635711 -v 0.215463 -0.298814 0.598909 -v 0.190709 -0.419835 0.539146 -v 0.157395 -0.525911 0.458719 -v 0.116801 -0.612965 0.360718 -v 0.070489 -0.677652 0.248909 -v 0.020236 -0.717486 0.127590 -v -0.005382 0.655614 0.135361 -v 0.020236 0.615780 0.264153 -v 0.043846 0.551093 0.382849 -v 0.064541 0.464039 0.486886 -v 0.081524 0.357963 0.572267 -v 0.094144 0.236942 0.635711 -v 0.101915 0.105627 0.674780 -v 0.104539 -0.030936 0.687971 -v 0.101915 -0.167499 0.674780 -v 0.094144 -0.298814 0.635711 -v 0.081524 -0.419835 0.572267 -v 0.064541 -0.525911 0.486886 -v 0.043846 -0.612965 0.382849 -v 0.020236 -0.677652 0.264153 -v -0.005382 -0.717486 0.135361 -v -0.032024 0.655614 0.137985 -v -0.032024 0.615780 0.269300 -v -0.032024 0.551093 0.390321 -v -0.032024 0.464039 0.496397 -v -0.032024 0.357963 0.583450 -v -0.032024 0.236942 0.648138 -v -0.032024 0.105627 0.687971 -v -0.032024 -0.030936 0.701422 -v -0.032024 -0.167499 0.687971 -v -0.032024 -0.298814 0.648138 -v -0.032024 -0.419835 0.583451 -v -0.032024 -0.525911 0.496397 -v -0.032024 -0.612965 0.390321 -v -0.032024 -0.677652 0.269300 -v -0.032024 -0.717486 0.137985 -v -0.058666 0.655614 0.135361 -v -0.084285 0.615780 0.264153 -v -0.107894 0.551093 0.382848 -v -0.128589 0.464039 0.486886 -v -0.145572 0.357963 0.572267 -v -0.158192 0.236942 0.635711 -v -0.165963 0.105627 0.674780 -v -0.168587 -0.030936 0.687971 -v -0.165963 -0.167499 0.674780 -v -0.158192 -0.298814 0.635711 -v -0.145572 -0.419835 0.572267 -v -0.128589 -0.525911 0.486886 -v -0.107894 -0.612965 0.382848 -v -0.084284 -0.677652 0.264153 -v -0.058666 -0.717486 0.135361 -v -0.084284 0.655614 0.127590 -v -0.134537 0.615780 0.248909 -v -0.180849 0.551093 0.360718 -v -0.221443 0.464039 0.458719 -v -0.254757 0.357963 0.539146 -v -0.279511 0.236942 0.598909 -v -0.294755 0.105627 0.635711 -v -0.299902 -0.030936 0.648137 -v -0.294755 -0.167499 0.635711 -v -0.279511 -0.298814 0.598909 -v -0.254757 -0.419835 0.539146 -v -0.221443 -0.525911 0.458719 -v -0.180849 -0.612965 0.360718 -v -0.134537 -0.677652 0.248909 -v -0.084284 -0.717486 0.127590 -v -0.107894 0.655614 0.114970 -v -0.180849 0.615780 0.224155 -v -0.248085 0.551093 0.324780 -v -0.307017 0.464039 0.412978 -v -0.355382 0.357963 0.485361 -v -0.391320 0.236942 0.539146 -v -0.413450 0.105627 0.572267 -v -0.420923 -0.030936 0.583450 -v -0.413450 -0.167499 0.572267 -v -0.391320 -0.298814 0.539146 -v -0.355382 -0.419835 0.485361 -v -0.307017 -0.525911 0.412978 -v -0.248085 -0.612965 0.324780 -v -0.180849 -0.677652 0.224155 -v -0.107894 -0.717486 0.114970 -v -0.128589 0.655614 0.097987 -v -0.221443 0.615780 0.190841 -v -0.307017 0.551093 0.276415 -v -0.382024 0.464039 0.351422 -v -0.443580 0.357963 0.412978 -v -0.489321 0.236942 0.458719 -v -0.517488 0.105627 0.486886 -v -0.526999 -0.030936 0.496396 -v -0.517488 -0.167499 0.486886 -v -0.489321 -0.298814 0.458719 -v -0.443580 -0.419835 0.412978 -v -0.382024 -0.525911 0.351422 -v -0.307017 -0.612965 0.276415 -v -0.221443 -0.677652 0.190841 -v -0.128589 -0.717486 0.097987 -v -0.145572 0.655614 0.077293 -v -0.254757 0.615780 0.150247 -v -0.355382 0.551093 0.217483 -v -0.443580 0.464039 0.276415 -v -0.515963 0.357963 0.324780 -v -0.569748 0.236942 0.360718 -v -0.602869 0.105627 0.382848 -v -0.614052 -0.030936 0.390321 -v -0.602869 -0.167499 0.382848 -v -0.569748 -0.298814 0.360718 -v -0.515963 -0.419835 0.324780 -v -0.443580 -0.525911 0.276415 -v -0.355382 -0.612965 0.217483 -v -0.254757 -0.677652 0.150247 -v -0.145572 -0.717486 0.077293 -v -0.032024 0.669064 0.001422 -v -0.158192 0.655614 0.053683 -v -0.279511 0.615780 0.103935 -v -0.391320 0.551093 0.150247 -v -0.489321 0.464039 0.190841 -v -0.569748 0.357963 0.224155 -v -0.629511 0.236942 0.248909 -v -0.666313 0.105627 0.264153 -v -0.678739 -0.030936 0.269300 -v -0.666313 -0.167499 0.264153 -v -0.629511 -0.298814 0.248909 -v -0.569748 -0.419835 0.224155 -v -0.489321 -0.525911 0.190841 -v -0.391320 -0.612965 0.150247 -v -0.279511 -0.677652 0.103935 -v -0.158192 -0.717486 0.053683 -v -0.165963 0.655614 0.028064 -v -0.294755 0.615780 0.053683 -v -0.413450 0.551093 0.077292 -v -0.517488 0.464039 0.097987 -v -0.602869 0.357963 0.114970 -v -0.666313 0.236942 0.127590 -v -0.705382 0.105627 0.135361 -v -0.718573 -0.030936 0.137985 -v -0.705382 -0.167499 0.135361 -v -0.666313 -0.298814 0.127590 -v -0.602869 -0.419835 0.114970 -v -0.517488 -0.525911 0.097987 -v -0.413450 -0.612965 0.077292 -v -0.294755 -0.677652 0.053683 -v -0.165963 -0.717486 0.028064 -v -0.168587 0.655614 0.001422 -v -0.299902 0.615780 0.001422 -v -0.420923 0.551093 0.001422 -v -0.526999 0.464039 0.001422 -v -0.614052 0.357963 0.001422 -v -0.678739 0.236942 0.001422 -v -0.718573 0.105627 0.001422 -v -0.732023 -0.030936 0.001422 -v -0.718573 -0.167499 0.001422 -v -0.678740 -0.298814 0.001422 -v -0.614052 -0.419835 0.001422 -v -0.526999 -0.525911 0.001422 -v -0.420923 -0.612965 0.001422 -v -0.299902 -0.677652 0.001422 -v -0.168587 -0.717486 0.001422 -v -0.165963 0.655614 -0.025220 -v -0.294755 0.615780 -0.050838 -v -0.413450 0.551093 -0.074448 -v -0.517488 0.464039 -0.095143 -v -0.602869 0.357963 -0.112126 -v -0.666313 0.236942 -0.124746 -v -0.705381 0.105627 -0.132517 -v -0.718573 -0.030936 -0.135141 -v -0.705381 -0.167499 -0.132517 -v -0.666313 -0.298814 -0.124746 -v -0.602869 -0.419835 -0.112126 -v -0.517488 -0.525911 -0.095143 -v -0.413450 -0.612965 -0.074448 -v -0.294755 -0.677652 -0.050838 -v -0.165963 -0.717486 -0.025220 -v -0.158192 0.655614 -0.050838 -v -0.279511 0.615780 -0.101091 -v -0.391320 0.551093 -0.147403 -v -0.489321 0.464039 -0.187997 -v -0.569748 0.357963 -0.221311 -v -0.629511 0.236942 -0.246065 -v -0.666313 0.105627 -0.261309 -v -0.678739 -0.030936 -0.266456 -v -0.666313 -0.167499 -0.261309 -v -0.629511 -0.298814 -0.246065 -v -0.569748 -0.419835 -0.221311 -v -0.489321 -0.525911 -0.187997 -v -0.391320 -0.612965 -0.147403 -v -0.279511 -0.677652 -0.101091 -v -0.158192 -0.717486 -0.050838 -v -0.145572 0.655614 -0.074448 -v -0.254757 0.615780 -0.147403 -v -0.355382 0.551093 -0.214639 -v -0.443580 0.464039 -0.273571 -v -0.515963 0.357963 -0.321936 -v -0.569748 0.236942 -0.357874 -v -0.602869 0.105627 -0.380004 -v -0.614052 -0.030936 -0.387477 -v -0.602869 -0.167499 -0.380004 -v -0.569748 -0.298814 -0.357874 -v -0.515963 -0.419835 -0.321936 -v -0.443580 -0.525911 -0.273571 -v -0.355382 -0.612965 -0.214639 -v -0.254757 -0.677652 -0.147403 -v -0.145572 -0.717486 -0.074448 -v -0.128589 0.655614 -0.095143 -v -0.221443 0.615780 -0.187997 -v -0.307017 0.551093 -0.273571 -v -0.382024 0.464039 -0.348578 -v -0.443580 0.357963 -0.410134 -v -0.489321 0.236942 -0.455875 -v -0.517488 0.105627 -0.484042 -v -0.526998 -0.030936 -0.493552 -v -0.517488 -0.167499 -0.484042 -v -0.489321 -0.298814 -0.455875 -v -0.443580 -0.419835 -0.410134 -v -0.382024 -0.525911 -0.348578 -v -0.307017 -0.612965 -0.273571 -v -0.221443 -0.677652 -0.187997 -v -0.128589 -0.717486 -0.095143 -v -0.107894 0.655614 -0.112126 -v -0.180849 0.615780 -0.221311 -v -0.248085 0.551093 -0.321936 -v -0.307017 0.464039 -0.410134 -v -0.355382 0.357963 -0.482517 -v -0.391320 0.236942 -0.536302 -v -0.413450 0.105627 -0.569423 -v -0.420922 -0.030936 -0.580606 -v -0.413450 -0.167499 -0.569423 -v -0.391320 -0.298814 -0.536302 -v -0.355382 -0.419835 -0.482517 -v -0.307017 -0.525911 -0.410134 -v -0.248085 -0.612965 -0.321936 -v -0.180849 -0.677652 -0.221311 -v -0.107894 -0.717486 -0.112126 -v -0.084284 0.655614 -0.124746 -v -0.134537 0.615780 -0.246065 -v -0.180849 0.551093 -0.357874 -v -0.221443 0.464039 -0.455875 -v -0.254757 0.357963 -0.536302 -v -0.279511 0.236942 -0.596065 -v -0.294755 0.105627 -0.632867 -v -0.299902 -0.030936 -0.645293 -v -0.294755 -0.167499 -0.632867 -v -0.279511 -0.298814 -0.596065 -v -0.254757 -0.419835 -0.536302 -v -0.221443 -0.525911 -0.455875 -v -0.180849 -0.612965 -0.357874 -v -0.134537 -0.677652 -0.246065 -v -0.084284 -0.717486 -0.124746 -v -0.058666 0.655614 -0.132517 -v -0.084284 0.615780 -0.261309 -v -0.107894 0.551093 -0.380004 -v -0.128589 0.464039 -0.484042 -v -0.145572 0.357963 -0.569423 -v -0.158192 0.236942 -0.632867 -v -0.165963 0.105627 -0.671935 -v -0.168587 -0.030936 -0.685127 -v -0.165963 -0.167499 -0.671935 -v -0.158192 -0.298814 -0.632867 -v -0.145572 -0.419835 -0.569423 -v -0.128589 -0.525911 -0.484042 -v -0.107894 -0.612965 -0.380004 -v -0.084284 -0.677652 -0.261309 -v -0.058666 -0.717486 -0.132517 -v -0.032024 -0.030936 -0.698577 -v -0.032024 -0.167499 -0.685127 -v -0.032024 -0.298814 -0.645293 -v -0.032024 -0.677652 -0.266456 -v -0.032024 -0.717486 -0.135141 -v -0.032024 -0.730936 0.001422 -vt 0.750000 0.375000 -vt 0.750000 0.437500 -vt 0.718750 0.437500 -vt 0.718750 0.375000 -vt 0.750000 0.812500 -vt 0.750000 0.875000 -vt 0.718750 0.875000 -vt 0.718750 0.812500 -vt 0.750000 0.312500 -vt 0.718750 0.312500 -vt 0.750000 0.750000 -vt 0.718750 0.750000 -vt 0.750000 0.250000 -vt 0.718750 0.250000 -vt 0.750000 0.687500 -vt 0.718750 0.687500 -vt 0.750000 0.187500 -vt 0.718750 0.187500 -vt 0.750000 0.625000 -vt 0.718750 0.625000 -vt 0.750000 0.125000 -vt 0.718750 0.125000 -vt 0.750000 0.562500 -vt 0.718750 0.562500 -vt 0.750000 0.062500 -vt 0.718750 0.062500 -vt 0.750000 0.500000 -vt 0.718750 0.500000 -vt 0.750000 0.937500 -vt 0.734375 1.000000 -vt 0.718750 0.937500 -vt 0.734375 0.000000 -vt 0.703125 1.000000 -vt 0.687500 0.937500 -vt 0.703125 0.000000 -vt 0.687500 0.062500 -vt 0.687500 0.500000 -vt 0.687500 0.437500 -vt 0.687500 0.875000 -vt 0.687500 0.375000 -vt 0.687500 0.812500 -vt 0.687500 0.312500 -vt 0.687500 0.750000 -vt 0.687500 0.250000 -vt 0.687500 0.687500 -vt 0.687500 0.187500 -vt 0.687500 0.625000 -vt 0.687500 0.125000 -vt 0.687500 0.562500 -vt 0.656250 0.750000 -vt 0.656250 0.687500 -vt 0.656250 0.250000 -vt 0.656250 0.187500 -vt 0.656250 0.625000 -vt 0.656250 0.125000 -vt 0.656250 0.562500 -vt 0.656250 0.062500 -vt 0.656250 0.500000 -vt 0.671875 1.000000 -vt 0.656250 0.937500 -vt 0.671875 0.000000 -vt 0.656250 0.437500 -vt 0.656250 0.875000 -vt 0.656250 0.375000 -vt 0.656250 0.812500 -vt 0.656250 0.312500 -vt 0.625000 0.500000 -vt 0.625000 0.437500 -vt 0.625000 0.937500 -vt 0.625000 0.875000 -vt 0.625000 0.375000 -vt 0.625000 0.812500 -vt 0.625000 0.312500 -vt 0.625000 0.750000 -vt 0.625000 0.250000 -vt 0.625000 0.687500 -vt 0.625000 0.187500 -vt 0.625000 0.625000 -vt 0.625000 0.125000 -vt 0.625000 0.562500 -vt 0.625000 0.062500 -vt 0.640625 1.000000 -vt 0.640625 0.000000 -vt 0.593750 0.250000 -vt 0.593750 0.187500 -vt 0.593750 0.687500 -vt 0.593750 0.625000 -vt 0.593750 0.125000 -vt 0.593750 0.562500 -vt 0.593750 0.062500 -vt 0.593750 0.500000 -vt 0.609375 1.000000 -vt 0.593750 0.937500 -vt 0.609375 0.000000 -vt 0.593750 0.437500 -vt 0.593750 0.875000 -vt 0.593750 0.375000 -vt 0.593750 0.812500 -vt 0.593750 0.312500 -vt 0.593750 0.750000 -vt 0.562500 0.437500 -vt 0.562500 0.375000 -vt 0.562500 0.875000 -vt 0.562500 0.812500 -vt 0.562500 0.312500 -vt 0.562500 0.750000 -vt 0.562500 0.250000 -vt 0.562500 0.687500 -vt 0.562500 0.187500 -vt 0.562500 0.625000 -vt 0.562500 0.125000 -vt 0.562500 0.562500 -vt 0.562500 0.062500 -vt 0.562500 0.500000 -vt 0.578125 1.000000 -vt 0.562500 0.937500 -vt 0.578125 0.000000 -vt 0.531250 0.187500 -vt 0.531250 0.125000 -vt 0.531250 0.625000 -vt 0.531250 0.562500 -vt 0.531250 0.062500 -vt 0.531250 0.500000 -vt 0.546875 1.000000 -vt 0.531250 0.937500 -vt 0.546875 0.000000 -vt 0.531250 0.437500 -vt 0.531250 0.875000 -vt 0.531250 0.375000 -vt 0.531250 0.812500 -vt 0.531250 0.312500 -vt 0.531250 0.750000 -vt 0.531250 0.250000 -vt 0.531250 0.687500 -vt 0.500000 0.875000 -vt 0.500000 0.812500 -vt 0.500000 0.375000 -vt 0.500000 0.312500 -vt 0.500000 0.750000 -vt 0.500000 0.250000 -vt 0.500000 0.687500 -vt 0.500000 0.187500 -vt 0.500000 0.625000 -vt 0.500000 0.125000 -vt 0.500000 0.562500 -vt 0.500000 0.062500 -vt 0.500000 0.500000 -vt 0.515625 1.000000 -vt 0.500000 0.937500 -vt 0.515625 0.000000 -vt 0.500000 0.437500 -vt 0.468750 0.625000 -vt 0.468750 0.562500 -vt 0.468750 0.125000 -vt 0.468750 0.062500 -vt 0.468750 0.500000 -vt 0.484375 1.000000 -vt 0.468750 0.937500 -vt 0.484375 0.000000 -vt 0.468750 0.437500 -vt 0.468750 0.875000 -vt 0.468750 0.375000 -vt 0.468750 0.812500 -vt 0.468750 0.312500 -vt 0.468750 0.750000 -vt 0.468750 0.250000 -vt 0.468750 0.687500 -vt 0.468750 0.187500 -vt 0.437500 0.375000 -vt 0.437500 0.312500 -vt 0.437500 0.812500 -vt 0.437500 0.750000 -vt 0.437500 0.250000 -vt 0.437500 0.687500 -vt 0.437500 0.187500 -vt 0.437500 0.625000 -vt 0.437500 0.125000 -vt 0.437500 0.562500 -vt 0.437500 0.062500 -vt 0.437500 0.500000 -vt 0.453125 1.000000 -vt 0.437500 0.937500 -vt 0.453125 0.000000 -vt 0.437500 0.437500 -vt 0.437500 0.875000 -vt 0.406250 0.125000 -vt 0.406250 0.062500 -vt 0.406250 0.562500 -vt 0.406250 0.500000 -vt 0.421875 1.000000 -vt 0.406250 0.937500 -vt 0.421875 0.000000 -vt 0.406250 0.437500 -vt 0.406250 0.875000 -vt 0.406250 0.375000 -vt 0.406250 0.812500 -vt 0.406250 0.312500 -vt 0.406250 0.750000 -vt 0.406250 0.250000 -vt 0.406250 0.687500 -vt 0.406250 0.187500 -vt 0.406250 0.625000 -vt 0.375000 0.812500 -vt 0.375000 0.750000 -vt 0.375000 0.312500 -vt 0.375000 0.250000 -vt 0.375000 0.687500 -vt 0.375000 0.187500 -vt 0.375000 0.625000 -vt 0.375000 0.125000 -vt 0.375000 0.562500 -vt 0.375000 0.062500 -vt 0.375000 0.500000 -vt 0.390625 1.000000 -vt 0.375000 0.937500 -vt 0.390625 0.000000 -vt 0.375000 0.437500 -vt 0.375000 0.875000 -vt 0.375000 0.375000 -vt 0.343750 0.562500 -vt 0.343750 0.500000 -vt 0.359375 1.000000 -vt 0.343750 0.937500 -vt 0.359375 0.000000 -vt 0.343750 0.062500 -vt 0.343750 0.437500 -vt 0.343750 0.875000 -vt 0.343750 0.375000 -vt 0.343750 0.812500 -vt 0.343750 0.312500 -vt 0.343750 0.750000 -vt 0.343750 0.250000 -vt 0.343750 0.687500 -vt 0.343750 0.187500 -vt 0.343750 0.625000 -vt 0.343750 0.125000 -vt 0.312500 0.312500 -vt 0.312500 0.250000 -vt 0.312500 0.750000 -vt 0.312500 0.687500 -vt 0.312500 0.187500 -vt 0.312500 0.625000 -vt 0.312500 0.125000 -vt 0.312500 0.562500 -vt 0.312500 0.062500 -vt 0.312500 0.500000 -vt 0.328125 1.000000 -vt 0.312500 0.937500 -vt 0.328125 0.000000 -vt 0.312500 0.437500 -vt 0.312500 0.875000 -vt 0.312500 0.375000 -vt 0.312500 0.812500 -vt 0.296875 0.000000 -vt 0.281250 0.062500 -vt 0.281250 0.500000 -vt 0.281250 0.437500 -vt 0.281250 0.937500 -vt 0.281250 0.875000 -vt 0.281250 0.375000 -vt 0.281250 0.812500 -vt 0.281250 0.312500 -vt 0.281250 0.750000 -vt 0.281250 0.250000 -vt 0.281250 0.687500 -vt 0.281250 0.187500 -vt 0.281250 0.625000 -vt 0.281250 0.125000 -vt 0.281250 0.562500 -vt 0.296875 1.000000 -vt 0.250000 0.250000 -vt 0.250000 0.187500 -vt 0.250000 0.687500 -vt 0.250000 0.625000 -vt 0.250000 0.125000 -vt 0.250000 0.562500 -vt 0.250000 0.062500 -vt 0.250000 0.500000 -vt 0.265625 1.000000 -vt 0.250000 0.937500 -vt 0.265625 0.000000 -vt 0.250000 0.437500 -vt 0.250000 0.875000 -vt 0.250000 0.375000 -vt 0.250000 0.812500 -vt 0.250000 0.312500 -vt 0.250000 0.750000 -vt 0.218750 0.937500 -vt 0.218750 0.875000 -vt 0.218750 0.437500 -vt 0.218750 0.375000 -vt 0.218750 0.812500 -vt 0.218750 0.312500 -vt 0.218750 0.750000 -vt 0.218750 0.250000 -vt 0.218750 0.687500 -vt 0.218750 0.187500 -vt 0.218750 0.625000 -vt 0.218750 0.125000 -vt 0.218750 0.562500 -vt 0.218750 0.062500 -vt 0.218750 0.500000 -vt 0.234375 1.000000 -vt 0.234375 0.000000 -vt 0.187500 0.687500 -vt 0.187500 0.625000 -vt 0.187500 0.187500 -vt 0.187500 0.125000 -vt 0.187500 0.562500 -vt 0.187500 0.062500 -vt 0.187500 0.500000 -vt 0.203125 1.000000 -vt 0.187500 0.937500 -vt 0.203125 0.000000 -vt 0.187500 0.437500 -vt 0.187500 0.875000 -vt 0.187500 0.375000 -vt 0.187500 0.812500 -vt 0.187500 0.312500 -vt 0.187500 0.750000 -vt 0.187500 0.250000 -vt 0.156250 0.437500 -vt 0.156250 0.375000 -vt 0.156250 0.875000 -vt 0.156250 0.812500 -vt 0.156250 0.312500 -vt 0.156250 0.750000 -vt 0.156250 0.250000 -vt 0.156250 0.687500 -vt 0.156250 0.187500 -vt 0.156250 0.625000 -vt 0.156250 0.125000 -vt 0.156250 0.562500 -vt 0.156250 0.062500 -vt 0.156250 0.500000 -vt 0.171875 1.000000 -vt 0.156250 0.937500 -vt 0.171875 0.000000 -vt 0.125000 0.187500 -vt 0.125000 0.125000 -vt 0.125000 0.625000 -vt 0.125000 0.562500 -vt 0.125000 0.062500 -vt 0.125000 0.500000 -vt 0.140625 1.000000 -vt 0.125000 0.937500 -vt 0.140625 0.000000 -vt 0.125000 0.437500 -vt 0.125000 0.875000 -vt 0.125000 0.375000 -vt 0.125000 0.812500 -vt 0.125000 0.312500 -vt 0.125000 0.750000 -vt 0.125000 0.250000 -vt 0.125000 0.687500 -vt 0.093750 0.875000 -vt 0.093750 0.812500 -vt 0.093750 0.375000 -vt 0.093750 0.312500 -vt 0.093750 0.750000 -vt 0.093750 0.250000 -vt 0.093750 0.687500 -vt 0.093750 0.187500 -vt 0.093750 0.625000 -vt 0.093750 0.125000 -vt 0.093750 0.562500 -vt 0.093750 0.062500 -vt 0.093750 0.500000 -vt 0.109375 1.000000 -vt 0.093750 0.937500 -vt 0.109375 0.000000 -vt 0.093750 0.437500 -vt 0.062500 0.625000 -vt 0.062500 0.562500 -vt 0.062500 0.125000 -vt 0.062500 0.062500 -vt 0.062500 0.500000 -vt 0.078125 1.000000 -vt 0.062500 0.937500 -vt 0.078125 0.000000 -vt 0.062500 0.437500 -vt 0.062500 0.875000 -vt 0.062500 0.375000 -vt 0.062500 0.812500 -vt 0.062500 0.312500 -vt 0.062500 0.750000 -vt 0.062500 0.250000 -vt 0.062500 0.687500 -vt 0.062500 0.187500 -vt 0.031250 0.375000 -vt 0.031250 0.312500 -vt 0.031250 0.812500 -vt 0.031250 0.750000 -vt 0.031250 0.250000 -vt 0.031250 0.687500 -vt 0.031250 0.187500 -vt 0.031250 0.625000 -vt 0.031250 0.125000 -vt 0.031250 0.562500 -vt 0.031250 0.062500 -vt 0.031250 0.500000 -vt 0.046875 1.000000 -vt 0.031250 0.937500 -vt 0.046875 0.000000 -vt 0.031250 0.437500 -vt 0.031250 0.875000 -vt 0.000000 0.125000 -vt 0.000000 0.062500 -vt 0.000000 0.562500 -vt 0.000000 0.500000 -vt 0.015625 1.000000 -vt 0.000000 0.937500 -vt 0.015625 0.000000 -vt 0.000000 0.437500 -vt 0.000000 0.875000 -vt 0.000000 0.375000 -vt 0.000000 0.812500 -vt 0.000000 0.312500 -vt 0.000000 0.750000 -vt 0.000000 0.250000 -vt 0.000000 0.687500 -vt 0.000000 0.187500 -vt 0.000000 0.625000 -vt 1.000000 0.250000 -vt 1.000000 0.312500 -vt 0.968750 0.312500 -vt 0.968750 0.250000 -vt 1.000000 0.687500 -vt 1.000000 0.750000 -vt 0.968750 0.750000 -vt 0.968750 0.687500 -vt 1.000000 0.187500 -vt 0.968750 0.187500 -vt 1.000000 0.625000 -vt 0.968750 0.625000 -vt 1.000000 0.125000 -vt 0.968750 0.125000 -vt 1.000000 0.562500 -vt 0.968750 0.562500 -vt 1.000000 0.062500 -vt 0.968750 0.062500 -vt 1.000000 0.500000 -vt 0.968750 0.500000 -vt 1.000000 0.937500 -vt 0.984375 1.000000 -vt 0.968750 0.937500 -vt 0.984375 0.000000 -vt 1.000000 0.437500 -vt 0.968750 0.437500 -vt 1.000000 0.875000 -vt 0.968750 0.875000 -vt 1.000000 0.375000 -vt 0.968750 0.375000 -vt 1.000000 0.812500 -vt 0.968750 0.812500 -vt 0.953125 1.000000 -vt 0.937500 0.937500 -vt 0.953125 0.000000 -vt 0.937500 0.062500 -vt 0.937500 0.500000 -vt 0.937500 0.437500 -vt 0.937500 0.875000 -vt 0.937500 0.375000 -vt 0.937500 0.812500 -vt 0.937500 0.312500 -vt 0.937500 0.750000 -vt 0.937500 0.250000 -vt 0.937500 0.687500 -vt 0.937500 0.187500 -vt 0.937500 0.625000 -vt 0.937500 0.125000 -vt 0.937500 0.562500 -vt 0.906250 0.750000 -vt 0.906250 0.687500 -vt 0.906250 0.250000 -vt 0.906250 0.187500 -vt 0.906250 0.625000 -vt 0.906250 0.125000 -vt 0.906250 0.562500 -vt 0.906250 0.062500 -vt 0.906250 0.500000 -vt 0.921875 1.000000 -vt 0.906250 0.937500 -vt 0.921875 0.000000 -vt 0.906250 0.437500 -vt 0.906250 0.875000 -vt 0.906250 0.375000 -vt 0.906250 0.812500 -vt 0.906250 0.312500 -vt 0.875000 0.500000 -vt 0.875000 0.437500 -vt 0.875000 0.937500 -vt 0.875000 0.875000 -vt 0.875000 0.375000 -vt 0.875000 0.812500 -vt 0.875000 0.312500 -vt 0.875000 0.750000 -vt 0.875000 0.250000 -vt 0.875000 0.687500 -vt 0.875000 0.187500 -vt 0.875000 0.625000 -vt 0.875000 0.125000 -vt 0.875000 0.562500 -vt 0.875000 0.062500 -vt 0.890625 1.000000 -vt 0.890625 0.000000 -vt 0.843750 0.250000 -vt 0.843750 0.187500 -vt 0.843750 0.687500 -vt 0.843750 0.625000 -vt 0.843750 0.125000 -vt 0.843750 0.562500 -vt 0.843750 0.062500 -vt 0.843750 0.500000 -vt 0.859375 1.000000 -vt 0.843750 0.937500 -vt 0.859375 0.000000 -vt 0.843750 0.437500 -vt 0.843750 0.875000 -vt 0.843750 0.375000 -vt 0.843750 0.812500 -vt 0.843750 0.312500 -vt 0.843750 0.750000 -vt 0.812500 0.937500 -vt 0.812500 0.875000 -vt 0.812500 0.437500 -vt 0.812500 0.375000 -vt 0.812500 0.812500 -vt 0.812500 0.312500 -vt 0.812500 0.750000 -vt 0.812500 0.250000 -vt 0.812500 0.687500 -vt 0.812500 0.187500 -vt 0.812500 0.625000 -vt 0.812500 0.125000 -vt 0.812500 0.562500 -vt 0.812500 0.062500 -vt 0.812500 0.500000 -vt 0.828125 1.000000 -vt 0.828125 0.000000 -vt 0.781250 0.687500 -vt 0.781250 0.625000 -vt 0.781250 0.187500 -vt 0.781250 0.125000 -vt 0.781250 0.562500 -vt 0.781250 0.062500 -vt 0.781250 0.500000 -vt 0.796875 1.000000 -vt 0.781250 0.937500 -vt 0.796875 0.000000 -vt 0.781250 0.437500 -vt 0.781250 0.875000 -vt 0.781250 0.375000 -vt 0.781250 0.812500 -vt 0.781250 0.312500 -vt 0.781250 0.750000 -vt 0.781250 0.250000 -vt 0.765625 1.000000 -vt 0.765625 0.000000 -vn 0.0938 -0.2890 -0.9527 -vn 0.0464 0.8810 -0.4709 -vn 0.0865 -0.4696 -0.8786 -vn 0.0624 0.7715 -0.6332 -vn 0.0759 -0.6326 -0.7708 -vn 0.0759 0.6326 -0.7708 -vn 0.0624 -0.7715 -0.6332 -vn 0.0865 0.4696 -0.8786 -vn 0.0464 -0.8810 -0.4709 -vn 0.0938 0.2890 -0.9527 -vn 0.0286 -0.9565 -0.2902 -vn 0.0975 0.0975 -0.9904 -vn 0.0097 0.9951 -0.0980 -vn 0.0097 -0.9951 -0.0980 -vn 0.0975 -0.0975 -0.9904 -vn 0.0286 0.9565 -0.2902 -vn 0.0286 0.9951 -0.0942 -vn 0.0286 -0.9951 -0.0942 -vn 0.2889 -0.0975 -0.9524 -vn 0.0846 0.9565 -0.2790 -vn 0.2779 -0.2890 -0.9161 -vn 0.1374 0.8810 -0.4528 -vn 0.2563 -0.4696 -0.8448 -vn 0.1847 0.7715 -0.6088 -vn 0.2248 -0.6326 -0.7412 -vn 0.2248 0.6326 -0.7412 -vn 0.1847 -0.7715 -0.6088 -vn 0.2563 0.4696 -0.8448 -vn 0.1374 -0.8810 -0.4528 -vn 0.2779 0.2890 -0.9161 -vn 0.0846 -0.9565 -0.2790 -vn 0.2889 0.0975 -0.9524 -vn 0.3651 0.6326 -0.6831 -vn 0.2999 -0.7715 -0.5611 -vn 0.4162 0.4696 -0.7786 -vn 0.2231 -0.8810 -0.4173 -vn 0.4513 0.2890 -0.8443 -vn 0.1374 -0.9565 -0.2571 -vn 0.4691 0.0975 -0.8777 -vn 0.0464 0.9951 -0.0869 -vn 0.0464 -0.9951 -0.0869 -vn 0.4691 -0.0975 -0.8777 -vn 0.1374 0.9565 -0.2571 -vn 0.4513 -0.2890 -0.8443 -vn 0.2230 0.8810 -0.4173 -vn 0.4162 -0.4696 -0.7786 -vn 0.2999 0.7715 -0.5611 -vn 0.3651 -0.6326 -0.6831 -vn 0.6314 -0.0975 -0.7693 -vn 0.1850 0.9565 -0.2254 -vn 0.6073 -0.2890 -0.7400 -vn 0.3002 0.8810 -0.3658 -vn 0.5601 -0.4696 -0.6825 -vn 0.4036 0.7715 -0.4918 -vn 0.4913 -0.6326 -0.5987 -vn 0.4913 0.6326 -0.5987 -vn 0.4036 -0.7715 -0.4918 -vn 0.5601 0.4696 -0.6825 -vn 0.3002 -0.8810 -0.3658 -vn 0.6073 0.2890 -0.7400 -vn 0.1850 -0.9565 -0.2254 -vn 0.6314 0.0975 -0.7693 -vn 0.0625 0.9951 -0.0761 -vn 0.0625 -0.9951 -0.0761 -vn 0.4918 -0.7715 -0.4036 -vn 0.6825 0.4696 -0.5601 -vn 0.3658 -0.8810 -0.3002 -vn 0.7400 0.2890 -0.6073 -vn 0.2254 -0.9565 -0.1850 -vn 0.7693 0.0975 -0.6314 -vn 0.0761 0.9951 -0.0625 -vn 0.0761 -0.9951 -0.0625 -vn 0.7693 -0.0975 -0.6314 -vn 0.2254 0.9565 -0.1850 -vn 0.7400 -0.2890 -0.6073 -vn 0.3658 0.8810 -0.3002 -vn 0.6825 -0.4696 -0.5601 -vn 0.4918 0.7715 -0.4036 -vn 0.5987 -0.6326 -0.4913 -vn 0.5987 0.6326 -0.4913 -vn 0.8443 -0.2890 -0.4513 -vn 0.4173 0.8810 -0.2231 -vn 0.7786 -0.4696 -0.4162 -vn 0.5611 0.7715 -0.2999 -vn 0.6831 -0.6326 -0.3651 -vn 0.6831 0.6326 -0.3651 -vn 0.5611 -0.7715 -0.2999 -vn 0.7786 0.4696 -0.4162 -vn 0.4173 -0.8810 -0.2230 -vn 0.8443 0.2890 -0.4513 -vn 0.2571 -0.9565 -0.1374 -vn 0.8777 0.0975 -0.4691 -vn 0.0869 0.9951 -0.0464 -vn 0.0869 -0.9951 -0.0464 -vn 0.8777 -0.0975 -0.4691 -vn 0.2571 0.9565 -0.1374 -vn 0.4528 -0.8810 -0.1374 -vn 0.9161 0.2890 -0.2779 -vn 0.2790 -0.9565 -0.0846 -vn 0.9524 0.0975 -0.2889 -vn 0.0942 0.9951 -0.0286 -vn 0.0942 -0.9951 -0.0286 -vn 0.9524 -0.0975 -0.2889 -vn 0.2790 0.9565 -0.0846 -vn 0.9161 -0.2890 -0.2779 -vn 0.4528 0.8810 -0.1374 -vn 0.8448 -0.4696 -0.2563 -vn 0.6088 0.7715 -0.1847 -vn 0.7412 -0.6326 -0.2248 -vn 0.7412 0.6326 -0.2248 -vn 0.6088 -0.7715 -0.1847 -vn 0.8448 0.4696 -0.2563 -vn 0.4709 0.8810 -0.0464 -vn 0.8786 -0.4696 -0.0865 -vn 0.6332 0.7715 -0.0624 -vn 0.7708 -0.6326 -0.0759 -vn 0.7708 0.6326 -0.0759 -vn 0.6332 -0.7715 -0.0624 -vn 0.8786 0.4696 -0.0865 -vn 0.4709 -0.8810 -0.0464 -vn 0.9527 0.2890 -0.0938 -vn 0.2902 -0.9565 -0.0286 -vn 0.9904 0.0975 -0.0975 -vn 0.0980 0.9951 -0.0097 -vn 0.0980 -0.9951 -0.0097 -vn 0.9904 -0.0975 -0.0975 -vn 0.2902 0.9565 -0.0286 -vn 0.9527 -0.2890 -0.0938 -vn 0.9527 0.2890 0.0938 -vn 0.2902 -0.9565 0.0286 -vn 0.9904 0.0975 0.0976 -vn 0.0980 0.9951 0.0097 -vn 0.0980 -0.9951 0.0097 -vn 0.9904 -0.0975 0.0976 -vn 0.2902 0.9565 0.0286 -vn 0.9527 -0.2890 0.0938 -vn 0.4709 0.8810 0.0464 -vn 0.8786 -0.4696 0.0865 -vn 0.6332 0.7715 0.0624 -vn 0.7708 -0.6326 0.0759 -vn 0.7708 0.6326 0.0759 -vn 0.6332 -0.7715 0.0624 -vn 0.8786 0.4696 0.0865 -vn 0.4709 -0.8810 0.0464 -vn 0.8448 -0.4696 0.2563 -vn 0.6088 0.7715 0.1847 -vn 0.7412 -0.6326 0.2248 -vn 0.7412 0.6326 0.2248 -vn 0.6088 -0.7715 0.1847 -vn 0.8448 0.4696 0.2563 -vn 0.4528 -0.8810 0.1374 -vn 0.9161 0.2890 0.2779 -vn 0.2790 -0.9565 0.0846 -vn 0.9524 0.0975 0.2889 -vn 0.0942 0.9951 0.0286 -vn 0.0942 -0.9951 0.0286 -vn 0.9524 -0.0975 0.2889 -vn 0.2790 0.9565 0.0846 -vn 0.9161 -0.2890 0.2779 -vn 0.4528 0.8810 0.1374 -vn 0.2571 -0.9565 0.1374 -vn 0.8777 0.0975 0.4691 -vn 0.0869 0.9951 0.0464 -vn 0.0869 -0.9951 0.0464 -vn 0.8777 -0.0975 0.4691 -vn 0.2571 0.9565 0.1374 -vn 0.8443 -0.2890 0.4513 -vn 0.4173 0.8810 0.2231 -vn 0.7786 -0.4696 0.4162 -vn 0.5611 0.7715 0.2999 -vn 0.6831 -0.6326 0.3651 -vn 0.6831 0.6326 0.3651 -vn 0.5611 -0.7715 0.2999 -vn 0.7786 0.4696 0.4162 -vn 0.4173 -0.8810 0.2231 -vn 0.8443 0.2890 0.4513 -vn 0.4918 0.7715 0.4036 -vn 0.5987 -0.6326 0.4913 -vn 0.5987 0.6326 0.4913 -vn 0.4918 -0.7715 0.4036 -vn 0.6825 0.4696 0.5601 -vn 0.3658 -0.8810 0.3002 -vn 0.7400 0.2890 0.6073 -vn 0.2254 -0.9565 0.1850 -vn 0.7693 0.0975 0.6314 -vn 0.0761 0.9951 0.0625 -vn 0.0761 -0.9951 0.0625 -vn 0.7693 -0.0975 0.6314 -vn 0.2254 0.9565 0.1850 -vn 0.7400 -0.2890 0.6073 -vn 0.3658 0.8810 0.3002 -vn 0.6825 -0.4696 0.5601 -vn 0.6314 0.0975 0.7693 -vn 0.0625 0.9951 0.0761 -vn 0.0625 -0.9951 0.0761 -vn 0.6314 -0.0975 0.7693 -vn 0.1850 0.9565 0.2254 -vn 0.6073 -0.2890 0.7400 -vn 0.3002 0.8810 0.3658 -vn 0.5601 -0.4696 0.6825 -vn 0.4036 0.7715 0.4918 -vn 0.4913 -0.6326 0.5987 -vn 0.4913 0.6326 0.5987 -vn 0.4036 -0.7715 0.4918 -vn 0.5601 0.4696 0.6825 -vn 0.3002 -0.8810 0.3658 -vn 0.6073 0.2890 0.7400 -vn 0.1850 -0.9565 0.2254 -vn 0.3651 -0.6326 0.6831 -vn 0.3651 0.6326 0.6831 -vn 0.2999 -0.7715 0.5611 -vn 0.4162 0.4696 0.7786 -vn 0.2230 -0.8810 0.4173 -vn 0.4513 0.2890 0.8443 -vn 0.1374 -0.9565 0.2571 -vn 0.4691 0.0975 0.8777 -vn 0.0464 0.9951 0.0869 -vn 0.0464 -0.9951 0.0869 -vn 0.4691 -0.0975 0.8777 -vn 0.1374 0.9565 0.2571 -vn 0.4513 -0.2890 0.8443 -vn 0.2230 0.8810 0.4173 -vn 0.4162 -0.4696 0.7786 -vn 0.2999 0.7715 0.5611 -vn 0.0286 -0.9951 0.0942 -vn 0.2889 -0.0975 0.9524 -vn 0.0846 0.9565 0.2790 -vn 0.2779 -0.2890 0.9161 -vn 0.1374 0.8810 0.4528 -vn 0.2563 -0.4696 0.8448 -vn 0.1847 0.7715 0.6088 -vn 0.2248 -0.6326 0.7412 -vn 0.2248 0.6326 0.7412 -vn 0.1847 -0.7715 0.6088 -vn 0.2563 0.4696 0.8448 -vn 0.1374 -0.8810 0.4528 -vn 0.2779 0.2890 0.9161 -vn 0.0846 -0.9565 0.2790 -vn 0.2889 0.0975 0.9524 -vn 0.0286 0.9951 0.0942 -vn 0.0624 -0.7715 0.6332 -vn 0.0865 0.4696 0.8786 -vn 0.0464 -0.8810 0.4709 -vn 0.0938 0.2890 0.9527 -vn 0.0286 -0.9565 0.2902 -vn 0.0975 0.0975 0.9904 -vn 0.0097 0.9951 0.0980 -vn 0.0097 -0.9951 0.0980 -vn 0.0975 -0.0975 0.9904 -vn 0.0286 0.9565 0.2902 -vn 0.0938 -0.2890 0.9527 -vn 0.0464 0.8810 0.4709 -vn 0.0865 -0.4696 0.8786 -vn 0.0624 0.7715 0.6332 -vn 0.0759 -0.6326 0.7708 -vn 0.0759 0.6326 0.7708 -vn -0.0286 0.9565 0.2902 -vn -0.0938 -0.2890 0.9527 -vn -0.0464 0.8810 0.4709 -vn -0.0865 -0.4696 0.8786 -vn -0.0624 0.7715 0.6332 -vn -0.0759 -0.6326 0.7708 -vn -0.0759 0.6326 0.7708 -vn -0.0624 -0.7715 0.6332 -vn -0.0865 0.4696 0.8786 -vn -0.0464 -0.8810 0.4709 -vn -0.0938 0.2890 0.9527 -vn -0.0286 -0.9565 0.2902 -vn -0.0976 0.0975 0.9904 -vn -0.0097 0.9951 0.0980 -vn -0.0097 -0.9951 0.0980 -vn -0.0976 -0.0975 0.9904 -vn -0.2563 0.4696 0.8448 -vn -0.1374 -0.8810 0.4528 -vn -0.2779 0.2890 0.9161 -vn -0.0846 -0.9565 0.2790 -vn -0.2889 0.0975 0.9524 -vn -0.0286 0.9951 0.0942 -vn -0.0286 -0.9951 0.0942 -vn -0.2889 -0.0975 0.9524 -vn -0.0846 0.9565 0.2790 -vn -0.2779 -0.2890 0.9161 -vn -0.1374 0.8810 0.4528 -vn -0.2563 -0.4696 0.8448 -vn -0.1847 0.7715 0.6088 -vn -0.2248 -0.6326 0.7412 -vn -0.2248 0.6326 0.7412 -vn -0.1847 -0.7715 0.6088 -vn -0.4513 -0.2890 0.8443 -vn -0.2231 0.8810 0.4173 -vn -0.4162 -0.4696 0.7786 -vn -0.2999 0.7715 0.5611 -vn -0.3651 -0.6326 0.6831 -vn -0.3651 0.6326 0.6831 -vn -0.2999 -0.7715 0.5611 -vn -0.4162 0.4696 0.7786 -vn -0.2231 -0.8810 0.4173 -vn -0.4513 0.2890 0.8443 -vn -0.1374 -0.9565 0.2571 -vn -0.4691 0.0975 0.8777 -vn -0.0464 0.9951 0.0869 -vn -0.0464 -0.9951 0.0869 -vn -0.4691 -0.0975 0.8777 -vn -0.1374 0.9565 0.2571 -vn -0.3002 -0.8810 0.3658 -vn -0.6073 0.2890 0.7400 -vn -0.1850 -0.9565 0.2254 -vn -0.6314 0.0975 0.7693 -vn -0.0625 0.9951 0.0761 -vn -0.0625 -0.9951 0.0761 -vn -0.6314 -0.0975 0.7693 -vn -0.1850 0.9565 0.2254 -vn -0.6073 -0.2890 0.7400 -vn -0.3002 0.8810 0.3658 -vn -0.5601 -0.4696 0.6825 -vn -0.4036 0.7715 0.4918 -vn -0.4913 -0.6326 0.5987 -vn -0.4913 0.6326 0.5987 -vn -0.4036 -0.7715 0.4918 -vn -0.5601 0.4696 0.6825 -vn -0.3658 0.8810 0.3002 -vn -0.6825 -0.4696 0.5601 -vn -0.4918 0.7715 0.4036 -vn -0.5987 -0.6326 0.4913 -vn -0.5987 0.6326 0.4913 -vn -0.4918 -0.7715 0.4036 -vn -0.6825 0.4696 0.5601 -vn -0.3658 -0.8810 0.3002 -vn -0.7400 0.2890 0.6073 -vn -0.2254 -0.9565 0.1850 -vn -0.7693 0.0975 0.6314 -vn -0.0761 0.9951 0.0625 -vn -0.0761 -0.9951 0.0625 -vn -0.7693 -0.0975 0.6314 -vn -0.2254 0.9565 0.1850 -vn -0.7400 -0.2890 0.6073 -vn -0.8443 0.2890 0.4513 -vn -0.2571 -0.9565 0.1374 -vn -0.8777 0.0975 0.4691 -vn -0.0869 0.9951 0.0464 -vn -0.0869 -0.9951 0.0464 -vn -0.8777 -0.0975 0.4691 -vn -0.2571 0.9565 0.1374 -vn -0.8443 -0.2890 0.4513 -vn -0.4173 0.8810 0.2230 -vn -0.7786 -0.4696 0.4162 -vn -0.5611 0.7715 0.2999 -vn -0.6831 -0.6326 0.3651 -vn -0.6831 0.6326 0.3651 -vn -0.5611 -0.7715 0.2999 -vn -0.7786 0.4696 0.4162 -vn -0.4173 -0.8810 0.2230 -vn -0.8448 -0.4696 0.2563 -vn -0.6088 0.7715 0.1847 -vn -0.7412 -0.6326 0.2248 -vn -0.7412 0.6326 0.2248 -vn -0.6088 -0.7715 0.1847 -vn -0.8448 0.4696 0.2563 -vn -0.4528 -0.8810 0.1374 -vn -0.9161 0.2890 0.2779 -vn -0.2790 -0.9565 0.0846 -vn -0.9524 0.0975 0.2889 -vn -0.0942 0.9951 0.0286 -vn -0.0942 -0.9951 0.0286 -vn -0.9524 -0.0975 0.2889 -vn -0.2790 0.9565 0.0846 -vn -0.9161 -0.2890 0.2779 -vn -0.4528 0.8810 0.1374 -vn -0.2902 -0.9565 0.0286 -vn -0.9904 0.0975 0.0975 -vn -0.0980 0.9951 0.0097 -vn -0.0980 -0.9951 0.0097 -vn -0.9904 -0.0975 0.0975 -vn -0.2902 0.9565 0.0286 -vn -0.9527 -0.2890 0.0938 -vn -0.4709 0.8810 0.0464 -vn -0.8786 -0.4696 0.0865 -vn -0.6332 0.7715 0.0624 -vn -0.7708 -0.6326 0.0759 -vn -0.7708 0.6326 0.0759 -vn -0.6332 -0.7715 0.0624 -vn -0.8786 0.4696 0.0865 -vn -0.4709 -0.8810 0.0464 -vn -0.9527 0.2890 0.0938 -vn -0.7708 -0.6326 -0.0759 -vn -0.7708 0.6326 -0.0759 -vn -0.6332 -0.7715 -0.0624 -vn -0.8786 0.4696 -0.0865 -vn -0.4709 -0.8810 -0.0464 -vn -0.9527 0.2890 -0.0938 -vn -0.2902 -0.9565 -0.0286 -vn -0.9904 0.0975 -0.0976 -vn -0.0980 0.9951 -0.0097 -vn -0.0980 -0.9951 -0.0097 -vn -0.9904 -0.0975 -0.0976 -vn -0.2902 0.9565 -0.0286 -vn -0.9527 -0.2890 -0.0938 -vn -0.4709 0.8810 -0.0464 -vn -0.8786 -0.4696 -0.0865 -vn -0.6332 0.7715 -0.0624 -vn -0.0942 0.9951 -0.0286 -vn -0.0942 -0.9951 -0.0286 -vn -0.9524 -0.0975 -0.2889 -vn -0.2790 0.9565 -0.0846 -vn -0.9161 -0.2890 -0.2779 -vn -0.4528 0.8810 -0.1374 -vn -0.8448 -0.4696 -0.2563 -vn -0.6088 0.7715 -0.1847 -vn -0.7412 -0.6326 -0.2248 -vn -0.7412 0.6326 -0.2248 -vn -0.6088 -0.7715 -0.1847 -vn -0.8448 0.4696 -0.2563 -vn -0.4528 -0.8810 -0.1374 -vn -0.9161 0.2890 -0.2779 -vn -0.2790 -0.9565 -0.0846 -vn -0.9524 0.0975 -0.2889 -vn -0.6831 0.6326 -0.3651 -vn -0.5611 -0.7715 -0.2999 -vn -0.7786 0.4696 -0.4162 -vn -0.4173 -0.8810 -0.2231 -vn -0.8443 0.2890 -0.4513 -vn -0.2571 -0.9565 -0.1374 -vn -0.8777 0.0975 -0.4691 -vn -0.0869 0.9951 -0.0464 -vn -0.0869 -0.9951 -0.0464 -vn -0.8777 -0.0975 -0.4691 -vn -0.2571 0.9565 -0.1374 -vn -0.8443 -0.2890 -0.4513 -vn -0.4173 0.8810 -0.2231 -vn -0.7786 -0.4696 -0.4162 -vn -0.5611 0.7715 -0.2999 -vn -0.6831 -0.6326 -0.3651 -vn -0.7693 -0.0975 -0.6314 -vn -0.2254 0.9565 -0.1850 -vn -0.7400 -0.2890 -0.6073 -vn -0.3658 0.8810 -0.3002 -vn -0.6825 -0.4696 -0.5601 -vn -0.4918 0.7715 -0.4036 -vn -0.5987 -0.6326 -0.4913 -vn -0.5987 0.6326 -0.4913 -vn -0.4918 -0.7715 -0.4036 -vn -0.6825 0.4696 -0.5601 -vn -0.3658 -0.8810 -0.3002 -vn -0.7400 0.2890 -0.6073 -vn -0.2254 -0.9565 -0.1850 -vn -0.7693 0.0975 -0.6314 -vn -0.0761 0.9951 -0.0625 -vn -0.0761 -0.9951 -0.0625 -vn -0.4036 -0.7715 -0.4918 -vn -0.5601 0.4696 -0.6825 -vn -0.3002 -0.8810 -0.3658 -vn -0.6073 0.2890 -0.7400 -vn -0.1850 -0.9565 -0.2254 -vn -0.6314 0.0975 -0.7693 -vn -0.0625 0.9951 -0.0761 -vn -0.0625 -0.9951 -0.0761 -vn -0.6314 -0.0975 -0.7693 -vn -0.1850 0.9565 -0.2254 -vn -0.6073 -0.2890 -0.7400 -vn -0.3002 0.8810 -0.3658 -vn -0.5601 -0.4696 -0.6825 -vn -0.4036 0.7715 -0.4918 -vn -0.4913 -0.6326 -0.5987 -vn -0.4913 0.6326 -0.5987 -vn -0.1374 0.9565 -0.2571 -vn -0.4513 -0.2890 -0.8443 -vn -0.2230 0.8810 -0.4173 -vn -0.4162 -0.4696 -0.7786 -vn -0.2999 0.7715 -0.5611 -vn -0.3651 -0.6326 -0.6831 -vn -0.3651 0.6326 -0.6831 -vn -0.2999 -0.7715 -0.5611 -vn -0.4162 0.4696 -0.7786 -vn -0.2230 -0.8810 -0.4173 -vn -0.4513 0.2890 -0.8443 -vn -0.1374 -0.9565 -0.2571 -vn -0.4691 0.0975 -0.8777 -vn -0.0464 0.9951 -0.0869 -vn -0.0464 -0.9951 -0.0869 -vn -0.4691 -0.0975 -0.8777 -vn -0.2563 0.4696 -0.8448 -vn -0.1374 -0.8810 -0.4528 -vn -0.2779 0.2890 -0.9161 -vn -0.0846 -0.9565 -0.2790 -vn -0.2889 0.0975 -0.9524 -vn -0.0286 0.9951 -0.0942 -vn -0.0286 -0.9951 -0.0942 -vn -0.2889 -0.0975 -0.9524 -vn -0.0846 0.9565 -0.2790 -vn -0.2779 -0.2890 -0.9161 -vn -0.1374 0.8810 -0.4528 -vn -0.2563 -0.4696 -0.8448 -vn -0.1847 0.7715 -0.6088 -vn -0.2248 -0.6326 -0.7412 -vn -0.2248 0.6326 -0.7412 -vn -0.1847 -0.7715 -0.6088 -vn -0.0938 -0.2890 -0.9527 -vn -0.0464 0.8810 -0.4709 -vn -0.0865 -0.4696 -0.8786 -vn -0.0624 0.7715 -0.6332 -vn -0.0759 -0.6326 -0.7708 -vn -0.0759 0.6326 -0.7708 -vn -0.0624 -0.7715 -0.6332 -vn -0.0865 0.4696 -0.8786 -vn -0.0464 -0.8810 -0.4709 -vn -0.0938 0.2890 -0.9527 -vn -0.0286 -0.9565 -0.2902 -vn -0.0976 0.0975 -0.9904 -vn -0.0097 0.9951 -0.0980 -vn -0.0097 -0.9951 -0.0980 -vn -0.0975 -0.0975 -0.9904 -vn -0.0286 0.9565 -0.2902 -usemtl Standard1 -s off -f 479/1/1 478/2/1 19/3/1 20/4/1 -f 3/5/2 2/6/2 12/7/2 13/8/2 -f 8/9/3 479/1/3 20/4/3 21/10/3 -f 4/11/4 3/5/4 13/8/4 14/12/4 -f 9/13/5 8/9/5 21/10/5 22/14/5 -f 5/15/6 4/11/6 14/12/6 15/16/6 -f 10/17/7 9/13/7 22/14/7 23/18/7 -f 6/19/8 5/15/8 15/16/8 16/20/8 -f 480/21/9 10/17/9 23/18/9 24/22/9 -f 7/23/10 6/19/10 16/20/10 17/24/10 -f 481/25/11 480/21/11 24/22/11 25/26/11 -f 477/27/12 7/23/12 17/24/12 18/28/12 -f 1/29/13 326/30/13 11/31/13 -f 482/32/14 481/25/14 25/26/14 -f 478/2/15 477/27/15 18/28/15 19/3/15 -f 2/6/16 1/29/16 11/31/16 12/7/16 -f 11/31/17 326/33/17 26/34/17 -f 482/35/18 25/26/18 40/36/18 -f 19/3/19 18/28/19 33/37/19 34/38/19 -f 12/7/20 11/31/20 26/34/20 27/39/20 -f 20/4/21 19/3/21 34/38/21 35/40/21 -f 13/8/22 12/7/22 27/39/22 28/41/22 -f 21/10/23 20/4/23 35/40/23 36/42/23 -f 14/12/24 13/8/24 28/41/24 29/43/24 -f 22/14/25 21/10/25 36/42/25 37/44/25 -f 15/16/26 14/12/26 29/43/26 30/45/26 -f 23/18/27 22/14/27 37/44/27 38/46/27 -f 16/20/28 15/16/28 30/45/28 31/47/28 -f 24/22/29 23/18/29 38/46/29 39/48/29 -f 17/24/30 16/20/30 31/47/30 32/49/30 -f 25/26/31 24/22/31 39/48/31 40/36/31 -f 18/28/32 17/24/32 32/49/32 33/37/32 -f 30/45/33 29/43/33 44/50/33 45/51/33 -f 38/46/34 37/44/34 52/52/34 53/53/34 -f 31/47/35 30/45/35 45/51/35 46/54/35 -f 39/48/36 38/46/36 53/53/36 54/55/36 -f 32/49/37 31/47/37 46/54/37 47/56/37 -f 40/36/38 39/48/38 54/55/38 55/57/38 -f 33/37/39 32/49/39 47/56/39 48/58/39 -f 26/34/40 326/59/40 41/60/40 -f 482/61/41 40/36/41 55/57/41 -f 34/38/42 33/37/42 48/58/42 49/62/42 -f 27/39/43 26/34/43 41/60/43 42/63/43 -f 35/40/44 34/38/44 49/62/44 50/64/44 -f 28/41/45 27/39/45 42/63/45 43/65/45 -f 36/42/46 35/40/46 50/64/46 51/66/46 -f 29/43/47 28/41/47 43/65/47 44/50/47 -f 37/44/48 36/42/48 51/66/48 52/52/48 -f 49/62/49 48/58/49 63/67/49 64/68/49 -f 42/63/50 41/60/50 56/69/50 57/70/50 -f 50/64/51 49/62/51 64/68/51 65/71/51 -f 43/65/52 42/63/52 57/70/52 58/72/52 -f 51/66/53 50/64/53 65/71/53 66/73/53 -f 44/50/54 43/65/54 58/72/54 59/74/54 -f 52/52/55 51/66/55 66/73/55 67/75/55 -f 45/51/56 44/50/56 59/74/56 60/76/56 -f 53/53/57 52/52/57 67/75/57 68/77/57 -f 46/54/58 45/51/58 60/76/58 61/78/58 -f 54/55/59 53/53/59 68/77/59 69/79/59 -f 47/56/60 46/54/60 61/78/60 62/80/60 -f 55/57/61 54/55/61 69/79/61 70/81/61 -f 48/58/62 47/56/62 62/80/62 63/67/62 -f 41/60/63 326/82/63 56/69/63 -f 482/83/64 55/57/64 70/81/64 -f 68/77/65 67/75/65 82/84/65 83/85/65 -f 61/78/66 60/76/66 75/86/66 76/87/66 -f 69/79/67 68/77/67 83/85/67 84/88/67 -f 62/80/68 61/78/68 76/87/68 77/89/68 -f 70/81/69 69/79/69 84/88/69 85/90/69 -f 63/67/70 62/80/70 77/89/70 78/91/70 -f 56/69/71 326/92/71 71/93/71 -f 482/94/72 70/81/72 85/90/72 -f 64/68/73 63/67/73 78/91/73 79/95/73 -f 57/70/74 56/69/74 71/93/74 72/96/74 -f 65/71/75 64/68/75 79/95/75 80/97/75 -f 58/72/76 57/70/76 72/96/76 73/98/76 -f 66/73/77 65/71/77 80/97/77 81/99/77 -f 59/74/78 58/72/78 73/98/78 74/100/78 -f 67/75/79 66/73/79 81/99/79 82/84/79 -f 60/76/80 59/74/80 74/100/80 75/86/80 -f 80/97/81 79/95/81 94/101/81 95/102/81 -f 73/98/82 72/96/82 87/103/82 88/104/82 -f 81/99/83 80/97/83 95/102/83 96/105/83 -f 74/100/84 73/98/84 88/104/84 89/106/84 -f 82/84/85 81/99/85 96/105/85 97/107/85 -f 75/86/86 74/100/86 89/106/86 90/108/86 -f 83/85/87 82/84/87 97/107/87 98/109/87 -f 76/87/88 75/86/88 90/108/88 91/110/88 -f 84/88/89 83/85/89 98/109/89 99/111/89 -f 77/89/90 76/87/90 91/110/90 92/112/90 -f 85/90/91 84/88/91 99/111/91 100/113/91 -f 78/91/92 77/89/92 92/112/92 93/114/92 -f 71/93/93 326/115/93 86/116/93 -f 482/117/94 85/90/94 100/113/94 -f 79/95/95 78/91/95 93/114/95 94/101/95 -f 72/96/96 71/93/96 86/116/96 87/103/96 -f 99/111/97 98/109/97 113/118/97 114/119/97 -f 92/112/98 91/110/98 106/120/98 107/121/98 -f 100/113/99 99/111/99 114/119/99 115/122/99 -f 93/114/100 92/112/100 107/121/100 108/123/100 -f 86/116/101 326/124/101 101/125/101 -f 482/126/102 100/113/102 115/122/102 -f 94/101/103 93/114/103 108/123/103 109/127/103 -f 87/103/104 86/116/104 101/125/104 102/128/104 -f 95/102/105 94/101/105 109/127/105 110/129/105 -f 88/104/106 87/103/106 102/128/106 103/130/106 -f 96/105/107 95/102/107 110/129/107 111/131/107 -f 89/106/108 88/104/108 103/130/108 104/132/108 -f 97/107/109 96/105/109 111/131/109 112/133/109 -f 90/108/110 89/106/110 104/132/110 105/134/110 -f 98/109/111 97/107/111 112/133/111 113/118/111 -f 91/110/112 90/108/112 105/134/112 106/120/112 -f 103/130/113 102/128/113 117/135/113 118/136/113 -f 111/131/114 110/129/114 125/137/114 126/138/114 -f 104/132/115 103/130/115 118/136/115 119/139/115 -f 112/133/116 111/131/116 126/138/116 127/140/116 -f 105/134/117 104/132/117 119/139/117 120/141/117 -f 113/118/118 112/133/118 127/140/118 128/142/118 -f 106/120/119 105/134/119 120/141/119 121/143/119 -f 114/119/120 113/118/120 128/142/120 129/144/120 -f 107/121/121 106/120/121 121/143/121 122/145/121 -f 115/122/122 114/119/122 129/144/122 130/146/122 -f 108/123/123 107/121/123 122/145/123 123/147/123 -f 101/125/124 326/148/124 116/149/124 -f 482/150/125 115/122/125 130/146/125 -f 109/127/126 108/123/126 123/147/126 124/151/126 -f 102/128/127 101/125/127 116/149/127 117/135/127 -f 110/129/128 109/127/128 124/151/128 125/137/128 -f 122/145/129 121/143/129 136/152/129 137/153/129 -f 130/146/130 129/144/130 144/154/130 145/155/130 -f 123/147/131 122/145/131 137/153/131 138/156/131 -f 116/149/132 326/157/132 131/158/132 -f 482/159/133 130/146/133 145/155/133 -f 124/151/134 123/147/134 138/156/134 139/160/134 -f 117/135/135 116/149/135 131/158/135 132/161/135 -f 125/137/136 124/151/136 139/160/136 140/162/136 -f 118/136/137 117/135/137 132/161/137 133/163/137 -f 126/138/138 125/137/138 140/162/138 141/164/138 -f 119/139/139 118/136/139 133/163/139 134/165/139 -f 127/140/140 126/138/140 141/164/140 142/166/140 -f 120/141/141 119/139/141 134/165/141 135/167/141 -f 128/142/142 127/140/142 142/166/142 143/168/142 -f 121/143/143 120/141/143 135/167/143 136/152/143 -f 129/144/144 128/142/144 143/168/144 144/154/144 -f 141/164/145 140/162/145 155/169/145 156/170/145 -f 134/165/146 133/163/146 148/171/146 149/172/146 -f 142/166/147 141/164/147 156/170/147 157/173/147 -f 135/167/148 134/165/148 149/172/148 150/174/148 -f 143/168/149 142/166/149 157/173/149 158/175/149 -f 136/152/150 135/167/150 150/174/150 151/176/150 -f 144/154/151 143/168/151 158/175/151 159/177/151 -f 137/153/152 136/152/152 151/176/152 152/178/152 -f 145/155/153 144/154/153 159/177/153 160/179/153 -f 138/156/154 137/153/154 152/178/154 153/180/154 -f 131/158/155 326/181/155 146/182/155 -f 482/183/156 145/155/156 160/179/156 -f 139/160/157 138/156/157 153/180/157 154/184/157 -f 132/161/158 131/158/158 146/182/158 147/185/158 -f 140/162/159 139/160/159 154/184/159 155/169/159 -f 133/163/160 132/161/160 147/185/160 148/171/160 -f 160/179/161 159/177/161 174/186/161 175/187/161 -f 153/180/162 152/178/162 167/188/162 168/189/162 -f 146/182/163 326/190/163 161/191/163 -f 482/192/164 160/179/164 175/187/164 -f 154/184/165 153/180/165 168/189/165 169/193/165 -f 147/185/166 146/182/166 161/191/166 162/194/166 -f 155/169/167 154/184/167 169/193/167 170/195/167 -f 148/171/168 147/185/168 162/194/168 163/196/168 -f 156/170/169 155/169/169 170/195/169 171/197/169 -f 149/172/170 148/171/170 163/196/170 164/198/170 -f 157/173/171 156/170/171 171/197/171 172/199/171 -f 150/174/172 149/172/172 164/198/172 165/200/172 -f 158/175/173 157/173/173 172/199/173 173/201/173 -f 151/176/174 150/174/174 165/200/174 166/202/174 -f 159/177/175 158/175/175 173/201/175 174/186/175 -f 152/178/176 151/176/176 166/202/176 167/188/176 -f 164/198/177 163/196/177 178/203/177 179/204/177 -f 172/199/178 171/197/178 186/205/178 187/206/178 -f 165/200/179 164/198/179 179/204/179 180/207/179 -f 173/201/180 172/199/180 187/206/180 188/208/180 -f 166/202/181 165/200/181 180/207/181 181/209/181 -f 174/186/182 173/201/182 188/208/182 189/210/182 -f 167/188/183 166/202/183 181/209/183 182/211/183 -f 175/187/184 174/186/184 189/210/184 190/212/184 -f 168/189/185 167/188/185 182/211/185 183/213/185 -f 161/191/186 326/214/186 176/215/186 -f 482/216/187 175/187/187 190/212/187 -f 169/193/188 168/189/188 183/213/188 184/217/188 -f 162/194/189 161/191/189 176/215/189 177/218/189 -f 170/195/190 169/193/190 184/217/190 185/219/190 -f 163/196/191 162/194/191 177/218/191 178/203/191 -f 171/197/192 170/195/192 185/219/192 186/205/192 -f 183/213/193 182/211/193 197/220/193 198/221/193 -f 176/215/194 326/222/194 191/223/194 -f 482/224/195 190/212/195 205/225/195 -f 184/217/196 183/213/196 198/221/196 199/226/196 -f 177/218/197 176/215/197 191/223/197 192/227/197 -f 185/219/198 184/217/198 199/226/198 200/228/198 -f 178/203/199 177/218/199 192/227/199 193/229/199 -f 186/205/200 185/219/200 200/228/200 201/230/200 -f 179/204/201 178/203/201 193/229/201 194/231/201 -f 187/206/202 186/205/202 201/230/202 202/232/202 -f 180/207/203 179/204/203 194/231/203 195/233/203 -f 188/208/204 187/206/204 202/232/204 203/234/204 -f 181/209/205 180/207/205 195/233/205 196/235/205 -f 189/210/206 188/208/206 203/234/206 204/236/206 -f 182/211/207 181/209/207 196/235/207 197/220/207 -f 190/212/208 189/210/208 204/236/208 205/225/208 -f 202/232/209 201/230/209 216/237/209 217/238/209 -f 195/233/210 194/231/210 209/239/210 210/240/210 -f 203/234/211 202/232/211 217/238/211 218/241/211 -f 196/235/212 195/233/212 210/240/212 211/242/212 -f 204/236/213 203/234/213 218/241/213 219/243/213 -f 197/220/214 196/235/214 211/242/214 212/244/214 -f 205/225/215 204/236/215 219/243/215 220/245/215 -f 198/221/216 197/220/216 212/244/216 213/246/216 -f 191/223/217 326/247/217 206/248/217 -f 482/249/218 205/225/218 220/245/218 -f 199/226/219 198/221/219 213/246/219 214/250/219 -f 192/227/220 191/223/220 206/248/220 207/251/220 -f 200/228/221 199/226/221 214/250/221 215/252/221 -f 193/229/222 192/227/222 207/251/222 208/253/222 -f 201/230/223 200/228/223 215/252/223 216/237/223 -f 194/231/224 193/229/224 208/253/224 209/239/224 -f 482/254/225 220/245/225 235/255/225 -f 214/250/226 213/246/226 228/256/226 229/257/226 -f 207/251/227 206/248/227 221/258/227 222/259/227 -f 215/252/228 214/250/228 229/257/228 230/260/228 -f 208/253/229 207/251/229 222/259/229 223/261/229 -f 216/237/230 215/252/230 230/260/230 231/262/230 -f 209/239/231 208/253/231 223/261/231 224/263/231 -f 217/238/232 216/237/232 231/262/232 232/264/232 -f 210/240/233 209/239/233 224/263/233 225/265/233 -f 218/241/234 217/238/234 232/264/234 233/266/234 -f 211/242/235 210/240/235 225/265/235 226/267/235 -f 219/243/236 218/241/236 233/266/236 234/268/236 -f 212/244/237 211/242/237 226/267/237 227/269/237 -f 220/245/238 219/243/238 234/268/238 235/255/238 -f 213/246/239 212/244/239 227/269/239 228/256/239 -f 206/248/240 326/270/240 221/258/240 -f 233/266/241 232/264/241 247/271/241 248/272/241 -f 226/267/242 225/265/242 240/273/242 241/274/242 -f 234/268/243 233/266/243 248/272/243 249/275/243 -f 227/269/244 226/267/244 241/274/244 242/276/244 -f 235/255/245 234/268/245 249/275/245 250/277/245 -f 228/256/246 227/269/246 242/276/246 243/278/246 -f 221/258/247 326/279/247 236/280/247 -f 482/281/248 235/255/248 250/277/248 -f 229/257/249 228/256/249 243/278/249 244/282/249 -f 222/259/250 221/258/250 236/280/250 237/283/250 -f 230/260/251 229/257/251 244/282/251 245/284/251 -f 223/261/252 222/259/252 237/283/252 238/285/252 -f 231/262/253 230/260/253 245/284/253 246/286/253 -f 224/263/254 223/261/254 238/285/254 239/287/254 -f 232/264/255 231/262/255 246/286/255 247/271/255 -f 225/265/256 224/263/256 239/287/256 240/273/256 -f 237/283/257 236/280/257 251/288/257 252/289/257 -f 245/284/258 244/282/258 259/290/258 260/291/258 -f 238/285/259 237/283/259 252/289/259 253/292/259 -f 246/286/260 245/284/260 260/291/260 261/293/260 -f 239/287/261 238/285/261 253/292/261 254/294/261 -f 247/271/262 246/286/262 261/293/262 262/295/262 -f 240/273/263 239/287/263 254/294/263 255/296/263 -f 248/272/264 247/271/264 262/295/264 263/297/264 -f 241/274/265 240/273/265 255/296/265 256/298/265 -f 249/275/266 248/272/266 263/297/266 264/299/266 -f 242/276/267 241/274/267 256/298/267 257/300/267 -f 250/277/268 249/275/268 264/299/268 265/301/268 -f 243/278/269 242/276/269 257/300/269 258/302/269 -f 236/280/270 326/303/270 251/288/270 -f 482/304/271 250/277/271 265/301/271 -f 244/282/272 243/278/272 258/302/272 259/290/272 -f 256/298/273 255/296/273 270/305/273 271/306/273 -f 264/299/274 263/297/274 278/307/274 279/308/274 -f 257/300/275 256/298/275 271/306/275 272/309/275 -f 265/301/276 264/299/276 279/308/276 280/310/276 -f 258/302/277 257/300/277 272/309/277 273/311/277 -f 251/288/278 326/312/278 266/313/278 -f 482/314/279 265/301/279 280/310/279 -f 259/290/280 258/302/280 273/311/280 274/315/280 -f 252/289/281 251/288/281 266/313/281 267/316/281 -f 260/291/282 259/290/282 274/315/282 275/317/282 -f 253/292/283 252/289/283 267/316/283 268/318/283 -f 261/293/284 260/291/284 275/317/284 276/319/284 -f 254/294/285 253/292/285 268/318/285 269/320/285 -f 262/295/286 261/293/286 276/319/286 277/321/286 -f 255/296/287 254/294/287 269/320/287 270/305/287 -f 263/297/288 262/295/288 277/321/288 278/307/288 -f 275/317/289 274/315/289 289/322/289 290/323/289 -f 268/318/290 267/316/290 282/324/290 283/325/290 -f 276/319/291 275/317/291 290/323/291 291/326/291 -f 269/320/292 268/318/292 283/325/292 284/327/292 -f 277/321/293 276/319/293 291/326/293 292/328/293 -f 270/305/294 269/320/294 284/327/294 285/329/294 -f 278/307/295 277/321/295 292/328/295 293/330/295 -f 271/306/296 270/305/296 285/329/296 286/331/296 -f 279/308/297 278/307/297 293/330/297 294/332/297 -f 272/309/298 271/306/298 286/331/298 287/333/298 -f 280/310/299 279/308/299 294/332/299 295/334/299 -f 273/311/300 272/309/300 287/333/300 288/335/300 -f 266/313/301 326/336/301 281/337/301 -f 482/338/302 280/310/302 295/334/302 -f 274/315/303 273/311/303 288/335/303 289/322/303 -f 267/316/304 266/313/304 281/337/304 282/324/304 -f 294/332/305 293/330/305 308/339/305 309/340/305 -f 287/333/306 286/331/306 301/341/306 302/342/306 -f 295/334/307 294/332/307 309/340/307 310/343/307 -f 288/335/308 287/333/308 302/342/308 303/344/308 -f 281/337/309 326/345/309 296/346/309 -f 482/347/310 295/334/310 310/343/310 -f 289/322/311 288/335/311 303/344/311 304/348/311 -f 282/324/312 281/337/312 296/346/312 297/349/312 -f 290/323/313 289/322/313 304/348/313 305/350/313 -f 283/325/314 282/324/314 297/349/314 298/351/314 -f 291/326/315 290/323/315 305/350/315 306/352/315 -f 284/327/316 283/325/316 298/351/316 299/353/316 -f 292/328/317 291/326/317 306/352/317 307/354/317 -f 285/329/318 284/327/318 299/353/318 300/355/318 -f 293/330/319 292/328/319 307/354/319 308/339/319 -f 286/331/320 285/329/320 300/355/320 301/341/320 -f 298/351/321 297/349/321 312/356/321 313/357/321 -f 306/352/322 305/350/322 320/358/322 321/359/322 -f 299/353/323 298/351/323 313/357/323 314/360/323 -f 307/354/324 306/352/324 321/359/324 322/361/324 -f 300/355/325 299/353/325 314/360/325 315/362/325 -f 308/339/326 307/354/326 322/361/326 323/363/326 -f 301/341/327 300/355/327 315/362/327 316/364/327 -f 309/340/328 308/339/328 323/363/328 324/365/328 -f 302/342/329 301/341/329 316/364/329 317/366/329 -f 310/343/330 309/340/330 324/365/330 325/367/330 -f 303/344/331 302/342/331 317/366/331 318/368/331 -f 296/346/332 326/369/332 311/370/332 -f 482/371/333 310/343/333 325/367/333 -f 304/348/334 303/344/334 318/368/334 319/372/334 -f 297/349/335 296/346/335 311/370/335 312/356/335 -f 305/350/336 304/348/336 319/372/336 320/358/336 -f 317/366/337 316/364/337 332/373/337 333/374/337 -f 325/367/338 324/365/338 340/375/338 341/376/338 -f 318/368/339 317/366/339 333/374/339 334/377/339 -f 311/370/340 326/378/340 327/379/340 -f 482/380/341 325/367/341 341/376/341 -f 319/372/342 318/368/342 334/377/342 335/381/342 -f 312/356/343 311/370/343 327/379/343 328/382/343 -f 320/358/344 319/372/344 335/381/344 336/383/344 -f 313/357/345 312/356/345 328/382/345 329/384/345 -f 321/359/346 320/358/346 336/383/346 337/385/346 -f 314/360/347 313/357/347 329/384/347 330/386/347 -f 322/361/348 321/359/348 337/385/348 338/387/348 -f 315/362/349 314/360/349 330/386/349 331/388/349 -f 323/363/350 322/361/350 338/387/350 339/389/350 -f 316/364/351 315/362/351 331/388/351 332/373/351 -f 324/365/352 323/363/352 339/389/352 340/375/352 -f 337/385/353 336/383/353 351/390/353 352/391/353 -f 330/386/354 329/384/354 344/392/354 345/393/354 -f 338/387/355 337/385/355 352/391/355 353/394/355 -f 331/388/356 330/386/356 345/393/356 346/395/356 -f 339/389/357 338/387/357 353/394/357 354/396/357 -f 332/373/358 331/388/358 346/395/358 347/397/358 -f 340/375/359 339/389/359 354/396/359 355/398/359 -f 333/374/360 332/373/360 347/397/360 348/399/360 -f 341/376/361 340/375/361 355/398/361 356/400/361 -f 334/377/362 333/374/362 348/399/362 349/401/362 -f 327/379/363 326/402/363 342/403/363 -f 482/404/364 341/376/364 356/400/364 -f 335/381/365 334/377/365 349/401/365 350/405/365 -f 328/382/366 327/379/366 342/403/366 343/406/366 -f 336/383/367 335/381/367 350/405/367 351/390/367 -f 329/384/368 328/382/368 343/406/368 344/392/368 -f 356/400/369 355/398/369 370/407/369 371/408/369 -f 349/401/370 348/399/370 363/409/370 364/410/370 -f 342/403/371 326/411/371 357/412/371 -f 482/413/372 356/400/372 371/408/372 -f 350/405/373 349/401/373 364/410/373 365/414/373 -f 343/406/374 342/403/374 357/412/374 358/415/374 -f 351/390/375 350/405/375 365/414/375 366/416/375 -f 344/392/376 343/406/376 358/415/376 359/417/376 -f 352/391/377 351/390/377 366/416/377 367/418/377 -f 345/393/378 344/392/378 359/417/378 360/419/378 -f 353/394/379 352/391/379 367/418/379 368/420/379 -f 346/395/380 345/393/380 360/419/380 361/421/380 -f 354/396/381 353/394/381 368/420/381 369/422/381 -f 347/397/382 346/395/382 361/421/382 362/423/382 -f 355/398/383 354/396/383 369/422/383 370/407/383 -f 348/399/384 347/397/384 362/423/384 363/409/384 -f 368/424/385 367/425/385 382/426/385 383/427/385 -f 361/428/386 360/429/386 375/430/386 376/431/386 -f 369/432/387 368/424/387 383/427/387 384/433/387 -f 362/434/388 361/428/388 376/431/388 377/435/388 -f 370/436/389 369/432/389 384/433/389 385/437/389 -f 363/438/390 362/434/390 377/435/390 378/439/390 -f 371/440/391 370/436/391 385/437/391 386/441/391 -f 364/442/392 363/438/392 378/439/392 379/443/392 -f 357/444/393 326/445/393 372/446/393 -f 482/447/394 371/440/394 386/441/394 -f 365/448/395 364/442/395 379/443/395 380/449/395 -f 358/450/396 357/444/396 372/446/396 373/451/396 -f 366/452/397 365/448/397 380/449/397 381/453/397 -f 359/454/398 358/450/398 373/451/398 374/455/398 -f 367/425/399 366/452/399 381/453/399 382/426/399 -f 360/429/400 359/454/400 374/455/400 375/430/400 -f 372/446/401 326/456/401 387/457/401 -f 482/458/402 386/441/402 401/459/402 -f 380/449/403 379/443/403 394/460/403 395/461/403 -f 373/451/404 372/446/404 387/457/404 388/462/404 -f 381/453/405 380/449/405 395/461/405 396/463/405 -f 374/455/406 373/451/406 388/462/406 389/464/406 -f 382/426/407 381/453/407 396/463/407 397/465/407 -f 375/430/408 374/455/408 389/464/408 390/466/408 -f 383/427/409 382/426/409 397/465/409 398/467/409 -f 376/431/410 375/430/410 390/466/410 391/468/410 -f 384/433/411 383/427/411 398/467/411 399/469/411 -f 377/435/412 376/431/412 391/468/412 392/470/412 -f 385/437/413 384/433/413 399/469/413 400/471/413 -f 378/439/414 377/435/414 392/470/414 393/472/414 -f 386/441/415 385/437/415 400/471/415 401/459/415 -f 379/443/416 378/439/416 393/472/416 394/460/416 -f 391/468/417 390/466/417 405/473/417 406/474/417 -f 399/469/418 398/467/418 413/475/418 414/476/418 -f 392/470/419 391/468/419 406/474/419 407/477/419 -f 400/471/420 399/469/420 414/476/420 415/478/420 -f 393/472/421 392/470/421 407/477/421 408/479/421 -f 401/459/422 400/471/422 415/478/422 416/480/422 -f 394/460/423 393/472/423 408/479/423 409/481/423 -f 387/457/424 326/482/424 402/483/424 -f 482/484/425 401/459/425 416/480/425 -f 395/461/426 394/460/426 409/481/426 410/485/426 -f 388/462/427 387/457/427 402/483/427 403/486/427 -f 396/463/428 395/461/428 410/485/428 411/487/428 -f 389/464/429 388/462/429 403/486/429 404/488/429 -f 397/465/430 396/463/430 411/487/430 412/489/430 -f 390/466/431 389/464/431 404/488/431 405/473/431 -f 398/467/432 397/465/432 412/489/432 413/475/432 -f 410/485/433 409/481/433 424/490/433 425/491/433 -f 403/486/434 402/483/434 417/492/434 418/493/434 -f 411/487/435 410/485/435 425/491/435 426/494/435 -f 404/488/436 403/486/436 418/493/436 419/495/436 -f 412/489/437 411/487/437 426/494/437 427/496/437 -f 405/473/438 404/488/438 419/495/438 420/497/438 -f 413/475/439 412/489/439 427/496/439 428/498/439 -f 406/474/440 405/473/440 420/497/440 421/499/440 -f 414/476/441 413/475/441 428/498/441 429/500/441 -f 407/477/442 406/474/442 421/499/442 422/501/442 -f 415/478/443 414/476/443 429/500/443 430/502/443 -f 408/479/444 407/477/444 422/501/444 423/503/444 -f 416/480/445 415/478/445 430/502/445 431/504/445 -f 409/481/446 408/479/446 423/503/446 424/490/446 -f 402/483/447 326/505/447 417/492/447 -f 482/506/448 416/480/448 431/504/448 -f 429/500/449 428/498/449 443/507/449 444/508/449 -f 422/501/450 421/499/450 436/509/450 437/510/450 -f 430/502/451 429/500/451 444/508/451 445/511/451 -f 423/503/452 422/501/452 437/510/452 438/512/452 -f 431/504/453 430/502/453 445/511/453 446/513/453 -f 424/490/454 423/503/454 438/512/454 439/514/454 -f 417/492/455 326/515/455 432/516/455 -f 482/517/456 431/504/456 446/513/456 -f 425/491/457 424/490/457 439/514/457 440/518/457 -f 418/493/458 417/492/458 432/516/458 433/519/458 -f 426/494/459 425/491/459 440/518/459 441/520/459 -f 419/495/460 418/493/460 433/519/460 434/521/460 -f 427/496/461 426/494/461 441/520/461 442/522/461 -f 420/497/462 419/495/462 434/521/462 435/523/462 -f 428/498/463 427/496/463 442/522/463 443/507/463 -f 421/499/464 420/497/464 435/523/464 436/509/464 -f 433/519/465 432/516/465 447/524/465 448/525/465 -f 441/520/466 440/518/466 455/526/466 456/527/466 -f 434/521/467 433/519/467 448/525/467 449/528/467 -f 442/522/468 441/520/468 456/527/468 457/529/468 -f 435/523/469 434/521/469 449/528/469 450/530/469 -f 443/507/470 442/522/470 457/529/470 458/531/470 -f 436/509/471 435/523/471 450/530/471 451/532/471 -f 444/508/472 443/507/472 458/531/472 459/533/472 -f 437/510/473 436/509/473 451/532/473 452/534/473 -f 445/511/474 444/508/474 459/533/474 460/535/474 -f 438/512/475 437/510/475 452/534/475 453/536/475 -f 446/513/476 445/511/476 460/535/476 461/537/476 -f 439/514/477 438/512/477 453/536/477 454/538/477 -f 432/516/478 326/539/478 447/524/478 -f 482/540/479 446/513/479 461/537/479 -f 440/518/480 439/514/480 454/538/480 455/526/480 -f 452/534/481 451/532/481 466/541/481 467/542/481 -f 460/535/482 459/533/482 474/543/482 475/544/482 -f 453/536/483 452/534/483 467/542/483 468/545/483 -f 461/537/484 460/535/484 475/544/484 476/546/484 -f 454/538/485 453/536/485 468/545/485 469/547/485 -f 447/524/486 326/548/486 462/549/486 -f 482/550/487 461/537/487 476/546/487 -f 455/526/488 454/538/488 469/547/488 470/551/488 -f 448/525/489 447/524/489 462/549/489 463/552/489 -f 456/527/490 455/526/490 470/551/490 471/553/490 -f 449/528/491 448/525/491 463/552/491 464/554/491 -f 457/529/492 456/527/492 471/553/492 472/555/492 -f 450/530/493 449/528/493 464/554/493 465/556/493 -f 458/531/494 457/529/494 472/555/494 473/557/494 -f 451/532/495 450/530/495 465/556/495 466/541/495 -f 459/533/496 458/531/496 473/557/496 474/543/496 -f 471/553/497 470/551/497 478/2/497 479/1/497 -f 464/554/498 463/552/498 2/6/498 3/5/498 -f 472/555/499 471/553/499 479/1/499 8/9/499 -f 465/556/500 464/554/500 3/5/500 4/11/500 -f 473/557/501 472/555/501 8/9/501 9/13/501 -f 466/541/502 465/556/502 4/11/502 5/15/502 -f 474/543/503 473/557/503 9/13/503 10/17/503 -f 467/542/504 466/541/504 5/15/504 6/19/504 -f 475/544/505 474/543/505 10/17/505 480/21/505 -f 468/545/506 467/542/506 6/19/506 7/23/506 -f 476/546/507 475/544/507 480/21/507 481/25/507 -f 469/547/508 468/545/508 7/23/508 477/27/508 -f 462/549/509 326/558/509 1/29/509 -f 482/559/510 476/546/510 481/25/510 -f 470/551/511 469/547/511 477/27/511 478/2/511 -f 463/552/512 462/549/512 1/29/512 2/6/512 diff --git a/Assets/Objects/barrel.glb b/Assets/Objects/barrel.glb new file mode 100644 index 00000000..16855be9 Binary files /dev/null and b/Assets/Objects/barrel.glb differ diff --git a/Assets/Objects/streetlamp.glb b/Assets/Objects/streetlamp.glb new file mode 100644 index 00000000..685aaeee Binary files /dev/null and b/Assets/Objects/streetlamp.glb differ diff --git a/Assets/Objects/torch.glb b/Assets/Objects/torch.glb new file mode 100644 index 00000000..5ad103f6 Binary files /dev/null and b/Assets/Objects/torch.glb differ diff --git a/Assets/Shaders/basic.fs b/Assets/Shaders/basic.fs index 5bba789d..ff6e211f 100644 --- a/Assets/Shaders/basic.fs +++ b/Assets/Shaders/basic.fs @@ -1,28 +1,168 @@ #version 330 core + +layout (location = 0) out vec4 gColor; +layout (location = 1) out vec4 BrightColor; + out vec4 FragColor; in vec2 TexCoords; in vec3 fragPos; -in vec3 Color; +in vec3 meshColor; +in vec3 TangentLightPos; +in vec3 TangentFragPos; +in vec3 TangentViewPos; +in vec3 Normal; +in vec3 worldNormal; +in vec3 camPos; +in mat3 TBN; +in mat4 viewMatrix; +in vec4 clipSpacePos; +uniform float uTime = 1; uniform vec3 viewPos; -uniform bool useMaterial = false; - -#include "pipeline/fog/fog.glsl" +uniform vec3 ambientLightColor = vec3(1.0, 1.0, 1.0); +uniform float ambientLightColorIntensity = 1.0; +uniform bool directionLightEnable = false; +uniform bool shadowsEnable = false; +uniform bool pbrEnabled = false; +uniform bool overrideColorMesh = false; +uniform sampler2D metalness; +uniform sampler2D roughnessMap; -uniform sampler2D texture_diffuse1; +#include "functions/fog.glsl" +#include "functions/lights.glsl" +#include "functions/reflection.glsl" +#include "functions/shadows.glsl" +#include "functions/alpha.glsl" void main() { - if (useMaterial) { - FragColor = vec4(Color, 0); + float shadow = 0.0; + vec3 ambientColor = ambientLightColor * ambientLightColorIntensity; + vec4 albedoTexture = useMaterial ? vec4(meshColor, 1.0) : texture(material.ambient, TexCoords); + + if (overrideColorMesh && useMaterial) { + albedoTexture = vec4(ambientLightColor, 1.0); + } + + //float metalness = pbrEnabled ? texture(metalness, TexCoords).r : 0.0; + //float roughness = pbrEnabled ? texture(roughnessMap, TexCoords).r : 0.5; + + float metalness = pbrEnabled ? texture(metalness, TexCoords).b : 0.0; + float roughness = pbrEnabled ? texture(roughnessMap, TexCoords).g : 0.5; + vec3 F0 = vec3(0.04); + + if (pbrEnabled) { + roughness = clamp(roughness, 0.05, 1.0); + metalness = clamp(metalness, 0.0, 1.0); + F0 = useMaterial ? mix(F0, pow(albedoTexture.xyz, vec3(2.2)), metalness) : mix(F0, pow(albedoTexture.rgb, vec3(2.2)), metalness); + } + + vec3 normal = Normal; + if (normalMapEnabled) { + vec3 tangentNormal = texture(material.diffuse, TexCoords).rgb; + tangentNormal = tangentNormal * 2.0 - 1.0; // [0,1] -> [-1,1] + normal = TBN * tangentNormal; + } + + vec3 color = useMaterial ? albedoTexture.xyz : albedoTexture.rgb; + vec3 viewDir = normalize(camPos - fragPos); + vec3 ambient = vec3(0.0); + + if (pbrEnabled) { + // A) PBR Ambient (IBL) + vec3 kS = fresnelSchlick(max(dot(normal, viewDir), 0.0), F0); + vec3 kD = 1.0 - kS; + kD *= 1.0 - metalness; + + vec3 irradiance = vec3(0.03); + vec3 reflections = vec3(0.0); + + if (iblEnabled) { + irradiance = CalcIBLDiffuse(normal); + vec3 R = reflect(-viewDir, normal); + reflections = CalcIBLSpecular(R, roughness, F0) * kS; + } else { + irradiance = ambientLightColor * 0.1; + } + + vec3 diffusePart = irradiance * albedoTexture.rgb; + + // Ambient Occlusion + float ao = texture(material.aoMap, TexCoords).r; + if (ao < 0.01) ao = 1.0; + + // Výsledný ambient scény + ambient = (kD * diffusePart + reflections) * ao * ambientLightColorIntensity; + } else { - FragColor = texture(texture_diffuse1, TexCoords); + vec3 color = useMaterial ? albedoTexture.xyz : albedoTexture.rgb; + ambient = useMaterial ? color : ambientLightColor * ambientLightColorIntensity * color; + } + + vec3 final = ambient; + + if (directionLightEnable) { + if (pbrEnabled) { + final += CalcDirLightPBR(dirLight, normal, fragPos, viewDir, ambient, roughness, metalness, F0); + } else { + if (shadowsEnable) { + vec4 fragPosView = viewMatrix * vec4(fragPos, 1.0); + float viewDepth = -fragPosView.z; + int cascadeIndex = int(GetCascadeIndex(viewDepth)); + vec3 shadowNormal = normalMapEnabled ? Normal : worldNormal; + + if (cascadeIndex == 0) { + shadow = ShadowCalculation2(fragPos, shadowNormal, -dirLight.direction, cascadeIndex, lightSpaceMatrix0); + } else if (cascadeIndex == 1) + shadow = ShadowCalculation2(fragPos, shadowNormal, -dirLight.direction, cascadeIndex, lightSpaceMatrix1); + else + shadow = ShadowCalculation2(fragPos, shadowNormal, -dirLight.direction, cascadeIndex, lightSpaceMatrix2); + } + + final = CalcDirLight(dirLight, normal, viewDir, ambient, shadow); + } + } + + vec3 lightAlbedo = vec3(texture(material.ambient, TexCoords)); + vec3 lightSpecular = vec3(texture(material.specular, TexCoords)); + + if (pbrEnabled) { + for(int i = 0; i < numPointLights; i++) + { + final += CalcPointLightPBR(pointLight[i], normal, fragPos, viewDir, + ambient, roughness, metalness, F0); + } + } else { + for(int i = 0; i < numPointLights; i++) + { + final += CalcPointLight(pointLight[i], normal, fragPos, viewDir, ambient, lightAlbedo, lightSpecular); + } + } + + for(int i = 0; i < numSpotLights; i++) + { + final += CalcSpotLight(spotLight[i], normalize(Normal), fragPos, viewDir, ambient, uTime, lightAlbedo, lightSpecular); } if (fogEnable) { - float d = distance(viewPos, fragPos); - float alpha = getFogFactor(d); - FragColor = mix(FragColor, vec4(0.6f, 0.6f, 0.7f, 1.f), alpha); + float d = distance(viewPos, fragPos); + float alpha = getFogFactor(d); + FragColor = mix(vec4(final, 1.0), vec4(0.6f, 0.6f, 0.7f, 0.9f), alpha); + } else { + FragColor = alphaBlending(pow(final, vec3(1.0/2.2))); + } + + if (reflectionEnable) { + FragColor = vec4(calcReflexion(clipSpacePos, FragColor.rgb), FragColor.a); + } + + gColor = FragColor; + float brightness = dot(FragColor.rgb, vec3(0.2126, 0.7152, 0.0722)); + + if (brightness > 1.0) { + BrightColor = FragColor; + } else { + BrightColor = vec4(0.0, 0.0, 0.0, 1.0); } } \ No newline at end of file diff --git a/Assets/Shaders/basic.vs b/Assets/Shaders/basic.vs index 1d751da6..b96d8023 100644 --- a/Assets/Shaders/basic.vs +++ b/Assets/Shaders/basic.vs @@ -1,34 +1,72 @@ #version 330 core + layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aNormal; layout (location = 2) in vec3 aColor; layout (location = 3) in vec2 aTexCoords; +layout (location = 4) in vec3 aTangent; +layout (location = 5) in vec3 aBitangent; layout (location = 6) in ivec4 boneIds; layout (location = 7) in vec4 weights; out vec2 TexCoords; out vec3 fragPos; -out vec3 Color; - -uniform bool useBones = true; +out vec3 TangentLightPos; +out vec3 TangentFragPos; +out vec3 TangentViewPos; +out vec3 Normal; +out vec3 worldNormal; +out vec3 modelNormal; +out mat3 TBN; +out vec3 camPos; +out vec3 meshColor; +out mat4 viewMatrix; +out vec4 clipSpacePos; +uniform vec3 viewPos; uniform mat4 model; uniform mat4 view; uniform mat4 projection; +uniform bool useBones = true; +uniform vec2 uvScale = vec2(1.0, 1.0); +uniform vec2 uvOffset = vec2(0.0, 0.0); +uniform vec3 lightPos = vec3(0.0, 0.0, 0.0); +//uniform vec3 cameraPos; -#include "pipeline/skeleton/bonesTransform.glsl" +#include "functions/bonesTransform.glsl" void main() { + mat4 viewModel = view * model; + if (useBones) { - mat4 viewModel = view * model; gl_Position = projection * viewModel * boneTransform(boneIds, weights); } else { - gl_Position = projection * view * model * vec4(aPos, 1.0); + gl_Position = projection * viewModel * vec4(aPos, 1.0); } - TexCoords = aTexCoords; - Color = aColor; - fragPos = vec3(model * vec4(aPos, 1.0)); -} + vec2 uv = aTexCoords * uvScale + uvOffset; + TexCoords = uv; + fragPos = vec3(model * (useBones ? boneTransform(boneIds, weights) : vec4(aPos, 1.0))); + + mat3 normalMatrix = transpose(inverse(mat3(model))); + vec3 T = normalize(normalMatrix * aTangent); + vec3 N = normalize(normalMatrix * aNormal); + T = normalize(T - dot(T, N) * N); + vec3 B = cross(N, T); + TBN = mat3(T, B, N); + TangentLightPos = TBN * lightPos; + TangentFragPos = TBN * fragPos; + TangentViewPos = TBN * viewPos; + + Normal = mat3(transpose(inverse(viewModel))) * aNormal; + worldNormal = mat3(model) * aNormal; + //worldNormal = normalize(normalMatrix * aNormal); + modelNormal = normalize(transpose(inverse(mat3(model))) * aNormal); + //Normal = mat3(model) * aNormal; + camPos = viewPos; + meshColor = aColor; + viewMatrix = view; + clipSpacePos = gl_Position; +} diff --git a/Assets/Shaders/basic_2d.fs b/Assets/Shaders/basic_2d.fs new file mode 100644 index 00000000..087df9c5 --- /dev/null +++ b/Assets/Shaders/basic_2d.fs @@ -0,0 +1,20 @@ +#version 330 core + +out vec4 FragColor; + +in vec2 TexCoords; + +uniform sampler2D textureMap; +uniform vec3 color; +uniform bool useMaterial = false; + +#include "functions/alpha.glsl" + +void main() +{ + if (useMaterial) { + FragColor = alphaBlending(color); + } else { + FragColor = alphaBlending(texture(textureMap, TexCoords).rgb); + } +} \ No newline at end of file diff --git a/Assets/Shaders/basic_2d.vs b/Assets/Shaders/basic_2d.vs new file mode 100644 index 00000000..3712e00c --- /dev/null +++ b/Assets/Shaders/basic_2d.vs @@ -0,0 +1,19 @@ +#version 330 core + +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec2 aTexCoords; +layout (location = 2) in vec3 aColor; + +out vec2 TexCoords; + +uniform mat4 model; +uniform mat4 projection; + +#include "functions/expansion.glsl" + +void main() +{ + TexCoords = aTexCoords; + vec2 expanded = compute_2d_expansion(aPos.xy); + gl_Position = projection * model * vec4(expanded.x, expanded.y, 0.0, 1.0); +} \ No newline at end of file diff --git a/Assets/Shaders/bloom/bloom.fs b/Assets/Shaders/bloom/bloom.fs index b3ffab9d..0c01ad10 100644 --- a/Assets/Shaders/bloom/bloom.fs +++ b/Assets/Shaders/bloom/bloom.fs @@ -1,4 +1,5 @@ #version 330 core + layout (location = 0) out vec4 FragColor; layout (location = 1) out vec4 BrightColor; @@ -13,37 +14,18 @@ struct Light { vec3 Color; }; -uniform Light lights[4]; -uniform sampler2D diffuseTexture; -uniform vec3 viewPos; +uniform vec3 lightColor; void main() { - vec3 color = texture(diffuseTexture, fs_in.TexCoords).rgb; - vec3 normal = normalize(fs_in.Normal); - // ambient - vec3 ambient = 0.0 * color; - // lighting - vec3 lighting = vec3(0.0); - vec3 viewDir = normalize(viewPos - fs_in.FragPos); - for(int i = 0; i < 4; i++) - { - // diffuse - vec3 lightDir = normalize(lights[i].Position - fs_in.FragPos); - float diff = max(dot(lightDir, normal), 0.0); - vec3 result = lights[i].Color * diff * color; - // attenuation (use quadratic as we have gamma correction) - float distance = length(fs_in.FragPos - lights[i].Position); - result *= 1.0 / (distance * distance); - lighting += result; + vec3 color = lightColor; //texture(scene, TexCoords).rgb; + float brightness = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722)); + if (brightness > 1.0) { + BrightColor = vec4(color, 1.0); + } else { + BrightColor = vec4(0.0); } - vec3 result = ambient + lighting; - // check whether result is higher than some threshold, if so, output as bloom threshold color - float brightness = dot(result, vec3(0.2126, 0.7152, 0.0722)); - if(brightness > 1.0) - BrightColor = vec4(result, 1.0); - else - BrightColor = vec4(0.0, 0.0, 0.0, 1.0); - FragColor = vec4(result, 1.0); + + FragColor = vec4(color, 1.0); } \ No newline at end of file diff --git a/Assets/Shaders/bloom/bloom_final.fs b/Assets/Shaders/bloom/bloom_final.fs index cb00cfdd..fac73cef 100644 --- a/Assets/Shaders/bloom/bloom_final.fs +++ b/Assets/Shaders/bloom/bloom_final.fs @@ -1,4 +1,5 @@ #version 330 core + out vec4 FragColor; in vec2 TexCoords; @@ -17,9 +18,13 @@ void main() hdrColor += bloomColor; // additive blending } // tone mapping - vec3 result = hdrColor; + //vec3 result = hdrColor; + vec3 result = hdrColor / vec3(1.05); + //vec3 result = vec3(1.0) - exp(-hdrColor * 2.0); // lightning storm // also gamma correct while we're at it + result *= exposure; result = pow(result, vec3(1.0 / gamma)); + FragColor = vec4(result, 1.0); } \ No newline at end of file diff --git a/Assets/Shaders/bolt/bolt.fs b/Assets/Shaders/bolt/bolt.fs new file mode 100644 index 00000000..07434d62 --- /dev/null +++ b/Assets/Shaders/bolt/bolt.fs @@ -0,0 +1,14 @@ +#version 330 core +out vec4 FragColor; + +layout (location = 1) out vec4 BrightColor; + +uniform float brightness; +uniform float time; + +void main() { + vec3 boltColor = vec3(0.3, 0.7, 1.0) * 15.0; + float pulse = sin(time * 30.0) * 0.5 + 0.5; + vec3 finalColor = boltColor * brightness * (1.0 + pulse * 0.3); + FragColor = BrightColor = vec4(finalColor, 1.0); +} diff --git a/Assets/Shaders/bolt/bolt.vs b/Assets/Shaders/bolt/bolt.vs new file mode 100644 index 00000000..df8c3c01 --- /dev/null +++ b/Assets/Shaders/bolt/bolt.vs @@ -0,0 +1,13 @@ +#version 330 core +layout (location = 0) in vec3 aPos; + +uniform mat4 model; +uniform mat4 view; +uniform mat4 projection; +uniform vec3 startPos; +uniform vec3 endPos; + +void main() { + vec3 position = mix(startPos, endPos, aPos.x); + gl_Position = projection * view * vec4(position, 1.0); +} diff --git a/Assets/Shaders/bolt/flash.fs b/Assets/Shaders/bolt/flash.fs new file mode 100644 index 00000000..378a016d --- /dev/null +++ b/Assets/Shaders/bolt/flash.fs @@ -0,0 +1,8 @@ +#version 330 core +out vec4 FragColor; + +uniform float alpha; + +void main() { + FragColor = vec4(1.0, 1.0, 1.0, alpha); +} \ No newline at end of file diff --git a/Assets/Shaders/bolt/flash.vs b/Assets/Shaders/bolt/flash.vs new file mode 100644 index 00000000..dc45063c --- /dev/null +++ b/Assets/Shaders/bolt/flash.vs @@ -0,0 +1,10 @@ +#version 330 core +layout (location = 0) in vec2 aPos; +layout (location = 1) in vec2 aTexCoords; + +out vec2 TexCoords; + +void main() { + TexCoords = aTexCoords; + gl_Position = vec4(aPos.xy, 0.0, 1.0); +} \ No newline at end of file diff --git a/Assets/Shaders/color.fs b/Assets/Shaders/color.fs new file mode 100644 index 00000000..abd789c4 --- /dev/null +++ b/Assets/Shaders/color.fs @@ -0,0 +1,24 @@ +#version 330 core + +in vec2 vQuad; + +layout (location = 0) out vec4 gColor; +layout (location = 1) out vec4 BrightColor; +out vec4 FragColor; + +uniform vec3 color; +uniform float pulse; + +void main() { + float dist = length(vQuad); // 0 = střed, ~1 = okraj + + float outer = 1.0; + float inner = 0.9; // tenká páska mezi inner a outer + + if (dist < inner || dist > outer) discard; // jen páska + + // pulzování průhlednosti + float alpha = 1.0 * (0.5 + 0.5 * pulse); // můžeš zmenšit základní opačnost + vec3 finalColor = color * (0.8 + 0.2 * pulse); + BrightColor = gColor = FragColor = vec4(finalColor, alpha); +} \ No newline at end of file diff --git a/Assets/Shaders/color.vs b/Assets/Shaders/color.vs new file mode 100644 index 00000000..32d94cdf --- /dev/null +++ b/Assets/Shaders/color.vs @@ -0,0 +1,19 @@ +#version 330 core + +layout(location=0) in vec2 aQuad; + +uniform vec3 ringCenter; +uniform mat4 view; +uniform mat4 proj; +uniform float radius; +uniform float pulse; + +out vec2 vQuad; + +void main() { + vQuad = aQuad; + float scale = radius * (1.0 + 0.1 * pulse); + vec3 offset = vec3(aQuad * scale, 0.0); // kruh v rovině XY, Z-up + vec4 worldPos = vec4(ringCenter + offset, 1.0); + gl_Position = proj * view * worldPos; +} \ No newline at end of file diff --git a/Assets/Shaders/corner/corner.fs b/Assets/Shaders/corner/corner.fs new file mode 100644 index 00000000..b60b5391 --- /dev/null +++ b/Assets/Shaders/corner/corner.fs @@ -0,0 +1,44 @@ +#version 330 core +out vec4 FragColor; +in vec2 TexCoords; + +uniform vec2 quadSize; // width, height +uniform float borderWidth; +uniform vec3 borderColor; +uniform float radius; + +// distance od rounded rect ze středu +float roundedRectDist(vec2 p, vec2 size, float r) +{ + vec2 halfSize = size * 0.5; + vec2 d = abs(p) - (halfSize - vec2(r)); + return length(max(d,0.0)) - r; +} + +float rectDist(vec2 p, vec2 size) +{ + vec2 halfSize = size * 0.5; + vec2 d = abs(p) - halfSize; + return max(max(d.x, d.y), 0.0); +} + +void main() +{ + // uv ve středu quadu [-width/2, width/2] + vec2 uv = TexCoords * quadSize - quadSize * 0.5; + + // vnější a vnitřní rounded rect + float distOuter = roundedRectDist(uv, quadSize, radius); + //float distInner = roundedRectDist(uv, quadSize - 2.0 * vec2(borderWidth), radius); + float distInner = rectDist(uv, quadSize - 2.0 * vec2(borderWidth)); + + // Discard mimo vnější tvar – odstraní černé trojúhelníky + if (distOuter > 0.0) + discard; + + // vykresli jen okraj + if(distOuter <= 0.0 && distInner > 0.0) + FragColor = vec4(borderColor, 0.7); + else + FragColor = vec4(0.0, 0.0, 0.0, 0.3); +} diff --git a/Assets/Shaders/debug_quad.fs b/Assets/Shaders/debug_quad.fs index f54de5cb..9a1d3f2d 100644 --- a/Assets/Shaders/debug_quad.fs +++ b/Assets/Shaders/debug_quad.fs @@ -3,7 +3,7 @@ out vec4 FragColor; in vec2 TexCoords; -uniform sampler2D depthMap; +uniform sampler2DArray depthMap; uniform float near_plane; uniform float far_plane; @@ -16,7 +16,7 @@ float LinearizeDepth(float depth) void main() { - float depthValue = texture(depthMap, TexCoords).r; + float depthValue = texture(depthMap, vec3(TexCoords.xy, 0)).r; // FragColor = vec4(vec3(LinearizeDepth(depthValue) / far_plane), 1.0); // perspective FragColor = vec4(vec3(depthValue), 1.0); // orthographic } \ No newline at end of file diff --git a/Assets/Shaders/explosion/explosion.fs b/Assets/Shaders/explosion/explosion.fs new file mode 100644 index 00000000..f972f5a3 --- /dev/null +++ b/Assets/Shaders/explosion/explosion.fs @@ -0,0 +1,12 @@ +#version 450 + +layout (location = 0) out vec4 gColor; +layout (location = 1) out vec4 BrightColor; + +in vec4 vColor; +out vec4 FragColor; + +void main() +{ + FragColor = vColor; +} \ No newline at end of file diff --git a/Assets/Shaders/explosion/explosion.geom b/Assets/Shaders/explosion/explosion.geom new file mode 100644 index 00000000..9b3ad982 --- /dev/null +++ b/Assets/Shaders/explosion/explosion.geom @@ -0,0 +1,58 @@ +#version 450 + +layout(triangles) in; +layout(triangle_strip, max_vertices = 3) out; + +in vec3 fColor[]; +in vec3 vPos[]; +in vec3 vNormal[]; +out vec4 vColor; + +uniform mat4 view; +uniform mat4 projection; + +uniform float time; // čas od startu exploze +uniform float explosionRadius; // maximální rozlet (dřív "speed") +uniform float fadeTime; // délka celé animace + +float hash(vec3 p) { + return fract(sin(dot(p ,vec3(12.9898,78.233, 45.164))) * 43758.5453); +} + +vec3 randDir(vec3 pos) +{ + return normalize(vec3( + hash(pos.xyz + 1.23), + hash(pos.yzx + 4.56), + hash(pos.zxy + 7.89) + ) * 2.0 - 1.0); +} + +void main() +{ + // normalizovaný čas 0..1 + float t = clamp(time / fadeTime, 0.0, 1.0); + + // křivka rozletu (rychle na začátku, pak zpomaluje) + float moveFactor = smoothstep(0.0, 1.0, t) * explosionRadius; + + for(int i = 0; i < 3; i++) + { + vec3 dir = normalize(vNormal[i] * 0.6 + randDir(vPos[i]) * 0.4); + + // konečná pozice – nikdy dál než explosionRadius + vec3 displaced = vPos[i] + dir * moveFactor; + + float alpha = 1.0 - t; + + //float randVal = hash(vPos[i]); + //vec3 baseColor = mix(vec3(1.0, 0.3, 0.0), vec3(1.0, 0.7, 0.2), randVal); + + //fColor = vec4(baseColor, alpha); + vColor = vec4(fColor[i], alpha); + + gl_Position = projection * view * vec4(displaced, 1.0); + EmitVertex(); + } + EndPrimitive(); +} diff --git a/Assets/Shaders/explosion/explosion.vs b/Assets/Shaders/explosion/explosion.vs new file mode 100644 index 00000000..dd5c97da --- /dev/null +++ b/Assets/Shaders/explosion/explosion.vs @@ -0,0 +1,20 @@ +#version 450 + +layout(location = 0) in vec3 aPos; +layout(location = 1) in vec3 aNormal; +layout (location = 2) in vec3 aColor; + +out vec3 vPos; +out vec3 vNormal; +out vec3 fColor; + +uniform mat4 model; +uniform mat4 view; +uniform mat4 projection; + +void main() +{ + vPos = vec3(model * vec4(aPos, 1.0)); // světové souřadnice + vNormal = mat3(transpose(inverse(model))) * aNormal; // transformovaná normála + fColor = aColor; +} \ No newline at end of file diff --git a/Assets/Shaders/fire/fire.fs b/Assets/Shaders/fire/fire.fs new file mode 100644 index 00000000..b348c150 --- /dev/null +++ b/Assets/Shaders/fire/fire.fs @@ -0,0 +1,33 @@ +#version 330 core + +layout (location = 0) out vec4 gColor; +layout (location = 1) out vec4 BrightColor; + +in vec2 TexCoords; +in vec4 ParticleColor; // Přichází z VS, může mít hodnoty > 1.0 (HDR) + +uniform sampler2D fire_texture; // Ujisti se, že název uniformu sedí + +void main() { + float mask = texture(fire_texture, TexCoords).r; + float proceduralMask = 1.0 - smoothstep(0.45, 0.5, length(TexCoords - vec2(0.5))); + float finalMask = mask * proceduralMask; + + // Finální barva částice s HDR hodnotami + vec4 finalColor = vec4(ParticleColor.rgb, ParticleColor.a * finalMask); + + if (finalColor.a < 0.01) { + discard; + } + + // Pro lepší kontrolu můžeme říct, že zářit mají jen opravdu jasné části + float brightness = dot(finalColor.rgb, vec3(0.2126, 0.7152, 0.0722)); + if (brightness > 1.0) { // Práh jasu pro bloom + BrightColor = finalColor; + } else { + BrightColor = vec4(0.0, 0.0, 0.0, 1.0); // Pokud není dost jasná, do bloomu neposílá nic + } + + // Do finální scény vykreslíme barvu vždy + gColor = finalColor; +} \ No newline at end of file diff --git a/Assets/Shaders/fire/fire.vs b/Assets/Shaders/fire/fire.vs new file mode 100644 index 00000000..c0252170 --- /dev/null +++ b/Assets/Shaders/fire/fire.vs @@ -0,0 +1,26 @@ +#version 330 core + +layout (location = 0) in vec2 aPos; +layout (location = 1) in vec2 aTexCoords; +layout (location = 2) in vec3 instancePosition; +layout (location = 3) in vec4 instanceColor; + +out vec2 TexCoords; +out vec4 ParticleColor; + +uniform mat4 view; +uniform mat4 projection; +uniform float particleSize; + +void main() { + TexCoords = aTexCoords; + ParticleColor = instanceColor; + + vec3 cameraRight = vec3(view[0][0], view[1][0], view[2][0]); + vec3 cameraUp = vec3(view[0][1], view[1][1], view[2][1]); + + // Použijeme uniform proměnnou pro velikost + vec3 pos = instancePosition + cameraRight * aPos.x * particleSize + cameraUp * aPos.y * particleSize; + + gl_Position = projection * view * vec4(pos, 1.0); +} \ No newline at end of file diff --git a/Assets/Shaders/fire/smoke.fs b/Assets/Shaders/fire/smoke.fs new file mode 100644 index 00000000..6bc6d57d --- /dev/null +++ b/Assets/Shaders/fire/smoke.fs @@ -0,0 +1,26 @@ +#version 330 core + +layout (location = 0) out vec4 gColor; +layout (location = 1) out vec4 gBloom; + +in vec2 TexCoords; +in vec4 ParticleColor; // Barva kouře z VS + +uniform sampler2D smoke_texture; // Ujisti se, že název uniformu sedí + +void main() { + float textureMask = texture(smoke_texture, TexCoords).r; + float proceduralMask = 1.0 - smoothstep(0.45, 0.5, length(TexCoords - vec2(0.5))); + float finalMask = textureMask * proceduralMask; + + vec4 finalColor = vec4(ParticleColor.rgb, ParticleColor.a * finalMask); + + if (finalColor.a < 0.01) { + discard; + } + + // Kouř je vidět ve finální scéně + gColor = finalColor; + // Kouř ale nezáří, takže do bloom bufferu pošleme černou + gBloom = vec4(0.0, 0.0, 0.0, 1.0); +} \ No newline at end of file diff --git a/Assets/Shaders/fire/smoke.vs b/Assets/Shaders/fire/smoke.vs new file mode 100644 index 00000000..e429b20a --- /dev/null +++ b/Assets/Shaders/fire/smoke.vs @@ -0,0 +1,37 @@ +#version 330 core + +layout (location = 0) in vec2 aPos; +layout (location = 1) in vec2 aTexCoords; +layout (location = 2) in vec3 instancePosition; +layout (location = 3) in vec4 instanceColor; +// Přidáme další atributy pro každou instanci +layout (location = 4) in vec2 sizeAndRotation; // x = velikost, y = rotace v radiánech + +out vec2 TexCoords; +out vec4 ParticleColor; + +uniform mat4 view; +uniform mat4 projection; +uniform float overallSize; + +void main() { + TexCoords = aTexCoords; + ParticleColor = instanceColor; + + float particleSize = sizeAndRotation.x * overallSize; + float particleRotation = sizeAndRotation.y; + + // Rotační matice pro 2D rotaci billboardu + mat2 rotMatrix = mat2( + cos(particleRotation), -sin(particleRotation), + sin(particleRotation), cos(particleRotation) + ); + vec2 rotatedPos = rotMatrix * aPos; + + vec3 cameraRight = vec3(view[0][0], view[1][0], view[2][0]); + vec3 cameraUp = vec3(view[0][1], view[1][1], view[2][1]); + + vec3 pos = instancePosition + (cameraRight * rotatedPos.x + cameraUp * rotatedPos.y) * particleSize; + + gl_Position = projection * view * vec4(pos, 1.0); +} \ No newline at end of file diff --git a/Assets/Shaders/pipeline/blending/alpha.glsl b/Assets/Shaders/functions/alpha.glsl similarity index 100% rename from Assets/Shaders/pipeline/blending/alpha.glsl rename to Assets/Shaders/functions/alpha.glsl diff --git a/Assets/Shaders/pipeline/skeleton/bonesTransform.glsl b/Assets/Shaders/functions/bonesTransform.glsl similarity index 100% rename from Assets/Shaders/pipeline/skeleton/bonesTransform.glsl rename to Assets/Shaders/functions/bonesTransform.glsl diff --git a/Assets/Shaders/functions/easing.glsl b/Assets/Shaders/functions/easing.glsl new file mode 100644 index 00000000..da8080d1 --- /dev/null +++ b/Assets/Shaders/functions/easing.glsl @@ -0,0 +1,21 @@ +uniform float animProgress; + +float easeOutBack(float x); +float easeOutElastic(float x); + + +// --- EASING FUNKCE --- +// BackOut: Prestreli cil a vrati se zpet. C1 urcuje velikost prestreleni. +float easeOutBack(float x) { + const float c1 = 1.70158; + const float c3 = c1 + 1.0; + return 1.0 + c3 * pow(x - 1.0, 3.0) + c1 * pow(x - 1.0, 2.0); +} + +// Nebo ElasticOut (jeste pruznejsi, jako zele) +float easeOutElastic(float x) { + const float c4 = (2.0 * 3.14159) / 3.0; + return x == 0.0 ? 0.0 : x == 1.0 ? 1.0 : pow(2.0, -10.0 * x) * sin((x * 10.0 - 0.75) * c4) + 1.0; +} + +float easingScale = easeOutBack(animProgress); \ No newline at end of file diff --git a/Assets/Shaders/functions/expansion.glsl b/Assets/Shaders/functions/expansion.glsl new file mode 100644 index 00000000..06c9c1bc --- /dev/null +++ b/Assets/Shaders/functions/expansion.glsl @@ -0,0 +1,7 @@ +uniform float expansion = 1.0; + +vec2 compute_2d_expansion(vec2 pos); + +vec2 compute_2d_expansion(vec2 pos) { + return vec2(pos.x * expansion, pos.y * expansion); +} \ No newline at end of file diff --git a/Assets/Shaders/pipeline/fog/fog.glsl b/Assets/Shaders/functions/fog.glsl similarity index 100% rename from Assets/Shaders/pipeline/fog/fog.glsl rename to Assets/Shaders/functions/fog.glsl diff --git a/Assets/Shaders/functions/lights.glsl b/Assets/Shaders/functions/lights.glsl new file mode 100644 index 00000000..2a248fe7 --- /dev/null +++ b/Assets/Shaders/functions/lights.glsl @@ -0,0 +1,387 @@ +struct Material { + sampler2D ambient; + sampler2D diffuse; + sampler2D specular; + sampler2D aoMap; + float shininess; +}; + +struct DirLight { + vec3 position; + vec3 direction; + + vec3 ambient; + vec3 diffuse; + vec3 specular; +}; + +struct MaterialDirLight { + vec3 direction; + + vec3 diffuse; + vec3 specular; +}; + +struct PointLight { + vec3 position; + + float constant; + float linear; + float quadratic; + //float energy; + + vec3 ambient; + vec3 diffuse; + vec3 specular; +}; + +struct SpotLight { + vec3 position; + vec3 direction; + float cutOff; + float outerCutOff; + + float constant; + float linear; + float quadratic; + + vec3 ambient; + vec3 diffuse; + vec3 specular; + + bool pulse; +}; + +#define NR_POINT_LIGHTS 8 + +uniform DirLight dirLight; +uniform MaterialDirLight materialDirLight; +uniform PointLight pointLight[NR_POINT_LIGHTS]; +uniform SpotLight spotLight[NR_POINT_LIGHTS]; +uniform Material material; +uniform int numPointLights = 0; +uniform int numSpotLights = 0; +uniform bool useMaterial = false; +uniform bool normalMapEnabled = false; +uniform bool specularMapEnabled = false; +uniform bool hasAlbedoTexture = false; +uniform samplerCube environmentMap; +uniform bool iblEnabled = false; +uniform float uShadowAmbientDarken = 0.85; // how much to darken ambient in shadow (0..1) +uniform float uShadowDesaturateStrength = 1.0; // how strong the gray shift is in shadow (0..1) + +// function prototypes +vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir, vec3 ambientColor, float shadow); +vec3 CalcDirLightPBR(DirLight light, vec3 fragPos, vec3 normal, vec3 viewDir, vec3 ambientColor, float roughness, float metalness, vec3 F0); +vec3 CalcDirLightMaterial(MaterialDirLight light, vec3 normal, vec3 viewDir, vec3 fragPos, vec3 ambient); +vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir, vec3 materialColor); +vec3 CalcPointLightPBR(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir, vec3 albedo, float roughness, float metalness, vec3 F0); +vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir, vec3 materialColor, float timer, vec3 albedoColor, vec3 specularColor); +vec3 CalcSpotLightPBR(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir); +vec3 fresnelSchlick(float cosTheta, vec3 F0); +float DistributionGGX(vec3 N, vec3 H, float roughness); +float GeometrySchlickGGX(float NdotV, float roughness); +float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness); +vec3 CalcIBLSpecular(vec3 R, float roughness, vec3 F0); +vec3 CalcIBLDiffuse(vec3 N); + +// calculates the color when using a directional light. +vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir, vec3 ambientColor, float shadow) +{ + normal = normalize(normal); + viewDir = normalize(viewDir); + + vec3 lightDir = normalize(-light.direction); + // diffuse shading + float diff = max(dot(normal, lightDir), 0.0); + // specular shading + vec3 reflectDir = reflect(-lightDir, normal); + float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); + // combine results + vec3 ambient = ambientColor; + if (useMaterial == false) { + ambient = vec3(texture(material.ambient, TexCoords)); + } + + vec3 diffuse = light.diffuse * diff; + if (normalMapEnabled) { + diffuse *= vec3(texture(material.diffuse, TexCoords)); + } + vec3 specular = light.specular * spec; + if (specularMapEnabled) { + specular *= vec3(texture(material.specular, TexCoords)); + } + + // Darken and desaturate ambient in shadow so cores are darker and edges go through gray + vec3 ambientLit = ambient * light.ambient; + float shadowAmount = clamp(shadow, 0.0, 1.0); + + // 1) darken toward black based on shadow + vec3 ambientDark = mix(ambientLit, vec3(0.0), shadowAmount * clamp(uShadowAmbientDarken, 0.0, 1.0)); + + // 2) desaturate toward gray based on shadow + float luminance = dot(ambientDark, vec3(0.299, 0.587, 0.114)); + vec3 ambientGray = vec3(luminance); + vec3 ambientAdjusted = mix(ambientDark, ambientGray, shadowAmount * clamp(uShadowDesaturateStrength, 0.0, 1.0)); + + return ambientAdjusted + (diffuse + specular) * (1.0 - shadow); +} + +vec3 CalcDirLightPBR( + DirLight light, + vec3 normal, + vec3 fragPos, + vec3 viewDir, + vec3 ambientColor, + float roughness, + float metalness, + vec3 F0 + ) { + vec3 N = normalize(normal); + vec3 V = normalize(viewDir); + vec3 L = normalize(-light.direction); + vec3 H = normalize(V + L); + + float NdotL = max(dot(N, L), 0.0); + + // PBR Cook-Torrance + float NDF = DistributionGGX(N, H, roughness); + float G = GeometrySmith(N, V, L, roughness); + vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0); + + vec3 numerator = NDF * G * F; + float denominator = 4.0 * max(dot(N, V), 0.0) * NdotL + 0.0001; + vec3 specular = numerator / denominator; + + vec3 kS = F; + vec3 kD = vec3(1.0) - kS; + kD *= 1.0 - metalness; + + vec3 albedo = texture(material.ambient, TexCoords).rgb; + + // Výsledek je jen Diffuse + Specular od SLUNCE + return (kD * albedo / 3.14159265 + specular) * light.diffuse * NdotL; +} + +vec3 CalcDirLightMaterial(MaterialDirLight light, vec3 normal, vec3 viewDir, vec3 fragPos, vec3 ambient) +{ + vec3 lightDir = normalize(light.direction - fragPos); + // diffuse shading + float diff = max(dot(normal, lightDir), 0.0); + // specular shading + vec3 reflectDir = reflect(-lightDir, normal); + float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); + // combine results + vec3 diffuse = light.diffuse * diff; + vec3 specular = light.specular * spec; + + return (ambient + diffuse + specular); +} + +// calculates the color when using a point light. +vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir, vec3 materialColor, vec3 albedoColor, vec3 specularColor) +{ + vec3 N = normalize(normal); + vec3 lightDir = normalize(light.position - fragPos); + float diff = max(dot(N, lightDir), 0.0); + + vec3 reflectDir = reflect(-lightDir, N); + float spec = pow(max(dot(normalize(viewDir), reflectDir), 0.0), material.shininess); + + float distance = length(light.position - fragPos); + distance = max(distance, 0.1); + float ld = (light.constant + light.linear * distance + light.quadratic * distance * distance); + ld = max(ld, 0.1); + float attenuation = 1.0 / ld; + + vec3 albedo = hasAlbedoTexture ? albedoColor : materialColor; + vec3 ambient = light.ambient * albedo; + vec3 diffuse = light.diffuse * diff * albedo; + vec3 specular = useMaterial ? light.specular * spec : light.specular * spec * specularColor; + + return (ambient + diffuse + specular) * attenuation; +} + +vec3 CalcPointLightPBR(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir, vec3 albedo, float roughness, float metalness, vec3 F0) +{ + roughness = max(roughness, 0.05); + + vec3 L = normalize(light.position - fragPos); + vec3 H = normalize(viewDir + L); + + // Ochrana distance (proti crashi) + float distance = length(light.position - fragPos); + distance = max(distance, 0.05); + float ld = (light.constant + light.linear * distance + light.quadratic * distance * distance); + ld = max(ld, 0.05); + + float attenuation = 1.0 / ld; + vec3 radiance = light.diffuse * attenuation; + + float NDF = DistributionGGX(normal, H, roughness); + float G = GeometrySmith(normal, viewDir, L, roughness); + vec3 F = fresnelSchlick(max(dot(H, viewDir), 0.0), F0); + + vec3 numerator = NDF * G * F; + float denominator = 4.0 * max(dot(normal, viewDir), 0.0) * max(dot(normal, L), 0.0) + 0.0001; + vec3 specular = numerator / denominator; + + vec3 kS = F; + vec3 kD = vec3(1.0) - kS; + kD *= 1.0 - metalness; + + float NdotL = max(dot(normal, L), 0.001); + + return (kD * albedo / 3.14159265 + specular) * radiance * NdotL; +} + +float computePulse(float timer) +{ + // ==== Pulzování ==== + float pulse = 0.7f + + 0.1f * sin(timer * 0.7f) + + 0.05f * sin(timer * 1.3f + 1.1f) + + 0.03f * sin(timer * 2.1f + 2.4f); + + // Výsledná intenzita mezi 0.6 – 0.9, s velmi plynulými změnami + return clamp(pulse, 0.6f, 0.9f); +} + +// calculates the color when using a spot light. +vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir, vec3 materialColor, float timer, vec3 albedoColor, vec3 specularColor) +{ + float currentCutOff = light.cutOff; + float currentOuterCutOff = light.outerCutOff; + + if (light.pulse) { + float baseAngle = acos(light.cutOff); + float baseOuterAngle = acos(light.outerCutOff); + float angleDelta = radians(0.5) * sin(timer * 3.5); + currentCutOff = cos(baseAngle + angleDelta); + currentOuterCutOff = cos(baseOuterAngle + angleDelta); + } + vec3 lightDir = normalize(light.position - fragPos); + // diffuse shading + float diff = max(dot(normal, lightDir), 0.0); + // specular shading + vec3 reflectDir = reflect(-lightDir, normal); + float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); + // attenuation + float distance = length(light.position - fragPos); + float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance)); + // spotlight intensity + float theta = dot(lightDir, normalize(-light.direction)); + + //float offset = 0.03 * sin(timer * 2.3 + fragPos.x * 10.0) + 0.02 * sin(timer * 1.7 + fragPos.y * 12.0); + + float epsilon = currentCutOff - currentOuterCutOff; + float intensity = clamp((theta - currentOuterCutOff) / epsilon, 0.0, 1.0); + // combine results + vec3 ambient = light.ambient * (hasAlbedoTexture ? albedoColor : materialColor); + vec3 diffuse = light.diffuse * diff; + vec3 specular = light.specular * spec; + if (specularMapEnabled) { + specular = specular * specularColor; + } + ambient *= attenuation * intensity; + diffuse *= attenuation * intensity; + specular *= attenuation * intensity; + + if (light.pulse) { + float pulse = computePulse(timer); + ambient *= pulse; + diffuse *= pulse; + } + + return (ambient + diffuse + specular); +} + +//vec3 CalcSpotLightPBR(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir) +//{ +// vec3 N = normalize(normal); +// vec3 V = normalize(viewDir); +// vec3 L = normalize(light.position - fragPos); +// vec3 H = normalize(V + L); +// +// float NdotL = max(dot(N, L), 0.0); +// +// // PBR Fresnel, NDF, Geometry +// float NDF = DistributionGGX(N, H, roughness); +// float G = GeometrySmith(N, V, L, roughness); +// vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0); +// +// vec3 specular = (NDF * G * F) / max(4.0 * max(dot(N, V),0.0) * NdotL, 0.001); +// vec3 kS = F; +// vec3 kD = vec3(1.0) - kS; +// kD *= 1.0 - metalness; +// +// vec3 diffuse = kD * albedo / 3.141592; +// +// // attenuation +// float distance = length(light.position - fragPos); +// float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * distance * distance); +// +// // spotlight intensity +// float theta = dot(L, normalize(-light.direction)); +// float epsilon = light.cutOff - light.outerCutOff; +// float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0); +// +// // ambient +// vec3 ambient = light.ambient * albedo; +// +// return (ambient + (diffuse + specular) * light.diffuse * NdotL) * attenuation * intensity; +//} + +vec3 fresnelSchlick(float cosTheta, vec3 F0) +{ + return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0); +} + +float DistributionGGX(vec3 N, vec3 H, float roughness) +{ + float a = roughness * roughness; + float a2 = a * a; + float NdotH = max(dot(N, H), 0.0); + float NdotH2 = NdotH * NdotH; + + float nom = a2; + float denom = (NdotH2 * (a2 - 1.0) + 1.0); + denom = 3.14159265 * denom * denom; + return nom / max(denom, 0.000001); +} + +float GeometrySchlickGGX(float NdotV, float roughness) +{ + float r = (roughness + 1.0); + float k = (r * r) / 8.0; + + float nom = NdotV; + float denom = NdotV * (1.0 - k) + k; + return nom / max(denom, 0.000001); // Ochrana +} + +float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) +{ + float NdotV = max(dot(N, V), 0.0); + float NdotL = max(dot(N, L), 0.0); + float ggx2 = GeometrySchlickGGX(NdotV, roughness); + float ggx1 = GeometrySchlickGGX(NdotL, roughness); + + return ggx1 * ggx2; +} + +vec3 CalcIBLSpecular(vec3 R, float roughness, vec3 F0) { + vec3 prefilteredColor; + if (iblEnabled) { + return textureLod(environmentMap, R, roughness * 4.0).rgb; + } else { + prefilteredColor = F0 * vec3(0) * 0.3; + } + return F0 * prefilteredColor; +} + +vec3 CalcIBLDiffuse(vec3 N) { + // difuzní irradiance z environment mapy + vec3 irradiance = texture(environmentMap, N).rgb; + return irradiance; +} \ No newline at end of file diff --git a/Assets/Shaders/functions/reflection.glsl b/Assets/Shaders/functions/reflection.glsl new file mode 100644 index 00000000..1986a798 --- /dev/null +++ b/Assets/Shaders/functions/reflection.glsl @@ -0,0 +1,19 @@ +uniform vec4 clipPlane = vec4(0, 0, 1, 1.01); +uniform sampler2D reflectionTexture; +uniform bool reflectionEnable = false; + +vec3 calcReflexion(vec4 spacePos, vec3 color); + +vec3 calcReflexion(vec4 spacePos, vec3 color) { + // Screen-space souřadnice pro odraz + vec2 ndc = (spacePos.xy / spacePos.w) / 2.0 + 0.5; + ndc.x = 1.0 - ndc.x; + vec4 reflectionColor = texture(reflectionTexture, ndc); + + float reflectionStrength = 0.5; // Sníženo, aby více vyniklo albedo + + // Zkombinujeme barvu s odrazem + vec3 result = mix(color, reflectionColor.rgb, reflectionStrength); + + return result * vec3(1.05, 1.05, 1.05) + vec3(0.05); +} \ No newline at end of file diff --git a/Assets/Shaders/functions/shadows.glsl b/Assets/Shaders/functions/shadows.glsl new file mode 100644 index 00000000..d63aac60 --- /dev/null +++ b/Assets/Shaders/functions/shadows.glsl @@ -0,0 +1,93 @@ +#define NUM_CASCADES 3 + +uniform sampler2DArray shadowMap; + +uniform mat4 lightSpaceMatrix0; +uniform mat4 lightSpaceMatrix1; +uniform mat4 lightSpaceMatrix2; + +uniform float cascadeEnds0 = 0; +uniform float cascadeEnds1 = 1; +uniform float cascadeEnds2 = 2; + +int GetCascadeIndex(float viewDepth); +float ShadowCalculation(vec3 fragPos, int index, mat4 matrix); +float ShadowCalculation2(vec3 fragPos, vec3 normal, vec3 lightDir, int index, mat4 matrix); + +float ShadowCalculation(vec3 fragPos, int index, mat4 matrix) +{ + vec4 fragPosLightSpace = matrix * vec4(fragPos, 1.0); + // perform perspective divide + vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; + // transform to [0,1] range + projCoords = projCoords * 0.5 + 0.5; + // get closest depth value from light's perspective (using [0,1] range fragPosLight as coords) + float closestDepth = texture(shadowMap, vec3(projCoords.xy, index)).r; + // get depth of current fragment from light's perspective + float currentDepth = projCoords.z; + // check whether current frag pos is in shadow + float shadow = currentDepth > closestDepth ? 1.0 : 0.0; + + return shadow; +} + +float saturate(float val) { + return clamp(val, 0.0, 1.0); +} + +float ShadowCalculation2(vec3 fragPos, vec3 normal, vec3 lightDir, int index, mat4 matrix) +{ + const float MIN_BIAS = 0.0005; + float cosTheta = saturate(dot(normal, -lightDir)); + float sinTheta = sqrt(1.0 - cosTheta * cosTheta); + fragPos += normal * (sinTheta * MIN_BIAS); + + vec4 fragPosLightSpace = matrix * vec4(fragPos, 1.0); + vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; + projCoords = projCoords * 0.5 + 0.5; + + if (projCoords.z > 1.0) + return 0.0; + + float currentDepth = projCoords.z; + + float dotNL = max(dot(normal, lightDir), 0.0); + float bias = MIN_BIAS * (1.0 - dotNL); + + int kernel = 1; + int samples = (kernel * 2 + 1) * (kernel * 2 + 1); + vec2 texelSize = 1.0 / textureSize(shadowMap, 0).xy; + + float shadow = 0.0; + float edgeThreshold = 0.0009; // šířka hranového gradientu + + for(int x = -kernel; x <= kernel; ++x) + { + for(int y = -kernel; y <= kernel; ++y) + { + float pcfDepth = texture(shadowMap, vec3(projCoords.xy + vec2(x, y) * texelSize, index)).r; + float diff = currentDepth - bias - pcfDepth; + + // gradient jen na hraně + float edgeFactor = smoothstep(0.0, edgeThreshold, diff); + shadow += diff > 0.0 ? edgeFactor : 0.0; + } + } + + shadow /= float(samples); + + return shadow; // 0 = osvětleno, 1 = ve stínu, gradient jen na hraně +} + +int GetCascadeIndex(float viewDepth) +{ + if(viewDepth < cascadeEnds0) { + return 0; + } + + if(viewDepth < cascadeEnds1) { + return 1; + } + + return 2; +} \ No newline at end of file diff --git a/Assets/Shaders/gizmo/arrow.frag b/Assets/Shaders/gizmo/arrow.frag new file mode 100644 index 00000000..8c1fae21 --- /dev/null +++ b/Assets/Shaders/gizmo/arrow.frag @@ -0,0 +1,10 @@ +#version 330 core +out vec4 FragColor; + +uniform vec3 uColor; + +void main() +{ + // Vykreslíme plnou barvu + FragColor = vec4(uColor, 1.0); +} \ No newline at end of file diff --git a/Assets/Shaders/gizmo/arrow.vert b/Assets/Shaders/gizmo/arrow.vert new file mode 100644 index 00000000..f871119d --- /dev/null +++ b/Assets/Shaders/gizmo/arrow.vert @@ -0,0 +1,13 @@ +#version 330 core +layout (location = 0) in vec3 aPos; + +// Matice +uniform mat4 model; +uniform mat4 view; +uniform mat4 projection; + +void main() +{ + // Klasická transformace: Model -> View -> Projection + gl_Position = projection * view * model * vec4(aPos, 1.0); +} \ No newline at end of file diff --git a/Assets/Shaders/gizmo/gizmo.fs b/Assets/Shaders/gizmo/gizmo.fs new file mode 100644 index 00000000..a5f4aa62 --- /dev/null +++ b/Assets/Shaders/gizmo/gizmo.fs @@ -0,0 +1,9 @@ +#version 330 core + +out vec4 FragColor; + +uniform vec3 color; + +void main() { + FragColor = vec4(color, 1.0); +} \ No newline at end of file diff --git a/Assets/Shaders/gizmo/gizmo.geom b/Assets/Shaders/gizmo/gizmo.geom new file mode 100644 index 00000000..e3d3666b --- /dev/null +++ b/Assets/Shaders/gizmo/gizmo.geom @@ -0,0 +1,92 @@ +#version 330 core + +layout(lines) in; +layout(triangle_strip, max_vertices = 8) out; + +uniform mat4 view; +uniform mat4 proj; +uniform float thickness; +uniform vec2 viewportSize; +uniform bool isArrowHead; +uniform int axisType; // 0 = X, 1 = Y, 2 = Z + +in vec3 v_worldPos[]; +out vec3 f_worldPos; + +const float NDC_SCALE = 2.0; +const float NDC_OFFSET = 1.0; +const float HALF_FACTOR = 0.5; +const float ARROW_WIDTH_FACTOR = 2.5;// Šířka špičky šipky relativně k tloušťce čáry +const float ARROW_LENGTH_FACTOR = 3.0;// Délka špičky šipky relativně k tloušťce čáry + +vec2 worldToScreenCoords(vec4 clipPos) { + vec3 ndc = clipPos.xyz / clipPos.w; + return vec2( + (ndc.x * HALF_FACTOR + HALF_FACTOR) * viewportSize.x, + (NDC_OFFSET - (ndc.y * HALF_FACTOR + HALF_FACTOR)) * viewportSize.y + ); +} + +vec2 screenToNDC(vec2 screenPos) { + float x = (screenPos.x / viewportSize.x) * NDC_SCALE - NDC_OFFSET; + float y = NDC_OFFSET - (screenPos.y / viewportSize.y) * NDC_SCALE; + return vec2(x, y); +} + +void emitLineVertex(vec2 ndcPos, float clipZ, float clipW, vec3 worldPos) { + gl_Position = vec4(ndcPos, clipZ / clipW, 1.0) * clipW; + f_worldPos = worldPos; + EmitVertex(); +} + +void main() { + vec4 clipStart = proj * view * vec4(v_worldPos[0], 1.0); + vec4 clipEnd = proj * view * vec4(v_worldPos[1], 1.0); + + vec2 screenStart = worldToScreenCoords(clipStart); + vec2 screenEnd = worldToScreenCoords(clipEnd); + + if (isArrowHead) { + vec2 lineDir = normalize(screenEnd - screenStart); + vec2 normalDir = vec2(-lineDir.y, lineDir.x); + float arrowWidth = thickness * ARROW_WIDTH_FACTOR; + float arrowLength = thickness * ARROW_LENGTH_FACTOR; + + // Zde je ta klíčová změna - šipka musí být na konci pro každou osu + vec2 arrowTip = screenEnd; // Vždy použijeme konec čáry pro špičku + vec2 arrowBase = arrowTip - lineDir * arrowLength; + vec2 arrowLeft = arrowBase + normalDir * arrowWidth; + vec2 arrowRight = arrowBase - normalDir * arrowWidth; + + vec2 ndcTip = screenToNDC(arrowTip); + vec2 ndcLeft = screenToNDC(arrowLeft); + vec2 ndcRight = screenToNDC(arrowRight); + + emitLineVertex(ndcLeft, clipEnd.z, clipEnd.w, v_worldPos[1]); + emitLineVertex(ndcTip, clipEnd.z, clipEnd.w, v_worldPos[1]); + emitLineVertex(ndcRight, clipEnd.z, clipEnd.w, v_worldPos[1]); + + EndPrimitive(); + } else { + // Původní kód pro čáry zůstává stejný + vec2 lineDir = normalize(screenEnd - screenStart); + vec2 normalDir = vec2(-lineDir.y, lineDir.x); + float halfThickness = thickness * HALF_FACTOR; + + vec2 offsetTopStart = screenStart + normalDir * halfThickness; + vec2 offsetBottomStart = screenStart - normalDir * halfThickness; + vec2 offsetTopEnd = screenEnd + normalDir * halfThickness; + vec2 offsetBottomEnd = screenEnd - normalDir * halfThickness; + + vec2 ndcTopStart = screenToNDC(offsetTopStart); + vec2 ndcBottomStart = screenToNDC(offsetBottomStart); + vec2 ndcTopEnd = screenToNDC(offsetTopEnd); + vec2 ndcBottomEnd = screenToNDC(offsetBottomEnd); + + emitLineVertex(ndcTopStart, clipStart.z, clipStart.w, v_worldPos[0]); + emitLineVertex(ndcBottomStart, clipStart.z, clipStart.w, v_worldPos[0]); + emitLineVertex(ndcTopEnd, clipEnd.z, clipEnd.w, v_worldPos[1]); + emitLineVertex(ndcBottomEnd, clipEnd.z, clipEnd.w, v_worldPos[1]); + EndPrimitive(); + } +} \ No newline at end of file diff --git a/Assets/Shaders/gizmo/gizmo.vs b/Assets/Shaders/gizmo/gizmo.vs new file mode 100644 index 00000000..02b8ce7d --- /dev/null +++ b/Assets/Shaders/gizmo/gizmo.vs @@ -0,0 +1,17 @@ +#version 330 core + +layout(location=0) in vec3 aPos; + +uniform mat4 view; +uniform mat4 proj; +uniform vec3 gizmoCenter; +uniform float scale; + +out vec3 v_worldPos; + +void main() { + vec3 scaled = aPos * scale; + vec4 world = vec4(gizmoCenter + scaled, 1.0); + v_worldPos = world.xyz; + gl_Position = proj * view * world; +} \ No newline at end of file diff --git a/Assets/Shaders/normal_map.fs b/Assets/Shaders/normal_map.fs index f2a93fb8..8f662cfb 100644 --- a/Assets/Shaders/normal_map.fs +++ b/Assets/Shaders/normal_map.fs @@ -16,15 +16,15 @@ uniform sampler2D diffuseMap; uniform sampler2D normalMap; uniform sampler2D specularMap; -uniform vec3 lightPos; uniform vec3 viewPos; - -uniform bool useMaterial = false; uniform bool parallaxEnable = false; +vec2 TexCoords = fs_in.TexCoords; + #include "pipeline/parallax/parallax.glsl" -#include "pipeline/fog/fog.glsl" -#include "pipeline/blending/alpha.glsl" +#include "functions/fog.glsl" +#include "functions/alpha.glsl" +#include "functions/lights.glsl" void main() { @@ -33,20 +33,9 @@ void main() float ambientStrength = 0.4; vec3 ambient = ambientStrength * fs_in.Color; - // diffuse - vec3 norm = normalize(fs_in.FragPos); - vec3 lightDir = normalize(lightPos - fs_in.FragPos); - float diff = max(dot(norm, lightDir), 0.0); - vec3 diffuse = diff * vec3(0.8, 0.8, 0.8); - - // specular - float specularStrength = 0.6; vec3 viewDir = normalize(viewPos - fs_in.FragPos); - vec3 reflectDir = reflect(-lightDir, norm); - float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32); - vec3 specular = specularStrength * spec * fs_in.Color; - - vec3 result = (ambient + diffuse + specular) * fs_in.Color; + vec3 norm = normalize(fs_in.FragPos); + vec3 result = CalcDirLightMaterial(materialDirLight, norm, viewDir, fs_in.FragPos, ambient) * fs_in.Color; FragColor = alphaBlending(result); } else { vec2 texCoords = fs_in.TexCoords; @@ -65,20 +54,19 @@ void main() // get diffuse color vec3 color = texture(diffuseMap, texCoords).rgb; // ambient - vec3 ambient = 0.1 * color; + vec3 ambient = 0.0001 * color; // diffuse vec3 lightDir = normalize(fs_in.TangentLightPos - fs_in.TangentFragPos); float diff = max(dot(lightDir, normal), 0.0); - vec3 diffuse = diff * color; + vec3 diffuse = (diff * color) / 2; // specular - //vec3 viewDir = normalize(fs_in.TangentViewPos - fs_in.TangentFragPos); - vec3 reflectDir = reflect(-lightDir, normal); + //vec3 reflectDir = reflect(-lightDir, normal); vec3 halfwayDir = normalize(lightDir + viewDir); float spec = pow(max(dot(normal, halfwayDir), 0.0), 32.0); // vec3 specular = vec3(0.2) * spec; - vec3 specular = vec3(1.0, 1.0, 1.0) * spec * vec3(texture(specularMap, texCoords)); + vec3 specular = vec3(1.0, 1.0, 1.0) * spec * vec3(texture(specularMap, texCoords)) / 2; FragColor = alphaBlending(vec3(ambient + diffuse + specular)); } diff --git a/Assets/Shaders/normal_map.vs b/Assets/Shaders/normal_map.vs index 9fe12277..49efd00e 100644 --- a/Assets/Shaders/normal_map.vs +++ b/Assets/Shaders/normal_map.vs @@ -1,4 +1,5 @@ #version 330 core + layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aNormal; layout (location = 2) in vec3 aColor; @@ -26,7 +27,7 @@ uniform mat4 model; uniform vec3 lightPos; uniform vec3 viewPos; -#include "pipeline/skeleton/bonesTransform.glsl" +#include "functions/bonesTransform.glsl" void main() { diff --git a/Assets/Shaders/particle/particle_3d_render.fs b/Assets/Shaders/particle/particle_3d_render.fs new file mode 100644 index 00000000..0af1be95 --- /dev/null +++ b/Assets/Shaders/particle/particle_3d_render.fs @@ -0,0 +1,10 @@ +#version 330 core + +in vec4 vColor; +out vec4 FragColor; + +uniform float u_colorSensitivity; + +void main() { + FragColor = vColor * u_colorSensitivity; +} diff --git a/Assets/Shaders/particle/particle_3d_render.vs b/Assets/Shaders/particle/particle_3d_render.vs new file mode 100644 index 00000000..b51ca5cf --- /dev/null +++ b/Assets/Shaders/particle/particle_3d_render.vs @@ -0,0 +1,84 @@ +#version 330 core + +layout(location = 0) in vec3 aPos; +layout(location = 4) in vec3 iPos; +layout(location = 5) in vec3 iVel; +layout(location = 6) in float iLife; +layout(location = 7) in float iSeed; + +out vec4 vColor; + +uniform mat4 view; +uniform mat4 projection; +uniform mat4 model; + +uniform float u_lifeMax; +uniform float u_sizeMin; +uniform float u_sizeMax; +uniform vec4 u_colorStart; +uniform vec4 u_colorEnd; +uniform float u_stretch; +uniform int u_mode;// 0 = Billboard, 1 = Rain/Stretched + +mat3 rotateAxis(vec3 axis, float angle) { + axis = normalize(axis); + float s = sin(angle); + float c = cos(angle); + float oc = 1.0 - c; + return mat3( + oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, + oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, + oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c + ); +} + +void main() { + float t = clamp(iLife / max(u_lifeMax, 0.0001), 0.0, 1.0); + float s; + if (u_mode == 1) { + float variation = fract(sin(iSeed) * 43758.5453); + s = mix(u_sizeMin, u_sizeMax, variation); + } else { + s = mix(u_sizeMin, u_sizeMax, t); + } + vColor = mix(u_colorEnd, u_colorStart, t); + + vec3 camRight = vec3(view[0][0], view[1][0], view[2][0]); + vec3 camUp = vec3(view[0][1], view[1][1], view[2][1]); + + vec3 finalWorldPos; + + if (u_mode == 1) { + // --- RAIN / STRETCHED --- + vec3 stretchDir = normalize(iVel); + float speed = length(iVel); + vec3 offset = (camRight * aPos.x * s) + (stretchDir * aPos.z * (s + speed * u_stretch)); + + // OPRAVA: I déšť musí reagovat na 'model' matici, aby fungovalo setPosition! + finalWorldPos = (model * vec4(iPos + offset, 1.0)).xyz; + } + else if (u_mode == 2) { + // --- MESH / CUBES --- + vec3 randomAxis = normalize(vec3(sin(iSeed), cos(iSeed * 1.5), sin(iSeed * 3.2))); + float angle = iSeed * 10.0 + (1.0 - t) * 10.0; + mat3 rotation = rotateAxis(randomAxis, angle); + + vec3 localPos = rotation * (aPos * s); + // OPRAVA: Jen vypočítáme světovou pozici + finalWorldPos = (model * vec4(iPos + localPos, 1.0)).xyz; + } + else { + // --- BILLBOARD (Exploze, Oheň) --- + // Střed částice transformovaný do světa + vec3 worldCenter = (model * vec4(iPos, 1.0)).xyz; + + float sx = s; + float sy = s + length(iVel) * u_stretch; + + vec3 offset = (camRight * aPos.x * sx) + (camUp * aPos.z * sy); + finalWorldPos = worldCenter + offset; + } + + // JEDINÝ zápis do gl_Position na konci + gl_Position = projection * view * vec4(finalWorldPos, 1.0); +} \ No newline at end of file diff --git a/Assets/Shaders/particle/particle_3d_render_tex.fs b/Assets/Shaders/particle/particle_3d_render_tex.fs new file mode 100644 index 00000000..5d7448ee --- /dev/null +++ b/Assets/Shaders/particle/particle_3d_render_tex.fs @@ -0,0 +1,17 @@ +#version 330 core + +in vec2 vTex; +in vec4 vColor; +out vec4 FragColor; + +uniform sampler2D uTexture0; +uniform float u_colorSensitivity; + +void main() { + vec4 tex = texture(uTexture0, vTex); + + float alpha = tex.a * vColor.a; + vec3 color = tex.rgb * vColor.rgb * u_colorSensitivity; + + FragColor = vec4(color, alpha); +} \ No newline at end of file diff --git a/Assets/Shaders/particle/particle_3d_render_tex.vs b/Assets/Shaders/particle/particle_3d_render_tex.vs new file mode 100644 index 00000000..53f84d4d --- /dev/null +++ b/Assets/Shaders/particle/particle_3d_render_tex.vs @@ -0,0 +1,84 @@ +#version 330 core + +layout(location = 0) in vec3 aPos; +layout(location = 3) in vec2 aTex; + +layout(location = 4) in vec3 iPos; +layout(location = 5) in vec3 iVel; +layout(location = 6) in float iLife; +layout(location = 7) in float iSeed; + +out vec2 vTex; +out vec4 vColor; + +uniform mat4 view; +uniform mat4 projection; +uniform mat4 model; + +uniform float u_lifeMax; +uniform float u_sizeMin; +uniform float u_sizeMax; +uniform vec4 u_colorStart; +uniform vec4 u_colorEnd; +uniform float u_stretch; +uniform int u_mode; // 0 = Billboard, 1 = Rain/Stretched + +mat3 rotateAxis(vec3 axis, float angle) { + axis = normalize(axis); + float s = sin(angle); + float c = cos(angle); + float oc = 1.0 - c; + return mat3( + oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, + oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, + oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c + ); +} + +void main() { + vTex = aTex; + + float t = clamp(iLife / max(u_lifeMax, 0.0001), 0.0, 1.0); + float s; + if (u_mode == 1) { + float variation = fract(sin(iSeed) * 43758.5453); + s = mix(u_sizeMin, u_sizeMax, variation); + } else { + s = mix(u_sizeMin, u_sizeMax, t); + } + vColor = mix(u_colorEnd, u_colorStart, t); + + vec3 camRight = vec3(view[0][0], view[1][0], view[2][0]); + vec3 camUp = vec3(view[0][1], view[1][1], view[2][1]); + + vec3 finalWorldPos; + + if (u_mode == 1) { + // --- RAIN / STRETCHED --- + vec3 stretchDir = normalize(iVel); + float speed = length(iVel); + vec3 offset = (camRight * aPos.x * s) + (stretchDir * aPos.z * (s + speed * u_stretch)); + finalWorldPos = (model * vec4(iPos + offset, 1.0)).xyz; + } + else if (u_mode == 2) { + // --- MESH / CUBES --- + vec3 randomAxis = normalize(vec3(sin(iSeed), cos(iSeed * 1.5), sin(iSeed * 3.2))); + float angle = iSeed * 10.0 + (1.0 - t) * 10.0; + mat3 rotation = rotateAxis(randomAxis, angle); + + vec3 localPos = rotation * (aPos * s); + finalWorldPos = (model * vec4(iPos + localPos, 1.0)).xyz; + } + else { + // --- BILLBOARD (Exploze, Oheň) --- + vec3 worldCenter = (model * vec4(iPos, 1.0)).xyz; + + float sx = s; + float sy = s + length(iVel) * u_stretch; + + vec3 offset = (camRight * aPos.x * sx) + (camUp * aPos.z * sy); + finalWorldPos = worldCenter + offset; + } + + gl_Position = projection * view * vec4(finalWorldPos, 1.0); +} \ No newline at end of file diff --git a/Assets/Shaders/particle/particle_render_2d.fs b/Assets/Shaders/particle/particle_render_2d.fs new file mode 100644 index 00000000..ae436460 --- /dev/null +++ b/Assets/Shaders/particle/particle_render_2d.fs @@ -0,0 +1,44 @@ +#version 330 core + +in vec2 vUV; +in float vAlpha; + +out vec4 FragColor; + +uniform vec4 u_colorStart; +uniform vec4 u_colorEnd; + +void main() { + // Vzdálenost od středu (0.5, 0.5) -> transform na (-1..1) + vec2 center = vUV * 2.0 - 1.0; + float dist = length(center); + + // Oříznutí do kruhu + if (dist > 1.0) discard; + + // --- PROCEDURÁLNÍ VZHLED KAPKY VODY --- + + // 1. Normála (vypouklá čočka) + float z = sqrt(1.0 - dist * dist); + vec3 normal = vec3(center.x, center.y, z); + + // 2. Světlo (zprava nahoře) + vec3 lightDir = normalize(vec3(0.5, 0.5, 1.0)); + + // 3. Specular (odlesk) - malá ostrá tečka + float spec = pow(max(dot(normal, lightDir), 0.0), 40.0); + + // 4. Rim (okraj) - kapka je na okrajích trochu tmavší/výraznější + float rim = smoothstep(0.8, 1.0, dist); + + // 5. Míchání barev + vec4 baseColor = u_colorStart; + + // Voda je průhledná uprostřed, více viditelná na okrajích + odlesk + float finalAlpha = baseColor.a * (0.1 + rim * 0.5) * vAlpha; + + vec3 finalRGB = baseColor.rgb; + finalRGB += vec3(1.0) * spec; // Přičtení bílého odlesku + + FragColor = vec4(finalRGB, finalAlpha); +} \ No newline at end of file diff --git a/Assets/Shaders/particle/particle_render_2d.vs b/Assets/Shaders/particle/particle_render_2d.vs new file mode 100644 index 00000000..462050f2 --- /dev/null +++ b/Assets/Shaders/particle/particle_render_2d.vs @@ -0,0 +1,47 @@ +#version 330 core + +// Atributy modelu (Quad 1x1) +layout (location = 0) in vec2 aPos; // (-0.5 až 0.5) +layout (location = 1) in vec2 aUV; // (0.0 až 1.0) - pokud nemáš v meshi, spočítáme z aPos + +// INSTANCED atributy (z TF bufferu) - musí sedět s glVertexAttribPointer v render metodě +layout (location = 3) in vec2 iPos; +layout (location = 4) in vec2 iVel; +layout (location = 5) in float iLife; +layout (location = 6) in float iSeed; + +uniform float u_aspectRatio; // Width / Height +uniform float u_sizeMin; +uniform float u_sizeMax; + +out vec2 vUV; +out float vAlpha; + +void main() { + // 1. Určení velikosti podle rychlosti (rychlejší = větší/delší) + float speed = length(iVel); + float sizeBase = mix(u_sizeMin, u_sizeMax, clamp(speed * 2.0, 0.0, 1.0)); + + // 2. Stretch efekt (protažení ve směru pohybu) + vec2 scale = vec2(sizeBase); + + // Pokud se hýbe, protáhneme ji ve směru Y (jednoduchá verze) + // Pro komplexnější rotaci bys musel použít matici rotace podle velocity vectoru + if (speed > 0.05) { + scale.y *= (1.0 + speed * 1.5); + scale.x *= (1.0 - speed * 0.1); + } + + // 3. Výpočet pozice + // aPos * scale * aspectCorrection -> aby kruh byl kruh i na širokoúhlém monitoru + vec2 vertexPos = iPos + (aPos * scale * vec2(1.0, u_aspectRatio)); + + gl_Position = vec4(vertexPos, 0.0, 1.0); + + // UV předáme dál (nebo vypočítáme aPos + 0.5) + vUV = aPos + 0.5; + + // Fade in/out na začátku a konci života + float fade = min(iLife, 1.0); // Jednoduchý fade out na konci + vAlpha = fade; +} \ No newline at end of file diff --git a/Assets/Shaders/particle/particle_render_2d_tex.fs b/Assets/Shaders/particle/particle_render_2d_tex.fs new file mode 100644 index 00000000..243605a2 --- /dev/null +++ b/Assets/Shaders/particle/particle_render_2d_tex.fs @@ -0,0 +1,51 @@ +#version 330 core + +in vec2 vUV; +in vec2 vScreenUV; +in vec2 vVelocity; +in float vAlpha; + +out vec4 FragColor; + +uniform sampler2D uSceneTexture; +uniform sampler2D uTexture0; + +void main() { + float speed = length(vVelocity); + + vec2 trailUV = vUV; + float refractionStrength = 0.04; + vec4 normalData = texture(uTexture0, trailUV); + float shapeAlpha = normalData.a; + + if (shapeAlpha < 0.01) discard; + + vec3 normal = normalize(normalData.rgb * 2.0 - 1.0); + + normal.y = -normal.y; + vec2 offset = normal.xy * refractionStrength; + offset.y *= -1.0; + vec2 coords = vScreenUV + offset; + coords = clamp(coords, 0.005, 0.995); + vec3 sceneColor = texture(uSceneTexture, coords).rgb; + + // ------------------------------------------------------------------- + // 3. ODLESKY A STÍNOVÁNÍ + // ------------------------------------------------------------------- + + vec3 viewDir = vec3(0.0, 0.0, 1.0); + vec3 lightDir = normalize(vec3(0.5, 0.8, 0.5)); + + vec3 reflectDir = reflect(-lightDir, normal); + float spec = pow(max(dot(viewDir, reflectDir), 0.0), 64.0); + float fresnel = 1.0 - dot(normal, viewDir); + float edgeDarkening = smoothstep(0.6, 1.0, fresnel); + + vec3 finalRGB = sceneColor; + finalRGB *= (1.0 - edgeDarkening * 0.5); + finalRGB += vec3(1.0) * spec * 1.5; + + float finalAlpha = smoothstep(0.0, 0.2, shapeAlpha) * vAlpha; + + FragColor = vec4(finalRGB, finalAlpha); +} \ No newline at end of file diff --git a/Assets/Shaders/particle/particle_render_2d_tex.vs b/Assets/Shaders/particle/particle_render_2d_tex.vs new file mode 100644 index 00000000..5e442999 --- /dev/null +++ b/Assets/Shaders/particle/particle_render_2d_tex.vs @@ -0,0 +1,46 @@ +#version 330 core + +layout (location = 0) in vec2 aPos; +layout (location = 1) in vec2 aUV; +layout (location = 3) in vec3 iPos; +layout (location = 4) in vec3 iVel; +layout (location = 5) in float iLife; +layout (location = 6) in float iSeed; + +uniform int u_mode; +uniform float u_aspectRatio; +uniform float u_sizeMin; +uniform float u_sizeMax; +uniform float u_lifeMax; + +out vec2 vUV; +out vec2 vScreenUV; +out vec2 vVelocity; +out float vAlpha; + +void main() { + float speed = length(iVel); + float sizeBase = mix(u_sizeMin, u_sizeMax, clamp(speed * 3.0, 0.0, 1.0)); + + float headBoost = 0.7; + vec2 scale = vec2(sizeBase); + vec2 offsetPos = vec2(0.0); + + if (aPos.y < 0.0) { + scale.y *= 4; + offsetPos.y += 0.003; + } else { + scale *= headBoost; + } + + vec2 vertexPos = iPos.xy + ((aPos + offsetPos) * scale * vec2(1.0, u_aspectRatio)); + + vScreenUV = vertexPos * 0.5 + 0.5; + vVelocity = iVel.xy; + + vUV = aUV; + float lifeNorm = clamp(iLife / u_lifeMax, 0.0, 1.0); + vAlpha = smoothstep(0.0, 0.2, lifeNorm); + + gl_Position = vec4(vertexPos, 0.0, 1.0); +} \ No newline at end of file diff --git a/Assets/Shaders/particle/particle_update.vs b/Assets/Shaders/particle/particle_update.vs new file mode 100644 index 00000000..a9e948d4 --- /dev/null +++ b/Assets/Shaders/particle/particle_update.vs @@ -0,0 +1,86 @@ +#version 330 core + +layout(location = 0) in vec3 inPos; +layout(location = 1) in vec3 inVel; +layout(location = 2) in float inLife; +layout(location = 3) in float inSeed; + +// UNIFORM BUFFER +layout (std140) uniform ParticleParams { + vec4 u_lifeSizeStretch; // x=minLife, y=maxLife, z=minSize, w=maxSize + vec4 u_velMinStretch; // xyz=velMin, w=stretch + vec4 u_velMaxDrag; // xyz=velMax, w=drag + vec4 u_gravity; // xyz=gravity, w=colorSensitivity + vec4 u_emitterPosShape; // xyz=pos, w=spawnShape (0,1,2) + vec4 u_emitterSizeRadius; // xy=size, z=radius, w=yOffset + vec4 u_spawnArea; // x=minRad, y=maxRad, z=height, w=spawnPerFrame + vec4 u_turbulenceTime; // xy=turb, z=timeOffset, w=respawnMode + vec4 u_colorStart; + vec4 u_colorEnd; +}; + +uniform float u_dt; +uniform float u_timeAccum; +uniform float u_burstInterval = 3.0; +uniform float u_spawnWindow = 0.1; +uniform bool u_is2D; + +#define u_lifeMin u_lifeSizeStretch.x +#define u_lifeMax u_lifeSizeStretch.y +#define u_minRadius u_spawnArea.x +#define u_maxRadius u_spawnArea.y +#define u_spawnHeight u_spawnArea.z + +#define u_velMin u_velMinStretch.xyz +#define u_velMax u_velMaxDrag.xyz +#define u_drag u_velMaxDrag.w +#define u_gravityVec3 u_gravity.xyz + +#define u_emitterPos u_emitterPosShape.xyz +#define u_emitterSize u_emitterSizeRadius.xy +#define u_emitterRadius u_emitterSizeRadius.z +#define u_emitterYOffset u_emitterSizeRadius.w + +#define u_turbulence u_turbulenceTime.xy +#define u_spawnShape int(u_emitterPosShape.w + 0.1) +#define u_respawnMode int(u_turbulenceTime.w + 0.1) + +out vec3 outPos; +out vec3 outVel; +out float outLife; +out float outSeed; + +float rand(float n) { + return fract(sin(n) * 43758.5453123); +} + +struct OutputData { + vec3 outPos; + vec3 outVel; + float outLife; + float outSeed; +}; + +#include "particle_update_3d.vs" +#include "particle_update_2d.vs" + +void main() { + OutputData data; + data.outPos = inPos; + data.outVel = inVel; + data.outLife = inLife; + data.outSeed = inSeed; + + if (u_is2D) { + data = particle_update_2d(inPos, inVel, inLife, inSeed); + } else { + data = particle_update_3d(inPos, inVel, inLife, inSeed); + } + + outPos = data.outPos; + outVel = data.outVel; + outLife = data.outLife; + outSeed = data.outSeed; + + gl_Position = vec4(0.0); +} \ No newline at end of file diff --git a/Assets/Shaders/particle/particle_update_2d.vs b/Assets/Shaders/particle/particle_update_2d.vs new file mode 100644 index 00000000..658aa1fd --- /dev/null +++ b/Assets/Shaders/particle/particle_update_2d.vs @@ -0,0 +1,64 @@ +OutputData particle_update_2d(vec3 inPos, vec3 inVel, float inLife, float inSeed) { + OutputData data; + vec2 pos = inPos.xy; + vec2 vel = inVel.xy; + float newSeed = inSeed; + float newLife = inLife - u_dt; + + // 2. Kontrola smrti (vypršel čas) nebo vypadnutí z obrazovky (y < -1.2) + if (newLife <= 0.0 || pos.y < -1.2) { + + // --- RESPAWN LOGIKA --- + + // Generujeme novou náhodu + float seedBase = u_timeAccum + inSeed + gl_VertexID * 0.1; + float rnd1 = rand(seedBase); + float rnd2 = rand(seedBase + 1.0); + float rnd3 = rand(seedBase + 2.0); + + // Reset života + newLife = mix(u_lifeMin, u_lifeMax, rnd3); + + // Reset pozice + if (u_respawnMode == 1) { + // Spawn nahoře (déšť přicházející shora) + pos.x = (rnd1 * 2.0 - 1.0) * (u_emitterSize.x / 2.0) + u_emitterPos.x; + pos.y = 1.1; + } + else if (u_respawnMode == 2) { + // Celá obrazovka (pro počáteční naplnění nebo statický šum) + pos.x = (rnd1 * 2.0 - 1.0) * (u_emitterSize.x / 2.0) + u_emitterPos.x; + pos.y = (rnd2 * 2.0 - 1.0) * (u_emitterSize.y / 2.0) + u_emitterPos.y; + } + else { + // Bod (emitování z bodu) + pos = u_emitterPos.xy; + } + + // Reset rychlosti + vel = mix(u_velMin.xy, u_velMax.xy, rnd2); + + // Refresh seedu pro další cyklus + newSeed = rnd1 * 100.0; + } + else { + // --- FYZIKA POHYBU --- + vel += u_gravityVec3.xy * u_dt; + vel *= (1.0 - min(u_dt * u_drag, 1.0)); + float currentSpeed = length(vel); + float slideChance = rand(u_timeAccum * 10.0 + inSeed); + + if (currentSpeed < 0.1 && slideChance < (u_turbulence.x * u_dt * 10.0)) { + vel.y -= 0.8;// Impuls dolů + } + + pos += vel * u_dt; + } + + data.outPos = vec3(pos.xy, 0); + data.outVel = vec3(vel.xy, 0); + data.outLife = newLife; + data.outSeed = newSeed; + + return data; +} \ No newline at end of file diff --git a/Assets/Shaders/particle/particle_update_3d.vs b/Assets/Shaders/particle/particle_update_3d.vs new file mode 100644 index 00000000..2ef3347f --- /dev/null +++ b/Assets/Shaders/particle/particle_update_3d.vs @@ -0,0 +1,112 @@ +vec3 spawnRing(float seed, float minR, float maxR) { + float a = rand(seed) * 6.2831853; + float r = sqrt(mix(minR * minR, maxR * maxR, rand(seed * 1.37))); + return vec3(cos(a) * r, sin(a) * r, 0.0); +} + +OutputData particle_update_3d(vec3 inPos, vec3 inVel, float inLife, float inSeed) { + OutputData data; + vec3 pos = inPos; + vec3 vel = inVel; + float life = inLife; + bool respawn = false; + + if (life <= 0.0) { + respawn = true; + } + + if (length(vel) < 0.001 && length(u_gravityVec3) > 0.001) { + respawn = true; + } + + // SPECIÁLNÍ LOGIKA PRO ČEKÁNÍ EXPLOZE + if (u_spawnShape == 2 && respawn) { + float cycleTime = mod(u_timeAccum, u_burstInterval); + if (cycleTime > u_spawnWindow) { + data.outPos = inPos; + data.outVel = inVel; + data.outLife = inLife; + data.outSeed = inSeed; + + return data; + } + } + + // 2. FYZIKA + if (!respawn) { + if (u_turbulence.x > 0.0) { + float sway = sin(u_timeAccum * u_turbulence.y + inSeed) * u_turbulence.x; + pos.x += sway * u_dt; + pos.y += cos(u_timeAccum * (u_turbulence.y * 0.8) + inSeed) * (u_turbulence.x * 0.5) * u_dt; + } + + if (u_spawnShape == 2) vel *= pow(0.8, u_dt); + + vel += u_gravityVec3 * u_dt; + pos += vel * u_dt; + + if (u_respawnMode == 1) { + float floorLevel = u_emitterPos.z - u_spawnHeight; + if (pos.z < floorLevel) { + pos.z += u_spawnHeight * 1.5; + vec3 offset = spawnRing(inSeed + u_timeAccum, 0.0, u_maxRadius * 0.2); + pos.x += offset.x; + pos.y += offset.y; + } + + float dist = distance(pos.xy, u_emitterPos.xy); + if (dist > u_maxRadius * 1.2 || (dist < u_minRadius && pos.z < u_emitterPos.z)) { + respawn = true; + } + + life = u_lifeMax; + } else { + life -= u_dt; + } + } + + // 3. SPAWN / RESPAWN + if (respawn) { + float r0 = rand(inSeed + u_timeAccum); + float r1 = rand(inSeed * 1.45 + u_dt); + float r2 = rand(inSeed * 2.11); + + if (u_spawnShape == 1) { + vec3 ring = spawnRing(inSeed + u_timeAccum, u_minRadius, u_maxRadius); + pos.x = u_emitterPos.x + ring.x; + pos.y = u_emitterPos.y + ring.y; + pos.z = u_emitterPos.z + mix(-u_spawnHeight * 0.5, u_spawnHeight, r2); + + vel = vec3( + mix(u_velMin.x, u_velMax.x, r0), + mix(u_velMin.y, u_velMax.y, r1), + mix(u_velMin.z, u_velMax.z, r2) + ); + life = u_lifeMax; + } + else if (u_spawnShape == 2) { + float theta = r0 * 6.2831853; + float phi = r1 * 3.14159 * 0.6; + float sinPhi = sin(phi); + vec3 dir = normalize(vec3(sinPhi * cos(theta), sinPhi * sin(theta), cos(phi))); + pos = u_emitterPos + dir * (u_emitterRadius * r2); + vel = dir * mix(u_velMin.x, u_velMax.x, r2); + life = mix(u_lifeMin, u_lifeMax, r0); + } + else { + float ang = r0 * 6.2831853; + float rad = sqrt(r1); + vec2 disk = vec2(cos(ang), sin(ang)) * rad; + pos = u_emitterPos + vec3(disk.x * u_emitterRadius, disk.y * u_emitterRadius, u_emitterYOffset); + vel = mix(u_velMin, u_velMax, vec3(r0, r1, r2)); + life = mix(u_lifeMin, u_lifeMax, r2); + } + } + + data.outPos = pos; + data.outVel = vel; + data.outLife = life; + data.outSeed = inSeed; + + return data; +} \ No newline at end of file diff --git a/Assets/Shaders/pipeline/shading/shading.glsl b/Assets/Shaders/pipeline/shading/shading.glsl deleted file mode 100644 index 5c9ca03b..00000000 --- a/Assets/Shaders/pipeline/shading/shading.glsl +++ /dev/null @@ -1,34 +0,0 @@ -//float ShadowCalculation(vec4 fragPosLightSpace, sampler2D shadowMap, sampler2D normalMap) -//{ -// // perform perspective divide -// vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; -// // transform to [0,1] range -// projCoords = projCoords * 0.5 + 0.5; -// // get closest depth value from light's perspective (using [0,1] range fragPosLight as coords) -// float closestDepth = texture(shadowMap, projCoords.xy).r; -// // get depth of current fragment from light's perspective -// float currentDepth = projCoords.z; -// const float MIN_BIAS = 0.0005; -// vec3 normal = texture(normalMap, fs_in.TexCoords).rgb; -// float bias = max(0.05 * (1.0 - dot(normal, vec3(-fragPosLightSpace.xyz))), MIN_BIAS); -// // check whether current frag pos is in shadow -// float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0; -// -// return shadow; -//} - -float ShadowCalculation(vec4 fragPosLightSpace, sampler2D shadowMap) -{ - // perform perspective divide - vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; - // transform to [0,1] range - projCoords = projCoords * 0.5 + 0.5; - // get closest depth value from light's perspective (using [0,1] range fragPosLight as coords) - float closestDepth = texture(shadowMap, projCoords.xy).r; - // get depth of current fragment from light's perspective - float currentDepth = projCoords.z; - // check whether current frag pos is in shadow - float shadow = currentDepth > closestDepth ? 1.0 : 0.0; - - return shadow; -} \ No newline at end of file diff --git a/Assets/Shaders/preloader/preloader.fs b/Assets/Shaders/preloader/preloader.fs new file mode 100644 index 00000000..404d8bc8 --- /dev/null +++ b/Assets/Shaders/preloader/preloader.fs @@ -0,0 +1,10 @@ +#version 330 core + +out vec4 FragColor; + +uniform vec3 objectColor; + +void main() +{ + FragColor = vec4(objectColor, 1.0); +} \ No newline at end of file diff --git a/Assets/Shaders/preloader/preloader.vs b/Assets/Shaders/preloader/preloader.vs new file mode 100644 index 00000000..ee4df6bd --- /dev/null +++ b/Assets/Shaders/preloader/preloader.vs @@ -0,0 +1,12 @@ +#version 330 core + +layout (location = 0) in vec3 aPos; + +uniform mat4 model; +uniform mat4 view; +uniform mat4 projection; + +void main() +{ + gl_Position = projection * view * model * vec4(aPos, 1.0); +} \ No newline at end of file diff --git a/Assets/Shaders/radar.fs b/Assets/Shaders/radar.fs deleted file mode 100644 index e20a4b5f..00000000 --- a/Assets/Shaders/radar.fs +++ /dev/null @@ -1,17 +0,0 @@ -#version 330 core -out vec4 FragColor; - -in vec2 TexCoords; - -uniform sampler2D texture_diffuse1; -uniform vec3 Color; -uniform bool useMaterial = false; - -void main() -{ - if (useMaterial) { - FragColor = vec4(Color, 0); - } else { - FragColor = texture(texture_diffuse1, TexCoords); - } -} \ No newline at end of file diff --git a/Assets/Shaders/radar.vs b/Assets/Shaders/radar.vs deleted file mode 100644 index ab6f8504..00000000 --- a/Assets/Shaders/radar.vs +++ /dev/null @@ -1,17 +0,0 @@ -#version 330 core -layout (location = 0) in vec3 aPos; -layout (location = 1) in vec3 aNormal; -layout (location = 2) in vec2 aColor; -layout (location = 3) in vec2 aTexCoords; - -out vec2 TexCoords; - -uniform mat4 model; -uniform mat4 view; -uniform mat4 projection; - -void main() -{ - TexCoords = aTexCoords; - gl_Position = projection * model * vec4(aPos.x, aPos.y, 0.0, 1.0); -} \ No newline at end of file diff --git a/Assets/Shaders/rain/raindrop.fs b/Assets/Shaders/rain/raindrop.fs index b55967a4..fc4463db 100644 --- a/Assets/Shaders/rain/raindrop.fs +++ b/Assets/Shaders/rain/raindrop.fs @@ -6,7 +6,7 @@ in vec3 fragPos; uniform float TIME; -uniform float roughness = 0.2; +// uniform float roughness = 0.2; uniform float normal_scale = 2.0; uniform float ring_width = 0.005; uniform float fadeout = 0.3; @@ -42,9 +42,4 @@ void main() normap = mix(normap, vec3(inverted_nor, 0.0), ring_outer); FragColor = vec4(normap, 1); - - //ALBEDO = water_color.rgb; - //ROUGHNESS = roughness; - //NORMALMAP = normap; - //NORMALMAP_DEPTH = clamp(fade, 0.0, 1.0) * normal_scale; } \ No newline at end of file diff --git a/Assets/Shaders/respawn/respawn.fs b/Assets/Shaders/respawn/respawn.fs new file mode 100644 index 00000000..c8b828c0 --- /dev/null +++ b/Assets/Shaders/respawn/respawn.fs @@ -0,0 +1,67 @@ +#version 330 core + +in vec2 TexCoords; +in vec3 meshColor; +in vec3 Normal; +in vec3 camPos; +in vec3 fragPos; + +uniform sampler2D u_NoiseTexture; +uniform vec4 u_LightColor; +uniform float u_Speed; +uniform float u_FloatParameter; +uniform float u_Time; +uniform float u_Delay; +uniform bool u_useMaterial = false; + +out vec4 FragColor; + +#include "../functions/lights.glsl" + +void main() +{ + // 1. Získání hodnoty šumu + vec4 noiseSample = texture(u_NoiseTexture, TexCoords); + float noiseR = noiseSample.r; + float pi = 3.14159; + + // 2. Výpočet pulzující hodnoty animace (0 -> 1) + // Čas animace s delay + // Délka animace v sekundách, můžeš ji např. počítat jako + float duration = pi / u_Speed; // π / u_Speed, tj. půlvlnu sin + + // Čas animace s delay a ořezem na max duration + float animTime = clamp(max(u_Time - u_Delay, 0.0), 0.0, duration); + + // Výpočet fáze s posunem -π/2, aby začínala od 0 + float t = animTime / duration * pi; // 0 až π + float remapped = (sin(t - (pi / 2)) + 1.0) * 0.5; + + // ================================================================= + // Pokud je hodnota šumu menší než práh animace, pixel se nezobrazí. + // ================================================================= + if (noiseR > remapped) { + discard; + } + + // 3. Výpočet prahu pro zářící okraj + float edgeThreshold = remapped + u_FloatParameter; + float stepValue = step(noiseR, edgeThreshold); + + // ================================================================= + // Místo `vec3(stepValue)` (což dává bílou), použijeme `stepValue` + // jako násobič pro u_LightColor. + // ================================================================= + vec3 emission = stepValue * mix(u_LightColor.rgb, meshColor.rgb, float(u_useMaterial)); + + // 4. Finální barva + // Sečteme základní barvu a barvu emise. + // Alfa kanál vezmeme z textury šumu, aby okraj byl jemnější. + vec3 finalColor = mix(meshColor.rgb, emission, stepValue); + vec3 viewDir = normalize(camPos - fragPos); + + finalColor = CalcDirLight(dirLight, Normal, viewDir, emission, 0.0); + finalColor /= 1; + + FragColor = vec4(pow(finalColor, vec3(1.0/2.2)), 1.0); +} \ No newline at end of file diff --git a/Assets/Shaders/shadow_map.fs b/Assets/Shaders/shadow_map.fs index de9c0d4d..f5e30b6e 100644 --- a/Assets/Shaders/shadow_map.fs +++ b/Assets/Shaders/shadow_map.fs @@ -5,23 +5,25 @@ in VS_OUT { vec3 FragPos; vec3 Normal; vec2 TexCoords; - vec4 FragPosLightSpace; } fs_in; +uniform float uTime = 1; +uniform sampler2D specularMap; uniform sampler2D diffuseMap; -uniform sampler2D shadowMap; uniform sampler2D normalMap; -uniform sampler2D specularMap; - uniform vec3 lightPos; uniform vec3 viewPos; -uniform bool shadowsEnable = true; +uniform bool shadowsEnable; + +vec2 TexCoords = fs_in.TexCoords; -#include "pipeline/shading/shading.glsl" -#include "pipeline/fog/fog.glsl" +#include "functions/shadows.glsl" +#include "functions/fog.glsl" +#include "functions/lights.glsl" void main() { + vec3 specularMapValue = texture(specularMap, fs_in.TexCoords).rgb; vec3 normal = texture(normalMap, fs_in.TexCoords).rgb; vec3 color = texture(diffuseMap, fs_in.TexCoords).rgb; if (length(normal) == 0.0) // normal texture is not set @@ -33,6 +35,7 @@ void main() vec3 lightColor = vec3(0.3); if (shadowsEnable == false) { lightColor = texture(diffuseMap, fs_in.TexCoords).rgb; + lightColor /= 2; } // ambient vec3 ambient = 0.3 * lightColor; @@ -48,15 +51,40 @@ void main() spec = pow(max(dot(normal, halfwayDir), 0.0), 64.0); vec3 specular = spec * lightColor; - if (shadowsEnable == false) { - spec = pow(max(dot(normal, halfwayDir), 0.0), 32.0); - specular = vec3(1.0, 1.0, 1.0) * spec * vec3(texture(specularMap, fs_in.TexCoords)); - FragColor = vec4(ambient + diffuse + specular, 1.0); - } else { + if (shadowsEnable) { // calculate shadow - float shadow = ShadowCalculation(fs_in.FragPosLightSpace, shadowMap); + float viewDepth = length(fs_in.FragPos - viewPos); + int cascadeIndex = int(GetCascadeIndex(viewDepth)); + float shadow = 0.0; + if(cascadeIndex == 0) + shadow = ShadowCalculation(fs_in.FragPos, cascadeIndex, lightSpaceMatrix0); + else if(cascadeIndex == 1) + shadow = ShadowCalculation(fs_in.FragPos, cascadeIndex, lightSpaceMatrix1); + else + shadow = ShadowCalculation(fs_in.FragPos, cascadeIndex, lightSpaceMatrix2); + +// if(cascadeIndex == 0) +// shadow = ShadowCalculation2(fs_in.FragPos, normalize(fs_in.Normal), -lightDir, cascadeIndex, lightSpaceMatrix0); +// else if(cascadeIndex == 1) +// shadow = ShadowCalculation2(fs_in.FragPos, normalize(fs_in.Normal), -lightDir, cascadeIndex, lightSpaceMatrix1); +// else +// shadow = ShadowCalculation2(fs_in.FragPos, normalize(fs_in.Normal), -lightDir, cascadeIndex, lightSpaceMatrix2); + vec3 lighting = (ambient + (1.0 - shadow) * (diffuse + specular)) * color; FragColor = vec4(lighting, 1.0); + } else { + spec = pow(max(dot(normal, halfwayDir), 0.0), 32.0); + specular = vec3(1.0, 1.0, 1.0) * spec * specularMapValue; +// specular = vec3(0.0); + + vec3 norm = normalize(fs_in.Normal); + vec3 result = vec3(0); + +// for(int i = 0; i < numSpotLights; i++) { +// result += CalcSpotLight(spotLight[i], normal, fs_in.FragPos, viewDir, ambient, uTime); +// } + + FragColor = vec4(result + lightColor / 4 + specular, 1.0); } if (fogEnable) { diff --git a/Assets/Shaders/shadow_map.vs b/Assets/Shaders/shadow_map.vs index db13cd46..dd36ea73 100644 --- a/Assets/Shaders/shadow_map.vs +++ b/Assets/Shaders/shadow_map.vs @@ -9,7 +9,6 @@ out VS_OUT { vec3 FragPos; vec3 Normal; vec2 TexCoords; - vec4 FragPosLightSpace; } vs_out; uniform mat4 projection; @@ -22,6 +21,5 @@ void main() vs_out.FragPos = vec3(model * vec4(aPos, 1.0)); vs_out.Normal = transpose(inverse(mat3(model))) * aNormal; vs_out.TexCoords = aTexCoords; - vs_out.FragPosLightSpace = lightSpaceMatrix * vec4(vs_out.FragPos, 1.0); gl_Position = projection * view * model * vec4(aPos, 1.0); } \ No newline at end of file diff --git a/Assets/Shaders/skybox.fs b/Assets/Shaders/skybox.fs index 9cede7a7..30b3ffd4 100644 --- a/Assets/Shaders/skybox.fs +++ b/Assets/Shaders/skybox.fs @@ -7,5 +7,5 @@ uniform samplerCube skybox; void main() { - FragColor = texture(skybox, TexCoords); + FragColor = texture(skybox, TexCoords) / 3; } \ No newline at end of file diff --git a/Assets/Shaders/skybox.vs b/Assets/Shaders/skybox.vs index 5a21be94..da52bf5f 100644 --- a/Assets/Shaders/skybox.vs +++ b/Assets/Shaders/skybox.vs @@ -5,10 +5,11 @@ out vec3 TexCoords; uniform mat4 projection; uniform mat4 view; +uniform mat4 model; void main() { - TexCoords = aPos; + TexCoords = mat3(model) * aPos; vec4 pos = projection * view * vec4(aPos, 1.0); gl_Position = pos.xyww; } \ No newline at end of file diff --git a/Assets/Shaders/text.fs b/Assets/Shaders/text.fs index 6ebd4ad1..7d4d3d81 100644 --- a/Assets/Shaders/text.fs +++ b/Assets/Shaders/text.fs @@ -1,14 +1,15 @@ #version 330 core + in vec2 TexCoords; -out vec4 color; +out vec4 FragColor; -uniform sampler2D text; +uniform sampler2D textTexture; uniform vec3 textColor; -#include "pipeline/blending/alpha.glsl" +#include "functions/alpha.glsl" void main() { - vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r); - color = alphaBlending(vec3(1, 1, 1)) * sampled; + vec4 sampled = vec4(1.0, 1.0, 1.0, texture(textTexture, TexCoords).r); + FragColor = alphaBlending(textColor) * sampled; } \ No newline at end of file diff --git a/Assets/Shaders/text.vs b/Assets/Shaders/text.vs index 94b6118d..44446e38 100644 --- a/Assets/Shaders/text.vs +++ b/Assets/Shaders/text.vs @@ -1,11 +1,14 @@ #version 330 core -layout (location = 0) in vec4 vertex; // + +layout (location = 0) in vec4 vertex; + out vec2 TexCoords; uniform mat4 projection; +uniform mat4 model; void main() { - gl_Position = projection * vec4(vertex.xy, 0.0, 1.0); + gl_Position = projection * model * vec4(vertex.xy,0.0,1.0); TexCoords = vertex.zw; } \ No newline at end of file diff --git a/Assets/Textures/Albedo/box/brickwall.jpg b/Assets/Textures/Albedo/box/brickwall.jpg new file mode 100644 index 00000000..39478d3e Binary files /dev/null and b/Assets/Textures/Albedo/box/brickwall.jpg differ diff --git a/Assets/Textures/bricks2.jpg b/Assets/Textures/Albedo/bricks2.jpg similarity index 100% rename from Assets/Textures/bricks2.jpg rename to Assets/Textures/Albedo/bricks2.jpg diff --git a/Assets/Textures/bricks2_disp.jpg b/Assets/Textures/Albedo/bricks2_disp.jpg similarity index 100% rename from Assets/Textures/bricks2_disp.jpg rename to Assets/Textures/Albedo/bricks2_disp.jpg diff --git a/Assets/Textures/brickwork-cavity.jpg b/Assets/Textures/Albedo/brickwork-cavity.jpg similarity index 100% rename from Assets/Textures/brickwork-cavity.jpg rename to Assets/Textures/Albedo/brickwork-cavity.jpg diff --git a/Assets/Textures/brickwork-texture.jpg b/Assets/Textures/Albedo/brickwork-texture.jpg similarity index 100% rename from Assets/Textures/brickwork-texture.jpg rename to Assets/Textures/Albedo/brickwork-texture.jpg diff --git a/Assets/Textures/coin/Coin_Gold_albedo.png b/Assets/Textures/Albedo/coin/Coin_Gold_albedo.png similarity index 100% rename from Assets/Textures/coin/Coin_Gold_albedo.png rename to Assets/Textures/Albedo/coin/Coin_Gold_albedo.png diff --git a/Assets/Textures/Albedo/explosion/explosion.png b/Assets/Textures/Albedo/explosion/explosion.png new file mode 100644 index 00000000..086e2e71 Binary files /dev/null and b/Assets/Textures/Albedo/explosion/explosion.png differ diff --git a/Assets/Textures/Albedo/fast_noise.bmp b/Assets/Textures/Albedo/fast_noise.bmp new file mode 100644 index 00000000..3b042728 Binary files /dev/null and b/Assets/Textures/Albedo/fast_noise.bmp differ diff --git a/Assets/Textures/Albedo/fire/fire.png b/Assets/Textures/Albedo/fire/fire.png new file mode 100644 index 00000000..26272ed1 Binary files /dev/null and b/Assets/Textures/Albedo/fire/fire.png differ diff --git a/Assets/Textures/Albedo/fire/smoke.png b/Assets/Textures/Albedo/fire/smoke.png new file mode 100644 index 00000000..0f1007ee Binary files /dev/null and b/Assets/Textures/Albedo/fire/smoke.png differ diff --git a/Assets/Textures/gamefield.bmp b/Assets/Textures/Albedo/gamefield/gamefield.bmp similarity index 100% rename from Assets/Textures/gamefield.bmp rename to Assets/Textures/Albedo/gamefield/gamefield.bmp diff --git a/Assets/Textures/gamefield_specular.jpg b/Assets/Textures/Albedo/gamefield/gamefield_specular.jpg similarity index 100% rename from Assets/Textures/gamefield_specular.jpg rename to Assets/Textures/Albedo/gamefield/gamefield_specular.jpg diff --git a/Assets/Textures/Albedo/gamefield/tile.png b/Assets/Textures/Albedo/gamefield/tile.png new file mode 100644 index 00000000..6b55aa8e Binary files /dev/null and b/Assets/Textures/Albedo/gamefield/tile.png differ diff --git a/Assets/Textures/head.bmp b/Assets/Textures/Albedo/head.bmp similarity index 100% rename from Assets/Textures/head.bmp rename to Assets/Textures/Albedo/head.bmp diff --git a/Assets/Textures/rain.jpg b/Assets/Textures/Albedo/rain.jpg similarity index 100% rename from Assets/Textures/rain.jpg rename to Assets/Textures/Albedo/rain.jpg diff --git a/Assets/Textures/Albedo/rain/rain.png b/Assets/Textures/Albedo/rain/rain.png new file mode 100644 index 00000000..8dbce973 Binary files /dev/null and b/Assets/Textures/Albedo/rain/rain.png differ diff --git a/Assets/Textures/rain/raindrops.png b/Assets/Textures/Albedo/rain/raindrops.png similarity index 100% rename from Assets/Textures/rain/raindrops.png rename to Assets/Textures/Albedo/rain/raindrops.png diff --git a/Assets/Textures/rain/raindrops_nor.png b/Assets/Textures/Albedo/rain/raindrops_nor.png similarity index 100% rename from Assets/Textures/rain/raindrops_nor.png rename to Assets/Textures/Albedo/rain/raindrops_nor.png diff --git a/Assets/Textures/Albedo/rusted/rusted_albedo.png b/Assets/Textures/Albedo/rusted/rusted_albedo.png new file mode 100644 index 00000000..91896adf Binary files /dev/null and b/Assets/Textures/Albedo/rusted/rusted_albedo.png differ diff --git a/Assets/Textures/snake.bmp b/Assets/Textures/Albedo/snake.bmp similarity index 100% rename from Assets/Textures/snake.bmp rename to Assets/Textures/Albedo/snake.bmp diff --git a/Assets/Textures/Albedo/snow/snow.png b/Assets/Textures/Albedo/snow/snow.png new file mode 100644 index 00000000..f7678c02 Binary files /dev/null and b/Assets/Textures/Albedo/snow/snow.png differ diff --git a/Assets/Textures/tile.bmp b/Assets/Textures/Albedo/tile.bmp similarity index 100% rename from Assets/Textures/tile.bmp rename to Assets/Textures/Albedo/tile.bmp diff --git a/Assets/Textures/Albedo/torch.png b/Assets/Textures/Albedo/torch.png new file mode 100644 index 00000000..7b2c5610 Binary files /dev/null and b/Assets/Textures/Albedo/torch.png differ diff --git a/Assets/Textures/coin/Coin_Gold_metalness.png b/Assets/Textures/Others/Coin_Gold_metalness.png similarity index 100% rename from Assets/Textures/coin/Coin_Gold_metalness.png rename to Assets/Textures/Others/Coin_Gold_metalness.png diff --git a/Assets/Textures/coin/Coin_Gold_nm.png b/Assets/Textures/Others/Coin_Gold_nm.png similarity index 100% rename from Assets/Textures/coin/Coin_Gold_nm.png rename to Assets/Textures/Others/Coin_Gold_nm.png diff --git a/Assets/Textures/coin/Coin_Gold_rough.png b/Assets/Textures/Others/Coin_Gold_rough.png similarity index 100% rename from Assets/Textures/coin/Coin_Gold_rough.png rename to Assets/Textures/Others/Coin_Gold_rough.png diff --git a/Assets/Textures/Others/ao.png b/Assets/Textures/Others/ao.png new file mode 100644 index 00000000..5ca4099f Binary files /dev/null and b/Assets/Textures/Others/ao.png differ diff --git a/Assets/Textures/bricks2_normal.jpg b/Assets/Textures/Others/bricks2_normal.jpg similarity index 100% rename from Assets/Textures/bricks2_normal.jpg rename to Assets/Textures/Others/bricks2_normal.jpg diff --git a/Assets/Textures/Others/brickwall_normal.jpg b/Assets/Textures/Others/brickwall_normal.jpg new file mode 100644 index 00000000..a0a5fb62 Binary files /dev/null and b/Assets/Textures/Others/brickwall_normal.jpg differ diff --git a/Assets/Textures/brickwork-bump-map.jpg b/Assets/Textures/Others/brickwork-bump-map.jpg similarity index 100% rename from Assets/Textures/brickwork-bump-map.jpg rename to Assets/Textures/Others/brickwork-bump-map.jpg diff --git a/Assets/Textures/brickwork_normal-map.jpg b/Assets/Textures/Others/brickwork_normal-map.jpg similarity index 100% rename from Assets/Textures/brickwork_normal-map.jpg rename to Assets/Textures/Others/brickwork_normal-map.jpg diff --git a/Assets/Textures/Others/drop_normal.png b/Assets/Textures/Others/drop_normal.png new file mode 100644 index 00000000..7c189d3d Binary files /dev/null and b/Assets/Textures/Others/drop_normal.png differ diff --git a/Assets/Textures/gamefield_normal.jpg b/Assets/Textures/Others/gamefield_normal.jpg similarity index 100% rename from Assets/Textures/gamefield_normal.jpg rename to Assets/Textures/Others/gamefield_normal.jpg diff --git a/Assets/Textures/Others/rusted_metallic.png b/Assets/Textures/Others/rusted_metallic.png new file mode 100644 index 00000000..76ca751b Binary files /dev/null and b/Assets/Textures/Others/rusted_metallic.png differ diff --git a/Assets/Textures/Others/rusted_normal.png b/Assets/Textures/Others/rusted_normal.png new file mode 100644 index 00000000..f604ab51 Binary files /dev/null and b/Assets/Textures/Others/rusted_normal.png differ diff --git a/Assets/Textures/Others/rusted_roughness.png b/Assets/Textures/Others/rusted_roughness.png new file mode 100644 index 00000000..c5cecf79 Binary files /dev/null and b/Assets/Textures/Others/rusted_roughness.png differ diff --git a/Assets/Textures/Others/torch_normal.png b/Assets/Textures/Others/torch_normal.png new file mode 100644 index 00000000..a74f2814 Binary files /dev/null and b/Assets/Textures/Others/torch_normal.png differ diff --git a/Assets/Textures/radar/red_screen.bmp b/Assets/Textures/radar/red_screen.bmp deleted file mode 100755 index 8ec80459..00000000 Binary files a/Assets/Textures/radar/red_screen.bmp and /dev/null differ diff --git a/Assets/texture_manifest.json b/Assets/texture_manifest.json new file mode 100644 index 00000000..2c66ce6c --- /dev/null +++ b/Assets/texture_manifest.json @@ -0,0 +1,182 @@ +[ + { + "name": "brickwall.jpg", + "path": "Albedo/box/brickwall.jpg", + "category": "Albedo" + }, + { + "name": "bricks2.jpg", + "path": "Albedo/bricks2.jpg", + "category": "Albedo" + }, + { + "name": "bricks2_disp.jpg", + "path": "Albedo/bricks2_disp.jpg", + "category": "Albedo" + }, + { + "name": "brickwork-cavity.jpg", + "path": "Albedo/brickwork-cavity.jpg", + "category": "Albedo" + }, + { + "name": "brickwork-texture.jpg", + "path": "Albedo/brickwork-texture.jpg", + "category": "Albedo" + }, + { + "name": "Coin_Gold_albedo.png", + "path": "Albedo/coin/Coin_Gold_albedo.png", + "category": "Albedo" + }, + { + "name": "fast_noise.bmp", + "path": "Albedo/fast_noise.bmp", + "category": "Albedo" + }, + { + "name": "fire.png", + "path": "Albedo/fire/fire.png", + "category": "Albedo" + }, + { + "name": "smoke.png", + "path": "Albedo/fire/smoke.png", + "category": "Albedo" + }, + { + "name": "gamefield.bmp", + "path": "Albedo/gamefield/gamefield.bmp", + "category": "Albedo" + }, + { + "name": "gamefield_specular.jpg", + "path": "Albedo/gamefield/gamefield_specular.jpg", + "category": "Albedo" + }, + { + "name": "tile.png", + "path": "Albedo/gamefield/tile.png", + "category": "Albedo" + }, + { + "name": "head.bmp", + "path": "Albedo/head.bmp", + "category": "Albedo" + }, + { + "name": "rain.png", + "path": "Albedo/rain/rain.png", + "category": "Albedo" + }, + { + "name": "raindrops.png", + "path": "Albedo/rain/raindrops.png", + "category": "Albedo" + }, + { + "name": "raindrops_nor.png", + "path": "Albedo/rain/raindrops_nor.png", + "category": "Albedo" + }, + { + "name": "snow.png", + "path": "Albedo/snow/snow.png", + "category": "Albedo" + }, + { + "name": "explosion.png", + "path": "Albedo/explosion/explosion.png", + "category": "Albedo" + }, + { + "name": "rusted_albedo.png", + "path": "Albedo/rusted/rusted_albedo.png", + "category": "Albedo" + }, + { + "name": "snake.bmp", + "path": "Albedo/snake.bmp", + "category": "Albedo" + }, + { + "name": "tile.bmp", + "path": "Albedo/tile.bmp", + "category": "Albedo" + }, + { + "name": "torch.png", + "path": "Albedo/torch.png", + "category": "Albedo" + }, + { + "name": "Coin_Gold_metalness.png", + "path": "Others/Coin_Gold_metalness.png", + "category": "Others" + }, + { + "name": "Coin_Gold_nm.png", + "path": "Others/Coin_Gold_nm.png", + "category": "Others" + }, + { + "name": "Coin_Gold_rough.png", + "path": "Others/Coin_Gold_rough.png", + "category": "Others" + }, + { + "name": "ao.png", + "path": "Others/ao.png", + "category": "Others" + }, + { + "name": "bricks2_normal.jpg", + "path": "Others/bricks2_normal.jpg", + "category": "Others" + }, + { + "name": "brickwall_normal.jpg", + "path": "Others/brickwall_normal.jpg", + "category": "Others" + }, + { + "name": "brickwork-bump-map.jpg", + "path": "Others/brickwork-bump-map.jpg", + "category": "Others" + }, + { + "name": "brickwork_normal-map.jpg", + "path": "Others/brickwork_normal-map.jpg", + "category": "Others" + }, + { + "name": "gamefield_normal.jpg", + "path": "Others/gamefield_normal.jpg", + "category": "Others" + }, + { + "name": "rusted_metallic.png", + "path": "Others/rusted_metallic.png", + "category": "Others" + }, + { + "name": "rusted_normal.png", + "path": "Others/rusted_normal.png", + "category": "Others" + }, + { + "name": "rusted_roughness.png", + "path": "Others/rusted_roughness.png", + "category": "Others" + }, + { + "name": "torch_normal.png", + "path": "Others/torch_normal.png", + "category": "Others" + }, + { + "name": "drop_normal.png", + "path": "Others/drop_normal.png", + "category": "Others" + } +] diff --git a/CMakeLists.txt b/CMakeLists.txt index c3db797c..ef766629 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,18 +6,643 @@ set(CMAKE_CXX_STANDARD 23) find_package(glfw3 3.1 REQUIRED) find_package(OpenGL REQUIRED) find_package(OpenAL REQUIRED) +find_package(assimp 5.2 REQUIRED) +find_package(nlohmann_json CONFIG REQUIRED) + include_directories(/usr/include/freetype2) include_directories(/usr/include/AL) -add_executable(Snake3 main.cpp ItemsDto/BaseItem.cpp ItemsDto/BaseItem.h stdafx.h ItemsDto/Cube.cpp ItemsDto/Cube.h App.cpp App.h Manager/ResourceManager.cpp Manager/ResourceManager.h ItemsDto/GameField.cpp ItemsDto/GameField.h Renderer/Opengl/GameFieldRenderer.cpp Renderer/Opengl/GameFieldRenderer.h Renderer/Opengl/BaseRenderer.cpp Renderer/Opengl/BaseRenderer.h Manager/RenderManager.cpp Manager/RenderManager.h ItemsDto/Snake.cpp ItemsDto/Snake.h Renderer/Opengl/SnakeRenderer.cpp Renderer/Opengl/SnakeRenderer.h Manager/KeyboardManager.cpp Manager/KeyboardManager.h Handler/SnakeMoveHandler.cpp Handler/SnakeMoveHandler.h Handler/BaseHandler.cpp Handler/BaseHandler.h Physic/CollisionDetector.cpp Physic/CollisionDetector.h ItemsDto/BaseContainer.cpp ItemsDto/BaseContainer.h ItemsDto/BaseContainerInterface.cpp ItemsDto/BaseContainerInterface.h Renderer/Opengl/EatRenderer.cpp Renderer/Opengl/EatRenderer.h Renderer/Opengl/TextRenderer.cpp Renderer/Opengl/TextRenderer.h ItemsDto/Text.cpp ItemsDto/Text.h Manager/EatManager.cpp Manager/EatManager.h Handler/EatLocationHandler.cpp Handler/EatLocationHandler.h ItemsDto/Eat.cpp ItemsDto/Eat.h ItemsDto/ObjItem.cpp ItemsDto/ObjItem.h ItemsDto/Radar.cpp ItemsDto/Radar.h Renderer/Opengl/RadarRenderer.cpp Renderer/Opengl/RadarRenderer.h Handler/RadarHandler.cpp Handler/RadarHandler.h Handler/BaseKeydownHandle.cpp Handler/BaseKeydownHandle.h Renderer/Opengl/EatRemoveAnimateRenderer.cpp Renderer/Opengl/EatRemoveAnimateRenderer.h ItemsDto/Barriers.cpp ItemsDto/Barriers.h Renderer/Opengl/BarrierRenderer.cpp Renderer/Opengl/BarrierRenderer.h Manager/ShaderManager.cpp Manager/ShaderManager.h Manager/LevelManager.cpp Manager/LevelManager.h Manager/VboIndexer.cpp Manager/VboIndexer.h ItemsDto/ObjWall.cpp ItemsDto/ObjWall.h Renderer/Opengl/ObjWallRenderer.cpp Renderer/Opengl/ObjWallRenderer.h Manager/Camera.cpp Manager/Camera.h Renderer/Opengl/Model/TextModel.cpp Renderer/Opengl/Model/TextModel.h Renderer/Opengl/Model/Character.cpp Renderer/Opengl/Model/Character.h Renderer/Opengl/Model/Utils/Vao.cpp Renderer/Opengl/Model/Utils/Vao.h Renderer/Opengl/Model/Utils/Vbo.cpp Renderer/Opengl/Model/Utils/Vbo.h Renderer/Opengl/Model/Utils/Ebo.cpp Renderer/Opengl/Model/Utils/Ebo.h Renderer/Opengl/Model/Utils/Mesh.cpp Renderer/Opengl/Model/Utils/Mesh.h Renderer/Opengl/Model/GameFieldModel.cpp Renderer/Opengl/Model/GameFieldModel.h Renderer/Opengl/Model/RadarModel.cpp Renderer/Opengl/Model/RadarModel.h Resource/ObjModelLoader.cpp Resource/ObjModelLoader.h Resource/TextureLoader.cpp Resource/TextureLoader.h Resource/ShaderLoader.cpp Resource/ShaderLoader.h Manager/TextureManager.cpp Manager/TextureManager.h Renderer/Opengl/SkyboxRenderer.cpp Renderer/Opengl/SkyboxRenderer.h Renderer/Opengl/DepthMapRenderer.cpp Renderer/Opengl/DepthMapRenderer.h Renderer/Opengl/Line.h Renderer/Opengl/BloomRenderer.cpp Renderer/Opengl/BloomRenderer.h Renderer/Opengl/Model/RainModel.cpp Renderer/Opengl/Model/RainModel.h Renderer/Opengl/RainRenderer.cpp Renderer/Opengl/RainRenderer.h Renderer/Opengl/RainDropRenderer.cpp Renderer/Opengl/RainDropRenderer.h Renderer/Opengl/Model/MeshModel.cpp Renderer/Opengl/Model/MeshModel.h Resource/AnimLoader.cpp Resource/AnimLoader.h ItemsDto/AnimItem.cpp ItemsDto/AnimItem.h Renderer/Opengl/Model/AnimationModel.cpp Renderer/Opengl/Model/AnimationModel.h Renderer/Opengl/AnimRenderer.cpp Renderer/Opengl/AnimRenderer.h Renderer/Opengl/Model/Utils/Tree.h) -target_link_libraries(${PROJECT_NAME} glfw glut GLU GL GLEW freetype openal alut assimp) +add_custom_target(copy_assets ALL + COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/Assets + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/Assets ${CMAKE_BINARY_DIR}/Assets + COMMENT "Cleaning and copying Assets folder" +) + +add_executable(Snake3 main.cpp ItemsDto/BaseItem.cpp ItemsDto/BaseItem.h stdafx.h App.cpp App.h Manager/ResourceManager.cpp Manager/ResourceManager.h Renderer/Opengl/BaseRenderer.cpp Renderer/Opengl/BaseRenderer.h Manager/RenderManager.cpp Manager/RenderManager.h Manager/KeyboardManager.cpp Manager/KeyboardManager.h Handler/SnakeMoveHandler.cpp Handler/SnakeMoveHandler.h Handler/BaseHandler.cpp Handler/BaseHandler.h Physic/CollisionDetector.cpp Physic/CollisionDetector.h Manager/EatManager.cpp Manager/EatManager.h Handler/EatLocationHandler.cpp Handler/EatLocationHandler.h Handler/RadarHandler.cpp Handler/RadarHandler.h Handler/BaseKeydownHandle.h Manager/ShaderManager.cpp Manager/ShaderManager.h Manager/LevelManager.cpp Manager/LevelManager.h Manager/VboIndexer.cpp Manager/VboIndexer.h Manager/Camera.cpp Manager/Camera.h Renderer/Opengl/Model/Character.cpp Renderer/Opengl/Model/Character.h Renderer/Opengl/Model/Utils/Vao.cpp Renderer/Opengl/Model/Utils/Vao.h Renderer/Opengl/Model/Utils/Vbo.cpp Renderer/Opengl/Model/Utils/Vbo.h Renderer/Opengl/Model/Utils/Ebo.cpp Renderer/Opengl/Model/Utils/Ebo.h Renderer/Opengl/Model/Utils/Mesh.cpp Renderer/Opengl/Model/Utils/Mesh.h Resource/ObjModelLoader.cpp Resource/ObjModelLoader.h Resource/TextureLoader.cpp Resource/TextureLoader.h Resource/ShaderLoader.cpp Resource/ShaderLoader.h Manager/TextureManager.cpp Manager/TextureManager.h Renderer/Opengl/DepthMapRenderer.cpp Renderer/Opengl/DepthMapRenderer.h Renderer/Opengl/Line.h Renderer/Opengl/BloomRenderer.cpp Renderer/Opengl/BloomRenderer.h Renderer/Opengl/Model/RainModel.cpp Renderer/Opengl/Model/RainModel.h Renderer/Opengl/RainDropRenderer.cpp Renderer/Opengl/RainDropRenderer.h Renderer/Opengl/Model/MeshModel.cpp Renderer/Opengl/Model/MeshModel.h Resource/AnimLoader.cpp Resource/AnimLoader.h ItemsDto/AnimItem.cpp ItemsDto/AnimItem.h Renderer/Opengl/Model/Utils/Tree.h + Particle/FireParticleSystem.cpp + Particle/FireParticleSystem.h + Particle/SmokeParticleSystem.cpp + Particle/SmokeParticleSystem.h + Renderer/Opengl/FireRenderer.cpp + Renderer/Opengl/FireRenderer.h + Renderer/Opengl/PlanarReflectionRenderer.cpp + Renderer/Opengl/PlanarReflectionRenderer.h + Renderer/Opengl/BoltRenderer.cpp + Renderer/Opengl/BoltRenderer.h + Renderer/Opengl/Effects/LightningFlashEffect.cpp + Renderer/Opengl/Effects/LightningFlashEffect.h + Handler/Debug/PositionHandler.cpp + Handler/Debug/PositionHandler.h + Renderer/Opengl/Model/Standard/StandardMesh.cpp + Renderer/Opengl/Model/Standard/StandardMesh.h + Renderer/Opengl/Model/Standard/PlaneMesh.cpp + Renderer/Opengl/Model/Standard/PlaneMesh.h + Renderer/Opengl/Model/Standard/BoxMesh.cpp + Renderer/Opengl/Model/Standard/BoxMesh.h + Renderer/Opengl/Model/Standard/CylinderMesh.cpp + Renderer/Opengl/Model/Standard/CylinderMesh.h + Renderer/Opengl/Node3DRenderer.cpp + Renderer/Opengl/Node3DRenderer.h + Renderer/Opengl/Material/StandardMaterial.cpp + Renderer/Opengl/Material/StandardMaterial.h + Renderer/Opengl/Material/BaseMaterial.h + Renderer/Opengl/Material/PlanarReflectionMaterial.cpp + Renderer/Opengl/Material/PlanarReflectionMaterial.h + Renderer/Opengl/Material/ShaderMaterial.cpp + Renderer/Opengl/Material/ShaderMaterial.h + Renderer/Opengl/Material/IAlbedoMaterial.h + Tools/Timer.cpp + Tools/Timer.h + Tools/WorldEnvironment.cpp + Tools/WorldEnvironment.h + Tools/Environment.cpp + Tools/Environment.h + Lights/DirectionalLight.cpp + Lights/DirectionalLight.h + Lights/SpotLight.cpp + Lights/SpotLight.h + Tools/Clear.h + ItemsDto/Transform.cpp + ItemsDto/Transform.h + Lights/PointLight.cpp + Lights/PointLight.h + Renderer/Opengl/Model/Standard/SphereMesh.cpp + Renderer/Opengl/Model/Standard/SphereMesh.h + Renderer/Opengl/Model/Standard/CapsuleMesh.cpp + Renderer/Opengl/Model/Standard/CapsuleMesh.h + Renderer/Opengl/Model/Standard/ArrayMesh.cpp + Renderer/Opengl/Model/Standard/ArrayMesh.h + Renderer/Opengl/Model/Standard/AnimationArrayMesh.cpp + Renderer/Opengl/Model/Standard/AnimationArrayMesh.h + Renderer/Opengl/Model/Standard/Animation/AnimationPlayer.cpp + Renderer/Opengl/Model/Standard/Animation/AnimationPlayer.h + Renderer/Opengl/Scene/Scene.cpp + Renderer/Opengl/Scene/Scene.h + Renderer/Opengl/Scene/SceneRenderer.cpp + Renderer/Opengl/Scene/SceneRenderer.h + Renderer/Opengl/Material/IUniform.cpp + Renderer/Opengl/Material/IUniform.h + Renderer/Opengl/Material/Uniform/TextureUniform.cpp + Renderer/Opengl/Material/Uniform/TextureUniform.h + Renderer/Opengl/Material/Uniform/TextureArrayUniform.cpp + Renderer/Opengl/Material/Uniform/TextureArrayUniform.h + Resource/ResourceLoader.cpp + Resource/ResourceLoader.h + Renderer/Opengl/Model/SpinnerMesh.cpp + Renderer/Opengl/Model/SpinnerMesh.h + Scenes/MainScene.cpp + Scenes/MainScene.h + Renderer/Opengl/Model/Standard/MeshNode3D.cpp + Renderer/Opengl/Model/Standard/MeshNode3D.h + Scenes/PlayerScene.cpp + Scenes/PlayerScene.h + Renderer/Opengl/Model/Game/SnakeMeshNode3D.cpp + Renderer/Opengl/Model/Game/SnakeMeshNode3D.h + ItemsDto/Vector3i.cpp + ItemsDto/Vector3i.h + Renderer/Opengl/Model/Game/CoinMeshNode3D.cpp + Renderer/Opengl/Model/Game/CoinMeshNode3D.h + ItemsDto/Visibility.cpp + ItemsDto/Visibility.h + Renderer/Opengl/Model/Standard/2D/LabelNode2D.cpp + Renderer/Opengl/Model/Standard/2D/LabelNode2D.h + Renderer/Opengl/Model/Standard/2D/BaseNode2D.cpp + Renderer/Opengl/Model/Standard/2D/BaseNode2D.h + Renderer/Opengl/Material/2D/LabelSettings.cpp + Renderer/Opengl/Material/2D/LabelSettings.h + Renderer/Opengl/Material/2D/Font.cpp + Renderer/Opengl/Material/2D/Font.h + Renderer/Opengl/Node2DRenderer.cpp + Renderer/Opengl/Node2DRenderer.h + Renderer/Opengl/Model/Standard/2D/MeshNode2D.cpp + Renderer/Opengl/Model/Standard/2D/MeshNode2D.h + Renderer/Opengl/Model/Utils/TextMesh.cpp + Renderer/Opengl/Model/Utils/TextMesh.h + Renderer/Opengl/Material/Uniform/FadeOutUniform.cpp + Renderer/Opengl/Material/Uniform/FadeOutUniform.h + Renderer/Opengl/Material/Uniform/FadeInUniform.cpp + Renderer/Opengl/Material/Uniform/FadeInUniform.h + Renderer/Opengl/Model/Standard/2D/QuadNode2D.cpp + Renderer/Opengl/Model/Standard/2D/QuadNode2D.h + Renderer/Opengl/Model/Utils/Mesh2D.cpp + Renderer/Opengl/Model/Utils/Mesh2D.h + Renderer/Opengl/Model/Game/RadarMeshNode2D.cpp + Renderer/Opengl/Model/Game/RadarMeshNode2D.h + Renderer/Opengl/Material/Uniform/TimerUniform.cpp + Renderer/Opengl/Material/Uniform/TimerUniform.h + Scenes/BarriersScene.cpp + Scenes/BarriersScene.h + Renderer/Opengl/Model/Game/RadarItem.cpp + Renderer/Opengl/Model/Game/RadarItem.h + Scenes/CoinScene.cpp + Scenes/CoinScene.h + Tools/Capabilities.h + Tools/BlendFactor.h + Tools/Blending.h + Tools/ContextState.cpp + Tools/ContextState.h + Scenes/PreloaderScene.cpp + Scenes/PreloaderScene.h + Renderer/Opengl/Model/Standard/2D/TringleNode2D.cpp + Renderer/Opengl/Model/Standard/2D/TringleNode2D.h + Renderer/Opengl/Model/Standard/TringleMesh3D.cpp + Renderer/Opengl/Model/Standard/TringleMesh3D.h + Renderer/Opengl/Model/Standard/QuadMesh3D.cpp + Renderer/Opengl/Model/Standard/QuadMesh3D.h + Renderer/Opengl/Model/Standard/GPUParticle3D.cpp + Renderer/Opengl/Model/Standard/GPUParticle3D.h + Renderer/Opengl/Model/Standard/2D/GPUParticle2D.cpp + Renderer/Opengl/Model/Standard/2D/GPUParticle2D.h + Renderer/Opengl/Material/Particle/BaseProcessMaterial.cpp + Renderer/Opengl/Material/Particle/BaseProcessMaterial.h + Renderer/Opengl/Material/Particle/ParticleProcessMaterial.cpp + Renderer/Opengl/Material/Particle/ParticleProcessMaterial.h + Renderer/Opengl/Material/Interface/BlendingInterface.cpp + Renderer/Opengl/Material/Interface/BlendingInterface.h + Tools/DepthFunc.h + Scenes/TorchScene.cpp + Scenes/TorchScene.h + Scenes/WeatherScene.cpp + Scenes/WeatherScene.h + Renderer/Opengl/Model/Standard/WireframeArrowMesh.cpp + Renderer/Opengl/Model/Standard/WireframeArrowMesh.h + Renderer/Opengl/Model/Debug/LightNode3D.cpp + Renderer/Opengl/Model/Debug/LightNode3D.h + Renderer/Opengl/Model/Debug/DirectionalLightNode3D.cpp + Renderer/Opengl/Model/Debug/DirectionalLightNode3D.h + Tools/DrawElement.h + Renderer/Opengl/Model/Standard/SkyboxNode3D.cpp + Renderer/Opengl/Model/Standard/SkyboxNode3D.h + Renderer/Opengl/Material/Uniform/CallbackUniform.cpp + Renderer/Opengl/Material/Uniform/CallbackUniform.h + Renderer/Opengl/Model/Game/StreetLampNode3D.cpp + Renderer/Opengl/Model/Game/StreetLampNode3D.h + Physic/Shape.h + Physic/BoxShape.cpp + Physic/BoxShape.h + Renderer/Opengl/Model/Game/BarrelNode3D.cpp + Renderer/Opengl/Model/Game/BarrelNode3D.h + Renderer/Opengl/Model/Collision/CollisionShape3D.cpp + Renderer/Opengl/Model/Collision/CollisionShape3D.h + Physic/CollisionCheck.h + Physic/CollisionSystem3D.cpp + Physic/CollisionSystem3D.h + Physic/CollisionCheck.cpp + Physic/Algorithms/CollisionAlgorithms.cpp + Physic/Algorithms/CollisionAlgorithms.h + Physic/SphereShape.cpp + Physic/SphereShape.h + Physic/CapsuleShape.cpp + Physic/CapsuleShape.h + Physic/CylinderShape.cpp + Physic/CylinderShape.h + Handler/Debug/ManipulatorHandler.cpp + Handler/Debug/ManipulatorHandler.h + Handler/Debug/RotationHandler.cpp + Handler/Debug/RotationHandler.h + Handler/Debug/ScaleHandler.cpp + Handler/Debug/ScaleHandler.h + Handler/Debug/DirectionalLightDirectionHandler.cpp + Handler/Debug/DirectionalLightDirectionHandler.h + Handler/Debug/CollisionShapeHandler.cpp + Handler/Debug/CollisionShapeHandler.h +) +target_link_libraries(${PROJECT_NAME} glfw glut GLU GL GLEW freetype openal alut assimp nlohmann_json::nlohmann_json) file(COPY Assets DESTINATION ${CMAKE_BINARY_DIR}) ### TESTING ################################################# +# Povolení testování enable_testing() -# -file(GLOB_RECURSE TEST_SOURCES Tests/*.cpp) -add_executable(Tests Tests/main.cpp ${TEST_SOURCES} ItemsDto/BaseItem.cpp ItemsDto/BaseItem.h stdafx.h ItemsDto/Cube.cpp ItemsDto/Cube.h Manager/ResourceManager.cpp Manager/ResourceManager.h ItemsDto/GameField.cpp ItemsDto/GameField.h Renderer/Opengl/GameFieldRenderer.cpp Renderer/Opengl/GameFieldRenderer.h Renderer/Opengl/BaseRenderer.cpp Renderer/Opengl/BaseRenderer.h Manager/RenderManager.cpp Manager/RenderManager.h ItemsDto/Snake.cpp ItemsDto/Snake.h Renderer/Opengl/SnakeRenderer.cpp Renderer/Opengl/SnakeRenderer.h Manager/KeyboardManager.cpp Manager/KeyboardManager.h Handler/SnakeMoveHandler.cpp Handler/SnakeMoveHandler.h Handler/BaseHandler.cpp Handler/BaseHandler.h Physic/CollisionDetector.cpp Physic/CollisionDetector.h ItemsDto/BaseContainer.cpp ItemsDto/BaseContainer.h ItemsDto/BaseContainerInterface.cpp ItemsDto/BaseContainerInterface.h Renderer/Opengl/EatRenderer.cpp Renderer/Opengl/EatRenderer.h Renderer/Opengl/TextRenderer.cpp Renderer/Opengl/TextRenderer.h ItemsDto/Text.cpp ItemsDto/Text.h Manager/EatManager.cpp Manager/EatManager.h Handler/EatLocationHandler.cpp Handler/EatLocationHandler.h ItemsDto/Eat.cpp ItemsDto/Eat.h ItemsDto/ObjItem.cpp ItemsDto/ObjItem.h ItemsDto/Radar.cpp ItemsDto/Radar.h Renderer/Opengl/RadarRenderer.cpp Renderer/Opengl/RadarRenderer.h Handler/RadarHandler.cpp Handler/RadarHandler.h Handler/BaseKeydownHandle.cpp Handler/BaseKeydownHandle.h Renderer/Opengl/EatRemoveAnimateRenderer.cpp Renderer/Opengl/EatRemoveAnimateRenderer.h ItemsDto/Barriers.cpp ItemsDto/Barriers.h Renderer/Opengl/BarrierRenderer.cpp Renderer/Opengl/BarrierRenderer.h Manager/ShaderManager.cpp Manager/ShaderManager.h Manager/LevelManager.cpp Manager/LevelManager.h Manager/VboIndexer.cpp Manager/VboIndexer.h ItemsDto/ObjWall.cpp ItemsDto/ObjWall.h Renderer/Opengl/ObjWallRenderer.cpp Renderer/Opengl/ObjWallRenderer.h Manager/Camera.cpp Manager/Camera.h Renderer/Opengl/Model/TextModel.cpp Renderer/Opengl/Model/TextModel.h Renderer/Opengl/Model/Character.cpp Renderer/Opengl/Model/Character.h Renderer/Opengl/Model/Utils/Vao.cpp Renderer/Opengl/Model/Utils/Vao.h Renderer/Opengl/Model/Utils/Vbo.cpp Renderer/Opengl/Model/Utils/Vbo.h Renderer/Opengl/Model/Utils/Ebo.cpp Renderer/Opengl/Model/Utils/Ebo.h Renderer/Opengl/Model/Utils/Mesh.cpp Renderer/Opengl/Model/Utils/Mesh.h Renderer/Opengl/Model/GameFieldModel.cpp Renderer/Opengl/Model/GameFieldModel.h Renderer/Opengl/Model/RadarModel.cpp Renderer/Opengl/Model/RadarModel.h Resource/ObjModelLoader.cpp Resource/ObjModelLoader.h Resource/TextureLoader.cpp Resource/TextureLoader.h Resource/ShaderLoader.cpp Resource/ShaderLoader.h Manager/TextureManager.cpp Manager/TextureManager.h Renderer/Opengl/BloomRenderer.cpp Renderer/Opengl/BloomRenderer.h Renderer/Opengl/Model/RainModel.cpp Renderer/Opengl/Model/RainModel.h Renderer/Opengl/RainRenderer.cpp Renderer/Opengl/RainRenderer.h Renderer/Opengl/RainDropRenderer.cpp Renderer/Opengl/RainDropRenderer.h Renderer/Opengl/Model/MeshModel.cpp Renderer/Opengl/Model/MeshModel.h Resource/AnimLoader.cpp Resource/AnimLoader.h ItemsDto/AnimItem.cpp ItemsDto/AnimItem.h Renderer/Opengl/Model/AnimationModel.cpp Renderer/Opengl/Model/AnimationModel.h Renderer/Opengl/AnimRenderer.cpp Renderer/Opengl/AnimRenderer.h Renderer/Opengl/Model/Utils/Tree.h) -target_link_libraries(Tests glfw glut GLU GL GLEW freetype assimp) \ No newline at end of file +# --- FetchContent pro Catch2 --- +include(FetchContent) +FetchContent_Declare( + catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG v3.4.0 # Zvažte použití novější verze, např. v3.6.0 +) +FetchContent_MakeAvailable(catch2) + +# --- Zdrojové soubory APLIKACE --- +# Definujte zde všechny .cpp soubory vaší hry (mimo složku Tests/) +# POZNÁMKA: Hlavičkové soubory (.h) se sem nepíšou! +set(APP_SOURCES + ItemsDto/BaseItem.cpp + Manager/ResourceManager.cpp + Manager/RenderManager.cpp + Manager/KeyboardManager.cpp + Manager/EatManager.cpp + Manager/ShaderManager.cpp + Manager/LevelManager.cpp + Manager/VboIndexer.cpp + Manager/Camera.cpp + Manager/TextureManager.cpp + ItemsDto/AnimItem.cpp + Renderer/Opengl/BaseRenderer.cpp + Renderer/Opengl/BloomRenderer.cpp + Renderer/Opengl/RainDropRenderer.cpp + Renderer/Opengl/PlanarReflectionRenderer.cpp + Renderer/Opengl/PlanarReflectionRenderer.h + Renderer/Opengl/Model/Character.cpp + Renderer/Opengl/Model/Utils/Vao.cpp + Renderer/Opengl/Model/Utils/Vbo.cpp + Renderer/Opengl/Model/Utils/Ebo.cpp + Renderer/Opengl/Model/Utils/Mesh.cpp + Renderer/Opengl/Model/RainModel.cpp + Renderer/Opengl/Model/MeshModel.cpp + Handler/SnakeMoveHandler.cpp + Handler/BaseHandler.cpp + Handler/EatLocationHandler.cpp + Handler/RadarHandler.cpp + Physic/CollisionDetector.cpp + Resource/ObjModelLoader.cpp + Resource/TextureLoader.cpp + Resource/ShaderLoader.cpp + Resource/AnimLoader.cpp + Handler/Debug/PositionHandler.cpp + Handler/Debug/PositionHandler.h + Renderer/Opengl/Model/Standard/BoxMesh.cpp + Renderer/Opengl/Model/Standard/BoxMesh.h + Renderer/Opengl/Model/Standard/CapsuleMesh.cpp + Renderer/Opengl/Model/Standard/CapsuleMesh.h + Renderer/Opengl/Model/Standard/CylinderMesh.cpp + Renderer/Opengl/Model/Standard/CylinderMesh.h + Renderer/Opengl/Model/Standard/PlaneMesh.cpp + Renderer/Opengl/Model/Standard/PlaneMesh.h + Renderer/Opengl/Model/Standard/StandardMesh.cpp + Renderer/Opengl/Model/Standard/StandardMesh.h + Renderer/Opengl/Node3DRenderer.cpp + Renderer/Opengl/Node3DRenderer.h + Renderer/Opengl/Material/StandardMaterial.cpp + Renderer/Opengl/Material/StandardMaterial.h + Renderer/Opengl/Material/BaseMaterial.h + Renderer/Opengl/Material/PlanarReflectionMaterial.cpp + Renderer/Opengl/Material/PlanarReflectionMaterial.h + Renderer/Opengl/Material/ShaderMaterial.cpp + Renderer/Opengl/Material/ShaderMaterial.h + Renderer/Opengl/Material/IAlbedoMaterial.h + Tools/Timer.cpp + Tools/Timer.h + Tools/WorldEnvironment.cpp + Tools/WorldEnvironment.h + Tools/Environment.cpp + Tools/Environment.h + Lights/DirectionalLight.cpp + Lights/DirectionalLight.h + Lights/SpotLight.cpp + Lights/SpotLight.h + Tools/Clear.h + ItemsDto/Transform.cpp + ItemsDto/Transform.h + Lights/PointLight.cpp + Lights/PointLight.h + Renderer/Opengl/Model/Standard/SphereMesh.cpp + Renderer/Opengl/Model/Standard/SphereMesh.h + Renderer/Opengl/Model/Standard/ArrayMesh.cpp + Renderer/Opengl/Model/Standard/ArrayMesh.h + Renderer/Opengl/Model/Standard/AnimationArrayMesh.cpp + Renderer/Opengl/Model/Standard/AnimationArrayMesh.h + Renderer/Opengl/Model/Standard/Animation/AnimationPlayer.cpp + Renderer/Opengl/Model/Standard/Animation/AnimationPlayer.h + Renderer/Opengl/Scene/Scene.cpp + Renderer/Opengl/Scene/Scene.h + Renderer/Opengl/Material/IUniform.cpp + Renderer/Opengl/Material/IUniform.h + Renderer/Opengl/Material/Uniform/TextureUniform.cpp + Renderer/Opengl/Material/Uniform/TextureUniform.h + Renderer/Opengl/Material/Uniform/TextureArrayUniform.cpp + Renderer/Opengl/Material/Uniform/TextureArrayUniform.h + Resource/ResourceLoader.cpp + Resource/ResourceLoader.h + Renderer/Opengl/Model/SpinnerMesh.cpp + Renderer/Opengl/Model/SpinnerMesh.h + Scenes/MainScene.cpp + Scenes/MainScene.h + Renderer/Opengl/Scene/SceneRenderer.cpp + Renderer/Opengl/Scene/SceneRenderer.h + Renderer/Opengl/Model/Standard/MeshNode3D.cpp + Renderer/Opengl/Model/Standard/MeshNode3D.h + Scenes/PlayerScene.cpp + Scenes/PlayerScene.h + Renderer/Opengl/Model/Game/SnakeMeshNode3D.cpp + Renderer/Opengl/Model/Game/SnakeMeshNode3D.h + ItemsDto/Vector3i.cpp + ItemsDto/Vector3i.h + Renderer/Opengl/Model/Game/CoinMeshNode3D.cpp + Renderer/Opengl/Model/Game/CoinMeshNode3D.h + ItemsDto/Visibility.cpp + ItemsDto/Visibility.h + Renderer/Opengl/Model/Standard/2D/LabelNode2D.cpp + Renderer/Opengl/Model/Standard/2D/LabelNode2D.h + Renderer/Opengl/Model/Standard/2D/BaseNode2D.cpp + Renderer/Opengl/Model/Standard/2D/BaseNode2D.h + Renderer/Opengl/Material/2D/LabelSettings.cpp + Renderer/Opengl/Material/2D/LabelSettings.h + Renderer/Opengl/Material/2D/Font.cpp + Renderer/Opengl/Material/2D/Font.h + Renderer/Opengl/Node2DRenderer.cpp + Renderer/Opengl/Node2DRenderer.h + Renderer/Opengl/Model/Standard/2D/MeshNode2D.cpp + Renderer/Opengl/Model/Standard/2D/MeshNode2D.h + Renderer/Opengl/Model/Utils/TextMesh.cpp + Renderer/Opengl/Model/Utils/TextMesh.h + Renderer/Opengl/Material/Uniform/FadeOutUniform.cpp + Renderer/Opengl/Material/Uniform/FadeOutUniform.h + Renderer/Opengl/Material/Uniform/FadeInUniform.cpp + Renderer/Opengl/Material/Uniform/FadeInUniform.h + Renderer/Opengl/Model/Standard/2D/QuadNode2D.cpp + Renderer/Opengl/Model/Standard/2D/QuadNode2D.h + Renderer/Opengl/Model/Utils/Mesh2D.cpp + Renderer/Opengl/Model/Utils/Mesh2D.h + Renderer/Opengl/Model/Game/RadarMeshNode2D.cpp + Renderer/Opengl/Model/Game/RadarMeshNode2D.h + Renderer/Opengl/Material/Uniform/TimerUniform.cpp + Renderer/Opengl/Material/Uniform/TimerUniform.h + Scenes/BarriersScene.cpp + Scenes/BarriersScene.h + Renderer/Opengl/Model/Game/RadarItem.cpp + Renderer/Opengl/Model/Game/RadarItem.h + Scenes/CoinScene.cpp + Scenes/CoinScene.h + Tools/Capabilities.h + Tools/BlendFactor.h + Tools/Blending.h + Tools/ContextState.cpp + Tools/ContextState.h + Scenes/PreloaderScene.cpp + Scenes/PreloaderScene.h + Renderer/Opengl/Model/Standard/2D/TringleNode2D.cpp + Renderer/Opengl/Model/Standard/2D/TringleNode2D.h + Renderer/Opengl/Model/Standard/TringleMesh3D.cpp + Renderer/Opengl/Model/Standard/TringleMesh3D.h + Renderer/Opengl/Model/Standard/QuadMesh3D.cpp + Renderer/Opengl/Model/Standard/QuadMesh3D.h + Renderer/Opengl/Model/Standard/GPUParticle3D.cpp + Renderer/Opengl/Model/Standard/GPUParticle3D.h + Renderer/Opengl/Model/Standard/2D/GPUParticle2D.cpp + Renderer/Opengl/Model/Standard/2D/GPUParticle2D.h + Renderer/Opengl/Material/Particle/BaseProcessMaterial.cpp + Renderer/Opengl/Material/Particle/BaseProcessMaterial.h + Renderer/Opengl/Material/Particle/ParticleProcessMaterial.cpp + Renderer/Opengl/Material/Particle/ParticleProcessMaterial.h + Renderer/Opengl/Material/Interface/BlendingInterface.cpp + Renderer/Opengl/Material/Interface/BlendingInterface.h + Tools/DepthFunc.h + Scenes/TorchScene.cpp + Scenes/TorchScene.h + Scenes/WeatherScene.cpp + Scenes/WeatherScene.h + Renderer/Opengl/Model/Standard/WireframeArrowMesh.cpp + Renderer/Opengl/Model/Standard/WireframeArrowMesh.h + Renderer/Opengl/Model/Debug/LightNode3D.cpp + Renderer/Opengl/Model/Debug/LightNode3D.h + Renderer/Opengl/Model/Debug/DirectionalLightNode3D.cpp + Renderer/Opengl/Model/Debug/DirectionalLightNode3D.h + Tools/DrawElement.h + Renderer/Opengl/Model/Standard/SkyboxNode3D.cpp + Renderer/Opengl/Model/Standard/SkyboxNode3D.h + Renderer/Opengl/Material/Uniform/CallbackUniform.cpp + Renderer/Opengl/Material/Uniform/CallbackUniform.h + Renderer/Opengl/Model/Game/StreetLampNode3D.cpp + Renderer/Opengl/Model/Game/StreetLampNode3D.h + Physic/Shape.h + Physic/BoxShape.cpp + Physic/BoxShape.h + Renderer/Opengl/Model/Game/BarrelNode3D.cpp + Renderer/Opengl/Model/Game/BarrelNode3D.h + Renderer/Opengl/Model/Collision/CollisionShape3D.cpp + Renderer/Opengl/Model/Collision/CollisionShape3D.h + Physic/CollisionCheck.h + Physic/CollisionSystem3D.cpp + Physic/CollisionSystem3D.h + Physic/CollisionCheck.cpp + Physic/Algorithms/CollisionAlgorithms.cpp + Physic/Algorithms/CollisionAlgorithms.h + Physic/SphereShape.cpp + Physic/SphereShape.h + Physic/CapsuleShape.cpp + Physic/CapsuleShape.h + Physic/CylinderShape.cpp + Physic/CylinderShape.h + Handler/Debug/ManipulatorHandler.cpp + Handler/Debug/ManipulatorHandler.h + Handler/Debug/RotationHandler.cpp + Handler/Debug/RotationHandler.h + Handler/Debug/ScaleHandler.cpp + Handler/Debug/ScaleHandler.h + Handler/Debug/DirectionalLightDirectionHandler.cpp + Handler/Debug/DirectionalLightDirectionHandler.h + Handler/Debug/CollisionShapeHandler.cpp + Handler/Debug/CollisionShapeHandler.h +) + +# --- Vytvoření KNIHOVNY z herních souborů --- +add_library(snake3d_lib ${APP_SOURCES} + Renderer/Opengl/PlanarReflectionRenderer.cpp + Renderer/Opengl/PlanarReflectionRenderer.h + Renderer/Opengl/Material/PlanarReflectionMaterial.cpp + Renderer/Opengl/Material/PlanarReflectionMaterial.h + Renderer/Opengl/Model/Standard/BoxMesh.cpp + Renderer/Opengl/Model/Standard/BoxMesh.h + Renderer/Opengl/Model/Standard/StandardMesh.cpp + Renderer/Opengl/Model/Standard/StandardMesh.h + Renderer/Opengl/Node3DRenderer.cpp + Renderer/Opengl/Node3DRenderer.h + Renderer/Opengl/Material/StandardMaterial.cpp + Renderer/Opengl/Material/StandardMaterial.h + Renderer/Opengl/Material/BaseMaterial.h + Renderer/Opengl/Material/ShaderMaterial.cpp + Renderer/Opengl/Material/ShaderMaterial.h + Renderer/Opengl/Material/IAlbedoMaterial.h + Tools/Timer.cpp + Tools/Timer.h + Tools/WorldEnvironment.cpp + Tools/WorldEnvironment.h + Tools/Environment.cpp + Tools/Environment.h + Lights/DirectionalLight.cpp + Lights/DirectionalLight.h + Lights/SpotLight.cpp + Lights/SpotLight.h + Tools/Clear.h + ItemsDto/Transform.cpp + ItemsDto/Transform.h + Lights/PointLight.cpp + Lights/PointLight.h + Renderer/Opengl/Model/Standard/SphereMesh.cpp + Renderer/Opengl/Model/Standard/SphereMesh.h + Renderer/Opengl/Model/Standard/CapsuleMesh.cpp + Renderer/Opengl/Model/Standard/CapsuleMesh.h + Renderer/Opengl/Model/Standard/ArrayMesh.cpp + Renderer/Opengl/Model/Standard/ArrayMesh.h + Renderer/Opengl/Model/Standard/AnimationArrayMesh.cpp + Renderer/Opengl/Model/Standard/AnimationArrayMesh.h + Renderer/Opengl/Model/Standard/Animation/AnimationPlayer.cpp + Renderer/Opengl/Model/Standard/Animation/AnimationPlayer.h + Renderer/Opengl/Model/Standard/CylinderMesh.cpp + Renderer/Opengl/Model/Standard/CylinderMesh.h + Renderer/Opengl/Scene/Scene.cpp + Renderer/Opengl/Scene/Scene.h + Renderer/Opengl/Material/IUniform.cpp + Renderer/Opengl/Material/IUniform.h + Renderer/Opengl/Material/Uniform/TextureUniform.cpp + Renderer/Opengl/Material/Uniform/TextureUniform.h + Renderer/Opengl/Material/Uniform/TextureArrayUniform.cpp + Renderer/Opengl/Material/Uniform/TextureArrayUniform.h + Resource/ResourceLoader.cpp + Resource/ResourceLoader.h + Renderer/Opengl/Model/SpinnerMesh.cpp + Renderer/Opengl/Model/SpinnerMesh.h + Scenes/MainScene.cpp + Scenes/MainScene.h + Renderer/Opengl/Model/Standard/MeshNode3D.cpp + Renderer/Opengl/Model/Standard/MeshNode3D.h + Scenes/PlayerScene.cpp + Scenes/PlayerScene.h + Renderer/Opengl/Model/Game/SnakeMeshNode3D.cpp + Renderer/Opengl/Model/Game/SnakeMeshNode3D.h + ItemsDto/Vector3i.cpp + ItemsDto/Vector3i.h + Renderer/Opengl/Model/Game/CoinMeshNode3D.cpp + Renderer/Opengl/Model/Game/CoinMeshNode3D.h + ItemsDto/Visibility.cpp + ItemsDto/Visibility.h + Renderer/Opengl/Model/Standard/2D/LabelNode2D.cpp + Renderer/Opengl/Model/Standard/2D/LabelNode2D.h + Renderer/Opengl/Model/Standard/2D/BaseNode2D.cpp + Renderer/Opengl/Model/Standard/2D/BaseNode2D.h + Renderer/Opengl/Material/2D/LabelSettings.cpp + Renderer/Opengl/Material/2D/LabelSettings.h + Renderer/Opengl/Material/2D/Font.cpp + Renderer/Opengl/Material/2D/Font.h + Renderer/Opengl/Node2DRenderer.cpp + Renderer/Opengl/Node2DRenderer.h + Renderer/Opengl/Model/Standard/2D/MeshNode2D.cpp + Renderer/Opengl/Model/Standard/2D/MeshNode2D.h + Renderer/Opengl/Model/Utils/TextMesh.cpp + Renderer/Opengl/Model/Utils/TextMesh.h + Renderer/Opengl/Material/Uniform/FadeOutUniform.cpp + Renderer/Opengl/Material/Uniform/FadeOutUniform.h + Renderer/Opengl/Material/Uniform/FadeInUniform.cpp + Renderer/Opengl/Material/Uniform/FadeInUniform.h + Renderer/Opengl/Model/Standard/2D/QuadNode2D.cpp + Renderer/Opengl/Model/Standard/2D/QuadNode2D.h + Renderer/Opengl/Model/Utils/Mesh2D.cpp + Renderer/Opengl/Model/Utils/Mesh2D.h + Renderer/Opengl/Model/Game/RadarMeshNode2D.cpp + Renderer/Opengl/Model/Game/RadarMeshNode2D.h + Renderer/Opengl/Material/Uniform/TimerUniform.cpp + Renderer/Opengl/Material/Uniform/TimerUniform.h + Scenes/BarriersScene.cpp + Scenes/BarriersScene.h + Renderer/Opengl/Model/Game/RadarItem.cpp + Renderer/Opengl/Model/Game/RadarItem.h + Scenes/CoinScene.cpp + Scenes/CoinScene.h + Tools/Capabilities.h + Tools/BlendFactor.h + Tools/Blending.h + Tools/ContextState.cpp + Tools/ContextState.h + Scenes/PreloaderScene.cpp + Scenes/PreloaderScene.h + Renderer/Opengl/Model/Standard/2D/TringleNode2D.cpp + Renderer/Opengl/Model/Standard/2D/TringleNode2D.h + Renderer/Opengl/Model/Standard/TringleMesh3D.cpp + Renderer/Opengl/Model/Standard/TringleMesh3D.h + Renderer/Opengl/Model/Standard/QuadMesh3D.cpp + Renderer/Opengl/Model/Standard/QuadMesh3D.h + Renderer/Opengl/Model/Standard/GPUParticle3D.cpp + Renderer/Opengl/Model/Standard/GPUParticle3D.h + Renderer/Opengl/Model/Standard/2D/GPUParticle2D.cpp + Renderer/Opengl/Model/Standard/2D/GPUParticle2D.h + Renderer/Opengl/Material/Particle/BaseProcessMaterial.cpp + Renderer/Opengl/Material/Particle/BaseProcessMaterial.h + Renderer/Opengl/Material/Particle/ParticleProcessMaterial.cpp + Renderer/Opengl/Material/Particle/ParticleProcessMaterial.h + Renderer/Opengl/Material/Interface/BlendingInterface.cpp + Renderer/Opengl/Material/Interface/BlendingInterface.h + Tools/DepthFunc.h + Scenes/TorchScene.cpp + Scenes/TorchScene.h + Scenes/WeatherScene.cpp + Scenes/WeatherScene.h + Renderer/Opengl/Model/Standard/WireframeArrowMesh.cpp + Renderer/Opengl/Model/Standard/WireframeArrowMesh.h + Renderer/Opengl/Model/Debug/LightNode3D.cpp + Renderer/Opengl/Model/Debug/LightNode3D.h + Renderer/Opengl/Model/Debug/DirectionalLightNode3D.cpp + Renderer/Opengl/Model/Debug/DirectionalLightNode3D.h + Tools/DrawElement.h + Renderer/Opengl/Model/Standard/SkyboxNode3D.cpp + Renderer/Opengl/Model/Standard/SkyboxNode3D.h + Renderer/Opengl/Material/Uniform/CallbackUniform.cpp + Renderer/Opengl/Material/Uniform/CallbackUniform.h + Renderer/Opengl/Model/Game/StreetLampNode3D.cpp + Renderer/Opengl/Model/Game/StreetLampNode3D.h + Physic/Shape.h + Physic/BoxShape.cpp + Physic/BoxShape.h + Renderer/Opengl/Model/Game/BarrelNode3D.cpp + Renderer/Opengl/Model/Game/BarrelNode3D.h + Renderer/Opengl/Model/Collision/CollisionShape3D.cpp + Renderer/Opengl/Model/Collision/CollisionShape3D.h + Physic/CollisionCheck.h + Physic/CollisionSystem3D.cpp + Physic/CollisionSystem3D.h + Physic/CollisionCheck.cpp + Physic/Algorithms/CollisionAlgorithms.cpp + Physic/Algorithms/CollisionAlgorithms.h + Physic/SphereShape.cpp + Physic/SphereShape.h + Physic/CapsuleShape.cpp + Physic/CapsuleShape.h + Physic/CylinderShape.cpp + Physic/CylinderShape.h + Handler/Debug/ManipulatorHandler.cpp + Handler/Debug/ManipulatorHandler.h + Handler/Debug/RotationHandler.cpp + Handler/Debug/RotationHandler.h + Handler/Debug/ScaleHandler.cpp + Handler/Debug/ScaleHandler.h + Handler/Debug/DirectionalLightDirectionHandler.cpp + Handler/Debug/DirectionalLightDirectionHandler.h + Handler/Debug/CollisionShapeHandler.cpp + Handler/Debug/CollisionShapeHandler.h +) + +# Přidání cest k hlavičkovým souborům pro vaši knihovnu +# PUBLIC znamená, že kdokoliv, kdo se nalinkuje na snake3d_lib, také dostane tyto cesty +target_include_directories(snake3d_lib PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + # Můžete explicitně vyjmenovat složky, pokud jich není moc + # např. ${CMAKE_CURRENT_SOURCE_DIR}/Manager +) + +# Linkování knihovny proti jejím závislostem (GLFW, OpenGL, atd.) +target_link_libraries(snake3d_lib PUBLIC + glfw glut GLU GL GLEW freetype assimp +) + + +# --- Zdrojové soubory TESTŮ --- +# Použijte GLOB pouze pro testy, pokud chcete, ale lepší je vypsat je +file(GLOB TEST_SOURCES "Tests/*.cpp") + +# --- Vytvoření spustitelného souboru pro TESTY --- +add_executable(Tests ${TEST_SOURCES}) + +# --- Linkování TESTŮ proti herní knihovně a Catch2 --- +# Použijte Catch2::Catch2WithMain, aby Catch2 automaticky vytvořil funkci main(). +# Je to jednodušší a spolehlivější. +target_link_libraries(Tests PRIVATE + snake3d_lib + Catch2::Catch2WithMain +) + +# --- Integrace CTest --- +include(CTest) +include(Catch) +catch_discover_tests(Tests) +#target_include_directories(Tests PRIVATE ${catch2_SOURCE_DIR}/single_include) \ No newline at end of file diff --git a/Handler/BaseKeydownHandle.cpp b/Handler/BaseKeydownHandle.cpp deleted file mode 100644 index 6c81b614..00000000 --- a/Handler/BaseKeydownHandle.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "BaseKeydownHandle.h" - -namespace Handler { - void BaseKeydownHandle::onDefaultHandler() { - } -} // Handler \ No newline at end of file diff --git a/Handler/BaseKeydownHandle.h b/Handler/BaseKeydownHandle.h index 05c24cf7..ca94dca1 100644 --- a/Handler/BaseKeydownHandle.h +++ b/Handler/BaseKeydownHandle.h @@ -7,8 +7,8 @@ namespace Handler { class BaseKeydownHandle : BaseHandler { public: - void onDefaultHandler() override; - virtual void onEventHandler(unsigned int key) = 0; + void onDefaultHandler() override = 0; + virtual void onEventHandler(unsigned int key, int scancode, int action, int mods) = 0; }; } // Handler diff --git a/Handler/Debug/CollisionShapeHandler.cpp b/Handler/Debug/CollisionShapeHandler.cpp new file mode 100644 index 00000000..62270235 --- /dev/null +++ b/Handler/Debug/CollisionShapeHandler.cpp @@ -0,0 +1,6 @@ +#include "CollisionShapeHandler.h" + +namespace Handler { + namespace Debug { + } // Debug +} // Handler \ No newline at end of file diff --git a/Handler/Debug/CollisionShapeHandler.h b/Handler/Debug/CollisionShapeHandler.h new file mode 100644 index 00000000..105fe952 --- /dev/null +++ b/Handler/Debug/CollisionShapeHandler.h @@ -0,0 +1,11 @@ +#ifndef SNAKE3_COLLISIONSHAPEHANDLER_H +#define SNAKE3_COLLISIONSHAPEHANDLER_H + +namespace Handler { + namespace Debug { + class CollisionShapeHandler { + }; + } // Debug +} // Handler + +#endif //SNAKE3_COLLISIONSHAPEHANDLER_H \ No newline at end of file diff --git a/Handler/Debug/DirectionalLightDirectionHandler.cpp b/Handler/Debug/DirectionalLightDirectionHandler.cpp new file mode 100644 index 00000000..485f5172 --- /dev/null +++ b/Handler/Debug/DirectionalLightDirectionHandler.cpp @@ -0,0 +1,6 @@ +#include "DirectionalLightDirectionHandler.h" + +namespace Handler { + namespace Debug { + } // Debug +} // Handler \ No newline at end of file diff --git a/Handler/Debug/DirectionalLightDirectionHandler.h b/Handler/Debug/DirectionalLightDirectionHandler.h new file mode 100644 index 00000000..04cc3ba2 --- /dev/null +++ b/Handler/Debug/DirectionalLightDirectionHandler.h @@ -0,0 +1,11 @@ +#ifndef SNAKE3_DIRECTIONALLIGHTDIRECTIONHANDLER_H +#define SNAKE3_DIRECTIONALLIGHTDIRECTIONHANDLER_H + +namespace Handler { + namespace Debug { + class DirectionalLightDirectionHandler { + }; + } // Debug +} // Handler + +#endif //SNAKE3_DIRECTIONALLIGHTDIRECTIONHANDLER_H \ No newline at end of file diff --git a/Handler/Debug/ManipulatorHandler.cpp b/Handler/Debug/ManipulatorHandler.cpp new file mode 100644 index 00000000..16295e96 --- /dev/null +++ b/Handler/Debug/ManipulatorHandler.cpp @@ -0,0 +1,6 @@ +#include "ManipulatorHandler.h" + +namespace Handler { + namespace Debug { + } // Debug +} // Handler \ No newline at end of file diff --git a/Handler/Debug/ManipulatorHandler.h b/Handler/Debug/ManipulatorHandler.h new file mode 100644 index 00000000..597abb3d --- /dev/null +++ b/Handler/Debug/ManipulatorHandler.h @@ -0,0 +1,11 @@ +#ifndef SNAKE3_MANIPULATORHANDLER_H +#define SNAKE3_MANIPULATORHANDLER_H + +#include "../BaseKeydownHandle.h" + +namespace Handler::Debug { + class ManipulatorHandler final : public BaseKeydownHandle { + }; +} + +#endif //SNAKE3_MANIPULATORHANDLER_H \ No newline at end of file diff --git a/Handler/Debug/PositionHandler.cpp b/Handler/Debug/PositionHandler.cpp new file mode 100644 index 00000000..c83a0ae0 --- /dev/null +++ b/Handler/Debug/PositionHandler.cpp @@ -0,0 +1,251 @@ +#include "PositionHandler.h" + +#include "../../Lights/DirectionalLight.h" + +namespace Handler { + PositionHandler::PositionHandler(const shared_ptr &camera): camera(camera), enabled(false) { + cameraOriginalStickyPoint = camera->getStickyPoint(); + } + + void PositionHandler::onDefaultHandler() { + } + + void PositionHandler::onEventHandler(const unsigned int key, int scancode, const int action, const int mods) { + if (activeItem == nullptr) { + return; + } + + const float sensitivity = (mods & GLFW_MOD_SHIFT) ? 2.0f : 0.05f; + glm::vec3 pos = activeItem->getPosition(); + glm::vec3 zoom = activeItem->getScale(); + const float rotationX = activeItem->getRotationX(); + const float rotationY = activeItem->getRotationY(); + const float rotationZ = activeItem->getRotationZ(); + + switch (key) { + case GLFW_KEY_F8: + enabled = !enabled; + camera->setStickyPoint(enabled ? activeItem : cameraOriginalStickyPoint); + break; + case GLFW_KEY_RIGHT: + if (enabled) { + // if (mods & GLFW_MOD_SHIFT) { + // if (mods & GLFW_MOD_CONTROL) { + // if (shared_ptr directLight = std::dynamic_pointer_cast(activeItem)) { + // glm::vec3 direction = directLight->getDirection(); + // direction.x += 0.01f; + // directLight->setDirection(direction); + // } + // } + // } + + const float movement = sensitivity * zoom.x; // * deltaTime; + pos.x += movement; + + activeItem->setPosition(pos); + } + break; + case GLFW_KEY_LEFT: + if (enabled) { + const float movement = sensitivity * zoom.x; // * deltaTime; + + // if (mods & GLFW_MOD_SHIFT) { + // if (mods & GLFW_MOD_CONTROL) { + // if (shared_ptr directLight = std::dynamic_pointer_cast(activeItem)) { + // glm::vec3 direction = directLight->getDirection(); + // direction.x -= 0.01f; + // directLight->setDirection(direction); + // } + // } else { + // pos.x -= 0.1f; // * SCALE + // } + // } else { + // pos.x -= 0.0001f; + // } + + pos.x -= movement; + activeItem->setPosition(pos); + } + break; + case GLFW_KEY_UP: + if (enabled) { + // if (mods & GLFW_MOD_SHIFT) { + // if (mods & GLFW_MOD_CONTROL) { + // if (shared_ptr directLight = std::dynamic_pointer_cast(activeItem)) { + // glm::vec3 direction = directLight->getDirection(); + // direction.y += 0.01f; + // directLight->setDirection(direction); + // } + // } else { + // pos.y += 0.1f; + // } + // } else { + // pos.y += 0.0001f; + // } + + + const float movement = sensitivity * zoom.y; + pos.y += movement; + + activeItem->setPosition(pos); + } + break; + case GLFW_KEY_DOWN: + if (enabled) { + // if (mods & GLFW_MOD_SHIFT) { + // if (mods & GLFW_MOD_CONTROL) { + // if (shared_ptr directLight = std::dynamic_pointer_cast(activeItem)) { + // glm::vec3 direction = directLight->getDirection(); + // direction.y -= 0.01f; + // directLight->setDirection(direction); + // } + // } else { + // pos.y -= 0.1f; + // } + // } else { + // pos.y -= 0.0001f; + // } + + const float movement = sensitivity * zoom.y; + pos.y -= movement; + + activeItem->setPosition(pos); + } + break; + case GLFW_KEY_PAGE_UP: + if (enabled) { + // if (mods & GLFW_MOD_SHIFT) { + // if (mods & GLFW_MOD_CONTROL) { + // if (shared_ptr directLight = std::dynamic_pointer_cast(activeItem)) { + // glm::vec3 direction = directLight->getDirection(); + // direction.z += 0.01f; + // directLight->setDirection(direction); + // } + // } else { + // pos.z += 0.01f; + // } + // } else { + // if (mods & GLFW_MOD_CONTROL) { + // if (shared_ptr directLight = std::dynamic_pointer_cast(activeItem)) { + // glm::vec3 direction = directLight->getDirection(); + // direction.z += 0.0001f; + // directLight->setDirection(direction); + // } + // } else { + // pos.z += 0.0001f; + // } + // } + + const float movement = sensitivity * zoom.z; + pos.z += movement; + + activeItem->setPosition(pos); + } + break; + case GLFW_KEY_PAGE_DOWN: + if (enabled) { + // if (mods & GLFW_MOD_SHIFT) { + // if (mods & GLFW_MOD_CONTROL) { + // if (shared_ptr directLight = std::dynamic_pointer_cast(activeItem)) { + // glm::vec3 direction = directLight->getDirection(); + // direction.z -= 0.01f; + // directLight->setDirection(direction); + // } + // } else { + // pos.z -= 0.01f; + // } + // } else { + // if (mods & GLFW_MOD_CONTROL) { + // if (mods & GLFW_MOD_CONTROL) { + // if (shared_ptr directLight = std::dynamic_pointer_cast(activeItem)) { + // glm::vec3 direction = directLight->getDirection(); + // direction.z -= 0.0001f; + // directLight->setDirection(direction); + // } + // } + // } else { + // pos.z -= 0.0001f; + // } + // } + + const float movement = sensitivity * zoom.z; + pos.z -= movement; + + activeItem->setPosition(pos); + } + break; + case GLFW_KEY_KP_ADD: + if (enabled) { + if (mods & GLFW_MOD_SHIFT) { + zoom.x += 0.01f; + zoom.y += 0.01f; + zoom.z += 0.01f; + } else { + zoom.x += 0.001f; + zoom.y += 0.001f; + zoom.z += 0.001f; + } + activeItem->setScale(zoom); + } + break; + case GLFW_KEY_KP_SUBTRACT: + if (enabled) { + if (mods & GLFW_MOD_SHIFT) { + zoom.x -= 0.01f; + zoom.y -= 0.01f; + zoom.z -= 0.01f; + } else { + zoom.x -= 0.001f; + zoom.y -= 0.001f; + zoom.z -= 0.001f; + } + activeItem->setScale(zoom); + } + break; + case GLFW_KEY_TAB: + if (enabled) { + activeItem = findNextItem(); + camera->setStickyPoint(activeItem); + } + break; + case GLFW_KEY_SPACE: + cout << "Position: " << pos.x << ", " << pos.y << ", " << pos.z << endl; + cout << "Zoom: " << zoom.x << ", " << zoom.y << ", " << zoom.z << endl; + cout << "Rotation X: " << rotationX << endl; + cout << "Rotation Y: " << rotationY << endl; + cout << "Rotation Z: " << rotationZ << endl; + if (const shared_ptr light = std::dynamic_pointer_cast(activeItem)) { + cout << "Direction: " << light->getDirection().x << ", " << light->getDirection().y << ", " << light->getDirection().z << endl; + } + break; + default: + break; + } + } + + void PositionHandler::addItem(const shared_ptr &item) { + items.push_back(item); + + if (activeItem == nullptr) { + activeItem = item; + } + } + + shared_ptr PositionHandler::findNextItem() { + if (items.empty()) { + return nullptr; + } + + for (auto it = items.begin(); it != items.end(); ++it) { + if (activeItem == *it) { + if (it + 1 == items.end()) { + return *items.begin(); + } + + return *(it + 1); + } + } + + return nullptr; + } +} // Handler diff --git a/Handler/Debug/PositionHandler.h b/Handler/Debug/PositionHandler.h new file mode 100644 index 00000000..3f836611 --- /dev/null +++ b/Handler/Debug/PositionHandler.h @@ -0,0 +1,31 @@ +#ifndef POSITIONHANDLER_H +#define POSITIONHANDLER_H + +#include +#include + +#include "../../Manager/Camera.h" +#include "../BaseKeydownHandle.h" +#include "../../Lights/DirectionalLight.h" + +using namespace Manager; +using namespace Lights; + +namespace Handler { + class PositionHandler final : public BaseKeydownHandle { + public: + explicit PositionHandler(const shared_ptr &camera); + void onDefaultHandler() override; + void onEventHandler(unsigned key, int scancode, int action, int mods) override; + void addItem(const shared_ptr &item); + shared_ptr findNextItem(); + protected: + shared_ptr camera = nullptr; + vector > items; + shared_ptr activeItem = nullptr; + shared_ptr cameraOriginalStickyPoint = nullptr; + bool enabled; + }; +} // Handler + +#endif //POSITIONHANDLER_H diff --git a/Handler/Debug/RotationHandler.cpp b/Handler/Debug/RotationHandler.cpp new file mode 100644 index 00000000..892944bf --- /dev/null +++ b/Handler/Debug/RotationHandler.cpp @@ -0,0 +1,6 @@ +#include "RotationHandler.h" + +namespace Handler { + namespace Debug { + } // Debug +} // Handler \ No newline at end of file diff --git a/Handler/Debug/RotationHandler.h b/Handler/Debug/RotationHandler.h new file mode 100644 index 00000000..e5f8107d --- /dev/null +++ b/Handler/Debug/RotationHandler.h @@ -0,0 +1,11 @@ +#ifndef SNAKE3_ROTATIONHANDLER_H +#define SNAKE3_ROTATIONHANDLER_H + +namespace Handler { + namespace Debug { + class RotationHandler { + }; + } // Debug +} // Handler + +#endif //SNAKE3_ROTATIONHANDLER_H \ No newline at end of file diff --git a/Handler/Debug/ScaleHandler.cpp b/Handler/Debug/ScaleHandler.cpp new file mode 100644 index 00000000..2cd9caa9 --- /dev/null +++ b/Handler/Debug/ScaleHandler.cpp @@ -0,0 +1,6 @@ +#include "ScaleHandler.h" + +namespace Handler { + namespace Debug { + } // Debug +} // Handler \ No newline at end of file diff --git a/Handler/Debug/ScaleHandler.h b/Handler/Debug/ScaleHandler.h new file mode 100644 index 00000000..838f02db --- /dev/null +++ b/Handler/Debug/ScaleHandler.h @@ -0,0 +1,11 @@ +#ifndef SNAKE3_SCALEHANDLER_H +#define SNAKE3_SCALEHANDLER_H + +namespace Handler { + namespace Debug { + class ScaleHandler { + }; + } // Debug +} // Handler + +#endif //SNAKE3_SCALEHANDLER_H \ No newline at end of file diff --git a/Handler/EatLocationHandler.cpp b/Handler/EatLocationHandler.cpp index fb2448e8..85989953 100644 --- a/Handler/EatLocationHandler.cpp +++ b/Handler/EatLocationHandler.cpp @@ -1,8 +1,10 @@ #include "EatLocationHandler.h" +#include namespace Handler { - EatLocationHandler::EatLocationHandler(Barriers* barriers, Snake *snake, Eat *eat, Radar* radar) : - barriers(barriers), eat(eat), snake(snake), counter(0), radar(radar) { + EatLocationHandler::EatLocationHandler(const shared_ptr &barriers, const shared_ptr &snake, + const shared_ptr &eat) + : barriers(barriers), snake(snake), eat(eat), counter(0) { } EatLocationHandler::~EatLocationHandler() = default; @@ -13,39 +15,42 @@ namespace Handler { rePosition(); } - bool EatLocationHandler::rePosition() { + void EatLocationHandler::rePosition() const { try { - glm::vec2 newPos = getPosition(); + const glm::vec2 newPos = getPosition(); glm::vec3 pos = eat->getPosition(); //cout << "Eat: X=" << newPos.x << ", Y=" << newPos.y << endl; - eat->setVirtualX((int)newPos.x * 32 + 16); - eat->setVirtualY((int)newPos.y * 32 + 16); + eat->x = static_cast(newPos.x) * 32 + 16; + eat->y = static_cast(newPos.y) * 32 + 16; eat->setPosition({-69 + (newPos.x * 6), -69 + (newPos.y * 6), pos.z}); eat->setVisible(true); - - return true; } catch (const std::invalid_argument &e) { eat->setVisible(false); - - return false; } } - bool EatLocationHandler::isFieldEmpty(int x, int y) { + bool EatLocationHandler::isFieldEmpty(const int x, const int y) const { + const int posX = x * 32; + const int posY = y * 32; - int posX = x * 32; - int posY = y * 32; + if (snake->x - 16 + 32 >= posX && snake->x - 16 <= posX + && snake->y - 16 + 32 >= posY && snake->y - 16 <= posY) { + return false; + } - for (auto Iter = snake->getItems().begin(); Iter < snake->getItems().end(); Iter++) { - if ((int) (*Iter)->tile->getVirtualX() - 16 + 32 >= posX && (int) (*Iter)->tile->getVirtualX() - 16 <= posX - && (int) (*Iter)->tile->getVirtualY() - 16 + 32 >= posY && (int) (*Iter)->tile->getVirtualY() - 16 <= posY) { + for (auto Iter = snake->getChildren().begin(); Iter < snake->getChildren().end(); ++Iter) { + if ((*Iter)->x - 16 + 32 >= posX && (*Iter)->x - 16 <= posX + && (*Iter)->y - 16 + 32 >= posY && (*Iter)->y - 16 <= posY) { return false; } } - for (auto Iter = barriers->getItems().begin(); Iter < barriers->getItems().end(); Iter++) { - if ((int) (*Iter)->getVirtualX() - 16 + 32 > posX && (int) (*Iter)->getVirtualX() - 16 <= posX - && (int) (*Iter)->getVirtualY() - 16 + 32 > posY && (int) (*Iter)->getVirtualY() - 16 <= posY) { + if (barriers->x == posX && barriers->y == posY) { + return false; + } + + for (auto Iter = barriers->getChildren().begin(); Iter < barriers->getChildren().end(); ++Iter) { + if ((*Iter)->x == posX && (*Iter)->y == posY) { return false; } } @@ -53,14 +58,15 @@ namespace Handler { return true; } - void EatLocationHandler::onFirstPlaceHandler() { + void EatLocationHandler::onFirstPlaceHandler() const { while (true) { try { - glm::vec2 newPos = getPosition(); + const glm::vec2 newPos = getPosition(); glm::vec3 pos = eat->getPosition(); - eat->setVirtualX((int)newPos.x * 32 + 16); - eat->setVirtualY((int)newPos.y * 32 + 16); + eat->x = static_cast(newPos.x) * 32 + 16; + eat->y = static_cast(newPos.y) * 32 + 16; eat->setPosition({-69 + (newPos.x * 6), -69 + (newPos.y * 6), pos.z}); + eat->setScale({0.013888889, 0.013888889, 0.013888889}); eat->setVisible(true); break; } catch (const std::invalid_argument &e) { @@ -68,28 +74,25 @@ namespace Handler { } } - glm::vec2 EatLocationHandler::getPosition() { - - random_device rd; // obtain a random number from hardware - mt19937 gen(rd()); // seed the generator - uniform_int_distribution<> fields(0, 47); // define the range + glm::vec2 EatLocationHandler::getPosition() const { + random_device rd; + mt19937 gen(rd()); + uniform_int_distribution<> fields(0, 47); - int numberX = fields(gen); - int numberY = fields(gen); + const int numberX = fields(gen); + const int numberY = fields(gen); // tady musim checknout, ze je to policko prazdne, jinak musim najit jine - glm::vec3 pos = eat->getPosition(); if (isFieldEmpty(numberX, numberY)) { - glm::vec2 pos = {numberX, numberY}; - - return pos; + return {numberX, numberY}; } throw std::invalid_argument(""); } - void EatLocationHandler::onCheckPlaceHandler() { - if (!eat->isVisible() && (*snake->getItems().begin())->direction > STOP && (*snake->getItems().begin())->direction < CRASH) { + void EatLocationHandler::onCheckPlaceHandler() const { + if (!eat->isVisible() && + snake->getDirection() > SnakeMeshNode3D::STOP && snake->getDirection() < SnakeMeshNode3D::CRASH) { rePosition(); } } @@ -97,16 +100,12 @@ namespace Handler { void EatLocationHandler::addTile() { counter++; - for(int x = 0; x < counter + 1; x++) { - auto tile = snake->addTile((*snake->getItems().begin())->direction); - if (tile != nullptr) { - radar->addItem(tile->tile, {0.278, 1., 0.}); - } + for (int x = 0; x < counter + 1; x++) { + snake->addTile(snake->getDirection()); } } void EatLocationHandler::onCleanHandler() { counter = 0; } - -} // Handler \ No newline at end of file +} // Handler diff --git a/Handler/EatLocationHandler.h b/Handler/EatLocationHandler.h index b18d67f4..db2a53eb 100644 --- a/Handler/EatLocationHandler.h +++ b/Handler/EatLocationHandler.h @@ -1,39 +1,45 @@ #ifndef SNAKE3_EATLOCATIONHANDLER_H #define SNAKE3_EATLOCATIONHANDLER_H -#include "../ItemsDto/Snake.h" #include "BaseHandler.h" #include "../Physic/CollisionDetector.h" -#include "../ItemsDto/Eat.h" -#include "../ItemsDto/Radar.h" -#include +#include "../Renderer/Opengl/Model/Game/CoinMeshNode3D.h" +#include "../Renderer/Opengl/Model/Game/SnakeMeshNode3D.h" using namespace ItemsDto; using namespace Physic; using namespace std; namespace Handler { - - class EatLocationHandler : public BaseHandler { + class EatLocationHandler final : public BaseHandler { public: ~EatLocationHandler() override; - explicit EatLocationHandler(Barriers* barriers, Snake* snake, Eat* eat, Radar* radar); + + explicit EatLocationHandler(const shared_ptr &barriers, const shared_ptr &snake, + const shared_ptr &eat); + void onDefaultHandler() override; - void onFirstPlaceHandler(); - void onCheckPlaceHandler(); + + void onFirstPlaceHandler() const; + + void onCheckPlaceHandler() const; + void onCleanHandler(); - bool rePosition(); - bool isFieldEmpty(int x, int y); + + void rePosition() const; + + [[nodiscard]] bool isFieldEmpty(int x, int y) const; + protected: - Radar* radar; - Snake* snake; - Barriers* barriers; - Eat* eat; - void addTile(); - glm::vec2 getPosition(); + shared_ptr barriers; + shared_ptr snake; + shared_ptr eat; int counter; - }; + void addTile(); + + [[nodiscard]] glm::vec2 getPosition() const; + }; } // Handler #endif //SNAKE3_EATLOCATIONHANDLER_H diff --git a/Handler/RadarHandler.cpp b/Handler/RadarHandler.cpp index 4355665e..02acf267 100644 --- a/Handler/RadarHandler.cpp +++ b/Handler/RadarHandler.cpp @@ -1,16 +1,12 @@ #include "RadarHandler.h" namespace Handler { - RadarHandler::RadarHandler(Radar *radar) : radar(radar) {} + RadarHandler::RadarHandler(const shared_ptr &radar) : radar(radar) {} - void RadarHandler::onEventHandler(unsigned int key) { + void RadarHandler::onEventHandler(const unsigned int key, int scancode, const int action, int mods) { if (key == GLFW_KEY_R) { - radar->toggleVisible(); + radar->setVisible(!radar->isVisible()); } } - void RadarHandler::onDefaultHandler() { - BaseKeydownHandle::onDefaultHandler(); - } - } // Handler \ No newline at end of file diff --git a/Handler/RadarHandler.h b/Handler/RadarHandler.h index 832bcbef..371b302a 100644 --- a/Handler/RadarHandler.h +++ b/Handler/RadarHandler.h @@ -1,21 +1,24 @@ #ifndef SNAKE3_RADARHANDLER_H #define SNAKE3_RADARHANDLER_H -#include "../ItemsDto/Radar.h" +#include + #include "BaseKeydownHandle.h" +#include "../Renderer/Opengl/Model/Standard/MeshNode3D.h" -using namespace ItemsDto; +using namespace Model; +using namespace std; namespace Handler { class RadarHandler : public BaseKeydownHandle { public: - explicit RadarHandler(Radar *radar); - void onEventHandler(unsigned int key) override; - void onDefaultHandler() override; + explicit RadarHandler(const shared_ptr &radar); + void onEventHandler(unsigned int key, int scancode, int action, int mods) override; + void onDefaultHandler() override = 0; protected: - Radar* radar; + shared_ptr radar; }; } // Handler diff --git a/Handler/SnakeMoveHandler.cpp b/Handler/SnakeMoveHandler.cpp index b0645ecb..dfbf8b5c 100644 --- a/Handler/SnakeMoveHandler.cpp +++ b/Handler/SnakeMoveHandler.cpp @@ -1,42 +1,74 @@ #include "SnakeMoveHandler.h" namespace Handler { - SnakeMoveHandler::SnakeMoveHandler(Snake *snake, AnimationModel* animHead) : snake(snake), animHead(animHead), eatenUpCallbackCalled(false) { - snakeHead = (*snake->getItems().begin()); + SnakeMoveHandler::SnakeMoveHandler(const shared_ptr &snake_mesh_node) + : snakeMeshNode(snake_mesh_node), eatenUpCallbackCalled(false) { stop = false; + lastTime = glfwGetTime(); + moveAccumulator = 0.0; + constexpr double stepsPerTile = static_cast(CUBE_SIZE) / static_cast(VIRTUAL_MOVE); + constexpr double tilesPerSecond = 3.5; + moveInterval = 1.0 / (tilesPerSecond * stepsPerTile); } SnakeMoveHandler::~SnakeMoveHandler() = default; - void SnakeMoveHandler::onEventHandler(unsigned int key) { + void SnakeMoveHandler::onEventHandler(const unsigned int key, int scancode, const int action, int mods) { switch (key) { case GLFW_KEY_L: case GLFW_KEY_I: case GLFW_KEY_J: case GLFW_KEY_K: - if (!stop) { - ChangeMove(key); + if (!stop && snakeMeshNode->isReady()) { + changeMove(key); } break; case GLFW_KEY_SPACE: - StopMove(); + stopMove(); break; default: break; } } - void SnakeMoveHandler::StopMove() { + void SnakeMoveHandler::stopMove() { if (!changeCallback) { stop = !stop; - animHead->setGlobalPause(stop); + stopMoveCallback(stop); } } - void SnakeMoveHandler::ChangeMove(unsigned int direction) { + void SnakeMoveHandler::moveTile(const shared_ptr &snakeMeshNode) { + glm::vec3 pos = snakeMeshNode->getPosition(); + switch (snakeMeshNode->getDirection()) { + case SnakeMeshNode3D::LEFT: + pos.x -= UNIT_MOVE; + snakeMeshNode->x = snakeMeshNode->x - VIRTUAL_MOVE; + break; + case SnakeMeshNode3D::RIGHT: + pos.x += UNIT_MOVE; + snakeMeshNode->x = snakeMeshNode->x + VIRTUAL_MOVE; + break; + case SnakeMeshNode3D::UP: + pos.y += UNIT_MOVE; + snakeMeshNode->y = snakeMeshNode->y + VIRTUAL_MOVE; + break; + case SnakeMeshNode3D::DOWN: + pos.y -= UNIT_MOVE; + snakeMeshNode->y = snakeMeshNode->y - VIRTUAL_MOVE; + break; + case SnakeMeshNode3D::CRASH: + case SnakeMeshNode3D::PAUSE: + case SnakeMeshNode3D::STOP: + case SnakeMeshNode3D::NONE: + break; + } + snakeMeshNode->setPosition(pos); + } + void SnakeMoveHandler::changeMove(const unsigned int direction) { if (changeCallback || - !isNewDirectionCorrect(snakeHead, direction)) { + !isNewDirectionCorrect(direction)) { return; } @@ -44,40 +76,46 @@ namespace Handler { } void SnakeMoveHandler::createChangeCallback(unsigned int direction) { - changeCallback = [this, direction](sSNAKE_TILE *head) { - if (isChangeDirectionAllowed(head)) { - - int x = (int) head->tile->getPosition().x - 1; - int y = (int) head->tile->getPosition().y - 1; - if (x % CUBE_SIZE == 0 && y % CUBE_SIZE == 0) { - cout << "Zmena smeru povolena: " << head->tile->getPosition().x << ", " << head->tile->getPosition().y << endl; - } - - if (head->direction == STOP && startMoveCallback) { // start hry - startMoveCallback(); + changeCallback = [this, direction](const shared_ptr &snakeMeshNode) { + if (isChangeDirectionAllowed()) { + if (snakeMeshNode->getDirection() == SnakeMeshNode3D::NONE && !startMoveCallbacks.empty()) { + // start hry + for (auto &startMoveCallback: startMoveCallbacks) { + startMoveCallback(); + } } switch (direction) { case GLFW_KEY_J: // left - head->direction = ItemsDto::LEFT; + snakeMeshNode->setDirection(SnakeMeshNode3D::LEFT); + snakeMeshNode->setRotationX(90); + snakeMeshNode->setRotationY(180); break; case GLFW_KEY_L: // right - head->direction = ItemsDto::RIGHT; + snakeMeshNode->setDirection(SnakeMeshNode3D::RIGHT); + snakeMeshNode->setRotationX(90); + snakeMeshNode->setRotationY(0); break; case GLFW_KEY_I: - head->direction = UP; + snakeMeshNode->setDirection(SnakeMeshNode3D::UP); + snakeMeshNode->setRotationX(90); + snakeMeshNode->setRotationY(90); break; case GLFW_KEY_K: - head->direction = DOWN; + snakeMeshNode->setDirection(SnakeMeshNode3D::DOWN); + snakeMeshNode->setRotationX(90); + snakeMeshNode->setRotationY(-90); break; default: break; } - for (auto Iter = snake->getItems().begin() + 1; Iter < snake->getItems().end(); Iter++) { + for (auto &node: snakeMeshNode->getChildren()) { // prvni rozbehnuti tela je vzdy vpravo, protoze na startu je hlava vpravo od tela - if ((*Iter)->direction == NONE) { - (*Iter)->direction = ItemsDto::RIGHT; + const auto snakeMesh = dynamic_pointer_cast(node); + const auto nodeDirection = snakeMesh->getDirection(); + if (nodeDirection == SnakeMeshNode3D::NONE) { + snakeMesh->setDirection(SnakeMeshNode3D::RIGHT); } } @@ -89,120 +127,99 @@ namespace Handler { } void SnakeMoveHandler::onDefaultHandler() { + const double now = glfwGetTime(); + const double deltaTime = now - lastTime; + lastTime = now; + if (stop) { return; } - double now = glfwGetTime(); + moveAccumulator += deltaTime; + + // Zpracovávej kroky v pevném intervalu odvozeném od požadované rychlosti (dlaždice/s) + while (moveAccumulator >= moveInterval) { + moveAccumulator -= moveInterval; - if (now - next_time >= 0.005) { if (changeCallback) { - if (changeCallback(snakeHead)) { + if (changeCallback(snakeMeshNode)) { changeCallback = nullptr; } } - if (snakeHead->direction == NONE || snakeHead->direction == STOP) { + if (snakeMeshNode->getDirection() == SnakeMeshNode3D::NONE || snakeMeshNode->getDirection() == + SnakeMeshNode3D::STOP) { + moveAccumulator = 0; // Resetujeme akumulátor, pokud se had zastavil nebo ještě nevyjel return; } - bool allowed = isChangeDirectionAllowed(snakeHead); + const bool allowed = isChangeDirectionAllowed(); + const auto &children = snakeMeshNode->getChildren(); - for (auto Iter = snake->getItems().end() - 1; Iter >= snake->getItems().begin(); Iter--) { - eDIRECTION direction = (*Iter)->direction; + for (auto Iter = children.rbegin(); Iter != children.rend(); ++Iter) { + const auto &tile = dynamic_pointer_cast(*Iter); - if (snake->getItems().begin() != Iter && allowed) { - auto PrevIter = Iter - 1; - direction = findDirection((*PrevIter), (*Iter)); - (*Iter)->direction = direction; - } - - glm::vec3 pos = (*Iter)->tile->getPosition(); - switch (direction) { - case ItemsDto::LEFT: - pos.x -= UNIT_MOVE; - (*Iter)->tile->setVirtualX((*Iter)->tile->getVirtualX() - VIRTUAL_MOVE); - break; - case ItemsDto::RIGHT: - pos.x += UNIT_MOVE; - (*Iter)->tile->setVirtualX((*Iter)->tile->getVirtualX() + VIRTUAL_MOVE); - break; - case UP: - pos.y += UNIT_MOVE; - (*Iter)->tile->setVirtualY((*Iter)->tile->getVirtualY() + VIRTUAL_MOVE); - break; - case DOWN: - pos.y -= UNIT_MOVE; - (*Iter)->tile->setVirtualY((*Iter)->tile->getVirtualY() - VIRTUAL_MOVE); - break; - case CRASH: - case PAUSE: - case STOP: - case NONE: - break; + if (allowed) { + tile->setDirection(findDirection(std::next(Iter).base())); } - (*Iter)->tile->setPosition(pos); + moveTile(tile); } - next_time = now; - // detekujeme jen kdyz je predmet na kterem detekujeme v pohybu - if (snakeHead->direction > STOP && snakeHead->direction < CRASH) { - // pokud je hlava a pohnula se, tak checkneme zda je komplet v hraci kosticce - // pokud ano, tak checkneme kolizi s jidlem - bool allowed = isChangeDirectionAllowed(snakeHead); - if (allowed && collisionDetector->detectWithStaticItem(snakeHead->tile)) { - cout << "Head position(eaten): " << snakeHead->tile->getPosition().x << ", " << snakeHead->tile->getPosition().y << endl; - eatenUpCallback(); - if (snakeHead->direction == STOP) { // doslo k postupu do dalsiho level - changeCallback = nullptr; - } - } + moveTile(snakeMeshNode); - if (collisionDetector->perimeterDetect(snakeHead->tile) - || collisionDetector->barrierCollision(snakeHead->tile) - || Physic::CollisionDetector::intoHimSelf(snake) - ) { - if (crashCallback) { - crashCallback(); // doslo k narazu - } - changeCallback = nullptr; - } + // detekujeme jen kdyz je predmet na kterem detekujeme v pohybu + if (collisionDetector && snakeMeshNode->getDirection() > SnakeMeshNode3D::STOP && snakeMeshNode->getDirection() < SnakeMeshNode3D::CRASH) { + const bool l_allowed = isChangeDirectionAllowed(); + if (l_allowed && collisionDetector->detectWithStaticItem(snakeMeshNode)) { + cout << "Head position(eaten): " << snakeMeshNode->getPosition().x << ", " << snakeMeshNode->getPosition().y << endl; + if (eatenUpCallback) { + eatenUpCallback(); + } + if (snakeMeshNode->getDirection() == SnakeMeshNode3D::STOP) { // doslo k postupu do dalsiho level + changeCallback = nullptr; + } + } } } } - bool SnakeMoveHandler::isChangeDirectionAllowed(sSNAKE_TILE *snake) { - int x = (int) snake->tile->getVirtualX() - 16; - int y = (int) snake->tile->getVirtualY() - 16; + bool SnakeMoveHandler::isChangeDirectionAllowed() const { + const int x = snakeMeshNode->x - 16; + const int y = snakeMeshNode->y - 16; return x % CUBE_SIZE == 0 && y % CUBE_SIZE == 0; } - bool SnakeMoveHandler::isNewDirectionCorrect(sSNAKE_TILE *headTile, unsigned int direction) { - - if (headTile->direction == PAUSE) { + bool SnakeMoveHandler::isNewDirectionCorrect(const unsigned int direction) const { + if (snakeMeshNode->getDirection() == SnakeMeshNode3D::PAUSE) { return false; } switch (direction) { case GLFW_KEY_L: - if (headTile->direction == ItemsDto::LEFT || headTile->direction == ItemsDto::RIGHT) { + if (snakeMeshNode->getDirection() == SnakeMeshNode3D::LEFT || snakeMeshNode->getDirection() == + SnakeMeshNode3D::RIGHT) { return false; } return true; case GLFW_KEY_I: - if (headTile->direction == DOWN || headTile->direction == UP) { + if (snakeMeshNode->getDirection() == SnakeMeshNode3D::DOWN || snakeMeshNode->getDirection() == + SnakeMeshNode3D::UP) { return false; } return true; case GLFW_KEY_J: - if (headTile->direction == ItemsDto::RIGHT || headTile->direction == ItemsDto::LEFT || headTile->direction == STOP || headTile->direction == CRASH) { + if (snakeMeshNode->getDirection() == SnakeMeshNode3D::RIGHT || + snakeMeshNode->getDirection() == SnakeMeshNode3D::LEFT || + snakeMeshNode->getDirection() == SnakeMeshNode3D::STOP || + snakeMeshNode->getDirection() == SnakeMeshNode3D::CRASH) { return false; } return true; case GLFW_KEY_K: - if (headTile->direction == UP || headTile->direction == DOWN) { + if (snakeMeshNode->getDirection() == SnakeMeshNode3D::UP || snakeMeshNode->getDirection() == + SnakeMeshNode3D::DOWN) { return false; } return true; @@ -211,12 +228,16 @@ namespace Handler { } } - void SnakeMoveHandler::setCollisionDetector(CollisionDetector *collisionDetector) { - SnakeMoveHandler::collisionDetector = collisionDetector; + void SnakeMoveHandler::setCollisionDetector(shared_ptr collisionDetector) { + SnakeMoveHandler::collisionDetector = std::move(collisionDetector); } - void SnakeMoveHandler::setStartMoveCallback(const function &startMoveCallback) { - SnakeMoveHandler::startMoveCallback = startMoveCallback; + void SnakeMoveHandler::addStartMoveCallback(const function &startMoveCallback) { + startMoveCallbacks.push_back(startMoveCallback); + } + + void SnakeMoveHandler::setStopMoveCallback(const function &stopMoveCallback) { + SnakeMoveHandler::stopMoveCallback = stopMoveCallback; } void SnakeMoveHandler::setCrashCallback(const function &crashCallback) { @@ -227,19 +248,26 @@ namespace Handler { SnakeMoveHandler::eatenUpCallback = eatenUpCallback; } - eDIRECTION SnakeMoveHandler::findDirection(sSNAKE_TILE* snakeTile, sSNAKE_TILE* mySelf) { - // najdi kosticku co je hned vedle - if (snakeTile->tile->getPosition().x > mySelf->tile->getPosition().x) { - return ItemsDto::RIGHT; - } else if (snakeTile->tile->getPosition().x < mySelf->tile->getPosition().x) { - return ItemsDto::LEFT; - } else if (snakeTile->tile->getPosition().y < mySelf->tile->getPosition().y) { - return DOWN; - } else if (snakeTile->tile->getPosition().y > mySelf->tile->getPosition().y) { - return UP; + template + SnakeMeshNode3D::eDIRECTION SnakeMoveHandler::findDirection(Iter iter) const { + const auto &children = snakeMeshNode->getChildren(); + const shared_ptr tile = iter == children.begin() + ? snakeMeshNode + : dynamic_pointer_cast(*(iter - 1)); + + if (tile->getPosition().x > iter->get()->getPosition().x) { + return SnakeMeshNode3D::RIGHT; + } + if (tile->getPosition().x < iter->get()->getPosition().x) { + return SnakeMeshNode3D::LEFT; + } + if (tile->getPosition().y < iter->get()->getPosition().y) { + return SnakeMeshNode3D::DOWN; + } + if (tile->getPosition().y > iter->get()->getPosition().y) { + return SnakeMeshNode3D::UP; } - return NONE; + return SnakeMeshNode3D::NONE; } - -} // Handler \ No newline at end of file +} // Handler diff --git a/Handler/SnakeMoveHandler.h b/Handler/SnakeMoveHandler.h index 124ce05a..ae5cb834 100644 --- a/Handler/SnakeMoveHandler.h +++ b/Handler/SnakeMoveHandler.h @@ -1,10 +1,13 @@ #ifndef SNAKE3_SNAKEMOVEHANDLER_H #define SNAKE3_SNAKEMOVEHANDLER_H -#include "../ItemsDto/Snake.h" -#include "../Renderer/Opengl/Model/AnimationModel.h" +#define UNIT_MOVE 0.125 +#define VIRTUAL_MOVE 2 // kvuli nepresnosti float cislum pocitame virtualne v integer formatu +#define CUBE_SIZE 32 + #include "../Physic/CollisionDetector.h" #include "BaseKeydownHandle.h" +#include "../Renderer/Opengl/Model/Game/SnakeMeshNode3D.h" using namespace ItemsDto; using namespace Physic; @@ -12,37 +15,40 @@ using namespace Model; namespace Handler { - class SnakeMoveHandler : public BaseKeydownHandle { + class SnakeMoveHandler final : public BaseKeydownHandle { public: ~SnakeMoveHandler() override; - explicit SnakeMoveHandler(Snake *snake, AnimationModel* animHead); - void onEventHandler(unsigned int key) override; + explicit SnakeMoveHandler(const shared_ptr &snake_mesh_node); + void onEventHandler(unsigned int key, int scancode, int action, int mods) override; void onDefaultHandler() override; - void setCollisionDetector(CollisionDetector *collisionDetector); - void setStartMoveCallback(const function &startMoveCallback); + void setCollisionDetector(shared_ptr collisionDetector); + void addStartMoveCallback(const function &startMoveCallback); + void setStopMoveCallback(const function &stopMoveCallback); void setCrashCallback(const function &crashCallback); void setEatenUpCallback(const function &eatenUpCallback); protected: - void ChangeMove(unsigned int direction); - void StopMove(); - static eDIRECTION findDirection(sSNAKE_TILE* snakeTile, sSNAKE_TILE* mySelf); + void changeMove(unsigned int direction); + void stopMove(); + static void moveTile(const shared_ptr &snakeMeshNode); + template + [[nodiscard]] SnakeMeshNode3D::eDIRECTION findDirection(Iter iter) const; void createChangeCallback(unsigned int direction); - static bool isChangeDirectionAllowed(sSNAKE_TILE* snake); - static bool isNewDirectionCorrect(sSNAKE_TILE* headTile, unsigned int direction); - Snake* snake; - AnimationModel* animHead; - sSNAKE_TILE* snakeHead; - CollisionDetector* collisionDetector{}; - double next_time{}; + [[nodiscard]] bool isChangeDirectionAllowed() const; + [[nodiscard]] bool isNewDirectionCorrect(unsigned int direction) const; + shared_ptr snakeMeshNode; + shared_ptr collisionDetector{}; + double lastTime{}; + double moveAccumulator{}; + double moveInterval = 0.1; // Výchozí interval pohybu (např. 10 kroků za sekundu) bool stop; bool eatenUpCallbackCalled; - std::function changeCallback; - std::function startMoveCallback; + std::function)> changeCallback; + vector> startMoveCallbacks; + std::function stopMoveCallback; std::function crashCallback; std::function eatenUpCallback; }; - } // Manager #endif //SNAKE3_SNAKEMOVEHANDLER_H diff --git a/ItemsDto/AnimItem.cpp b/ItemsDto/AnimItem.cpp index 5eda08a0..8528010d 100644 --- a/ItemsDto/AnimItem.cpp +++ b/ItemsDto/AnimItem.cpp @@ -1,13 +1,23 @@ #include "AnimItem.h" -ItemsDto::AnimationNode::AnimationNode(decltype(positions) _positions, decltype(rotations) _rotations, decltype(scales) _scales, Bone& _bone) noexcept - : rotations(std::move(_rotations)) - , positions(std::move(_positions)) - , scales(std::move(_scales)) +ItemsDto::AnimationNode::AnimationNode(decltype(positions) positions, decltype(rotations) rotations, decltype(scales) scales, const shared_ptr &_bone) noexcept + : rotations(std::move(rotations)) + , positions(std::move(positions)) + , scales(std::move(scales)) , bone(_bone) { } -glm::vec3 ItemsDto::AnimationNode::positionLerp(double anim_time) const { +ItemsDto::AnimationNode::AnimationNode(decltype(positions) positions, decltype(rotations) rotations, decltype(scales) scales) noexcept + : rotations(std::move(rotations)) + , positions(std::move(positions)) + , scales(std::move(scales)) { +} + +void ItemsDto::AnimationNode::setAlphaFrames(decltype(alphas) alphas) noexcept { + this->alphas = std::move(alphas); +} + +glm::vec3 ItemsDto::AnimationNode::positionLerp(const double anim_time) const { if (positions.empty()) { return glm::vec3{0.0f}; } @@ -16,22 +26,25 @@ glm::vec3 ItemsDto::AnimationNode::positionLerp(double anim_time) const { return positions[0].data; } - auto index = findPositionKeyframe(anim_time); + const auto index = findPositionKeyframe(anim_time); auto& a = positions[index]; auto& b = positions[index + 1]; - double dt = b.time - a.time; - double norm = (anim_time - a.time) / dt; + const double dt = b.time - a.time; + const double norm = (anim_time - a.time) / dt; // if (!(norm >= 0.f && norm <= 1.f)) { // throw std::runtime_error("norm >= 0.f && norm <= 1.f"); // } + const double eased = ease(norm, easing_value); + + return a.data * static_cast(1.0 - eased) + b.data * static_cast(eased); // return glm::mix(a.data, b.data, norm); - return a.data * (float)(1.0 - norm) + b.data * (float)norm; + //return a.data * static_cast(1.0 - norm) + b.data * static_cast(norm); } -glm::fquat ItemsDto::AnimationNode::rotationLerp(double anim_time) const { +glm::fquat ItemsDto::AnimationNode::rotationLerp(const double anim_time) const { if (rotations.empty()) { return glm::fquat{1.f, 0.f, 0.f, 0.f}; } @@ -39,12 +52,12 @@ glm::fquat ItemsDto::AnimationNode::rotationLerp(double anim_time) const { return rotations[0].data; } - auto index = findRotationKeyframe(anim_time); + const auto index = findRotationKeyframe(anim_time); auto& a = rotations[index]; auto& b = rotations[index + 1]; - double dt = b.time - a.time; - double norm = (anim_time - a.time) / dt; + const double dt = b.time - a.time; + const double norm = (anim_time - a.time) / dt; // if (!(norm >= 0.f && norm <= 1.f)) { // throw std::runtime_error("norm >= 0.f && norm <= 1.f"); @@ -53,7 +66,32 @@ glm::fquat ItemsDto::AnimationNode::rotationLerp(double anim_time) const { return glm::normalize(glm::slerp(a.data, b.data, static_cast(norm))); } -glm::vec3 ItemsDto::AnimationNode::scalingLerp(double anim_time) const { +float ItemsDto::AnimationNode::alphaLerp(const double anim_time) const { + if (alphas.empty()) { + return 1.0f; + } + + if (alphas.size() == 1) { + return alphas[0].data; + } + + const size_t index = findAlphaKeyframe(anim_time); + const auto& a = alphas[index]; + const auto& b = alphas[index + 1]; + + const double dt = b.time - a.time; + if (dt <= 0.0) { + return b.data; + } + + double t = (anim_time - a.time) / dt; + t = glm::clamp(t, 0.0, 1.0); + t = ease(t, easing_value); + + return static_cast(a.data + (b.data - a.data) * t); +} + +glm::vec3 ItemsDto::AnimationNode::scalingLerp(const double anim_time) const { if (scales.empty()) { return glm::vec3{1.f}; } @@ -76,7 +114,20 @@ glm::vec3 ItemsDto::AnimationNode::scalingLerp(double anim_time) const { return glm::mix(a.data, b.data, norm); } -size_t ItemsDto::AnimationNode::findPositionKeyframe(double anim_time) const { +double ItemsDto::AnimationNode::ease(double t, const double curve) { + if (t < 0.0) t = 0.0; + else if (t > 1.0) t = 1.0; + + if (curve > 0.0) { + return 1.0 - std::pow(1.0 - t, 1.0 + curve * 3.0); + } + if (curve < 0.0) { + return std::pow(t, 1.0 - curve * 3.0); + } + return t; +} + +size_t ItemsDto::AnimationNode::findPositionKeyframe(const double anim_time) const { for (size_t i = 0; i < positions.size() - 1; ++i) { if (anim_time <= positions[i + 1].time) { return i; @@ -86,7 +137,7 @@ size_t ItemsDto::AnimationNode::findPositionKeyframe(double anim_time) const { throw std::out_of_range("no position keyframe for time " + std::to_string(anim_time)); } -size_t ItemsDto::AnimationNode::findRotationKeyframe(double anim_time) const { +size_t ItemsDto::AnimationNode::findRotationKeyframe(const double anim_time) const { for (size_t i = 0; i < rotations.size() - 1; ++i) { if (anim_time <= rotations[i + 1].time) return i; @@ -95,7 +146,7 @@ size_t ItemsDto::AnimationNode::findRotationKeyframe(double anim_time) const { throw std::out_of_range("no rotation keyframe for time " + std::to_string(anim_time)); } -size_t ItemsDto::AnimationNode::findScalingKeyframe(double anim_time) const { +size_t ItemsDto::AnimationNode::findScalingKeyframe(const double anim_time) const { for (size_t i = 0; i < scales.size() - 1; ++i) { if (anim_time <= scales[i + 1].time) return i; @@ -103,3 +154,12 @@ size_t ItemsDto::AnimationNode::findScalingKeyframe(double anim_time) const { return 0; throw std::out_of_range("no scaling keyframe for time " + std::to_string(anim_time)); } + +size_t ItemsDto::AnimationNode::findAlphaKeyframe(const double anim_time) const { + for (size_t i = 0; i < alphas.size() - 1; ++i) { + if (anim_time <= alphas[i + 1].time) + return i; + } + return 0; + throw std::out_of_range("no scaling keyframe for time " + std::to_string(anim_time)); +} \ No newline at end of file diff --git a/ItemsDto/AnimItem.h b/ItemsDto/AnimItem.h index b8bbe0b6..d4f06091 100644 --- a/ItemsDto/AnimItem.h +++ b/ItemsDto/AnimItem.h @@ -3,6 +3,7 @@ #include "BaseItem.h" #include +#include #include #include @@ -14,7 +15,7 @@ namespace ItemsDto { T data; double time; - KeyFrame(T data, double time) noexcept + KeyFrame(T data, const double time) noexcept : data{std::move(data)} , time(time) {} }; @@ -40,9 +41,9 @@ namespace ItemsDto { std::array bone_index; std::array weight; - void addBoneWeight(uint32_t id, float w) noexcept { + void addBoneWeight(const uint32_t id, const float w) noexcept { uint32_t i; - for (i = 0; i < BONE_COUNT && weight[i] != 0.0f; ++i); + for (i = 0; i < BONE_COUNT && weight[i] != 0.0f; ++i) {} if (i >= BONE_COUNT) {/* no more weight slots */ return; } @@ -55,30 +56,50 @@ namespace ItemsDto { vector> rotations; vector> positions; vector> scales; - Bone &bone; + vector> alphas; + shared_ptr bone = nullptr; + float easing_value = 0.0f; +// 0 → linear +// 0.5 → mírné ease-out +// 2 → hodně silné ease-out +// –2 → silné ease-in +// –15 → ultra rychlý ease-in - AnimationNode(decltype(positions) positions, decltype(rotations) rotations, decltype(scales) scales, Bone &_bone) noexcept; + AnimationNode(decltype(positions) positions, decltype(rotations) rotations, decltype(scales) scales, const shared_ptr &_bone) noexcept; + AnimationNode(decltype(positions) positions, decltype(rotations) rotations, decltype(scales) scales) noexcept; + + //void addFrame(double time, const glm::fquat &rotation, const glm::vec3 &position, const glm::vec3 &scale) noexcept; + void setAlphaFrames(decltype(alphas) alphas) noexcept; [[nodiscard]] size_t findPositionKeyframe(double anim_time) const; [[nodiscard]] size_t findRotationKeyframe(double anim_time) const; [[nodiscard]] size_t findScalingKeyframe(double anim_time) const; + [[nodiscard]] size_t findAlphaKeyframe(double anim_time) const; [[nodiscard]] glm::vec3 positionLerp(double anim_time) const; [[nodiscard]] glm::fquat rotationLerp(double anim_time) const; [[nodiscard]] glm::vec3 scalingLerp(double anim_time) const; + [[nodiscard]] float alphaLerp(double anim_time) const; + [[nodiscard]] double static ease(double t, double curve); }; struct Animation { - std::vector nodes; + std::vector > nodes; std::string name; double duration; double tps; - Animation(std::string name, double duration, double tps, decltype(nodes) nodes) noexcept + Animation(std::string name, const double duration, const double tps, decltype(nodes) nodes) noexcept : nodes(std::move(nodes)) , name(std::move(name)) , duration(duration) , tps(tps) { } + + Animation(std::string name, const double duration, const double tps) noexcept + : name(std::move(name)) + , duration(duration) + , tps(tps) { + } }; } diff --git a/ItemsDto/Barriers.cpp b/ItemsDto/Barriers.cpp deleted file mode 100644 index 56993a0f..00000000 --- a/ItemsDto/Barriers.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include "Barriers.h" - -namespace ItemsDto { - Barriers::~Barriers() { - Barriers::release(); - } - - void Barriers::release() { - if (!walls.empty()) { - for (auto Iter = walls.begin(); Iter < walls.end(); Iter++) { - delete (*Iter); - } - - walls.clear(); - } - } - - void Barriers::init() { - walls.push_back(wallFactory({240.0, 336.0, 15.0})); - walls.push_back(wallFactory({272.0, 336.0, 15.0})); - walls.push_back(wallFactory({304.0, 336.0, 15.0})); - walls.push_back(wallFactory({336.0, 336.0, 15.0})); - walls.push_back(wallFactory({368.0, 336.0, 15.0})); - walls.push_back(wallFactory({400.0, 336.0, 15.0})); - walls.push_back(wallFactory({432.0, 336.0, 15.0})); - walls.push_back(wallFactory({464.0, 336.0, 15.0})); - walls.push_back(wallFactory({496.0, 336.0, 15.0})); - walls.push_back(wallFactory({528.0, 336.0, 15.0})); - walls.push_back(wallFactory({560.0, 336.0, 15.0})); - walls.push_back(wallFactory({592.0, 336.0, 15.0})); - walls.push_back(wallFactory({624.0, 336.0, 15.0})); - - walls.push_back(wallFactory({656.0, 336.0, 15.0})); - walls.push_back(wallFactory({752.0, 336.0, 15.0})); - walls.push_back(wallFactory({784.0, 336.0, 15.0})); - walls.push_back(wallFactory({816.0, 336.0, 15.0})); - walls.push_back(wallFactory({848.0, 336.0, 15.0})); - walls.push_back(wallFactory({880.0, 336.0, 15.0})); - walls.push_back(wallFactory({912.0, 336.0, 15.0})); - walls.push_back(wallFactory({944.0, 336.0, 15.0})); - walls.push_back(wallFactory({976.0, 336.0, 15.0})); - walls.push_back(wallFactory({1008.0, 336.0, 15.0})); - walls.push_back(wallFactory({1040.0, 336.0, 15.0})); - } - - Cube *Barriers::wallFactory(const glm::vec3 &position) { - auto wall = new Cube(); - wall->setPosition(position); - wall->setVirtualX((((int)(position.x - (-23)) / 2) * 32)); - wall->setVirtualY((((int)(position.y - (-23)) / 2) * 32)); - wall->setVisible(true); - - return wall; - } - - const vector &Barriers::getItems() const { - return walls; - } - - int Barriers::getMaxX() const { - return 0; - } - - int Barriers::getMaxY() const { - return 0; - } - - int Barriers::getMinX() const { - return 0; - } - - int Barriers::getMinY() const { - return 0; - } - - void Barriers::createWall(int x, int y) { - walls.push_back(wallFactory({(float)x, (float)y, -23.0})); - } - - void Barriers::reset() { - release(); - } - -} // ItemsDto \ No newline at end of file diff --git a/ItemsDto/Barriers.h b/ItemsDto/Barriers.h deleted file mode 100644 index da1727da..00000000 --- a/ItemsDto/Barriers.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef SNAKE3_BARRIERS_H -#define SNAKE3_BARRIERS_H - -#include "Cube.h" -#include "BaseContainer.h" -#include "ObjWall.h" -#include - -using namespace std; - -namespace ItemsDto { - - class Barriers : public BaseContainer { - public: - ~Barriers(); - void init() override; - [[nodiscard]] const vector &getItems() const override; - void createWall(int x, int y); - void reset(); - [[nodiscard]] int getMaxX() const override; - [[nodiscard]] int getMaxY() const override; - [[nodiscard]] int getMinX() const override; - [[nodiscard]] int getMinY() const override; - - protected: - static Cube* wallFactory(const glm::vec3 &position); - void release() override; - vector walls; - }; - -} // ItemsDto - -#endif //SNAKE3_BARRIERS_H diff --git a/ItemsDto/BaseContainer.cpp b/ItemsDto/BaseContainer.cpp deleted file mode 100644 index 259de845..00000000 --- a/ItemsDto/BaseContainer.cpp +++ /dev/null @@ -1,4 +0,0 @@ -#include "BaseContainer.h" - -namespace ItemsDto { -} // ItemsDto \ No newline at end of file diff --git a/ItemsDto/BaseContainer.h b/ItemsDto/BaseContainer.h deleted file mode 100644 index 8c10d7d3..00000000 --- a/ItemsDto/BaseContainer.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef SNAKE3_BASECONTAINER_H -#define SNAKE3_BASECONTAINER_H - -#include "BaseItem.h" -#include "BaseContainerInterface.h" -#include - -using namespace std; - -namespace ItemsDto { - template - class BaseContainer : public BaseContainerInterface { - public: - virtual void init() = 0; - [[nodiscard]] virtual const vector &getItems() const = 0; - [[nodiscard]] virtual int getMaxX() const = 0; - [[nodiscard]] virtual int getMaxY() const = 0; - [[nodiscard]] virtual int getMinX() const = 0; - [[nodiscard]] virtual int getMinY() const = 0; - protected: - virtual void release() = 0; - int maxX{}; - int maxY{}; - int minX{}; - int minY{}; - }; - -} // ItemsDto - -#endif //SNAKE3_BASECONTAINER_H diff --git a/ItemsDto/BaseContainerInterface.cpp b/ItemsDto/BaseContainerInterface.cpp deleted file mode 100644 index 30cf1c67..00000000 --- a/ItemsDto/BaseContainerInterface.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "BaseContainerInterface.h" - -namespace ItemsDto { - BaseContainerInterface::BaseContainerInterface() = default; -} // ItemsDto \ No newline at end of file diff --git a/ItemsDto/BaseContainerInterface.h b/ItemsDto/BaseContainerInterface.h deleted file mode 100644 index f6f52fe8..00000000 --- a/ItemsDto/BaseContainerInterface.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef SNAKE3_BASECONTAINERINTERFACE_H -#define SNAKE3_BASECONTAINERINTERFACE_H - -namespace ItemsDto { - - class BaseContainerInterface { - protected: - BaseContainerInterface(); - }; - -} // ItemsDto - -#endif //SNAKE3_BASECONTAINERINTERFACE_H diff --git a/ItemsDto/BaseItem.cpp b/ItemsDto/BaseItem.cpp index 2af39592..a82708df 100644 --- a/ItemsDto/BaseItem.cpp +++ b/ItemsDto/BaseItem.cpp @@ -1,25 +1,15 @@ #include "BaseItem.h" +#include namespace ItemsDto { - BaseItem::BaseItem(): width(1), height(1), visible(true), alpha(1.0f), startFadeOut(false) { - rotate[0].a = rotate[0].x = rotate[0].y = rotate[0].z = 0.0f; - rotate[1].a = rotate[1].x = rotate[1].y = rotate[1].z = 0.0f; - rotate[2].a = rotate[2].x = rotate[2].y = rotate[2].z = 0.0f; - } - - const glm::vec3 &BaseItem::getPosition() const { - return position; - } - - void BaseItem::setPosition(const glm::vec3 &position) { - BaseItem::position = position; + BaseItem::BaseItem() : visible(true), width(1), height(1), startFadeOut(false), alpha(1.0f) { } bool BaseItem::isVisible() const { return visible; } - void BaseItem::setVisible(bool visible) { + void BaseItem::setVisible(const bool visible) { BaseItem::visible = visible; alpha = 1.0f; startFadeOut = false; @@ -30,7 +20,7 @@ namespace ItemsDto { return width; } - void BaseItem::setWidth(GLfloat width) { + void BaseItem::setWidth(const GLfloat width) { BaseItem::width = width; } @@ -38,48 +28,14 @@ namespace ItemsDto { return height; } - void BaseItem::setHeight(GLfloat height) { + void BaseItem::setHeight(const GLfloat height) { BaseItem::height = height; } - const glm::vec3 &BaseItem::getZoom() const { - return zoom; - } - - void BaseItem::setZoom(const glm::vec3 &zoom) { - BaseItem::zoom = zoom; - } - - const glm::vec4 *BaseItem::getRotate() const { - return rotate; - } - - void BaseItem::setRotate(const glm::vec4 &rotateX, const glm::vec4 &rotateY, const glm::vec4 &rotateZ) { - rotate[0] = rotateX; - rotate[1] = rotateY; - rotate[2] = rotateZ; - } - void BaseItem::toggleVisible() { this->visible = !this->visible; } - int BaseItem::getVirtualX() const { - return virtual_X; - } - - void BaseItem::setVirtualX(int virtualX) { - virtual_X = virtualX; - } - - int BaseItem::getVirtualY() const { - return virtual_Y; - } - - void BaseItem::setVirtualY(int virtualY) { - virtual_Y = virtualY; - } - void BaseItem::fadeOut() { if (alpha == 1.0) { startFadeOut = true; @@ -100,19 +56,20 @@ namespace ItemsDto { return startFadeOut || startFadeIn; } - void BaseItem::setAlpha(float alpha) { + void BaseItem::setAlpha(const float alpha) { BaseItem::alpha = alpha; } void BaseItem::fadeStep(const float FADE_STEP) { - double now = glfwGetTime(); + const double now = glfwGetTime(); if (now > lastTime + 0.0001) { lastTime = now; if (startFadeOut && alpha > 0) { alpha -= FADE_STEP; return; - } else if (startFadeIn && alpha < 1) { + } + if (startFadeIn && alpha < 1) { alpha += FADE_STEP; return; @@ -123,5 +80,4 @@ namespace ItemsDto { startFadeIn = false; } } - -} // ItemsDto \ No newline at end of file +} // ItemsDto diff --git a/ItemsDto/BaseItem.h b/ItemsDto/BaseItem.h index 734dfe51..b24d9d73 100644 --- a/ItemsDto/BaseItem.h +++ b/ItemsDto/BaseItem.h @@ -3,17 +3,19 @@ #include "../stdafx.h" #include -#include +#include "Transform.h" +#include "Vector3i.h" +using namespace Node3D; using namespace std; namespace ItemsDto { - class BaseItem { + class BaseItem : public Transform, public Vector3i { public: BaseItem(); - void setPosition(const glm::vec3 &position); - [[nodiscard]] const glm::vec3 &getPosition() const; + ~BaseItem() override = default; + [[nodiscard]] bool isVisible() const; void setVisible(bool visible); void toggleVisible(); @@ -21,14 +23,6 @@ namespace ItemsDto { void setWidth(GLfloat width); [[nodiscard]] GLfloat getHeight() const; void setHeight(GLfloat height); - [[nodiscard]] const glm::vec3 &getZoom() const; - void setZoom(const glm::vec3 &zoom); - [[nodiscard]] const glm::vec4 *getRotate() const; - void setRotate(const glm::vec4 &rotateX, const glm::vec4 &rotateY, const glm::vec4 &rotateZ); - [[nodiscard]] int getVirtualX() const; - void setVirtualX(int virtualX); - [[nodiscard]] int getVirtualY() const; - void setVirtualY(int virtualY); void fadeOut(); void fadeIn(); [[nodiscard]] float getAlpha() const; @@ -37,14 +31,9 @@ namespace ItemsDto { [[nodiscard]] bool isStartFade() const; protected: - glm::vec3 position{}; - glm::vec3 zoom{}; - glm::vec4 rotate[3]{}; bool visible{}; GLfloat width; GLfloat height; - int virtual_X{}; - int virtual_Y{}; double lastTime{}; bool startFadeOut; bool startFadeIn{}; diff --git a/ItemsDto/Cube.cpp b/ItemsDto/Cube.cpp deleted file mode 100644 index be9cda66..00000000 --- a/ItemsDto/Cube.cpp +++ /dev/null @@ -1,8 +0,0 @@ -// -// Created by viper on 24.7.22. -// - -#include "Cube.h" - -namespace ItemsDto { -} // ItemsDto \ No newline at end of file diff --git a/ItemsDto/Cube.h b/ItemsDto/Cube.h deleted file mode 100644 index 0e6fa8e7..00000000 --- a/ItemsDto/Cube.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef SNAKE3_CUBE_H -#define SNAKE3_CUBE_H - -#include "BaseItem.h" - -namespace ItemsDto { - - class Cube : public BaseItem { - }; - -} // ItemsDto - -#endif //SNAKE3_CUBE_H diff --git a/ItemsDto/Eat.cpp b/ItemsDto/Eat.cpp deleted file mode 100644 index 44df394f..00000000 --- a/ItemsDto/Eat.cpp +++ /dev/null @@ -1,4 +0,0 @@ -#include "Eat.h" - -namespace ItemsDto { -} // ItemsDto \ No newline at end of file diff --git a/ItemsDto/Eat.h b/ItemsDto/Eat.h deleted file mode 100644 index 75f79b52..00000000 --- a/ItemsDto/Eat.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef SNAKE3_EAT_H -#define SNAKE3_EAT_H - -#include "../stdafx.h" -#include "Cube.h" - -namespace ItemsDto { - - class Eat : public Cube { - }; - -} // ItemsDto - -#endif //SNAKE3_EAT_H diff --git a/ItemsDto/GameField.cpp b/ItemsDto/GameField.cpp deleted file mode 100644 index a8e699fe..00000000 --- a/ItemsDto/GameField.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "GameField.h" - -namespace ItemsDto { - GameField::~GameField() { - Release(); - } - - void GameField::Release() { - for (auto Iter = tile.begin(); Iter < tile.end(); Iter++) { - delete (*Iter); - } - - tile.clear(); - } - - void GameField::Init() { - Cube* field; - - field = new Cube(); - field->setPosition({0.0f, 0.0f, 0.0f}); - field->setZoom({1, 1, 0.0}); - field->setVisible(true); - - tile.push_back(field); - } - - const vector &GameField::getTiles() const { - return tile; - } -} // ItemsDto \ No newline at end of file diff --git a/ItemsDto/GameField.h b/ItemsDto/GameField.h deleted file mode 100644 index 1ea3c5ab..00000000 --- a/ItemsDto/GameField.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef SNAKE3_GAMEFIELD_H -#define SNAKE3_GAMEFIELD_H - -#include -#include "BaseItem.h" -#include "Cube.h" - -using namespace std; - -namespace ItemsDto { - - class GameField : public BaseItem { - public: - virtual ~GameField(); - void Init(); - [[nodiscard]] const vector &getTiles() const; - protected: - void Release(); - vector tile; - }; - -} // ItemsDto - -#endif //SNAKE3_GAMEFIELD_H diff --git a/ItemsDto/ObjItem.cpp b/ItemsDto/ObjItem.cpp deleted file mode 100644 index 525f314d..00000000 --- a/ItemsDto/ObjItem.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include "ObjItem.h" - -namespace ItemsDto { - - ObjItem::ObjItem(const vector &vertices, const vector &uvs, const vector &normals) - : vertices(vertices), uvs(uvs), normals(normals) { - generateBuffer(); - } - - ObjItem::ObjItem() = default; - - const vector &ObjItem::getVertices() const { - return vertices; - } - - const vector &ObjItem::getUvs() const { - return uvs; - } - - const vector &ObjItem::getNormals() const { - return normals; - } - - void ObjItem::generateBuffer() { - Manager::VboIndexer::computeTangentBasis(vertices, uvs, normals, tangents, biTangents); - Manager::VboIndexer::indexVBO_TBN(vertices, uvs, normals, tangents, biTangents, - indices, indexed_vertices, - indexed_uvs, indexed_normals, indexed_tangents, indexed_biTangents); - - vector vertices; - int index = 0; - for (auto vert: indexed_vertices) { - auto uvs = indexed_uvs.begin() + index; - auto normals = indexed_normals.begin() + index; - auto tangents = indexed_tangents.begin() + index; - auto biTangents = indexed_biTangents.begin() + index; - Vertex vertex{}; - vertex.position = vert; - vertex.normal = (*normals); - vertex.color = {1.0f, 1.0f, 1.0f}; - vertex.texUV = (*uvs); - vertex.tangents = (*tangents); - vertex.biTangents = (*biTangents); - - vertices.push_back(vertex); - index++; - } - - mesh = new Mesh(vertices, indices); - } - - const vector &ObjItem::getIndices() const { - return indices; - } - - const vector &ObjItem::getIndexedVertices() const { - return indexed_vertices; - } - - const vector &ObjItem::getIndexedUvs() const { - return indexed_uvs; - } - - const vector &ObjItem::getIndexedNormals() const { - return indexed_normals; - } - - const vector &ObjItem::getIndexedTangents() const { - return indexed_tangents; - } - - const vector &ObjItem::getIndexedBiTangents() const { - return indexed_biTangents; - } - - Mesh *ObjItem::getMesh() const { - return mesh; - } - - ObjItem::~ObjItem() { - delete mesh; - } - -} // ItemsDto \ No newline at end of file diff --git a/ItemsDto/ObjItem.h b/ItemsDto/ObjItem.h deleted file mode 100644 index ee5756c8..00000000 --- a/ItemsDto/ObjItem.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef SNAKE3_OBJITEM_H -#define SNAKE3_OBJITEM_H - -#include "BaseItem.h" -#include "../Manager/VboIndexer.h" -#include "../Renderer/Opengl/Model/Utils/Mesh.h" -#include - -using namespace ModelUtils; - -namespace ItemsDto { - - class ObjItem { - public: - ObjItem(const vector &vertices, const vector &uvs, const vector &normals); - ObjItem(); - - virtual ~ObjItem(); - - [[nodiscard]] const vector &getVertices() const; - [[nodiscard]] const vector &getUvs() const; - [[nodiscard]] const vector &getNormals() const; - [[nodiscard]] const vector &getIndexedVertices() const; - [[nodiscard]] const vector &getIndexedUvs() const; - [[nodiscard]] const vector &getIndexedNormals() const; - [[nodiscard]] const vector &getIndexedTangents() const; - [[nodiscard]] const vector &getIndexedBiTangents() const; - [[nodiscard]] const vector &getIndices() const; - [[nodiscard]] Mesh *getMesh() const; - - protected: - void generateBuffer(); - std::vector indices; - vector vertices; - vector uvs; - vector normals; - vector tangents; - vector biTangents; - std::vector indexed_vertices; - std::vector indexed_uvs; - std::vector indexed_normals; - std::vector indexed_tangents; - std::vector indexed_biTangents; - Mesh* mesh{}; - }; - -} // ItemsDto - -#endif //SNAKE3_OBJITEM_H diff --git a/ItemsDto/ObjWall.cpp b/ItemsDto/ObjWall.cpp deleted file mode 100644 index b5c22f72..00000000 --- a/ItemsDto/ObjWall.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "ObjWall.h" - -namespace ItemsDto { - ObjWall::~ObjWall() { - ObjWall::release(); - } - - void ObjWall::release() { - if (!walls.empty()) { - for (auto Iter = walls.begin(); Iter < walls.end(); Iter++) { - delete (*Iter); - } - - walls.clear(); - } - } - - const vector &ObjWall::getItems() const { - return walls; - } - - void ObjWall::init() { - glm::vec3 wallPos = {-16, -25, -23.0}; - for (int round = 0; round < 2; round++) { - for (int x = -25; x <= 73; x += 2) { - auto *wall = new Cube(); - wallPos.x = (float) x; - wall->setPosition(wallPos); - wall->setVisible(true); - - walls.push_back(wall); - } - - maxX = 1520; - minX = 16; - wallPos.x = -25; - wallPos.y = 73; - } - - wallPos.x = -25; - wallPos.y = -25; - minY = 16; - maxY = (784 * 2) - 16 - 32; - - for (int round = 0; round < 2; round++) { - for (int y = -25; y <= 73; y += 2) { - auto wall = new Cube(); - wallPos.y = (float) y; - wall->setPosition(wallPos); - wall->setVisible(true); - - this->walls.push_back(wall); - } - - wallPos.x = 73; - wallPos.y = -25; - } - } - - int ObjWall::getMaxX() const { - return maxX; - } - - int ObjWall::getMaxY() const { - return maxY; - } - - int ObjWall::getMinX() const { - return minX; - } - - int ObjWall::getMinY() const { - return minY; - } -} // ItemsDto \ No newline at end of file diff --git a/ItemsDto/ObjWall.h b/ItemsDto/ObjWall.h deleted file mode 100644 index 0c7c5b46..00000000 --- a/ItemsDto/ObjWall.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef SNAKE3_OBJWALL_H -#define SNAKE3_OBJWALL_H - -#include "BaseContainer.h" -#include "Cube.h" - -namespace ItemsDto { - - class ObjWall : public BaseContainer { - public: - ~ObjWall(); - void init() override; - [[nodiscard]] const vector &getItems() const override; - [[nodiscard]] int getMaxX() const override; - [[nodiscard]] int getMaxY() const override; - [[nodiscard]] int getMinX() const override; - [[nodiscard]] int getMinY() const override; - protected: - void release() override; - vector walls; - }; - -} // ItemsDto - -#endif //SNAKE3_OBJWALL_H diff --git a/ItemsDto/Radar.cpp b/ItemsDto/Radar.cpp deleted file mode 100644 index a1fb5c06..00000000 --- a/ItemsDto/Radar.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "Radar.h" - -namespace ItemsDto { - - const vector &Radar::getItems() const { - return items; - } - - void Radar::updatePositions() { - for(auto Iter = items.end() - 1; Iter >= items.begin(); Iter--) { - glm::vec3 pos; - pos.x = 125-88 + 176; - pos.x = 125-88 + ((this->getWidth() / 1520) * (float)(*Iter).item->getVirtualX()); - pos.y = 160-88 + 176 - ((this->getHeight() / 1520) * (float)(*Iter).item->getVirtualY()); - pos.z = 0; - - (*Iter).radarPresent->setPosition(pos); - } - } - - void Radar::addItem(const BaseItem *item, glm::vec3 color) { - sRADAR_item radarItem{}; - radarItem.item = item; - radarItem.radarPresent = new BaseItem(); - radarItem.radarPresent->setVisible(true); - radarItem.radarPresent->setZoom({2,2,1}); - radarItem.color = color; - - items.push_back(radarItem); - } - - void Radar::reset() { - items.clear(); - } - -} // ItemsDto \ No newline at end of file diff --git a/ItemsDto/Radar.h b/ItemsDto/Radar.h deleted file mode 100644 index a252f636..00000000 --- a/ItemsDto/Radar.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef SNAKE3_RADAR_H -#define SNAKE3_RADAR_H - -#include "BaseItem.h" -#include "../Manager/TextureManager.h" -#include - -using namespace std; -using namespace Manager; - -namespace ItemsDto { - - class Radar : public BaseItem { - struct sRADAR_item { - const BaseItem* item; - BaseItem* radarPresent; - glm::vec3 color; - }; - public: - void updatePositions(); - void addItem(const BaseItem* item, glm::vec3 color); - void reset(); - [[nodiscard]] const vector &getItems() const; - - protected: - vector items; - }; - -} // ItemsDto - -#endif //SNAKE3_RADAR_H diff --git a/ItemsDto/Snake.cpp b/ItemsDto/Snake.cpp deleted file mode 100644 index 01dc5c48..00000000 --- a/ItemsDto/Snake.cpp +++ /dev/null @@ -1,143 +0,0 @@ -#include "Snake.h" - -namespace ItemsDto { - Snake::~Snake() { - Snake::release(); - } - - void Snake::release() { - for (auto Iter = tiles.begin(); Iter < tiles.end(); Iter++) { - delete (*Iter)->tile; - delete (*Iter); - } - - tiles.clear(); - } - - void Snake::init() { - auto *snakeTile = new sSNAKE_TILE; - snakeTile->tile = new Cube(); - snakeTile->tile->setVisible(true); - snakeTile->alpha = 1.0; - - tiles.push_back(snakeTile); - reset(); - } - - sSNAKE_TILE* Snake::addTile(eDIRECTION aDirection) { - if (!tiles.empty()) { - auto *snakeTile = new sSNAKE_TILE; - snakeTile->tile = new Cube(); - glm::vec3 pos = {-19, 67, -23}; - - auto PrevIter = tiles.end()-1; - - if ((*(tiles.begin()))->direction != STOP) { - pos = (*PrevIter)->tile->getPosition(); - } else { - switch (aDirection) { - case LEFT: - if ((*PrevIter)->tile->getPosition().x - 2 >= -25) { - pos.x = (*PrevIter)->tile->getPosition().x - 2; - pos.y = (*PrevIter)->tile->getPosition().y; - } else { - delete snakeTile; - return nullptr; - } - break; - case RIGHT: - if ((*PrevIter)->tile->getPosition().x + 2 <= 752) { - pos.x = (*PrevIter)->tile->getPosition().x + 2; - pos.y = (*PrevIter)->tile->getPosition().y; - } else { - delete snakeTile; - return nullptr; - } - break; - case UP: - if ((*PrevIter)->tile->getPosition().y - 2 >= -25) { - pos.x = (*PrevIter)->tile->getPosition().x; - pos.y = (*PrevIter)->tile->getPosition().y - 2; - } else { - delete snakeTile; - return nullptr; - } - break; - case DOWN: - if ((*PrevIter)->tile->getPosition().y + 2 <= 752) { - pos.x = (*PrevIter)->tile->getPosition().x; - pos.y = (*PrevIter)->tile->getPosition().y + 2; - } else { - delete snakeTile; - return nullptr; - } - break; - default: - break; - } - } - - snakeTile->tile->setVirtualX((((int)(pos.x - (-23)) / 2) * 32) + 16); - snakeTile->tile->setVirtualY((((int)(pos.y - (-23)) / 2) * 32) + 16); - snakeTile->tile->setPosition(pos); - snakeTile->tile->setVisible(true); - snakeTile->prevPauseDirection = NONE; - tiles.push_back(snakeTile); - - return snakeTile; - } - - return nullptr; - } - - const vector &Snake::getItems() const { - return tiles; - } - - Cube *Snake::getHeadTile() { - if (!tiles.empty()) { - return (*tiles.begin())->tile; - } - - return nullptr; - } - - int Snake::getMaxX() const { - return 0; - } - - int Snake::getMaxY() const { - return 0; - } - - int Snake::getMinX() const { - return 0; - } - - int Snake::getMinY() const { - return 0; - } - - void Snake::reset() { - for (auto Iter = tiles.end() - 1; Iter != tiles.begin(); Iter--) { - delete (*Iter)->tile; - delete (*Iter); - - tiles.erase(Iter); - } - - sSNAKE_TILE* snakeTile = (*tiles.begin()); - - snakeTile->tile->setPosition({23, -3, -23}); // start pozice -// snakeTile->tile->setPosition({45, -3, -23}); - snakeTile->tile->setVirtualX((((int)(23 - (-23)) / 2) * 32) + 16); - snakeTile->tile->setVirtualY((((int)(-3 - (-23)) / 2) * 32) + 16); - snakeTile->direction = STOP; - snakeTile->prevPauseDirection = NONE; - - addTile(LEFT); - addTile(LEFT); - addTile(LEFT); - } - -} // ItemsDto \ No newline at end of file diff --git a/ItemsDto/Snake.h b/ItemsDto/Snake.h deleted file mode 100644 index 03a80dad..00000000 --- a/ItemsDto/Snake.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef SNAKE3_SNAKE_H -#define SNAKE3_SNAKE_H - -#define UNIT_MOVE 0.125 -#define VIRTUAL_MOVE 2 // kvuli nepresnosti float cislum pocitame virtualne v integer formatu -#define CUBE_SIZE 32 - -#include -#include "BaseContainer.h" -#include "ObjItem.h" -#include "Cube.h" - -namespace ItemsDto { - - enum eDIRECTION { - NONE = -1, - STOP = 0, - LEFT = 1, - RIGHT = 2, - UP = 3, - DOWN = 4, - CRASH = 100, - PAUSE = 200, - }; - - struct sSNAKE_TILE { - Cube* tile; - eDIRECTION direction = NONE; - eDIRECTION prevPauseDirection; // smer pred pauzou (je treba si zapamatovat smer nez doslo ke stisku pause) - float alpha; - vector> moveCallbacks; - }; - - class Snake: public BaseContainer{ - public: - virtual ~Snake(); - void init() override; - sSNAKE_TILE* addTile(eDIRECTION aDirection); - void reset(); - Cube* getHeadTile(); - [[nodiscard]] const vector &getItems() const override; - [[nodiscard]] int getMaxX() const override; - [[nodiscard]] int getMaxY() const override; - [[nodiscard]] int getMinX() const override; - [[nodiscard]] int getMinY() const override; - - protected: - void release() override; - vector tiles; - }; - -} // ItemsDto - -#endif //SNAKE3_SNAKE_H diff --git a/ItemsDto/Text.cpp b/ItemsDto/Text.cpp deleted file mode 100644 index 2956bd1a..00000000 --- a/ItemsDto/Text.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "Text.h" - -#include - -namespace ItemsDto { - Text::Text(string text) : text(std::move(text)) {} - - void Text::setText(const string &text) { - Text::text = text; - } - - void Text::setColor(const glm::vec3 &color) { - Text::color = color; - } - - const string &Text::getText() const { - return text; - } - - const glm::vec3 &Text::getColor() const { - return color; - } - - unsigned int Text::getFontSize() const { - return fontSize; - } - - void Text::setFontSize(unsigned int fontSize) { - Text::fontSize = fontSize; - } - - const string &Text::getFontPath() const { - return fontPath; - } - - void Text::setFontPath(const string &fontPath) { - Text::fontPath = fontPath; - } -} \ No newline at end of file diff --git a/ItemsDto/Text.h b/ItemsDto/Text.h deleted file mode 100644 index cda49c6d..00000000 --- a/ItemsDto/Text.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef SNAKE3_TEXT_H -#define SNAKE3_TEXT_H - -#include "../stdafx.h" -#include "BaseItem.h" -#include - -using namespace std; - -namespace ItemsDto { - - class Text : public BaseItem { - public: - explicit Text(string text); - void setText(const string &text); - void setColor(const glm::vec3 &color); - [[nodiscard]] const string &getText() const; - [[nodiscard]] const glm::vec3 &getColor() const; - [[nodiscard]] unsigned int getFontSize() const; - void setFontSize(unsigned int fontSize); - [[nodiscard]] const string &getFontPath() const; - void setFontPath(const string &fontPath); - - protected: - string text; - glm::vec3 color{}; - string fontPath; - unsigned int fontSize{}; - }; - -} // ItemsDto - -#endif //SNAKE3_TEXT_H diff --git a/ItemsDto/Transform.cpp b/ItemsDto/Transform.cpp new file mode 100644 index 00000000..00541341 --- /dev/null +++ b/ItemsDto/Transform.cpp @@ -0,0 +1,69 @@ +#include "Transform.h" + +namespace Node3D { + + const glm::vec3 &Transform::getPosition() const { + return position; + } + + glm::mat4 Transform::getModelMatrix() const { + auto model = glm::mat4(1.0f); + + model = glm::scale(model, scale); + model = glm::translate(model, position); + + model = glm::rotate(model, glm::radians(rotationX), {1.0, 0.0, 0.0}); + model = glm::rotate(model, glm::radians(rotationY), {0.0, 1.0, 0.0}); + model = glm::rotate(model, glm::radians(rotationZ), {0.0, 0.0, 1.0}); + + return model; + } + + void Transform::setPosition(const glm::vec3 &position) { + Transform::position = position; + } + + void Transform::setPosition(const shared_ptr &object) { + position = object->getPosition(); + } + + const glm::vec3 &Transform::getScale() const { + return scale; + } + + void Transform::setScale(const glm::vec3 &scale) { + Transform::scale = scale; + } + + void Transform::setRotationX(const float rotation_x) { + rotationX = rotation_x; + } + + void Transform::setRotationY(const float rotation_y) { + rotationY = rotation_y; + } + + void Transform::setRotationZ(const float rotation_z) { + rotationZ = rotation_z; + } + + float Transform::getRotationX() const { + return rotationX; + } + + float Transform::getRotationY() const { + return rotationY; + } + + float Transform::getRotationZ() const { + return rotationZ; + } + + void Transform::setTransform(const shared_ptr &transform) { + this->position = transform->getPosition(); + this->scale = transform->getScale(); + this->rotationX = transform->getRotationX(); + this->rotationY = transform->getRotationY(); + this->rotationZ = transform->getRotationZ(); + } +} // Node3D \ No newline at end of file diff --git a/ItemsDto/Transform.h b/ItemsDto/Transform.h new file mode 100644 index 00000000..e8568edd --- /dev/null +++ b/ItemsDto/Transform.h @@ -0,0 +1,54 @@ +#ifndef SNAKE3_TRANSFORM_H +#define SNAKE3_TRANSFORM_H + +#include +#include +#include +#include + +using namespace std; + +namespace Node3D { + class Transform { + public: + Transform() = default; + + virtual ~Transform() = default; + + void setPosition(const glm::vec3 &position); + + void setPosition(const shared_ptr &object); + + [[nodiscard]] const glm::vec3 &getPosition() const; + + [[nodiscard]] virtual glm::mat4 getModelMatrix() const; + + [[nodiscard]] const glm::vec3 &getScale() const; + + void setScale(const glm::vec3 &scale); + + void setRotationX(float rotation_x); + + void setRotationY(float rotation_y); + + void setRotationZ(float rotation_z); + + [[nodiscard]] float getRotationX() const; + + [[nodiscard]] float getRotationY() const; + + [[nodiscard]] float getRotationZ() const; + + void setTransform(const shared_ptr &transform); + + protected: + glm::mat4 worldMatrix{}; + glm::vec3 position{}; + glm::vec3 scale{1.0f, 1.0f, 1.0f}; + float rotationX = 0; + float rotationY = 0; + float rotationZ = 0; + }; +} // Node3D + +#endif //SNAKE3_TRANSFORM_H diff --git a/ItemsDto/Vector3i.cpp b/ItemsDto/Vector3i.cpp new file mode 100644 index 00000000..aa2438e8 --- /dev/null +++ b/ItemsDto/Vector3i.cpp @@ -0,0 +1,8 @@ +#include "Vector3i.h" + +namespace Node3D { + Vector3i::Vector3i(const int x, const int y, const int z): x(x), y(y), z(z) { + } + + Vector3i::Vector3i(): x(0), y(0), z(0) {} +} // Node3D \ No newline at end of file diff --git a/ItemsDto/Vector3i.h b/ItemsDto/Vector3i.h new file mode 100644 index 00000000..279c603b --- /dev/null +++ b/ItemsDto/Vector3i.h @@ -0,0 +1,15 @@ +#ifndef SNAKE3_VECTOR3I_H +#define SNAKE3_VECTOR3I_H + +namespace Node3D { + class Vector3i { + public: + Vector3i(int x, int y, int z); + Vector3i(); + int x; + int y; + int z; + }; +} // Node3D + +#endif //SNAKE3_VECTOR3I_H \ No newline at end of file diff --git a/ItemsDto/Visibility.cpp b/ItemsDto/Visibility.cpp new file mode 100644 index 00000000..c7504c43 --- /dev/null +++ b/ItemsDto/Visibility.cpp @@ -0,0 +1,11 @@ +#include "Visibility.h" + +namespace Node3D { + bool Visibility::isVisible() const { + return visible; + } + + void Visibility::setVisible(const bool visible) { + this->visible = visible; + } +} // Node3D \ No newline at end of file diff --git a/ItemsDto/Visibility.h b/ItemsDto/Visibility.h new file mode 100644 index 00000000..6d52b51c --- /dev/null +++ b/ItemsDto/Visibility.h @@ -0,0 +1,16 @@ +#ifndef SNAKE3_VISIBILITY_H +#define SNAKE3_VISIBILITY_H + +namespace Node3D { + class Visibility { + public: + [[nodiscard]] bool isVisible() const; + + void setVisible(bool visible); + + protected: + bool visible = true; + }; +} // Node3D + +#endif //SNAKE3_VISIBILITY_H diff --git a/LICENSE b/LICENSE index 86a11f05..59711677 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 - 2022 Martin Chudoba +Copyright (c) 2020 - 2025 Martin Chudoba Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Lights/DirectionalLight.cpp b/Lights/DirectionalLight.cpp new file mode 100644 index 00000000..c0b63ee0 --- /dev/null +++ b/Lights/DirectionalLight.cpp @@ -0,0 +1,49 @@ +#include "DirectionalLight.h" + +namespace Lights { + glm::vec3 DirectionalLight::getDirection() const { + return direction; + } + + void DirectionalLight::setDirection(const glm::vec3 &direction) { + this->direction = direction; + } + + glm::vec3 DirectionalLight::getAmbient() const { + return ambient; + } + + void DirectionalLight::setAmbient(const glm::vec3 &ambient) { + this->ambient = ambient; + } + + glm::vec3 DirectionalLight::getDiffuse() const { + return diffuse; + } + + void DirectionalLight::setDiffuse(const glm::vec3 &diffuse) { + this->diffuse = diffuse; + } + + glm::vec3 DirectionalLight::getSpecular() const { + return specular; + } + + void DirectionalLight::setSpecular(const glm::vec3 &specular) { + this->specular = specular; + } + + void DirectionalLight::bind(const ShaderManager *shader) const { + // directional light + shader->use(); + shader->setVec3("lightPos", position); + shader->setVec3("dirLight.direction", direction); + shader->setVec3("dirLight.ambient", ambient); + shader->setVec3("dirLight.diffuse", diffuse); + shader->setVec3("dirLight.specular", specular); + shader->setFloat("material.shininess", shininess); + shader->setInt("material.ambient", 0); + shader->setInt("material.diffuse", 1); + shader->setInt("material.specular", 2); + } +} // Lights diff --git a/Lights/DirectionalLight.h b/Lights/DirectionalLight.h new file mode 100644 index 00000000..ed457cf3 --- /dev/null +++ b/Lights/DirectionalLight.h @@ -0,0 +1,40 @@ +#ifndef SNAKE3_DIRECTIONALLIGHT_H +#define SNAKE3_DIRECTIONALLIGHT_H + +#include +#include "../ItemsDto/Transform.h" +#include "../Manager/ShaderManager.h" + +using namespace Node3D; +using namespace Manager; + +namespace Lights { + class DirectionalLight : public Transform { + glm::vec3 direction = {}; + glm::vec3 ambient = {}; + glm::vec3 diffuse = {}; + glm::vec3 specular = {}; + float shininess = 32.0f; + + public: + [[nodiscard]] glm::vec3 getDirection() const; + + void setDirection(const glm::vec3 &direction); + + [[nodiscard]] glm::vec3 getAmbient() const; + + void setAmbient(const glm::vec3 &ambient); + + [[nodiscard]] glm::vec3 getDiffuse() const; + + void setDiffuse(const glm::vec3 &diffuse); + + [[nodiscard]] glm::vec3 getSpecular() const; + + void setSpecular(const glm::vec3 &specular); + + void bind(const ShaderManager *shader) const; + }; +} // Lights + +#endif //SNAKE3_DIRECTIONALLIGHT_H diff --git a/Lights/PointLight.cpp b/Lights/PointLight.cpp new file mode 100644 index 00000000..8a95adbc --- /dev/null +++ b/Lights/PointLight.cpp @@ -0,0 +1,63 @@ +#include "PointLight.h" + +namespace Lights { + glm::vec3 PointLight::getAmbient() const { + return ambient; + } + + void PointLight::setAmbient(const glm::vec3 &ambient) { + this->ambient = ambient; + } + + glm::vec3 PointLight::getDiffuse() const { + return diffuse; + } + + void PointLight::setDiffuse(const glm::vec3 &diffuse) { + this->diffuse = diffuse; + } + + glm::vec3 PointLight::getSpecular() const { + return specular; + } + + void PointLight::setSpecular(const glm::vec3 &specular) { + this->specular = specular; + } + + float PointLight::getConstant() const { + return constant; + } + + void PointLight::setConstant(const float constant) { + this->constant = constant; + } + + float PointLight::getLinear() const { + return linear; + } + + void PointLight::setLinear(const float linear) { + this->linear = linear; + } + + float PointLight::getQuadratic() const { + return quadratic; + } + + void PointLight::setQuadratic(const float quadratic) { + this->quadratic = quadratic; + } + + void PointLight::bind(const ShaderManager *shader, const int index) const { + shader->use(); + const string name = "pointLight[" + std::to_string(index) + "]"; + shader->setVec3(name + ".position", position); + shader->setVec3(name + ".ambient", ambient); + shader->setVec3(name + ".diffuse", diffuse); + shader->setVec3(name + ".specular", specular); + shader->setFloat(name + ".constant", constant); + shader->setFloat(name + ".linear", linear); + shader->setFloat(name + ".quadratic", quadratic); + } +} // Light \ No newline at end of file diff --git a/Lights/PointLight.h b/Lights/PointLight.h new file mode 100644 index 00000000..f2ccc458 --- /dev/null +++ b/Lights/PointLight.h @@ -0,0 +1,50 @@ +#ifndef SNAKE3_POINTLIGHT_H +#define SNAKE3_POINTLIGHT_H + +#include +#include "../ItemsDto/Transform.h" +#include "../ItemsDto/Visibility.h" +#include "../Manager/ShaderManager.h" + +using namespace Node3D; +using namespace Manager; + +namespace Lights { + class PointLight : public Transform, public Visibility { + glm::vec3 ambient = {}; + glm::vec3 diffuse = {}; + glm::vec3 specular = {}; + float constant = 1.0f; + float linear = 0.19f; + float quadratic = 0.032f; + + public: + [[nodiscard]] glm::vec3 getAmbient() const; + + void setAmbient(const glm::vec3 &ambient); + + [[nodiscard]] glm::vec3 getDiffuse() const; + + void setDiffuse(const glm::vec3 &diffuse); + + [[nodiscard]] glm::vec3 getSpecular() const; + + void setSpecular(const glm::vec3 &specular); + + [[nodiscard]] float getConstant() const; + + void setConstant(float constant); + + [[nodiscard]] float getLinear() const; + + void setLinear(float linear); + + [[nodiscard]] float getQuadratic() const; + + void setQuadratic(float quadratic); + + void bind(const ShaderManager *shader, int index) const; + }; +} // Light + +#endif //SNAKE3_POINTLIGHT_H diff --git a/Lights/SpotLight.cpp b/Lights/SpotLight.cpp new file mode 100644 index 00000000..ba4019b2 --- /dev/null +++ b/Lights/SpotLight.cpp @@ -0,0 +1,99 @@ +#include "SpotLight.h" + +namespace Lights { + glm::vec3 SpotLight::getDirection() const { + return direction; + } + + void SpotLight::setDirection(const glm::vec3 &direction) { + this->direction = direction; + } + + glm::vec3 SpotLight::getAmbient() const { + return ambient; + } + + void SpotLight::setAmbient(const glm::vec3 &ambient) { + this->ambient = ambient; + } + + glm::vec3 SpotLight::getDiffuse() const { + return diffuse; + } + + void SpotLight::setDiffuse(const glm::vec3 &diffuse) { + this->diffuse = diffuse; + } + + glm::vec3 SpotLight::getSpecular() const { + return specular; + } + + void SpotLight::setSpecular(const glm::vec3 &specular) { + this->specular = specular; + } + + float SpotLight::getConstant() const { + return constant; + } + + void SpotLight::setConstant(const float constant) { + this->constant = constant; + } + + float SpotLight::getLinear() const { + return linear; + } + + void SpotLight::setLinear(const float linear) { + this->linear = linear; + } + + float SpotLight::getQuadratic() const { + return quadratic; + } + + void SpotLight::setQuadratic(const float quadratic) { + this->quadratic = quadratic; + } + + float SpotLight::setCutOff() const { + return cutOff; + } + + void SpotLight::setCutOff(const float cut_off) { + cutOff = cut_off; + } + + float SpotLight::getOuterCutOff() const { + return outerCutOff; + } + + void SpotLight::setOuterCutOff(const float outer_cut_off) { + outerCutOff = outer_cut_off; + } + + bool SpotLight::isPulse() const { + return pulse; + } + + void SpotLight::setPulse(const bool pulse) { + this->pulse = pulse; + } + + void SpotLight::bind(const ShaderManager *shader, const int index = 0) const { + shader->use(); + const string name = "spotLight[" + std::to_string(index) + "]"; + shader->setVec3(name + ".position", position); + shader->setVec3(name + ".direction",glm::normalize(direction - position)); + shader->setVec3(name + ".ambient", ambient); + shader->setVec3(name + ".diffuse", diffuse); + shader->setVec3(name + ".specular", specular); + shader->setFloat(name + ".constant", constant); + shader->setFloat(name + ".linear", linear); + shader->setFloat(name + ".quadratic", quadratic); + shader->setBool(name + ".pulse", pulse); + shader->setFloat(name + ".cutOff", glm::cos(glm::radians(cutOff))); + shader->setFloat(name + ".outerCutOff", glm::cos(glm::radians(outerCutOff))); + } +} // Lights diff --git a/Lights/SpotLight.h b/Lights/SpotLight.h new file mode 100644 index 00000000..f6b0b29c --- /dev/null +++ b/Lights/SpotLight.h @@ -0,0 +1,69 @@ +#ifndef SNAKE3_SPOTLIGHT_H +#define SNAKE3_SPOTLIGHT_H + +#include "../ItemsDto/Transform.h" +#include "../ItemsDto/Visibility.h" +#include "../Manager/ShaderManager.h" + +using namespace Node3D; +using namespace Manager; + +namespace Lights { + class SpotLight : public Transform, public Visibility { + glm::vec3 direction = {}; + glm::vec3 ambient = {}; + glm::vec3 diffuse = {}; + glm::vec3 specular = {}; + float constant = 1.0f; + float linear = 0.19f; + float quadratic = 0.032f; + float cutOff = 0.0f; + float outerCutOff = 0.0f; + bool pulse = false; + + public: + [[nodiscard]] glm::vec3 getDirection() const; + + void setDirection(const glm::vec3 &direction); + + [[nodiscard]] glm::vec3 getAmbient() const; + + void setAmbient(const glm::vec3 &ambient); + + [[nodiscard]] glm::vec3 getDiffuse() const; + + void setDiffuse(const glm::vec3 &diffuse); + + [[nodiscard]] glm::vec3 getSpecular() const; + + void setSpecular(const glm::vec3 &specular); + + [[nodiscard]] float getConstant() const; + + void setConstant(float constant); + + [[nodiscard]] float getLinear() const; + + void setLinear(float linear); + + [[nodiscard]] float getQuadratic() const; + + void setQuadratic(float quadratic); + + [[nodiscard]] float setCutOff() const; + + void setCutOff(float cut_off); + + [[nodiscard]] float getOuterCutOff() const; + + void setOuterCutOff(float outer_cut_off); + + [[nodiscard]] bool isPulse() const; + + void setPulse(bool pulse); + + void bind(const ShaderManager *shader, int index) const; + }; +} // Lights + +#endif //SNAKE3_SPOTLIGHT_H diff --git a/Manager/Camera.cpp b/Manager/Camera.cpp index eebc8620..9d4000b0 100644 --- a/Manager/Camera.cpp +++ b/Manager/Camera.cpp @@ -1,22 +1,23 @@ #include "Camera.h" +#include + namespace Manager { Camera::Camera(glm::vec3 position, glm::vec3 up) : front(glm::vec3(0.0f, 0.0f, -1.0f)) { this->position = position; worldUp = up; - zoom = 20; + zoom = 28; updateCameraVectors(); } void Camera::updateCameraVectors() { - // calculate the new Front vector - glm::vec3 calculateFront; - calculateFront.x = (float) (cos(glm::radians(YAW)) * cos(glm::radians(PITCH))); - calculateFront.y = (float) sin(glm::radians(PITCH)); - calculateFront.z = (float) (sin(glm::radians(YAW)) * cos(glm::radians(PITCH))); - front = glm::normalize(calculateFront); - // also re-calculate the Right and Up vector - // normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement. + glm::vec3 newFront; + newFront.x = cos(glm::radians(PITCH)) * cos(glm::radians(YAW)); + newFront.y = cos(glm::radians(PITCH)) * sin(glm::radians(YAW)); + newFront.z = sin(glm::radians(PITCH)); + front = glm::normalize(newFront); + + constexpr glm::vec3 worldUp(0.0f, 0.0f, 1.0f); // Z nahoru right = glm::normalize(glm::cross(front, worldUp)); up = glm::normalize(glm::cross(right, front)); } @@ -25,7 +26,18 @@ namespace Manager { return zoom; } - glm::mat4 Camera::getViewMatrix() const { + glm::mat4 Camera::getViewMatrix() { + if (!rightButtonPressed) { + // --- STANDARDNÍ MÓD --- + // Kamera je fixována na 'stickyPoint' s daným offsetem + const auto targetPos = glm::vec3(stickyPoint->getModelMatrix() * glm::vec4(0, 0, 0, 1)); + this->setPosition(targetPos + offsetFromTarget); + return glm::lookAt(position, targetPos, worldUp); + } + + // --- SPECTATOR MÓD --- + // Kamera se volně pohybuje a dívá se, kam míří její 'front' vektor + // Používáme 'position' jako volnou pozici kamery return glm::lookAt(position, position + front, up); } @@ -33,44 +45,137 @@ namespace Manager { return position; } - void Camera::setStickyPoint(BaseItem *stickyPoint) { + const glm::vec3 & Camera::getFront() const { + return front; + } + + glm::vec3 Camera::getUp() const { + return up; + } + + glm::vec3 Camera::getRight() const { + return right; + } + + void Camera::setStickyPoint(const shared_ptr &stickyPoint) { this->stickyPoint = stickyPoint; + + const auto targetPos = glm::vec3(stickyPoint->getModelMatrix() * glm::vec4(0, 0, 0, 1)); + position = targetPos + offsetFromTarget; + + const glm::vec3 dirToTarget = glm::normalize(targetPos - position); + + // YAW: úhel v rovině X-Y + YAW = glm::degrees(atan2(dirToTarget.y, dirToTarget.x)); + + // PITCH: úhel vzhůru/dolů podle Z + PITCH = glm::degrees(asin(dirToTarget.z)); + + updateCameraVectors(); } - void Camera::updateStickyPoint() { - glm::vec3 pos = stickyPoint->getPosition(); - position = pos; - position.x /= 26; - position.y /= 26; - position.y -= 3; - position.z = 1; // -3 + shared_ptr Camera::getStickyPoint() const { + return stickyPoint; } - glm::vec3 Camera::getStickyPosition() { + glm::vec3 Camera::getStickyPosition() const { return stickyPoint->getPosition(); } - void Camera::processMouseMovement(double x, double y) { - x *= 0.1; - y *= 0.1; + void Camera::setPosition(const glm::vec3& pos) { + position = pos; + } - YAW += (float)x; - PITCH += (float)y; + void Camera::setFront(const glm::vec3& front) { + this->front = glm::normalize(front); + } + + void Camera::setUp(const glm::vec3& up) { + this->up = up; + } + + void Camera::onMouseDown(const int button, const int action, int mods) { + if (button == GLFW_MOUSE_BUTTON_RIGHT) { + if (action == GLFW_PRESS && stickyPoint) { + rightButtonPressed = true; + firstMouse = true; + + // Uložíme si aktuální pozici kamery jako startovní bod pro spectator mód + const auto targetPos = glm::vec3(stickyPoint->getModelMatrix() * glm::vec4(0, 0, 0, 1)); + position = targetPos + offsetFromTarget; // Nastavíme 'position' na aktuální vizuální pozici + + // Vypočítáme YAW a PITCH, aby přechod byl plynulý + const glm::vec3 dirToTarget = glm::normalize(targetPos - position); + YAW = glm::degrees(atan2(dirToTarget.z, dirToTarget.x)); + PITCH = glm::degrees(asin(dirToTarget.y)); + updateCameraVectors(); // Ihned aktualizujeme vektory + cout << "Spectator started..." << endl; + } else if (action == GLFW_RELEASE) { + rightButtonPressed = false; + // Není potřeba nic resetovat,getViewMatrix se postará o návrat na původní pozici + } + } + } + + void Camera::processMouseMovement(const double x, const double y) { + if (!rightButtonPressed) return; + + if (firstMouse) { + lastX = static_cast(x); + lastY = static_cast(y); + firstMouse = false; + return; + } + + auto xoffset = static_cast(x - lastX); + auto yoffset = static_cast(lastY - y); + + lastX = static_cast(x); + lastY = static_cast(y); + + constexpr float sensitivity = 0.1f; + xoffset *= sensitivity; + yoffset *= sensitivity; + + YAW -= xoffset; + PITCH += yoffset; + + if (PITCH > 89.0f) PITCH = 89.0f; + if (PITCH < -89.0f) PITCH = -89.0f; updateCameraVectors(); } - void Camera::processKeyboard(Camera_Movement direction, float deltaTime) + void Camera::setKeyState(const int key, const bool pressed) { + if (key >= 0 && key < 1024) { + keys[key] = pressed; + } + } + + void Camera::processKeyboard(GLFWwindow *window, const float deltaTime) { - float velocity = 0.1f * deltaTime; - if (direction == FORWARD) - position += front * velocity; - if (direction == BACKWARD) - position -= front * velocity; - if (direction == LEFT) - position -= right * velocity; - if (direction == RIGHT) - position += right * velocity; - } - -} \ No newline at end of file + if (!rightButtonPressed) return; + + const float velocity = 0.04f * deltaTime; + + // Vytvoříme nulový vektor pohybu + glm::vec3 moveDirection(0.0f); + + if (keys[GLFW_KEY_W]) { + moveDirection += front; // Dopředu + } + if (keys[GLFW_KEY_S]) { + moveDirection -= front; // Dozadu + } + if (keys[GLFW_KEY_A]) { + moveDirection -= right; // Doleva + } + if (keys[GLFW_KEY_D]) { + moveDirection += right; // Doprava + } + + if (glm::length(moveDirection) > 0.0f) { + position += glm::normalize(moveDirection) * velocity; + } + } +} diff --git a/Manager/Camera.h b/Manager/Camera.h index 2c866cde..0859958f 100644 --- a/Manager/Camera.h +++ b/Manager/Camera.h @@ -1,9 +1,10 @@ #ifndef SNAKE3_CAMERA_H #define SNAKE3_CAMERA_H +#include + #include "../ItemsDto/BaseItem.h" #include -#include using namespace ItemsDto; @@ -20,24 +21,40 @@ namespace Manager { public: explicit Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f)); [[nodiscard]] float getZoom() const; - [[nodiscard]] glm::mat4 getViewMatrix() const; + [[nodiscard]] glm::mat4 getViewMatrix(); [[nodiscard]] const glm::vec3 &getPosition() const; - void setStickyPoint(BaseItem *stickyPoint); - glm::vec3 getStickyPosition(); + [[nodiscard]] const glm::vec3 &getFront() const; + [[nodiscard]] glm::vec3 getUp() const; + [[nodiscard]] glm::vec3 getRight() const; + void setStickyPoint(const shared_ptr &stickyPoint); + [[nodiscard]] shared_ptr getStickyPoint() const; + [[nodiscard]] glm::vec3 getStickyPosition() const; void updateStickyPoint(); void processMouseMovement(double x, double y); - void processKeyboard(Camera_Movement direction, float deltaTime); + void processKeyboard(GLFWwindow *window, float deltaTime); + + void setPosition(const glm::vec3& pos); + void setFront(const glm::vec3& front); + void setUp(const glm::vec3& up); + void onMouseDown(int button, int action, int mods); + void setKeyState(int key, bool pressed); protected: + bool keys[1024] = { false }; glm::vec3 position{}; glm::vec3 front; glm::vec3 up{}; glm::vec3 right{}; glm::vec3 worldUp{}; float zoom; - BaseItem* stickyPoint{}; + shared_ptr stickyPoint; float YAW = -90.0f; // 90 float PITCH = 56.0f; + bool rightButtonPressed = true; + bool firstMouse = true; + float lastX = 0.0f; + float lastY = 0.0f; + glm::vec3 offsetFromTarget = glm::vec3(0.0f, -3.5f, 3.0f); void updateCameraVectors(); }; diff --git a/Manager/EatManager.cpp b/Manager/EatManager.cpp index e40aa848..74d3c5a8 100644 --- a/Manager/EatManager.cpp +++ b/Manager/EatManager.cpp @@ -1,13 +1,9 @@ #include "EatManager.h" namespace Manager { - EatManager::EatManager(EatLocationHandler *handler) : handler(handler) {} + EatManager::EatManager(const shared_ptr &handler) : handler(handler) {} - EatManager::~EatManager() { - delete handler; - } - - void EatManager::run(EatManager::eat_EVENT event) { + void EatManager::run(const eat_EVENT event) const { switch (event) { case eatenUp: handler->onDefaultHandler(); diff --git a/Manager/EatManager.h b/Manager/EatManager.h index d8393eec..4e441f59 100644 --- a/Manager/EatManager.h +++ b/Manager/EatManager.h @@ -1,26 +1,23 @@ #ifndef SNAKE3_EATMANAGER_H #define SNAKE3_EATMANAGER_H -#include "../stdafx.h" -#include "../Handler/BaseHandler.h" #include "../Handler/EatLocationHandler.h" -#include using namespace Handler; using namespace std; namespace Manager { - - class EatManager { + class EatManager final { public: - explicit EatManager(EatLocationHandler *handler); - virtual ~EatManager(); + explicit EatManager(const shared_ptr &handler); + enum eat_EVENT { none, eatenUp, firstPlace, checkPlace, clean }; - void run(eat_EVENT event = none); + + void run(eat_EVENT event = none) const; + protected: - EatLocationHandler* handler; + shared_ptr handler; }; - } // Manager #endif //SNAKE3_EATMANAGER_H diff --git a/Manager/KeyboardManager.cpp b/Manager/KeyboardManager.cpp index 7e3d7f14..86777d26 100644 --- a/Manager/KeyboardManager.cpp +++ b/Manager/KeyboardManager.cpp @@ -1,24 +1,19 @@ #include "KeyboardManager.h" namespace Manager { - KeyboardManager::~KeyboardManager() { - for (auto Iter = handlers.begin(); Iter < handlers.end(); Iter++) { - delete (*Iter); - } - } - void KeyboardManager::onKeyPress(int keyCode) { - for (auto Iter = handlers.begin(); Iter < handlers.end(); Iter++) { - (*Iter)->onEventHandler(keyCode); + void KeyboardManager::onKeyPress(const int keyCode, const int scancode, const int action, const int mods) { + for (auto Iter = handlers.begin(); Iter < handlers.end(); ++Iter) { + (*Iter)->onEventHandler(keyCode, scancode, action, mods); } } - void KeyboardManager::addEventHandler(BaseKeydownHandle *handler) { - handlers.push_back(handler); + void KeyboardManager::addEventHandler(shared_ptr handler) { + handlers.push_back(std::move(handler)); } void KeyboardManager::runDefault() { - for (auto Iter = handlers.begin(); Iter < handlers.end(); Iter++) { + for (auto Iter = handlers.begin(); Iter < handlers.end(); ++Iter) { (*Iter)->onDefaultHandler(); } } diff --git a/Manager/KeyboardManager.h b/Manager/KeyboardManager.h index c71fa30d..46a6b973 100644 --- a/Manager/KeyboardManager.h +++ b/Manager/KeyboardManager.h @@ -13,12 +13,12 @@ namespace Manager { class KeyboardManager { public: - virtual ~KeyboardManager(); - void addEventHandler(BaseKeydownHandle* handler); - void onKeyPress(int keyCode); + virtual ~KeyboardManager() = default; + void addEventHandler(shared_ptr handler); + void onKeyPress(int keyCode, int scancode, int action, int mods); void runDefault(); protected: - vector handlers; + vector > handlers; }; } // Manager diff --git a/Manager/LevelManager.cpp b/Manager/LevelManager.cpp index 6d4f0784..f263842f 100644 --- a/Manager/LevelManager.cpp +++ b/Manager/LevelManager.cpp @@ -1,14 +1,19 @@ #include "LevelManager.h" +#include + +#include "../Renderer/Opengl/Model/Standard/BoxMesh.h" namespace Manager { - LevelManager::LevelManager(int level, int live, Barriers *barriers) : level(level), live(live), - barriers(barriers), eatCounter(0) {} + LevelManager::LevelManager(const shared_ptr &contextState, + const int level, const int live, const shared_ptr &resourceManager) + : level(level), live(live), eatCounter(0), resourceManager(resourceManager), contextState(contextState) { + } - void LevelManager::setLevel(int level) { + void LevelManager::setLevel(const int level) { LevelManager::level = level; } - void LevelManager::setLive(int live) { + void LevelManager::setLive(const int live) { LevelManager::live = live; } @@ -20,25 +25,65 @@ namespace Manager { return live; } - void LevelManager::createLevel(int level) { + shared_ptr LevelManager::createLevel(int level, + shared_ptr &directionalLight, + const vector > &spotLights, + const vector > &pointLights) { + const auto shader = resourceManager ? resourceManager->getShader("basicShader") : nullptr; + const auto shadowsShader = resourceManager ? resourceManager->getShader("shadowDepthShader") : nullptr; + const auto boxMesh = make_shared(shader, 2.0, 2.0, 2.0); + + if (resourceManager) { + const auto brickWall = resourceManager->getTexture("brickwork-texture.jpg"); + const auto brickWallNormal = resourceManager->getTexture("brickwork_normal-map.jpg"); + const auto brickWallSpecular = resourceManager->getTexture("brickwork-bump-map.jpg"); + const auto boxMaterial = make_shared(StandardMaterial(shader, shadowsShader)); + boxMaterial->setColor({1.0, 1.0, 1.0}); + boxMaterial->setNormalEnabled(true); + boxMaterial->setAlbedo(brickWall); + boxMaterial->setNormal(brickWallNormal); + boxMaterial->setSpecular(brickWallSpecular); + boxMaterial->setDirectionalLight(directionalLight); + boxMaterial->setSpotLights(spotLights); + boxMaterial->setPointLights(pointLights); + boxMesh->setMaterial(boxMaterial); + } + + const auto boxNode3D = make_shared(contextState, boxMesh, resourceManager); + boxNode3D->setPosition({0.0, 0.0, -23.0}); + boxNode3D->setScale({0.041666667f, 0.041666667f, 0.041666667f}); + boxNode3D->setTransformDetached(true); + this->level = level; this->eatCounter = 0; - barriers->reset(); string filename = "Assets/Levels/level"; filename += std::to_string(level); filename += ".txt"; ifstream infile(filename); if (infile.is_open()) { + bool isFirst = true; std::string line; int y = 0; while (std::getline(infile, line)) { int x = 0; - for (char & c : line) - { - if (c == 49) { // "1" - barriers->createWall(-25 + ((x + 1) * 2), -25 + ((y + 1) * 2)); + for (char &c: line) { + if (c == 49) { + // "1" + if (isFirst) { + boxNode3D->setPosition({-25 + ((x + 1) * 2), -25 + ((y + 1) * 2), -23.0}); + boxNode3D->x = (x) * 32; + boxNode3D->y = (y) * 32; + isFirst = false; + continue; + } + const auto childBoxNode3D = make_shared(contextState, boxMesh, resourceManager); + childBoxNode3D->setPosition({-25 + ((x + 1) * 2), -25 + ((y + 1) * 2), -23.0}); + childBoxNode3D->setScale({0.041666667f, 0.041666667f, 0.041666667f}); + childBoxNode3D->x = (x) * 32; + childBoxNode3D->y = (y) * 32; + boxNode3D->addNode(childBoxNode3D); } x++; @@ -48,14 +93,15 @@ namespace Manager { } infile.close(); } + + return boxNode3D; } int LevelManager::getEatCounter() const { return eatCounter; } - void LevelManager::setEatCounter(int eatCounter) { + void LevelManager::setEatCounter(const int eatCounter) { LevelManager::eatCounter = eatCounter; } - -} // Manager \ No newline at end of file +} // Manager diff --git a/Manager/LevelManager.h b/Manager/LevelManager.h index 56b436b6..129b6638 100644 --- a/Manager/LevelManager.h +++ b/Manager/LevelManager.h @@ -1,20 +1,27 @@ #ifndef SNAKE3_LEVELMANAGER_H #define SNAKE3_LEVELMANAGER_H -#include "../ItemsDto/Barriers.h" -#include +#define MAX_POINT 6 +#define MAX_LIVES 4 +#define START_LEVEL 2 + +#include + +#include "../Renderer/Opengl/Model/Standard/MeshNode3D.h" using namespace ItemsDto; +using namespace Model; using namespace std; namespace Manager { class LevelManager { public: - LevelManager(int level, int live, Barriers *barriers); + LevelManager(const shared_ptr &contextState, int level, int live, const shared_ptr &resourceManager); void setLevel(int level); void setLive(int live); - void createLevel(int level); + shared_ptr createLevel(int level, shared_ptr &directionalLight, const vector > &spotLights, + const vector > &pointLights); [[nodiscard]] int getLevel() const; [[nodiscard]] int getLive() const; [[nodiscard]] int getEatCounter() const; @@ -24,7 +31,8 @@ namespace Manager { int level; int live; int eatCounter; - Barriers* barriers; + shared_ptr resourceManager; + shared_ptr contextState; }; } // Manager diff --git a/Manager/RenderManager.cpp b/Manager/RenderManager.cpp index 68562fe8..36716f13 100644 --- a/Manager/RenderManager.cpp +++ b/Manager/RenderManager.cpp @@ -1,83 +1,155 @@ #include "RenderManager.h" namespace Manager { - RenderManager::RenderManager(int width, int height) : - width(width), height(height), bloom(false), shadows(false), fog(false) { + RenderManager::RenderManager(const shared_ptr &contextState, const shared_ptr &camera, + const shared_ptr &resourceManager, const glm::mat4 &projection, const int width, const int height) + : resourceManager(resourceManager), camera(camera), contextState(contextState), projection(projection), width(width), height(height), + shadows(false), bloom(false), reflections(false), fog(false) { glClearDepth(1.0f); glEnable(GL_DEPTH_TEST); glEnable(GL_TEXTURE_2D); } - RenderManager::~RenderManager() { - for (auto Iter = renderers.begin(); Iter < renderers.end(); Iter++) { - delete (*Iter); - } - delete depthMapRenderer; - delete bloomRenderer; + shared_ptr RenderManager::getContextState() const { + return contextState; + } + + void RenderManager::initBloom() { + bloomRenderer = make_unique(resourceManager, width, height); + } + + void RenderManager::initShadowMapping() { + depthMapRenderer = make_unique(camera.get(), projection, resourceManager.get()); } - void RenderManager::addRenderer(BaseRenderer *renderer) { - renderers.push_back(renderer); + void RenderManager::initReflection() { + planarReflectionRenderer = make_unique(contextState, resourceManager, camera, projection, width, height); + planarReflectionRenderer->updateRenderers(renderers); } - void RenderManager::render() { + void RenderManager::addRenderer(shared_ptr renderer, const int priority) { + renderers.push_back({std::move(renderer), priority}); + stable_sort(renderers.begin(), renderers.end(), + [](auto &a, auto &b) { return a.priority > b.priority; }); + if (planarReflectionRenderer) { + planarReflectionRenderer->updateRenderers(renderers); + } + } + + void RenderManager::render(const float dt) { + if (reflections && planarReflectionRenderer) { + planarReflectionRenderer->render3D(dt, gFrameId); + } + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + glEnable(GL_DEPTH_TEST); glLoadIdentity(); - glClearColor(.0, .0, .0, 0); + if (!bloom) { + glClearColor(.0, .0, .0, 1.0); + } else { + constexpr auto backgroundColor = glm::vec3(0.0, 0.0, 0.0); + constexpr float backgroundIntensity = {2.0f}; + glClearColor( + backgroundColor.r * backgroundIntensity, + backgroundColor.g * backgroundIntensity, + backgroundColor.b * backgroundIntensity, + 1.0f + ); + } + glViewport(0, 0, width, height); - if (shadows) { + if (shadows && depthMapRenderer) { + glDepthFunc(GL_LESS); glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(3.0f, 3.0f); - if (depthMapRenderer) { - depthMapRenderer->beforeRender(); + + glm::vec3 sceneMin(FLT_MAX); + glm::vec3 sceneMax(-FLT_MAX); + + for (auto Iter = renderers.begin(); Iter < renderers.end(); ++Iter) { + sceneMin = Iter->renderer->compareSceneMin(sceneMin); + sceneMax = Iter->renderer->compareSceneMax(sceneMax); } - glCullFace(GL_FRONT); - for (auto Iter = renderers.begin(); Iter < renderers.end(); Iter++) { - if ((*Iter)->isShadow()) { - (*Iter)->renderShadowMap(); - } + constexpr float padding = 2.0f; + sceneMin -= glm::vec3(padding); + sceneMax += glm::vec3(padding); + + // constexpr glm::vec3 centerScene = {0, 0.0f, 0.0f}; //(sceneMin + sceneMax) / 2.0f; + + shared_ptr light = directionalLight; + if (directionalLight == nullptr) { + light = make_shared(); + light->setPosition({0.0f, 7.0f, 11.0f}); + light->setDirection({1, 1.0, -3}); } - glCullFace(GL_BACK); // don't forget to reset original culling face + //const auto lightSpacesMatrix = depthMapRenderer->computeLightSpaceMatrixForPlane(light, centerScene, 14, 14); + const auto lightSpacesMatrix = depthMapRenderer->computeLightSpaceMatrix(light); + int index = 0; + + for (auto & matrix : lightSpacesMatrix) { + depthMapRenderer->beforeRender(index); + depthMapRenderer->bind(index, matrix); + + glCullFace(GL_FRONT); + + for (auto Iter = renderers.begin(); Iter < renderers.end(); ++Iter) { + if (Iter->renderer->isShadow()) { + Iter->renderer->renderShadowMap(); + } + } + glCullFace(GL_BACK); - if (depthMapRenderer) { glBindFramebuffer(GL_FRAMEBUFFER, 0); // reset viewport glViewport(0, 0, width, height); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - depthMapRenderer->render(); - depthMapRenderer->afterRender(); + depthMapRenderer->render(dt); + // depthMapRenderer->afterRender(); + + index++; } + glDisable(GL_POLYGON_OFFSET_FILL); } + constexpr GLenum attachments[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }; + glDrawBuffers(2, attachments); + if (bloom) { - bloomRenderer->beforeRender(); + bloomRenderer->beforeRender(MODE::bloom); } - for (auto Iter = renderers.begin(); Iter < renderers.end(); Iter++) { - (*Iter)->beforeRender(); - (*Iter)->render(); - (*Iter)->afterRender(); + for (auto Iter = renderers.begin(); Iter < renderers.end(); ++Iter) { + Iter->renderer->beforeRender(standard); + Iter->renderer->render3D(dt, gFrameId); + Iter->renderer->afterRender(); + } + + for (auto Iter = renderers.begin(); Iter < renderers.end(); ++Iter) { + Iter->renderer->beforeRender(standard); + Iter->renderer->render2D(dt, gFrameId); + Iter->renderer->afterRender(); } if (bloom) { this->bloomRenderer->afterRender(); } + gFrameId++; } - void RenderManager::setWidth(int width) { + void RenderManager::setWidth(const int width) { RenderManager::width = width; } - void RenderManager::setHeight(int height) { + void RenderManager::setHeight(const int height) { RenderManager::height = height; } - void RenderManager::setDepthMapRenderer(DepthMapRenderer *depthMapRenderer) { - RenderManager::depthMapRenderer = depthMapRenderer; + void RenderManager::setDepthMapRenderer(unique_ptr &depthMapRenderer) { + RenderManager::depthMapRenderer = std::move(depthMapRenderer); } void RenderManager::enableShadows() { @@ -91,8 +163,8 @@ namespace Manager { } void RenderManager::updateShadows() { - for (auto Iter = renderers.begin(); Iter < renderers.end(); Iter++) { - (*Iter)->setShadow(shadows); + for (auto Iter = renderers.begin(); Iter < renderers.end(); ++Iter) { + Iter->renderer->setShadow(shadows); } } @@ -101,8 +173,12 @@ namespace Manager { updateShadows(); } - void RenderManager::setBloomRenderer(BloomRenderer *bloomRenderer) { - RenderManager::bloomRenderer = bloomRenderer; + void RenderManager::setBloomRenderer(unique_ptr &bloomRenderer) { + RenderManager::bloomRenderer = std::move(bloomRenderer); + } + + void RenderManager::setPlanarReflectionRenderer(unique_ptr planarReflectionRenderer) { + RenderManager::planarReflectionRenderer = std::move(planarReflectionRenderer); } void RenderManager::toggleBloom() { @@ -114,9 +190,29 @@ namespace Manager { updateFog(); } + void RenderManager::toggleReflections() { + reflections = !reflections; + } + + bool RenderManager::isReflectionsEnabled() const { + return reflections; + } + + void RenderManager::reset() { + renderers.clear(); + } + + const vector& RenderManager::getRenderers() const { + return renderers; + } + + void RenderManager::updateDirectionalLight(const shared_ptr &light) { + directionalLight = light; + } + void RenderManager::updateFog() { - for (auto Iter = renderers.begin(); Iter < renderers.end(); Iter++) { - (*Iter)->setFog(fog); + for (auto Iter = renderers.begin(); Iter < renderers.end(); ++Iter) { + Iter->renderer->setFog(fog); } } diff --git a/Manager/RenderManager.h b/Manager/RenderManager.h index 13646000..2b762d81 100644 --- a/Manager/RenderManager.h +++ b/Manager/RenderManager.h @@ -3,48 +3,93 @@ #include "../Renderer/Opengl/BaseRenderer.h" #include "../Renderer/Opengl/DepthMapRenderer.h" -#include "../stdafx.h" #include "../Renderer/Opengl/BloomRenderer.h" -#include -#include -#include +#include "../Renderer/Opengl/PlanarReflectionRenderer.h" #include -#include using namespace std; using namespace Renderer; namespace Manager { + enum class RenderPassType { + Shadow, + Scene, + PostProcess + }; - class RenderManager { + class RenderManager final { public: - RenderManager(int width, int height); - virtual ~RenderManager(); - void render(); - void addRenderer(BaseRenderer* renderer); - void setDepthMapRenderer(DepthMapRenderer *depthMapRenderer); - void setBloomRenderer(BloomRenderer *bloomRenderer); + RenderManager(const shared_ptr &contextState, + const shared_ptr &camera, const shared_ptr &resourceManager, + const glm::mat4 &projection, + int width, int height); + + ~RenderManager() = default; + + [[nodiscard]] shared_ptr getContextState() const; + + void initBloom(); + + void initShadowMapping(); + + void initReflection(); + + void render(float dt); + + void addRenderer(shared_ptr renderer, int priority = 0); + + void setDepthMapRenderer(unique_ptr &depthMapRenderer); + + void setBloomRenderer(unique_ptr &bloomRenderer); + + void setPlanarReflectionRenderer(unique_ptr planarReflectionRenderer); + void setWidth(int width); + void setHeight(int height); + void enableShadows(); + void disableShadows(); + void toggleShadows(); + void toggleBloom(); + void toggleFog(); + void toggleReflections(); + + [[nodiscard]] bool isReflectionsEnabled() const; + + void reset(); + + [[nodiscard]] const vector &getRenderers() const; + + void updateDirectionalLight(const shared_ptr & light); + protected: void updateShadows(); + void updateFog(); - vector renderers; - DepthMapRenderer* depthMapRenderer{}; - BloomRenderer* bloomRenderer{}; + + vector renderers; + unique_ptr depthMapRenderer; + unique_ptr bloomRenderer; + unique_ptr planarReflectionRenderer; + shared_ptr directionalLight; + shared_ptr resourceManager; + shared_ptr camera; + shared_ptr contextState; + glm::mat4 projection{}; int width; int height; bool shadows; bool bloom; + bool reflections; bool fog; + uint64_t gFrameId = 0; }; - } // Manager #endif //SNAKE3_RENDERMANAGER_H diff --git a/Manager/ResourceManager.cpp b/Manager/ResourceManager.cpp index f99e0a27..5e7aa364 100644 --- a/Manager/ResourceManager.cpp +++ b/Manager/ResourceManager.cpp @@ -1,64 +1,108 @@ #include "ResourceManager.h" +#include "../Resource/ShaderLoader.h" +#include "../Resource/TextureLoader.h" + namespace Manager { + ResourceManager::ResourceManager() { + std::unique_lock lock(mutex); + loader = make_unique(); + } + ResourceManager::~ResourceManager() { - Release(); + release(); } - bool ResourceManager::Release() { + bool ResourceManager::release() { + std::unique_lock lock(mutex); + + if (loader) { + loader->stop(); + loader.reset(); + } + + for (auto &t: threads) { + if (t.joinable()) { + t.join(); + } + } + threads.clear(); { + std::lock_guard guard(pendingMutex); + while (!pending.empty()) pending.pop(); + waitingModels.clear(); + } + animationModel.clear(); model.clear(); texture.clear(); + shader.clear(); return true; } - void ResourceManager::addTexture(const string& name, const shared_ptr& res) { + void ResourceManager::clearTextures() { + for (auto it = texture.begin(); it != texture.end();) { + if (it->first != "depth") { + it = texture.erase(it); + } else { + ++it; + } + } + } + + void ResourceManager::addTexture(const string &name, const shared_ptr &res) { std::unique_lock lock(mutex); - const auto result = texture.emplace(name, res); - if (!result.second) { + if (const auto [fst, snd] = texture.emplace(name, res); !snd) { throw invalid_argument("Failed to add texture " + name + ", already contains."); } } - TextureManager* ResourceManager::getTexture(const string &name) { + void ResourceManager::replaceTexture(const string &name, const shared_ptr &res) { + std::unique_lock lock(mutex); + if (const auto [fst, snd] = texture.emplace(name, res); !snd) { + texture.erase(texture.find(name)); + if (const auto [fst, snd] = texture.emplace(name, res); !snd) { + throw invalid_argument("Failed to add texture " + name + ", already contains."); + } + } + } + + std::shared_ptr ResourceManager::getTexture(const string &name) const { std::unique_lock lock(mutex); try { - return texture.at(name).get(); + return texture.at(name); } catch (...) { throw invalid_argument("No such resource called " + name); } } - void ResourceManager::addModel(const string &name, std::shared_ptr res) { + void ResourceManager::addModel(const string &name, std::shared_ptr &res) { std::unique_lock lock(mutex); - const auto result = model.emplace(name, std::move(res)); - if (!result.second) { + if (const auto [fst, snd] = model.emplace(name, res); !snd) { throw invalid_argument("Failed to add model " + name + ", already contains."); } } - void ResourceManager::addModel(const string &name, std::shared_ptr res) { + void ResourceManager::addModel(const string &name, std::shared_ptr res) { std::unique_lock lock(mutex); - const auto result = animationModel.emplace(name, std::move(res)); - if (!result.second) { + if (const auto [fst, snd] = animationModel.emplace(name, std::move(res)); !snd) { throw invalid_argument("Failed to add model " + name + ", already contains."); } } - ObjItem* ResourceManager::getModel(const string &name) { + shared_ptr ResourceManager::getModel(const string &name) const { std::unique_lock lock(mutex); try { - return model.at(name).get(); + return model.at(name); } catch (...) { throw invalid_argument("No such resource called " + name); } } - AnimationModel* ResourceManager::getAnimationModel(const string &name) { + shared_ptr ResourceManager::getAnimationModel(const string &name) const { std::unique_lock lock(mutex); try { - return animationModel.at(name).get(); + return animationModel.at(name); } catch (...) { throw invalid_argument("No such resource called " + name); } @@ -66,19 +110,149 @@ namespace Manager { void ResourceManager::addShader(const string &name, const shared_ptr &res) { std::unique_lock lock(mutex); - const auto result = shader.emplace(name, res); - if (!result.second) { + if (const auto [fst, snd] = shader.emplace(name, res); !snd) { throw invalid_argument("Failed to add baseShader " + name + ", already contains."); } } - ShaderManager *ResourceManager::getShader(const string &name) { + std::shared_ptr ResourceManager::getShader(const string &name) const { std::unique_lock lock(mutex); try { - return shader.at(name).get(); + return shader.at(name); } catch (...) { throw invalid_argument("No such resource called " + name); } } -} // Manager \ No newline at end of file + void ResourceManager::loadAsyncTexture( + const std::string &path, + const std::string &name, + const bool albedo, + const std::function &onReady) { + std::unique_lock lock(mutex); + waitingModels.push_back(name); + ++loadingCount; + + threads.emplace_back([this, path, name, albedo, onReady]() { + loader->enqueueTexture(path, albedo, + [this, name, onReady](const vector &buffer, const bool isAlbedo) { + { + std::lock_guard guard(pendingMutex); + pendingTextures.push({name, buffer, isAlbedo, onReady}); + const auto it = std::find(waitingModels.begin(), waitingModels.end(), name); + if (it != waitingModels.end()) waitingModels.erase(it); + } + --loadingCount; + }); + }); + } + + void ResourceManager::loadAsyncShader( + const std::string &name, + const std::string &vertexPath, + const std::string &geometryPath, + const std::string &fragmentPath, + const std::function &onReady) { + std::unique_lock lock(mutex); + waitingModels.push_back(name); + ++loadingCount; + + threads.emplace_back([this, vertexPath, geometryPath, fragmentPath, name, onReady]() { + loader->enqueueShader(vertexPath, geometryPath, fragmentPath, + [this, name, onReady](const vector &vertexBuffer, + const vector &fragmentBuffer, + const vector &geometryBuffer + ) { + { + std::lock_guard guard(pendingMutex); + pendingShaders.push({ + name, vertexBuffer, geometryBuffer, fragmentBuffer, onReady + }); + const auto it = std::find(waitingModels.begin(), waitingModels.end(), name); + if (it != waitingModels.end()) waitingModels.erase(it); + } + --loadingCount; + }); + }); + } + + void ResourceManager::processPending() { + std::lock_guard lock(pendingMutex); + + while (!pending.empty()) { + auto p = pending.front(); + pending.pop(); + + // Nahrání do GPU atd. zde: + if (p.model.size() == 1) { + addModel(p.name, p.model[0]); + } else { + int i = 0; + for (auto &m : p.model) { + addModel(p.name + "_" + std::to_string(i), m); + // m-> + i++; + } + } + + if (p.onReady) p.onReady(); + } + + while (!pendingAnim.empty()) { + auto p = pendingAnim.front(); + pendingAnim.pop(); + + addModel(p.name, p.model); + if (p.onReady) p.onReady(); + } + + while (!pendingTextures.empty()) { + auto p = pendingTextures.front(); + pendingTextures.pop(); + + // tady mozna misto v p.Textures mit jen buffer a ten rovnou nahrat do GPU uz tady ???? + auto texture = make_shared(); + texture->addTexture(TextureLoader::bindFromBuffer(p.buffer, p.albedo)); + addTexture(p.name, texture); + p.buffer.clear(); + if (p.onReady) p.onReady(); + } + + while (!pendingShaders.empty()) { + auto p = pendingShaders.front(); + pendingShaders.pop(); + + string vertexBuffer(p.vertexBuffer.begin(), p.vertexBuffer.end()); + string geometryBuffer(p.geometryBuffer.begin(), p.geometryBuffer.end()); + string fragmentBuffer(p.fragmentBuffer.begin(), p.fragmentBuffer.end()); + + if (geometryBuffer.empty()) { + auto shader = make_shared(ShaderLoader::bindFromBuffer(vertexBuffer, fragmentBuffer)); + addShader(p.name, shader); + } else { + auto shader = make_shared(ShaderLoader::bindFromBuffer(vertexBuffer, geometryBuffer, fragmentBuffer)); + addShader(p.name, shader); + } + p.vertexBuffer.clear(); + p.geometryBuffer.clear(); + p.fragmentBuffer.clear(); + if (p.onReady) p.onReady(); + } + } + + bool ResourceManager::isAllLoaded() const { + std::lock_guard lock(pendingMutex); + return waitingModels.empty() && pending.empty() && loadingCount.load() == 0; + } + + void ResourceManager::waitForAll() { + // Počká na všechna vlákna + for (auto &t: threads) { + if (t.joinable()) t.join(); + } + threads.clear(); + + // Zpracuj zbytek pending modelů + processPending(); + } +} // Manager diff --git a/Manager/ResourceManager.h b/Manager/ResourceManager.h index 2db75c1d..8928e84b 100644 --- a/Manager/ResourceManager.h +++ b/Manager/ResourceManager.h @@ -1,45 +1,151 @@ #ifndef SNAKE3_RESOURCEMANAGER_H #define SNAKE3_RESOURCEMANAGER_H -#include "../stdafx.h" -#include -#include +#include #include #include #include -#include "../ItemsDto/ObjItem.h" -#include "../Resource/TextureLoader.h" +#include #include "TextureManager.h" #include "ShaderManager.h" -#include "../Renderer/Opengl/Model/AnimationModel.h" +#include "../Resource/ResourceLoader.h" using namespace std; -using namespace ItemsDto; using namespace Resource; -using namespace Model; +using namespace Animations; namespace Manager { + template + inline constexpr bool always_false = false; - class ResourceManager { + class ResourceManager final { public: - virtual ~ResourceManager(); - void addTexture(const string& name, const shared_ptr& res); - void addShader(const string& name, const shared_ptr& res); - void addModel(const string& name, shared_ptr res); - void addModel(const string& name, shared_ptr res); - TextureManager* getTexture(const string &name); - ShaderManager* getShader(const string &name); - ObjItem* getModel(const string &name); - AnimationModel* getAnimationModel(const string &name); - bool Release(); + ResourceManager(); + + ~ResourceManager(); + + void addTexture(const string &name, const shared_ptr &res); + + void replaceTexture(const string &name, const shared_ptr &res); + + void addShader(const string &name, const shared_ptr &res); + + void addModel(const string &name, shared_ptr &res); + + void addModel(const string &name, shared_ptr res); + + shared_ptr getTexture(const string &name) const; + + shared_ptr getShader(const string &name) const; + + shared_ptr getModel(const string &name) const; + + shared_ptr getAnimationModel(const string &name) const; + + void loadAsyncTexture(const string &path, const string &name, bool albedo, const function &onReady = nullptr); + + void loadAsyncShader( + const std::string &name, + const std::string &vertexPath, + const std::string &geometryPath, + const std::string &fragmentPath, + const std::function &onReady + ); + + template + void loadAsyncModel(const std::string &path, const std::string &name, const std::function &onReady = nullptr) { + std::lock_guard lock(pendingMutex); + waitingModels.push_back(name); + ++loadingCount; + + threads.emplace_back([this, path, name, onReady]() { + try { + if constexpr (std::is_same_v) { + loader->enqueue(path, [this, name, onReady](const std::vector> &model) { + { + std::lock_guard guard(pendingMutex); + pending.push({name, model, onReady}); + const auto it = std::find(waitingModels.begin(), waitingModels.end(), name); + if (it != waitingModels.end()) waitingModels.erase(it); + } + --loadingCount; + }); + } else if constexpr (std::is_same_v) { + loader->enqueueAnimation(path, [this, name, onReady](const std::shared_ptr &model) { + { + std::lock_guard guard(pendingMutex); + pendingAnim.push({name, model, onReady}); + const auto it = std::find(waitingModels.begin(), waitingModels.end(), name); + if (it != waitingModels.end()) waitingModels.erase(it); + } + --loadingCount; + }); + } else { + static_assert(always_false, "Unsupported type for loadAsyncModel"); + } + } catch (const std::exception &e) { + std::cerr << "[ResourceManager] Failed to load model " << name << ": " << e.what() << std::endl; + --loadingCount; + } + }); + } + + void processPending(); + + bool isAllLoaded() const; + + void waitForAll(); + + bool release(); + + void clearTextures(); + protected: - mutable std::mutex mutex {}; - std::unordered_map> texture; - std::unordered_map> shader; - std::unordered_map> model; - std::unordered_map> animationModel; - }; + mutable std::mutex mutex{}; + std::unordered_map > texture; + std::unordered_map > shader; + std::unordered_map > model; + std::unordered_map > animationModel; + std::unique_ptr loader; + mutable std::mutex pendingMutex; + + struct PendingItem { + std::string name; + std::vector> model; + std::function onReady; + }; + + struct PendingAnimation { + std::string name; + std::shared_ptr model; + std::function onReady; + }; + + struct PendingTexture { + std::string name; + vector buffer; + bool albedo; + std::function onReady; + }; + + struct PendingShader { + std::string name; + vector vertexBuffer; + vector geometryBuffer; + vector fragmentBuffer; + std::function onReady; + }; + + std::queue pendingAnim; + std::queue pending; + std::queue pendingTextures; + std::queue pendingShaders; + std::vector waitingModels; + std::vector threads; + + std::atomic loadingCount{0}; + }; } // Manager #endif //SNAKE3_RESOURCEMANAGER_H diff --git a/Manager/ShaderManager.cpp b/Manager/ShaderManager.cpp index b6f43800..13ef6907 100644 --- a/Manager/ShaderManager.cpp +++ b/Manager/ShaderManager.cpp @@ -1,18 +1,49 @@ #include "ShaderManager.h" +#include + namespace Manager { + ShaderManager::ShaderManager(const GLuint id) : id(id) { + } - ShaderManager::ShaderManager(GLuint id) : id(id) {} + GLuint ShaderManager::getId() const { + return id; + } void ShaderManager::use() const { glUseProgram(id); } + void ShaderManager::printActiveUniforms() const { + use(); + GLint uniformCount; + glGetProgramiv(id, GL_ACTIVE_UNIFORMS, &uniformCount); + + std::cout << "Active uniforms in program " << id << ": " << uniformCount << std::endl; + + GLchar name[256]; + for (GLint i = 0; i < uniformCount; ++i) { + GLint size; + GLenum type; + glGetActiveUniform(id, i, sizeof(name), nullptr, &size, &type, name); + GLint location = glGetUniformLocation(id, name); + std::cout << "Uniform #" << i << ": " << name + << " | Type: " << type + << " | Size: " << size + << " | Location: " << location << std::endl; + } + } + void ShaderManager::setBool(const string &name, bool value) const { - glUniform1i(glGetUniformLocation(id, name.c_str()), (int) value); + glUniform1i(glGetUniformLocation(id, name.c_str()), value); } void ShaderManager::setInt(const string &name, int value) const { + // GLint location = glGetUniformLocation(id, name.c_str()); + // if (location == -1) { + // std::cerr << "Uniform " << name << " not found in shader\n"; + // return; + // } glUniform1i(glGetUniformLocation(id, name.c_str()), value); } @@ -20,6 +51,14 @@ namespace Manager { glUniform1f(glGetUniformLocation(id, name.c_str()), value); } + void ShaderManager::setDouble(const string &name, double value) const { + glUniform1d(glGetUniformLocation(id, name.c_str()), value); + } + + void ShaderManager::setFloatArr(const string &name, const vector &floats) const { + glUniform1fv(glGetUniformLocation(id, name.c_str()), static_cast(floats.size()), floats.data()); + } + void ShaderManager::setVec2(const string &name, const glm::vec2 &value) const { glUniform2fv(glGetUniformLocation(id, name.c_str()), 1, &value[0]); } @@ -56,4 +95,131 @@ namespace Manager { glUniformMatrix4fv(glGetUniformLocation(id, name.c_str()), 1, GL_FALSE, &mat[0][0]); } -} // Manager \ No newline at end of file + void ShaderManager::setMat4Array(const string &name, const vector &matrices) const { + glUniformMatrix4fv( + glGetUniformLocation(id, name.c_str()), + static_cast(matrices.size()), + GL_FALSE, + matrices.empty() ? nullptr : reinterpret_cast(matrices.data()) + ); + } + + bool ShaderManager::hasUniform(const string &name) const { + return glGetUniformLocation(id, name.c_str()) != -1; + } + + template<> + void ShaderManager::setUniformArray(const std::string &name, const std::vector &values) const { + const GLint location = glGetUniformLocation(id, name.c_str()); + if (location == -1) return; + + glUniform1fv( + location, + static_cast(values.size()), + values.empty() ? nullptr : values.data() + ); + } + + template<> + void ShaderManager::setUniformArray< + glm::vec2>(const std::string &name, const std::vector &values) const { + const GLint location = glGetUniformLocation(id, name.c_str()); + if (location == -1) return; + + glUniform2fv( + location, + static_cast(values.size()), + values.empty() ? nullptr : reinterpret_cast(values.data()) + ); + } + + template<> + void ShaderManager::setUniformArray< + glm::vec3>(const std::string &name, const std::vector &values) const { + const GLint location = glGetUniformLocation(id, name.c_str()); + if (location == -1) return; + + glUniform3fv( + location, + static_cast(values.size()), + values.empty() ? nullptr : reinterpret_cast(values.data()) + ); + } + + template<> + void ShaderManager::setUniformArray< + glm::vec4>(const std::string &name, const std::vector &values) const { + const GLint location = glGetUniformLocation(id, name.c_str()); + if (location == -1) return; + + glUniform4fv( + location, + static_cast(values.size()), + values.empty() ? nullptr : reinterpret_cast(values.data()) + ); + } + + template<> + void ShaderManager::setUniformArray< + glm::mat4>(const std::string &name, const std::vector &values) const { + const GLint location = glGetUniformLocation(id, name.c_str()); + if (location == -1) return; + + glUniformMatrix4fv( + location, + static_cast(values.size()), + GL_FALSE, + values.empty() ? nullptr : reinterpret_cast(values.data()) + ); + } + + void ShaderManager::setUniformBlock(const std::string &name, const GLuint blockBinding) const { + const GLuint blockIndex = glGetUniformBlockIndex(id, name.c_str()); + if (blockIndex != GL_INVALID_INDEX) { + glUniformBlockBinding(id, blockIndex, blockBinding); + return; + } + + std::cerr << "Uniform block index " << name << " not found in shader\n"; + } + + void ShaderManager::setUniform(const std::string &name, const UniformValue &value) const { + GLint location = glGetUniformLocation(id, name.c_str()); + if (location == -1) { + std::cerr << "Uniform " << name << " not found in shader\n"; + return; + } + + std::visit([&](T0 &&val) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + glUniform1i(location, static_cast(val)); + } else if constexpr (std::is_same_v) { + glUniform1i(location, val); + } else if constexpr (std::is_same_v) { + glUniform1f(location, val); + } else if constexpr (std::is_same_v >) { + glUniform1fv(location, static_cast(val.size()), val.data()); + } else if constexpr (std::is_same_v) { + glUniform2fv(location, 1, &val[0]); + } else if constexpr (std::is_same_v) { + glUniform3fv(location, 1, &val[0]); + } else if constexpr (std::is_same_v) { + glUniform4fv(location, 1, &val[0]); + } else if constexpr (std::is_same_v) { + glUniformMatrix2fv(location, 1, GL_FALSE, &val[0][0]); + } else if constexpr (std::is_same_v) { + glUniformMatrix3fv(location, 1, GL_FALSE, &val[0][0]); + } else if constexpr (std::is_same_v) { + glUniformMatrix4fv(location, 1, GL_FALSE, &val[0][0]); + } else if constexpr (std::is_same_v >) { + glUniformMatrix4fv( + glGetUniformLocation(id, name.c_str()), + static_cast(val.size()), + GL_FALSE, + val.empty() ? nullptr : reinterpret_cast(val.data()) + ); + } + }, value); + } +} // Manager diff --git a/Manager/ShaderManager.h b/Manager/ShaderManager.h index 87e55d42..f7634b63 100644 --- a/Manager/ShaderManager.h +++ b/Manager/ShaderManager.h @@ -3,21 +3,36 @@ #include "../stdafx.h" #include -#include -#include +#include +#include #include using namespace std; +using UniformValue = std::variant< + bool, + int, + float, + glm::vec2, + glm::vec3, + glm::vec4, + glm::mat2, + glm::mat3, + glm::mat4 +>; + namespace Manager { class ShaderManager { public: explicit ShaderManager(GLuint id); void use() const; + void printActiveUniforms() const; void setBool(const string &name, bool value) const; void setInt(const string &name, int value) const; void setFloat(const string &name, float value) const; + void setDouble(const string &name, double value) const; + void setFloatArr(const string &name, const vector &floats) const; void setVec2(const string &name, const glm::vec2 &value) const; void setVec2(const string &name, float x, float y) const; void setVec3(const string &name, const glm::vec3 &value) const; @@ -27,10 +42,19 @@ namespace Manager { void setMat2(const string &name, const glm::mat2 &mat) const; void setMat3(const string &name, const glm::mat3 &mat) const; void setMat4(const string &name, const glm::mat4 &mat) const; + void setMat4Array(const string &name, const vector &matrices) const; + [[nodiscard]] bool hasUniform(const string &name) const; + + template + void setUniformArray(const std::string &name, const std::vector &values) const = delete; + + void setUniformBlock(const std::string &name, GLuint blockBinding) const; + + void setUniform(const std::string& name, const UniformValue& value) const; + [[nodiscard]] GLuint getId() const; protected: GLuint id; }; - } // Manager #endif //SNAKE3_SHADERMANAGER_H diff --git a/Manager/TextureManager.cpp b/Manager/TextureManager.cpp index e69853ff..667f26c7 100644 --- a/Manager/TextureManager.cpp +++ b/Manager/TextureManager.cpp @@ -1,7 +1,17 @@ #include #include "TextureManager.h" +#include "../Resource/TextureLoader.h" + namespace Manager { + TextureManager::TextureManager(const unsigned int id) { + textures.push_back(id); + } + + TextureManager::TextureManager(const aiTexel *buffer, const unsigned int size) { + this->buffer.resize(size); + std::memcpy(this->buffer.data(), buffer, size); + } TextureManager::~TextureManager() { for (auto texture: textures) { @@ -13,36 +23,60 @@ namespace Manager { void TextureManager::bind() const { int index = 0; - for (auto texture: textures) { + for (const auto texture: textures) { glActiveTexture(GL_TEXTURE0 + index); glBindTexture(GL_TEXTURE_2D, texture); + index++; } } void TextureManager::unbind() const { int index = 0; for (auto texture: textures) { - glActiveTexture(GL_TEXTURE0 + index); - glBindTexture(GL_TEXTURE_2D, 0); + unbind(index); + index++; } } - void TextureManager::addTexture(unsigned int id) { + void TextureManager::lazyLoad(const bool isAlbedo = true) { + if (textures.empty()) { + addTexture(Resource::TextureLoader::bindFromBuffer(buffer, isAlbedo)); + buffer.clear(); + } + } + + bool TextureManager::hasTexture() const { + return !textures.empty(); + } + + void TextureManager::addTexture(const unsigned int id) { textures.push_back(id); } - void TextureManager::bind(int index, int item) { + void TextureManager::bind(const int index, const int item) { glActiveTexture(GL_TEXTURE0 + index); - auto id = textures.begin() + item; + const auto id = textures.begin() + item; if (id < textures.end()) { glBindTexture(GL_TEXTURE_2D, (*id)); } } - void TextureManager::cubeBind() const { - glActiveTexture(GL_TEXTURE0); - auto id = textures.begin(); - glBindTexture(GL_TEXTURE_CUBE_MAP, (*id)); + void TextureManager::bindArr(const int index, const int item) { + glActiveTexture(GL_TEXTURE0 + index); + const auto id = textures.begin() + item; + if (id < textures.end()) { + glBindTexture(GL_TEXTURE_2D_ARRAY, (*id)); + } } + void TextureManager::unbind(const int index) const { + glActiveTexture(GL_TEXTURE0 + index); + glBindTexture(GL_TEXTURE_2D, 0); + } + + void TextureManager::cubeBind(const int index) const { + glActiveTexture(GL_TEXTURE0 + index); + const auto id = textures.begin(); + glBindTexture(GL_TEXTURE_CUBE_MAP, (*id)); + } } // Manager \ No newline at end of file diff --git a/Manager/TextureManager.h b/Manager/TextureManager.h index 5559e6ef..8b9abe16 100644 --- a/Manager/TextureManager.h +++ b/Manager/TextureManager.h @@ -2,24 +2,43 @@ #define SNAKE3_TEXTUREMANAGER_H #include +#include using namespace std; namespace Manager { - - class TextureManager { + class TextureManager final { public: - virtual ~TextureManager(); + explicit TextureManager() = default; + + explicit TextureManager(unsigned int id); + + explicit TextureManager(const aiTexel *buffer, unsigned int size); + + ~TextureManager(); void addTexture(unsigned int id); + void bind() const; - void cubeBind() const; + + void cubeBind(int index = 0) const; + void bind(int index, int item = 0); + + void bindArr(int index, int item); + + void unbind(int index) const; + void unbind() const; + + void lazyLoad(bool isAlbedo); + + [[nodiscard]] bool hasTexture() const; + protected: vector textures; + vector buffer; }; - } // Manager #endif //SNAKE3_TEXTUREMANAGER_H diff --git a/Particle/FireParticleSystem.cpp b/Particle/FireParticleSystem.cpp new file mode 100644 index 00000000..ead419c1 --- /dev/null +++ b/Particle/FireParticleSystem.cpp @@ -0,0 +1,128 @@ +#include "FireParticleSystem.h" +#include +#include + +// Konstruktor si uloží referenci na ResourceManager +FireParticleSystem::FireParticleSystem(ResourceManager& resourceManager, const int maxParticles) + : maxParticles(maxParticles), resourceManager(resourceManager) { + init(); +} + +void FireParticleSystem::init() { + particles.resize(maxParticles); + + // Vytvoření VAO a VBO pro quad (geometrie částice) + float quadVertices[] = { -0.5f, -0.5f, 0.0f, 0.0f, 0.5f, -0.5f, 1.0f, 0.0f, 0.5f, 0.5f, 1.0f, 1.0f, -0.5f, 0.5f, 0.0f, 1.0f }; + const unsigned int indices[] = { 0, 1, 2, 2, 3, 0 }; + unsigned int EBO; + glGenVertexArrays(1, &VAO); + glGenBuffers(1, &quadVBO); + glGenBuffers(1, &EBO); + glBindVertexArray(VAO); + glBindBuffer(GL_ARRAY_BUFFER, quadVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), quadVertices, GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), nullptr); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), reinterpret_cast(2 * sizeof(float))); + + glGenBuffers(1, &instanceVBO); + glBindBuffer(GL_ARRAY_BUFFER, instanceVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(FireParticle) * maxParticles, nullptr, GL_DYNAMIC_DRAW); + + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(FireParticle), static_cast(nullptr)); + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(FireParticle), reinterpret_cast(offsetof(FireParticle, color))); + glVertexAttribDivisor(2, 1); + glVertexAttribDivisor(3, 1); + + glBindVertexArray(0); +} + +void FireParticleSystem::render(const glm::mat4& view, const glm::mat4& projection) const { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + glDepthMask(GL_FALSE); + + const auto fireShader = this->resourceManager.getShader("fire"); + fireShader->use(); + fireShader->setMat4("view", view); + fireShader->setMat4("projection", projection); + fireShader->setFloat("particleSize", 0.008f); + + this->resourceManager.getTexture("fire.png")->bind(); + + std::vector liveParticles; + liveParticles.reserve(maxParticles); + for (const auto& p : particles) { + if (p.life > 0.0f) { + liveParticles.push_back(p); + } + } + + if (!liveParticles.empty()) { + glBindBuffer(GL_ARRAY_BUFFER, instanceVBO); + glBufferSubData(GL_ARRAY_BUFFER, 0, liveParticles.size() * sizeof(FireParticle), liveParticles.data()); + + glBindVertexArray(VAO); + glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr, liveParticles.size()); + glBindVertexArray(0); + } + + glDepthMask(GL_TRUE); + glDisable(GL_BLEND); +} + +void FireParticleSystem::update(float dt, glm::vec3 offset) { + // Každý snímek přidáme několik nových částic pro plynulý efekt + for(int i = 0; i < 5; ++i) { + addParticle(offset); + } + + for (int i = 0; i < maxParticles; ++i) { + FireParticle& p = particles[i]; + if (p.life > 0.0f) { + p.life -= dt; + p.position += p.velocity * dt; + + // Plynulý přechod barev a mizení + float lifeRatio = p.life / 1.5f; + + p.color.r = glm::mix(2.0f, 7.0f, lifeRatio); // Červená složka od 2.0 do 7.0 + p.color.g = glm::mix(0.5f, 4.5f, lifeRatio); // Zelená složka od 0.5 do 4.5 + p.color.b = glm::mix(0.1f, 1.5f, lifeRatio); // Modrá složka jen lehce + + p.color.a = glm::smoothstep(0.0f, 0.8f, lifeRatio); + } + } +} + +void FireParticleSystem::addParticle(const glm::vec3 offset) { + // Najdeme první "mrtvou" částici, kterou můžeme znovu použít + for (int i = 0; i < maxParticles; ++i) { + int index = (lastUsedParticle + i) % maxParticles; + if (particles[index].life <= 0.0f) { + FireParticle& p = particles[index]; + + // Rodí se v menší oblasti (poloměr 0.15f místo 0.3f) + glm::vec2 spawnDisk = glm::diskRand(0.015f); + //p.position = glm::vec3(0.038 + spawnDisk.x, -0.012f, -0.76f); + p.position = glm::vec3(spawnDisk.x, 0.046f, spawnDisk.y) + offset; + + // Rychlost je menší, aby oheň nebyl tak vysoký + p.velocity.x = glm::linearRand(-0.005f, 0.005f); // Menší rozptyl do stran + p.velocity.y = glm::linearRand(0.01f, 0.001f); // Menší rychlost nahoru + p.velocity.z = glm::linearRand(0.05f, 0.09f); + + p.color = glm::vec4(6.0f, 3.5f, 1.0f, 1.0f); + p.life = 1.5f; // Životnost + lastUsedParticle = index; + return; // Našli jsme volné místo, končíme + } + } + // Pokud jsme nenašli žádné volné místo, tak tento snímek žádnou částici nepřidáme. +} + diff --git a/Particle/FireParticleSystem.h b/Particle/FireParticleSystem.h new file mode 100644 index 00000000..cca8bc50 --- /dev/null +++ b/Particle/FireParticleSystem.h @@ -0,0 +1,38 @@ +#ifndef FIREPARTICLESYSTEM_H +#define FIREPARTICLESYSTEM_H + +#include +#include "../Manager/ResourceManager.h" + +using namespace Manager; + +// FireParticle struct (zůstává stejná) +struct FireParticle { + glm::vec3 position, velocity; + glm::vec4 color; + float life; +}; + +class FireParticleSystem { +public: + FireParticleSystem(ResourceManager& resourceManager, int maxParticles); + + void update(float dt, glm::vec3 offset); + void render(const glm::mat4& view, const glm::mat4& projection) const; + +private: + void init(); + void addParticle(glm::vec3 offset); + + std::vector particles; + int maxParticles; + + // VAO a VBO buffery jsou nyní členské proměnné + unsigned int VAO{}; + unsigned int quadVBO{}, instanceVBO{}; + + ResourceManager& resourceManager; + int lastUsedParticle = 0; +}; + +#endif //FIREPARTICLESYSTEM_H diff --git a/Particle/SmokeParticleSystem.cpp b/Particle/SmokeParticleSystem.cpp new file mode 100644 index 00000000..9f690677 --- /dev/null +++ b/Particle/SmokeParticleSystem.cpp @@ -0,0 +1,142 @@ +#include "SmokeParticleSystem.h" + +#include + +namespace Particle { + // Vytvoříme si pomocnou strukturu pro posílání dat do VBO, aby odpovídala atributům v shaderu + struct SmokeParticleGPUData { + glm::vec3 position; + glm::vec4 color; + glm::vec2 sizeAndRotation; + }; + + SmokeParticleSystem::SmokeParticleSystem(ResourceManager& resourceManager, int maxParticles) + : resourceManager(resourceManager), maxParticles(maxParticles) { + init(); + } + + void SmokeParticleSystem::init() { + particles.resize(maxParticles); + // Vytvoření VAO a VBO je identické jako u ohně + float quadVertices[] = { + -0.5f, -0.5f, 0.0f, 0.0f, 0.5f, -0.5f, 1.0f, 0.0f, 0.5f, 0.5f, 1.0f, 1.0f, -0.5f, 0.5f, 0.0f, 1.0f + }; + unsigned int indices[] = {0, 1, 2, 2, 3, 0}; + unsigned int EBO; + glGenVertexArrays(1, &VAO); + glGenBuffers(1, &quadVBO); + glGenBuffers(1, &EBO); + glBindVertexArray(VAO); + glBindBuffer(GL_ARRAY_BUFFER, quadVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), quadVertices, GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *) 0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *) (2 * sizeof(float))); + + + glGenBuffers(1, &instanceVBO); + glBindBuffer(GL_ARRAY_BUFFER, instanceVBO); + glBufferData(GL_ARRAY_BUFFER, maxParticles * sizeof(SmokeParticleGPUData), nullptr, GL_DYNAMIC_DRAW); + + glEnableVertexAttribArray(2); // Pozice + glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(SmokeParticleGPUData), (void*)offsetof(SmokeParticleGPUData, position)); + glEnableVertexAttribArray(3); // Barva + glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(SmokeParticleGPUData), (void*)offsetof(SmokeParticleGPUData, color)); + glEnableVertexAttribArray(4); // Velikost a rotace + glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, sizeof(SmokeParticleGPUData), (void*)offsetof(SmokeParticleGPUData, sizeAndRotation)); + + glVertexAttribDivisor(2, 1); + glVertexAttribDivisor(3, 1); + glVertexAttribDivisor(4, 1); + + glBindVertexArray(0); + } + + void SmokeParticleSystem::update(float dt, glm::vec3 offset) { + addParticle(offset); + + for (int i = 0; i < maxParticles; ++i) { + SmokeParticle& p = particles[i]; + if (p.life > 0.0f) { + p.life -= dt; + p.position += p.velocity * dt; + + // Lehký vítr zůstává + p.position.x += 0.0008f * dt; + + // **NOVINKA: Částice postupně zpomaluje (simulace odporu vzduchu)** + // Každý snímek ztratí kousek své rychlosti. + // Můžeš experimentovat s hodnotou 0.995f. Čím menší, tím dříve se zastaví. + p.velocity *= 0.995f; + + p.rotation += p.rotationSpeed * dt; + p.size += dt * 0.04f; + p.color.a = glm::smoothstep(0.0f, 0.4f, p.life / 3.0f); // Životnost je nyní kratší + } + } + } + + void SmokeParticleSystem::render(const glm::mat4& view, const glm::mat4& projection) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask(GL_FALSE); + + // Použijeme nový shader pro kouř + const auto smokeShader = this->resourceManager.getShader("smoke"); + smokeShader->use(); + smokeShader->setMat4("view", view); + smokeShader->setMat4("projection", projection); + smokeShader->setFloat("overallSize", 0.07f); + this->resourceManager.getTexture("smoke.png")->bind(); + + // Připravíme data pro GPU + std::vector gpuData; + gpuData.reserve(particles.size()); + for (const auto& p : particles) { + if (p.life > 0.0f) { + gpuData.push_back({p.position, p.color, {p.size, p.rotation}}); + } + } + + if (!gpuData.empty()) { + glBindBuffer(GL_ARRAY_BUFFER, instanceVBO); + glBufferSubData(GL_ARRAY_BUFFER, 0, gpuData.size() * sizeof(SmokeParticleGPUData), gpuData.data()); + glBindVertexArray(VAO); + glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0, gpuData.size()); + glBindVertexArray(0); + } + + glDepthMask(GL_TRUE); + glDisable(GL_BLEND); + } + + void SmokeParticleSystem::addParticle(glm::vec3 offset) { + for (int i = 0; i < maxParticles; ++i) { + int index = (lastUsedParticle + i) % maxParticles; + if (particles[index].life <= 0.0f) { + SmokeParticle& p = particles[index]; + + glm::vec2 spawnDisk = glm::diskRand(0.004f); + p.position = glm::vec3(spawnDisk.x, 0.05f, spawnDisk.y) + offset; + + p.velocity.x = glm::linearRand(-0.05f, 0.01f); + p.velocity.y = glm::linearRand(0.001f, 0.025f); // Kouř stoupá mnohem pomaleji + p.velocity.z = glm::linearRand(0.05f, 0.09f); + + const float shade = glm::linearRand(0.90f, 1.0f); + p.color = glm::vec4(shade, shade, shade, glm::linearRand(0.03f, 0.08f)); + p.life = 2.0f; + + p.size = glm::linearRand(0.08f, 0.11f); + p.rotation = glm::linearRand(0.0f, 2.0f * 3.14159f); + p.rotationSpeed = glm::linearRand(-0.2f, 0.2f); + + lastUsedParticle = index; + return; + } + } + } +} // Particle diff --git a/Particle/SmokeParticleSystem.h b/Particle/SmokeParticleSystem.h new file mode 100644 index 00000000..af59d372 --- /dev/null +++ b/Particle/SmokeParticleSystem.h @@ -0,0 +1,37 @@ +#ifndef SMOKEPARTICLESYSTEM_H +#define SMOKEPARTICLESYSTEM_H + +#include "FireParticleSystem.h" + +namespace Particle { + // Vytvoříme novou, bohatší strukturu pro částice kouře + struct SmokeParticle { + glm::vec3 position, velocity; + glm::vec4 color; + float life; + float size; + float rotation; + float rotationSpeed; + }; + + class SmokeParticleSystem { + public: + SmokeParticleSystem(ResourceManager& resourceManager, int maxParticles); + void update(float dt, glm::vec3 offset); + void render(const glm::mat4& view, const glm::mat4& projection); + private: + void init(); + void addParticle(glm::vec3 offset); + + std::vector particles; + int maxParticles; + + unsigned int VAO{}; + unsigned int quadVBO{}, instanceVBO{}; + + ResourceManager& resourceManager; + int lastUsedParticle = 0; + }; +} + +#endif //SMOKEPARTICLESYSTEM_H diff --git a/Physic/Algorithms/CollisionAlgorithms.cpp b/Physic/Algorithms/CollisionAlgorithms.cpp new file mode 100644 index 00000000..d90675dd --- /dev/null +++ b/Physic/Algorithms/CollisionAlgorithms.cpp @@ -0,0 +1,340 @@ +#include "CollisionAlgorithms.h" + +#include "../BoxShape.h" +#include "../SphereShape.h" +#include "../CapsuleShape.h" +#include "../CylinderShape.h" + +namespace { + // Helper function to find closest point on segment p0-p1 to point q + glm::vec3 ClosestPointOnSegment(const glm::vec3& p0, const glm::vec3& p1, const glm::vec3& q) { + const glm::vec3 v = p1 - p0; + const float t = glm::dot(q - p0, v) / glm::dot(v, v); + + return p0 + glm::clamp(t, 0.0f, 1.0f) * v; + } + + // Helper to find closest points between two segments + void ClosestPointsTwoSegments(const glm::vec3& p1, const glm::vec3& q1, + const glm::vec3& p2, const glm::vec3& q2, + glm::vec3& c1, glm::vec3& c2) { + const glm::vec3 d1 = q1 - p1; + const glm::vec3 d2 = q2 - p2; + const glm::vec3 r = p1 - p2; + const float a = glm::dot(d1, d1); + const float e = glm::dot(d2, d2); + const float f = glm::dot(d2, r); + + constexpr float EPSILON = 1e-6f; + float s, t; + + if (a <= EPSILON && e <= EPSILON) { + s = t = 0.0f; + c1 = p1; + c2 = p2; + return; + } + if (a <= EPSILON) { + s = 0.0f; + t = glm::clamp(f / e, 0.0f, 1.0f); + } else { + const float c = glm::dot(d1, r); + if (e <= EPSILON) { + t = 0.0f; + s = glm::clamp(-c / a, 0.0f, 1.0f); + } else { + const float b = glm::dot(d1, d2); + const float denom = a * e - b * b; + if (denom != 0.0f) { + s = glm::clamp((b * f - c * e) / denom, 0.0f, 1.0f); + } else { + s = 0.0f; + } + t = (b * s + f) / e; + if (t < 0.0f) { + t = 0.0f; + s = glm::clamp(-c / a, 0.0f, 1.0f); + } else if (t > 1.0f) { + t = 1.0f; + s = glm::clamp((b - c) / a, 0.0f, 1.0f); + } + } + } + c1 = p1 + d1 * s; + c2 = p2 + d2 * t; + } +} + +bool Algorithms::BoxVsBox(const CollisionEntry &entA, const CollisionEntry &entB) { + const auto obbA = std::static_pointer_cast( + entA.shapeNode->getShape())->BuildOBB(entA.parentObject->getModelMatrix() * entA.shapeNode->getModelMatrix() + ); + const auto obbB = std::static_pointer_cast( + entB.shapeNode->getShape())->BuildOBB(entB.parentObject->getModelMatrix() * entB.shapeNode->getModelMatrix() + ); + + const glm::vec3 T = obbB.center - obbA.center; + + glm::mat3 R; + glm::mat3 AbsR; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + R[i][j] = glm::dot(obbA.axes[i], obbB.axes[j]); + AbsR[i][j] = glm::abs(R[i][j]) + 1e-6f; + } + } + + float ra, rb; + + // Test axes L = A0, L = A1, L = A2 + for (int i = 0; i < 3; i++) { + ra = obbA.halfExtents[i]; + rb = obbB.halfExtents[0] * AbsR[i][0] + obbB.halfExtents[1] * AbsR[i][1] + obbB.halfExtents[2] * AbsR[i][2]; + if (glm::abs(glm::dot(T, obbA.axes[i])) > ra + rb) return false; + } + + // Test axes L = B0, L = B1, L = B2 + for (int i = 0; i < 3; i++) { + ra = obbA.halfExtents[0] * AbsR[0][i] + obbA.halfExtents[1] * AbsR[1][i] + obbA.halfExtents[2] * AbsR[2][i]; + rb = obbB.halfExtents[i]; + if (glm::abs(glm::dot(T, obbB.axes[i])) > ra + rb) return false; + } + + // Test axis L = A0 x B0 + ra = obbA.halfExtents[1] * AbsR[2][0] + obbA.halfExtents[2] * AbsR[1][0]; + rb = obbB.halfExtents[1] * AbsR[0][2] + obbB.halfExtents[2] * AbsR[0][1]; + if (glm::abs(glm::dot(T, obbA.axes[2]) * R[1][0] - glm::dot(T, obbA.axes[1]) * R[2][0]) > ra + rb) return false; + + // Test axis L = A0 x B1 + ra = obbA.halfExtents[1] * AbsR[2][1] + obbA.halfExtents[2] * AbsR[1][1]; + rb = obbB.halfExtents[0] * AbsR[0][2] + obbB.halfExtents[2] * AbsR[0][0]; + if (glm::abs(glm::dot(T, obbA.axes[2]) * R[1][1] - glm::dot(T, obbA.axes[1]) * R[2][1]) > ra + rb) return false; + + // Test axis L = A0 x B2 + ra = obbA.halfExtents[1] * AbsR[2][2] + obbA.halfExtents[2] * AbsR[1][2]; + rb = obbB.halfExtents[0] * AbsR[0][1] + obbB.halfExtents[1] * AbsR[0][0]; + if (glm::abs(glm::dot(T, obbA.axes[2]) * R[1][2] - glm::dot(T, obbA.axes[1]) * R[2][2]) > ra + rb) return false; + + // Test axis L = A1 x B0 + ra = obbA.halfExtents[0] * AbsR[2][0] + obbA.halfExtents[2] * AbsR[0][0]; + rb = obbB.halfExtents[1] * AbsR[1][2] + obbB.halfExtents[2] * AbsR[1][1]; + if (glm::abs(glm::dot(T, obbA.axes[0]) * R[2][0] - glm::dot(T, obbA.axes[2]) * R[0][0]) > ra + rb) return false; + + // Test axis L = A1 x B1 + ra = obbA.halfExtents[0] * AbsR[2][1] + obbA.halfExtents[2] * AbsR[0][1]; + rb = obbB.halfExtents[0] * AbsR[1][2] + obbB.halfExtents[2] * AbsR[1][0]; + if (glm::abs(glm::dot(T, obbA.axes[0]) * R[2][1] - glm::dot(T, obbA.axes[2]) * R[0][1]) > ra + rb) return false; + + // Test axis L = A1 x B2 + ra = obbA.halfExtents[0] * AbsR[2][2] + obbA.halfExtents[2] * AbsR[0][2]; + rb = obbB.halfExtents[0] * AbsR[1][1] + obbB.halfExtents[1] * AbsR[1][0]; + if (glm::abs(glm::dot(T, obbA.axes[0]) * R[2][2] - glm::dot(T, obbA.axes[2]) * R[0][2]) > ra + rb) return false; + + // Test axis L = A2 x B0 + ra = obbA.halfExtents[0] * AbsR[1][0] + obbA.halfExtents[1] * AbsR[0][0]; + rb = obbB.halfExtents[1] * AbsR[2][2] + obbB.halfExtents[2] * AbsR[2][1]; + if (glm::abs(glm::dot(T, obbA.axes[1]) * R[0][0] - glm::dot(T, obbA.axes[0]) * R[1][0]) > ra + rb) return false; + + // Test axis L = A2 x B1 + ra = obbA.halfExtents[0] * AbsR[1][1] + obbA.halfExtents[1] * AbsR[0][1]; + rb = obbB.halfExtents[0] * AbsR[2][2] + obbB.halfExtents[2] * AbsR[2][0]; + if (glm::abs(glm::dot(T, obbA.axes[1]) * R[0][1] - glm::dot(T, obbA.axes[0]) * R[1][1]) > ra + rb) return false; + + // Test axis L = A2 x B2 + ra = obbA.halfExtents[0] * AbsR[1][2] + obbA.halfExtents[1] * AbsR[0][2]; + rb = obbB.halfExtents[0] * AbsR[2][1] + obbB.halfExtents[1] * AbsR[2][0]; + if (glm::abs(glm::dot(T, obbA.axes[1]) * R[0][2] - glm::dot(T, obbA.axes[0]) * R[1][2]) > ra + rb) return false; + + return true; +} + +bool Algorithms::SphereVsSphere(const CollisionEntry& entA, const CollisionEntry& entB) { + auto sphereA = std::static_pointer_cast(entA.shapeNode->getShape()) + ->BuildSphere(entA.parentObject->getModelMatrix() * entA.shapeNode->getModelMatrix()); + + auto sphereB = std::static_pointer_cast(entB.shapeNode->getShape()) + ->BuildSphere(entB.parentObject->getModelMatrix() * entB.shapeNode->getModelMatrix()); + + const float distanceSq = glm::distance2(sphereA.center, sphereB.center); + const float radiusSum = sphereA.radius + sphereB.radius; + const float radiusSumSq = radiusSum * radiusSum; + + return distanceSq <= radiusSumSq; +} + +bool Algorithms::BoxVsSphere(const CollisionEntry& entBox, const CollisionEntry& entSphere) { + auto box = std::static_pointer_cast(entBox.shapeNode->getShape()) + ->BuildOBB(entBox.parentObject->getModelMatrix() * entBox.shapeNode->getModelMatrix()); + + auto sphere = std::static_pointer_cast(entSphere.shapeNode->getShape()) + ->BuildSphere(entSphere.parentObject->getModelMatrix() * entSphere.shapeNode->getModelMatrix()); + + const glm::vec3 relCenter = sphere.center - box.center; + const auto localCenter = glm::vec3( + glm::dot(relCenter, box.axes[0]), + glm::dot(relCenter, box.axes[1]), + glm::dot(relCenter, box.axes[2]) + ); + + glm::vec3 closestPoint; + closestPoint.x = glm::clamp(localCenter.x, -box.halfExtents.x, box.halfExtents.x); + closestPoint.y = glm::clamp(localCenter.y, -box.halfExtents.y, box.halfExtents.y); + closestPoint.z = glm::clamp(localCenter.z, -box.halfExtents.z, box.halfExtents.z); + + const float distanceSq = glm::distance2(closestPoint, localCenter); + + return distanceSq <= (sphere.radius * sphere.radius); +} + +bool Algorithms::SphereVsCapsule(const CollisionEntry& entSphere, const CollisionEntry& entCapsule) { + auto sphere = std::static_pointer_cast(entSphere.shapeNode->getShape()) + ->BuildSphere(entSphere.parentObject->getModelMatrix() * entSphere.shapeNode->getModelMatrix()); + + auto capsule = std::static_pointer_cast(entCapsule.shapeNode->getShape()) + ->BuildCapsule(entCapsule.parentObject->getModelMatrix() * entCapsule.shapeNode->getModelMatrix()); + + const glm::vec3 closestPoint = ClosestPointOnSegment(capsule.p0, capsule.p1, sphere.center); + const float distSq = glm::distance2(sphere.center, closestPoint); + const float radiusSum = sphere.radius + capsule.radius; + + return distSq <= (radiusSum * radiusSum); +} + +bool Algorithms::CapsuleVsCapsule(const CollisionEntry& entA, const CollisionEntry& entB) { + auto capA = std::static_pointer_cast(entA.shapeNode->getShape()) + ->BuildCapsule(entA.parentObject->getModelMatrix() * entA.shapeNode->getModelMatrix()); + + auto capB = std::static_pointer_cast(entB.shapeNode->getShape()) + ->BuildCapsule(entB.parentObject->getModelMatrix() * entB.shapeNode->getModelMatrix()); + + glm::vec3 c1, c2; + ClosestPointsTwoSegments(capA.p0, capA.p1, capB.p0, capB.p1, c1, c2); + const float distSq = glm::distance2(c1, c2); + const float radiusSum = capA.radius + capB.radius; + + return distSq <= (radiusSum * radiusSum); +} + +bool Algorithms::BoxVsCapsule(const CollisionEntry& entBox, const CollisionEntry& entCapsule) { + auto box = std::static_pointer_cast(entBox.shapeNode->getShape()) + ->BuildOBB(entBox.parentObject->getModelMatrix() * entBox.shapeNode->getModelMatrix()); + + auto capsule = std::static_pointer_cast(entCapsule.shapeNode->getShape()) + ->BuildCapsule(entCapsule.parentObject->getModelMatrix() * entCapsule.shapeNode->getModelMatrix()); + + // Or use the OBB data we have: + auto toLocal = [&](glm::vec3 worldP) { + const glm::vec3 rel = worldP - box.center; + return glm::vec3( + glm::dot(rel, box.axes[0]), + glm::dot(rel, box.axes[1]), + glm::dot(rel, box.axes[2]) + ); + }; + + glm::vec3 localP0 = toLocal(capsule.p0); + glm::vec3 localP1 = toLocal(capsule.p1); + glm::vec3 closestToCenter = ClosestPointOnSegment(localP0, localP1, glm::vec3(0.0f)); + glm::vec3 closestOnBox = glm::clamp(closestToCenter, -box.halfExtents, box.halfExtents); + glm::vec3 closestOnSegmentLocal = ClosestPointOnSegment(localP0, localP1, closestOnBox); + glm::vec3 finalClosestOnBox = glm::clamp(closestOnSegmentLocal, -box.halfExtents, box.halfExtents); + + float distSq = glm::distance2(closestOnSegmentLocal, finalClosestOnBox); + + return distSq <= (capsule.radius * capsule.radius); +} + +bool Algorithms::SphereVsCylinder(const CollisionEntry& entSphere, const CollisionEntry& entCylinder) { + auto sphere = std::static_pointer_cast(entSphere.shapeNode->getShape()) + ->BuildSphere(entSphere.parentObject->getModelMatrix() * entSphere.shapeNode->getModelMatrix()); + + auto cylinder = std::static_pointer_cast(entCylinder.shapeNode->getShape()) + ->BuildCylinder(entCylinder.parentObject->getModelMatrix() * entCylinder.shapeNode->getModelMatrix()); + + + const glm::vec3 d = cylinder.p1 - cylinder.p0; + const float hSq = glm::dot(d, d); + const float t = glm::dot(sphere.center - cylinder.p0, d) / hSq; + + if (t < 0.0f || t > 1.0f) { + // Sphere is beyond the flat ends. Check distance to the end disks. + // For a true cylinder, this is more complex, but Sphere vs Disk is: + const glm::vec3 endPoint = (t < 0.0f) ? cylinder.p0 : cylinder.p1; + const glm::vec3 rel = sphere.center - endPoint; + const float distToPlane = glm::abs(glm::dot(rel, glm::normalize(d))); + if (distToPlane > sphere.radius) return false; + + const float planarDistSq = glm::length2(rel - glm::dot(rel, glm::normalize(d)) * glm::normalize(d)); + if (planarDistSq > cylinder.radius * cylinder.radius) { + // Closest point is on the edge of the disk + const float planarDist = glm::sqrt(planarDistSq); + const float distToEdgeSq = glm::pow(planarDist - cylinder.radius, 2.0f) + distToPlane * distToPlane; + return distToEdgeSq <= (sphere.radius * sphere.radius); + } + return true; + } + + // Sphere is between the end planes + float distSqToAxis = glm::distance2(sphere.center, cylinder.p0 + t * d); + + return distSqToAxis <= (sphere.radius + cylinder.radius) * (sphere.radius + cylinder.radius); +} + +bool Algorithms::BoxVsCylinder(const CollisionEntry& entBox, const CollisionEntry& entCylinder) { + auto box = std::static_pointer_cast(entBox.shapeNode->getShape()) + ->BuildOBB(entBox.parentObject->getModelMatrix() * entBox.shapeNode->getModelMatrix()); + + auto cylinder = std::static_pointer_cast(entCylinder.shapeNode->getShape()) + ->BuildCylinder(entCylinder.parentObject->getModelMatrix() * entCylinder.shapeNode->getModelMatrix()); + + auto toLocal = [&](const glm::vec3 worldP) { + const glm::vec3 rel = worldP - box.center; + return glm::vec3( + glm::dot(rel, box.axes[0]), + glm::dot(rel, box.axes[1]), + glm::dot(rel, box.axes[2]) + ); + }; + + glm::vec3 localP0 = toLocal(cylinder.p0); + glm::vec3 localP1 = toLocal(cylinder.p1); + + glm::vec3 closestToCenter = ClosestPointOnSegment(localP0, localP1, glm::vec3(0.0f)); + glm::vec3 closestOnBox = glm::clamp(closestToCenter, -box.halfExtents, box.halfExtents); + glm::vec3 closestOnSegmentLocal = ClosestPointOnSegment(localP0, localP1, closestOnBox); + glm::vec3 finalClosestOnBox = glm::clamp(closestOnSegmentLocal, -box.halfExtents, box.halfExtents); + + float distSq = glm::distance2(closestOnSegmentLocal, finalClosestOnBox); + + return distSq <= cylinder.radius * cylinder.radius; +} + +bool Algorithms::CapsuleVsCylinder(const CollisionEntry& entCapsule, const CollisionEntry& entCylinder) { + auto cap = std::static_pointer_cast(entCapsule.shapeNode->getShape()) + ->BuildCapsule(entCapsule.parentObject->getModelMatrix() * entCapsule.shapeNode->getModelMatrix()); + + auto cyl = std::static_pointer_cast(entCylinder.shapeNode->getShape()) + ->BuildCylinder(entCylinder.parentObject->getModelMatrix() * entCylinder.shapeNode->getModelMatrix()); + + glm::vec3 c1, c2; + ClosestPointsTwoSegments(cap.p0, cap.p1, cyl.p0, cyl.p1, c1, c2); + const float distSq = glm::distance2(c1, c2); + const float radiusSum = cap.radius + cyl.radius; + + return distSq <= radiusSum * radiusSum; +} + +bool Algorithms::CylinderVsCylinder(const CollisionEntry& entA, const CollisionEntry& entB) { + auto cylA = std::static_pointer_cast(entA.shapeNode->getShape()) + ->BuildCylinder(entA.parentObject->getModelMatrix() * entA.shapeNode->getModelMatrix()); + + auto cylB = std::static_pointer_cast(entB.shapeNode->getShape()) + ->BuildCylinder(entB.parentObject->getModelMatrix() * entB.shapeNode->getModelMatrix()); + + glm::vec3 c1, c2; + ClosestPointsTwoSegments(cylA.p0, cylA.p1, cylB.p0, cylB.p1, c1, c2); + const float distSq = glm::distance2(c1, c2); + const float radiusSum = cylA.radius + cylB.radius; + + return distSq <= radiusSum * radiusSum; +} diff --git a/Physic/Algorithms/CollisionAlgorithms.h b/Physic/Algorithms/CollisionAlgorithms.h new file mode 100644 index 00000000..f3cdb01e --- /dev/null +++ b/Physic/Algorithms/CollisionAlgorithms.h @@ -0,0 +1,20 @@ +#ifndef SNAKE3_COLLISIONALGORITHMS_H +#define SNAKE3_COLLISIONALGORITHMS_H + +#include "../CollisionSystem3D.h" + +namespace Physic::Algorithms { + bool BoxVsBox(const CollisionEntry& a, const CollisionEntry& b); + bool SphereVsSphere(const CollisionEntry& entA, const CollisionEntry& entB); + bool BoxVsSphere(const CollisionEntry& entBox, const CollisionEntry& entSphere); + bool SphereVsCapsule(const CollisionEntry& entSphere, const CollisionEntry& entCapsule); + bool CapsuleVsCapsule(const CollisionEntry& entA, const CollisionEntry& entB); + bool BoxVsCapsule(const CollisionEntry& entBox, const CollisionEntry& entCapsule); + bool SphereVsCylinder(const CollisionEntry& entSphere, const CollisionEntry& entCylinder); + bool BoxVsCylinder(const CollisionEntry& entBox, const CollisionEntry& entCylinder); + bool CapsuleVsCylinder(const CollisionEntry& entCapsule, const CollisionEntry& entCylinder); + bool CylinderVsCylinder(const CollisionEntry& entA, const CollisionEntry& entB); +} // Algorithms +// Physic + +#endif //SNAKE3_COLLISIONALGORITHMS_H \ No newline at end of file diff --git a/Physic/BoxShape.cpp b/Physic/BoxShape.cpp new file mode 100644 index 00000000..2b28ffbb --- /dev/null +++ b/Physic/BoxShape.cpp @@ -0,0 +1,56 @@ +#include "BoxShape.h" + +#include "../Renderer/Opengl/Model/Standard/BoxMesh.h" + +namespace Physic { + BoxShape::BoxShape(const shared_ptr &resourceManager, const shared_ptr &contextState, + const glm::vec3 boxSize) : size(boxSize) { + const auto shader = resourceManager ? resourceManager->getShader("basicShader") : nullptr; + const auto shadowsShader = resourceManager ? resourceManager->getShader("shadowDepthShader") : nullptr; + // material + material = make_shared(shader, shadowsShader); + material->setNormalEnabled(false); + material->setAlpha(0.2); + material->setBlending(Blending::Translucent); + // BoxMesh size is already width/height/depth + auto boxMesh = make_shared(shader, size.x, size.y, size.z); + boxMesh->setMaterial(material); + meshNode = make_shared(contextState, boxMesh, resourceManager); + } + + void BoxShape::render(const shared_ptr &camera, const glm::mat4 &projection, const glm::mat4 t) { + const glm::vec3 color = isColliding() ? glm::vec3(1.0f, 0.2f, 0.2f) : glm::vec3(0.2f, 1.0f, 1.0f); + material->setColor(color); + + meshNode->setScale(glm::vec3(1.0f)); + meshNode->setPosition(glm::vec3(0.0f)); + meshNode->render(camera, projection, 0.0f, t, false); + } + + BoxShape::OBB BoxShape::BuildOBB(const glm::mat4 &modelMatrix) const { + OBB obb{}; + + obb.center = glm::vec3(modelMatrix * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); + + // Osy v S*T*R jsou v prvních třech sloupcích, ale jsou škálované. + obb.axes[0] = glm::normalize(glm::vec3(modelMatrix[0])); + obb.axes[1] = glm::normalize(glm::vec3(modelMatrix[1])); + obb.axes[2] = glm::normalize(glm::vec3(modelMatrix[2])); + + const auto scale = glm::vec3( + glm::length(glm::vec3(modelMatrix[0])), + glm::length(glm::vec3(modelMatrix[1])), + glm::length(glm::vec3(modelMatrix[2])) + ); + + obb.halfExtents.x = (this->size.x * 0.5f) * scale.x; + obb.halfExtents.y = (this->size.y * 0.5f) * scale.y; + obb.halfExtents.z = (this->size.z * 0.5f) * scale.z; + + return obb; + } + + AABB BoxShape::calculateAABB(const glm::mat4 &modelMatrix) { + return CalculateAABB(modelMatrix, this->size * 0.5f); + } +} // Physic diff --git a/Physic/BoxShape.h b/Physic/BoxShape.h new file mode 100644 index 00000000..e5b8babb --- /dev/null +++ b/Physic/BoxShape.h @@ -0,0 +1,46 @@ +#ifndef SNAKE3_BOXSHAPE_H +#define SNAKE3_BOXSHAPE_H + +#include "Shape.h" +#include + +#include "../Renderer/Opengl/Model/Standard/MeshNode3D.h" + +using namespace Model; + +namespace Physic { + class BoxShape : public Shape { + struct OBB { + glm::vec3 center; // Střed boxu ve světě + glm::vec3 axes[3]; // Normalizované směrové vektory (Right, Up, Forward) + glm::vec3 halfExtents; // Poloviční velikost boxu (započítaný scale) + }; + + public: + explicit BoxShape( + const shared_ptr &resourceManager, + const shared_ptr &contextState, + glm::vec3 boxSize = glm::vec3(1.0f) + ); + + ShapeType getType() override { return ShapeType::Box; } + + void render(const shared_ptr &camera, const glm::mat4 &projection, glm::mat4 t) override; + + [[nodiscard]] OBB BuildOBB(const glm::mat4 &modelMatrix) const; + + AABB calculateAABB(const glm::mat4 &modelMatrix) override; + + [[nodiscard]] const shared_ptr &getMeshNode() const { return meshNode; } + + private: + + glm::vec3 size; + + shared_ptr meshNode; + + shared_ptr material; + }; +} // Physic + +#endif //SNAKE3_BOXSHAPE_H diff --git a/Physic/CapsuleShape.cpp b/Physic/CapsuleShape.cpp new file mode 100644 index 00000000..08ee251d --- /dev/null +++ b/Physic/CapsuleShape.cpp @@ -0,0 +1,63 @@ +#include "CapsuleShape.h" +#include "../Renderer/Opengl/Model/Standard/CapsuleMesh.h" + +namespace Physic { + CapsuleShape::CapsuleShape(const shared_ptr &resourceManager, + const shared_ptr &contextState, + const float radius, const float height) + : radius(radius), height(height) { + + const auto shader = resourceManager ? resourceManager->getShader("basicShader") : nullptr; + const auto shadowsShader = resourceManager ? resourceManager->getShader("shadowDepthShader") : nullptr; + + material = make_shared(shader, shadowsShader); + material->setNormalEnabled(false); + material->setAlpha(0.2); + material->setBlending(Blending::Translucent); + + auto capsuleMesh = make_shared(shader, height, radius); + capsuleMesh->setMaterial(material); + meshNode = make_shared(contextState, capsuleMesh, resourceManager); + } + + CapsuleShape::CapsuleWorldData CapsuleShape::BuildCapsule(const glm::mat4 &modelMatrix) const { + CapsuleWorldData data{}; + + const auto scale = glm::vec3( + glm::length(glm::vec3(modelMatrix[0])), + glm::length(glm::vec3(modelMatrix[1])), + glm::length(glm::vec3(modelMatrix[2])) + ); + + float cylinderHeight = height - 2.0f * radius; + if (cylinderHeight < 0) cylinderHeight = 0; + + const float halfCylHeight = (cylinderHeight * 0.5f) * scale.y; + + const auto center = glm::vec3(modelMatrix * glm::vec4(0, 0, 0, 1)); + const glm::vec3 up = glm::normalize(glm::vec3(modelMatrix[1])); + + data.p0 = center - up * halfCylHeight; + data.p1 = center + up * halfCylHeight; + data.radius = radius * glm::max(scale.x, scale.z); + + return data; + } + + AABB CapsuleShape::calculateAABB(const glm::mat4 &modelMatrix) { + auto data = BuildCapsule(modelMatrix); + const glm::vec3 min = glm::min(data.p0, data.p1) - glm::vec3(data.radius); + const glm::vec3 max = glm::max(data.p0, data.p1) + glm::vec3(data.radius); + + return {min, max}; + } + + void CapsuleShape::render(const shared_ptr &camera, const glm::mat4 &projection, glm::mat4 t) { + const glm::vec3 color = isColliding() ? glm::vec3(1.0f, 0.2f, 0.2f) : glm::vec3(0.2f, 1.0f, 1.0f); + material->setColor(color); + + meshNode->setScale(glm::vec3(1.0f)); + meshNode->setPosition(glm::vec3(0.0f)); + meshNode->render(camera, projection, 0.0f, t, false); + } +} // Physic diff --git a/Physic/CapsuleShape.h b/Physic/CapsuleShape.h new file mode 100644 index 00000000..1bcbd31b --- /dev/null +++ b/Physic/CapsuleShape.h @@ -0,0 +1,43 @@ +#ifndef SNAKE3_CAPSULESHAPE_H +#define SNAKE3_CAPSULESHAPE_H + +#include "Shape.h" +#include "../Renderer/Opengl/Model/Standard/MeshNode3D.h" + +using namespace Model; + +namespace Physic { + class CapsuleShape : public Shape { + public: + struct CapsuleWorldData { + glm::vec3 p0; // Start point of the segment (bottom center) + glm::vec3 p1; // End point of the segment (top center) + float radius; + }; + + private: + float radius; + float height; // Total height including hemispherical ends + + shared_ptr meshNode; + shared_ptr material; + + public: + explicit CapsuleShape(const shared_ptr &resourceManager, + const shared_ptr &contextState, + float radius = 0.5f, float height = 2.0f); + + ShapeType getType() override { return ShapeType::Capsule; } + + [[nodiscard]] float getRadius() const { return radius; } + [[nodiscard]] float getHeight() const { return height; } + + [[nodiscard]] CapsuleWorldData BuildCapsule(const glm::mat4& modelMatrix) const; + + AABB calculateAABB(const glm::mat4& modelMatrix) override; + + void render(const shared_ptr &camera, const glm::mat4 &projection, glm::mat4 t) override; + }; +} // Physic + +#endif //SNAKE3_CAPSULESHAPE_H diff --git a/Physic/CollisionCheck.cpp b/Physic/CollisionCheck.cpp new file mode 100644 index 00000000..0a41edea --- /dev/null +++ b/Physic/CollisionCheck.cpp @@ -0,0 +1,58 @@ +#include "CollisionCheck.h" + +#include "Algorithms/CollisionAlgorithms.h" + +namespace Physic { + bool CollisionCheck::IntersectExact(const CollisionEntry &a, const CollisionEntry &b) { + const ShapeType typeA = a.shapeNode->getShape()->getType(); + const ShapeType typeB = b.shapeNode->getShape()->getType(); + + if (typeA > typeB) { + return IntersectExact(b, a); + } + + switch (typeA) { + case ShapeType::Box: + if (typeB == ShapeType::Box) + return Algorithms::BoxVsBox(a, b); + if (typeB == ShapeType::Sphere) + return Algorithms::BoxVsSphere(a, b); + if (typeB == ShapeType::Capsule) + return Algorithms::BoxVsCapsule(a, b); + if (typeB == ShapeType::Cylinder) + return Algorithms::BoxVsCylinder(a, b); + break; + + case ShapeType::Sphere: + if (typeB == ShapeType::Sphere) + return Algorithms::SphereVsSphere(a, b); + if (typeB == ShapeType::Capsule) + return Algorithms::SphereVsCapsule(a, b); + if (typeB == ShapeType::Cylinder) + return Algorithms::SphereVsCylinder(a, b); + break; + + case ShapeType::Capsule: + if (typeB == ShapeType::Capsule) + return Algorithms::CapsuleVsCapsule(a, b); + if (typeB == ShapeType::Box) + return Algorithms::BoxVsCapsule(b, a); + if (typeB == ShapeType::Cylinder) + return Algorithms::CapsuleVsCylinder(a, b); + break; + + case ShapeType::Cylinder: + if (typeB == ShapeType::Cylinder) + return Algorithms::CylinderVsCylinder(a, b); + if (typeB == ShapeType::Box) + return Algorithms::BoxVsCylinder(b, a); + if (typeB == ShapeType::Sphere) + return Algorithms::SphereVsCylinder(b, a); + if (typeB == ShapeType::Capsule) + return Algorithms::CapsuleVsCylinder(b, a); + break; + } + + return false; + } +} diff --git a/Physic/CollisionCheck.h b/Physic/CollisionCheck.h new file mode 100644 index 00000000..b04e0f7d --- /dev/null +++ b/Physic/CollisionCheck.h @@ -0,0 +1,19 @@ +#ifndef SNAKE3_COLLISIONCHECK_H +#define SNAKE3_COLLISIONCHECK_H + +#include "CollisionSystem3D.h" + +namespace Physic { + class CollisionCheck { + public: + static bool IntersectAABB(const AABB& a, const AABB& b) { + return (a.min.x <= b.max.x && a.max.x >= b.min.x) && + (a.min.y <= b.max.y && a.max.y >= b.min.y) && + (a.min.z <= b.max.z && a.max.z >= b.min.z); + } + + static bool IntersectExact(const CollisionEntry& a, const CollisionEntry& b); + }; +} + +#endif //SNAKE3_COLLISIONCHECK_H \ No newline at end of file diff --git a/Physic/CollisionDetector.cpp b/Physic/CollisionDetector.cpp index 7897a6f3..ebe7ae0d 100644 --- a/Physic/CollisionDetector.cpp +++ b/Physic/CollisionDetector.cpp @@ -1,5 +1,7 @@ #include "CollisionDetector.h" +#include "../Renderer/Opengl/Model/Standard/MeshNode3D.h" + namespace Physic { CollisionDetector::~CollisionDetector() { @@ -7,48 +9,52 @@ namespace Physic { staticItems.clear(); } - void CollisionDetector::addStaticItem(BaseItem *item) { + void CollisionDetector::addStaticItem(const shared_ptr &item) { staticItems.push_back(item); } - void CollisionDetector::setPerimeter(ObjWall *wall) { - this->perimeter = wall; - } - - void CollisionDetector::setBarriers(Barriers *barriers) { - CollisionDetector::barriers = barriers; - } - - bool CollisionDetector::perimeterDetect(BaseItem *snakeHead) { - - for (auto Iter = perimeter->getItems().begin(); Iter < perimeter->getItems().end(); Iter++) { - blendBarrierDetect(snakeHead, (*Iter)); - } - - int x = snakeHead->getVirtualX(); - int y = snakeHead->getVirtualY(); - - if (x > perimeter->getMaxX() - || x < perimeter->getMinX() - || y > perimeter->getMaxY() - || y < perimeter->getMinY() - ) { // detekujeme ohradu kolem hraciho pole - return true; - } - - return false; - } - - bool CollisionDetector::detectWithStaticItem(BaseItem *snakeHead) { - for (auto Iter = staticItems.begin(); Iter < staticItems.end(); Iter++) { + // void CollisionDetector::setPerimeter(const shared_ptr &wall) { + // this->perimeter = wall; + // } + // + // void CollisionDetector::setBarriers(const shared_ptr &barriers) { + // CollisionDetector::barriers = barriers; + // } + // + // bool CollisionDetector::perimeterDetect(const shared_ptr &snakeHead) const { + // + // if (perimeter == nullptr) { + // return false; + // } + // + // // for (auto Iter = perimeter->getItems().begin(); Iter < perimeter->getItems().end(); ++Iter) { + // // blendBarrierDetect(snakeHead, Iter); + // // } + // + // const int x = snakeHead->x; + // const int y = snakeHead->y; + // + // if (x > perimeter->getMaxX() + // || x < perimeter->getMinX() + // || y > perimeter->getMaxY() + // || y < perimeter->getMinY() + // ) { // detekujeme ohradu kolem hraciho pole + // return true; + // } + // + // return false; + // } + + bool CollisionDetector::detectWithStaticItem(const shared_ptr &node) { + for (auto Iter = staticItems.begin(); Iter < staticItems.end(); ++Iter) { if (!(*Iter)->isVisible()) { continue; } - int x = snakeHead->getVirtualX(); - int y = snakeHead->getVirtualY(); - int secondX = (*Iter)->getVirtualX(); - int secondY = (*Iter)->getVirtualY(); + const int x = node->x; + const int y = node->y; + const int secondX = (*Iter)->x; + const int secondY = (*Iter)->y; if (x - 16 + 32 >= secondX && x - 16 <= secondX @@ -61,11 +67,11 @@ namespace Physic { return false; } - bool CollisionDetector::detect(BaseItem* first, BaseItem* second) { - int x = first->getVirtualX(); - int y = first->getVirtualY(); - int secondX = second->getVirtualX(); - int secondY = second->getVirtualY(); + bool CollisionDetector::detect(const shared_ptr &first, const shared_ptr &second) { + const int x = first->x; + const int y = first->y; + const int secondX = second->x; + const int secondY = second->y; if (x - 16 + 32 > secondX && x - 16 <= secondX && y - 16 + 32 > secondY && y - 16 <= secondY) { return true; @@ -74,19 +80,20 @@ namespace Physic { return false; } - bool CollisionDetector::barrierCollision(BaseItem *snakeHead) { - for (auto Iter = barriers->getItems().begin(); Iter < barriers->getItems().end(); Iter++) { - blendBarrierDetect(snakeHead, (*Iter)); - bool result = detect(snakeHead, (*Iter)); - if (result) { - return true; - } - } - - return false; - } - - void CollisionDetector::blendBarrierDetect(BaseItem *snakeHead, Cube *barrier) { + // bool CollisionDetector::barrierCollision(const shared_ptr &snakeHead) const { + // if (barriers) { + // for (auto Iter = barriers->getItems().begin(); Iter < barriers->getItems().end(); ++Iter) { + // //blendBarrierDetect(snakeHead, Iter); + // if (detect(snakeHead, static_pointer_cast(*Iter))) { + // return true; + // } + // } + // } + // + // return false; + // } + + void CollisionDetector::blendBarrierDetect(shared_ptr &snakeHead, shared_ptr &barrier) { // glm::vec3 headPosition = snakeHead->getPosition(); // glm::vec3 barrierPosition = barrier->getPosition(); // @@ -99,21 +106,20 @@ namespace Physic { // } } - bool CollisionDetector::intoHimSelf(Snake* snake) { - auto snakeHead = (*snake->getItems().begin())->tile; - - for (auto Iter = snake->getItems().begin()+3; Iter < snake->getItems().end(); Iter++) { - if (!(*Iter)->tile->isVisible()) { - continue; - } - - bool result = detect(snakeHead, (*Iter)->tile); - if (result) { - return true; - } - } - - return false; - } + // bool CollisionDetector::intoHimSelf(const shared_ptr &snake) { + // const auto snakeHead = snake->getItems().begin()->get()->tile; + // + // for (auto Iter = snake->getItems().begin()+3; Iter < snake->getItems().end(); ++Iter) { + // if (!(*Iter)->tile->isVisible()) { + // continue; + // } + // + // if (detect(snakeHead, (*Iter)->tile)) { + // return true; + // } + // } + // + // return false; + // } } // Physic \ No newline at end of file diff --git a/Physic/CollisionDetector.h b/Physic/CollisionDetector.h index 95fde9af..c052c8cd 100644 --- a/Physic/CollisionDetector.h +++ b/Physic/CollisionDetector.h @@ -2,32 +2,30 @@ #define SNAKE3_COLLISIONDETECTOR_H #include "../ItemsDto/BaseItem.h" -#include "../ItemsDto/Barriers.h" -#include "../ItemsDto/BaseContainer.h" -#include "../ItemsDto/Snake.h" -#include "../ItemsDto/ObjWall.h" +#include "../Renderer/Opengl/Model/Standard/MeshNode3D.h" using namespace ItemsDto; +using namespace Model; namespace Physic { - class CollisionDetector { + class CollisionDetector final { public: - virtual ~CollisionDetector(); - bool perimeterDetect(BaseItem* snakeHead); - bool detectWithStaticItem(BaseItem* snakeHead); - bool barrierCollision(BaseItem* snakeHead); - static bool intoHimSelf(Snake* snake); - static bool detect(BaseItem* first, BaseItem* second); - void blendBarrierDetect(BaseItem* snakeHead, Cube* barrier); - void setPerimeter(ObjWall* wall); - void setBarriers(Barriers *barriers); - void addStaticItem(BaseItem* item); + ~CollisionDetector(); + bool perimeterDetect(const shared_ptr &snakeHead) const; + bool detectWithStaticItem(const shared_ptr &node); + bool barrierCollision(const shared_ptr &snakeHead) const; + // static bool intoHimSelf(const shared_ptr &snake); + static bool detect(const shared_ptr &first, const shared_ptr &second); + void blendBarrierDetect(shared_ptr &snakeHead, shared_ptr &barrier); + // void setPerimeter(const shared_ptr &wall); + // void setBarriers(const shared_ptr &barriers); + void addStaticItem(const shared_ptr &item); protected: - vector movingItems; - ObjWall* perimeter; - Barriers* barriers; - vector staticItems; + vector > movingItems; + // shared_ptr perimeter; + // shared_ptr barriers; + vector > staticItems; }; } // Physic diff --git a/Physic/CollisionSystem3D.cpp b/Physic/CollisionSystem3D.cpp new file mode 100644 index 00000000..b3f2d964 --- /dev/null +++ b/Physic/CollisionSystem3D.cpp @@ -0,0 +1,64 @@ +#include "CollisionSystem3D.h" +#include "CollisionCheck.h" + +namespace Physic { + void CollisionSystem3D::addCollider(const shared_ptr &collider) { + const auto &shapes = collider->getCollisionShapes(); + + for (const auto &shapeNode: shapes) { + if (const auto collisionShape = std::dynamic_pointer_cast(shapeNode)) { + CollisionEntry entry; + entry.shapeNode = collisionShape; + entry.parentObject = collider; + flatEntries.push_back(entry); + } + } + } + + void CollisionSystem3D::removeCollider(const std::shared_ptr &collider) { + std::erase_if(flatEntries, + [&collider](const CollisionEntry &entry) { + return entry.parentObject == collider; + }); + } + + void CollisionSystem3D::clearColliders() { + flatEntries.clear(); + } + + void CollisionSystem3D::update() const { + if (flatEntries.empty()) return; + + std::vector worldAABBs(flatEntries.size()); + + for (size_t i = 0; i < flatEntries.size(); ++i) { + auto &entry = flatEntries[i]; + + // Resetujeme stav kolize + entry.shapeNode->getShape()->setColliding(false); + + // WorldMatrix = Parent (Object) * Local (Shape) + const glm::mat4 worldMatrix = entry.parentObject->getModelMatrix() * entry.shapeNode->getModelMatrix(); + worldAABBs[i] = entry.shapeNode->getShape()->calculateAABB(worldMatrix); + } + + for (size_t i = 0; i < flatEntries.size(); i++) { + for (size_t j = i + 1; j < flatEntries.size(); j++) { + // Owner filtering: Nepočítej kolize mezi tvary stejného objektu + if (flatEntries[i].parentObject == flatEntries[j].parentObject) { + continue; + } + + if (CollisionCheck::IntersectAABB(worldAABBs[i], worldAABBs[j])) { + // flatEntries[i].shapeNode->getShape()->setColliding(true); + // flatEntries[j].shapeNode->getShape()->setColliding(true); + + if (CollisionCheck::IntersectExact(flatEntries[i], flatEntries[j])) { + flatEntries[i].shapeNode->getShape()->setColliding(true); + flatEntries[j].shapeNode->getShape()->setColliding(true); + } + } + } + } + } +} // Physic diff --git a/Physic/CollisionSystem3D.h b/Physic/CollisionSystem3D.h new file mode 100644 index 00000000..7bbfcf51 --- /dev/null +++ b/Physic/CollisionSystem3D.h @@ -0,0 +1,28 @@ +#ifndef SNAKE3_COLLISIONSYSTEM3D_H +#define SNAKE3_COLLISIONSYSTEM3D_H + +#include "../Renderer/Opengl/Model/Collision/CollisionShape3D.h" +#include "../Renderer/Opengl/Model/Standard/MeshNode3D.h" +#include + +using namespace Model; +using namespace CollisionShape; + +namespace Physic { + struct CollisionEntry { + shared_ptr shapeNode; + shared_ptr parentObject; + }; + + class CollisionSystem3D { + std::vector > colliders; + std::vector flatEntries; + public: + void addCollider(const shared_ptr &collider); + void update() const; + void removeCollider(const std::shared_ptr &collider); + void clearColliders(); + }; +} // Physic + +#endif //SNAKE3_COLLISIONSYSTEM3D_H \ No newline at end of file diff --git a/Physic/CylinderShape.cpp b/Physic/CylinderShape.cpp new file mode 100644 index 00000000..0de8ff18 --- /dev/null +++ b/Physic/CylinderShape.cpp @@ -0,0 +1,64 @@ +#include "CylinderShape.h" +#include "../Renderer/Opengl/Model/Standard/CylinderMesh.h" + +namespace Physic { + CylinderShape::CylinderShape(const shared_ptr &resourceManager, + const shared_ptr &contextState, + const float radius, const float height) + : radius(radius), height(height) { + + const auto shader = resourceManager ? resourceManager->getShader("basicShader") : nullptr; + const auto shadowsShader = resourceManager ? resourceManager->getShader("shadowDepthShader") : nullptr; + + material = make_shared(shader, shadowsShader); + material->setNormalEnabled(false); + material->setAlpha(0.2); + material->setBlending(Blending::Translucent); + + auto cylinderMesh = make_shared(shader, radius, radius, height, 8, 32); + cylinderMesh->setMaterial(material); + meshNode = make_shared(contextState, cylinderMesh, resourceManager); + } + + CylinderShape::CylinderWorldData CylinderShape::BuildCylinder(const glm::mat4 &modelMatrix) const { + CylinderWorldData data{}; + + const auto scale = glm::vec3( + glm::length(glm::vec3(modelMatrix[0])), + glm::length(glm::vec3(modelMatrix[1])), + glm::length(glm::vec3(modelMatrix[2])) + ); + + const float halfH = (height * 0.5f) * scale.y; + + const auto center = glm::vec3(modelMatrix * glm::vec4(0, 0, 0, 1)); + const glm::vec3 up = glm::normalize(glm::vec3(modelMatrix[1])); + + data.p0 = center - up * halfH; + data.p1 = center + up * halfH; + data.radius = radius * glm::max(scale.x, scale.z); + + return data; + } + + AABB CylinderShape::calculateAABB(const glm::mat4 &modelMatrix) { + auto data = BuildCylinder(modelMatrix); + const glm::vec3 min = glm::min(data.p0, data.p1) - glm::vec3(data.radius); + const glm::vec3 max = glm::max(data.p0, data.p1) + glm::vec3(data.radius); + + // const glm::vec3 d = data.p1 - data.p0; + // glm::vec3 e = data.radius * glm::sqrt(1.0f - (d * d) / glm::dot(d, d)); + + // Simplify for now as we did for Capsule + return {min, max}; + } + + void CylinderShape::render(const shared_ptr &camera, const glm::mat4 &projection, glm::mat4 t) { + const glm::vec3 color = isColliding() ? glm::vec3(1.0f, 0.2f, 0.2f) : glm::vec3(0.2f, 1.0f, 1.0f); + material->setColor(color); + + meshNode->setScale(glm::vec3(1.0f)); + meshNode->setPosition(glm::vec3(0.0f)); + meshNode->render(camera, projection, 0.0f, t, false); + } +} // Physic diff --git a/Physic/CylinderShape.h b/Physic/CylinderShape.h new file mode 100644 index 00000000..949a657b --- /dev/null +++ b/Physic/CylinderShape.h @@ -0,0 +1,39 @@ +#ifndef SNAKE3_CYLINDERSHAPE_H +#define SNAKE3_CYLINDERSHAPE_H + +#include "Shape.h" +#include "../Renderer/Opengl/Model/Standard/MeshNode3D.h" + +using namespace Model; + +namespace Physic { + class CylinderShape : public Shape { + public: + struct CylinderWorldData { + glm::vec3 p0; // Spodní střed + glm::vec3 p1; // Horní střed + float radius; + }; + + private: + float radius; + float height; + shared_ptr meshNode; + shared_ptr material; + + public: + explicit CylinderShape(const shared_ptr &resourceManager, + const shared_ptr &contextState, + float radius = 1.0f, float height = 2.0f); + + ShapeType getType() override { return ShapeType::Cylinder; } + + [[nodiscard]] CylinderWorldData BuildCylinder(const glm::mat4 &modelMatrix) const; + + AABB calculateAABB(const glm::mat4 &modelMatrix) override; + + void render(const shared_ptr &camera, const glm::mat4 &projection, glm::mat4 t) override; + }; +} // Physic + +#endif //SNAKE3_CYLINDERSHAPE_H diff --git a/Physic/Shape.h b/Physic/Shape.h new file mode 100644 index 00000000..8d874a5b --- /dev/null +++ b/Physic/Shape.h @@ -0,0 +1,74 @@ +#ifndef SNAKE3_SHAPE_H +#define SNAKE3_SHAPE_H + +#include +#include + +#include "../ItemsDto/Transform.h" +#include "../Manager/Camera.h" + +using namespace Manager; +using namespace Node3D; + +namespace Physic { + + enum class ShapeType { + Box, Sphere, Capsule, Cylinder, + }; + + struct AABB { + glm::vec3 min; + glm::vec3 max; + }; + + class Shape { + public: + virtual ~Shape() = default; + + virtual ShapeType getType() = 0; + virtual void render(const shared_ptr &camera, const glm::mat4 &projection, glm::mat4 t) = 0; + virtual AABB calculateAABB(const glm::mat4& modelMatrix) = 0; + + void setColliding(const bool colliding) { this->colliding = colliding; } + [[nodiscard]] bool isColliding() const { return colliding; } + + static AABB CalculateAABB(const glm::mat4& modelMatrix, const glm::vec3& localHalfExtents) { + + const auto center = glm::vec3(modelMatrix * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); + + const glm::vec3 right = glm::abs(glm::vec3(modelMatrix[0])); + const glm::vec3 up = glm::abs(glm::vec3(modelMatrix[1])); + const glm::vec3 forward = glm::abs(glm::vec3(modelMatrix[2])); + + const glm::vec3 newHalfExtents = + right * localHalfExtents.x + + up * localHalfExtents.y + + forward * localHalfExtents.z; + + return { center - newHalfExtents, center + newHalfExtents }; + } + + static vector GetAABBCorners(const AABB& aabb) { + std::vector corners(8); + + // Spodní stěna (Y = min.y) + corners[0] = glm::vec3(aabb.min.x, aabb.min.y, aabb.min.z); // min-min-min (Levý-Dolní-Zadní) + corners[1] = glm::vec3(aabb.max.x, aabb.min.y, aabb.min.z); // max-min-min + corners[2] = glm::vec3(aabb.max.x, aabb.min.y, aabb.max.z); // max-min-max + corners[3] = glm::vec3(aabb.min.x, aabb.min.y, aabb.max.z); // min-min-max + + // Horní stěna (Y = max.y) + corners[4] = glm::vec3(aabb.min.x, aabb.max.y, aabb.min.z); // min-max-min + corners[5] = glm::vec3(aabb.max.x, aabb.max.y, aabb.min.z); // max-max-min + corners[6] = glm::vec3(aabb.max.x, aabb.max.y, aabb.max.z); // max-max-max + corners[7] = glm::vec3(aabb.min.x, aabb.max.y, aabb.max.z); // min-max-max + + return corners; + } + + private: + bool colliding = false; + }; +} // Physic + +#endif //SNAKE3_SHAPE_H \ No newline at end of file diff --git a/Physic/SphereShape.cpp b/Physic/SphereShape.cpp new file mode 100644 index 00000000..0786db1e --- /dev/null +++ b/Physic/SphereShape.cpp @@ -0,0 +1,44 @@ +#include "SphereShape.h" + +#include "../Renderer/Opengl/Model/Standard/SphereMesh.h" + +namespace Physic { + SphereShape::SphereShape(const shared_ptr &resourceManager, + const shared_ptr &contextState, const float radius) : radius(radius) { + const auto shader = resourceManager ? resourceManager->getShader("basicShader") : nullptr; + const auto shadowsShader = resourceManager ? resourceManager->getShader("shadowDepthShader") : nullptr; + // material + material = make_shared(shader, shadowsShader); + material->setNormalEnabled(false); + material->setAlpha(0.2); + material->setBlending(Blending::Translucent); + // SphereMesh default radius is 0.5, we want to match our radius + auto sphereMesh = make_shared(shader, radius * 2.0f, radius); + sphereMesh->setMaterial(material); + meshNode = make_shared(contextState, sphereMesh, resourceManager); + } + + SphereShape::SphereWorldData SphereShape::BuildSphere(const glm::mat4 &modelMatrix) const { + SphereWorldData data{}; + // V S*T*R matici je světová pozice ovlivněna škálováním. + data.center = glm::vec3(modelMatrix * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); + + const auto scale = glm::vec3( + glm::length(glm::vec3(modelMatrix[0])), + glm::length(glm::vec3(modelMatrix[1])), + glm::length(glm::vec3(modelMatrix[2])) + ); + data.radius = radius * glm::max(scale.x, glm::max(scale.y, scale.z)); + + return data; + } + + void SphereShape::render(const shared_ptr &camera, const glm::mat4 &projection, glm::mat4 t) { + const glm::vec3 color = isColliding() ? glm::vec3(1.0f, 0.2f, 0.2f) : glm::vec3(0.2f, 1.0f, 1.0f); + material->setColor(color); + + meshNode->setScale(glm::vec3(1.0f)); + meshNode->setPosition(glm::vec3(0.0f)); + meshNode->render(camera, projection, 0.0f, t, false); + } +} // Physic \ No newline at end of file diff --git a/Physic/SphereShape.h b/Physic/SphereShape.h new file mode 100644 index 00000000..79fd468c --- /dev/null +++ b/Physic/SphereShape.h @@ -0,0 +1,47 @@ +#ifndef SNAKE3_SPHERESHAPE_H +#define SNAKE3_SPHERESHAPE_H + +#include "Shape.h" + +#include "../Renderer/Opengl/Model/Standard/MeshNode3D.h" + +using namespace Model; + +namespace Physic { + class SphereShape : public Shape { + public: + struct SphereWorldData { + glm::vec3 center; + float radius; + }; + + private: + float radius; + + shared_ptr meshNode; + + shared_ptr material; + + public: + explicit SphereShape(const shared_ptr &resourceManager, + const shared_ptr &contextState, float radius = 1.0f); + + ShapeType getType() override { return ShapeType::Sphere; } + + [[nodiscard]] float getRadius() const { return radius; } + + [[nodiscard]] SphereWorldData BuildSphere(const glm::mat4& modelMatrix) const; + + AABB calculateAABB(const glm::mat4& modelMatrix) override { + auto [center, radius] = BuildSphere(modelMatrix); + return { + center - glm::vec3(radius), + center + glm::vec3(radius) + }; + } + + void render(const shared_ptr &camera, const glm::mat4 &projection, glm::mat4 t) override; + }; +} // Physic + +#endif //SNAKE3_SPHERESHAPE_H \ No newline at end of file diff --git a/README.md b/README.md index 7f857b6b..09cf05b7 100644 --- a/README.md +++ b/README.md @@ -35,9 +35,8 @@ and rain with drops ![Printscreen](Screens/rain_drops.gif "Video rain with drops") ### Dependencies: -- assimp - https://assimp-docs.readthedocs.io/en/latest/ +- assimp - v5.2.0 https://github.com/assimp/assimp - stb_image - v2.27 - public domain image loader - http://nothings.org/stb -- tiny_obj_loader - v2.00 - obj model loader - https://github.com/tinyobjloader/tinyobjloader - OpenAL - ALut - freetype2 diff --git a/Renderer/Opengl/AnimRenderer.cpp b/Renderer/Opengl/AnimRenderer.cpp deleted file mode 100644 index 9344b8ac..00000000 --- a/Renderer/Opengl/AnimRenderer.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include "AnimRenderer.h" -#include "Model/Utils/Tree.h" - -namespace Renderer { - - AnimRenderer::AnimRenderer(sSNAKE_TILE* tile, AnimationModel *sharedPtr, Camera *camera, const glm::mat4 &projection, - ResourceManager *resManager): show(true) { - model = sharedPtr; - resourceManager = resManager; - this->camera = camera; - this->projection = projection; - this->tile = tile; - baseShader = resourceManager->getShader("normalShader"); - shadowShader = resourceManager->getShader("shadowDepthShader"); - } - - AnimRenderer::~AnimRenderer() { - animationPlay.clear(); - } - - void AnimRenderer::render() { - if (show) { - baseShader->use(); - baseShader->setMat4("view", camera->getViewMatrix()); - baseShader->setMat4("projection", projection); - baseShader->setVec3("viewPos", camera->getPosition()); - baseShader->setBool("useMaterial", true); - glm::vec3 lightPos(model->getBaseItem()->getPosition().x, model->getBaseItem()->getPosition().y + 6, -55.3f); - baseShader->setVec3("lightPos", lightPos); - - renderScene(baseShader); - - baseShader->setBool("useBones", false); - baseShader->setBool("useMaterial", false); - } - } - - void AnimRenderer::renderScene(ShaderManager *shader) { - glm::mat4 modelTrans = glm::mat4(1.0f); - glm::vec3 position = model->getBaseItem()->getPosition(); - modelTrans = glm::translate(modelTrans, {0.0, 0.0, 0.0}); - modelTrans = glm::scale(modelTrans, {0.041667f, 0.041667f, 0.041667f}); - modelTrans = glm::translate(modelTrans, position); - - const glm::vec4 *rotate = model->getBaseItem()->getRotate(); - - switch (tile->direction) { - case ItemsDto::RIGHT: - model->getBaseItem()->setRotate({90, 1, 0, 0}, {0, 0, 1, 0}, rotate[2]); - modelTrans = glm::rotate(modelTrans, glm::radians(rotate[0].x), {1.0, 0.0, 0.0f}); - modelTrans = glm::rotate(modelTrans, glm::radians(rotate[1].x), {0.0, 1.0, 0.0f}); - break; - case ItemsDto::LEFT: - model->getBaseItem()->setRotate({90, 1, 0, 0}, {180, 0, 1, 0}, rotate[2]); - modelTrans = glm::rotate(modelTrans, glm::radians(rotate[0].x), {1.0, 0.0, 0.0f}); - modelTrans = glm::rotate(modelTrans, glm::radians(rotate[1].x), {0.0, 1.0, 0.0f}); - break; - case ItemsDto::UP: - model->getBaseItem()->setRotate({90, 1, 0, 0}, {90, 0, 1, 0}, rotate[2]); - modelTrans = glm::rotate(modelTrans, glm::radians(rotate[0].x), {1.0, 0.0, 0.0f}); - modelTrans = glm::rotate(modelTrans, glm::radians(rotate[1].x), {0.0, 1.0, 0.0f}); - break; - case ItemsDto::DOWN: - model->getBaseItem()->setRotate({90, 1, 0, 0}, {-90, 0, 1, 0}, rotate[2]); - modelTrans = glm::rotate(modelTrans, glm::radians(rotate[0].x), {1.0, 0.0, 0.0f}); - modelTrans = glm::rotate(modelTrans, glm::radians(rotate[1].x), {0.0, 1.0, 0.0f}); - break; - default: - model->getBaseItem()->setRotate({90, 1, 0, 0}, {0, 0, 1, 0}, rotate[2]); - modelTrans = glm::rotate(modelTrans, glm::radians(rotate[0].x), {1.0, 0.0, 0.0f}); - modelTrans = glm::rotate(modelTrans, glm::radians(rotate[1].x), {0.0, 1.0, 0.0f}); - break; - } - - for (auto animName: animationPlay) { - const auto found = std::find_if(model->getAnimations().begin(), model->getAnimations().end(), - [&](const auto &anim) { - return animName == anim.name; - }); - if (found != model->getAnimations().end()) { - auto animation = &(*found); - model->updateAnimation(animation); - for (int i = 0; i < model->getMetadata(animation)->bone_transform.size(); ++i) { - baseShader->setMat4("finalBonesMatrices[" + std::to_string(i) + "]", - model->getMetadata(animation)->bone_transform[i]); - } - for (auto item: model->getMeshes()) { - if (item->getName() == - animation->nodes[0].bone.meshName) { - if (!item->isHasBones()) { - baseShader->setBool("useBones", false); - shader->setMat4("model", modelTrans * item->getGlobalTransformation()); - } else { - baseShader->setBool("useBones", true); - shader->setMat4("model", modelTrans); - } - item->bind(); - glDrawElements(GL_TRIANGLES, (int) item->getIndices().size(), GL_UNSIGNED_INT, nullptr); - } - } - } - } - - for (auto item: model->getNoBonesMeshes()) { - baseShader->setBool("useBones", false); - shader->setMat4("model", modelTrans * item->getGlobalTransformation()); - item->bind(); - glDrawElements(GL_TRIANGLES, (int) item->getIndices().size(), GL_UNSIGNED_INT, nullptr); - } - } - - void AnimRenderer::renderShadowMap() { - shadowShader->use(); - renderScene(shadowShader); - } - - void AnimRenderer::beforeRender() { - } - - void AnimRenderer::afterRender() { - - } - - void AnimRenderer::addPlay(const string& name) { - animationPlay.push_back(name); - } - - void AnimRenderer::setShow(bool show) { - AnimRenderer::show = show; - } - -} // Renderer \ No newline at end of file diff --git a/Renderer/Opengl/AnimRenderer.h b/Renderer/Opengl/AnimRenderer.h deleted file mode 100644 index eaefa5a2..00000000 --- a/Renderer/Opengl/AnimRenderer.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef SNAKE3_ANIMRENDERER_H -#define SNAKE3_ANIMRENDERER_H - -#include "BaseRenderer.h" -#include "Model/AnimationModel.h" -#include "../../Manager/ResourceManager.h" -#include "../../ItemsDto/Snake.h" -#include -#include - -using namespace Model; -using namespace Manager; -using namespace ItemsDto; - -namespace Renderer { - class AnimRenderer : public BaseRenderer { - public: - explicit AnimRenderer(sSNAKE_TILE* tile, - AnimationModel* sharedPtr, Camera *camera, const glm::mat4 &projection, ResourceManager* resManager); - ~AnimRenderer() override; - void render() override; - void renderShadowMap() override; - void beforeRender() override; - void afterRender() override; - void addPlay(const string& name); - void setShow(bool show); - - protected: - void renderScene(ShaderManager* shader); - AnimationModel* model; - ResourceManager* resourceManager; - ShaderManager* baseShader; - ShaderManager* shadowShader; - glm::mat4 projection{}; - Camera* camera; - vector animationPlay; - sSNAKE_TILE* tile; - bool show; - }; - -} // Renderer - -#endif //SNAKE3_ANIMRENDERER_H diff --git a/Renderer/Opengl/BarrierRenderer.cpp b/Renderer/Opengl/BarrierRenderer.cpp deleted file mode 100644 index ee0ca2fb..00000000 --- a/Renderer/Opengl/BarrierRenderer.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "BarrierRenderer.h" - -namespace Renderer { - BarrierRenderer::BarrierRenderer(Barriers *item, Camera* camera, glm::mat4 proj, ResourceManager* resManager) - : ObjWallRenderer((ObjWall* )item, camera, proj, resManager) { - } -} // Renderer \ No newline at end of file diff --git a/Renderer/Opengl/BarrierRenderer.h b/Renderer/Opengl/BarrierRenderer.h deleted file mode 100644 index 9430f84c..00000000 --- a/Renderer/Opengl/BarrierRenderer.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef SNAKE3_BARRIERRENDERER_H -#define SNAKE3_BARRIERRENDERER_H - -#include "ObjWallRenderer.h" -#include "../../ItemsDto/Barriers.h" - -namespace Renderer { - - class BarrierRenderer : public ObjWallRenderer { - public: - explicit BarrierRenderer(Barriers *item, Camera* camera, glm::mat4 proj, ResourceManager* resManager); - }; - -} // Renderer - -#endif //SNAKE3_BARRIERRENDERER_H diff --git a/Renderer/Opengl/BaseRenderer.cpp b/Renderer/Opengl/BaseRenderer.cpp index 3a6f77a9..5bb2635f 100644 --- a/Renderer/Opengl/BaseRenderer.cpp +++ b/Renderer/Opengl/BaseRenderer.cpp @@ -1,21 +1,17 @@ #include "BaseRenderer.h" +#include "Model/Standard/PlaneMesh.h" -namespace Manager { - -} // Manager -Renderer::BaseRenderer::BaseRenderer(): item(nullptr), shadow(false), fog(false) {} -Renderer::BaseRenderer::BaseRenderer(BaseItem *item) : item(item), shadow(false), fog(false) {} - -Renderer::BaseRenderer::~BaseRenderer() { - delete item; +Renderer::BaseRenderer::BaseRenderer() : shadows(false), fog(false) { } -void Renderer::BaseRenderer::setShadow(bool shadow) { - BaseRenderer::shadow = shadow; +Renderer::BaseRenderer::~BaseRenderer() = default; + +void Renderer::BaseRenderer::setShadow(const bool shadow) { + shadows = shadow; } bool Renderer::BaseRenderer::isShadow() const { - return shadow; + return shadows; } void Renderer::BaseRenderer::setFog(bool fog) { @@ -25,3 +21,11 @@ void Renderer::BaseRenderer::setFog(bool fog) { bool Renderer::BaseRenderer::isFog() const { return fog; } + +glm::vec3 Renderer::BaseRenderer::compareSceneMin(const glm::vec3 sceneMin) { + return sceneMin; +} + +glm::vec3 Renderer::BaseRenderer::compareSceneMax(const glm::vec3 sceneMax) { + return sceneMax; +} diff --git a/Renderer/Opengl/BaseRenderer.h b/Renderer/Opengl/BaseRenderer.h index 93476b2b..1fc85a73 100644 --- a/Renderer/Opengl/BaseRenderer.h +++ b/Renderer/Opengl/BaseRenderer.h @@ -1,32 +1,58 @@ #ifndef SNAKE3_BASERENDERER_H #define SNAKE3_BASERENDERER_H -#include "../../ItemsDto/BaseItem.h" - -using namespace ItemsDto; +#include +#include +#include namespace Renderer { + class BaseRenderer; + + struct RendererEntry { + std::shared_ptr renderer; + int priority; + }; + + enum MODE { + standard = 0, + shadowMap = 1, + reflection = 2, + bloom = 3 + }; class BaseRenderer { public: - BaseRenderer(); - explicit BaseRenderer(BaseItem *item); + explicit BaseRenderer(); + virtual ~BaseRenderer(); - virtual void render() = 0; + + virtual void render3D(float dt, uint64_t frameId) = 0; + + virtual void render2D(float dt, uint64_t frameId) {}; + virtual void renderShadowMap() = 0; - virtual void beforeRender() = 0; + + virtual void beforeRender(MODE mode) = 0; + virtual void afterRender() = 0; - void setShadow(bool shadow); + + virtual void setShadow(bool shadow); + [[nodiscard]] bool isShadow() const; + void setFog(bool fog); + [[nodiscard]] bool isFog() const; + glm::vec3 compareSceneMin(glm::vec3 sceneMin); + + glm::vec3 compareSceneMax(glm::vec3 sceneMax); + protected: - BaseItem* item{}; - bool shadow; + bool shadows; bool fog; + MODE mode; }; - } // Manager #endif //SNAKE3_BASERENDERER_H diff --git a/Renderer/Opengl/BloomRenderer.cpp b/Renderer/Opengl/BloomRenderer.cpp index e023161a..7c771ed2 100644 --- a/Renderer/Opengl/BloomRenderer.cpp +++ b/Renderer/Opengl/BloomRenderer.cpp @@ -1,23 +1,14 @@ #include "BloomRenderer.h" #include "../../Resource/ShaderLoader.h" -Renderer::BloomRenderer::BloomRenderer(ResourceManager* resManager, int width, int height): width(width), height(height) { - resourceManager = resManager; +Renderer::BloomRenderer::BloomRenderer(const shared_ptr &resManager, const int width, const int height) + : resourceManager(resManager), width(width), height(height) { - resourceManager->addShader("bloom", std::make_shared( - ShaderLoader::loadShader("Assets/Shaders/bloom/bloom.vs", "Assets/Shaders/bloom/bloom.fs"))); - resourceManager->addShader("blur", std::make_shared( - ShaderLoader::loadShader("Assets/Shaders/bloom/blur.vs", "Assets/Shaders/bloom/blur.fs"))); - resourceManager->addShader("bloomFinal", std::make_shared( - ShaderLoader::loadShader("Assets/Shaders/bloom/bloom_final.vs", "Assets/Shaders/bloom/bloom_final.fs"))); - - shader = resourceManager->getShader("bloom"); shaderBlur = resourceManager->getShader("blur"); shaderBloomFinal = resourceManager->getShader("bloomFinal"); glGenFramebuffers(1, &hdrFBO); glBindFramebuffer(GL_FRAMEBUFFER, hdrFBO); - // create 2 floating point color buffers (1 for normal rendering, other for brightness threshold values) glGenTextures(2, colorBuffers); for (unsigned int i = 0; i < 2; i++) { @@ -25,45 +16,43 @@ Renderer::BloomRenderer::BloomRenderer(ResourceManager* resManager, int width, i glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width, height, 0, GL_RGB, GL_FLOAT, nullptr); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // we clamp to the edge as the blur filter would otherwise sample repeated texture values! + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - // attach texture to framebuffer glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, colorBuffers[i], 0); } - // create and attach depth buffer (renderbuffer) unsigned int rboDepth; glGenRenderbuffers(1, &rboDepth); glBindRenderbuffer(GL_RENDERBUFFER, rboDepth); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboDepth); - // tell OpenGL which color attachments we'll use (of this framebuffer) for rendering - unsigned int attachments[2] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }; + + constexpr unsigned int attachments[2] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }; glDrawBuffers(2, attachments); - // finally check if framebuffer is complete + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) std::cout << "Framebuffer not complete!" << std::endl; glBindFramebuffer(GL_FRAMEBUFFER, 0); // ping-pong-framebuffer for blurring glGenFramebuffers(2, pingpongFBO); - glGenTextures(2, pingpongColorbuffers); + glGenTextures(2, pingpongColorBuffers); for (unsigned int i = 0; i < 2; i++) { glBindFramebuffer(GL_FRAMEBUFFER, pingpongFBO[i]); - glBindTexture(GL_TEXTURE_2D, pingpongColorbuffers[i]); + glBindTexture(GL_TEXTURE_2D, pingpongColorBuffers[i]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width, height, 0, GL_RGB, GL_FLOAT, nullptr); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // we clamp to the edge as the blur filter would otherwise sample repeated texture values! + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, pingpongColorbuffers[i], 0); - // also check if framebuffers are complete (no need for depth buffer) + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, pingpongColorBuffers[i], 0); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) std::cout << "Framebuffer not complete!" << std::endl; } - shader->use(); - shader->setInt("diffuseTexture", 0); + const auto sceneTexture = make_shared(colorBuffers[0]); + resourceManager->replaceTexture("SceneTexture", sceneTexture); + shaderBlur->use(); shaderBlur->setInt("image", 0); shaderBloomFinal->use(); @@ -71,18 +60,19 @@ Renderer::BloomRenderer::BloomRenderer(ResourceManager* resManager, int width, i shaderBloomFinal->setInt("bloomBlur", 1); } -void Renderer::BloomRenderer::render() { +void Renderer::BloomRenderer::render3D(float dt, uint64_t frameId) { } void Renderer::BloomRenderer::afterRender() { bool horizontal = true, first_iteration = true; - unsigned int amount = 10; + constexpr unsigned int amount = 10; shaderBlur->use(); for (unsigned int i = 0; i < amount; i++) { glBindFramebuffer(GL_FRAMEBUFFER, pingpongFBO[horizontal]); shaderBlur->setInt("horizontal", horizontal); - glBindTexture(GL_TEXTURE_2D, first_iteration ? colorBuffers[1] : pingpongColorbuffers[!horizontal]); // bind texture of other framebuffer (or scene if first iteration) + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, first_iteration ? colorBuffers[1] : pingpongColorBuffers[!horizontal]); renderQuad(); horizontal = !horizontal; if (first_iteration) { @@ -96,21 +86,19 @@ void Renderer::BloomRenderer::afterRender() { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, colorBuffers[0]); glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, pingpongColorbuffers[!horizontal]); + glBindTexture(GL_TEXTURE_2D, pingpongColorBuffers[!horizontal]); shaderBloomFinal->setInt("bloom", true); shaderBloomFinal->setFloat("exposure", 1.2f); renderQuad(); } void Renderer::BloomRenderer::renderQuad() { - if (quadVAO == 0) - { - float quadVertices[] = { - // positions // texture Coords - -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, - -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, - 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, - 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, + if (quadVAO == 0) { + constexpr float quadVertices[] = { + -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, + 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, + 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, }; // setup plane VAO glGenVertexArrays(1, &quadVAO); @@ -119,18 +107,19 @@ void Renderer::BloomRenderer::renderQuad() { glBindBuffer(GL_ARRAY_BUFFER, quadVBO); glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW); glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)nullptr); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), static_cast(nullptr)); glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float))); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast(3 * sizeof(float))); } glBindVertexArray(quadVAO); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glBindVertexArray(0); } -void Renderer::BloomRenderer::beforeRender() { +void Renderer::BloomRenderer::beforeRender(const MODE mode) { glBindFramebuffer(GL_FRAMEBUFFER, hdrFBO); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + this->mode = mode; } void Renderer::BloomRenderer::renderShadowMap() {} diff --git a/Renderer/Opengl/BloomRenderer.h b/Renderer/Opengl/BloomRenderer.h index d25cb99b..cd5090f6 100644 --- a/Renderer/Opengl/BloomRenderer.h +++ b/Renderer/Opengl/BloomRenderer.h @@ -4,30 +4,28 @@ #include "../../Manager/ResourceManager.h" #include "../../Manager/ShaderManager.h" #include "BaseRenderer.h" -#include -#include using namespace Manager; using namespace std; namespace Renderer { - class BloomRenderer : public BaseRenderer { + class BloomRenderer final : public BaseRenderer { public: - explicit BloomRenderer(ResourceManager* resManager, int width, int height); - void beforeRender() override; + explicit BloomRenderer(const shared_ptr &resManager, int width, int height); + void beforeRender(MODE mode) override; void afterRender() override; - void render() override; + void render3D(float dt, uint64_t frameId) override; void renderShadowMap() override; protected: void renderQuad(); - ResourceManager* resourceManager; - ShaderManager* shader; - ShaderManager* shaderBlur; - ShaderManager* shaderBloomFinal; + shared_ptr resourceManager; + shared_ptr shader; + shared_ptr shaderBlur; + shared_ptr shaderBloomFinal; unsigned int hdrFBO{}; unsigned int pingpongFBO[2]{}; unsigned int colorBuffers[2]{}; - unsigned int pingpongColorbuffers[2]{}; + unsigned int pingpongColorBuffers[2]{}; unsigned int quadVAO = 0; unsigned int quadVBO{}; int width; diff --git a/Renderer/Opengl/BoltRenderer.cpp b/Renderer/Opengl/BoltRenderer.cpp new file mode 100644 index 00000000..bb077dff --- /dev/null +++ b/Renderer/Opengl/BoltRenderer.cpp @@ -0,0 +1,197 @@ +#include "BoltRenderer.h" +#include + +namespace Renderer { + BoltRenderer::BoltRenderer(Camera *camera, glm::mat4 proj, ResourceManager *resManager) { + this->camera = camera; + this->projection = proj; + this->resourceManager = resManager; + this->shader = resourceManager->getShader("boltShader").get(); + this->timeSinceLastBolt = 0.0f; + this->nextBoltTime = 3.0f + static_cast(rand()) / RAND_MAX * 10.0f; // 5-15 sekund + this->isActive = false; + this->lightning = new LightningFlashEffect(camera, proj, resManager); + this->lightning->init(); + + createBoltGeometry(); + } + + BoltRenderer::~BoltRenderer() { + cleanup(); + } + + void BoltRenderer::cleanup() { + if (VAO != 0) { + glDeleteVertexArrays(1, &VAO); + VAO = 0; + } + if (VBO != 0) { + glDeleteBuffers(1, &VBO); + VBO = 0; + } + + delete lightning; + } + + + void BoltRenderer::generateBoltSegments(const glm::vec3 &start, const glm::vec3 &end) { + segments.clear(); + + const int numSegments = 25; + const float maxOffset = 1.5f; + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_real_distribution displace(-maxOffset, maxOffset); + std::uniform_real_distribution brightness(0.8f, 1.2f); + + glm::vec3 direction = end - start; + glm::vec3 segmentStep = direction / static_cast(numSegments); + glm::vec3 current = start; + + // Vytvoříme základní kolmý vektor pro celý blesk + glm::vec3 mainPerpendicular = glm::normalize(glm::cross(direction, glm::vec3(0, 1, 0))); + + // Hlavní dráha blesku + for (int i = 0; i < numSegments; i++) { + BoltSegment segment; + segment.start = current; + + // Přidáme náhodné vychýlení pro další bod + glm::vec3 next = current + segmentStep; + if (i != numSegments - 1) { + // Poslední segment necháme mířit přímo do cíle + next += mainPerpendicular * displace(gen); + next += glm::vec3(0, 1, 0) * displace(gen) * 0.5f; + } + + segment.end = next; + segment.thickness = 0.15f * (1.0f - static_cast(i) / numSegments * 0.5f); + segment.brightness = brightness(gen); + + segments.push_back(segment); + + // Občas přidáme menší odbočku + if (i > 0 && i < numSegments - 2) { + if (static_cast(rand()) / RAND_MAX < 0.2f) { + // 20% šance na odbočku + BoltSegment branch; + branch.start = current; + glm::vec3 branchEnd = current + segmentStep * 0.5f; + branchEnd += mainPerpendicular * displace(gen) * 2.0f; + branch.end = branchEnd; + branch.thickness = segment.thickness * 0.5f; + branch.brightness = segment.brightness * 0.7f; + segments.push_back(branch); + } + } + + current = next; + } + + // Zajistíme, že poslední segment končí přesně v cílovém bodě + if (!segments.empty()) { + segments.back().end = end; + } + } + + void BoltRenderer::beforeRender(const MODE mode) { + glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + this->mode = mode; + } + + void BoltRenderer::afterRender() { + glEnable(GL_DEPTH_TEST); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + void BoltRenderer::render3D(float dt, uint64_t frameId) { + if (!isActive) return; + + timeSinceLastBolt += dt; + + shader->use(); + shader->setMat4("projection", projection); + shader->setMat4("view", camera->getViewMatrix()); + shader->setFloat("time", static_cast(glfwGetTime())); + + glLineWidth(3.0f); + + for (const auto& segment : segments) { + shader->setVec3("startPos", segment.start); + shader->setVec3("endPos", segment.end); + shader->setFloat("brightness", segment.brightness * 2.0f); + + glBindVertexArray(VAO); + glDrawArrays(GL_LINES, 0, 2); + } + + glLineWidth(1.0f); + + if (timeSinceLastBolt > 0.15f) { + isActive = false; + } + + lightning->update(dt); + lightning->render(); + } + + void BoltRenderer::triggerBolt() { + isActive = true; + + // Získáme pozici a směr kamery + glm::vec3 camPos = camera->getPosition(); + glm::vec3 camFront = camera->getFront(); + glm::vec3 camRight = glm::normalize(glm::cross(camFront, glm::vec3(0.0f, 1.0f, 0.0f))); + + // Náhodné hodnoty pro pozicování + float forwardOffset = 10.0f + static_cast(rand()) / RAND_MAX * 15.0f; + float sideOffset = -8.0f + static_cast(rand()) / RAND_MAX * 16.0f; + float heightOffset = 15.0f + static_cast(rand()) / RAND_MAX * 10.0f; + + // Počáteční pozice blesku + glm::vec3 startPos = camPos + + camFront * forwardOffset + // Vzdálenost před kamerou + camRight * sideOffset + // Posun do stran + glm::vec3(0.0f, heightOffset, 0.0f); // Výška + + // Koncový bod - vždy směřuje k zemi, ale s mírnou náhodnou odchylkou + glm::vec3 endPos = startPos + glm::vec3( + -2.0f + static_cast(rand()) / RAND_MAX * 4.0f, // Náhodná X odchylka + -heightOffset - 5.0f, // Dolů k zemi + -2.0f + static_cast(rand()) / RAND_MAX * 4.0f // Náhodná Z odchylka + ); + + generateBoltSegments(startPos, endPos); + + // Reset časovače + timeSinceLastBolt = 0.0f; + + lightning->triggerSequence({1.0f, 0.6f, 0.8f}, 0.05f); + } + + void BoltRenderer::renderShadowMap() { + } + + void BoltRenderer::createBoltGeometry() { + constexpr float vertices[] = { + 0.0f, 0.0f, 0.0f, // První bod + 1.0f, 0.0f, 0.0f // Druhý bod + }; + + glGenVertexArrays(1, &VAO); + glGenBuffers(1, &VBO); + + glBindVertexArray(VAO); + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + } +} diff --git a/Renderer/Opengl/BoltRenderer.h b/Renderer/Opengl/BoltRenderer.h new file mode 100644 index 00000000..e3bac388 --- /dev/null +++ b/Renderer/Opengl/BoltRenderer.h @@ -0,0 +1,55 @@ +#ifndef SNAKE3_BOLTRENDERER_H +#define SNAKE3_BOLTRENDERER_H + +#include +#include + +#include "../../Manager/ResourceManager.h" +#include "../../Manager/ShaderManager.h" +#include "BaseRenderer.h" +#include "Effects/LightningFlashEffect.h" + +using namespace Manager; +using namespace Effects; + +namespace Renderer { + class BoltRenderer : public BaseRenderer { + Camera* camera; + glm::mat4 projection{}; + ResourceManager* resourceManager; + ShaderManager* shader; + unsigned int VAO{}; + unsigned int VBO{}; + float boltProgress{0.0f}; + float boltDuration{0.15f}; + LightningFlashEffect* lightning; + + struct BoltSegment { + glm::vec3 start; + glm::vec3 end; + float thickness; + float brightness; + }; + + std::vector segments; + float timeSinceLastBolt; + float nextBoltTime; + bool isActive; + + void generateBoltSegments(const glm::vec3& start, const glm::vec3& end); + void createBoltGeometry(); + void cleanup(); + + public: + BoltRenderer(Camera* camera, glm::mat4 proj, ResourceManager* resManager); + ~BoltRenderer() override; + + void render3D(float dt, uint64_t frameId) override; + void beforeRender(MODE mode) override; + void afterRender() override; + void renderShadowMap() override; + void triggerBolt(); // Manuální spuštění blesku + }; +} + +#endif //SNAKE3_BOLTRENDERER_H \ No newline at end of file diff --git a/Renderer/Opengl/DepthMapRenderer.cpp b/Renderer/Opengl/DepthMapRenderer.cpp index 0663d991..01bdcfaf 100644 --- a/Renderer/Opengl/DepthMapRenderer.cpp +++ b/Renderer/Opengl/DepthMapRenderer.cpp @@ -1,104 +1,186 @@ #include "DepthMapRenderer.h" namespace Renderer { - DepthMapRenderer::DepthMapRenderer(Camera *camera, glm::mat4 proj, ResourceManager *resManager) { + DepthMapRenderer::DepthMapRenderer(Camera *camera, const glm::mat4 &proj, ResourceManager *resManager) { resourceManager = resManager; this->camera = camera; this->projection = proj; - shader = resourceManager->getShader("shadowShader"); + shader = resourceManager->getShader("shadowShader").get(); shader->use(); shader->setMat4("projection", projection); shader->setInt("diffuseMap", 0); - shader->setInt("shadowMap", 1); + shader->setInt("shadowMap", 4); shader->setInt("normalMap", 2); shader->setInt("specularMap", 3); shader->setFloat("alpha", 1.0); glGenFramebuffers(1, &depthMapFBO); - // create depth texture - glGenTextures(1, &depthMap); - glBindTexture(GL_TEXTURE_2D, depthMap); - glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, - nullptr); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - - float borderColor[] = {1.0, 1.0, 1.0, 1.0}; - glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); - // attach depth texture as FBO's depth buffer - glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0); - glDrawBuffer(GL_NONE); - glReadBuffer(GL_NONE); - glBindFramebuffer(GL_FRAMEBUFFER, 0); + glGenTextures(1, &depthMap); { + glBindTexture(GL_TEXTURE_2D_ARRAY, depthMap); + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT, + SHADOW_WIDTH, SHADOW_HEIGHT, 3, 0, + GL_DEPTH_COMPONENT, GL_FLOAT, nullptr); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - auto texture = std::make_shared(); - texture->addTexture(depthMap); - resourceManager->addTexture("depth", texture); - lightPos = {0.0f, 7.0f, 11.0f}; - - glm::mat4 lightProjection, lightView; - float near_plane = 1.0f, far_plane = 17.5f; - lightProjection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane); - lightView = glm::lookAt(lightPos, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0, 1.0, 0.0)); - lightSpaceMatrix = lightProjection * lightView; - auto simpleDepthShader = resourceManager->getShader("shadowDepthShader"); - simpleDepthShader->use(); - simpleDepthShader->setMat4("lightSpaceMatrix", lightSpaceMatrix); + constexpr float borderColor[] = {1.0, 1.0, 1.0, 1.0}; + glTexParameterfv(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BORDER_COLOR, borderColor); + glBindTexture(GL_TEXTURE_2D_ARRAY, 0); + + const auto texture = std::make_shared(); + + texture->addTexture(depthMap); + resourceManager->replaceTexture("depth", texture); + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); } - void DepthMapRenderer::beforeRender() { + void DepthMapRenderer::beforeRender(const int index) const { + glDisable(GL_BLEND); glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT); glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO); + glDrawBuffer(GL_NONE); + glReadBuffer(GL_NONE); + glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + depthMap, 0, index); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + std::cerr << "Framebuffer not complete!" << std::endl; + } glClear(GL_DEPTH_BUFFER_BIT); } - void DepthMapRenderer::render() { + void DepthMapRenderer::render(float dt) const { shader->use(); - shader->setMat4("view", camera->getViewMatrix()); - shader->setVec3("viewPos", camera->getPosition()); - shader->setVec3("lightPos", lightPos); - shader->setMat4("lightSpaceMatrix", lightSpaceMatrix); - } - void DepthMapRenderer::renderShadowMap() { + int index = 0; + constexpr int NUM_CASCADES = 3; + float cascadeEnds[NUM_CASCADES]; + + const float lambda = 0.95f; + const float nearClip = 0.1f; + const float farClip = 1000.0f; + + for (int i = 0; i < NUM_CASCADES; i++) { + const float p = static_cast(i + 1) / static_cast(NUM_CASCADES); + const float logSplit = nearClip * std::pow(farClip / nearClip, p); + const float linSplit = nearClip + (farClip - nearClip) * p; + cascadeEnds[i] = lambda * logSplit + (1.0f - lambda) * linSplit; + } + + shader->setFloat("cascadeEnds" + std::to_string(0), cascadeEnds[0]); + shader->setFloat("cascadeEnds" + std::to_string(1), cascadeEnds[1]); + shader->setFloat("cascadeEnds" + std::to_string(2), cascadeEnds[2]); + + const auto basicShader = resourceManager->getShader("basicShader"); + basicShader->use(); + index = 0; + for (const auto &lightSpaceMatrice: lightSpaceMatrices) { + basicShader->setMat4("lightSpaceMatrix" + std::to_string(index), lightSpaceMatrice); + index++; + } + + basicShader->setFloat("cascadeEnds" + std::to_string(0), cascadeEnds[0]); + basicShader->setFloat("cascadeEnds" + std::to_string(1), cascadeEnds[1]); + basicShader->setFloat("cascadeEnds" + std::to_string(2), cascadeEnds[2]); } - void DepthMapRenderer::afterRender() { -// auto debugDepthQuad = resourceManager->getShader("debugQuadShader"); -// debugDepthQuad->use(); -// debugDepthQuad->setFloat("near_plane", 1.0f); -// debugDepthQuad->setFloat("far_plane", 7.5f); -// glActiveTexture(GL_TEXTURE0); -// glBindTexture(GL_TEXTURE_2D, depthMap); -// renderQuad(); + void DepthMapRenderer::bind(const int index, const glm::mat4 &lightSpaceMatrix) const { + shader->use(); + shader->setMat4("lightSpaceMatrix" + std::to_string(index), lightSpaceMatrix); + const auto simpleDepthShader = resourceManager->getShader("shadowDepthShader"); + simpleDepthShader->use(); + simpleDepthShader->setMat4("lightSpaceMatrix", lightSpaceMatrix); } - void DepthMapRenderer::renderQuad() { - if (quadVAO == 0) { - float quadVertices[] = { - // positions // texture Coords - -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, - -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, - 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, - 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, - }; - // setup plane VAO - glGenVertexArrays(1, &quadVAO); - glGenBuffers(1, &quadVBO); - glBindVertexArray(quadVAO); - glBindBuffer(GL_ARRAY_BUFFER, quadVBO); - glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW); - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *) nullptr); - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *) (3 * sizeof(float))); + std::vector DepthMapRenderer::computeLightSpaceMatrix(const shared_ptr &light) { + lightSpaceMatrices.clear(); + lightSpaceMatrices.resize(NUM_CASCADES); + + const glm::vec3 lightDir = glm::normalize(light->getDirection()); + + for (int i = 0; i < NUM_CASCADES; ++i) { + constexpr float cameraFar = 80.0f; + constexpr float cameraNear = 0.1f; + + const float cascadeNear = (i == 0) ? cameraNear : cameraNear + cascadeSplits[i - 1] * (cameraFar - cameraNear); + const float cascadeFar = cameraNear + cascadeSplits[i] * (cameraFar - cameraNear); + + auto frustumCorners = getFrustumCornersWorldSpace(cascadeNear, cascadeFar); + + glm::vec3 center(0.0f); + for (auto &corner: frustumCorners) center += corner; + center /= static_cast(frustumCorners.size()); + + glm::mat4 lightView = glm::lookAt( + center + lightDir, + center, + glm::vec3(0, 1, 0) + ); + + float minX = std::numeric_limits::max(); + float maxX = std::numeric_limits::lowest(); + float minY = std::numeric_limits::max(); + float maxY = std::numeric_limits::lowest(); + float minZ = std::numeric_limits::max(); + float maxZ = std::numeric_limits::lowest(); + + for (auto &corner: frustumCorners) { + glm::vec4 trf = lightView * glm::vec4(corner, 1.0f); + minX = std::min(minX, trf.x); + maxX = std::max(maxX, trf.x); + minY = std::min(minY, trf.y); + maxY = std::max(maxY, trf.y); + minZ = std::min(minZ, trf.z); + maxZ = std::max(maxZ, trf.z); + } + + constexpr float zMargin = 50.0f; + minZ -= zMargin; + maxZ += zMargin; + + glm::mat4 lightProjection = glm::ortho(minX, maxX, minY, maxY, -maxZ, -minZ); + + lightSpaceMatrices[i] = lightProjection * lightView; } - glBindVertexArray(quadVAO); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - glBindVertexArray(0); + + return lightSpaceMatrices; } -} // Renderer \ No newline at end of file + std::vector DepthMapRenderer::getFrustumCornersWorldSpace( + const float nearPlane, + const float farPlane + ) const { + constexpr float aspect = static_cast(1920) / 1080; + const float fov = glm::radians(camera->getZoom()); + const float tanHalfFov = tanf(fov * 0.5f); + + std::vector corners(8); + + const float nh = nearPlane * tanHalfFov; + const float nw = nh * aspect; + const float fh = farPlane * tanHalfFov; + const float fw = fh * aspect; + + const glm::vec3 nc = camera->getPosition() + camera->getFront() * nearPlane; + const glm::vec3 fc = camera->getPosition() + camera->getFront() * farPlane; + const glm::vec3 camUp = camera->getUp(); + const glm::vec3 camRight = camera->getRight(); + + // near plane + corners[0] = nc + camUp * nh - camRight * nw; + corners[1] = nc + camUp * nh + camRight * nw; + corners[2] = nc - camUp * nh + camRight * nw; + corners[3] = nc - camUp * nh - camRight * nw; + + // far plane + corners[4] = fc + camUp * fh - camRight * fw; + corners[5] = fc + camUp * fh + camRight * fw; + corners[6] = fc - camUp * fh + camRight * fw; + corners[7] = fc - camUp * fh - camRight * fw; + + return corners; + } +} // Renderer diff --git a/Renderer/Opengl/DepthMapRenderer.h b/Renderer/Opengl/DepthMapRenderer.h index 917a19a1..dfe93504 100644 --- a/Renderer/Opengl/DepthMapRenderer.h +++ b/Renderer/Opengl/DepthMapRenderer.h @@ -4,38 +4,51 @@ #include "../../Manager/ResourceManager.h" #include "../../Manager/ShaderManager.h" #include "../../Manager/Camera.h" -#include "BaseRenderer.h" -#include +#include "../../Lights/DirectionalLight.h" using namespace Manager; +using namespace Lights; namespace Renderer { + constexpr int SHADOW_WIDTH = 4096; + constexpr int SHADOW_HEIGHT = 4096; + constexpr int NUM_CASCADES = 3; - const int SHADOW_WIDTH = 4096; - const int SHADOW_HEIGHT = 4096; - - class DepthMapRenderer : public BaseRenderer { + class DepthMapRenderer { public: - DepthMapRenderer(Camera* camera, glm::mat4 proj, ResourceManager* resManager); - void render() override; - void beforeRender() override; - void afterRender() override; - void renderQuad(); - void renderShadowMap() override; + DepthMapRenderer(Camera *camera, const glm::mat4 &proj, ResourceManager *resManager); + + ~DepthMapRenderer() = default; + + void render(float dt) const; + + void beforeRender(int index) const; + + void afterRender() = delete; + + void renderShadowMap(); + + void bind(int index, const glm::mat4 &lightSpaceMatrix) const; + + std::vector computeLightSpaceMatrix(const shared_ptr &light); protected: - ResourceManager* resourceManager; - ShaderManager* shader; - Camera* camera; + ResourceManager *resourceManager; + ShaderManager *shader; + Camera *camera; glm::mat4 projection{}; - unsigned int depthMapFBO{}; - unsigned int depthMap{}; - glm::mat4 lightSpaceMatrix{}; + GLuint depthMapFBO{}; + GLuint depthMap{}; + std::vector lightSpaceMatrices; unsigned int quadVAO = 0; unsigned int quadVBO{}; - glm::vec3 lightPos{}; - }; + float cascadeSplits[NUM_CASCADES] = {0.1f, 0.3f, 1.0f}; + [[nodiscard]] std::vector getFrustumCornersWorldSpace( + float nearPlane, + float farPlane + ) const; + }; } // Renderer #endif //SNAKE3_DEPTHMAPRENDERER_H diff --git a/Renderer/Opengl/EatRemoveAnimateRenderer.cpp b/Renderer/Opengl/EatRemoveAnimateRenderer.cpp deleted file mode 100644 index d9216a71..00000000 --- a/Renderer/Opengl/EatRemoveAnimateRenderer.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "EatRemoveAnimateRenderer.h" - -namespace Renderer { - - EatRemoveAnimateRenderer::EatRemoveAnimateRenderer(Eat *eat, Camera *camera, - const glm::mat4 &projection, ResourceManager *resourceManager) : - EatRenderer(eat, camera, projection, resourceManager) { - mesh = resourceManager->getModel("coin")->getMesh(); - zoom = {0.013888889, 0.013888889, 0.013888889}; - } - - EatRemoveAnimateRenderer::~EatRemoveAnimateRenderer() { - delete eat; - } - - void EatRemoveAnimateRenderer::render() { - - if (eat && eat->isVisible()) { - baseShader->use(); - baseShader->setMat4("view", camera->getViewMatrix()); - baseShader->setMat4("projection", this->projection); - baseShader->setInt("diffuseMap", 0); - baseShader->setInt("normalMap", 1); - baseShader->setInt("specularMap", 2); - baseShader->setFloat("alpha", eat->getAlpha()); - baseShader->setBool("fogEnable", fog); - - // lighting info - // ------------- - glm::vec3 lightPos(camera->getPosition().x - 26, camera->getPosition().y - 26, 26.3f); - - glLoadIdentity(); - - texture1->bind(0); - texture2->bind(1); - texture3->bind(2); - - glm::vec3 position = eat->getPosition(); - const glm::vec4 *rotate = eat->getRotate(); - - double now = glfwGetTime(); - float angle = rotate[1].x; - if (now > lastTime + 0.0001) { - angle++; - position.z += 0.2; - - eat->setPosition(position); - lastTime = now; - } - - // Initialize matrices - glm::mat4 model = glm::mat4(1.0f); - // Transform the matrices to their correct form - model = glm::translate(model, {0.0, 0.0, 0.0}); - model = glm::scale(model, {zoom.x, zoom.y, zoom.z}); - //model = glm::translate(model, {-25.0, -25.0, -23.0f}); // levy spodni okraj - model = glm::translate(model, position); - model = glm::rotate(model, glm::radians(90.0f), {1.0, 0.0, 0.0f}); - model = glm::rotate(model, glm::radians(angle), {0.0, 1.0, 0.0f}); - - baseShader->setMat4("model", model); - baseShader->setVec3("viewPos", camera->getPosition()); - baseShader->setVec3("lightPos", lightPos); - - mesh->bind(); - glDrawElements(GL_TRIANGLES, (int) mesh->getIndices().size(), GL_UNSIGNED_INT, nullptr); - - eat->setRotate(rotate[0], {angle, 0, 1, 0}, rotate[2]); - - glActiveTexture(GL_TEXTURE0); - - eat->fadeStep(0.02f); - } - } - - void EatRemoveAnimateRenderer::beforeRender() { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - - void EatRemoveAnimateRenderer::afterRender() { - glDisable(GL_BLEND); - } - - void EatRemoveAnimateRenderer::renderShadowMap() { - } - -} // Renderer \ No newline at end of file diff --git a/Renderer/Opengl/EatRemoveAnimateRenderer.h b/Renderer/Opengl/EatRemoveAnimateRenderer.h deleted file mode 100644 index 0e6bb156..00000000 --- a/Renderer/Opengl/EatRemoveAnimateRenderer.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef SNAKE3_EATREMOVEANIMATERENDERER_H -#define SNAKE3_EATREMOVEANIMATERENDERER_H - -#include "BaseRenderer.h" -#include "EatRenderer.h" -#include "../../ItemsDto/Eat.h" -#include "../../Manager/ResourceManager.h" -#include "../../Manager/ShaderManager.h" -#include -#include - -using namespace ItemsDto; -using namespace Manager; - -namespace Renderer { - - class EatRemoveAnimateRenderer : public EatRenderer { - public: - explicit EatRemoveAnimateRenderer(Eat *eat, Camera *camera, const glm::mat4 &projection, - ResourceManager *resourceManager); - ~EatRemoveAnimateRenderer() override; - void render() override; - void renderShadowMap() override; - void beforeRender() override; - void afterRender() override; - protected: - double lastTime{}; - glm::vec3 zoom{}; - }; - -} // Renderer - -#endif //SNAKE3_EATREMOVEANIMATERENDERER_H diff --git a/Renderer/Opengl/EatRenderer.cpp b/Renderer/Opengl/EatRenderer.cpp deleted file mode 100644 index 8a97340b..00000000 --- a/Renderer/Opengl/EatRenderer.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include "EatRenderer.h" - -namespace Renderer { - - EatRenderer::EatRenderer(Eat *eat, Camera *camera, const glm::mat4 &projection, - ResourceManager *resourceManager) : eat(eat), camera(camera), - projection(projection), - resourceManager(resourceManager) { - mesh = resourceManager->getModel("coin")->getMesh(); - baseShader = resourceManager->getShader("normalShader"); - shadowShader = resourceManager->getShader("shadowDepthShader"); - texture1 = resourceManager->getTexture("Coin_Gold_albedo.png"); - texture2 = resourceManager->getTexture("Coin_Gold_nm.png"); - texture3 = resourceManager->getTexture("Coin_Gold_metalness.png"); - depthTexture = resourceManager->getTexture("depth"); - } - - void EatRenderer::render() { - baseShader->setMat4("view", camera->getViewMatrix()); - baseShader->setMat4("projection", projection); - baseShader->setInt("diffuseMap", 0); - baseShader->setInt("normalMap", 1); - baseShader->setInt("specularMap", 2); - baseShader->setFloat("alpha", 1.0); - baseShader->setVec3("viewPos", camera->getPosition()); - baseShader->setBool("parallaxEnable", false); - baseShader->setBool("fogEnable", fog); - texture1->bind(0); - texture2->bind(1); - texture3->bind(2); - renderScene(baseShader); - } - - void EatRenderer::renderShadowMap() { - depthTexture->bind(0); - renderScene(shadowShader); - } - - void EatRenderer::renderScene(ShaderManager *shader) { - if (eat->isVisible()) { - shader->use(); - glLoadIdentity(); - - glm::vec3 position = eat->getPosition(); - const glm::vec4 *rotate = eat->getRotate(); - - double now = glfwGetTime(); - float angle = rotate[1].x; - if (now > lastTime + 0.005) { - angle++; - lastTime = now; - } - - // Initialize matrices - glm::mat4 model = glm::mat4(1.0f); - // Transform the matrices to their correct form - model = glm::translate(model, {0.0, 0.0, 0.0}); - model = glm::scale(model, {0.013888889, 0.013888889, 0.013888889}); - //model = glm::translate(model, {-25.0, -25.0, -23.0f}); // levy spodni okraj - model = glm::translate(model, position); - model = glm::rotate(model, glm::radians(90.0f), {1.0, 0.0, 0.0f}); - model = glm::rotate(model, glm::radians(angle), {0.0, 1.0, 0.0f}); - - shader->setMat4("model", model); - - mesh->bind(); - glDrawElements(GL_TRIANGLES, (int) mesh->getIndices().size(), GL_UNSIGNED_INT, nullptr); - - eat->setRotate(rotate[0], {angle, 0, 1, 0}, rotate[2]); - - } - - glActiveTexture(GL_TEXTURE0); - } - - void EatRenderer::beforeRender() { - } - - void EatRenderer::afterRender() { - } -} \ No newline at end of file diff --git a/Renderer/Opengl/EatRenderer.h b/Renderer/Opengl/EatRenderer.h deleted file mode 100644 index 1448dfcd..00000000 --- a/Renderer/Opengl/EatRenderer.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef SNAKE3_EATRENDERER_H -#define SNAKE3_EATRENDERER_H - -#include "BaseRenderer.h" -#include "../../ItemsDto/Eat.h" -#include "../../Manager/ResourceManager.h" -#include "../../Manager/ShaderManager.h" -#include -#include - -using namespace ItemsDto; -using namespace Manager; - -namespace Renderer { - - class EatRenderer : public BaseRenderer { - public: - EatRenderer(Eat *eat, Camera *camera, const glm::mat4 &projection, - ResourceManager *resourceManager); - - void render() override; - void renderShadowMap() override; - void beforeRender() override; - void afterRender() override; - protected: - void renderScene(ShaderManager* shader); - Eat* eat; - Mesh* mesh; - Camera* camera; - glm::mat4 projection; - ResourceManager* resourceManager; - ShaderManager* baseShader; - ShaderManager* shadowShader; - TextureManager* texture1; - TextureManager* texture2; - TextureManager* texture3; - TextureManager* depthTexture; - double lastTime{}; - }; - -} // Renderer - -#endif //SNAKE3_EATRENDERER_H diff --git a/Renderer/Opengl/Effects/LightningFlashEffect.cpp b/Renderer/Opengl/Effects/LightningFlashEffect.cpp new file mode 100644 index 00000000..41b9605d --- /dev/null +++ b/Renderer/Opengl/Effects/LightningFlashEffect.cpp @@ -0,0 +1,88 @@ +#include "LightningFlashEffect.h" +#include + +namespace Effects { + LightningFlashEffect::LightningFlashEffect(Camera* camera, const glm::mat4 &proj, ResourceManager* resManager) { + this->camera = camera; + this->projection = proj; + this->resourceManager = resManager; + this->flashShader = resourceManager->getShader("flash").get(); + } + + void LightningFlashEffect::init() { + setupFullscreenQuad(); + flashShader->use(); + flashShader->setInt("screenTexture", 0); + } + + void LightningFlashEffect::triggerSequence(const std::vector& pulses, float duration) { + while (!pulseQueue.empty()) pulseQueue.pop(); + for (float p : pulses) pulseQueue.push(p); + pulseDuration = duration; + nextPulse(); + } + + void LightningFlashEffect::nextPulse() { + if (pulseQueue.empty()) { + flashing = false; + alpha = 0.0f; + } else { + alpha = pulseQueue.front(); + pulseQueue.pop(); + timer = 0.0f; + flashing = true; + } + } + + void LightningFlashEffect::update(float deltaTime) { + if (!flashing) return; + timer += deltaTime; + alpha = alpha * (1.0f - (timer / pulseDuration)); + if (timer >= pulseDuration) nextPulse(); + } + + bool LightningFlashEffect::isActive() const { + return flashing || alpha > 0.0f; + } + + void LightningFlashEffect::render() const { + if (alpha <= 0.0f) return; + + glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + flashShader->use(); + flashShader->setFloat("alpha", alpha); + + glBindVertexArray(quadVAO); + glDrawArrays(GL_TRIANGLES, 0, 6); + glBindVertexArray(0); + + glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + } + + void LightningFlashEffect::setupFullscreenQuad() { + float quadVertices[] = { + // positions // texCoords + -1.0f, 1.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 0.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 0.0f, + + -1.0f, 1.0f, 0.0f, 1.0f, + 1.0f, -1.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 1.0f + }; + + glGenVertexArrays(1, &quadVAO); + glGenBuffers(1, &quadVBO); + glBindVertexArray(quadVAO); + glBindBuffer(GL_ARRAY_BUFFER, quadVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float))); + } +} // Effects \ No newline at end of file diff --git a/Renderer/Opengl/Effects/LightningFlashEffect.h b/Renderer/Opengl/Effects/LightningFlashEffect.h new file mode 100644 index 00000000..80a154e2 --- /dev/null +++ b/Renderer/Opengl/Effects/LightningFlashEffect.h @@ -0,0 +1,43 @@ +#ifndef LIGHTNINGFLASHEFFECT_H +#define LIGHTNINGFLASHEFFECT_H + +#include +#include +#include "../../../Manager/ResourceManager.h" +#include "../../../Manager/ShaderManager.h" +#include "../../../Manager/Camera.h" + +using namespace Manager; + +namespace Effects { + +class LightningFlashEffect { +public: + LightningFlashEffect(Camera* camera, const glm::mat4 &proj, ResourceManager* resManager); + void init(); + void triggerSequence(const std::vector& pulses, float pulseDuration); + void update(float deltaTime); + void render() const; + bool isActive() const; + +private: + float alpha = 0.0f; + float pulseDuration = 0.1f; + float timer = 0.0f; + bool flashing = false; + + GLuint quadVAO = 0; + GLuint quadVBO = 0; + Camera* camera; + glm::mat4 projection{}; + ResourceManager* resourceManager; + ShaderManager* flashShader; + std::queue pulseQueue; + + void setupFullscreenQuad(); + void nextPulse(); +}; + +} // Effects + +#endif //LIGHTNINGFLASHEFFECT_H diff --git a/Renderer/Opengl/FireRenderer.cpp b/Renderer/Opengl/FireRenderer.cpp new file mode 100644 index 00000000..b3e77d15 --- /dev/null +++ b/Renderer/Opengl/FireRenderer.cpp @@ -0,0 +1,33 @@ +#include "FireRenderer.h" + +namespace Renderer { + FireRenderer::FireRenderer(Camera *camera, const glm::mat4 &projection, ResourceManager *resManager): + camera(camera), projection(projection) + { + fires = new FireParticleSystem(*resManager, 500); // 500 = počet částic + smokes = new SmokeParticleSystem(*resManager, 500); // 500 = počet částic + offset = glm::vec3(0.038, -0.012f, -0.76f); + } + + FireRenderer::~FireRenderer() { + delete fires; + delete smokes; + } + + void FireRenderer::render3D(const float dt, uint64_t frameId) { + fires->update(dt, offset); + smokes->update(dt, offset); + fires->render(camera->getViewMatrix(), projection); + smokes->render(camera->getViewMatrix(), projection); + } + + void FireRenderer::beforeRender(const MODE mode) { + this->mode = mode; + } + + void FireRenderer::afterRender() { + } + + void FireRenderer::renderShadowMap() { + } +} // Renderer \ No newline at end of file diff --git a/Renderer/Opengl/FireRenderer.h b/Renderer/Opengl/FireRenderer.h new file mode 100644 index 00000000..a4cfb16d --- /dev/null +++ b/Renderer/Opengl/FireRenderer.h @@ -0,0 +1,31 @@ +#ifndef FIRERENDERER_H +#define FIRERENDERER_H + +#include "BaseRenderer.h" +#include "../../Manager/Camera.h" +#include "../../Particle/SmokeParticleSystem.h" + +using namespace Particle; +using namespace std; +using namespace Manager; + +namespace Renderer { + + class FireRenderer : public BaseRenderer { + public: + FireRenderer(Camera *camera, const glm::mat4 &projection, ResourceManager *resManager); + ~FireRenderer() override; + void render3D(float dt, uint64_t frameId) override; + void beforeRender(MODE mode) override; + void afterRender() override; + void renderShadowMap() override; + protected: + FireParticleSystem *fires; + SmokeParticleSystem *smokes; + Camera* camera; + glm::mat4 projection{}; + glm::vec3 offset{}; + }; +} // Renderer + +#endif //FIRERENDERER_H diff --git a/Renderer/Opengl/GameFieldRenderer.cpp b/Renderer/Opengl/GameFieldRenderer.cpp deleted file mode 100644 index af365188..00000000 --- a/Renderer/Opengl/GameFieldRenderer.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "GameFieldRenderer.h" - -Renderer::GameFieldRenderer::GameFieldRenderer(GameField *item, Camera *camera, glm::mat4 proj, - ResourceManager *resManager) { - gameField = item; - resourceManager = resManager; - this->camera = camera; - this->projection = proj; - model = new GameFieldModel((*gameField->getTiles().begin())); - baseShader = resourceManager->getShader("shadowShader"); - shadowShader = resourceManager->getShader("shadowDepthShader"); - texture1 = resourceManager->getTexture("gamefield.bmp"); - texture2 = resourceManager->getTexture("depth"); - texture3 = resourceManager->getTexture("gamefield_normal.jpg"); - texture4 = resourceManager->getTexture("gamefield_specular.jpg"); -} - -Renderer::GameFieldRenderer::~GameFieldRenderer() { - delete gameField; - delete model; -} - -void Renderer::GameFieldRenderer::render() { - baseShader->use(); - baseShader->setMat4("view", camera->getViewMatrix()); - baseShader->setMat4("projection", projection); - baseShader->setVec3("viewPos", camera->getPosition()); - baseShader->setBool("shadowsEnable", shadow); - baseShader->setBool("fogEnable", fog); - - if (!shadow) { - glm::vec3 lightPos(camera->getPosition().x + 6, camera->getPosition().y + 6, -1.3f); - baseShader->setVec3("lightPos", lightPos); - } - - texture1->bind(0); - texture2->bind(1); - texture3->bind(2); - texture4->bind(3); - - renderScene(baseShader); -} - -void Renderer::GameFieldRenderer::renderShadowMap() { - shadowShader->use(); - texture1->bind(0); - renderScene(shadowShader); -} - -void Renderer::GameFieldRenderer::renderScene(ShaderManager *shader) { - int x = 0; - - for (auto Iter = gameField->getTiles().begin(); Iter < gameField->getTiles().end(); Iter++) { - glLoadIdentity(); - if ((*Iter)->isVisible()) { - - glm::vec3 position = (*Iter)->getPosition(); - glm::vec3 zoom = (*Iter)->getZoom(); - - // Initialize matrices - glm::mat4 model = glm::mat4(1.0f); - model = glm::translate(model, position); - shader->setMat4("model", model); - - this->model->getMesh()->bind(); - glDrawElements(GL_TRIANGLES, (int) this->model->getMesh()->getIndices().size(), GL_UNSIGNED_INT, nullptr); - x++; - } - } -} - -void Renderer::GameFieldRenderer::beforeRender() { -} - -void Renderer::GameFieldRenderer::afterRender() { -} diff --git a/Renderer/Opengl/GameFieldRenderer.h b/Renderer/Opengl/GameFieldRenderer.h deleted file mode 100644 index 4629bd2c..00000000 --- a/Renderer/Opengl/GameFieldRenderer.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef SNAKE3_GAMEFIELDRENDERER_H -#define SNAKE3_GAMEFIELDRENDERER_H - -#include "../../ItemsDto/GameField.h" -#include "../../Manager/ResourceManager.h" -#include "../../Manager/ShaderManager.h" -#include "Model/GameFieldModel.h" -#include "BaseRenderer.h" -#include -#include -#include - -using namespace ItemsDto; -using namespace Model; -using namespace Manager; - -namespace Renderer { - class GameFieldRenderer : public BaseRenderer { - public: - explicit GameFieldRenderer(GameField *item, Camera* camera, glm::mat4 proj, ResourceManager* resManager); - ~GameFieldRenderer() override; - void render() override; - void beforeRender() override; - void afterRender() override; - void renderShadowMap() override; - - protected: - void renderScene(ShaderManager* shader); - ResourceManager* resourceManager; - ShaderManager* baseShader; - ShaderManager* shadowShader; - TextureManager* texture1; - TextureManager* texture2; - TextureManager* texture3; - TextureManager* texture4; - GameField* gameField; - GameFieldModel* model; - Camera* camera; - glm::mat4 projection{}; - }; -} - -#endif //SNAKE3_GAMEFIELDRENDERER_H diff --git a/Renderer/Opengl/Line.h b/Renderer/Opengl/Line.h index 742f245c..4b3a3e52 100644 --- a/Renderer/Opengl/Line.h +++ b/Renderer/Opengl/Line.h @@ -1,35 +1,32 @@ #include "../../stdafx.h" #include -#include -#include using namespace std; -using namespace glm; class Line { int shaderProgram; - unsigned int VBO, VAO; + unsigned int VBO{}, VAO{}; vector vertices; - vec3 startPoint; - vec3 endPoint; - mat4 MVP; - vec3 lineColor; + glm::vec3 startPoint{}; + glm::vec3 endPoint{}; + glm::mat4 MVP{}; + glm::vec3 lineColor{}; public: - Line(vec3 start, vec3 end) { + Line(glm::vec3 start, glm::vec3 end) { startPoint = start; endPoint = end; - lineColor = vec3(1,1,1); - MVP = mat4(1.0f); + lineColor = glm::vec3(1,1,1); + MVP = glm::mat4(1.0f); - const char *vertexShaderSource = "#version 330 core\n" + const auto vertexShaderSource = "#version 330 core\n" "layout (location = 0) in vec3 aPos;\n" "uniform mat4 MVP;\n" "void main()\n" "{\n" " gl_Position = MVP * vec4(aPos.x, aPos.y, aPos.z, 1.0);\n" "}\0"; - const char *fragmentShaderSource = "#version 330 core\n" + const auto fragmentShaderSource = "#version 330 core\n" "out vec4 FragColor;\n" "uniform vec3 color;\n" "void main()\n" @@ -39,13 +36,13 @@ class Line { // vertex baseShader int vertexShader = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); + glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr); glCompileShader(vertexShader); // check for baseShader compile errors // fragment baseShader - int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); + const int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr); glCompileShader(fragmentShader); // check for baseShader compile errors @@ -72,7 +69,7 @@ class Line { glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices.data(), GL_STATIC_DRAW); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), static_cast(nullptr)); glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); @@ -80,28 +77,24 @@ class Line { } - int setMVP(mat4 mvp) { + void setMVP(const glm::mat4 &mvp) { MVP = mvp; - return 1; } - int setColor(vec3 color) { + void setColor(const glm::vec3 color) { lineColor = color; - return 1; } - int draw() { + void draw() { glUseProgram(shaderProgram); glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "MVP"), 1, GL_FALSE, &MVP[0][0]); glUniform3fv(glGetUniformLocation(shaderProgram, "color"), 1, &lineColor[0]); glBindVertexArray(VAO); glDrawArrays(GL_LINES, 0, 2); - return 1; } ~Line() { - glDeleteVertexArrays(1, &VAO); glDeleteBuffers(1, &VBO); glDeleteProgram(shaderProgram); diff --git a/Renderer/Opengl/Material/2D/Font.cpp b/Renderer/Opengl/Material/2D/Font.cpp new file mode 100644 index 00000000..73fc7723 --- /dev/null +++ b/Renderer/Opengl/Material/2D/Font.cpp @@ -0,0 +1,91 @@ +#include "Font.h" +#include +#include +#include +#include +#include +#include FT_FREETYPE_H + +namespace Material { + Font::Font(std::string path, const int pixelSize) + : fontSize(pixelSize), ascender_pixels(0), fontPath(std::move(path)) { + buildAtlas(); + } + + Font::~Font() { + if (atlasTexture) + glDeleteTextures(1, &atlasTexture); + } + + float Font::getAscenderPixels() const { + return ascender_pixels; + } + + void Font::buildAtlas() { + constexpr int atlasWidth = 1024; + constexpr int atlasHeight = 1024; + FT_Library ft; + if (FT_Init_FreeType(&ft)) { + std::cerr << "FT init fail\n"; + return; + } + FT_Face face; + if (FT_New_Face(ft, fontPath.c_str(), 0, &face)) { + std::cerr << "FT load face fail\n"; + return; + } + FT_Set_Pixel_Sizes(face, 0, fontSize); + + std::vector atlasData(atlasWidth * atlasHeight, 0); + + int x = 0, y = 0, rowH = 0; + + for (unsigned char c = 32; c < 128; c++) { + if (FT_Load_Char(face, c,FT_LOAD_RENDER)) continue; + const FT_Bitmap &bmp = face->glyph->bitmap; + if (x + bmp.width >= atlasWidth) { + x = 0; + y += rowH; + rowH = 0; + } + for (int row = 0; row < bmp.rows; row++) + for (int col = 0; col < bmp.width; col++) + atlasData[(x + col) + (y + row) * atlasWidth] = bmp.buffer[col + row * bmp.width]; + + Character ch{}; + ch.size = glm::ivec2(bmp.width, bmp.rows); + ch.bearing = glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top); + ch.advance = face->glyph->advance.x >> 6; + ch.uvOffset = glm::vec2(static_cast(x) / atlasWidth, static_cast(y) / atlasHeight); + ch.uvSize = glm::vec2(static_cast(bmp.width) / atlasWidth, static_cast(bmp.rows) / atlasHeight); + characters.insert({static_cast(c), ch}); + + x += static_cast(bmp.width); + if (bmp.rows > rowH) rowH = static_cast(bmp.rows); + } + + ascender_pixels = static_cast(face->size->metrics.ascender >> 6); + ascender = face->ascender; + descender = face->descender; + lineHeight = face->height; + scale = static_cast(fontSize) / static_cast(face->units_per_EM); + ascender_pixels = lineHeight * scale; + + FT_Done_Face(face); + FT_Done_FreeType(ft); + + glGenTextures(1, &atlasTexture); + glBindTexture(GL_TEXTURE_2D, atlasTexture); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexImage2D(GL_TEXTURE_2D, 0,GL_RED, atlasWidth, atlasHeight, 0,GL_RED,GL_UNSIGNED_BYTE, atlasData.data()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + + const Character *Font::getCharacter(const char c) const { + const auto it = characters.find(c); + + return it != characters.end() ? &it->second : nullptr; + } + +} // Material diff --git a/Renderer/Opengl/Material/2D/Font.h b/Renderer/Opengl/Material/2D/Font.h new file mode 100644 index 00000000..c61b8370 --- /dev/null +++ b/Renderer/Opengl/Material/2D/Font.h @@ -0,0 +1,52 @@ +#ifndef SNAKE3_FONT_H +#define SNAKE3_FONT_H + +#include +#include +#include +#include + +struct Character { + glm::vec2 uvOffset; // levý dolní roh znaku v atlasu + glm::vec2 uvSize; // velikost uv regionu (šířka/výška) + glm::ivec2 size; // velikost bitmapy v pixelech + glm::ivec2 bearing; // offset od baseline + unsigned int advance; // vzdálenost pro další znak +}; + +namespace Material { + class Font { + public: + explicit Font(std::string path, int pixelSize = 48); + + ~Font(); + + const Character *getCharacter(char c) const; + + unsigned int getAtlasTextureId() const { return atlasTexture; } + + int getSize() const { return fontSize; } + + [[nodiscard]] float getAscenderPixels() const; + + float getAscenderPx() const { return ascender * scale; } + float getDescenderPx() const { return descender * scale; } + float getLineHeightPx() const { return lineHeight * scale; } + + private: + void buildAtlas(); + + std::unordered_map characters; + unsigned int atlasTexture = 0; + int fontSize; + float ascender_pixels; + float scale{}; + float ascender{}; + float descender{}; + float lineHeight{}; + std::string fontPath; + std::vector atlasBuffer; + }; +} // Material + +#endif //SNAKE3_FONT_H diff --git a/Renderer/Opengl/Material/2D/LabelSettings.cpp b/Renderer/Opengl/Material/2D/LabelSettings.cpp new file mode 100644 index 00000000..d6c256f0 --- /dev/null +++ b/Renderer/Opengl/Material/2D/LabelSettings.cpp @@ -0,0 +1,22 @@ +#include "LabelSettings.h" + +namespace Material { + LabelSettings::LabelSettings(const std::shared_ptr &font) : font(font), color(glm::vec3(1.0f)) { + } + + shared_ptr LabelSettings::getFont() const { + return font; + } + + glm::vec3 LabelSettings::getColor() const { + return color; + } + + void LabelSettings::setColor(const glm::vec3 &color) { + this->color = color; + } + + float LabelSettings::getLetterSpacing() const { + return letterSpacing; + } +} // Material \ No newline at end of file diff --git a/Renderer/Opengl/Material/2D/LabelSettings.h b/Renderer/Opengl/Material/2D/LabelSettings.h new file mode 100644 index 00000000..78995901 --- /dev/null +++ b/Renderer/Opengl/Material/2D/LabelSettings.h @@ -0,0 +1,32 @@ +#ifndef SNAKE3_LABELSETTINGS_H +#define SNAKE3_LABELSETTINGS_H + +#include + +#include "Font.h" + +using namespace std; + +namespace Material { + class LabelSettings { + public: + explicit LabelSettings(const std::shared_ptr &font); + [[nodiscard]] shared_ptr getFont() const; + [[nodiscard]] glm::vec3 getColor() const; + void setColor(const glm::vec3 &color); + [[nodiscard]] float getLetterSpacing() const; + // void setLetterSpacing(float letterSpacing); + // float getLineSpacing() const; + // void setLineSpacing(float lineSpacing); + // bool isWordWrap() const; + // void setWordWrap(bool wordWrap); + private: + std::shared_ptr font; + glm::vec3 color; + float letterSpacing = 0.0f; + float lineSpacing = 1.0f; + bool wordWrap = false; + }; +} // Material + +#endif //SNAKE3_LABELSETTINGS_H \ No newline at end of file diff --git a/Renderer/Opengl/Material/BaseMaterial.h b/Renderer/Opengl/Material/BaseMaterial.h new file mode 100644 index 00000000..96bbe870 --- /dev/null +++ b/Renderer/Opengl/Material/BaseMaterial.h @@ -0,0 +1,19 @@ +#ifndef SNAKE3_BASEMATERIAL_H +#define SNAKE3_BASEMATERIAL_H + +#include + +#include "Interface/BlendingInterface.h" + +using namespace std; + +namespace Material { + class BaseMaterial : public BlendingInterface { + public: + ~BaseMaterial() override = default; + + [[nodiscard]] virtual shared_ptr clone() const = 0; + }; +} // Material + +#endif //SNAKE3_BASEMATERIAL_H diff --git a/Renderer/Opengl/Material/IAlbedoMaterial.h b/Renderer/Opengl/Material/IAlbedoMaterial.h new file mode 100644 index 00000000..2c07aabc --- /dev/null +++ b/Renderer/Opengl/Material/IAlbedoMaterial.h @@ -0,0 +1,16 @@ +#ifndef SNAKE3_IALBEDOMATERIAL_H +#define SNAKE3_IALBEDOMATERIAL_H + +#include "../../../Manager/TextureManager.h" + +using namespace std; +using namespace Manager; + +class IAlbedoMaterial { +public: + virtual ~IAlbedoMaterial() = default; + + [[nodiscard]] virtual shared_ptr getAlbedo() const = 0; +}; + +#endif //SNAKE3_IALBEDOMATERIAL_H diff --git a/Renderer/Opengl/Material/IUniform.cpp b/Renderer/Opengl/Material/IUniform.cpp new file mode 100644 index 00000000..620cf154 --- /dev/null +++ b/Renderer/Opengl/Material/IUniform.cpp @@ -0,0 +1,4 @@ +#include "IUniform.h" + +namespace Material { +} // Material \ No newline at end of file diff --git a/Renderer/Opengl/Material/IUniform.h b/Renderer/Opengl/Material/IUniform.h new file mode 100644 index 00000000..7f8b0683 --- /dev/null +++ b/Renderer/Opengl/Material/IUniform.h @@ -0,0 +1,19 @@ +#ifndef SNAKE3_IUNIFORM_H +#define SNAKE3_IUNIFORM_H + +#include +#include "../../../Manager/ShaderManager.h" + +using namespace std; +using namespace Manager; + +namespace Material { + class IUniform { + public: + virtual ~IUniform() = default; + virtual void bind(const shared_ptr& shader, const string& name) = 0; + [[nodiscard]] virtual shared_ptr clone() const = 0; + }; +} // Material + +#endif //SNAKE3_IUNIFORM_H \ No newline at end of file diff --git a/Renderer/Opengl/Material/Interface/BlendingInterface.cpp b/Renderer/Opengl/Material/Interface/BlendingInterface.cpp new file mode 100644 index 00000000..0b69a643 --- /dev/null +++ b/Renderer/Opengl/Material/Interface/BlendingInterface.cpp @@ -0,0 +1,11 @@ +#include "BlendingInterface.h" + +namespace Material { + void BlendingInterface::setBlending(const Blending blending) { + this->blending = blending; + } + + Blending BlendingInterface::getBlending() const { + return blending; + } +} // Material \ No newline at end of file diff --git a/Renderer/Opengl/Material/Interface/BlendingInterface.h b/Renderer/Opengl/Material/Interface/BlendingInterface.h new file mode 100644 index 00000000..6911ea92 --- /dev/null +++ b/Renderer/Opengl/Material/Interface/BlendingInterface.h @@ -0,0 +1,22 @@ +#ifndef SNAKE3_INTERFACE_BLENDING_H +#define SNAKE3_INTERFACE_BLENDING_H + +#include "../../../../Tools/Blending.h" + +using namespace Tools; + +namespace Material { + class BlendingInterface { + public: + virtual ~BlendingInterface() = default; + + void setBlending(Blending blending); + + [[nodiscard]] Blending getBlending() const; + + protected: + Blending blending = Blending::Opaque; + }; +} // Material + +#endif //SNAKE3_INTERFACE_BLENDING_H \ No newline at end of file diff --git a/Renderer/Opengl/Material/Particle/BaseProcessMaterial.cpp b/Renderer/Opengl/Material/Particle/BaseProcessMaterial.cpp new file mode 100644 index 00000000..4445f9ff --- /dev/null +++ b/Renderer/Opengl/Material/Particle/BaseProcessMaterial.cpp @@ -0,0 +1,247 @@ +#include "BaseProcessMaterial.h" + +namespace Material { + BaseProcessMaterial::BaseProcessMaterial(const shared_ptr &resource_manager) + : resourceManager(resource_manager) { + } + + float BaseProcessMaterial::get_life_min() const { + return lifeMin; + } + + void BaseProcessMaterial::set_life_min(const float life_min) { + lifeMin = life_min; + } + + float BaseProcessMaterial::get_life_max() const { + return lifeMax; + } + + void BaseProcessMaterial::set_life_max(const float life_max) { + lifeMax = life_max; + } + + float BaseProcessMaterial::get_size_min() const { + return sizeMin; + } + + void BaseProcessMaterial::set_size_min(const float size_min) { + sizeMin = size_min; + } + + float BaseProcessMaterial::get_size_max() const { + return sizeMax; + } + + void BaseProcessMaterial::set_size_max(const float size_max) { + sizeMax = size_max; + } + + float BaseProcessMaterial::get_stretch() const { + return stretch; + } + + void BaseProcessMaterial::set_stretch(const float stretch) { + this->stretch = stretch; + } + + glm::vec3 BaseProcessMaterial::get_vel_min() const { + return velMin; + } + + void BaseProcessMaterial::set_vel_min(const glm::vec3 &vel_min) { + velMin = vel_min; + } + + glm::vec3 BaseProcessMaterial::get_vel_max() const { + return velMax; + } + + void BaseProcessMaterial::set_vel_max(const glm::vec3 &vel_max) { + velMax = vel_max; + } + + glm::vec3 BaseProcessMaterial::get_gravity() const { + return gravity; + } + + void BaseProcessMaterial::set_gravity(const glm::vec3 &gravity) { + this->gravity = gravity; + } + + float BaseProcessMaterial::get_emitter_radius() const { + return emitterRadius; + } + + void BaseProcessMaterial::set_emitter_radius(const float emitter_radius) { + emitterRadius = emitter_radius; + } + + float BaseProcessMaterial::get_emitter_y_offset() const { + return emitterYOffset; + } + + void BaseProcessMaterial::set_emitter_y_offset(const float emitter_y_offset) { + emitterYOffset = emitter_y_offset; + } + + glm::vec4 BaseProcessMaterial::get_color_start() const { + return colorStart; + } + + void BaseProcessMaterial::set_color_start(const glm::vec4 &color_start) { + colorStart = color_start; + } + + glm::vec4 BaseProcessMaterial::get_color_end() const { + return colorEnd; + } + + void BaseProcessMaterial::set_color_end(const glm::vec4 &color_end) { + colorEnd = color_end; + } + + float BaseProcessMaterial::get_spawn_per_frame() const { + return spawnPerFrame; + } + + void BaseProcessMaterial::set_spawn_per_frame(const float spawn_per_frame) { + spawnPerFrame = spawn_per_frame; + } + + bool BaseProcessMaterial::is_smooth_start() const { + return smoothStart; + } + + void BaseProcessMaterial::set_smooth_start(const bool smooth_start) { + smoothStart = smooth_start; + } + + float BaseProcessMaterial::get_warmup_time() const { + return warmupTime; + } + + void BaseProcessMaterial::set_warmup_time(const float warmup_time) { + warmupTime = warmup_time; + } + + int BaseProcessMaterial::get_warmup_substeps() const { + return warmupSubsteps; + } + + void BaseProcessMaterial::set_warmup_substeps(const int warmup_substeps) { + warmupSubsteps = warmup_substeps; + } + + float BaseProcessMaterial::get_first_frame_clamp() const { + return firstFrameClamp; + } + + void BaseProcessMaterial::set_first_frame_clamp(const float first_frame_clamp) { + firstFrameClamp = first_frame_clamp; + } + + float BaseProcessMaterial::get_color_sensitivity() const { + return colorSensitivity; + } + + void BaseProcessMaterial::set_color_sensitivity(const float color_sensitivity) { + colorSensitivity = color_sensitivity; + } + + glm::vec2 BaseProcessMaterial::get_turbulence() const { + return turbulence; + } + + void BaseProcessMaterial::set_turbulence(const glm::vec2 &turbulence) { + this->turbulence = turbulence; + } + + int BaseProcessMaterial::get_spawn_shape() const { + return spawnShape; + } + + void BaseProcessMaterial::set_spawn_shape(const int spawn_shape) { + spawnShape = spawn_shape; + } + + int BaseProcessMaterial::get_respawn_mode() const { + return respawnMode; + } + + void BaseProcessMaterial::set_respawn_mode(const int respawn_mode) { + respawnMode = respawn_mode; + } + + float BaseProcessMaterial::get_min_radius() const { + return minRadius; + } + + void BaseProcessMaterial::set_min_radius(const float min_radius) { + minRadius = min_radius; + } + + float BaseProcessMaterial::get_max_radius() const { + return maxRadius; + } + + void BaseProcessMaterial::set_max_radius(const float max_radius) { + maxRadius = max_radius; + } + + float BaseProcessMaterial::get_spawn_height() const { + return spawnHeight; + } + + void BaseProcessMaterial::set_spawn_height(const float spawn_height) { + spawnHeight = spawn_height; + } + + float BaseProcessMaterial::get_time_offset() const { + return timeOffset; + } + + void BaseProcessMaterial::set_time_offset(const float time_offset) { + timeOffset = time_offset; + } + + std::string BaseProcessMaterial::get_texture() const { + return texture; + } + + void BaseProcessMaterial::set_texture(const std::string &texture) { + this->texture = texture; + } + + ParticleMode BaseProcessMaterial::get_mode() const { + return mode; + } + + void BaseProcessMaterial::set_mode(const ParticleMode mode) { + this->mode = mode; + } + + glm::vec3 BaseProcessMaterial::get_emitter_pos() const { + return emitterPos; + } + + void BaseProcessMaterial::set_emitter_pos(const glm::vec3 &emitter_pos) { + emitterPos = emitter_pos; + } + + glm::vec2 BaseProcessMaterial::get_emitter_size() const { + return emitterSize; + } + + void BaseProcessMaterial::set_emitter_size(const glm::vec2 &emitter_size) { + emitterSize = emitter_size; + } + + float BaseProcessMaterial::get_drag() const { + return drag; + } + + void BaseProcessMaterial::set_drag(const float drag) { + this->drag = drag; + } +} // Material \ No newline at end of file diff --git a/Renderer/Opengl/Material/Particle/BaseProcessMaterial.h b/Renderer/Opengl/Material/Particle/BaseProcessMaterial.h new file mode 100644 index 00000000..b7df7ebe --- /dev/null +++ b/Renderer/Opengl/Material/Particle/BaseProcessMaterial.h @@ -0,0 +1,198 @@ +#ifndef SNAKE3_BASEPROCESSMATERIAL_H +#define SNAKE3_BASEPROCESSMATERIAL_H + +#include +#include +#include +#include +#include + +#include "../../../../Manager/ResourceManager.h" + +using namespace std; + +namespace Material { + class BaseProcessMaterial; + + enum ParticleMode { + Mesh3D = 2, + Billboard = 1, + Stretched = 0 + }; + + class BaseProcessMaterial { + public: + virtual ~BaseProcessMaterial() = default; + + explicit BaseProcessMaterial(const shared_ptr &resource_manager); + + [[nodiscard]] float get_life_min() const; + + void set_life_min(float life_min); + + [[nodiscard]] float get_life_max() const; + + void set_life_max(float life_max); + + [[nodiscard]] float get_size_min() const; + + void set_size_min(float size_min); + + [[nodiscard]] float get_size_max() const; + + void set_size_max(float size_max); + + [[nodiscard]] float get_stretch() const; + + void set_stretch(float stretch); + + [[nodiscard]] glm::vec3 get_vel_min() const; + + void set_vel_min(const glm::vec3 &vel_min); + + [[nodiscard]] glm::vec3 get_vel_max() const; + + void set_vel_max(const glm::vec3 &vel_max); + + [[nodiscard]] glm::vec3 get_gravity() const; + + void set_gravity(const glm::vec3 &gravity); + + [[nodiscard]] float get_emitter_radius() const; + + void set_emitter_radius(float emitter_radius); + + [[nodiscard]] float get_emitter_y_offset() const; + + void set_emitter_y_offset(float emitter_y_offset); + + [[nodiscard]] glm::vec4 get_color_start() const; + + void set_color_start(const glm::vec4 &color_start); + + [[nodiscard]] glm::vec4 get_color_end() const; + + void set_color_end(const glm::vec4 &color_end); + + [[nodiscard]] float get_spawn_per_frame() const; + + void set_spawn_per_frame(float spawn_per_frame); + + [[nodiscard]] bool is_smooth_start() const; + + void set_smooth_start(bool smooth_start); + + [[nodiscard]] float get_warmup_time() const; + + void set_warmup_time(float warmup_time); + + [[nodiscard]] int get_warmup_substeps() const; + + void set_warmup_substeps(int warmup_substeps); + + [[nodiscard]] float get_first_frame_clamp() const; + + void set_first_frame_clamp(float first_frame_clamp); + + [[nodiscard]] float get_color_sensitivity() const; + + void set_color_sensitivity(float color_sensitivity); + + [[nodiscard]] glm::vec2 get_turbulence() const; + + void set_turbulence(const glm::vec2 &turbulence); + + [[nodiscard]] int get_spawn_shape() const; + + void set_spawn_shape(int spawn_shape); + + [[nodiscard]] int get_respawn_mode() const; + + void set_respawn_mode(int respawn_mode); + + [[nodiscard]] float get_min_radius() const; + + void set_min_radius(float min_radius); + + [[nodiscard]] float get_max_radius() const; + + void set_max_radius(float max_radius); + + [[nodiscard]] float get_spawn_height() const; + + void set_spawn_height(float spawn_height); + + [[nodiscard]] float get_time_offset() const; + + void set_time_offset(float time_offset); + + [[nodiscard]] std::string get_texture() const; + + void set_texture(const std::string &texture); + + [[nodiscard]] ParticleMode get_mode() const; + + void set_mode(ParticleMode mode); + + [[nodiscard]] glm::vec3 get_emitter_pos() const; + + void set_emitter_pos(const glm::vec3 &emitter_pos); + + [[nodiscard]] glm::vec2 get_emitter_size() const; + + void set_emitter_size(const glm::vec2 &emitter_size); + + [[nodiscard]] float get_drag() const; + + void set_drag(float drag); + + virtual void bind(shared_ptr shader) const = 0; + + virtual void update(shared_ptr shader, int maxParticles, float timeAccum, float timeOffset, float stepDt) = 0; + + protected: + float lifeMin = 1.0f; + float lifeMax = 2.0f; + float sizeMin = 0.02f; + float sizeMax = 0.08f; + float stretch = 0.15f; + + // rychlost a gravitace + glm::vec3 velMin = {-0.1f, 0.6f, -0.1f}; + glm::vec3 velMax = {0.1f, 1.6f, 0.1f}; + glm::vec3 gravity = {0.0f, -0.4f, 0.0f}; + + // kruhovy emitor + glm::vec3 emitterPos = {0.0f, 0.0f, 0.0f}; + glm::vec2 emitterSize = {0.0f, 0.0f}; + float emitterRadius = 0.05f; + float emitterYOffset = 0.0f; + + // barvy + glm::vec4 colorStart = {1.0f, 1.0f, 1.0f, 1.0f}; + glm::vec4 colorEnd = {1.0f, 1.0f, 1.0f, 0.0f}; + + float spawnPerFrame = 1.0f; + bool smoothStart = true; + float warmupTime = 2.5f; // doba náběhu (s) + int warmupSubsteps = 4; // počet dílčích kroků v prvním snímku + float firstFrameClamp = 1.0f / 30.0f; // maximální dt na prvním snímku + float colorSensitivity = 1.0f; + + glm::vec2 turbulence = {0.0f, 0.0f}; // x=sila, y=frekvence + int spawnShape = 0; // 0 = Local, 1 = Environment Ring, 2 = Sphere + int respawnMode = 0; // 0 = Die, 1 = Wrap infinite + float minRadius = 0.0f; // vnitrni polomer (safe zone) + float maxRadius = 1.0f; // vnejsi polomer (area) + float spawnHeight = 10.0f; // vyska sloupce + float timeOffset = 0.0f; + float drag = 0.0f; + + std::string texture; + ParticleMode mode = Billboard; + + shared_ptr resourceManager; + }; +} // Material + +#endif //SNAKE3_BASEPROCESSMATERIAL_H diff --git a/Renderer/Opengl/Material/Particle/ParticleProcessMaterial.cpp b/Renderer/Opengl/Material/Particle/ParticleProcessMaterial.cpp new file mode 100644 index 00000000..b0adfa02 --- /dev/null +++ b/Renderer/Opengl/Material/Particle/ParticleProcessMaterial.cpp @@ -0,0 +1,83 @@ +#include "ParticleProcessMaterial.h" + +namespace Material { + ParticleProcessMaterial::~ParticleProcessMaterial() { + glDeleteBuffers(1, &uboID); + } + + ParticleProcessMaterial::ParticleProcessMaterial(const shared_ptr &resource_manager) + : BaseProcessMaterial(resource_manager) { + glGenBuffers(1, &uboID); + glBindBuffer(GL_UNIFORM_BUFFER, uboID); + glBufferData(GL_UNIFORM_BUFFER, sizeof(ParticleDataGPU), nullptr, GL_DYNAMIC_DRAW); + glBindBufferBase(GL_UNIFORM_BUFFER, 0, uboID); + } + + void ParticleProcessMaterial::bind(const shared_ptr shader) const { + shader->use(); + shader->setInt("u_mode", mode); + shader->setFloat("u_lifeMin", lifeMin); + shader->setFloat("u_lifeMax", lifeMax); + shader->setFloat("u_sizeMin", sizeMin); + shader->setFloat("u_sizeMax", sizeMax); + shader->setFloat("u_stretch", stretch); + shader->setVec4("u_colorStart", colorStart); + shader->setVec4("u_colorEnd", colorEnd); + shader->setFloat("u_colorSensitivity", colorSensitivity); + + if (!texture.empty()) { + shader->setInt("uTexture0", 0); + resourceManager->getTexture(texture)->bind(); + } + } + + void ParticleProcessMaterial::update(const shared_ptr shader, const int maxParticles, + const float timeAccum, const float timeOffset, const float stepDt) { + const ParticleDataGPU gpuData = prepareUniformData(); + + glBindBuffer(GL_UNIFORM_BUFFER, uboID); + glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(ParticleDataGPU), &gpuData); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + glBindBufferBase(GL_UNIFORM_BUFFER, 0, uboID); + + shader->use(); + shader->setUniformBlock("ParticleParams", 0); + shader->setFloat("u_dt", stepDt); + shader->setFloat("u_timeAccum", timeAccum + timeOffset); + shader->setFloat("u_spawnPerFrame", spawnPerFrame); + } + + ParticleProcessMaterial::ParticleDataGPU ParticleProcessMaterial::prepareUniformData() const { + ParticleDataGPU data{}; + + // 1. Life & Size (bez stretch) + data.u_lifeSizeStretch = glm::vec4(lifeMin, lifeMax, sizeMin, sizeMax); + + // 2. Velocity Min + Stretch (packed) + data.u_velMinStretch = glm::vec4(velMin, stretch); + + // 3. Velocity Max + Drag (packed) + data.u_velMaxDrag = glm::vec4(velMax, drag); + + // 4. Gravity + colorSensitivity + data.u_gravity = glm::vec4(gravity, colorSensitivity); + + // 5. Emitter Pos + Shape (Shape přetypujeme na float) + data.u_emitterPosShape = glm::vec4(emitterPos, static_cast(spawnShape)); + + // 6. Emitter Size + Radius + Offset + data.u_emitterSizeRadius = glm::vec4(emitterSize, emitterRadius, emitterYOffset); + + // 7. Spawn Area Settings + data.u_spawnArea = glm::vec4(minRadius, maxRadius, spawnHeight, spawnPerFrame); + + // 8. Turbulence + Time + RespawnMode + data.u_turbulenceTime = glm::vec4(turbulence, timeOffset, static_cast(respawnMode)); + + // 9. Colors + data.u_colorStart = colorStart; + data.u_colorEnd = colorEnd; + + return data; + } +} // Material diff --git a/Renderer/Opengl/Material/Particle/ParticleProcessMaterial.h b/Renderer/Opengl/Material/Particle/ParticleProcessMaterial.h new file mode 100644 index 00000000..2e9a3510 --- /dev/null +++ b/Renderer/Opengl/Material/Particle/ParticleProcessMaterial.h @@ -0,0 +1,42 @@ +#ifndef SNAKE3_PARTICLEPROCESSMATERIAL_H +#define SNAKE3_PARTICLEPROCESSMATERIAL_H + +#include + +#include "BaseProcessMaterial.h" + +using namespace Manager; +using namespace std; + +namespace Material { + class ParticleProcessMaterial : public BaseProcessMaterial { + struct ParticleDataGPU { + glm::vec4 u_lifeSizeStretch; // x=lifeMin, y=lifeMax, z=sizeMin, w=sizeMax + glm::vec4 u_velMinStretch; // xyz=velMin, w=stretch + glm::vec4 u_velMaxDrag; // xyz=velMax, w=drag + glm::vec4 u_gravity; // xyz=gravity, w=colorSensitivity + glm::vec4 u_emitterPosShape; // xyz=pos, w=spawnShape (cast na float) + glm::vec4 u_emitterSizeRadius; // xy=size, z=radius, w=yOffset + glm::vec4 u_spawnArea; // x=minRadius, y=maxRadius, z=spawnHeight, w=spawnPerFrame + glm::vec4 u_turbulenceTime; // xy=turbulence, z=timeOffset, w=respawnMode + glm::vec4 u_colorStart; // rgba + glm::vec4 u_colorEnd; // rgba + }; + public: + ~ParticleProcessMaterial() override; + + explicit ParticleProcessMaterial(const shared_ptr &resource_manager); + + void bind(shared_ptr shader) const override; + + void update(shared_ptr shader, int maxParticles, float timeAccum, float timeOffset, + float stepDt) override; + + private: + [[nodiscard]] ParticleDataGPU prepareUniformData() const; + + GLuint uboID{}; + }; +} // Material + +#endif //SNAKE3_PARTICLEPROCESSMATERIAL_H diff --git a/Renderer/Opengl/Material/PlanarReflectionMaterial.cpp b/Renderer/Opengl/Material/PlanarReflectionMaterial.cpp new file mode 100644 index 00000000..9b126485 --- /dev/null +++ b/Renderer/Opengl/Material/PlanarReflectionMaterial.cpp @@ -0,0 +1,42 @@ +#include "PlanarReflectionMaterial.h" + +namespace Material { + PlanarReflectionMaterial::PlanarReflectionMaterial(const shared_ptr &baseShader, + const shared_ptr &shadowDepthShader, + const shared_ptr &worldEnv) + : StandardMaterial(baseShader, shadowDepthShader, worldEnv), reflectionEnable(false) { + } + + void PlanarReflectionMaterial::setReflectionTexture(const shared_ptr &texture) { + reflectionTexture = texture; + reflectionEnable = true; + } + + void PlanarReflectionMaterial::bind(const glm::vec3 &posView, const glm::mat4 &view, const glm::mat4 &projection, + const glm::mat4 &model, const bool shadows) const { + StandardMaterial::bind(posView, view, projection, model, shadows); + + shader->use(); + + if (reflectionTexture) { + reflectionTexture->bind(20); + shader->setInt("reflectionTexture", 20); + } + + shader->setBool("reflectionEnable", reflectionEnable); + + // Pro hlavní scénu nastavíme clipPlane tak, aby nic neořezával + shader->setVec4("clipPlane", glm::vec4(0, 0, 1, 1000.0f)); + } + + void PlanarReflectionMaterial::setReflectionEnabled(const bool reflection_enabled) { + reflectionEnable = reflection_enabled; + } + + void PlanarReflectionMaterial::unbind() const { + StandardMaterial::unbind(); + if (reflectionTexture) { + reflectionTexture->unbind(20); + } + } +} diff --git a/Renderer/Opengl/Material/PlanarReflectionMaterial.h b/Renderer/Opengl/Material/PlanarReflectionMaterial.h new file mode 100644 index 00000000..a2c0b833 --- /dev/null +++ b/Renderer/Opengl/Material/PlanarReflectionMaterial.h @@ -0,0 +1,28 @@ +#ifndef SNAKE3_PLANARREFLECTIONMATERIAL_H +#define SNAKE3_PLANARREFLECTIONMATERIAL_H + +#include "StandardMaterial.h" + +namespace Material { + class PlanarReflectionMaterial : public StandardMaterial { + public: + explicit PlanarReflectionMaterial(const shared_ptr &baseShader, + const shared_ptr &shadowDepthShader, + const shared_ptr &worldEnv = nullptr); + + void setReflectionTexture(const shared_ptr &texture); + + void bind(const glm::vec3 &posView, const glm::mat4 &view, const glm::mat4 &projection, + const glm::mat4 &model, bool shadows) const override; + + void setReflectionEnabled(bool reflection_enabled); + + void unbind() const override; + + protected: + shared_ptr reflectionTexture; + bool reflectionEnable; + }; +} + +#endif //SNAKE3_PLANARREFLECTIONMATERIAL_H diff --git a/Renderer/Opengl/Material/ShaderMaterial.cpp b/Renderer/Opengl/Material/ShaderMaterial.cpp new file mode 100644 index 00000000..e0c9d3eb --- /dev/null +++ b/Renderer/Opengl/Material/ShaderMaterial.cpp @@ -0,0 +1,93 @@ +#include "ShaderMaterial.h" + +#include +#include + +namespace Material { + ShaderMaterial::ShaderMaterial(shared_ptr baseShader, + shared_ptr shadowDepthShader, + const shared_ptr &worldEnv) : StandardMaterial( + std::move(baseShader), std::move(shadowDepthShader), worldEnv) { + } + + ShaderMaterial::~ShaderMaterial() = default; + + void ShaderMaterial::addUniform(const string &name, const UniformValue &value) { + uniforms[name] = value; + } + + void ShaderMaterial::bind(const glm::vec3 &posView, const glm::mat4 &view, const glm::mat4 &projection, + const glm::mat4 &model, const bool shadows) const { + shader->use(); + if (shader->hasUniform("view") && uniforms.contains("view") == false) { + shader->setMat4("view", view); + } + shader->setMat4("projection", projection); + shader->setMat4("model", model); + if (shader->hasUniform("viewPos")) { + shader->setVec3("viewPos", posView); + } + if (shader->hasUniform("shadows")) { + shader->setBool("shadows", shadows); + } + + if (directionalLight) { + directionalLight->bind(shader.get()); + shader->setBool("directionLightEnable", true); + } + + // POINT LIGHT + // -------------------------------- + shader->setInt("numPointLights", static_cast(pointLights.size())); + int index = 0; + for (const auto &pointLight: pointLights) { + pointLight->bind(shader.get(), index); + index++; + } + // -------------------------------- + // END POINT LIGHT + + // SPOT LIGHT + // -------------------------------- + shader->setInt("numSpotLights", static_cast(spotLights.size())); + index = 0; + for (const auto &spotLight: spotLights) { + spotLight->bind(shader.get(), index); + index++; + } + // ----------------------------------------------- + // END SPOT LIGHT + + for (auto& [name, value] : uniforms) { + std::visit([&](T0&& arg) { + using T = std::decay_t; + if constexpr (std::is_same_v>) { + if (arg) arg->bind(shader, name); + } else { + shader->setUniform(name, arg); + } + }, value); + } + } + + std::shared_ptr ShaderMaterial::clone() const { + auto cloned = std::make_shared( + shader, shadowDepthShader, worldEnvironment + ); + + for (const auto& [name, value] : uniforms) { + cloned->addUniform(name, std::visit([](T0&& v) -> UniformValue { + using T = std::decay_t; + if constexpr (std::is_base_of_v) { + return v.clone(); + } else if constexpr (std::is_same_v>) { + return v ? v->clone() : nullptr; + } else { + return v; + } + }, value)); + } + + return cloned; + } +} // Material diff --git a/Renderer/Opengl/Material/ShaderMaterial.h b/Renderer/Opengl/Material/ShaderMaterial.h new file mode 100644 index 00000000..c9dbee41 --- /dev/null +++ b/Renderer/Opengl/Material/ShaderMaterial.h @@ -0,0 +1,43 @@ +#ifndef SNAKE3_SHADERMATERIAL_H +#define SNAKE3_SHADERMATERIAL_H + +#include +#include "IUniform.h" +#include "StandardMaterial.h" + +namespace Material { + + using UniformValue = variant< + bool, + int, + float, + glm::vec2, + glm::vec3, + glm::vec4, + glm::mat2, + glm::mat3, + glm::mat4, + shared_ptr + >; + + class ShaderMaterial final : public StandardMaterial { + public: + explicit ShaderMaterial(shared_ptr baseShader, + shared_ptr shadowDepthShader = nullptr, + const shared_ptr &worldEnv = nullptr); + + ~ShaderMaterial() override; + + void addUniform(const string &name, const UniformValue &value); + + void bind(const glm::vec3 &posView, const glm::mat4 &view, const glm::mat4 &projection, + const glm::mat4 &model, bool shadows) const override; + + [[nodiscard]] std::shared_ptr clone() const override; + + protected: + std::map uniforms; + }; +} // Material + +#endif //SNAKE3_SHADERMATERIAL_H diff --git a/Renderer/Opengl/Material/StandardMaterial.cpp b/Renderer/Opengl/Material/StandardMaterial.cpp new file mode 100644 index 00000000..2030e555 --- /dev/null +++ b/Renderer/Opengl/Material/StandardMaterial.cpp @@ -0,0 +1,335 @@ +#include "StandardMaterial.h" + +Material::StandardMaterial::StandardMaterial(shared_ptr baseShader, + shared_ptr shadowDepthShader, + const shared_ptr &worldEnv) + : shader(std::move(baseShader)), shadowDepthShader(std::move(shadowDepthShader)), worldEnvironment(worldEnv) { + timer = make_shared(true); +}; + +Material::StandardMaterial::~StandardMaterial() = default; + +std::shared_ptr Material::StandardMaterial::getAlbedo() const { + return albedo; +} + +void Material::StandardMaterial::setAlbedo(const std::shared_ptr &albedo) { + this->albedo = albedo; +} + +std::shared_ptr Material::StandardMaterial::getNormal() const { + return normal; +} + +shared_ptr Material::StandardMaterial::getShadow() const { + return shadow; +} + +void Material::StandardMaterial::setNormalEnabled(const bool normal_enabled) { + this->normal_enabled = normal_enabled; +} + +void Material::StandardMaterial::bind(const glm::vec3 &posView, const glm::mat4 &view, const glm::mat4 &projection, + const glm::mat4 &model, const bool shadows) const { + shader->use(); + shader->setMat4("view", view); + shader->setMat4("projection", projection); + shader->setMat4("model", model); + shader->setVec3("viewPos", posView); + shader->setBool("useMaterial", true); + shader->setBool("useBones", false); + shader->setBool("shadowsEnable", false); + shader->setBool("iblEnabled", false); + shader->setBool("pbrEnabled", false); + shader->setBool("overrideColorMesh", false); + shader->setBool("hasAlbedoTexture", false); + shader->setFloat("ambientLightColorIntensity", ambientLightColorIntensity); + shader->setBool("fogEnable", false); + shader->setVec2("uvScale", UVScale); + shader->setVec2("uvOffset", UVOffset); + shader->setInt("material.ambient", 0); + shader->setInt("material.diffuse", 1); + shader->setInt("material.specular", 2); + shader->setInt("shadowMap", 3); + shader->setInt("metalness", 4); + shader->setInt("roughness", 5); + shader->setInt("environmentMap", 6); + shader->setInt("aoMap", 7); + shader->setFloat("alpha", alpha); + shader->setBool("reflectionEnable", false); + shader->setFloat("uTime", static_cast(timer->getNow())); + + if (shadowsEnabled && shadows) { + shader->setBool("shadowsEnable", true); + if (shadow && shadow->hasTexture()) { + shadow->bindArr(3, 0); + } + } + + if (worldEnvironment) { + if (worldEnvironment->getEnvironment()) { + shader->setVec3("ambientLightColor", worldEnvironment->getEnvironment()->getAmbientLight().color); + shader->setFloat("ambientLightColorIntensity", + worldEnvironment->getEnvironment()->getAmbientLight().intensity); + } else if (color) { + shader->setVec3("ambientLightColor", *color.get()); + shader->setBool("overrideColorMesh", true); + } + } else if (color) { + shader->setVec3("ambientLightColor", *color.get()); + shader->setBool("overrideColorMesh", true); + } else { + shader->setVec3("ambientLightColor", {1.0f, 1.0f, 1.0f}); + } + + shader->setFloat("material.shininess", shininess); + + // DIRECTIONAL LIGHT + // -------------------------------- + if (directionalLight) { + directionalLight->bind(shader.get()); + shader->setBool("directionLightEnable", true); + } else { + shader->setBool("directionLightEnable", false); + shader->setVec3("lightPos", {0, 0, 0}); + } + + // POINT LIGHT + // -------------------------------- + int index = 0; + for (const auto &pointLight: pointLights) { + if (pointLight->isVisible()) { + pointLight->bind(shader.get(), index); + index++; + } + } + shader->setInt("numPointLights", index); + // -------------------------------- + // END POINT LIGHT + + // SPOT LIGHT + // -------------------------------- + index = 0; + for (const auto &spotLight: spotLights) { + if (spotLight->isVisible()) { + spotLight->bind(shader.get(), index); + index++; + } + } + shader->setInt("numSpotLights", index); + // ----------------------------------------------- + // END SPOT LIGHT + + if (albedo && albedo->hasTexture()) { + shader->setBool("useMaterial", false); + shader->setBool("hasAlbedoTexture", true); + albedo->bind(0); + } else { + shader->setBool("useMaterial", true); + } + const bool shaderNormal = normal_enabled && normal && normal->hasTexture(); + shader->setBool("normalMapEnabled", shaderNormal); + if (shaderNormal) { + normal->bind(1); + } + + if (specular && specular->hasTexture()) { + specular->bind(2); + shader->setBool("specularMapEnabled", true); + } else { + shader->setBool("specularMapEnabled", false); + } + + if (metalness && metalness->hasTexture()) { + shader->setInt("metalness", 4); + shader->setBool("pbrEnabled", true); + metalness->bind(4); + } + + if (roughness && roughness->hasTexture()) { + shader->setInt("roughness", 5); + shader->setBool("pbrEnabled", true); + roughness->bind(5); + } + + if (aoMap && aoMap->hasTexture()) { + shader->setInt("aoMap", 7); + aoMap->bind(7); + } + + if (environmentMap && environmentMap->hasTexture()) { + shader->setBool("iblEnabled", true); + environmentMap->cubeBind(6); + } + + timer->update(); +} + +void Material::StandardMaterial::bindShadow(const glm::mat4 &model) const { + shadowDepthShader->use(); + shadowDepthShader->setMat4("model", model); +} + +void Material::StandardMaterial::unbind() const { + if (albedo) { + albedo->unbind(0); + } + if (normal_enabled) { + normal->unbind(1); + } + if (specular) { + specular->unbind(2); + } + if (shadow) { + shadow->unbind(3); + } + if (metalness) { + metalness->unbind(4); + } + if (roughness) { + roughness->unbind(5); + } + if (environmentMap) { + environmentMap->unbind(6); + } + if (aoMap) { + aoMap->unbind(7); + } +} + +glm::vec3 Material::StandardMaterial::getColor() const { + return *color.get(); +} + +void Material::StandardMaterial::setColor(const glm::vec3 &color) { + this->color = make_shared(color); +} + +bool Material::StandardMaterial::isShadowEnabled() const { + return shadowsEnabled; +} + +void Material::StandardMaterial::setShadow(const bool shadow_enabled) { + shadowsEnabled = shadow_enabled; +} + +void Material::StandardMaterial::setDirectionalLight(const shared_ptr &directional_light) { + directionalLight = directional_light; +} + +void Material::StandardMaterial::addSpotLight(const shared_ptr &spot_light) { + spotLights.push_back(spot_light); +} + +void Material::StandardMaterial::addPointLight(const shared_ptr &point_light) { + pointLights.push_back(point_light); +} + +void Material::StandardMaterial::setSpotLights(const vector> &spotLights) { + this->spotLights = spotLights; +} + +void Material::StandardMaterial::setPointLights(const vector> &pointLights) { + this->pointLights = pointLights; +} + +void Material::StandardMaterial::setShininess(const float shininess) { + this->shininess = shininess; +} + +shared_ptr Material::StandardMaterial::getRoughness() const { + return roughness; +} + +void Material::StandardMaterial::setRoughness(const shared_ptr &roughness) { + this->roughness = roughness; +} + +void Material::StandardMaterial::setAlpha(const float alpha) { + this->alpha = alpha; +} + +shared_ptr Material::StandardMaterial::getMetalness() const { + return metalness; +} + +void Material::StandardMaterial::setMetalness(const shared_ptr &metalness) { + this->metalness = metalness; +} + +void Material::StandardMaterial::setEnvironmentMap(const shared_ptr &environment_map) { + environmentMap = environment_map; +} + +void Material::StandardMaterial::setAoMap(const shared_ptr &ao_map) { + aoMap = ao_map; +} + +void Material::StandardMaterial::set_uv_scale(const glm::vec2 &uv_scale) { + UVScale = uv_scale; +} + +void Material::StandardMaterial::set_uv_offset(const glm::vec2 &uv_offset) { + UVOffset = uv_offset; +} + +void Material::StandardMaterial::setAmbientLightColorIntensity(const float intensity) { + ambientLightColorIntensity = intensity; +} + +std::shared_ptr Material::StandardMaterial::clone() const { + auto copy = std::make_shared(*this); + + if (albedo) copy->albedo = std::make_shared(*albedo); + if (normal) copy->normal = std::make_shared(*normal); + if (specular) copy->specular = std::make_shared(*specular); + if (roughness) copy->roughness = std::make_shared(*roughness); + if (metalness) copy->metalness = std::make_shared(*metalness); + if (shadow) copy->shadow = std::make_shared(*shadow); + if (aoMap) copy->aoMap = std::make_shared(*aoMap); + if (environmentMap) copy->environmentMap = std::make_shared(*environmentMap); + + copy->directionalLight = directionalLight; + copy->spotLights = spotLights; + copy->pointLights = pointLights; + copy->worldEnvironment = worldEnvironment; + copy->shader = shader; + copy->shadowDepthShader = shadowDepthShader; + + if (color) copy->color = std::make_shared(*color); + + return copy; +} + +void Material::StandardMaterial::bindUseBones(const bool useBones) const { + shader->setBool("useBones", useBones); +} + +void Material::StandardMaterial::bindBonesMatrices(const int index, const glm::mat4 &matrice) const { + shader->setMat4("finalBonesMatrices[" + std::to_string(index) + "]", matrice); +} + +void Material::StandardMaterial::bindModel(const glm::mat4 &model) const { + shader->setMat4("model", model); +} + +void Material::StandardMaterial::bindShadowModel(const glm::mat4 &model) const { + shadowDepthShader->setMat4("model", model); +} + +void Material::StandardMaterial::setNormal(const std::shared_ptr &normal) { + this->normal = normal; +} + +std::shared_ptr Material::StandardMaterial::getSpecular() const { + return specular; +} + +void Material::StandardMaterial::setSpecular(const std::shared_ptr &specular) { + this->specular = specular; +} + +void Material::StandardMaterial::setShadow(const std::shared_ptr &shadow) { + this->shadow = shadow; + this->shadowsEnabled = true; +} diff --git a/Renderer/Opengl/Material/StandardMaterial.h b/Renderer/Opengl/Material/StandardMaterial.h new file mode 100644 index 00000000..5596b656 --- /dev/null +++ b/Renderer/Opengl/Material/StandardMaterial.h @@ -0,0 +1,132 @@ +#ifndef SNAKE3_STANDARDMATERIAL_H +#define SNAKE3_STANDARDMATERIAL_H + +#include +#include "BaseMaterial.h" +#include "IAlbedoMaterial.h" +#include "../../../Lights/DirectionalLight.h" +#include "../../../Lights/PointLight.h" +#include "../../../Lights/SpotLight.h" +#include "../../../Manager/ShaderManager.h" +#include "../../../Manager/TextureManager.h" +#include "../../../Tools/Timer.h" +#include "../../../Tools/WorldEnvironment.h" + +using namespace Manager; +using namespace Lights; +using namespace Tools; +using namespace std; + +namespace Material { + class StandardMaterial : public BaseMaterial, public IAlbedoMaterial { + public: + explicit StandardMaterial(shared_ptr baseShader, + shared_ptr shadowDepthShader, + const shared_ptr &worldEnv = nullptr); + + ~StandardMaterial() override; + + void setAlbedo(const shared_ptr &albedo); + + void setNormal(const shared_ptr &normal); + + void setSpecular(const shared_ptr &specular); + + void setShadow(const shared_ptr &shadow); + + [[nodiscard]] shared_ptr getAlbedo() const override; + + [[nodiscard]] shared_ptr getSpecular() const; + + [[nodiscard]] shared_ptr getNormal() const; + + [[nodiscard]] shared_ptr getShadow() const; + + void setNormalEnabled(bool normal_enabled); + + virtual void bind(const glm::vec3 &posView, const glm::mat4 &view, const glm::mat4 &projection, + const glm::mat4 &model, bool shadows) const; + + void bindShadow(const glm::mat4 &model) const; + + virtual void unbind() const; + + [[nodiscard]] glm::vec3 getColor() const; + + void setColor(const glm::vec3 &color); + + [[nodiscard]] bool isShadowEnabled() const; + + void setShadow(bool shadow_enabled); + + void setDirectionalLight(const shared_ptr &directional_light); + + void addSpotLight(const shared_ptr &spot_light); + + void addPointLight(const shared_ptr &point_light); + + void setSpotLights(const vector > &spotLights); + + void setPointLights(const vector > &pointLights); + + void setShininess(float shininess); + + [[nodiscard]] shared_ptr getRoughness() const; + + void setRoughness(const shared_ptr &roughness); + + void setAlpha(float alpha); + + [[nodiscard]] shared_ptr getMetalness() const; + + void setMetalness(const shared_ptr &metalness); + + void setEnvironmentMap(const shared_ptr &environment_map); + + void setAoMap(const shared_ptr &ao_map); + + void set_uv_scale(const glm::vec2 &uv_scale); + + void set_uv_offset(const glm::vec2 &uv_offset); + + void setAmbientLightColorIntensity(float intensity); + + [[nodiscard]] std::shared_ptr clone() const override; + + void bindUseBones(bool useBones) const; + + void bindBonesMatrices(int index, const glm::mat4 &matrice) const; + + void bindModel(const glm::mat4 &model) const; + + void bindShadowModel(const glm::mat4 &model) const; + + protected: + shared_ptr timer; + shared_ptr albedo; + shared_ptr normal; + shared_ptr specular; + shared_ptr roughness; + shared_ptr metalness; + shared_ptr shadow; + shared_ptr aoMap; + shared_ptr environmentMap; + shared_ptr shader; + shared_ptr shadowDepthShader; + shared_ptr worldEnvironment; + shared_ptr directionalLight; + vector > spotLights; + vector > pointLights; + shared_ptr color = nullptr; + glm::vec2 UVScale = {1, 1}; + glm::vec2 UVOffset = {0, 0}; + bool normal_enabled = false; + bool shadowsEnabled = false; + bool unShaded = false; + float shininess = 32.0f; + float alpha = 1.0f; + float ambientLightColorIntensity = 0.05; + }; +} + +#endif //SNAKE3_STANDARDMATERIAL_H diff --git a/Renderer/Opengl/Material/Uniform/CallbackUniform.cpp b/Renderer/Opengl/Material/Uniform/CallbackUniform.cpp new file mode 100644 index 00000000..2ca66886 --- /dev/null +++ b/Renderer/Opengl/Material/Uniform/CallbackUniform.cpp @@ -0,0 +1,18 @@ +#include "CallbackUniform.h" + +#include + +namespace Uniform { + CallbackUniform::CallbackUniform(CallbackType callback) : callback(std::move(callback)) { + } + + void CallbackUniform::bind(const shared_ptr &shader, const string &name) { + callback(name, shader); + } + + shared_ptr CallbackUniform::clone() const { + auto cloned = make_shared(callback); + + return cloned; + } +} // Uniform diff --git a/Renderer/Opengl/Material/Uniform/CallbackUniform.h b/Renderer/Opengl/Material/Uniform/CallbackUniform.h new file mode 100644 index 00000000..62a4f099 --- /dev/null +++ b/Renderer/Opengl/Material/Uniform/CallbackUniform.h @@ -0,0 +1,27 @@ +#ifndef SNAKE3_CALLBACKUNIFORM_H +#define SNAKE3_CALLBACKUNIFORM_H + +#include + +#include "../IUniform.h" + +using namespace Material; +using namespace std; + +namespace Uniform { + class CallbackUniform : public IUniform { + public: + using CallbackType = std::function& shader)>; + + explicit CallbackUniform(CallbackType callback); + + void bind(const shared_ptr &shader, const string &name) override; + + [[nodiscard]] shared_ptr clone() const override; + + protected: + CallbackType callback; + }; +} // Uniform + +#endif //SNAKE3_CALLBACKUNIFORM_H diff --git a/Renderer/Opengl/Material/Uniform/FadeInUniform.cpp b/Renderer/Opengl/Material/Uniform/FadeInUniform.cpp new file mode 100644 index 00000000..c50ce021 --- /dev/null +++ b/Renderer/Opengl/Material/Uniform/FadeInUniform.cpp @@ -0,0 +1,31 @@ +#include "FadeInUniform.h" + +namespace Uniform { + void FadeInUniform::bind(const shared_ptr &shader, const string &name) { + if (timer->isRunning()) { + if (isFinished() == false) { + alpha += step * static_cast(timer->getDeltaTime()); + alpha = glm::clamp(alpha, 0.0f, 1.0f); + running = true; + } else { + running = false; + timer->stop(); + if (finished) { + finished(); + } + } + } + shader->setUniform(name, alpha); + timer->update(); + } + + void FadeInUniform::start() { + alpha = 0; + timer->reset(); + timer->start(); + } + + bool FadeInUniform::isFinished() const { + return running && alpha >= 1; + } +} // Uniform \ No newline at end of file diff --git a/Renderer/Opengl/Material/Uniform/FadeInUniform.h b/Renderer/Opengl/Material/Uniform/FadeInUniform.h new file mode 100644 index 00000000..c5234214 --- /dev/null +++ b/Renderer/Opengl/Material/Uniform/FadeInUniform.h @@ -0,0 +1,22 @@ +#ifndef SNAKE3_FADEINUNIFORM_H +#define SNAKE3_FADEINUNIFORM_H + +#include "FadeOutUniform.h" + +namespace Uniform { + class FadeInUniform final : public FadeOutUniform { + public: + explicit FadeInUniform() { + alpha = 0.0f; + } + + void bind(const shared_ptr &shader, const string &name) override; + + void start() override; + + private: + [[nodiscard]] bool isFinished() const override; + }; +} // Uniform + +#endif //SNAKE3_FADEINUNIFORM_H diff --git a/Renderer/Opengl/Material/Uniform/FadeOutUniform.cpp b/Renderer/Opengl/Material/Uniform/FadeOutUniform.cpp new file mode 100644 index 00000000..c856034a --- /dev/null +++ b/Renderer/Opengl/Material/Uniform/FadeOutUniform.cpp @@ -0,0 +1,58 @@ +#include "FadeOutUniform.h" + +namespace Uniform { + FadeOutUniform::FadeOutUniform() + : running(false), alpha(1.0f), step(0.5f) { + timer = make_unique(false); + } + + void FadeOutUniform::bind(const shared_ptr &shader, const string &name) { + if (timer->isRunning()) { + if (isFinished() == false) { + alpha -= step * static_cast(timer->getDeltaTime()); + alpha = glm::clamp(alpha, 0.0f, 1.0f); + running = true; + } else { + running = false; + timer->stop(); + if (finished) { + finished(); + } + } + } + shader->setUniform(name, alpha); + timer->update(); + } + + void FadeOutUniform::setAlpha(const float alpha) { + this->alpha = alpha; + } + + float FadeOutUniform::getAlpha() const { + return alpha; + } + + void FadeOutUniform::start() { + alpha = 1; + timer->reset(); + timer->start(); + } + + shared_ptr FadeOutUniform::clone() const { + auto cloned = make_shared(); + + return cloned; + } + + void FadeOutUniform::setStep(const float step) { + this->step = step; + } + + void FadeOutUniform::setFinishedCallback(const std::function &callback) { + finished = callback; + } + + bool FadeOutUniform::isFinished() const { + return running && alpha <= 0; + } +} // Uniform diff --git a/Renderer/Opengl/Material/Uniform/FadeOutUniform.h b/Renderer/Opengl/Material/Uniform/FadeOutUniform.h new file mode 100644 index 00000000..c2356d10 --- /dev/null +++ b/Renderer/Opengl/Material/Uniform/FadeOutUniform.h @@ -0,0 +1,36 @@ +#ifndef SNAKE3_FADEOUTUNIFORM_H +#define SNAKE3_FADEOUTUNIFORM_H + +#include + +#include "../IUniform.h" +#include "../../../../Tools/Timer.h" + +using namespace Tools; +using namespace Material; +using namespace Manager; +using namespace std; + +namespace Uniform { + class FadeOutUniform : public IUniform { + public: + explicit FadeOutUniform(); + void bind(const shared_ptr& shader, const string& name) override; + void setAlpha(float alpha); + [[nodiscard]] float getAlpha() const; + virtual void start(); + [[nodiscard]] shared_ptr clone() const override; + void setStep(float step); + void setFinishedCallback(const std::function &callback); + protected: + [[nodiscard]] virtual bool isFinished() const; + + bool running; + float alpha; + float step; + unique_ptr timer; + std::function finished; + }; +} // Uniform + +#endif //SNAKE3_FADEOUTUNIFORM_H \ No newline at end of file diff --git a/Renderer/Opengl/Material/Uniform/TextureArrayUniform.cpp b/Renderer/Opengl/Material/Uniform/TextureArrayUniform.cpp new file mode 100644 index 00000000..ded8d9ff --- /dev/null +++ b/Renderer/Opengl/Material/Uniform/TextureArrayUniform.cpp @@ -0,0 +1,18 @@ +#include "TextureArrayUniform.h" + +namespace Uniform { + TextureArrayUniform::TextureArrayUniform(const int index, const shared_ptr &texture) : texture(texture), + index(index) { + } + + void TextureArrayUniform::bind(const shared_ptr &shader, const string &name) { + texture->bindArr(index, 0); + shader.get()->setUniform(name, index); + } + + shared_ptr TextureArrayUniform::clone() const { + auto cloned = make_shared(index, texture); + + return cloned; + } +} // Uniform diff --git a/Renderer/Opengl/Material/Uniform/TextureArrayUniform.h b/Renderer/Opengl/Material/Uniform/TextureArrayUniform.h new file mode 100644 index 00000000..e4035357 --- /dev/null +++ b/Renderer/Opengl/Material/Uniform/TextureArrayUniform.h @@ -0,0 +1,25 @@ +#ifndef SNAKE3_TEXTUREARRAYUNIFORM_H +#define SNAKE3_TEXTUREARRAYUNIFORM_H + +#include "../IUniform.h" +#include "../../../../Manager/TextureManager.h" + +using namespace Material; +using namespace Manager; +using namespace std; + +namespace Uniform { + class TextureArrayUniform final : public IUniform { + public: + TextureArrayUniform(int index, const shared_ptr &texture); + void bind(const shared_ptr& shader, const string& name) override; + + [[nodiscard]] shared_ptr clone() const override; + + protected: + shared_ptr texture; + int index; + }; +} // Uniform + +#endif //SNAKE3_TEXTUREARRAYUNIFORM_H \ No newline at end of file diff --git a/Renderer/Opengl/Material/Uniform/TextureUniform.cpp b/Renderer/Opengl/Material/Uniform/TextureUniform.cpp new file mode 100644 index 00000000..2519f771 --- /dev/null +++ b/Renderer/Opengl/Material/Uniform/TextureUniform.cpp @@ -0,0 +1,22 @@ +#include "TextureUniform.h" + +namespace Uniform { + TextureUniform::TextureUniform(const int index, const shared_ptr &texture, const bool use_cube) + : texture(texture), index(index), cube(use_cube) { + } + + void TextureUniform::bind(const shared_ptr &shader, const string &name) { + if (cube) { + texture->cubeBind(index); + } else { + texture->bind(index, 0); + } + shader->setUniform(name, index); + } + + shared_ptr TextureUniform::clone() const { + auto cloned = make_shared(index, texture); + + return cloned; + } +} // Uniform diff --git a/Renderer/Opengl/Material/Uniform/TextureUniform.h b/Renderer/Opengl/Material/Uniform/TextureUniform.h new file mode 100644 index 00000000..869abecf --- /dev/null +++ b/Renderer/Opengl/Material/Uniform/TextureUniform.h @@ -0,0 +1,27 @@ +#ifndef SNAKE3_TEXTUREUNIFORM_H +#define SNAKE3_TEXTUREUNIFORM_H + +#include "../IUniform.h" +#include "../../../../Manager/TextureManager.h" + +using namespace Material; +using namespace Manager; +using namespace std; + +namespace Uniform { + class TextureUniform final : public IUniform { + public: + TextureUniform(int index, const shared_ptr &texture, bool use_cube = false); + + void bind(const shared_ptr &shader, const string &name) override; + + [[nodiscard]] shared_ptr clone() const override; + + protected: + shared_ptr texture; + int index; + bool cube; + }; +} // Uniform + +#endif //SNAKE3_TEXTUREUNIFORM_H diff --git a/Renderer/Opengl/Material/Uniform/TimerUniform.cpp b/Renderer/Opengl/Material/Uniform/TimerUniform.cpp new file mode 100644 index 00000000..95005c42 --- /dev/null +++ b/Renderer/Opengl/Material/Uniform/TimerUniform.cpp @@ -0,0 +1,33 @@ +#include "TimerUniform.h" + +namespace Uniform { + TimerUniform::TimerUniform(bool autostart) : autostart(autostart) { + timer = make_unique(autostart); + } + + void TimerUniform::bind(const shared_ptr &shader, const string &name) { + if (timer->isRunning()) { + shader->setUniform(name, static_cast(timer->getElapsedTime())); + } + timer->update(); + } + + shared_ptr TimerUniform::clone() const { + auto cloned = make_shared(autostart); + + return cloned; + } + + void TimerUniform::stop() const { + timer->stop(); + timer->reset(); + } + + void TimerUniform::start() const { + timer->start(); + } + + double TimerUniform::getElapsed() const { + return timer->getElapsedTime(); + } +} // Uniform \ No newline at end of file diff --git a/Renderer/Opengl/Material/Uniform/TimerUniform.h b/Renderer/Opengl/Material/Uniform/TimerUniform.h new file mode 100644 index 00000000..65a31975 --- /dev/null +++ b/Renderer/Opengl/Material/Uniform/TimerUniform.h @@ -0,0 +1,29 @@ +#ifndef SNAKE3_TIMERUNIFORM_H +#define SNAKE3_TIMERUNIFORM_H + +#include "../IUniform.h" +#include "../../../../Tools/Timer.h" +#include + +using namespace Tools; +using namespace Material; +using namespace Manager; +using namespace std; + +namespace Uniform { + class TimerUniform final : public IUniform { + public: + explicit TimerUniform(bool autostart = false); + void bind(const shared_ptr& shader, const string& name) override; + [[nodiscard]] shared_ptr clone() const override; + [[nodiscard]] double getElapsed() const; + void stop() const; + void start() const; + + private: + unique_ptr timer; + bool autostart; + }; +} // Uniform + +#endif //SNAKE3_TIMERUNIFORM_H \ No newline at end of file diff --git a/Renderer/Opengl/Model/AnimationModel.cpp b/Renderer/Opengl/Model/AnimationModel.cpp deleted file mode 100644 index faa2330c..00000000 --- a/Renderer/Opengl/Model/AnimationModel.cpp +++ /dev/null @@ -1,177 +0,0 @@ -#include "AnimationModel.h" - -namespace Model { - AnimationModel::AnimationModel(BaseItem* item, - vector &meshes, - decltype(animations)&& _animations, - decltype(bones)&& bones, - decltype(skeleton)&& skeleton, - decltype(bones_map)&& bones_map, - const glm::mat4& _global_matrix) : - baseItem(item), meshes(meshes), animations(std::move(_animations)), bones(std::move(bones)), - skeleton(skeleton), bones_map(bones_map), global_inverse{_global_matrix}, globalPause(true) { - - this->meshes.erase( - std::remove_if( - this->meshes.begin(), - this->meshes.end(), - [this](Mesh *p) { - if (!p->isHasBones()) { - noBonesMeshes.push_back(p); - return true; - } - return false; - } - ), - this->meshes.end() - ); - - for (const auto& anim: animations) { - auto meta = new AnimationMeta; - meta->name = anim.name; - meta->animation_duration = std::chrono::seconds(0); - meta->last_time = std::chrono::time_point(); - meta->bone_transform.resize(this->bones.size(), glm::mat4(1.0f)); - meta->pause = globalPause; - metadata.emplace(anim.name, meta); - } - } - - AnimationModel::~AnimationModel() { - animations.clear(); - bones.clear(); - bones_map.clear(); - - for (auto & iter : metadata) { - delete iter.second; - } - - metadata.clear(); - - for (auto mesh : meshes) { - delete mesh; - } - - meshes.clear(); - - for (auto mesh : noBonesMeshes) { - delete mesh; - } - - noBonesMeshes.clear(); - } - - const vector &AnimationModel::getMeshes() const { - return meshes; - } - - const vector &AnimationModel::getAnimations() const { - return animations; - } - - const vector &AnimationModel::getBones() const { - return bones; - } - - const Tree &AnimationModel::getSkeleton() const { - return skeleton; - } - - const glm::mat4 &AnimationModel::getGlobalInverse() const { - return global_inverse; - } - - const unordered_map &AnimationModel::getBonesMap() const { - return bones_map; - } - - const vector &AnimationModel::getNoBonesMeshes() const { - return noBonesMeshes; - } - - const AnimationNode * - AnimationModel::findAnimationNode(const Animation *animation, const Bone &bone) noexcept { - for (auto &node: animation->nodes) { - if (&node.bone == &bone) { - return &node; - } - } - - return nullptr; - } - - void AnimationModel::updateAnimation(const Animation *animation) const { - auto meta = metadata.at(animation->name); - if (meta && !meta->pause) { - const Animation &anim = *animation; - const auto current_time = std::chrono::steady_clock::now(); - if (meta->last_time == std::chrono::time_point()) { - meta->last_time = current_time; - } - const auto delta_time = current_time - meta->last_time; - meta->animation_duration += delta_time; - meta->last_time = current_time; - const auto animation_time = glm::mod(meta->animation_duration.count() * anim.tps, anim.duration); - - std::function &, const glm::mat4 &)> node_traversal; - node_traversal = [&](const Tree &node, const glm::mat4 &parent_mat) { - auto anim_node = Model::AnimationModel::findAnimationNode(animation, getBones()[*node]); - auto local_transform = !getBones()[*node].isFake() ? getBones()[*node].node_transform - : glm::mat4(1.f); - - if (anim_node) { - glm::vec3 scale = anim_node->scalingLerp(animation_time); - glm::vec3 position = anim_node->positionLerp(animation_time); - auto rotation = anim_node->rotationLerp(animation_time); - - auto translate = glm::translate(glm::mat4(1.f), position); - auto rotate = glm::mat4_cast(rotation); - auto scale_mat = glm::scale(glm::mat4(1.f), scale); - - local_transform = translate * rotate * scale_mat; - } - - auto transform = parent_mat * local_transform; - - if (anim_node) { - meta->bone_transform[*node] = parent_mat * local_transform * - (getBones()[*node]).offset_matrix; - } else { - meta->bone_transform[*node] = local_transform; - } - - for (const auto &n: node) { - node_traversal(n, transform); - } - }; - - try { - node_traversal(getSkeleton(), glm::mat4(1.f)); - } catch (const std::exception &e) { - throw std::runtime_error("Wrong TPS/duration. " + std::string(e.what())); - } - } else { - meta->last_time = std::chrono::steady_clock::now(); - } - } - - AnimationMeta *AnimationModel::getMetadata(const Animation *animation) const { - return metadata.at(animation->name); - } - - BaseItem *AnimationModel::getBaseItem() const { - return baseItem; - } - - void AnimationModel::setBaseItem(BaseItem *baseItem) { - AnimationModel::baseItem = baseItem; - } - - void AnimationModel::setGlobalPause(bool globalPause) { - AnimationModel::globalPause = globalPause; - for (auto &meta: metadata) { - meta.second->pause = globalPause; - } - } - -} // Model \ No newline at end of file diff --git a/Renderer/Opengl/Model/AnimationModel.h b/Renderer/Opengl/Model/AnimationModel.h deleted file mode 100644 index bbb5cdb6..00000000 --- a/Renderer/Opengl/Model/AnimationModel.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef SNAKE3_ANIMATIONMODEL_H -#define SNAKE3_ANIMATIONMODEL_H - -#include -#include -#include -#include "../../../ItemsDto/AnimItem.h" -#include "Utils/Mesh.h" -#include "Utils/Tree.h" - -using namespace Manager; -using namespace ItemsDto; -using namespace ModelUtils; - -namespace Model { - struct AnimationMeta { - std::string name; - chrono::time_point last_time; - chrono::duration animation_duration; - vector bone_transform; - bool pause; - }; - - class AnimationModel { - protected: - BaseItem* baseItem{}; - vector meshes; - vector noBonesMeshes; - vector animations; - vector bones; - unordered_map bones_map; - Tree skeleton; - glm::mat4 global_inverse; - unordered_map metadata; - bool globalPause; - public: - AnimationModel(BaseItem* item, vector &meshes, decltype(animations)&& _animations, decltype(bones)&& bones, - decltype(skeleton)&& skeleton, decltype(bones_map)&& bones_map, const glm::mat4& _global_matrix); - - virtual ~AnimationModel(); - - [[nodiscard]] const vector &getMeshes() const; - [[nodiscard]] const vector &getAnimations() const; - [[nodiscard]] const vector &getBones() const; - [[nodiscard]] const unordered_map &getBonesMap() const; - [[nodiscard]] const Tree &getSkeleton() const; - [[nodiscard]] const glm::mat4 &getGlobalInverse() const; - [[nodiscard]] const vector &getNoBonesMeshes() const; - [[nodiscard]] static const AnimationNode* findAnimationNode(const Animation * animation, const Bone& bone) noexcept; - void updateAnimation(const Animation* animation) const; - AnimationMeta* getMetadata(const Animation* animation) const; - BaseItem *getBaseItem() const; - void setBaseItem(BaseItem *baseItem); - void setGlobalPause(bool globalPause); - }; - -} // Model - -#endif //SNAKE3_ANIMATIONMODEL_H diff --git a/Renderer/Opengl/Model/Character.cpp b/Renderer/Opengl/Model/Character.cpp index bb416361..6931737d 100644 --- a/Renderer/Opengl/Model/Character.cpp +++ b/Renderer/Opengl/Model/Character.cpp @@ -1,35 +1,35 @@ #include "Character.h" namespace Model { - unsigned int Character::getTextureId() const { + unsigned int oCharacter::getTextureId() const { return textureId; } - void Character::setTextureId(unsigned int textureId) { - Character::textureId = textureId; + void oCharacter::setTextureId(unsigned int textureId) { + oCharacter::textureId = textureId; } - const glm::ivec2 &Character::getSize() const { + const glm::ivec2 &oCharacter::getSize() const { return size; } - void Character::setSize(const glm::ivec2 &size) { - Character::size = size; + void oCharacter::setSize(const glm::ivec2 &size) { + oCharacter::size = size; } - const glm::ivec2 &Character::getBearing() const { + const glm::ivec2 &oCharacter::getBearing() const { return bearing; } - void Character::setBearing(const glm::ivec2 &bearing) { - Character::bearing = bearing; + void oCharacter::setBearing(const glm::ivec2 &bearing) { + oCharacter::bearing = bearing; } - unsigned int Character::getAdvance() const { + unsigned int oCharacter::getAdvance() const { return advance; } - void Character::setAdvance(unsigned int advance) { - Character::advance = advance; + void oCharacter::setAdvance(unsigned int advance) { + oCharacter::advance = advance; } } // Model \ No newline at end of file diff --git a/Renderer/Opengl/Model/Character.h b/Renderer/Opengl/Model/Character.h index 9d58b6ad..00e56f17 100644 --- a/Renderer/Opengl/Model/Character.h +++ b/Renderer/Opengl/Model/Character.h @@ -4,7 +4,7 @@ namespace Model { - class Character { + class oCharacter { public: [[nodiscard]] unsigned int getTextureId() const; void setTextureId(unsigned int textureId); diff --git a/Renderer/Opengl/Model/Collision/CollisionShape3D.cpp b/Renderer/Opengl/Model/Collision/CollisionShape3D.cpp new file mode 100644 index 00000000..da992ad2 --- /dev/null +++ b/Renderer/Opengl/Model/Collision/CollisionShape3D.cpp @@ -0,0 +1,27 @@ +#include "CollisionShape3D.h" + +namespace CollisionShape { + CollisionShape3D::CollisionShape3D(const shared_ptr &contextState, + const shared_ptr &resourceManager, const shared_ptr &shape) + : MeshNode3D(contextState, nullptr, resourceManager), shape(shape) { + } + + void CollisionShape3D::render(const shared_ptr &camera, const glm::mat4 &projection, const float dt, + const glm::mat4 &parentTransform, const bool shadows) { + if (visible) { + const glm::mat4 finalTransform = parentTransform * this->getModelMatrix(); + // contextState->setBlendingMode(mesh->getBlending()); + // contextState->setDepthTest(mesh->getDepthTest()); + // contextState->setDepthWrite(mesh->getDepthWrite()); + shape->render(camera, projection, finalTransform); + + for (auto &node: children) { + node->render(camera, projection, dt, transformDetached ? glm::mat4(1.0f) : finalTransform, shadows); + } + } else if (transformDetached) { + for (auto &node: children) { + node->render(camera, projection, dt, glm::mat4(1.0f), shadows); + } + } + } +} // CollisionShape \ No newline at end of file diff --git a/Renderer/Opengl/Model/Collision/CollisionShape3D.h b/Renderer/Opengl/Model/Collision/CollisionShape3D.h new file mode 100644 index 00000000..4f914211 --- /dev/null +++ b/Renderer/Opengl/Model/Collision/CollisionShape3D.h @@ -0,0 +1,27 @@ +#ifndef SNAKE3_COLLISIONSHAPE3D_H +#define SNAKE3_COLLISIONSHAPE3D_H + +#include "../../../../Physic/Shape.h" +#include "../Standard/MeshNode3D.h" +#include + +using namespace Physic; +using namespace Model; + +namespace CollisionShape { + class CollisionShape3D : public MeshNode3D { + public: + CollisionShape3D(const shared_ptr &contextState, + const shared_ptr &resourceManager, const shared_ptr &shape); + + void render(const shared_ptr &camera, const glm::mat4 &projection, float dt, + const glm::mat4 &parentTransform, bool shadows) override; + + shared_ptr getShape() const { return shape; } + + private: + shared_ptr shape; + }; +} // CollisionShape + +#endif //SNAKE3_COLLISIONSHAPE3D_H diff --git a/Renderer/Opengl/Model/Debug/DirectionalLightNode3D.cpp b/Renderer/Opengl/Model/Debug/DirectionalLightNode3D.cpp new file mode 100644 index 00000000..14abd706 --- /dev/null +++ b/Renderer/Opengl/Model/Debug/DirectionalLightNode3D.cpp @@ -0,0 +1,23 @@ +#include "DirectionalLightNode3D.h" + +namespace Model { + DirectionalLightNode3D::DirectionalLightNode3D(const shared_ptr &contextState, + const shared_ptr &baseShader, + const shared_ptr &resourceManager) + : LightNode3D(baseShader), MeshNode3D(contextState, arrowMesh, resourceManager) { + } + + DirectionalLightNode3D::~DirectionalLightNode3D() = default; + + void DirectionalLightNode3D::render(const shared_ptr &camera, const glm::mat4 &projection, const float dt, + const glm::mat4 &parentTransform, const bool shadows) { + + const glm::mat4 model = calculateArrowTransform(directionalLight->getPosition(), -directionalLight->getDirection()); + + contextState->setBlendingMode(mesh->getBlending()); + contextState->setDepthTest(mesh->getDepthTest()); + contextState->setDepthWrite(mesh->getDepthWrite()); + reinterpret_pointer_cast(mesh)->setColor(directionalLight->getAmbient()); + mesh->render(camera, projection, 1, model, shadows); + } +} // Model diff --git a/Renderer/Opengl/Model/Debug/DirectionalLightNode3D.h b/Renderer/Opengl/Model/Debug/DirectionalLightNode3D.h new file mode 100644 index 00000000..f0c26e17 --- /dev/null +++ b/Renderer/Opengl/Model/Debug/DirectionalLightNode3D.h @@ -0,0 +1,30 @@ +#ifndef SNAKE3_DIRECTIONALLIGHTNODE3D_H +#define SNAKE3_DIRECTIONALLIGHTNODE3D_H + +#include + +#include "../Standard/MeshNode3D.h" +#include "LightNode3D.h" + +namespace Model { + class DirectionalLightNode3D : public LightNode3D, public MeshNode3D { + public: + DirectionalLightNode3D(const shared_ptr &contextState, + const shared_ptr &baseShader, + const shared_ptr &resourceManager); + + ~DirectionalLightNode3D() override; + + void setDirectionalLight(const shared_ptr &directional_light) override { + directionalLight = directional_light; + }; + + void render(const shared_ptr &camera, const glm::mat4 &projection, float dt, + const glm::mat4 &parentTransform, bool shadows) override; + + protected: + shared_ptr directionalLight; + }; +} // Model + +#endif //SNAKE3_DIRECTIONALLIGHTNODE3D_H diff --git a/Renderer/Opengl/Model/Debug/LightNode3D.cpp b/Renderer/Opengl/Model/Debug/LightNode3D.cpp new file mode 100644 index 00000000..e36c6aa1 --- /dev/null +++ b/Renderer/Opengl/Model/Debug/LightNode3D.cpp @@ -0,0 +1,55 @@ +#include "LightNode3D.h" + +namespace Model { + LightNode3D::LightNode3D(const shared_ptr &baseShader) { + arrowMesh = make_shared(baseShader); + } + + glm::mat4 LightNode3D::calculateArrowTransform(const glm::vec3 position, const glm::vec3 direction) { + // 1. Normalizovat cílový směr (kam to má svítit) + const glm::vec3 destDir = glm::normalize(direction); + + // 2. Definovat počáteční směr meshe (Model Space) + // Protože jsi mesh vygeneroval podél osy -Z (od 0 do -length), + // musíš algoritmu říct, že "přirozený" směr šipky je (0, 0, -1). + constexpr auto srcDir = glm::vec3(0, 0, -1); + + // 3. Spočítat osu rotace a úhel + const glm::vec3 rotAxis = glm::cross(srcDir, destDir); + const float dotProduct = glm::dot(srcDir, destDir); + + glm::mat4 rotationMatrix(1.0f); + + // Ošetření hraničních případů (paralelní vektory) + // Pokud je dotProduct blízko 1 nebo -1, cross product je blízko 0. + if (glm::length(rotAxis) < 0.001f) { + // Vektory jsou rovnoběžné + if (dotProduct < 0.0f) { + // Jsou proti sobě (180 stupňů) -> Světlo svítí přesně dozadu (+Z) + // Musíme to otočit o 180. Je jedno kolem jaké osy, třeba X nebo Y. + rotationMatrix = glm::rotate(glm::mat4(1.0f), glm::radians(180.0f), glm::vec3(0, 1, 0)); + } + // Pokud dotProduct > 0, jsou shodné -> Žádná rotace (identity matrix) + } else { + // Matematika pro Quaternion z dvou vektorů + // s = 2 * cos(theta/2) + const float s = sqrt((1.0f + dotProduct) * 2.0f); + const float invs = 1.0f / s; + + glm::quat q; + q.x = rotAxis.x * invs; + q.y = rotAxis.y * invs; + q.z = rotAxis.z * invs; + q.w = s * 0.5f; + + rotationMatrix = glm::mat4_cast(q); + } + + // 4. Sestavení finální Model Matrix + auto model = glm::mat4(1.0f); + model = glm::translate(model, position); // Nejdřív posun + model = model * rotationMatrix; // Pak rotace + + return model; + } +} // Model diff --git a/Renderer/Opengl/Model/Debug/LightNode3D.h b/Renderer/Opengl/Model/Debug/LightNode3D.h new file mode 100644 index 00000000..1c707965 --- /dev/null +++ b/Renderer/Opengl/Model/Debug/LightNode3D.h @@ -0,0 +1,22 @@ +#ifndef SNAKE3_LIGHTNODE3D_H +#define SNAKE3_LIGHTNODE3D_H + +#include + +#include "../Standard/WireframeArrowMesh.h" + +namespace Model { + class LightNode3D { + public: + explicit LightNode3D(const shared_ptr &baseShader); + + virtual ~LightNode3D() = default; + + protected: + glm::mat4 calculateArrowTransform(glm::vec3 position, glm::vec3 direction); + + shared_ptr arrowMesh; + }; +} // Model + +#endif //SNAKE3_LIGHTNODE3D_H diff --git a/Renderer/Opengl/Model/Game/BarrelNode3D.cpp b/Renderer/Opengl/Model/Game/BarrelNode3D.cpp new file mode 100644 index 00000000..24e889bd --- /dev/null +++ b/Renderer/Opengl/Model/Game/BarrelNode3D.cpp @@ -0,0 +1,34 @@ +#include "BarrelNode3D.h" + +#include "../Standard/ArrayMesh.h" + +namespace Model { + BarrelNode3D::BarrelNode3D(const shared_ptr &contextState, + const shared_ptr &resourceManager) : MeshNode3D(contextState, nullptr, resourceManager) { + } + + void BarrelNode3D::init() { + const auto shader = resourceManager->getShader("basicShader"); + const auto shadowsShader = resourceManager->getShader("shadowDepthShader"); + const auto barrel = make_shared(shader); + barrel->fromMesh(resourceManager->getModel("barrel_0")); + setPosition({0.6, 0.0f, -7.2f}); + setScale({0.13888889, 0.13888889, 0.13888889}); + setRotationX(90); + const auto barrelMaterial = make_shared(shader, shadowsShader); + barrelMaterial->setNormalEnabled(true); + barrelMaterial->setDirectionalLight(directionalLight); + barrelMaterial->setBlending(Blending::Opaque); + barrelMaterial->setShadow(resourceManager->getTexture("depth")); + const auto barrelAlbedoTexture = barrel->getMesh()->getTextures()[0].texture; + barrelAlbedoTexture->lazyLoad(true); + barrelMaterial->setAlbedo(barrelAlbedoTexture); + const auto barrelNormalTexture = barrel->getMesh()->getTextures()[1].texture; + barrelNormalTexture->lazyLoad(true); + barrelMaterial->setPointLights(pointLights); + barrelMaterial->setAmbientLightColorIntensity(2.5f); + barrel->setMaterial(barrelMaterial); + + mesh = barrel; + } +} // Model \ No newline at end of file diff --git a/Renderer/Opengl/Model/Game/BarrelNode3D.h b/Renderer/Opengl/Model/Game/BarrelNode3D.h new file mode 100644 index 00000000..54767e90 --- /dev/null +++ b/Renderer/Opengl/Model/Game/BarrelNode3D.h @@ -0,0 +1,14 @@ +#ifndef SNAKE3_BARRELNODE3D_H +#define SNAKE3_BARRELNODE3D_H + +#include "../Standard/MeshNode3D.h" + +namespace Model { + class BarrelNode3D final : public MeshNode3D { + public: + explicit BarrelNode3D(const shared_ptr &contextState, const shared_ptr &resourceManager); + void init(); + }; +} // Model + +#endif //SNAKE3_BARRELNODE3D_H \ No newline at end of file diff --git a/Renderer/Opengl/Model/Game/CoinMeshNode3D.cpp b/Renderer/Opengl/Model/Game/CoinMeshNode3D.cpp new file mode 100644 index 00000000..e075e07a --- /dev/null +++ b/Renderer/Opengl/Model/Game/CoinMeshNode3D.cpp @@ -0,0 +1,30 @@ +#include "CoinMeshNode3D.h" + +namespace Model { + CoinMeshNode3D::CoinMeshNode3D(const shared_ptr &spotLight, const shared_ptr &contextState, + const shared_ptr &mesh, + const shared_ptr &resourceManager) + : MeshNode3D(contextState, mesh, resourceManager), spotLight(spotLight) { + } + + void CoinMeshNode3D::update(const float dt, const uint64_t frameId) { + // update spotLight + if (spotLight) { + const auto coinPos = getPosition(); + if (coinPos == lastPos) { + return; + } + const auto coinScale = getScale(); + const float realX = coinPos.x * coinScale.x; + const float realY = coinPos.y * coinScale.y; + const glm::vec3 spotPos = {realX, realY, -1.5f}; + const glm::vec3 spotDir = {spotPos.x, spotPos.y, 0.0f}; + spotLight->setPosition(spotPos); + spotLight->setDirection(spotDir); + spotLight->setVisible(isVisible()); + lastPos = coinPos; + } + + MeshNode3D::update(dt, frameId); + } +} // Model diff --git a/Renderer/Opengl/Model/Game/CoinMeshNode3D.h b/Renderer/Opengl/Model/Game/CoinMeshNode3D.h new file mode 100644 index 00000000..7d9a7b77 --- /dev/null +++ b/Renderer/Opengl/Model/Game/CoinMeshNode3D.h @@ -0,0 +1,23 @@ +#ifndef SNAKE3_COINMESHNODE3D_H +#define SNAKE3_COINMESHNODE3D_H + +#include "../Standard/MeshNode3D.h" + +namespace Model { + class CoinMeshNode3D final : public MeshNode3D { + public: + // using MeshNode3D::MeshNode3D; + explicit CoinMeshNode3D(const shared_ptr &spotLight, + const shared_ptr &contextState, const shared_ptr &mesh, + const shared_ptr &resourceManager); + + void update(float dt, uint64_t frameId) override; + + protected: + double lastTime{}; + shared_ptr spotLight; + glm::vec3 lastPos{}; + }; +} // Model + +#endif //SNAKE3_COINMESHNODE3D_H diff --git a/Renderer/Opengl/Model/Game/RadarItem.cpp b/Renderer/Opengl/Model/Game/RadarItem.cpp new file mode 100644 index 00000000..af0b8302 --- /dev/null +++ b/Renderer/Opengl/Model/Game/RadarItem.cpp @@ -0,0 +1,48 @@ +#include "RadarItem.h" + +#include + +#include "../Standard/2D/QuadNode2D.h" + +namespace Model { + RadarItem::RadarItem(const shared_ptr &contextState, const shared_ptr &resourceManager, + const shared_ptr &mesh, const glm::vec3 &color, std::string name) + : mesh(mesh), changedSize(false), name(std::move(name)), + color(color) { + const auto quad = make_shared(4, 4, resourceManager->getShader("basic2d")); + quad->setColor(color); + radarItem = make_shared(contextState, quad, resourceManager); + radarItem->setVisible(mesh->isVisible()); + } + + void RadarItem::update() { + // update position + radarItem->setPosition(glm::vec3(-110 + 15 + (mesh->x - 16) / 32 * 4, 110 - 15 - (mesh->y - 16) / 32 * 4, 0)); + + if (mesh->hasChildrenChangedSignal()) { + changedSize = true; + } else { + changedSize = false; + } + } + + const vector> &RadarItem::getChildren() const { + return mesh->getChildren(); + } + + glm::vec3 RadarItem::getColor() const { + return color; + } + + shared_ptr RadarItem::getRadarItem() { + return radarItem; + } + + bool RadarItem::hasChangedSize() const { + return changedSize; + } + + std::string RadarItem::getName() const { + return name; + } +} // Model \ No newline at end of file diff --git a/Renderer/Opengl/Model/Game/RadarItem.h b/Renderer/Opengl/Model/Game/RadarItem.h new file mode 100644 index 00000000..57838a13 --- /dev/null +++ b/Renderer/Opengl/Model/Game/RadarItem.h @@ -0,0 +1,30 @@ +#ifndef SNAKE3_RADARITEM_H +#define SNAKE3_RADARITEM_H + +#include +#include +#include "../Standard/MeshNode3D.h" +#include "../Standard/2D/MeshNode2D.h" + +namespace Model { + class RadarItem { + public: + RadarItem(const shared_ptr &contextState, const shared_ptr &resourceManager, + const shared_ptr &mesh, const glm::vec3 &color, std::string name); + void update(); + shared_ptr getRadarItem(); + [[nodiscard]] bool hasChangedSize() const; + [[nodiscard]] bool hasReferenceHasOnlyRadar() const; + [[nodiscard]] std::string getName() const; + [[nodiscard]] const vector> &getChildren() const; + [[nodiscard]] glm::vec3 getColor() const; + private: + shared_ptr mesh; + shared_ptr radarItem; + bool changedSize; + std::string name; + glm::vec3 color; + }; +} // Model + +#endif //SNAKE3_RADARITEM_H \ No newline at end of file diff --git a/Renderer/Opengl/Model/Game/RadarMeshNode2D.cpp b/Renderer/Opengl/Model/Game/RadarMeshNode2D.cpp new file mode 100644 index 00000000..80adb25e --- /dev/null +++ b/Renderer/Opengl/Model/Game/RadarMeshNode2D.cpp @@ -0,0 +1,93 @@ +#include "RadarMeshNode2D.h" + +#include + +namespace Model { + RadarMeshNode2D::RadarMeshNode2D(const shared_ptr &contextState, const shared_ptr &mesh, + const shared_ptr &resourceManager) + : MeshNode2D(contextState, mesh, resourceManager) { + } + + void RadarMeshNode2D::render(const shared_ptr &camera, const glm::mat4 &ortho, const float dt, + const glm::mat4 &parentTransform) const { + + if (visible) { + const glm::mat4 finalTransform = parentTransform * this->getModelMatrix(); + for (const auto &snd: children | views::values) { + snd->render(camera, ortho, dt, transformDetached ? glm::mat4(1.0f) : finalTransform); + } + contextState->setBlendingMode(mesh->getBlending()); + contextState->setDepthTest(mesh->getDepthTest()); + contextState->setDepthWrite(mesh->getDepthWrite()); + mesh->render(camera, ortho, 1, finalTransform); + } else if (transformDetached) { + for (const auto &snd: children | views::values) { + snd->render(camera, ortho, dt, glm::mat4(1.0f)); + } + } + } + + void RadarMeshNode2D::update(const float dt, const uint64_t frameId) { + MeshNode2D::update(dt, frameId); + for (const auto &snd: items | views::values) { + snd->update(); + if (snd->hasChangedSize()) { + for (auto it = items.begin(); it != items.end(); ) { + if (it->first.rfind(snd->getName() + "-", 0) == 0) + it = items.erase(it); + else + ++it; + } + + for (auto it = children.begin(); it != children.end(); ) { + if (it->first.rfind(snd->getName() + "-", 0) == 0) + it = children.erase(it); + else + ++it; + } + + // create new radar items for a new snake tile + int index = 0; + for (const auto& child: snd->getChildren()) { + addItem(child, snd->getColor(), snd->getName() + "-" + std::to_string(index++)); + } + } + } + } + + void RadarMeshNode2D::addItem(const shared_ptr &item, const glm::vec3 &color, const std::string &name) { + const auto radarItem = make_shared(contextState, resourceManager, item, color, name); + radarItem->getRadarItem()->setVisible(item->isVisible()); + items.emplace(name, radarItem); + addNode(radarItem->getRadarItem(), name); + int index = 0; + + for (const auto &child: item->getChildren()) { + const std::string itemName = name + "-" + std::to_string(index++); + const auto subRadarItem = make_shared(contextState, resourceManager, child, color, itemName); + items.emplace(itemName, subRadarItem); + + addNode(subRadarItem->getRadarItem(), itemName); + } + } + + void RadarMeshNode2D::hideItems() const { + for (const auto &snd: children | views::values) { + snd->setVisible(false); + } + } + + void RadarMeshNode2D::showItems() const { + for (const auto &snd: children | views::values) { + snd->setVisible(true); + } + } + + void RadarMeshNode2D::hideItem(const std::string &name) const { + items.at(name)->getRadarItem()->setVisible(false); + } + + void RadarMeshNode2D::showItem(const std::string &name) const { + items.at(name)->getRadarItem()->setVisible(true); + } +} // Model diff --git a/Renderer/Opengl/Model/Game/RadarMeshNode2D.h b/Renderer/Opengl/Model/Game/RadarMeshNode2D.h new file mode 100644 index 00000000..5662ba2e --- /dev/null +++ b/Renderer/Opengl/Model/Game/RadarMeshNode2D.h @@ -0,0 +1,37 @@ +#ifndef SNAKE3_RADARMESHNODE2D_H +#define SNAKE3_RADARMESHNODE2D_H + +#include + +#include "RadarItem.h" +#include "../Standard/MeshNode3D.h" +#include "../Standard/2D/MeshNode2D.h" +#include "../Standard/2D/QuadNode2D.h" + +namespace Model { + class RadarMeshNode2D final : public MeshNode2D { + public: + RadarMeshNode2D(const shared_ptr &contextState, const shared_ptr &mesh, + const shared_ptr &resourceManager); + + void render(const shared_ptr &camera, const glm::mat4 &ortho, float dt, + const glm::mat4 &parentTransform) const override; + + void update(float dt, uint64_t frameId) override; + + void addItem(const shared_ptr &item, const glm::vec3 &color, const std::string &name); + + void hideItems() const; + + void showItems() const; + + void hideItem(const std::string &name) const; + + void showItem(const std::string &name) const; + + private: + map > items; + }; +} // Model + +#endif //SNAKE3_RADARMESHNODE2D_H \ No newline at end of file diff --git a/Renderer/Opengl/Model/Game/SnakeMeshNode3D.cpp b/Renderer/Opengl/Model/Game/SnakeMeshNode3D.cpp new file mode 100644 index 00000000..80eb6d25 --- /dev/null +++ b/Renderer/Opengl/Model/Game/SnakeMeshNode3D.cpp @@ -0,0 +1,308 @@ +#include "SnakeMeshNode3D.h" + +#include "../../../../Physic/BoxShape.h" +#include "../../../../Physic/SphereShape.h" +#include "../../../../Physic/CapsuleShape.h" +#include "../../../../Physic/CylinderShape.h" +#include "../../Material/Uniform/TextureUniform.h" +#include "../../Material/Uniform/TimerUniform.h" +#include "../Standard/AnimationArrayMesh.h" +#include "../Standard/SphereMesh.h" + +namespace Model { + SnakeMeshNode3D::SnakeMeshNode3D(const shared_ptr &contextState, const shared_ptr &mesh, + const shared_ptr &resourceManager, const shared_ptr &collisionSystem) + : MeshNode3D(contextState, mesh, resourceManager), collisionSystem(collisionSystem) { + timerUniform = make_shared(true); + if (resourceManager) { + const auto shader = resourceManager->getShader("basicShader"); + const auto shadowsShader = resourceManager->getShader("shadowDepthShader"); + tileMaterial = make_shared(StandardMaterial(shader, shadowsShader)); + tileMaterial->setColor({0.88, 0.05, 0.05}); + tileMaterial->setShadow(resourceManager->getTexture("depth")); + headMaterial = mesh->getMaterial(); + + timer = std::make_unique(false); + const auto respawnShader = resourceManager->getShader("respawnShader"); + respawnMaterial = make_shared(respawnShader, shadowsShader); + respawnMaterial->setShadow(resourceManager->getTexture("depth")); + respawnMaterial->addUniform("u_useMaterial", false); + respawnMaterial->addUniform("u_LightColor", glm::vec4(0.88, 0.05, 0.05, 1.0f)); + respawnMaterial->addUniform("u_Speed", 4.7f); + respawnMaterial->addUniform("u_Delay", 0.1f); + respawnMaterial->addUniform("u_FloatParameter", 0.1f); + + headRespawnMaterial = make_shared(respawnShader, shadowsShader); + headRespawnMaterial->setShadow(resourceManager->getTexture("depth")); + headRespawnMaterial->addUniform("u_useMaterial", true); + headRespawnMaterial->addUniform("u_Speed", 4.7f); + headRespawnMaterial->addUniform("u_Delay", 0.1f); + headRespawnMaterial->addUniform("u_FloatParameter", 0.1f); + + const auto textureUniform = make_shared(11, resourceManager->getTexture("fast_noise.bmp")); + respawnMaterial->addUniform("u_NoiseTexture", textureUniform); + respawnMaterial->addUniform("u_Time", timerUniform); + respawnMaterial->addUniform("useBones", false); + respawnMaterial->addUniform("useMaterial", true); + + headRespawnMaterial->addUniform("u_NoiseTexture", textureUniform); + headRespawnMaterial->addUniform("u_Time", timerUniform); + headRespawnMaterial->addUniform("useBones", false); + headRespawnMaterial->addUniform("useMaterial", true); + } + } + + void SnakeMeshNode3D::respawn() { + respawned = false; + + for (const auto &child: children) { + collisionSystem->removeCollider(child); + } + + children.clear(); + timerUniform->start(); + transformDetached = true; + mesh->setMaterial(headRespawnMaterial); + this->x = (23 - -23) / 2 * 32 + 16; + this->y = (-3 - -23) / 2 * 32 + 16; + this->setRotationX(90); + this->setRotationY(0); + this->setPosition({23, -3, -23}); + this->setDirection(NONE); + + const auto sphere = createTileNode(); + + const auto tile = make_shared(contextState, sphere, resourceManager, collisionSystem); + if (directionalLight) { + tile->setDirectionalLight(directionalLight); + } + tile->setSpotLights(spotLights); + tile->setPointLights(pointLights); + tile->setPosition({21, -3, -23}); + tile->setScale({0.041667f, 0.041667f, 0.041667f}); + tile->x = x - 32; + tile->y = y; + + const auto sphereShape = make_shared(resourceManager, contextState,0.77f); + const auto shape = make_shared(contextState, resourceManager, sphereShape); + tile->setCollisionShape(shape); + collisionSystem->addCollider(tile); + + addNode(tile); + + const auto tile2 = make_shared(contextState, sphere, resourceManager, collisionSystem); + if (directionalLight) { + tile2->setDirectionalLight(directionalLight); + } + tile2->setSpotLights(spotLights); + tile2->setPointLights(pointLights); + tile2->setScale({0.041667f, 0.041667f, 0.041667f}); + tile2->setPosition({19, -3, -23}); + tile2->x = x - 64; + tile2->y = y; + + const auto sphereShape2 = make_shared(resourceManager, contextState,0.77f); + const auto shape2 = make_shared(contextState, resourceManager, sphereShape2); + tile2->setCollisionShape(shape2); + collisionSystem->addCollider(tile2); + + addNode(tile2); + + const auto tile3 = make_shared(contextState, sphere, resourceManager, collisionSystem); + if (directionalLight) { + tile3->setDirectionalLight(directionalLight); + } + tile3->setPointLights(pointLights); + tile3->setSpotLights(spotLights); + tile3->setScale({0.041667f, 0.041667f, 0.041667f}); + tile3->setPosition({17, -3, -23}); + tile3->x = x - 96; + tile3->y = y; + + const auto sphereShape3 = make_shared(resourceManager, contextState,0.77f); + const auto shape3 = make_shared(contextState, resourceManager, sphereShape3); + tile3->setCollisionShape(shape3); + collisionSystem->addCollider(tile3); + + addNode(tile3); + + const auto tile4 = make_shared(contextState, sphere, resourceManager, collisionSystem); + if (directionalLight) { + tile4->setDirectionalLight(directionalLight); + } + tile4->setPointLights(pointLights); + tile4->setSpotLights(spotLights); + tile4->setScale({0.041667f, 0.041667f, 0.041667f}); + tile4->setPosition({15, -3, -23}); + tile4->x = x - 128; + tile4->y = y; + + const auto capsuleShape = make_shared(resourceManager, contextState, 0.8f, 2.5f); + const auto shape4 = make_shared(contextState, resourceManager, capsuleShape); + shape4->setRotationX(90.0f); + tile4->setCollisionShape(shape4); + collisionSystem->addCollider(tile4); + + addNode(tile4); + + const auto tile5 = make_shared(contextState, sphere, resourceManager, collisionSystem); + if (directionalLight) { + tile5->setDirectionalLight(directionalLight); + } + tile5->setPointLights(pointLights); + tile5->setSpotLights(spotLights); + tile5->setScale({0.041667f, 0.041667f, 0.041667f}); + tile5->setPosition({13, -3, -23}); + tile5->x = x - 160; + tile5->y = y; + + const auto cylinderShape = make_shared(resourceManager, contextState, 0.8f, 2.5f); + const auto shape5 = make_shared(contextState, resourceManager, cylinderShape); + shape5->setRotationX(90.0f); + tile5->setCollisionShape(shape5); + collisionSystem->addCollider(tile5); + + addNode(tile5); + } + + void SnakeMeshNode3D::setDirectionalLight(const shared_ptr &directional_light) { + directionalLight = directional_light; + if (resourceManager) { + tileMaterial->setDirectionalLight(directional_light); + respawnMaterial->setDirectionalLight(directionalLight); + } + for (auto &child: children) { + reinterpret_pointer_cast(child)->setDirectionalLight(directional_light); + } + } + + void SnakeMeshNode3D::setSpotLights(const vector> &spot_light) { + MeshNode3D::setSpotLights(spot_light); + if (resourceManager) { + tileMaterial->setSpotLights(spotLights); + respawnMaterial->setSpotLights(spotLights); + } + for (auto &child: children) { + reinterpret_pointer_cast(child)->setSpotLights(spot_light); + } + } + + void SnakeMeshNode3D::setPointLights(const vector> &point_light) { + MeshNode3D::setPointLights(point_light); + if (resourceManager) { + tileMaterial->setPointLights(point_light); + respawnMaterial->setPointLights(point_light); + } + for (auto &child: children) { + reinterpret_pointer_cast(child)->setPointLights(point_light); + } + } + + void SnakeMeshNode3D::setDirection(const eDIRECTION direction) { + this->direction = direction; + } + + shared_ptr SnakeMeshNode3D::createTileNode() const { + const auto sphere = make_shared(nullptr, 1.5, 0.75); + sphere->setMaterial(respawned ? tileMaterial : respawnMaterial); + + return sphere; + } + + void SnakeMeshNode3D::addTile(const eDIRECTION direction) { + glm::vec3 pos = {}; + const auto sphere = createTileNode(); + + const auto PrevIter = children.end() - 1; + + if (getDirection() != STOP) { + pos = (*PrevIter)->getPosition(); + } else { + switch (direction) { + case LEFT: + if ((*PrevIter)->getPosition().x - 2 >= -25) { + pos.x = (*PrevIter)->getPosition().x - 32; + pos.y = (*PrevIter)->getPosition().y; + } + break; + case RIGHT: + if ((*PrevIter)->getPosition().x + 2 <= 752) { + pos.x = (*PrevIter)->getPosition().x + 32; + pos.y = (*PrevIter)->getPosition().y; + } + break; + case UP: + if ((*PrevIter)->getPosition().y - 2 >= -25) { + pos.x = (*PrevIter)->getPosition().x; + pos.y = (*PrevIter)->getPosition().y - 32; + } + break; + case DOWN: + if ((*PrevIter)->getPosition().y + 2 <= 752) { + pos.x = (*PrevIter)->getPosition().x; + pos.y = (*PrevIter)->getPosition().y + 32; + } + break; + default: + break; + } + } + + const auto tile = make_shared(contextState, sphere, resourceManager, collisionSystem); + if (directionalLight) { + tile->setDirectionalLight(directionalLight); + } + tile->setSpotLights(spotLights); + tile->setPointLights(pointLights); + tile->setPosition(pos); + tile->setScale({0.041667f, 0.041667f, 0.041667f}); + tile->x = (*PrevIter)->x; + tile->y = (*PrevIter)->y; + + const auto sphereShape = make_shared(resourceManager, contextState,0.77f); + const auto shape = make_shared(contextState, resourceManager, sphereShape); + tile->setCollisionShape(shape); + collisionSystem->addCollider(tile); + + addNode(tile); + } + + SnakeMeshNode3D::eDIRECTION SnakeMeshNode3D::getDirection() const { + return direction; + } + + void SnakeMeshNode3D::render(const shared_ptr &camera, const glm::mat4 &projection, const float dt, + const glm::mat4 &parentTransform, const bool shadows) { + if (timerUniform->getElapsed() > 0.5f) { + timerUniform->stop(); + mesh->setMaterial(headMaterial); + for (auto &child: children) { + if (const auto tile = dynamic_pointer_cast(child)) { + tile->stopRespawn(); + } + } + respawned = true; + } + + MeshNode3D::render(camera, projection, dt, parentTransform, shadows); + + if (!collisionShapes.empty()) { + const glm::mat4 finalTransform = parentTransform * this->getModelMatrix(); + collisionShapes.begin()->get()->render(camera, projection, dt, finalTransform, shadows); + } + } + + void SnakeMeshNode3D::stopRespawn() { + mesh->setMaterial(tileMaterial); + respawned = true; + } + + bool SnakeMeshNode3D::isReady() const { + return respawned; + } + + void SnakeMeshNode3D::setCollisionShape(const shared_ptr &collisionShape) { + collisionShapes.clear(); + collisionShapes.push_back(collisionShape); + } +} // Model diff --git a/Renderer/Opengl/Model/Game/SnakeMeshNode3D.h b/Renderer/Opengl/Model/Game/SnakeMeshNode3D.h new file mode 100644 index 00000000..56f7f762 --- /dev/null +++ b/Renderer/Opengl/Model/Game/SnakeMeshNode3D.h @@ -0,0 +1,74 @@ +#ifndef SNAKE3_SNAKEMESHNODE3D_H +#define SNAKE3_SNAKEMESHNODE3D_H + +#include "../../../../Physic/Algorithms/CollisionAlgorithms.h" +#include "../../../../Tools/Timer.h" +#include "../../Material/ShaderMaterial.h" +#include "../../Material/Uniform/TimerUniform.h" +#include "../Standard/MeshNode3D.h" +#include "../Standard/SphereMesh.h" + +using namespace Tools; +using namespace Uniform; +using namespace Physic; +using namespace std; + +namespace Model { + class SnakeMeshNode3D final : public MeshNode3D { + public: + enum eDIRECTION { + NONE = -1, + STOP = 0, + LEFT = 1, + RIGHT = 2, + UP = 3, + DOWN = 4, + CRASH = 100, + PAUSE = 200, + }; + + using MeshNode3D::MeshNode3D; + + explicit SnakeMeshNode3D(const shared_ptr &contextState, + const shared_ptr &mesh, + const shared_ptr &resourceManager, + const shared_ptr &collisionSystem + ); + + void respawn(); + + void setDirectionalLight(const shared_ptr &directional_light) override; + + void setSpotLights(const vector > &spot_light) override; + + void setPointLights(const vector > &point_light) override; + + void setDirection(eDIRECTION direction); + + void addTile(eDIRECTION direction); + + [[nodiscard]] eDIRECTION getDirection() const; + + void render(const shared_ptr &camera, const glm::mat4 &projection, float dt, + const glm::mat4 &parentTransform, bool shadows) override; + + bool isReady() const; + + void setCollisionShape(const shared_ptr &collisionShape); + + private: + void stopRespawn(); + unique_ptr timer; + shared_ptr createTileNode() const; + shared_ptr tileMaterial; + shared_ptr headMaterial; + shared_ptr respawnMaterial; + shared_ptr headRespawnMaterial; + shared_ptr timerUniform; + shared_ptr collisionSystem; + eDIRECTION direction = NONE; + bool respawned = false; + }; +} // Model + +#endif //SNAKE3_SNAKEMESHNODE3D_H diff --git a/Renderer/Opengl/Model/Game/StreetLampNode3D.cpp b/Renderer/Opengl/Model/Game/StreetLampNode3D.cpp new file mode 100644 index 00000000..24f15b8b --- /dev/null +++ b/Renderer/Opengl/Model/Game/StreetLampNode3D.cpp @@ -0,0 +1,80 @@ +#include "StreetLampNode3D.h" + +#include "../Standard/ArrayMesh.h" + +namespace Model { + StreetLampNode3D::StreetLampNode3D(const shared_ptr &contextState, + const shared_ptr &resourceManager) : MeshNode3D(contextState, nullptr, resourceManager) { + } + + void StreetLampNode3D::init() { + const auto shader = resourceManager->getShader("basicShader"); + const auto shadowsShader = resourceManager->getShader("shadowDepthShader"); + + const auto streetLampMesh1 = make_shared(shader); + streetLampMesh1->fromMesh(resourceManager->getModel("streetlamp_0")); + + const auto streetLampMesh2 = make_shared(shader); + streetLampMesh2->fromMesh(resourceManager->getModel("streetlamp_1")); + streetLampMesh2->setDepthWrite(false); + + const auto streetLampMesh3 = make_shared(shader); + streetLampMesh3->fromMesh(resourceManager->getModel("streetlamp_2")); + + const auto streetLamp2 = make_shared(contextState, streetLampMesh2, resourceManager); + const auto streetLamp3 = make_shared(contextState, streetLampMesh3, resourceManager); + setPosition({0.0, 0.0f, -7.2f}); + setScale({0.13888889, 0.13888889, 0.13888889}); + setRotationX(90); + streetLamp2->addNode(streetLamp3); + + mesh = streetLampMesh1; + addNode(streetLamp2); + + const auto streetLampMaterial = make_shared(shader, shadowsShader); + streetLampMaterial->setNormalEnabled(true); + streetLampMaterial->setDirectionalLight(directionalLight); + streetLampMaterial->setBlending(Blending::Opaque); + streetLampMaterial->setShadow(resourceManager->getTexture("depth")); + const auto albedoTexture = mesh->getMesh()->getTextures()[0].texture; + albedoTexture->lazyLoad(true); + streetLampMaterial->setAlbedo(albedoTexture); + const auto normalTexture = mesh->getMesh()->getTextures()[1].texture; + normalTexture->lazyLoad(true); + const auto pbrTexture = mesh->getMesh()->getTextures()[2].texture; + pbrTexture->lazyLoad(true); + streetLampMaterial->setRoughness(pbrTexture); + streetLampMaterial->setMetalness(pbrTexture); + streetLampMaterial->setAoMap(pbrTexture); + streetLampMaterial->setEnvironmentMap(resourceManager->getTexture("skybox")); + streetLampMaterial->setNormal(normalTexture); + streetLampMaterial->setPointLights(pointLights); + streetLampMaterial->setAmbientLightColorIntensity(2.5f); + mesh->setMaterial(streetLampMaterial); + + const auto streetLampMaterial2 = make_shared(shader, shadowsShader); + streetLampMaterial2->setDirectionalLight(directionalLight); + streetLampMaterial2->setBlending(Blending::Additive); + streetLampMaterial2->setNormalEnabled(true); + streetLampMaterial2->setShadow(resourceManager->getTexture("depth")); + const auto albedoTexture2 = streetLampMesh2->getMesh()->getTextures()[0].texture; + albedoTexture2->lazyLoad(true); + streetLampMaterial2->setAlbedo(albedoTexture2); + streetLampMaterial2->setNormal(normalTexture); + streetLampMaterial2->setPointLights(pointLights); + streetLampMaterial2->setRoughness(pbrTexture); + streetLampMaterial2->setMetalness(pbrTexture); + streetLampMaterial2->setAoMap(pbrTexture); + streetLampMaterial2->setAmbientLightColorIntensity(2); + streetLampMesh2->setMaterial(streetLampMaterial2); + + const auto streetLampMaterial3 = make_shared(shader, shadowsShader); + streetLampMaterial3->setDirectionalLight(directionalLight); + streetLampMaterial3->setBlending(Blending::Opaque); + streetLampMaterial3->setNormalEnabled(true); + streetLampMaterial3->setColor({0.98 * 200, 0.99 * 200, 0.007 * 200}); + streetLampMaterial3->setNormal(streetLampMesh3->getMesh()->getTextures()[1].texture); + streetLampMaterial3->setPointLights(pointLights); + streetLampMesh3->setMaterial(streetLampMaterial3); + } +} // Model \ No newline at end of file diff --git a/Renderer/Opengl/Model/Game/StreetLampNode3D.h b/Renderer/Opengl/Model/Game/StreetLampNode3D.h new file mode 100644 index 00000000..4e6a7d4a --- /dev/null +++ b/Renderer/Opengl/Model/Game/StreetLampNode3D.h @@ -0,0 +1,14 @@ +#ifndef SNAKE3_STREETLAMPNODE3D_H +#define SNAKE3_STREETLAMPNODE3D_H + +#include "../Standard/MeshNode3D.h" + +namespace Model { + class StreetLampNode3D final : public MeshNode3D { + public: + explicit StreetLampNode3D(const shared_ptr &contextState, const shared_ptr &resourceManager); + void init(); + }; +} // Model + +#endif //SNAKE3_STREETLAMPNODE3D_H \ No newline at end of file diff --git a/Renderer/Opengl/Model/GameFieldModel.cpp b/Renderer/Opengl/Model/GameFieldModel.cpp deleted file mode 100644 index 5cc6048d..00000000 --- a/Renderer/Opengl/Model/GameFieldModel.cpp +++ /dev/null @@ -1,117 +0,0 @@ -#include "GameFieldModel.h" -#include "../../../Manager/VboIndexer.h" - -namespace Model { - GameFieldModel::GameFieldModel(BaseItem *baseItem) : gameField(baseItem) { - vector vbo_vertices; - vector vbo_normals; - vector vbo_uvs; - vector tangents; - vector biTangents; - - vector vertices; - vector indices; - - Vertex vertex{}; - vertex.position = {-1.0f, -1.0f, -1.0f}; - vertex.normal = {0.0f, 1.0f, 0.0f}; - vertex.color = {1.0f, 1.0f, 1.0f}; - vertex.texUV = {0.0f, 0.0f}; - - vbo_vertices.push_back(vertex.position); - vbo_uvs.push_back(vertex.texUV); - vbo_normals.push_back(vertex.normal); - - vertices.push_back(vertex); - indices.push_back(0); - - Vertex vertex2{}; - vertex2.position = {3.0f, -1.0f, -1.0f}; - vertex2.normal = {0.0f, 1.0f, 0.0f}; - vertex2.color = {1.0f, 1.0f, 1.0f}; - vertex2.texUV = {2.0f, 0.0f}; - - vbo_vertices.push_back(vertex2.position); - vbo_uvs.push_back(vertex2.texUV); - vbo_normals.push_back(vertex2.normal); - - vertices.push_back(vertex2); - indices.push_back(1); - - Vertex vertex3{}; - vertex3.position = {3.0f, 3.0f, -1.0f}; - vertex3.normal = {0.0f, 1.0f, 0.0f}; - vertex3.color = {1.0f, 1.0f, 1.0f}; - vertex3.texUV = {2.0f, 2.0f}; - - vbo_vertices.push_back(vertex3.position); - vbo_uvs.push_back(vertex3.texUV); - vbo_normals.push_back(vertex3.normal); - - vertices.push_back(vertex3); - indices.push_back(2); - - Vertex vertex4{}; - vertex4.position = {3.0f, 3.0f, -1.0f}; - vertex4.normal = {0.0f, 1.0f, 0.0f}; - vertex4.color = {1.0f, 1.0f, 1.0f}; - vertex4.texUV = {2.0f, 2.0f}; - - vbo_vertices.push_back(vertex4.position); - vbo_uvs.push_back(vertex4.texUV); - vbo_normals.push_back(vertex4.normal); - - vertices.push_back(vertex4); - indices.push_back(3); - - Vertex vertex5{}; - vertex5.position = {-1.0f, 3.0f, -1.0f}; - vertex5.normal = {0.0f, 1.0f, 0.0f}; - vertex5.color = {1.0f, 1.0f, 1.0f}; - vertex5.texUV = {0.0f, 2.0f}; - - vbo_vertices.push_back(vertex5.position); - vbo_uvs.push_back(vertex5.texUV); - vbo_normals.push_back(vertex5.normal); - - vertices.push_back(vertex5); - indices.push_back(4); - - Vertex vertex6{}; - vertex6.position = {-1.0f, -1.0f, -1.0f}; - vertex6.normal = {0.0f, 1.0f, 0.0f}; - vertex6.color = {1.0f, 1.0f, 1.0f}; - vertex6.texUV = {0.0f, 0.0f}; - - vbo_vertices.push_back(vertex6.position); - vbo_uvs.push_back(vertex6.texUV); - vbo_normals.push_back(vertex6.normal); - - vertices.push_back(vertex6); - indices.push_back(5); - - VboIndexer::computeTangentBasis(vbo_vertices, vbo_uvs, vbo_normals, tangents, biTangents); - - int index = 0; - for (auto iter : tangents) { - auto vertIter = vertices.begin() + index; - (*vertIter).tangents = iter; - index++; - } - - mesh = new Mesh(vertices, indices); - } - - GameFieldModel::~GameFieldModel() { - delete mesh; - } - - Mesh *GameFieldModel::getMesh() const { - return mesh; - } - - BaseItem *GameFieldModel::getGameField() const { - return gameField; - } - -} // Model \ No newline at end of file diff --git a/Renderer/Opengl/Model/GameFieldModel.h b/Renderer/Opengl/Model/GameFieldModel.h deleted file mode 100644 index 41c59ea3..00000000 --- a/Renderer/Opengl/Model/GameFieldModel.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef SNAKE3_GAMEFIELDMODEL_H -#define SNAKE3_GAMEFIELDMODEL_H - -#include "../../../Manager/ShaderManager.h" -#include "../../../ItemsDto/GameField.h" -#include "Utils/Mesh.h" - -using namespace Manager; -using namespace ItemsDto; -using namespace ModelUtils; -using namespace std; - -namespace Model { - - class GameFieldModel { - public: - explicit GameFieldModel(BaseItem *gameField); - virtual ~GameFieldModel(); - [[nodiscard]] Mesh *getMesh() const; - [[nodiscard]] BaseItem *getGameField() const; - - protected: - BaseItem* gameField{}; - Mesh* mesh; - }; - -} // Model - -#endif //SNAKE3_GAMEFIELDMODEL_H diff --git a/Renderer/Opengl/Model/MeshModel.cpp b/Renderer/Opengl/Model/MeshModel.cpp index cf48343e..20f1d85c 100644 --- a/Renderer/Opengl/Model/MeshModel.cpp +++ b/Renderer/Opengl/Model/MeshModel.cpp @@ -3,6 +3,7 @@ namespace Model { MeshModel::MeshModel(BaseItem *baseItem) : item(baseItem) { + using namespace Manager; vector vbo_vertices; vector vbo_normals; vector vbo_uvs; diff --git a/Renderer/Opengl/Model/MeshModel.h b/Renderer/Opengl/Model/MeshModel.h index ae4cde1b..4e7f1f4e 100644 --- a/Renderer/Opengl/Model/MeshModel.h +++ b/Renderer/Opengl/Model/MeshModel.h @@ -1,10 +1,11 @@ #ifndef SNAKE3_MESHMODEL_H #define SNAKE3_MESHMODEL_H +#include "../../../ItemsDto/BaseItem.h" #include "Utils/Mesh.h" -using namespace Manager; using namespace ModelUtils; +using namespace ItemsDto; namespace Model { diff --git a/Renderer/Opengl/Model/RadarModel.cpp b/Renderer/Opengl/Model/RadarModel.cpp deleted file mode 100644 index 9fa31337..00000000 --- a/Renderer/Opengl/Model/RadarModel.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include "RadarModel.h" -#include "../../../Manager/VboIndexer.h" - -namespace Model { - RadarModel::RadarModel(Radar *radar) : radar(radar) { - vector vertices; - - createVertices(); - } - - RadarModel::~RadarModel() { - delete mesh; - } - - void RadarModel::createVertices() { - vector vbo_vertices; - vector vbo_normals; - vector vbo_uvs; - vector tangents; - vector biTangents; - - vector vertices; - vector indices; - - Vertex vertex{}; - vertex.position = {-1.0f, -1.0f, -1.0f}; - vertex.normal = glm::normalize(vertex.position); - vertex.color = {1.0f, 1.0f, 1.0f}; - vertex.texUV = {0.0f, 0.0f}; - - vbo_vertices.push_back(vertex.position); - vbo_uvs.push_back(vertex.texUV); - vbo_normals.push_back(vertex.normal); - - vertices.push_back(vertex); - indices.push_back(0); - - Vertex vertex2{}; - vertex2.position = {1.0f, -1.0f, -1.0f}; - vertex2.normal = glm::normalize(vertex2.position); - vertex2.color = {1.0f, 1.0f, 1.0f}; - vertex2.texUV = {1.0f, 0.0f}; - - vbo_vertices.push_back(vertex2.position); - vbo_uvs.push_back(vertex2.texUV); - vbo_normals.push_back(vertex2.normal); - - vertices.push_back(vertex2); - indices.push_back(1); - - Vertex vertex3{}; - vertex3.position = {1.0f, 1.0f, -1.0f}; - vertex3.normal = glm::normalize(vertex3.position); - vertex3.color = {1.0f, 1.0f, 1.0f}; - vertex3.texUV = {1.0f, 1.0f}; - - vbo_vertices.push_back(vertex3.position); - vbo_uvs.push_back(vertex3.texUV); - vbo_normals.push_back(vertex3.normal); - - vertices.push_back(vertex3); - indices.push_back(2); - - Vertex vertex4{}; - vertex4.position = {1.0f, 1.0f, -1.0f}; - vertex4.normal = glm::normalize(vertex4.position); - vertex4.color = {1.0f, 1.0f, 1.0f}; - vertex4.texUV = {1.0f, 1.0f}; - - vbo_vertices.push_back(vertex4.position); - vbo_uvs.push_back(vertex4.texUV); - vbo_normals.push_back(vertex4.normal); - - vertices.push_back(vertex4); - indices.push_back(3); - - Vertex vertex5{}; - vertex5.position = {-1.0f, 1.0f, -1.0f}; - vertex5.normal = glm::normalize(vertex5.position); - vertex5.color = {1.0f, 1.0f, 1.0f}; - vertex5.texUV = {0.0f, 1.0f}; - - vbo_vertices.push_back(vertex5.position); - vbo_uvs.push_back(vertex5.texUV); - vbo_normals.push_back(vertex5.normal); - - vertices.push_back(vertex5); - indices.push_back(4); - - Vertex vertex6{}; - vertex6.position = {-1.0f, -1.0f, -1.0f}; - vertex6.normal = glm::normalize(vertex6.position); - vertex6.color = {1.0f, 1.0f, 1.0f}; - vertex6.texUV = {0.0f, 0.0f}; - - vbo_vertices.push_back(vertex6.position); - vbo_uvs.push_back(vertex6.texUV); - vbo_normals.push_back(vertex6.normal); - - vertices.push_back(vertex6); - indices.push_back(5); - - VboIndexer::computeTangentBasis(vbo_vertices, vbo_uvs, vbo_normals, tangents, biTangents); - - int index = 0; - for (auto iter : tangents) { - auto vertIter = vertices.begin() + index; - (*vertIter).tangents = iter; - index++; - } - - mesh = new Mesh(vertices, indices); - } - - Mesh *RadarModel::getMesh() const { - return mesh; - } - -} // ObjModelLoader \ No newline at end of file diff --git a/Renderer/Opengl/Model/RadarModel.h b/Renderer/Opengl/Model/RadarModel.h deleted file mode 100644 index 32700949..00000000 --- a/Renderer/Opengl/Model/RadarModel.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef SNAKE3_RADARMODEL_H -#define SNAKE3_RADARMODEL_H - -#include "../../../ItemsDto/Radar.h" -#include "Utils/Mesh.h" - -using namespace Manager; -using namespace ItemsDto; -using namespace ModelUtils; -using namespace std; - -namespace Model { - - class RadarModel { - public: - explicit RadarModel(Radar *radar); - virtual ~RadarModel(); - - Mesh *getMesh() const; - - protected: - void createVertices(); - Radar* radar{}; - Mesh* mesh{}; - }; - -} // Model - -#endif //SNAKE3_RADARMODEL_H diff --git a/Renderer/Opengl/Model/RainModel.cpp b/Renderer/Opengl/Model/RainModel.cpp index 458398d4..8995e8e5 100644 --- a/Renderer/Opengl/Model/RainModel.cpp +++ b/Renderer/Opengl/Model/RainModel.cpp @@ -1,9 +1,12 @@ #include "RainModel.h" + +#include + #include "../../../Manager/VboIndexer.h" Model::RainModel::RainModel(BaseItem *baseItem, unsigned int amount) : item(baseItem), amount(amount), lastUsedParticle(0) { - + using namespace Manager; vector vbo_vertices; vector vbo_normals; vector vbo_uvs; diff --git a/Renderer/Opengl/Model/RainModel.h b/Renderer/Opengl/Model/RainModel.h index f1008476..ef2031b7 100644 --- a/Renderer/Opengl/Model/RainModel.h +++ b/Renderer/Opengl/Model/RainModel.h @@ -1,11 +1,11 @@ #ifndef SNAKE3_RAINMODEL_H #define SNAKE3_RAINMODEL_H +#include "../../../ItemsDto/BaseItem.h" #include "Utils/Mesh.h" -#include -using namespace Manager; using namespace ModelUtils; +using namespace ItemsDto; namespace Model { diff --git a/Renderer/Opengl/Model/SpinnerMesh.cpp b/Renderer/Opengl/Model/SpinnerMesh.cpp new file mode 100644 index 00000000..3eee2808 --- /dev/null +++ b/Renderer/Opengl/Model/SpinnerMesh.cpp @@ -0,0 +1,70 @@ +#include "SpinnerMesh.h" +#include + +namespace Model { + SpinnerMesh::SpinnerMesh(const shared_ptr &baseShader) + : TringleMesh3D(baseShader, 0.04, 0.04), time(0) { + colors = { + {1.0f, 0.0f, 0.0f}, + {1.0f, 0.5f, 0.0f}, + {1.0f, 1.0f, 0.0f}, + {0.0f, 1.0f, 0.0f}, + {0.0f, 1.0f, 1.0f}, + {0.0f, 0.5f, 1.0f}, + {0.5f, 0.0f, 1.0f}, + {1.0f, 0.0f, 1.0f} + }; + } + + void SpinnerMesh::render(const shared_ptr &camera, const glm::mat4 &projection, float dt, + const glm::mat4 &parentTransform, bool shadows) const { + baseShader->use(); + baseShader->setUniform("view", camera->getViewMatrix()); + baseShader->setUniform("projection", projection); + mesh->bind(); + + for (int i = 0; i < numInstances; ++i) { + const float progress = static_cast(i) / static_cast(numInstances); + const float angle = progress * glm::two_pi() * spiralTurns - time; + const float radius = 0.1f + progress * 0.04f; + glm::vec3 position(radius * cos(angle), radius * sin(angle), 0.0f); + + const float scale = 0.5f + progress * 0.05f; + + auto model = glm::mat4(1.0f); + model = glm::translate(model, position); + model = glm::rotate(model, angle - glm::half_pi(), glm::vec3(0.0f, 0.0f, 1.0f)); + model = glm::scale(model, glm::vec3(scale)); + + float hue = fmod(progress - time * 0.2f, 1.0f); + if (hue < 0.0f) hue += 1.0f; + + baseShader->setUniform("model", parentTransform * model); + baseShader->setUniform("objectColor", hsvToRgb(hue, 0.9f, 1.0f)); + + glDrawElements(GL_TRIANGLES, static_cast(mesh->getIndices().size()), GL_UNSIGNED_INT, + nullptr); + } + } + + void SpinnerMesh::update(const float dt) { + time += dt * speed; + } + + glm::vec3 SpinnerMesh::hsvToRgb(const float h, const float s, float v) { + if (s == 0.0f) return {v, v, v}; + int i = static_cast(h * 6.0f); + const float f = h * 6.0f - static_cast(i); + float p = v * (1.0f - s); + float q = v * (1.0f - f * s); + float t = v * (1.0f - (1.0f - f) * s); + i %= 6; + if (i == 0) return {v, t, p}; + if (i == 1) return {q, v, p}; + if (i == 2) return {p, v, t}; + if (i == 3) return {p, q, v}; + if (i == 4) return {t, p, v}; + return {v, p, q}; + } + +} // Model diff --git a/Renderer/Opengl/Model/SpinnerMesh.h b/Renderer/Opengl/Model/SpinnerMesh.h new file mode 100644 index 00000000..5198f728 --- /dev/null +++ b/Renderer/Opengl/Model/SpinnerMesh.h @@ -0,0 +1,27 @@ +#ifndef SNAKE3_SPINNER_H +#define SNAKE3_SPINNER_H + +#include +#include "Standard/StandardMesh.h" +#include "Standard/TringleMesh3D.h" + +using namespace ModelUtils; + +namespace Model { + class SpinnerMesh final : public TringleMesh3D { + public: + explicit SpinnerMesh(const shared_ptr &baseShader); + void render(const shared_ptr &camera, const glm::mat4 &projection, float dt, + const glm::mat4 &parentTransform, bool shadows) const override; + void update(float dt) override; + protected: + static glm::vec3 hsvToRgb(float h, float s, float v); + std::vector colors; + float time; + int numInstances = 60; + float speed = 2.0f; + float spiralTurns = 3.0f; + }; +} // Model + +#endif //SNAKE3_SPINNER_H \ No newline at end of file diff --git a/Renderer/Opengl/Model/Standard/2D/BaseNode2D.cpp b/Renderer/Opengl/Model/Standard/2D/BaseNode2D.cpp new file mode 100644 index 00000000..f4ff7543 --- /dev/null +++ b/Renderer/Opengl/Model/Standard/2D/BaseNode2D.cpp @@ -0,0 +1,78 @@ +#include "BaseNode2D.h" + +#include "../../../Material/StandardMaterial.h" + +namespace Model { + BaseNode2D::BaseNode2D(const shared_ptr &baseShader) : baseShader(baseShader), color(1.0f) { + } + + void BaseNode2D::setMaterial(const shared_ptr &material) { + this->material = material; + } + + void BaseNode2D::setColor(const glm::vec3 &color) { + this->color = color; + } + + void BaseNode2D::render(const shared_ptr &camera, const glm::mat4 &ortho, float dt, + const glm::mat4 &parentTransform) const { + + if (const auto standardMaterial = std::dynamic_pointer_cast(material)) { + standardMaterial.get()->bind( + camera->getPosition(), + camera->getViewMatrix(), + ortho, + parentTransform, + false + ); + } else if (baseShader) { + baseShader->use(); + baseShader->setMat4("projection", ortho); + baseShader->setMat4("model", parentTransform); + baseShader->setVec3("color", color); + baseShader->setInt("textureMap", 0); + baseShader->setBool("useMaterial", true); + baseShader->setFloat("alpha", 1.0); + baseShader->setFloat("expansion", 1.0); + } + + mesh->bind(); + glDrawElements(GL_TRIANGLES, static_cast(mesh->getIndices().size()), GL_UNSIGNED_INT, + nullptr); + if (const auto standardMaterial = std::dynamic_pointer_cast(material)) { + standardMaterial.get()->unbind(); + } + } + + void BaseNode2D::setBlending(const Blending blending) { + this->blending = blending; + } + + void BaseNode2D::setDepthTest(const bool depthTest) { + this->depthTest = depthTest; + } + + bool BaseNode2D::getDepthTest() const { + return depthTest; + } + + void BaseNode2D::setDepthWrite(const bool depthWrite) { + this->depthWrite = depthWrite; + } + + bool BaseNode2D::getDepthWrite() const { + return depthWrite; + } + + Blending BaseNode2D::getBlending() const { + return blending; + } + + void BaseNode2D::bind() const { + mesh->bind(); + } + + unsigned long BaseNode2D::indicesCount() const { + return mesh->getIndices().size(); + } +} // Model \ No newline at end of file diff --git a/Renderer/Opengl/Model/Standard/2D/BaseNode2D.h b/Renderer/Opengl/Model/Standard/2D/BaseNode2D.h new file mode 100644 index 00000000..4537601c --- /dev/null +++ b/Renderer/Opengl/Model/Standard/2D/BaseNode2D.h @@ -0,0 +1,62 @@ +#ifndef SNAKE3_BASENODE2D_H +#define SNAKE3_BASENODE2D_H + +#include + +#include "../../../../../Manager/Camera.h" +#include "../../../../../Manager/ShaderManager.h" +#include "../../../Material/BaseMaterial.h" +#include "../../Utils/Mesh2D.h" + +using namespace std; +using namespace ModelUtils; +using namespace Manager; +using namespace Material; + +namespace Model { + class BaseNode2D { + public: + explicit BaseNode2D(const shared_ptr &baseShader); + + virtual ~BaseNode2D() = default; + + void setMaterial(const shared_ptr &material); + + void setColor(const glm::vec3 &color); + + virtual void render(const shared_ptr &camera, const glm::mat4 &ortho, float dt, + const glm::mat4 &parentTransform) const; + + [[nodiscard]] shared_ptr getMesh() const; + + virtual void update(float dt) {}; + + void setBlending(Blending blending); + + void setDepthTest(bool depthTest); + + void setDepthWrite(bool depthWrite); + + [[nodiscard]] Blending getBlending() const; + + [[nodiscard]] bool getDepthTest() const; + + [[nodiscard]] bool getDepthWrite() const; + + void bind() const; + + [[nodiscard]] unsigned long indicesCount() const; + + protected: + shared_ptr mesh; + shared_ptr material; + shared_ptr baseShader; + unsigned int textureId = 0; + glm::vec3 color; + Blending blending = Blending::Opaque; + bool depthTest = true; + bool depthWrite = true; + }; +} // Model + +#endif //SNAKE3_BASENODE2D_H diff --git a/Renderer/Opengl/Model/Standard/2D/GPUParticle2D.cpp b/Renderer/Opengl/Model/Standard/2D/GPUParticle2D.cpp new file mode 100644 index 00000000..b6adc4e3 --- /dev/null +++ b/Renderer/Opengl/Model/Standard/2D/GPUParticle2D.cpp @@ -0,0 +1,153 @@ +#include "GPUParticle2D.h" + +namespace Model { + + GPUParticle2D::GPUParticle2D(const shared_ptr &material, + const shared_ptr &contextState, const shared_ptr &mesh, + const shared_ptr &resourceManager, const int maxParticles) + : MeshNode2D(contextState, mesh, resourceManager), maxParticles(maxParticles), material(material) { + update_shader = resourceManager->getShader("particle_update_2d"); + render_shader = resourceManager->getShader("particle_render_2d"); + render_texture_shader = resourceManager->getShader("particle_render_2d_tex"); + initBuffers(); + } + + GPUParticle2D::~GPUParticle2D() { + glDeleteVertexArrays(2, VAO); + glDeleteBuffers(2, VBO); + } + + void GPUParticle2D::update(const float dt, const uint64_t frameId) { + if (lastUpdatedFrame == frameId) return; + lastUpdatedFrame = frameId; + + update_shader->use(); + + const int src = frameIndex % 2; + const int dst = (frameIndex + 1) % 2; + + update_shader->setFloat("u_dt", dt); + update_shader->setFloat("u_timeAccum", timeAccum); + update_shader->setBool("u_is2D", true); + + if (material) { + material->update(update_shader, maxParticles, timeAccum, timeOffset, dt); + } else { + throw std::invalid_argument("GPUParticle Process Material missing."); + } + + // Transform Feedback + glEnable(GL_RASTERIZER_DISCARD); + glBindVertexArray(VAO[src]); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, VBO[dst]); + + glBeginTransformFeedback(GL_POINTS); + glDrawArrays(GL_POINTS, 0, maxParticles); + glEndTransformFeedback(); + + glDisable(GL_RASTERIZER_DISCARD); + + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0); + glBindVertexArray(0); + + frameIndex++; + timeAccum += dt; + firstFrame = false; + } + + void GPUParticle2D::render(const shared_ptr &camera, const glm::mat4 &ortho, float dt, + const glm::mat4 &parentTransform) const { + contextState->setBlendingMode(mesh->getBlending()); + contextState->setDepthTest(mesh->getDepthTest()); + contextState->setDepthWrite(mesh->getDepthWrite()); + + if (material) { + const auto shader = !material->get_texture().empty() ? render_texture_shader : render_shader; + material->bind(shader); + shader->setFloat("u_aspectRatio", aspectRatio); + resourceManager->getTexture("SceneTexture")->bind(1); + shader->setInt("uSceneTexture", 1); + } else { + throw std::invalid_argument("GPUParticle Process Material missing."); + } + + mesh->bind(); + + const int src = frameIndex % 2; + glBindBuffer(GL_ARRAY_BUFFER, VBO[src]); + + // 1. Position (vec3) -> Location 3 + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(GPUParticle2DStruct), + reinterpret_cast(offsetof(GPUParticle2DStruct, position))); + glVertexAttribDivisor(3, 1); + + // 2. Velocity (vec3) -> Location 4 (pro motion blur nebo orientaci) + glEnableVertexAttribArray(4); + glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(GPUParticle2DStruct), + reinterpret_cast(offsetof(GPUParticle2DStruct, velocity))); + glVertexAttribDivisor(4, 1); + + // 3. Life (float) -> Location 5 + glEnableVertexAttribArray(5); + glVertexAttribPointer(5, 1, GL_FLOAT, GL_FALSE, sizeof(GPUParticle2DStruct), + reinterpret_cast(offsetof(GPUParticle2DStruct, life))); + glVertexAttribDivisor(5, 1); + + // 4. Seed (float) -> Location 6 (pro random variace v render shaderu) + glEnableVertexAttribArray(6); + glVertexAttribPointer(6, 1, GL_FLOAT, GL_FALSE, sizeof(GPUParticle2DStruct), + reinterpret_cast(offsetof(GPUParticle2DStruct, seed))); + glVertexAttribDivisor(6, 1); + + glDrawElementsInstanced(GL_TRIANGLES, + static_cast(mesh->indicesCount()), + GL_UNSIGNED_INT, nullptr, maxParticles); + + // Cleanup + glDisableVertexAttribArray(3); + glDisableVertexAttribArray(4); + glDisableVertexAttribArray(5); + glDisableVertexAttribArray(6); + } + + void GPUParticle2D::initBuffers() { + std::vector initial(maxParticles); + for (int i = 0; i < maxParticles; i++) { + initial[i].position = glm::vec3(-2.0f, -2.0f, 0.0f); + initial[i].velocity = glm::vec3(0.0f); + initial[i].life = 0.0f; + initial[i].seed = static_cast(random()) / static_cast(RAND_MAX); + } + + glGenVertexArrays(2, VAO); + glGenBuffers(2, VBO); + + for (int i = 0; i < 2; i++) { + glBindVertexArray(VAO[i]); + glBindBuffer(GL_ARRAY_BUFFER, VBO[i]); + glBufferData(GL_ARRAY_BUFFER, static_cast(maxParticles * sizeof(GPUParticle2DStruct)), initial.data(), GL_DYNAMIC_COPY); + + // Layout: 0=Pos, 1=Vel, 2=Life, 3=Seed + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,sizeof(GPUParticle2DStruct), + reinterpret_cast(offsetof(GPUParticle2DStruct, position)) + ); + + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(GPUParticle2DStruct), + reinterpret_cast(offsetof(GPUParticle2DStruct, velocity))); + + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, sizeof(GPUParticle2DStruct), + reinterpret_cast(offsetof(GPUParticle2DStruct, life))); + + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, sizeof(GPUParticle2DStruct), + reinterpret_cast(offsetof(GPUParticle2DStruct, seed))); + } + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + +} \ No newline at end of file diff --git a/Renderer/Opengl/Model/Standard/2D/GPUParticle2D.h b/Renderer/Opengl/Model/Standard/2D/GPUParticle2D.h new file mode 100644 index 00000000..fd8058a8 --- /dev/null +++ b/Renderer/Opengl/Model/Standard/2D/GPUParticle2D.h @@ -0,0 +1,56 @@ +#ifndef SNAKE3_GPUPARTICLE2D_H +#define SNAKE3_GPUPARTICLE2D_H + +#include + +#include "../../../../../Manager/ResourceManager.h" +#include "MeshNode2D.h" +#include "../../../Material/Particle/ParticleProcessMaterial.h" + +using namespace std; + +namespace Model { + struct GPUParticle2DStruct { + glm::vec3 position; + glm::vec3 velocity; + float life; + float seed; + }; + + class GPUParticle2D : public MeshNode2D { + public: + explicit GPUParticle2D(const shared_ptr &material, + const shared_ptr &contextState, + const shared_ptr &mesh, const shared_ptr &resourceManager, + int maxParticles); + + ~GPUParticle2D() override; + + void update(float dt, uint64_t frameId) override; + + void render(const shared_ptr &camera, const glm::mat4 &ortho, float dt, + const glm::mat4 &parentTransform) const override; + + void setTimeOffset(const float timeOffset) { this->timeOffset = timeOffset; } + + private: + void initBuffers(); + + GLuint VAO[2]{}; + GLuint VBO[2]{}; + + int frameIndex = 0; + float timeAccum = 0.0f; + bool firstFrame = true; + float timeOffset = 0.0f; + float aspectRatio = 1.6f; + int maxParticles; + + shared_ptr material; + shared_ptr update_shader; + shared_ptr render_shader; + shared_ptr render_texture_shader; + }; +} + +#endif //SNAKE3_GPUPARTICLE2D_H diff --git a/Renderer/Opengl/Model/Standard/2D/LabelNode2D.cpp b/Renderer/Opengl/Model/Standard/2D/LabelNode2D.cpp new file mode 100644 index 00000000..263a7d12 --- /dev/null +++ b/Renderer/Opengl/Model/Standard/2D/LabelNode2D.cpp @@ -0,0 +1,73 @@ +#include "LabelNode2D.h" + +#include +#include "../../../../../Manager/VboIndexer.h" +#include "../../../Material/StandardMaterial.h" +#include "../../Utils/TextMesh.h" + +namespace Model { + LabelNode2D::LabelNode2D(std::string text, + const std::shared_ptr &baseShader, + const std::shared_ptr &settings) + : BaseNode2D(baseShader), settings(settings), text(std::move(text)) { + if (!settings->getFont()) { + std::cerr << "LabelNode2D: Font is null!" << std::endl; + return; + } + + mesh = std::make_shared(); + mesh->update(this->text, settings->getFont()); + textureId = settings->getFont()->getAtlasTextureId(); + blending = Blending::Text; + } + + void LabelNode2D::setText(const string &text) { + this->text = text; + mesh->update(text, settings->getFont()); + align = glm::vec2(3.0f, mesh->getSizeY()); + } + + void LabelNode2D::render(const shared_ptr &camera, const glm::mat4 &ortho, float dt, + const glm::mat4 &parentTransform) const { + + const glm::mat4 model = translate(parentTransform, glm::vec3(align.x, align.y, 0.0f)); + + if (const auto standardMaterial = std::dynamic_pointer_cast(material)) { + standardMaterial.get()->bind( + camera->getPosition(), + camera->getViewMatrix(), + ortho, + model, + false + ); + } else { + baseShader->use(); + baseShader->setMat4("projection", ortho); + baseShader->setMat4("model", model); + baseShader->setVec3("textColor", settings->getColor()); + baseShader->setInt("textTexture", 0); + baseShader->setFloat("alpha", 1.0); + } + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, textureId); + + mesh->bind(); + glDrawArrays(GL_TRIANGLES,0,static_cast(mesh->getVertices().size())/4); + + glBindVertexArray(0); + glBindTexture(GL_TEXTURE_2D,0); + } + + void LabelNode2D::alignVerticalCenter(const float viewportWidth, const float viewportHeight) { + constexpr float left = 0.0f; + const float right = viewportWidth; + constexpr float bottom = 0.0f; + const float top = viewportHeight; + + const float xCenter = (left + right) / 2.0f - mesh->getWidth() * 0.5f; + const float yCenter = (bottom + top) / 2.0f - mesh->getSizeY() * 0.5f; + + align = glm::vec2(xCenter, yCenter); + } +} // namespace Model diff --git a/Renderer/Opengl/Model/Standard/2D/LabelNode2D.h b/Renderer/Opengl/Model/Standard/2D/LabelNode2D.h new file mode 100644 index 00000000..9c6f14f4 --- /dev/null +++ b/Renderer/Opengl/Model/Standard/2D/LabelNode2D.h @@ -0,0 +1,36 @@ +#ifndef SNAKE3_LABELNODE2D_H +#define SNAKE3_LABELNODE2D_H + +#include "BaseNode2D.h" +#include "../../../Material/2D/LabelSettings.h" +#include "../../Utils/TextMesh.h" + +using namespace std; +using namespace Material; + +namespace Model { + class LabelNode2D final : public BaseNode2D { + public: + explicit LabelNode2D(std::string text, const shared_ptr &baseShader, + const shared_ptr &settings); + + ~LabelNode2D() override = default; + + [[nodiscard]] unsigned int getTextureId() const { return textureId; } + + void setText(const string &text); + + void render(const shared_ptr &camera, const glm::mat4 &ortho, float dt, + const glm::mat4 &parentTransform) const override; + + void alignVerticalCenter(float viewportWidth, float viewportHeight); + + protected: + shared_ptr mesh; + const shared_ptr settings; + string text; + glm::vec2 align{}; + }; +} // Model + +#endif //SNAKE3_LABELNODE2D_H diff --git a/Renderer/Opengl/Model/Standard/2D/MeshNode2D.cpp b/Renderer/Opengl/Model/Standard/2D/MeshNode2D.cpp new file mode 100644 index 00000000..147429da --- /dev/null +++ b/Renderer/Opengl/Model/Standard/2D/MeshNode2D.cpp @@ -0,0 +1,67 @@ +#include "MeshNode2D.h" + +#include + +namespace Model { + MeshNode2D::MeshNode2D(const shared_ptr &contextState, const shared_ptr &mesh, + const shared_ptr &resourceManager) + : contextState(contextState), mesh(mesh), resourceManager(resourceManager), transformDetached(false){ + } + + void MeshNode2D::render(const shared_ptr &camera, const glm::mat4 &ortho, const float dt, + const glm::mat4 &parentTransform) const { + if (visible) { + const glm::mat4 finalTransform = parentTransform * this->getModelMatrix(); + contextState->setBlendingMode(mesh->getBlending()); + contextState->setDepthTest(mesh->getDepthTest()); + contextState->setDepthWrite(mesh->getDepthWrite()); + mesh->render(camera, ortho, 1, finalTransform); + + for (const auto &snd: children | views::values) { + snd->render(camera, ortho, dt, transformDetached ? glm::mat4(1.0f) : finalTransform); + } + } else if (transformDetached) { + for (const auto &snd: children | views::values) { + snd->render(camera, ortho, dt, glm::mat4(1.0f)); + } + } + } + + void MeshNode2D::update(const float dt, const uint64_t frameId) { + if (lastUpdatedFrame == frameId) { + return; + } + + lastUpdatedFrame = frameId; + mesh->update(dt); + for (const auto &snd: children | views::values) { + snd->update(dt, frameId); + } + } + + void MeshNode2D::addNode(const std::shared_ptr &node, const std::string &name) { + node->parent = shared_from_this(); + node->depth = this->depth + 1; + if (this->depth > 20) { + throw std::runtime_error("Depth limit reached. Maximum nesting scene nodes is 20"); + } + const auto [fst, snd] = children.emplace(name, node); + if (!snd) { + throw std::runtime_error("Duplicate node item key: " + name); + } + } + + void MeshNode2D::setTransformDetached(const bool transform_detached, const bool recursive) { + transformDetached = transform_detached; + + if (recursive) { + for (const auto &snd: children | views::values) { + snd->setTransformDetached(transform_detached, recursive); + } + } + } + + const map > & MeshNode2D::getChildren() const { + return children; + } +} // Model \ No newline at end of file diff --git a/Renderer/Opengl/Model/Standard/2D/MeshNode2D.h b/Renderer/Opengl/Model/Standard/2D/MeshNode2D.h new file mode 100644 index 00000000..6cf83bdf --- /dev/null +++ b/Renderer/Opengl/Model/Standard/2D/MeshNode2D.h @@ -0,0 +1,45 @@ +#ifndef SNAKE3_MESHNODE2D_H +#define SNAKE3_MESHNODE2D_H + +#include +#include + +#include "BaseNode2D.h" +#include "../../../../../ItemsDto/Visibility.h" +#include "../../../../../Manager/Camera.h" +#include "../../../../../Manager/ResourceManager.h" +#include "../../../../../Tools/ContextState.h" + +using namespace std; +using namespace Manager; + +namespace Model { + class MeshNode2D : public enable_shared_from_this, public Transform, public Visibility, public Vector3i { + public: + explicit MeshNode2D(const shared_ptr &contextState, + const shared_ptr &mesh, const shared_ptr &resourceManager); + + virtual void render(const shared_ptr &camera, const glm::mat4 &ortho, float dt, + const glm::mat4 &parentTransform) const; + + virtual void update(float dt, uint64_t frameId); + + void addNode(const std::shared_ptr &node, const std::string &name); + + void setTransformDetached(bool transform_detached, bool recursive = true); + + [[nodiscard]] const map > &getChildren() const; + + protected: + shared_ptr contextState; + shared_ptr mesh; + weak_ptr parent; + map > children; + shared_ptr resourceManager; + int depth = 0; + bool transformDetached; + uint64_t lastUpdatedFrame = 0; + }; +} // Model + +#endif //SNAKE3_MESHNODE2D_H diff --git a/Renderer/Opengl/Model/Standard/2D/QuadNode2D.cpp b/Renderer/Opengl/Model/Standard/2D/QuadNode2D.cpp new file mode 100644 index 00000000..65e9db56 --- /dev/null +++ b/Renderer/Opengl/Model/Standard/2D/QuadNode2D.cpp @@ -0,0 +1,24 @@ +#include "QuadNode2D.h" + +namespace Model { + QuadNode2D::QuadNode2D(const float width, const float height, const shared_ptr &baseShader) : BaseNode2D(baseShader) { + std::vector vertices(4); + + float halfW = width / 2.0f; + float halfH = height / 2.0f; + + vertices[0].position = {-halfW, -halfH, 0.0f}; + vertices[1].position = { halfW, -halfH, 0.0f}; + vertices[2].position = { halfW, halfH, 0.0f}; + vertices[3].position = {-halfW, halfH, 0.0f}; + + vertices[0].texUV = {0.0f, 0.0f}; + vertices[1].texUV = {1.0f, 0.0f}; + vertices[2].texUV = {1.0f, 1.0f}; + vertices[3].texUV = {0.0f, 1.0f}; + + std::vector indices = {0, 1, 2, 2, 3, 0}; + + mesh = std::make_shared(vertices, indices); + } +} // Model diff --git a/Renderer/Opengl/Model/Standard/2D/QuadNode2D.h b/Renderer/Opengl/Model/Standard/2D/QuadNode2D.h new file mode 100644 index 00000000..946bfb0f --- /dev/null +++ b/Renderer/Opengl/Model/Standard/2D/QuadNode2D.h @@ -0,0 +1,13 @@ +#ifndef SNAKE3_QUADNODE2D_H +#define SNAKE3_QUADNODE2D_H + +#include "BaseNode2D.h" + +namespace Model { + class QuadNode2D : public BaseNode2D { + public: + explicit QuadNode2D(float width, float height, const shared_ptr &baseShader = nullptr); + }; +} // Model + +#endif //SNAKE3_QUADNODE2D_H \ No newline at end of file diff --git a/Renderer/Opengl/Model/Standard/2D/TringleNode2D.cpp b/Renderer/Opengl/Model/Standard/2D/TringleNode2D.cpp new file mode 100644 index 00000000..2cac0352 --- /dev/null +++ b/Renderer/Opengl/Model/Standard/2D/TringleNode2D.cpp @@ -0,0 +1,24 @@ +#include "TringleNode2D.h" + +namespace Model { + TringleNode2D::TringleNode2D(const float width, const float height, const shared_ptr &baseShader) + : BaseNode2D(baseShader) { + std::vector vertices(3); + + float halfW = width / 2.0f; + float halfH = height / 2.0f; + + // Jednoduchý rovnostranný-ish trojúhelník okolo středu + vertices[0].position = { 0.0f, halfH, 0.0f }; // Top + vertices[1].position = { halfW, -halfH, 0.0f }; // Bottom-Right + vertices[2].position = { -halfW,-halfH, 0.0f }; // Bottom-Left + + vertices[0].texUV = {0.5f, 1.0f}; // horní střed tex + vertices[1].texUV = {1.0f, 0.0f}; // pravý spodní + vertices[2].texUV = {0.0f, 0.0f}; // levý spodní + + std::vector indices = {0, 1, 2}; + + mesh = std::make_shared(vertices, indices); + } +} // Model \ No newline at end of file diff --git a/Renderer/Opengl/Model/Standard/2D/TringleNode2D.h b/Renderer/Opengl/Model/Standard/2D/TringleNode2D.h new file mode 100644 index 00000000..baa90d03 --- /dev/null +++ b/Renderer/Opengl/Model/Standard/2D/TringleNode2D.h @@ -0,0 +1,13 @@ +#ifndef SNAKE3_TRINGLENODE2D_H +#define SNAKE3_TRINGLENODE2D_H + +#include "BaseNode2D.h" + +namespace Model { + class TringleNode2D : public BaseNode2D { + public: + explicit TringleNode2D(float width, float height, const shared_ptr &baseShader = nullptr); + }; +} // Model + +#endif //SNAKE3_TRINGLENODE2D_H \ No newline at end of file diff --git a/Renderer/Opengl/Model/Standard/Animation/AnimationPlayer.cpp b/Renderer/Opengl/Model/Standard/Animation/AnimationPlayer.cpp new file mode 100644 index 00000000..ad769d1c --- /dev/null +++ b/Renderer/Opengl/Model/Standard/Animation/AnimationPlayer.cpp @@ -0,0 +1,270 @@ +#include "AnimationPlayer.h" + +#include +#include +#include + +namespace Animations { + AnimationPlayer::AnimationPlayer(const string &name) { + createAnimation(name); + } + + AnimationPlayer::AnimationPlayer(const vector> &meshes, + const map> &animations, const vector> &bones, + const Tree &skeleton, const unordered_map &bones_map, + const glm::mat4 &global_matrix) : + animations(animations), meshes(meshes), bones(bones), bones_map(bones_map), skeleton(skeleton), + global_inverse(global_matrix) { + + std::erase_if( + this->meshes, + [this](const shared_ptr &p) { + if (!p->isHasBones()) { + noBonesMeshes.push_back(p); + return true; + } + return false; + } + ); + + for (const auto &key: animations | views::keys) { + auto meta = new AnimationMeta; + meta->name = key; + meta->animation_duration = std::chrono::seconds(0); + meta->last_time = std::chrono::time_point(); + meta->bone_transform.resize(this->bones.size(), glm::mat4(1.0f)); + meta->pause = true; + meta->alpha = 1.0f; + meta->world_transform = glm::mat4(1.0f); + metadata.emplace(key, meta); + } + } + + void AnimationPlayer::createAnimation(const string &name) { + animations.emplace(name, make_shared(name, 0, 25)); + const auto meta = make_shared(); + meta->name = name; + meta->animation_duration = std::chrono::seconds(0); + meta->last_time = std::chrono::time_point(); + meta->pause = false; + meta->alpha = 1.0f; + meta->world_transform = glm::mat4(1.0f); + metadata.emplace(name, meta); + } + + void AnimationPlayer::addAnimationNode(const string &name, + const shared_ptr &animationNode, const int duration) { + const auto anim = animations.find(name); + if (anim == animations.end()) { + throw std::invalid_argument("Animation not found"); + } + + const auto meta = metadata.at(name); + if (nullptr == meta) { + throw std::invalid_argument("Animation metadata not found"); + } + + anim->second->duration = duration; + anim->second->nodes.emplace_back(animationNode); + } + + void AnimationPlayer::setAcceleration(const float acceleration) { + this->acceleration = acceleration; + } + + void AnimationPlayer::start(const string &name, const bool loop) { + this->setRepeat(loop); + const auto meta = metadata.at(name); + if (!meta) { + throw std::invalid_argument("Animation metadata not found"); + } + meta->pause = false; + meta->alpha = 1.0f; + } + + void AnimationPlayer::stop(const string &name) const { + const auto meta = metadata.at(name); + if (!meta) { + throw std::invalid_argument("Animation metadata not found"); + } + meta->animation_duration = std::chrono::seconds(0); + meta->last_time = std::chrono::time_point(); + meta->pause = true; + meta->alpha = 1.0f; + meta->world_transform = glm::mat4(1.0f); + } + + void AnimationPlayer::pause(const string &name) const { + const auto meta = metadata.at(name); + if (!meta) { + throw std::invalid_argument("Animation metadata not found"); + } + meta->pause = true; + } + + void AnimationPlayer::resume(const string &name) const { + const auto meta = metadata.at(name); + if (!meta) { + throw std::invalid_argument("Animation metadata not found"); + } + meta->pause = false; + } + + shared_ptr AnimationPlayer::play(const string &name) { + const auto anim = animations.find(name); + if (anim == animations.end()) { + throw std::invalid_argument("Animation not found"); + } + const auto meta = metadata.at(name); + if (!meta) { + throw std::invalid_argument("Animation metadata not found"); + } + meta->current_animation = anim->second; + if (!meta->pause) { + const auto current_time = std::chrono::steady_clock::now(); + if (meta->last_time == std::chrono::time_point()) { + meta->last_time = current_time; + } + const auto delta_time = current_time - meta->last_time; + meta->animation_duration += delta_time * this->acceleration; + meta->last_time = current_time; + + auto raw_time = meta->animation_duration.count() * anim->second->tps; + if (raw_time >= anim->second->duration) { + if (repeat) { + raw_time = 0.0f; + meta->animation_duration = std::chrono::seconds(0); + } else { + completed = true; + meta->pause = true; + raw_time = anim->second->duration; + if (completedCallback) { + completedCallback(this); + } + } + } + + const auto animation_time = raw_time; + + if (!meta->bone_transform.empty()) { // mam kosti + this->updateBonesAnimation(anim->second, meta, animation_time); + } else { + const glm::vec3 scale = anim->second->nodes.begin()->get()->scalingLerp(animation_time); + const glm::vec3 position = anim->second->nodes.begin()->get()->positionLerp(animation_time); + const auto rotation = anim->second->nodes.begin()->get()->rotationLerp(animation_time); + const auto alpha = anim->second->nodes.begin()->get()->alphaLerp(animation_time); + + meta->alpha = alpha; + + const auto translate = glm::translate(glm::mat4(1.f), position); + const auto rotate = glm::mat4_cast(rotation); + const auto scale_mat = glm::scale(glm::mat4(1.f), scale); + + meta->world_transform = translate * rotate * scale_mat; + } + } else if (meta) { + meta->last_time = std::chrono::steady_clock::now(); + } + + return meta; + } + + vector> AnimationPlayer::getNoBonesMeshes() const { + return noBonesMeshes; + } + + vector> AnimationPlayer::getMeshes() const { + return meshes; + } + + bool AnimationPlayer::isCompleted() const { + return completed; + } + + void AnimationPlayer::setRepeat(const bool repeat) { + this->repeat = repeat; + } + + void AnimationPlayer::setCompletedCallback(const std::function &callback) { + completedCallback = callback; + } + + shared_ptr AnimationPlayer::getMetadata(const string &name) const { + const auto anim = animations.find(name); + if (anim == animations.end()) { + throw std::invalid_argument("Animation not found"); + } + const auto meta = metadata.at(name); + if (!meta) { + throw std::invalid_argument("Animation metadata not found"); + } + meta->current_animation = anim->second; + + return meta; + } + + void AnimationPlayer::reset(const string &name) { + completed = false; + const auto meta = metadata.at(name); + if (!meta) { + throw std::invalid_argument("Animation metadata not found"); + } + meta->animation_duration = std::chrono::seconds(0); + meta->last_time = std::chrono::time_point(); + meta->pause = false; + meta->alpha = 1.0f; + meta->world_transform = glm::mat4(1.0f); + } + + void AnimationPlayer::updateBonesAnimation( + const shared_ptr &anim, const shared_ptr &meta, const double animation_time) const { + function &, const glm::mat4 &)> node_traversal; + node_traversal = [&](const Tree &node, const glm::mat4 &parent_mat) { + const auto anim_node = findAnimationNode(anim, bones[*node]); + auto local_transform = !bones[*node]->isFake() + ? bones[*node]->node_transform + : glm::mat4(1.f); + + if (anim_node) { + const glm::vec3 scale = anim_node->scalingLerp(animation_time); + const glm::vec3 position = anim_node->positionLerp(animation_time); + const auto rotation = anim_node->rotationLerp(animation_time); + + const auto translate = glm::translate(glm::mat4(1.f), position); + const auto rotate = glm::mat4_cast(rotation); + const auto scale_mat = glm::scale(glm::mat4(1.f), scale); + + local_transform = translate * rotate * scale_mat; + } + + const auto transform = parent_mat * local_transform; + + if (anim_node) { + meta->bone_transform[*node] = parent_mat * local_transform * (bones[*node])->offset_matrix; + } else { + meta->bone_transform[*node] = local_transform; + } + + for (const auto &n: node) { + node_traversal(n, transform); + } + }; + + try { + node_traversal(*skeleton, glm::mat4(1.f)); + } catch (const std::exception &e) { + throw std::runtime_error("Wrong TPS/duration. " + std::string(e.what())); + } + } + + shared_ptr AnimationPlayer::findAnimationNode( + const shared_ptr &animation, const shared_ptr &bone) { + for (const auto &node: animation->nodes) { + if (node->bone == bone) { + return node; + } + } + + return nullptr; + } +} // Animation \ No newline at end of file diff --git a/Renderer/Opengl/Model/Standard/Animation/AnimationPlayer.h b/Renderer/Opengl/Model/Standard/Animation/AnimationPlayer.h new file mode 100644 index 00000000..8d99a8d1 --- /dev/null +++ b/Renderer/Opengl/Model/Standard/Animation/AnimationPlayer.h @@ -0,0 +1,93 @@ +#ifndef SNAKE3_ANIMATIONPLAYER_H +#define SNAKE3_ANIMATIONPLAYER_H + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../../ItemsDto/AnimItem.h" +#include "../../Utils/Mesh.h" +#include "../../Utils/Tree.h" + +using namespace std; +using namespace ItemsDto; +using namespace ModelUtils; + +namespace Animations { + struct AnimationMeta { + std::string name; + chrono::time_point last_time; + chrono::duration animation_duration; + vector bone_transform; + bool pause; + float alpha; + glm::mat4 world_transform; + shared_ptr current_animation{}; + }; + class AnimationPlayer { + public: + AnimationPlayer() = default; + explicit AnimationPlayer(const string &name); + AnimationPlayer(const vector > &meshes, + const map> &animations, + const vector > &bones, const Tree &skeleton, + const unordered_map &bones_map, + const glm::mat4 &global_matrix + ); + + void createAnimation(const string &name); + + void addAnimationNode(const string &name, const shared_ptr &animationNode, int duration); + + void setAcceleration(float acceleration); + + void start(const string &name, bool loop = true); + + void stop(const string &name) const; + + void pause(const string &name) const; + + void resume(const string &name) const; + + void reset(const string &name); + + shared_ptr play(const string &name); + + vector > getNoBonesMeshes() const; + + vector > getMeshes() const; + + bool isCompleted() const; + + void setRepeat(bool repeat); + + void setCompletedCallback(const std::function &callback); + + shared_ptr getMetadata(const string &name) const; + + protected: + void updateBonesAnimation(const shared_ptr &anim, const shared_ptr &meta, double animation_time) const; + static shared_ptr findAnimationNode(const shared_ptr &animation, const shared_ptr &bone); + + private: + map > animations; + unordered_map > metadata; + float acceleration = 1.0f; + bool repeat = false; + bool completed = false; + + std::function completedCallback; + vector > meshes; + vector > bones; + vector > noBonesMeshes; + unordered_map bones_map; + optional > skeleton; + glm::mat4 global_inverse{}; + }; +} // Animation + +#endif //SNAKE3_ANIMATIONPLAYER_H diff --git a/Renderer/Opengl/Model/Standard/AnimationArrayMesh.cpp b/Renderer/Opengl/Model/Standard/AnimationArrayMesh.cpp new file mode 100644 index 00000000..536539ce --- /dev/null +++ b/Renderer/Opengl/Model/Standard/AnimationArrayMesh.cpp @@ -0,0 +1,100 @@ +#include "AnimationArrayMesh.h" + +namespace Model { + AnimationArrayMesh::AnimationArrayMesh(const shared_ptr &model, + const shared_ptr &baseShader, const string &animationName) + : StandardMesh(baseShader), baseShader(baseShader) { + setAnimationPlayer(model); + animation = animationName; + } + + void AnimationArrayMesh::render(const shared_ptr &camera, const glm::mat4 &projection, float dt, + const glm::mat4 &parentTransform, const bool shadows) const { + if (const auto standardMaterial = dynamic_pointer_cast(material)) { + standardMaterial.get()->bind( + camera->getPosition(), + camera->getViewMatrix(), + projection, + parentTransform, + shadows + ); + } else { + baseShader->use(); + baseShader->setMat4("view", camera->getViewMatrix()); + baseShader->setMat4("projection", projection); + baseShader->setVec3("viewPos", camera->getPosition()); + baseShader->setBool("useMaterial", true); + baseShader->setMat4("model", parentTransform); + } + + renderMesh(parentTransform); + + if (const auto standardMaterial = dynamic_pointer_cast(material)) { + standardMaterial.get()->unbind(); + } + } + + void AnimationArrayMesh::renderShadowMap(const shared_ptr &camera, const glm::mat4 &projection, + float dt, const glm::mat4 &parentTransform) const { + const auto standardMaterial = std::dynamic_pointer_cast(material); + if (standardMaterial && standardMaterial->isShadowEnabled()) { + standardMaterial.get()->bindShadow(parentTransform); + + renderMesh(parentTransform * glm::mat4(1.0f), false); + + standardMaterial.get()->unbind(); + } + } + + void AnimationArrayMesh::renderMesh(const glm::mat4 &parentTransform, const bool animPlay) const { + const auto metadata = animPlay ? animationPlayer->play(animation) : animationPlayer->getMetadata(animation); + + for (int i = 0; i < metadata->bone_transform.size(); ++i) { + if (const auto standardMaterial = std::dynamic_pointer_cast(material)) { + standardMaterial->bindBonesMatrices(i, metadata->bone_transform[i]); + } else { + baseShader->setMat4("finalBonesMatrices[" + std::to_string(i) + "]", metadata->bone_transform[i]); + } + } + for (const auto &animMesh: animationPlayer->getMeshes()) { + if (animMesh->getName() == + metadata->current_animation->nodes[0]->bone->meshName) { + glm::mat4 finalTransform = animMesh->isHasBones() ? parentTransform : parentTransform * animMesh->getGlobalTransformation(); + if (const auto standardMaterial = std::dynamic_pointer_cast(material)) { + standardMaterial->bindUseBones(animMesh->isHasBones()); + if (animPlay) { + standardMaterial->bindModel(finalTransform); + } else { + standardMaterial->bindShadowModel(finalTransform); + } + } else { + baseShader->setBool("useBones", animMesh->isHasBones()); + baseShader->setMat4( + "model", finalTransform); + } + + animMesh->bind(); + glDrawElements(GL_TRIANGLES, static_cast(animMesh->getIndices().size()), + GL_UNSIGNED_INT, + nullptr); + } + } + + for (const auto& animMesh: animationPlayer->getNoBonesMeshes()) { + if (const auto standardMaterial = std::dynamic_pointer_cast(material)) { + standardMaterial->bindUseBones(false); + if (animPlay) { + standardMaterial->bindModel(parentTransform * animMesh->getGlobalTransformation()); + } else { + standardMaterial->bindShadowModel(parentTransform * animMesh->getGlobalTransformation()); + } + } else { + baseShader->setBool("useBones", false); + baseShader->setMat4("model", parentTransform * animMesh->getGlobalTransformation()); + } + animMesh->bind(); + glDrawElements(GL_TRIANGLES, static_cast(animMesh->getIndices().size()), GL_UNSIGNED_INT, + nullptr); + } + } +} // Model diff --git a/Renderer/Opengl/Model/Standard/AnimationArrayMesh.h b/Renderer/Opengl/Model/Standard/AnimationArrayMesh.h new file mode 100644 index 00000000..50c90427 --- /dev/null +++ b/Renderer/Opengl/Model/Standard/AnimationArrayMesh.h @@ -0,0 +1,25 @@ +#ifndef SNAKE3_ANIMATIONARRAYMESH_H +#define SNAKE3_ANIMATIONARRAYMESH_H + +#include + +#include "StandardMesh.h" +#include "Animation/AnimationPlayer.h" + +using namespace std; +using namespace Animations; + +namespace Model { + class AnimationArrayMesh final : public StandardMesh { + public: + AnimationArrayMesh(const shared_ptr &model, const shared_ptr& baseShader, const string &animationName); + void render(const shared_ptr &camera, const glm::mat4 &projection, float dt, const glm::mat4 &parentTransform, bool shadows) const override; + void renderShadowMap(const shared_ptr &camera, const glm::mat4 &projection, float dt, const glm::mat4 &parentTransform) const override; + + protected: + void renderMesh(const glm::mat4 &parentTransform, bool animPlay = true) const; + shared_ptr baseShader; + }; +} // Model + +#endif //SNAKE3_ANIMATIONARRAYMESH_H \ No newline at end of file diff --git a/Renderer/Opengl/Model/Standard/ArrayMesh.cpp b/Renderer/Opengl/Model/Standard/ArrayMesh.cpp new file mode 100644 index 00000000..9a8d6e84 --- /dev/null +++ b/Renderer/Opengl/Model/Standard/ArrayMesh.cpp @@ -0,0 +1,40 @@ +#include "ArrayMesh.h" +#include "../../../../Manager/VboIndexer.h" + +namespace Model { + void ArrayMesh::fromMesh(const shared_ptr &mesh) { + this->mesh = mesh; + } + + void ArrayMesh::fromVertexData(vector &vertices) { + std::vector indices; + + indices.reserve(6); + for (GLuint i = 0; i < 6; ++i) { + indices.push_back(i); + } + + std::vector positions, normals; + std::vector uvs; + + for (const auto &v: vertices) { + positions.push_back(v.position); + normals.push_back(v.normal); + uvs.push_back(v.texUV); + localMin = glm::min(localMin, v.position); + localMax = glm::max(localMax, v.position); + } + + std::vector tangents, biTangents; + VboIndexer::computeTangentBasis(positions, uvs, normals, tangents, biTangents); + + for (size_t i = 0; i < vertices.size(); ++i) { + vertices[i].tangents = tangents[i]; + } + for (size_t i = 0; i < vertices.size(); ++i) { + vertices[i].biTangents = biTangents[i]; + } + + mesh = std::make_shared(vertices, indices); + } +} // Model diff --git a/Renderer/Opengl/Model/Standard/ArrayMesh.h b/Renderer/Opengl/Model/Standard/ArrayMesh.h new file mode 100644 index 00000000..c1425d88 --- /dev/null +++ b/Renderer/Opengl/Model/Standard/ArrayMesh.h @@ -0,0 +1,19 @@ +#ifndef SNAKE3_ARRAYMESH_H +#define SNAKE3_ARRAYMESH_H + +#include "StandardMesh.h" + +using namespace std; + +namespace Model { + class ArrayMesh final : public StandardMesh { + public: + explicit ArrayMesh(const shared_ptr &baseShader) : StandardMesh(baseShader) {} + + void fromMesh(const shared_ptr &mesh); + + void fromVertexData(vector &vertices); + }; +} // Model + +#endif //SNAKE3_ARRAYMESH_H diff --git a/Renderer/Opengl/Model/Standard/BoxMesh.cpp b/Renderer/Opengl/Model/Standard/BoxMesh.cpp new file mode 100644 index 00000000..fd95492d --- /dev/null +++ b/Renderer/Opengl/Model/Standard/BoxMesh.cpp @@ -0,0 +1,80 @@ +#include "../../../../Manager/VboIndexer.h" +#include "BoxMesh.h" + +#include + +namespace Model { + BoxMesh::BoxMesh(shared_ptr baseShader, + const float width, const float height, const float depth) + : StandardMesh(std::move(baseShader)), depth(depth) { + float hw = width * 0.5f; + float hh = height * 0.5f; + float hd = depth * 0.5f; + + std::vector vertices; + std::vector indices; + + auto addVertex = [&](glm::vec3 pos, glm::vec3 normal, glm::vec2 uv, glm::vec3 color = {1.0f, 1.0f, 1.0f}) { + Vertex v{}; + v.position = pos; + v.normal = normal; + v.color = color; + v.texUV = uv; + vertices.push_back(v); + }; + + auto addFace = [&](const glm::vec3 v0, const glm::vec3 v1, const glm::vec3 v2, const glm::vec3 v3, + const glm::vec3 normal) { + const auto base = static_cast(vertices.size()); + addVertex(v0, normal, {0.0f, 0.0f}); + addVertex(v1, normal, {1.0f, 0.0f}); + addVertex(v2, normal, {1.0f, 1.0f}); + addVertex(v3, normal, {0.0f, 1.0f}); + + indices.push_back(base + 0); + indices.push_back(base + 1); + indices.push_back(base + 2); + + indices.push_back(base + 2); + indices.push_back(base + 3); + indices.push_back(base + 0); + }; + + // +X + addFace({hw, -hh, -hd}, {hw, -hh, hd}, {hw, hh, hd}, {hw, hh, -hd}, {1, 0, 0}); + // -X + addFace({-hw, -hh, hd}, {-hw, -hh, -hd}, {-hw, hh, -hd}, {-hw, hh, hd}, {-1, 0, 0}); + // +Y + addFace({-hw, hh, -hd}, {hw, hh, -hd}, {hw, hh, hd}, {-hw, hh, hd}, {0, 1, 0}); + // -Y + addFace({-hw, -hh, hd}, {hw, -hh, hd}, {hw, -hh, -hd}, {-hw, -hh, -hd}, {0, -1, 0}); + // +Z + addFace({hw, -hh, hd}, {-hw, -hh, hd}, {-hw, hh, hd}, {hw, hh, hd}, {0, 0, 1}); + // -Z + addFace({-hw, -hh, -hd}, {hw, -hh, -hd}, {hw, hh, -hd}, {-hw, hh, -hd}, {0, 0, -1}); + + std::vector positions, normals; + std::vector uvs; + positions.reserve(vertices.size()); + normals.reserve(vertices.size()); + uvs.reserve(vertices.size()); + + for (const auto &v: vertices) { + positions.push_back(v.position); + normals.push_back(v.normal); + uvs.push_back(v.texUV); + localMin = glm::min(localMin, v.position); + localMax = glm::max(localMax, v.position); + } + + std::vector tangents, biTangents; + VboIndexer::computeTangentBasis(positions, uvs, normals, tangents, biTangents); + + for (size_t i = 0; i < vertices.size(); ++i) { + vertices[i].tangents = tangents[i]; + vertices[i].biTangents = biTangents[i]; + } + + mesh = std::make_shared(vertices, indices); + } +} // Model diff --git a/Renderer/Opengl/Model/Standard/BoxMesh.h b/Renderer/Opengl/Model/Standard/BoxMesh.h new file mode 100644 index 00000000..8bfbfa03 --- /dev/null +++ b/Renderer/Opengl/Model/Standard/BoxMesh.h @@ -0,0 +1,16 @@ +#ifndef BOX_MESH_H +#define BOX_MESH_H + +#include "StandardMesh.h" + +using namespace std; + +namespace Model { + class BoxMesh final : public StandardMesh { + float depth; + public: + explicit BoxMesh(shared_ptr baseShader, float width, float height, float depth); + }; +} // Model + +#endif //BOX_MESH_H diff --git a/Renderer/Opengl/Model/Standard/CapsuleMesh.cpp b/Renderer/Opengl/Model/Standard/CapsuleMesh.cpp new file mode 100644 index 00000000..f8d62a90 --- /dev/null +++ b/Renderer/Opengl/Model/Standard/CapsuleMesh.cpp @@ -0,0 +1,129 @@ +#include "../../../../Manager/VboIndexer.h" +#include "CapsuleMesh.h" + +namespace Model { + CapsuleMesh::CapsuleMesh(shared_ptr baseShader, + const float height, const float radius, const int rings, + const int segments) : StandardMesh(std::move(baseShader)) { + std::vector vertices; + std::vector indices; + + float halfHeight = height * 0.5f; + float cylinderHeight = height - 2.0f * radius; + + auto addVertex = [&](glm::vec3 pos, glm::vec3 normal, glm::vec2 uv) { + Vertex v{}; + v.position = pos; + v.normal = normal; + v.color = {1.0f, 1.0f, 1.0f}; + v.texUV = uv; + vertices.push_back(v); + }; + + // --- 1. Horní polokoule (od vrcholu dolů k rovníku) --- + for (int y = 0; y <= rings; ++y) { + float theta = static_cast(y) / static_cast(rings) * glm::half_pi(); // 0..pi/2 + float cosTheta = cos(theta); + float sinTheta = sin(theta); + // Posun: horní polokoule nahoře + float py = halfHeight - radius + radius * cosTheta; + float vTex = 0.5f + 0.5f * sinTheta; // UV map + + for (int x = 0; x <= segments; ++x) { + float phi = static_cast(x) / static_cast(segments) * glm::two_pi(); + float px = radius * sinTheta * cos(phi); + float pz = radius * sinTheta * sin(phi); + + glm::vec3 pos(px, py, pz); + glm::vec3 normal = glm::normalize(pos - glm::vec3(0.0f, halfHeight - radius, 0.0f)); + addVertex(pos, normal, {static_cast(x) / static_cast(segments), vTex}); + } + } + + // --- 2. Válec --- + for (int y = 0; y <= 1; ++y) { + float py = halfHeight - radius - static_cast(y) * cylinderHeight; // horní = top polokoule rovník + float vTex = 0.5f - 0.5f * static_cast(y); + + for (int x = 0; x <= segments; ++x) { + float phi = float(x) / static_cast(segments) * glm::two_pi(); + float px = radius * cos(phi); + float pz = radius * sin(phi); + glm::vec3 pos(px, py, pz); + glm::vec3 normal = glm::normalize(glm::vec3(px, 0.0f, pz)); + addVertex(pos, normal, {float(x) / static_cast(segments), vTex}); + } + } + + // --- 3. Dolní polokoule (od dolíku nahoru k rovníku) --- + for (int y = 0; y <= rings; ++y) { + float theta = static_cast(y) / static_cast(rings) * glm::half_pi(); // 0..pi/2 + float cosTheta = cos(theta); + float sinTheta = sin(theta); + // Posun: dolní polokoule dole + float py = -halfHeight + radius - radius * cosTheta; + float vTex = 0.5f * sinTheta; // UV map + + for (int x = 0; x <= segments; ++x) { + float phi = static_cast(x) / static_cast(segments) * glm::two_pi(); + float px = radius * sinTheta * cos(phi); + float pz = radius * sinTheta * sin(phi); + + glm::vec3 pos(px, py, pz); + glm::vec3 normal = glm::normalize(pos - glm::vec3(0.0f, -halfHeight + radius, 0.0f)); + addVertex(pos, normal, {static_cast(x) / static_cast(segments), vTex}); + } + } + + // --- Generování indexů --- + auto buildIndices = [&](const int start, const int ringCount) { + for (int y = 0; y < ringCount; ++y) { + for (int x = 0; x < segments; ++x) { + GLuint i0 = start + y * (segments + 1) + x; + GLuint i1 = i0 + 1; + GLuint i2 = i0 + (segments + 1); + GLuint i3 = i2 + 1; + indices.push_back(i0); + indices.push_back(i2); + indices.push_back(i1); + indices.push_back(i1); + indices.push_back(i2); + indices.push_back(i3); + } + } + }; + + // Horní polokoule + buildIndices(0, rings); + // Válec + buildIndices((rings + 1) * (segments + 1), 1); + // Dolní polokoule + int bottomStart = (rings + 1) * (segments + 1) + 2 * (segments + 1); + buildIndices(bottomStart, rings); + + // --- Tangenty a bitangenty --- + std::vector positions, normalsVec; + std::vector uvsVec; + positions.reserve(vertices.size()); + normalsVec.reserve(vertices.size()); + uvsVec.reserve(vertices.size()); + + for (const auto &v: vertices) { + positions.push_back(v.position); + normalsVec.push_back(v.normal); + uvsVec.push_back(v.texUV); + localMin = glm::min(localMin, v.position); + localMax = glm::max(localMax, v.position); + } + + std::vector tangents, biTangents; + VboIndexer::computeTangentBasis(positions, uvsVec, normalsVec, tangents, biTangents); + + for (size_t i = 0; i < vertices.size(); ++i) { + vertices[i].tangents = tangents[i]; + vertices[i].biTangents = biTangents[i]; + } + + mesh = std::make_shared(vertices, indices); + } +} // Model diff --git a/Renderer/Opengl/Model/Standard/CapsuleMesh.h b/Renderer/Opengl/Model/Standard/CapsuleMesh.h new file mode 100644 index 00000000..9e7678da --- /dev/null +++ b/Renderer/Opengl/Model/Standard/CapsuleMesh.h @@ -0,0 +1,14 @@ +#ifndef CAPSULEMESH_H +#define CAPSULEMESH_H + +#include "StandardMesh.h" + +namespace Model { + class CapsuleMesh final : public StandardMesh { + public: + explicit CapsuleMesh(shared_ptr baseShader, + float height = 2.0, float radius = 0.5, int rings = 8, int segments = 64); + }; +} // Model + +#endif //CAPSULEMESH_H diff --git a/Renderer/Opengl/Model/Standard/CylinderMesh.cpp b/Renderer/Opengl/Model/Standard/CylinderMesh.cpp new file mode 100644 index 00000000..2ec5c43f --- /dev/null +++ b/Renderer/Opengl/Model/Standard/CylinderMesh.cpp @@ -0,0 +1,119 @@ +#include "../../../../Manager/VboIndexer.h" +#include "CylinderMesh.h" + +namespace Model { + CylinderMesh::CylinderMesh(std::shared_ptr baseShader, + float topRadius, float bottomRadius, + float height, int rings, int segments) + : StandardMesh(std::move(baseShader)) { + std::vector vertices; + std::vector indices; + + float halfHeight = height * 0.5f; + + auto addVertex = [&](glm::vec3 pos, glm::vec3 normal, glm::vec2 uv) { + Vertex v{}; + v.position = pos; + v.normal = normal; + v.color = {1.0f, 1.0f, 1.0f}; + v.texUV = uv; + vertices.push_back(v); + }; + + // --- 1. Boční plášť --- + for (int y = 0; y <= rings; ++y) { + float t = static_cast(y) / static_cast(rings); + float py = halfHeight - t * height; + float radius = glm::mix(topRadius, bottomRadius, t); + float vTex = 1.0f - t; + + for (int x = 0; x <= segments; ++x) { + float phi = static_cast(x) / static_cast(segments) * glm::two_pi(); + float px = radius * cos(phi); + float pz = radius * sin(phi); + + glm::vec3 pos(px, py, pz); + glm::vec3 normal = glm::normalize(glm::vec3(px, (bottomRadius - topRadius) / height, pz)); + glm::vec2 uv = {static_cast(x) / static_cast(segments), vTex}; + addVertex(pos, normal, uv); + } + } + + // --- 2. Horní a dolní základna --- + auto addDisk = [&](const float y, const float radius, const bool isTop) { + const int startIndex = static_cast(vertices.size()); + const glm::vec3 normal = isTop ? glm::vec3(0, 1, 0) : glm::vec3(0, -1, 0); + const glm::vec3 center(0, y, 0); + addVertex(center, normal, {0.5f, 0.5f}); + + for (int x = 0; x <= segments; ++x) { + const float phi = static_cast(x) / static_cast(segments) * glm::two_pi(); + const float px = radius * cos(phi); + const float pz = radius * sin(phi); + const glm::vec3 pos(px, y, pz); + const glm::vec2 uv = {0.5f + 0.5f * cos(phi), 0.5f + 0.5f * sin(phi)}; + addVertex(pos, normal, uv); + } + + for (int x = 0; x < segments; ++x) { + if (isTop) { + indices.push_back(startIndex); + indices.push_back(startIndex + x + 1); + indices.push_back(startIndex + x + 2); + } else { + indices.push_back(startIndex); + indices.push_back(startIndex + x + 2); + indices.push_back(startIndex + x + 1); + } + } + }; + + // horní disk + addDisk(+halfHeight, topRadius, true); + // dolní disk + addDisk(-halfHeight, bottomRadius, false); + + // --- 3. Indexy pro plášť --- + for (int y = 0; y < rings; ++y) { + for (int x = 0; x < segments; ++x) { + GLuint i0 = y * (segments + 1) + x; + GLuint i1 = i0 + 1; + GLuint i2 = i0 + (segments + 1); + GLuint i3 = i2 + 1; + + indices.push_back(i0); + indices.push_back(i2); + indices.push_back(i1); + + indices.push_back(i1); + indices.push_back(i2); + indices.push_back(i3); + } + } + + // --- Výpočet tangentů / bitangentů --- + std::vector positions, normalsVec; + std::vector uvsVec; + positions.reserve(vertices.size()); + normalsVec.reserve(vertices.size()); + uvsVec.reserve(vertices.size()); + + for (const auto &v: vertices) { + positions.push_back(v.position); + normalsVec.push_back(v.normal); + uvsVec.push_back(v.texUV); + localMin = glm::min(localMin, v.position); + localMax = glm::max(localMax, v.position); + } + + std::vector tangents, biTangents; + VboIndexer::computeTangentBasis(positions, uvsVec, normalsVec, tangents, biTangents); + + for (size_t i = 0; i < vertices.size(); ++i) { + vertices[i].tangents = tangents[i]; + vertices[i].biTangents = biTangents[i]; + } + + mesh = std::make_shared(vertices, indices); + } +} // Model diff --git a/Renderer/Opengl/Model/Standard/CylinderMesh.h b/Renderer/Opengl/Model/Standard/CylinderMesh.h new file mode 100644 index 00000000..c55dd420 --- /dev/null +++ b/Renderer/Opengl/Model/Standard/CylinderMesh.h @@ -0,0 +1,17 @@ +#ifndef CYLINDERMESH_H +#define CYLINDERMESH_H + +#include "StandardMesh.h" + +using namespace std; + +namespace Model { + class CylinderMesh final : public StandardMesh { + public: + CylinderMesh(shared_ptr baseShader, + float topRadius, float bottomRadius, + float height, int rings, int segments); + }; +} // Model + +#endif //CYLINDERMESH_H diff --git a/Renderer/Opengl/Model/Standard/GPUParticle3D.cpp b/Renderer/Opengl/Model/Standard/GPUParticle3D.cpp new file mode 100644 index 00000000..2c2e93e9 --- /dev/null +++ b/Renderer/Opengl/Model/Standard/GPUParticle3D.cpp @@ -0,0 +1,170 @@ +#include "GPUParticle3D.h" + +namespace Model { + GPUParticle3D::GPUParticle3D(const shared_ptr &material, + const shared_ptr &contextState, + const shared_ptr &camera, const shared_ptr &mesh, + const shared_ptr &resourceManager, const int maxParticles) + : MeshNode3D(contextState, mesh, resourceManager), VAO{}, + particleVBO{}, maxParticles(maxParticles), resourceManager(resourceManager), camera(camera), material(material) { + update_shader = resourceManager->getShader("particle_update"); + render_shader = resourceManager->getShader("particle_3d_render"); + render_texture_shader = resourceManager->getShader("particle_3d_render_tex"); + initBuffers(); + } + + GPUParticle3D::~GPUParticle3D() { + glDeleteVertexArrays(2, VAO); + glDeleteBuffers(2, particleVBO); + } + + void GPUParticle3D::update(const float dt, const uint64_t frameId) { + if (lastUpdatedFrame == frameId) { + return; + } + + lastUpdatedFrame = frameId; + auto runTfStep = [&](const float stepDt) { + const int src = frameIndex % 2; + const int dst = (frameIndex + 1) % 2; + + if (material) { + if (material->get_spawn_shape() == 1) { + material->set_emitter_pos(camera->getPosition()); + } + material->update(update_shader, maxParticles, timeAccum, timeOffset, stepDt); + frameIndex++; + } else { + throw std::invalid_argument("GPUParticle Process Material missing."); + } + + glEnable(GL_RASTERIZER_DISCARD); + glBindVertexArray(VAO[src]); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, particleVBO[dst]); + glBeginTransformFeedback(GL_POINTS); + glDrawArrays(GL_POINTS, 0, maxParticles); + glEndTransformFeedback(); + glDisable(GL_RASTERIZER_DISCARD); + }; + + if (firstFrame && material->is_smooth_start()) { + const int substeps = std::max(1, material->get_warmup_substeps()); + const float totalWarmup = std::max(0.0f, material->get_warmup_time()); + const float subDt = (substeps > 0 && totalWarmup > 0.0f) ? (totalWarmup / static_cast(substeps)) : 0.0f; + + for (int i = 0; i < substeps; ++i) { + runTfStep(subDt); + timeAccum += subDt; + } + firstFrame = false; + return; + } + + float usedDt = dt; + if (firstFrame) { + usedDt = std::min(dt, material->get_first_frame_clamp()); + firstFrame = false; + } + + runTfStep(usedDt); + timeAccum += usedDt; + } + + void GPUParticle3D::render(const shared_ptr &camera, const glm::mat4 &projection, float dt, + const glm::mat4 &parentTransform, const bool shadows) { + + const glm::mat4 finalTransform = parentTransform * this->getModelMatrix(); + contextState->setBlendingMode(mesh->getBlending()); + contextState->setDepthTest(mesh->getDepthTest()); + contextState->setDepthWrite(mesh->getDepthWrite()); + + if (material) { + const auto shader = !material->get_texture().empty() ? render_texture_shader : render_shader; + material->bind(shader); + shader->setMat4("view", camera->getViewMatrix()); + shader->setMat4("projection", projection); + shader->setMat4("model", material->get_mode() == Billboard ? glm::mat4(1.0f) : finalTransform); + } else { + throw std::invalid_argument("GPUParticle Process Material missing."); + } + + mesh->bind(); + + const int src = frameIndex % 2; + glBindBuffer(GL_ARRAY_BUFFER, particleVBO[src]); + // iPos @location 4 + glEnableVertexAttribArray(4); + glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(GPUParticle), reinterpret_cast(offsetof(GPUParticle, position))); + glVertexAttribDivisor(4, 1); + // iVel @location 5 + glEnableVertexAttribArray(5); + glVertexAttribPointer(5, 3, GL_FLOAT, GL_FALSE, sizeof(GPUParticle), reinterpret_cast(offsetof(GPUParticle, velocity))); + glVertexAttribDivisor(5, 1); + // iLife @location 6 + glEnableVertexAttribArray(6); + glVertexAttribPointer(6, 1, GL_FLOAT, GL_FALSE, sizeof(GPUParticle), reinterpret_cast(offsetof(GPUParticle, life))); + glVertexAttribDivisor(6, 1); + // iSeed @location 7 + glEnableVertexAttribArray(7); + glVertexAttribPointer(7, 1, GL_FLOAT, GL_FALSE, sizeof(GPUParticle), reinterpret_cast(offsetof(GPUParticle, seed))); + glVertexAttribDivisor(7, 1); + + glDrawElementsInstanced( + GL_TRIANGLES, + static_cast(mesh->indicesCount()), + GL_UNSIGNED_INT, + nullptr, + maxParticles + ); + } + + void GPUParticle3D::initBuffers() { + std::vector initial(maxParticles); + for (int i = 0; i < maxParticles; i++) { + initial[i].position = glm::vec3(0.0f); + initial[i].velocity = glm::vec3(0.0f); + initial[i].seed = static_cast(i) * 17.123f; + + const float maxL = material->get_life_max() > 0 ? material->get_life_max() : 2.0f; + initial[i].life = static_cast(random()) / static_cast(RAND_MAX) * maxL; + } + + // Ping-pong buffer pro TF + glGenVertexArrays(2, VAO); + glGenBuffers(2, particleVBO); + + for (int i = 0; i < 2; i++) { + glBindVertexArray(VAO[i]); + glBindBuffer(GL_ARRAY_BUFFER, particleVBO[i]); + glBufferData(GL_ARRAY_BUFFER, + static_cast(maxParticles * sizeof(GPUParticle)), + initial.data(), + GL_DYNAMIC_COPY); + + glEnableVertexAttribArray(0); // inPos + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, + sizeof(GPUParticle), + reinterpret_cast(offsetof(GPUParticle, position))); + + glEnableVertexAttribArray(1); // inVel + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, + sizeof(GPUParticle), + reinterpret_cast(offsetof(GPUParticle, velocity))); + + glEnableVertexAttribArray(2); // inLife + glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, + sizeof(GPUParticle), + reinterpret_cast(offsetof(GPUParticle, life))); + + glEnableVertexAttribArray(3); // inSeed + glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, + sizeof(GPUParticle), + reinterpret_cast(offsetof(GPUParticle, seed))); + } + + glBindVertexArray(0); + + // Unbind VAO + glBindVertexArray(0); + } +} // Model diff --git a/Renderer/Opengl/Model/Standard/GPUParticle3D.h b/Renderer/Opengl/Model/Standard/GPUParticle3D.h new file mode 100644 index 00000000..fbb40c24 --- /dev/null +++ b/Renderer/Opengl/Model/Standard/GPUParticle3D.h @@ -0,0 +1,63 @@ +#ifndef SNAKE3_GPUPARTICLE3D_H +#define SNAKE3_GPUPARTICLE3D_H + +#include +#include + +#include "MeshNode3D.h" +#include "../../../../Manager/Camera.h" +#include "../../../../Manager/ResourceManager.h" +#include "../../Material/Particle/ParticleProcessMaterial.h" + +using namespace Manager; + +namespace Model { + + struct GPUParticle { + glm::vec3 position; + glm::vec3 velocity; + float life; + float seed; + }; + + class GPUParticle3D : public MeshNode3D { + public: + GPUParticle3D(const shared_ptr &material, + const shared_ptr &contextState, const shared_ptr &camera, + const shared_ptr &mesh, const shared_ptr &resourceManager, int maxParticles); + + ~GPUParticle3D() override; + + void update(float dt, uint64_t frameId) override; + + void render(const shared_ptr &camera, const glm::mat4 &projection, float dt, + const glm::mat4 &parentTransform, bool shadows) override; + + void setTimeOffset(const float timeOffset) { this->timeOffset = timeOffset; } + + private: + void initBuffers(); + + GLuint VAO[2]; + GLuint meshVAO{}; + GLuint particleVBO[2]; + GLuint meshVBO{}; + GLuint particleInstanceVBO{}; + GLuint stateTFVBO[2]{}; + + int maxParticles; + int frameIndex = 0; + bool firstFrame = true; + float timeAccum = 0.0f; + float timeOffset = 0.0f; + + shared_ptr resourceManager; + shared_ptr camera; + shared_ptr material; + shared_ptr update_shader; + shared_ptr render_shader; + shared_ptr render_texture_shader; + }; +} // Model + +#endif //SNAKE3_GPUPARTICLE3D_H diff --git a/Renderer/Opengl/Model/Standard/MeshNode3D.cpp b/Renderer/Opengl/Model/Standard/MeshNode3D.cpp new file mode 100644 index 00000000..e342949e --- /dev/null +++ b/Renderer/Opengl/Model/Standard/MeshNode3D.cpp @@ -0,0 +1,179 @@ +#include "MeshNode3D.h" + +#include "../Collision/CollisionShape3D.h" + +namespace Model { + MeshNode3D::MeshNode3D(const shared_ptr &contextState, const shared_ptr &mesh, + const shared_ptr &resourceManager) + : contextState(contextState), mesh(mesh), resourceManager(resourceManager), transformDetached(false), + childrenChangedSignal(false), lastUpdatedFrame(0) { + } + + shared_ptr MeshNode3D::getMesh() const { + return mesh; + } + + void MeshNode3D::addNode(const std::shared_ptr &node) { + node->parent = shared_from_this(); + node->depth = this->depth + 1; + if (this->depth > 20) { + throw std::runtime_error("Depth limit reached. Maximum nesting scene nodes is 20"); + } + children.push_back(node); + childrenChangedSignal = true; + + if (const auto collisionShape = std::dynamic_pointer_cast(node)) { + collisionShapes.push_back(node); + } + } + + void MeshNode3D::render(const shared_ptr &camera, const glm::mat4 &projection, const float dt, + const glm::mat4 &parentTransform, const bool shadows) { + if (visible) { + const glm::mat4 finalTransform = parentTransform * this->getModelMatrix(); + contextState->setBlendingMode(mesh->getBlending()); + contextState->setDepthTest(mesh->getDepthTest()); + contextState->setDepthWrite(mesh->getDepthWrite()); + if (mesh != nullptr) { + mesh->render(camera, projection, 1, finalTransform, shadows); + } + + for (auto &node: children) { + node->render(camera, projection, dt, transformDetached ? glm::mat4(1.0f) : finalTransform, shadows); + } + } else if (transformDetached) { + for (auto &node: children) { + node->render(camera, projection, dt, glm::mat4(1.0f), shadows); + } + } + } + + void MeshNode3D::update(const float dt, const uint64_t frameId) { + if (lastUpdatedFrame == frameId) { + return; + } + + lastUpdatedFrame = frameId; + + if (mesh != nullptr) { + mesh->update(dt); + } + for (const auto &node: children) { + node->update(dt, frameId); + } + if (childrenChangedSignalCycles > 1) { + childrenChangedSignal = false; + childrenChangedSignalCycles = 0; + } else if (childrenChangedSignal) { + childrenChangedSignalCycles++; + } + } + + void MeshNode3D::renderShadows(const shared_ptr &camera, const glm::mat4 &projection, const float dt, + const glm::mat4 &parentTransform) const { + if (visible) { + const glm::mat4 finalTransform = parentTransform * this->getModelMatrix(); + if (mesh != nullptr) { + mesh->renderShadowMap(camera, projection, dt, finalTransform); + } + for (const auto &node: children) { + node->renderShadows(camera, projection, dt, transformDetached ? glm::mat4(1.0f) : finalTransform); + } + } else if (transformDetached) { + for (auto &node: children) { + node->renderShadows(camera, projection, dt, glm::mat4(1.0f)); + } + } + } + + const vector> &MeshNode3D::getChildren() const { + return children; + } + + const vector> & MeshNode3D::getCollisionShapes() const { + return collisionShapes; + } + + void MeshNode3D::setDirectionalLight(const shared_ptr &directional_light) { + directionalLight = directional_light; + } + + void MeshNode3D::setSpotLights(const vector> &spot_light) { + spotLights = spot_light; + } + + void MeshNode3D::setPointLights(const vector> &point_light) { + pointLights = point_light; + } + + void MeshNode3D::setTransformDetached(const bool transform_detached, const bool recursive) { + transformDetached = transform_detached; + + if (recursive) { + for (const auto &child: children) { + child->setTransformDetached(transform_detached, recursive); + } + } + } + + void MeshNode3D::animationStart(const string &name, const bool loop) { + try { + animation = name; + mesh->animationPlay(name, loop); + } catch (exception &e) { + cout << "Mesh is not AnimationArrayMesh instance." << endl; + } + } + + void MeshNode3D::animationStop(const string &name) const { + try { + mesh->animationStop(name); + } catch (exception &e) { + cout << "Mesh is not AnimationArrayMesh instance." << endl; + } + } + + void MeshNode3D::animationPause(const string &name) const { + mesh->animationPause(name); + } + + void MeshNode3D::animationResume(const string &name) const { + mesh->animationResume(name); + } + + void MeshNode3D::disablePlanarReflection() { + includePlanarReflection = false; + } + + bool MeshNode3D::isIncludeInPlanarReflection() const { + return includePlanarReflection; + } + + std::shared_ptr MeshNode3D::deepCopy() const { + auto copyMesh = std::make_shared(*mesh); + auto copyNode = std::make_shared(contextState, copyMesh, resourceManager); + + copyNode->setPosition(this->getPosition()); + copyNode->setScale(this->getScale()); + copyNode->setRotationX(this->rotationX); + copyNode->setRotationY(this->rotationY); + copyNode->setRotationZ(this->rotationZ); + + for (auto &child: this->children) { + auto childCopy = child->deepCopy(); + copyNode->addNode(childCopy); + } + + return copyNode; + } + + void MeshNode3D::make_unique() { + const auto copy = this->deepCopy(); + *this = *copy; + } + + bool MeshNode3D::hasChildrenChangedSignal() const { + return childrenChangedSignal; + } + +} // Model diff --git a/Renderer/Opengl/Model/Standard/MeshNode3D.h b/Renderer/Opengl/Model/Standard/MeshNode3D.h new file mode 100644 index 00000000..e5950769 --- /dev/null +++ b/Renderer/Opengl/Model/Standard/MeshNode3D.h @@ -0,0 +1,82 @@ +#ifndef SNAKE3_NODE3D_H +#define SNAKE3_NODE3D_H + +#include +#include "StandardMesh.h" +#include "../../../../ItemsDto/Visibility.h" +#include "../../../../Manager/ResourceManager.h" +#include "../../../../Tools/ContextState.h" + +using namespace Tools; + +namespace Model { + class MeshNode3D : public enable_shared_from_this, public Transform, public Visibility, public Vector3i { + public: + explicit MeshNode3D(const shared_ptr &contextState, + const shared_ptr &mesh, const shared_ptr &resourceManager); + + ~MeshNode3D() override = default; + + [[nodiscard]] shared_ptr getMesh() const; + + void addNode(const std::shared_ptr &node); + + virtual void render(const shared_ptr &camera, const glm::mat4 &projection, float dt, + const glm::mat4 &parentTransform, bool shadows); + + virtual void update(float dt, uint64_t frameId); + + virtual void renderShadows(const shared_ptr &camera, const glm::mat4 &projection, float dt, + const glm::mat4 &parentTransform) const; + + [[nodiscard]] const vector > &getChildren() const; + + [[nodiscard]] const vector > &getCollisionShapes() const; + + virtual void setDirectionalLight(const shared_ptr &directional_light); + + virtual void setSpotLights(const vector > &spot_light); + + virtual void setPointLights(const vector > &point_light); + + void setTransformDetached(bool transform_detached, bool recursive = true); + + void make_unique(); + + bool hasChildrenChangedSignal() const; + + void animationStart(const string &name, bool loop = true); + + void animationStop(const string &name) const; + + void animationPause(const string &name) const; + + void animationResume(const string &name) const; + + void disablePlanarReflection(); + + [[nodiscard]] bool isIncludeInPlanarReflection() const; + + protected: + shared_ptr deepCopy() const; + + shared_ptr contextState; + shared_ptr mesh; + weak_ptr parent; + vector > children; + vector > collisionShapes; + shared_ptr resourceManager; + shared_ptr directionalLight; + vector > spotLights; + vector > pointLights; + int depth = 0; + bool transformDetached; + bool childrenChangedSignal; + bool includePlanarReflection = true; + int childrenChangedSignalCycles = 0; + string animation; + uint64_t lastUpdatedFrame; + }; +} // Model + +#endif //SNAKE3_NODE3D_H diff --git a/Renderer/Opengl/Model/Standard/PlaneMesh.cpp b/Renderer/Opengl/Model/Standard/PlaneMesh.cpp new file mode 100644 index 00000000..552348a8 --- /dev/null +++ b/Renderer/Opengl/Model/Standard/PlaneMesh.cpp @@ -0,0 +1,61 @@ +#include "../../../../Manager/VboIndexer.h" +#include "PlaneMesh.h" + +#include + + +namespace Model { + PlaneMesh::PlaneMesh(shared_ptr baseShader, const float width, + const float height) : StandardMesh(std::move(baseShader)) { + float hw = width * 0.5f; + float hd = height * 0.5f; + + std::vector vertices; + std::vector indices; + + auto addVertex = [&](glm::vec3 pos, glm::vec2 uv, glm::vec3 color = {1.0f, 1.0f, 1.0f}) { + Vertex v{}; + v.position = pos; + v.normal = {0.0f, 1.0f, 0.0f}; + v.color = color; + v.texUV = uv; + vertices.push_back(v); + }; + + addVertex({-hw, 0.0f, -hd}, {0.0f, 0.0f}); // 0 + addVertex({hw, 0.0f, -hd}, {1.0f, 0.0f}); // 1 + addVertex({hw, 0.0f, hd}, {1.0f, 1.0f}); // 2 + + addVertex({-hw, 0.0f, -hd}, {0.0f, 0.0f}); // 3 + addVertex({hw, 0.0f, hd}, {1.0f, 1.0f}); // 4 + addVertex({-hw, 0.0f, hd}, {0.0f, 1.0f}); // 5 + + indices.reserve(6); + for (GLuint i = 0; i < 6; ++i) { + indices.push_back(i); + } + + std::vector positions, normals; + std::vector uvs; + + for (const auto &v: vertices) { + positions.push_back(v.position); + normals.push_back(v.normal); + uvs.push_back(v.texUV); + localMin = glm::min(localMin, v.position); + localMax = glm::max(localMax, v.position); + } + + std::vector tangents, biTangents; + VboIndexer::computeTangentBasis(positions, uvs, normals, tangents, biTangents); + + for (size_t i = 0; i < vertices.size(); ++i) { + vertices[i].tangents = tangents[i]; + } + for (size_t i = 0; i < vertices.size(); ++i) { + vertices[i].biTangents = biTangents[i]; + } + + mesh = std::make_shared(vertices, indices, false, "PlaneMesh"); + } +} // Model diff --git a/Renderer/Opengl/Model/Standard/PlaneMesh.h b/Renderer/Opengl/Model/Standard/PlaneMesh.h new file mode 100644 index 00000000..39e7ca43 --- /dev/null +++ b/Renderer/Opengl/Model/Standard/PlaneMesh.h @@ -0,0 +1,17 @@ +#ifndef PLANEMESH_H +#define PLANEMESH_H + +#include "StandardMesh.h" + +using namespace Manager; +using namespace ModelUtils; +using namespace std; + +namespace Model { + class PlaneMesh final : public StandardMesh { + public: + explicit PlaneMesh(shared_ptr baseShader, float width, float height); + }; +} // Model + +#endif //PLANEMESH_H diff --git a/Renderer/Opengl/Model/Standard/QuadMesh3D.cpp b/Renderer/Opengl/Model/Standard/QuadMesh3D.cpp new file mode 100644 index 00000000..cce82596 --- /dev/null +++ b/Renderer/Opengl/Model/Standard/QuadMesh3D.cpp @@ -0,0 +1,28 @@ +#include "QuadMesh3D.h" + +namespace Model { + QuadMesh3D::QuadMesh3D(shared_ptr baseShader, const float width, const float height) + : StandardMesh(std::move(baseShader)) { + std::vector vertices(4); + + float halfW = width / 2.0f; + float halfH = height / 2.0f; + + vertices[0].position = { -halfW, 0.0f, halfH }; // Top-Left + vertices[1].position = { halfW, 0.0f, halfH }; // Top-Right + vertices[2].position = { halfW, 0.0f, -halfH }; // Bottom-Right + vertices[3].position = { -halfW, 0.0f, -halfH }; // Bottom-Left + + vertices[0].texUV = { 0.0f, 1.0f }; // Top-Left + vertices[1].texUV = { 1.0f, 1.0f }; // Top-Right + vertices[2].texUV = { 1.0f, 0.0f }; // Bottom-Right + vertices[3].texUV = { 0.0f, 0.0f }; // Bottom-Left + + std::vector indices = { + 0, 1, 2, + 2, 3, 0 + }; + + mesh = std::make_shared(vertices, indices); + } +} // Model diff --git a/Renderer/Opengl/Model/Standard/QuadMesh3D.h b/Renderer/Opengl/Model/Standard/QuadMesh3D.h new file mode 100644 index 00000000..b54165d1 --- /dev/null +++ b/Renderer/Opengl/Model/Standard/QuadMesh3D.h @@ -0,0 +1,13 @@ +#ifndef SNAKE3_QUADMESH3D_H +#define SNAKE3_QUADMESH3D_H + +#include "StandardMesh.h" + +namespace Model { + class QuadMesh3D : public StandardMesh { + public: + explicit QuadMesh3D(shared_ptr baseShader, float width, float height); + }; +} // Model + +#endif //SNAKE3_QUADMESH3D_H \ No newline at end of file diff --git a/Renderer/Opengl/Model/Standard/SkyboxNode3D.cpp b/Renderer/Opengl/Model/Standard/SkyboxNode3D.cpp new file mode 100644 index 00000000..10eed5fa --- /dev/null +++ b/Renderer/Opengl/Model/Standard/SkyboxNode3D.cpp @@ -0,0 +1,43 @@ +#include "SkyboxNode3D.h" + +#include "BoxMesh.h" +#include "../../Material/ShaderMaterial.h" +#include "../../Material/Uniform/CallbackUniform.h" +#include "../../Material/Uniform/TextureUniform.h" + +namespace Model { + SkyboxNode3D::SkyboxNode3D(const shared_ptr &contextState, + const shared_ptr &resourceManager, const shared_ptr &camera) + : MeshNode3D( + contextState, + make_shared(resourceManager->getShader("skyboxShader"), 2.0f, 2.0f, 2.0f), + nullptr + ) { + + const auto skyboxMaterial = make_shared(resourceManager->getShader("skyboxShader")); + const auto textureUniform = make_shared(0, resourceManager->getTexture("skybox"), true); + CallbackUniform::CallbackType viewFunc = [camera](const string &name, const shared_ptr &shader) { + const auto view = glm::mat4(glm::mat3(camera->getViewMatrix())); + shader->setMat4(name, view); + }; + const auto viewUniform = make_shared(viewFunc); + skyboxMaterial->addUniform("view", viewUniform); + skyboxMaterial->addUniform("skybox", textureUniform); + const auto model = glm::rotate(glm::mat4(1.0f), glm::radians(-90.0f), {1.0, 0.0, 0.0}); + skyboxMaterial->addUniform("model", model); + mesh->setMaterial(skyboxMaterial); + disablePlanarReflection(); + } + + void SkyboxNode3D::render(const shared_ptr &camera, const glm::mat4 &projection, float dt, + const glm::mat4 &parentTransform, const bool shadows) { + if (visible) { + contextState->setBlendingMode(mesh->getBlending()); + contextState->setDepthTest(mesh->getDepthTest()); + contextState->setDepthWrite(mesh->getDepthWrite()); + contextState->setDepthFunc(DepthFunc::Lequal); + mesh->render(camera, projection, 1, glm::mat4(1.0f), false); + contextState->setDepthFunc(DepthFunc::Less); + } + } +} // Model \ No newline at end of file diff --git a/Renderer/Opengl/Model/Standard/SkyboxNode3D.h b/Renderer/Opengl/Model/Standard/SkyboxNode3D.h new file mode 100644 index 00000000..c50b0eec --- /dev/null +++ b/Renderer/Opengl/Model/Standard/SkyboxNode3D.h @@ -0,0 +1,24 @@ +#ifndef SNAKE3_SKYBOXNODE3D_H +#define SNAKE3_SKYBOXNODE3D_H + +#include "MeshNode3D.h" +#include "../../Material/Uniform/FadeInUniform.h" + +using namespace Uniform; + +namespace Model { + class SkyboxNode3D : public MeshNode3D { + public: + explicit SkyboxNode3D(const shared_ptr &contextState, + const shared_ptr &resourceManager, const shared_ptr &camera); + + void render(const shared_ptr &camera, const glm::mat4 &projection, float dt, + const glm::mat4 &parentTransform, bool shadows) override; + + void renderShadows(const shared_ptr &camera, const glm::mat4 &projection, float dt, + const glm::mat4 &parentTransform) const override { + } // no shadows - skybox + }; +} // Model + +#endif //SNAKE3_SKYBOXNODE3D_H diff --git a/Renderer/Opengl/Model/Standard/SphereMesh.cpp b/Renderer/Opengl/Model/Standard/SphereMesh.cpp new file mode 100644 index 00000000..474b754e --- /dev/null +++ b/Renderer/Opengl/Model/Standard/SphereMesh.cpp @@ -0,0 +1,89 @@ +#include "../../../../Manager/VboIndexer.h" +#include "SphereMesh.h" + +#include + +namespace Model { + SphereMesh::SphereMesh(shared_ptr baseShader, float height, + float radius, int rings, int segments) : StandardMesh(std::move(baseShader)) { + float halfHeight = height * 0.5f; + std::vector vertices; + std::vector indices; + + auto addVertex = [&](const glm::vec3 pos, const glm::vec3 normal, const glm::vec2 uv, const glm::vec3 color = {1.0f, 1.0f, 1.0f}) { + Vertex v{}; + v.position = pos; + v.normal = normal; + v.color = color; + v.texUV = uv; + vertices.push_back(v); + }; + + for (int y = 0; y <= rings; ++y) { + float v = static_cast(y) / static_cast(rings); // 0..1 + float theta = v * glm::pi(); // 0..pi + + float sinTheta = sin(theta); + float cosTheta = cos(theta); + + for (int x = 0; x <= segments; ++x) { + float u = static_cast(x) / static_cast(segments); // 0..1 + float phi = u * glm::two_pi(); // 0..2pi + + float sinPhi = sin(phi); + float cosPhi = cos(phi); + + auto pos = glm::vec3( + radius * sinTheta * cosPhi, // X + halfHeight * cosTheta, // Y + radius * sinTheta * sinPhi // Z + ); + + glm::vec3 normal = glm::normalize(glm::vec3(pos.x / radius, pos.y / halfHeight, pos.z / radius)); + + addVertex(pos, normal, {u, v}); + } + } + + for (int y = 0; y < rings; ++y) { + for (int x = 0; x < segments; ++x) { + GLuint i0 = y * (segments + 1) + x; + GLuint i1 = i0 + 1; + GLuint i2 = i0 + (segments + 1); + GLuint i3 = i2 + 1; + + indices.push_back(i0); + indices.push_back(i2); + indices.push_back(i1); + + indices.push_back(i1); + indices.push_back(i2); + indices.push_back(i3); + } + } + + std::vector positions, normalsVec; + std::vector uvsVec; + positions.reserve(vertices.size()); + normalsVec.reserve(vertices.size()); + uvsVec.reserve(vertices.size()); + + for (const auto &v: vertices) { + positions.push_back(v.position); + normalsVec.push_back(v.normal); + uvsVec.push_back(v.texUV); + localMin = glm::min(localMin, v.position); + localMax = glm::max(localMax, v.position); + } + + std::vector tangents, biTangents; + VboIndexer::computeTangentBasis(positions, uvsVec, normalsVec, tangents, biTangents); + + for (size_t i = 0; i < vertices.size(); ++i) { + vertices[i].tangents = tangents[i]; + vertices[i].biTangents = biTangents[i]; + } + + mesh = std::make_shared(vertices, indices); + } +} // Model diff --git a/Renderer/Opengl/Model/Standard/SphereMesh.h b/Renderer/Opengl/Model/Standard/SphereMesh.h new file mode 100644 index 00000000..a4ee5fe6 --- /dev/null +++ b/Renderer/Opengl/Model/Standard/SphereMesh.h @@ -0,0 +1,19 @@ +#ifndef SNAKE3_SPHEREMESH_H +#define SNAKE3_SPHEREMESH_H + +#include "StandardMesh.h" + +using namespace Manager; +using namespace ModelUtils; +using namespace std; + + +namespace Model { + class SphereMesh final : public StandardMesh { + public: + explicit SphereMesh(shared_ptr baseShader, float height = 1.0, + float radius = 0.5, int rings = 32, int segments = 64); + }; +} // Model + +#endif //SNAKE3_SPHEREMESH_H \ No newline at end of file diff --git a/Renderer/Opengl/Model/Standard/StandardMesh.cpp b/Renderer/Opengl/Model/Standard/StandardMesh.cpp new file mode 100644 index 00000000..3e73c372 --- /dev/null +++ b/Renderer/Opengl/Model/Standard/StandardMesh.cpp @@ -0,0 +1,206 @@ +#include "StandardMesh.h" +#include + +#include "../../../../Tools/ContextState.h" + +namespace Model { + StandardMesh::StandardMesh(shared_ptr baseShader) + : baseShader(std::move(baseShader)), localMin(+FLT_MAX), localMax(-FLT_MIN) { + } + + shared_ptr StandardMesh::getMesh() const { + return mesh; + } + + void StandardMesh::setMaterial(const shared_ptr &material) { + this->material = material; + } + + shared_ptr StandardMesh::getMaterial() const { + return material; + } + + void StandardMesh::bind() const { + mesh->bind(); + } + + unsigned long StandardMesh::indicesCount() const { + return mesh->getIndices().size(); + } + + void StandardMesh::render(const shared_ptr &camera, const glm::mat4 &projection, float dt, + const glm::mat4 &parentTransform, const bool shadows) const { + + glm::mat4 worldTransform = parentTransform; + float alpha = 1.0f; + + if (nullptr != animationPlayer) { + const auto metadata = animationPlayer->play(animation); + worldTransform = worldTransform * metadata->world_transform; + alpha = metadata->alpha; + } + + if (const auto standardMaterial = std::dynamic_pointer_cast(material)) { + if (nullptr != animationPlayer) { + standardMaterial->setAlpha(alpha); + } + standardMaterial->bind( + camera->getPosition(), + camera->getViewMatrix(), + projection, + worldTransform, + shadows + ); + } else { + baseShader->use(); + baseShader->setMat4("view", camera->getViewMatrix()); + baseShader->setMat4("projection", projection); + baseShader->setMat4("model", worldTransform); + baseShader->setVec3("viewPos", camera->getPosition()); + baseShader->setBool("useMaterial", true); + baseShader->setBool("useBones", false); + baseShader->setBool("shadowsEnable", false); + baseShader->setBool("iblEnabled", false); + baseShader->setBool("pbrEnabled", false); + baseShader->setBool("overrideColorMesh", false); + baseShader->setFloat("ambientLightColorIntensity", 0.05); + baseShader->setBool("fogEnable", false); + baseShader->setInt("numPointLights", 0); + baseShader->setInt("numSpotLights", 0); + baseShader->setBool("directionLightEnable", false); + baseShader->setFloat("alpha", alpha); + } + + mesh->bind(); + glDrawElements(static_cast(drawElement), static_cast(mesh->getIndices().size()), GL_UNSIGNED_INT, + nullptr); + if (const auto standardMaterial = std::dynamic_pointer_cast(material)) { + standardMaterial.get()->unbind(); + } + } + + void StandardMesh::renderShadowMap(const shared_ptr &camera, const glm::mat4 &projection, float dt, + const glm::mat4 &parentTransform) const { + const auto standardMaterial = std::dynamic_pointer_cast(material); + if (standardMaterial) { + standardMaterial.get()->bindShadow(parentTransform); + + mesh->bind(); + glDrawElements(GL_TRIANGLES, static_cast(mesh->getIndices().size()), GL_UNSIGNED_INT, + nullptr); + + standardMaterial.get()->unbind(); + } + } + + glm::vec3 StandardMesh::getMin(const glm::mat4 &worldMatrix) const { + const glm::vec3 corners[8] = { + {localMin.x, localMin.y, localMin.z}, + {localMin.x, localMin.y, localMax.z}, + {localMin.x, localMax.y, localMin.z}, + {localMin.x, localMax.y, localMax.z}, + {localMax.x, localMin.y, localMin.z}, + {localMax.x, localMin.y, localMax.z}, + {localMax.x, localMax.y, localMin.z}, + {localMax.x, localMax.y, localMax.z} + }; + + glm::vec3 worldMin(+FLT_MAX); + for (auto corner: corners) { + glm::vec4 worldPos = worldMatrix * glm::vec4(corner, 1.0f); + worldMin = glm::min(worldMin, glm::vec3(worldPos)); + } + return worldMin; + } + + glm::vec3 StandardMesh::getMax(const glm::mat4 &worldMatrix) const { + glm::vec3 corners[8] = { + {localMin.x, localMin.y, localMin.z}, + {localMin.x, localMin.y, localMax.z}, + {localMin.x, localMax.y, localMin.z}, + {localMin.x, localMax.y, localMax.z}, + {localMax.x, localMin.y, localMin.z}, + {localMax.x, localMin.y, localMax.z}, + {localMax.x, localMax.y, localMin.z}, + {localMax.x, localMax.y, localMax.z} + }; + + glm::vec3 worldMax(-FLT_MAX); + for (auto corner: corners) { + glm::vec4 worldPos = worldMatrix * glm::vec4(corner, 1.0f); + worldMax = glm::max(worldMax, glm::vec3(worldPos)); + } + return worldMax; + } + + shared_ptr StandardMesh::deepCopy() const { + auto newMesh = std::make_shared(baseShader); + + if (material) { + // newMesh->setMaterial(material->clone()); // TODO: not implemented + } + + return newMesh; + } + + void StandardMesh::setAnimationPlayer(const shared_ptr &animationPlayer) { + this->animationPlayer = animationPlayer; + } + + const shared_ptr & StandardMesh::getAnimationPlayer() const { + return animationPlayer; + } + + void StandardMesh::setBlending(const Blending blending) { + this->blending = blending; + } + + void StandardMesh::setDepthTest(const bool depthTest) { + this->depthTest = depthTest; + } + + bool StandardMesh::getDepthTest() const { + return depthTest; + } + + void StandardMesh::setDepthWrite(const bool depthWrite) { + this->depthWrite = depthWrite; + } + + bool StandardMesh::getDepthWrite() const { + return depthWrite; + } + + Blending StandardMesh::getBlending() const { + if (material) { + return material->getBlending(); + } + + return blending; + } + + void StandardMesh::animationPlay(const string &name, bool loop) { + if (animationPlayer) { + animationPlayer->start(name, loop); + animation = name; + } + } + + void StandardMesh::animationStop(const string &name) const { + if (animationPlayer) { + animationPlayer->stop(name); + } + } + + void StandardMesh::animationPause(const string &name) const { + if (animationPlayer) { + animationPlayer->pause(name); + } + } + + void StandardMesh::animationResume(const string &name) const { + if (animationPlayer) { + animationPlayer->resume(name); + } + } +} // Model diff --git a/Renderer/Opengl/Model/Standard/StandardMesh.h b/Renderer/Opengl/Model/Standard/StandardMesh.h new file mode 100644 index 00000000..44a94ff1 --- /dev/null +++ b/Renderer/Opengl/Model/Standard/StandardMesh.h @@ -0,0 +1,89 @@ +#ifndef SNAKE3_STANDARDMESH_H +#define SNAKE3_STANDARDMESH_H + +#include +#include "../Utils/Mesh.h" +#include "../../Material/StandardMaterial.h" +#include "../../../../Manager/ShaderManager.h" +#include "../../../../Tools/DrawElement.h" +#include "../../../../Manager/Camera.h" +#include "Animation/AnimationPlayer.h" + +using namespace ModelUtils; +using namespace Material; +using namespace Animations; +using namespace Tools; +using namespace std; + +namespace Model { + class StandardMesh { + public: + virtual ~StandardMesh() = default; + + explicit StandardMesh(shared_ptr baseShader); + + [[nodiscard]] shared_ptr getMesh() const; + + void setMaterial(const shared_ptr &material); + + [[nodiscard]] shared_ptr getMaterial() const; + + void bind() const; + + [[nodiscard]] unsigned long indicesCount() const; + + virtual void render(const shared_ptr &camera, const glm::mat4 &projection, float dt, + const glm::mat4 &parentTransform, bool shadows) const; + + virtual void update(float dt) { + }; + + virtual void renderShadowMap(const shared_ptr &camera, const glm::mat4 &projection, float dt, + const glm::mat4 &parentTransform) const; + + [[nodiscard]] glm::vec3 getMin(const glm::mat4 &worldMatrix) const; + + [[nodiscard]] glm::vec3 getMax(const glm::mat4 &worldMatrix) const; + + [[nodiscard]] shared_ptr deepCopy() const; + + void setAnimationPlayer(const shared_ptr &animationPlayer); + + [[nodiscard]] const shared_ptr &getAnimationPlayer() const; + + void setBlending(Blending blending); + + void setDepthTest(bool depthTest); + + void setDepthWrite(bool depthWrite); + + [[nodiscard]] Blending getBlending() const; + + [[nodiscard]] bool getDepthTest() const; + + [[nodiscard]] bool getDepthWrite() const; + + void animationPlay(const string &name, bool loop = true); + + void animationStop(const string &name) const; + + void animationPause(const string &name) const; + + void animationResume(const string &name) const; + + protected: + shared_ptr mesh; + shared_ptr material; + shared_ptr baseShader; + shared_ptr animationPlayer; + glm::vec3 localMin; + glm::vec3 localMax; + Blending blending = Blending::Opaque; + bool depthTest = true; + bool depthWrite = true; + string animation; + DrawElement drawElement = DrawElement::Triangles; + }; +} // Model + +#endif //SNAKE3_STANDARDMESH_H diff --git a/Renderer/Opengl/Model/Standard/TringleMesh3D.cpp b/Renderer/Opengl/Model/Standard/TringleMesh3D.cpp new file mode 100644 index 00000000..fbad00ce --- /dev/null +++ b/Renderer/Opengl/Model/Standard/TringleMesh3D.cpp @@ -0,0 +1,24 @@ +#include "TringleMesh3D.h" + +namespace Model { + TringleMesh3D::TringleMesh3D(shared_ptr baseShader, const float width, const float height) + : StandardMesh(std::move(baseShader)) { + std::vector vertices(3); + + float halfW = width / 2.0f; + float halfH = height / 2.0f; + + // Jednoduchý rovnostranný-ish trojúhelník okolo středu + vertices[0].position = { 0.0f, halfH, 0.0f }; // Top + vertices[1].position = { halfW, -halfH, 0.0f }; // Bottom-Right + vertices[2].position = { -halfW,-halfH, 0.0f }; // Bottom-Left + + vertices[0].texUV = {0.5f, 1.0f}; // horní střed tex + vertices[1].texUV = {1.0f, 0.0f}; // pravý spodní + vertices[2].texUV = {0.0f, 0.0f}; // levý spodní + + std::vector indices = {0, 1, 2}; + + mesh = std::make_shared(vertices, indices); + } +} // Model \ No newline at end of file diff --git a/Renderer/Opengl/Model/Standard/TringleMesh3D.h b/Renderer/Opengl/Model/Standard/TringleMesh3D.h new file mode 100644 index 00000000..db782e75 --- /dev/null +++ b/Renderer/Opengl/Model/Standard/TringleMesh3D.h @@ -0,0 +1,13 @@ +#ifndef SNAKE3_TRINGLENODE3D_H +#define SNAKE3_TRINGLENODE3D_H + +#include "StandardMesh.h" + +namespace Model { + class TringleMesh3D : public StandardMesh { + public: + explicit TringleMesh3D(shared_ptr baseShader, float width, float height); + }; +} // Model + +#endif //SNAKE3_TRINGLENODE3D_H \ No newline at end of file diff --git a/Renderer/Opengl/Model/Standard/WireframeArrowMesh.cpp b/Renderer/Opengl/Model/Standard/WireframeArrowMesh.cpp new file mode 100644 index 00000000..7745a147 --- /dev/null +++ b/Renderer/Opengl/Model/Standard/WireframeArrowMesh.cpp @@ -0,0 +1,97 @@ +#include "WireframeArrowMesh.h" + +namespace Model { + WireframeArrowMesh::WireframeArrowMesh(shared_ptr baseShader) + : StandardMesh(std::move(baseShader)) { + float totalLength = 0.5f; // Původně 2.0f + float headLength = 0.15f; // Původně 0.6f + + // Poloviční tloušťky (od středu ke kraji) + float shaftWidth = 0.01f; // Velmi tenká linka + float headWidth = 0.06f; // Šířka křídla šipky + + // Souřadnice Z (směrem do hloubky -Z) + float zStart = 0.0f; + float zNeck = -totalLength + headLength; // Místo, kde se dřík mění v hlavu + float zTip = -totalLength; // Špička + + std::vector vertices; + std::vector indices; + + auto addP = [&](const float x, const float y, const float z) { + Vertex v{}; + v.position = glm::vec3(x, y, z); + v.color = glm::vec3(1.0f); // Barvu přepíšeme v shaderu uniformem + vertices.push_back(v); + + return static_cast(vertices.size() - 1); + }; + + // Funkce pro vytvoření jedné PLOCHÉ šipky v dané rovině + // isVertical: true = rovina YZ, false = rovina XZ + auto createFlatArrow = [&](const bool isVertical) { + // Pomocné proměnné pro prohození os + auto vec = [&](const float w, const float z) { + return isVertical ? glm::vec3(0, w, z) : glm::vec3(w, 0, z); + }; + + // 1. Zadní část (Start) + GLuint backPos = addP(vec(shaftWidth, zStart).x, vec(shaftWidth, zStart).y, zStart); + GLuint backNeg = addP(vec(-shaftWidth, zStart).x, vec(-shaftWidth, zStart).y, zStart); + + // 2. Krk (Neck) - konec dříku + GLuint neckPosIn = addP(vec(shaftWidth, zNeck).x, vec(shaftWidth, zNeck).y, zNeck); + GLuint neckNegIn = addP(vec(-shaftWidth, zNeck).x, vec(-shaftWidth, zNeck).y, zNeck); + + // 3. Krk (Neck) - začátek křídla (rozšíření) + GLuint neckPosOut = addP(vec(headWidth, zNeck).x, vec(headWidth, zNeck).y, zNeck); + GLuint neckNegOut = addP(vec(-headWidth, zNeck).x, vec(-headWidth, zNeck).y, zNeck); + + // 4. Špička (Tip) + const GLuint tip = addP(0, 0, zTip); + + // === SPOJENÍ ČAR (Obrys) === + + // Dřík (Shaft) - dlouhé čáry + indices.push_back(backPos); + indices.push_back(neckPosIn); + indices.push_back(backNeg); + indices.push_back(neckNegIn); + + // Zadní stěna (uzavření dříku) + indices.push_back(backPos); + indices.push_back(backNeg); + + // Rozšíření hlavy (schodek) + indices.push_back(neckPosIn); + indices.push_back(neckPosOut); + indices.push_back(neckNegIn); + indices.push_back(neckNegOut); + + // Šikmé hrany ke špičce + indices.push_back(neckPosOut); + indices.push_back(tip); + indices.push_back(neckNegOut); + indices.push_back(tip); + }; + + createFlatArrow(true); + createFlatArrow(false); + + drawElement = DrawElement::Lines; + mesh = std::make_shared(vertices, indices); + } + + void WireframeArrowMesh::render(const shared_ptr &camera, const glm::mat4 &projection, const float dt, + const glm::mat4 &parentTransform, const bool shadows) const { + baseShader->use(); + baseShader->setMat4("view", camera->getViewMatrix()); + baseShader->setMat4("projection", projection); + baseShader->setMat4("model", parentTransform); + baseShader->setVec3("uColor", color); + + mesh->bind(); + glDrawElements(static_cast(drawElement), static_cast(mesh->getIndices().size()), GL_UNSIGNED_INT, + nullptr); + } +} // Model diff --git a/Renderer/Opengl/Model/Standard/WireframeArrowMesh.h b/Renderer/Opengl/Model/Standard/WireframeArrowMesh.h new file mode 100644 index 00000000..eba8b6b8 --- /dev/null +++ b/Renderer/Opengl/Model/Standard/WireframeArrowMesh.h @@ -0,0 +1,21 @@ +#ifndef SNAKE3_WIREFRAMEARROWMESH_H +#define SNAKE3_WIREFRAMEARROWMESH_H + +#include "StandardMesh.h" + +namespace Model { + class WireframeArrowMesh final : public StandardMesh { + public: + explicit WireframeArrowMesh(shared_ptr baseShader); + + void render(const shared_ptr &camera, const glm::mat4 &projection, float dt, + const glm::mat4 &parentTransform, bool shadows) const override; + + void setColor(const glm::vec3 color) { this->color = color; } + + protected: + glm::vec3 color = glm::vec3{1.0f}; + }; +} // Model + +#endif //SNAKE3_WIREFRAMEARROWMESH_H \ No newline at end of file diff --git a/Renderer/Opengl/Model/TextModel.cpp b/Renderer/Opengl/Model/TextModel.cpp deleted file mode 100644 index 14d55247..00000000 --- a/Renderer/Opengl/Model/TextModel.cpp +++ /dev/null @@ -1,143 +0,0 @@ -#include "TextModel.h" -#include -#include FT_FREETYPE_H - -namespace Model { - TextModel::TextModel(unsigned int width, unsigned int height, ShaderManager *shader) : shader(shader) { - this->shader = shader; - this->shader->use(); - this->shader->setMat4("projection", glm::ortho(0.0f, static_cast(width), static_cast(height), 0.0f, -1.0f, 1000.0f)); - this->shader->setInt("text", 0); - // configure VAO/VBO for texture quads - glGenVertexArrays(1, &this->VAO); - glGenBuffers(1, &this->VBO); - glBindVertexArray(this->VAO); - glBindBuffer(GL_ARRAY_BUFFER, this->VBO); - glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, nullptr, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), nullptr); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); - } - - TextModel::~TextModel() { - delete text; - for (auto c : characters) - { - delete c.second; - } - - characters.clear(); - } - - void TextModel::load(Text* text) { - this->text = text; - // first clear the previously loaded Characters - characters.clear(); - // then initialize and load the FreeType library - FT_Library ft; - if (FT_Init_FreeType(&ft)) // all functions return a value different than 0 whenever an error occurred - std::cout << "ERROR::FREETYPE: Could not init FreeType Library" << std::endl; - // load font as face - FT_Face face; - if (FT_New_Face(ft, text->getFontPath().c_str(), 0, &face)) - std::cout << "ERROR::FREETYPE: Failed to load font" << std::endl; - // set size to load glyphs as - FT_Set_Pixel_Sizes(face, 0, text->getFontSize()); - // disable byte-alignment restriction - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - // then for the first 128 ASCII characters, pre-load/compile their characters and store them - for (GLubyte c = 0; c < 128; c++) // lol see what I did there - { - // load character glyph - if (FT_Load_Char(face, c, FT_LOAD_RENDER)) - { - std::cout << "ERROR::FREETYPE: Failed to load Glyph" << std::endl; - continue; - } - // generate texture - unsigned int texture; - glGenTextures(1, &texture); - glBindTexture(GL_TEXTURE_2D, texture); - glTexImage2D( - GL_TEXTURE_2D, - 0, - GL_RED, - (int)face->glyph->bitmap.width, - (int)face->glyph->bitmap.rows, - 0, - GL_RED, - GL_UNSIGNED_BYTE, - face->glyph->bitmap.buffer - ); - // set texture options - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - // now store character for later use - auto* character = new Character(); - character->setTextureId(texture); - character->setSize(glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows)); - character->setBearing(glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top)); - character->setAdvance(static_cast(face->glyph->advance.x)); - characters.insert(std::pair(c, character)); - } - glBindTexture(GL_TEXTURE_2D, 0); - // destroy FreeType once we're finished - FT_Done_Face(face); - FT_Done_FreeType(ft); - } - - void TextModel::render() { - // activate corresponding render state - shader->use(); - shader->setVec3("textColor", text->getColor()); - shader->setFloat("alpha", text->getAlpha()); - - glActiveTexture(GL_TEXTURE0); - glBindVertexArray(this->VAO); - - float x = text->getPosition().x; - float y = text->getPosition().y; - float scale = text->getZoom().x; - for (char c : text->getText()) - { - Character* ch = characters[c]; - - float xpos = x + (float)ch->getBearing().x * scale; - float ypos = y + (float)(characters['H']->getBearing().y - ch->getBearing().y) * scale; - - float w = (float)ch->getSize().x * scale; - float h = (float)ch->getSize().y * scale; - // update VBO for each character - float vertices[6][4] = { - { xpos, ypos + h, 0.0f, 1.0f }, - { xpos + w, ypos, 1.0f, 0.0f }, - { xpos, ypos, 0.0f, 0.0f }, - - { xpos, ypos + h, 0.0f, 1.0f }, - { xpos + w, ypos + h, 1.0f, 1.0f }, - { xpos + w, ypos, 1.0f, 0.0f } - }; - // render glyph texture over quad - glBindTexture(GL_TEXTURE_2D, ch->getTextureId()); - // update content of VBO memory - glBindBuffer(GL_ARRAY_BUFFER, VBO); - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); // be sure to use glBufferSubData and not glBufferData - glBindBuffer(GL_ARRAY_BUFFER, 0); - // render quad - glDrawArrays(GL_TRIANGLES, 0, 6); - // now advance cursors for next glyph - x += (float)(ch->getAdvance() >> 6) * scale; // bitshift by 6 to get value in pixels (1/64th times 2^6 = 64) - } - glBindVertexArray(0); - glBindTexture(GL_TEXTURE_2D, 0); - } - - Text *TextModel::getText() const { - return text; - } - -} // Model \ No newline at end of file diff --git a/Renderer/Opengl/Model/TextModel.h b/Renderer/Opengl/Model/TextModel.h deleted file mode 100644 index 045714d4..00000000 --- a/Renderer/Opengl/Model/TextModel.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef SNAKE3_TEXTMODEL_H -#define SNAKE3_TEXTMODEL_H - -#include "../../../Manager/ShaderManager.h" -#include "../../../ItemsDto/Text.h" -#include "Character.h" -#include -#include - -using namespace Manager; -using namespace ItemsDto; -using namespace std; - -namespace Model { - - class TextModel { - public: - virtual ~TextModel(); - explicit TextModel(unsigned int width, unsigned int height, ShaderManager *shader); - void load(Text* text); - void render(); - [[nodiscard]] Text *getText() const; - - protected: - Text* text{}; - ShaderManager* shader; - map characters; - unsigned int VAO{}, VBO{}; - }; - -} // Model - -#endif //SNAKE3_TEXTMODEL_H diff --git a/Renderer/Opengl/Model/Utils/Ebo.cpp b/Renderer/Opengl/Model/Utils/Ebo.cpp index c14aceb2..9dfc72cd 100644 --- a/Renderer/Opengl/Model/Utils/Ebo.cpp +++ b/Renderer/Opengl/Model/Utils/Ebo.cpp @@ -1,10 +1,10 @@ #include "Ebo.h" namespace ModelUtils { - Ebo::Ebo(vector &indices) { + Ebo::Ebo(const vector &indices) { glGenBuffers(1, &ID); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ID); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, (long)(indices.size() * sizeof(GLuint)), indices.data(), GL_STATIC_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, static_cast(indices.size() * sizeof(GLuint)), indices.data(), GL_STATIC_DRAW); } void Ebo::bind() { diff --git a/Renderer/Opengl/Model/Utils/Ebo.h b/Renderer/Opengl/Model/Utils/Ebo.h index 143c496f..6cceb79c 100644 --- a/Renderer/Opengl/Model/Utils/Ebo.h +++ b/Renderer/Opengl/Model/Utils/Ebo.h @@ -12,7 +12,7 @@ namespace ModelUtils { class Ebo { public: // Constructor that generates a Elements Buffer Object and links it to indices - explicit Ebo(vector& indices); + explicit Ebo(const vector& indices); // ID reference of Elements Buffer Object GLuint ID{}; // Binds the EBO diff --git a/Renderer/Opengl/Model/Utils/Mesh.cpp b/Renderer/Opengl/Model/Utils/Mesh.cpp index 37887df7..bc6e5c21 100644 --- a/Renderer/Opengl/Model/Utils/Mesh.cpp +++ b/Renderer/Opengl/Model/Utils/Mesh.cpp @@ -3,27 +3,16 @@ #include namespace ModelUtils { - Mesh::Mesh(const vector &vertices, const vector &indices, bool hasBones, string name) - : vertices(vertices), indices(indices), hasBones(hasBones), name(std::move(name)) { - vao = new Vao(); - vao->bind(); - // Generates Vertex Buffer Object and links it to vertices - Vbo vbo(this->vertices); - // Generates Element Buffer Object and links it to indices - Ebo ebo(this->indices); - // Links VBO attributes such as coordinates and colors to VAO - vao->linkAttrib(vbo, 0, 3, GL_FLOAT, sizeof(Vertex), (void *) nullptr); - vao->linkAttrib(vbo, 1, 3, GL_FLOAT, sizeof(Vertex), (void *) (3 * sizeof(float))); - vao->linkAttrib(vbo, 2, 3, GL_FLOAT, sizeof(Vertex), (void *) (6 * sizeof(float))); - vao->linkAttrib(vbo, 3, 2, GL_FLOAT, sizeof(Vertex), (void *) (9 * sizeof(float))); - vao->linkAttrib(vbo, 4, 3, GL_FLOAT, sizeof(Vertex), (void *) (11 * sizeof(float))); - vao->linkAttrib(vbo, 5, 3, GL_FLOAT, sizeof(Vertex), (void *) (14 * sizeof(float))); - vao->linkAttribI(vbo, 6, 4, GL_INT, sizeof(Vertex), (void*)offsetof(Vertex, BoneIDs)); - vao->linkAttrib(vbo, 7, 4, GL_FLOAT, sizeof(Vertex), (void*)offsetof(Vertex, Weights)); - // Unbind all to prevent accidentally modifying them - vao->unBind(); - vbo.unBind(); - ebo.unBind(); + Mesh::Mesh(const vector &vertices, const vector &indices, const bool hasBones, string name) + : vertices(vertices), indices(indices), hasBones(hasBones), name(std::move(name)), localMin(+FLT_MAX), + localMax(-FLT_MIN) { + + vao = nullptr; + + for (const auto &v: vertices) { + localMin = glm::min(localMin, v.position); + localMax = glm::max(localMax, v.position); + } } Mesh::~Mesh() { @@ -34,7 +23,16 @@ namespace ModelUtils { return indices; } + const vector &Mesh::getVertices() const { + return vertices; + } + + const vector & Mesh::getTextures() const { + return textures; + } + void Mesh::bind() { + initialize(); vao->bind(); } @@ -46,6 +44,32 @@ namespace ModelUtils { Mesh::globalTransformation = globalTransformation; } + void Mesh::initialize() { + if (vao != nullptr) { + return; + } + + vao = new Vao(); + vao->bind(); + // Generates Vertex Buffer Object and links it to vertices + Vbo vbo(this->vertices); + // Generates Element Buffer Object and links it to indices + Ebo ebo(this->indices); + // Links VBO attributes such as coordinates and colors to VAO + vao->linkAttrib(vbo, 0, 3, GL_FLOAT, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, position))); + vao->linkAttrib(vbo, 1, 3, GL_FLOAT, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, normal))); + vao->linkAttrib(vbo, 2, 3, GL_FLOAT, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, color))); + vao->linkAttrib(vbo, 3, 2, GL_FLOAT, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, texUV))); + vao->linkAttrib(vbo, 4, 3, GL_FLOAT, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, tangents))); + vao->linkAttrib(vbo, 5, 2, GL_FLOAT, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, biTangents))); // vec2! + vao->linkAttribI(vbo, 6, 4, GL_INT, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, BoneIDs))); + vao->linkAttrib (vbo, 7, 4, GL_FLOAT, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, Weights))); + // Unbind all to prevent accidentally modifying them + vao->unBind(); + Vbo::unBind(); + ebo.unBind(); + } + bool Mesh::isHasBones() const { return hasBones; } @@ -54,4 +78,43 @@ namespace ModelUtils { return name; } -} // ModelUtils \ No newline at end of file + glm::vec3 Mesh::getMin(const glm::mat4 &modelMatrix) const { + const glm::vec3 corners[8] = { + {localMin.x, localMin.y, localMin.z}, + {localMin.x, localMin.y, localMax.z}, + {localMin.x, localMax.y, localMin.z}, + {localMin.x, localMax.y, localMax.z}, + {localMax.x, localMin.y, localMin.z}, + {localMax.x, localMin.y, localMax.z}, + {localMax.x, localMax.y, localMin.z}, + {localMax.x, localMax.y, localMax.z} + }; + + glm::vec3 worldMin(+FLT_MAX); + for (auto corner: corners) { + glm::vec4 worldPos = modelMatrix * glm::vec4(corner, 1.0f); + worldMin = glm::min(worldMin, glm::vec3(worldPos)); + } + return worldMin; + } + + glm::vec3 Mesh::getMax(const glm::mat4 &modelMatrix) const { + glm::vec3 corners[8] = { + {localMin.x, localMin.y, localMin.z}, + {localMin.x, localMin.y, localMax.z}, + {localMin.x, localMax.y, localMin.z}, + {localMin.x, localMax.y, localMax.z}, + {localMax.x, localMin.y, localMin.z}, + {localMax.x, localMin.y, localMax.z}, + {localMax.x, localMax.y, localMin.z}, + {localMax.x, localMax.y, localMax.z} + }; + + glm::vec3 worldMax(-FLT_MAX); + for (auto corner: corners) { + glm::vec4 worldPos = modelMatrix * glm::vec4(corner, 1.0f); + worldMax = glm::max(worldMax, glm::vec3(worldPos)); + } + return worldMax; + } +} // ModelUtils diff --git a/Renderer/Opengl/Model/Utils/Mesh.h b/Renderer/Opengl/Model/Utils/Mesh.h index 52113da7..4f9cc4e6 100644 --- a/Renderer/Opengl/Model/Utils/Mesh.h +++ b/Renderer/Opengl/Model/Utils/Mesh.h @@ -3,15 +3,9 @@ #include "Vao.h" #include "Ebo.h" -#include "../../../../ItemsDto/BaseItem.h" -#include "../../../../Manager/Camera.h" -#include -#include #include #include -#include -using namespace ItemsDto; using namespace std; namespace ModelUtils { @@ -21,23 +15,43 @@ namespace ModelUtils { Mesh(const vector &vertices, const vector &indices, bool hasBones = false, string name = ""); + Mesh(const vector &ve, const vector &i, const vector &t) + : vertices(ve), indices(i), textures(t) { + hasBones = false; name = ""; vao = nullptr; + for (const auto &v: vertices) { + localMin = glm::min(localMin, v.position); + localMax = glm::max(localMax, v.position); + } + } + virtual ~Mesh(); [[nodiscard]] const vector &getIndices() const; + [[nodiscard]] const vector &getVertices() const; + [[nodiscard]] const vector &getTextures() const; [[nodiscard]] const glm::mat4 &getGlobalTransformation() const; [[nodiscard]] bool isHasBones() const; [[nodiscard]] const string &getName() const; void setGlobalTransformation(const glm::mat4 &globalTransformation); + void initialize(); + void bind(); + [[nodiscard]] glm::vec3 getMin(const glm::mat4 &modelMatrix) const; + + [[nodiscard]] glm::vec3 getMax(const glm::mat4 &modelMatrix) const; + protected: vector vertices; vector indices; + std::vector textures; Vao *vao; bool hasBones; glm::mat4 globalTransformation{}; string name; + glm::vec3 localMin{}; + glm::vec3 localMax{}; }; } // ModelUtils diff --git a/Renderer/Opengl/Model/Utils/Mesh2D.cpp b/Renderer/Opengl/Model/Utils/Mesh2D.cpp new file mode 100644 index 00000000..a0e10f30 --- /dev/null +++ b/Renderer/Opengl/Model/Utils/Mesh2D.cpp @@ -0,0 +1,60 @@ +#include "Mesh2D.h" + +namespace ModelUtils { + Mesh2D::Mesh2D(const vector &vertices, const vector &indices, string name) + : vertices(vertices), indices(indices), name(std::move(name)) { + + vao = nullptr; + } + + Mesh2D::~Mesh2D() { + delete vao; + } + + const vector &Mesh2D::getIndices() const { + return indices; + } + + const vector &Mesh2D::getVertices() const { + return vertices; + } + + void Mesh2D::bind() { + initialize(); + vao->bind(); + } + + const glm::mat4 &Mesh2D::getGlobalTransformation() const { + return globalTransformation; + } + + void Mesh2D::setGlobalTransformation(const glm::mat4 &globalTransformation) { + Mesh2D::globalTransformation = globalTransformation; + } + + const string &Mesh2D::getName() const { + return name; + } + + void Mesh2D::initialize() { + if (vao != nullptr) { + return; + } + + vao = new Vao(); + vao->bind(); + // Generates Vertex Buffer Object and links it to vertices + Vbo vbo(this->vertices); + // Generates Element Buffer Object and links it to indices + Ebo ebo(this->indices); + // Links VBO attributes such as coordinates and colors to VAO + vao->linkAttrib(vbo, 0, 3, GL_FLOAT, sizeof(Vertex2D), reinterpret_cast(offsetof(Vertex2D, position))); + vao->linkAttrib(vbo, 1, 2, GL_FLOAT, sizeof(Vertex2D), reinterpret_cast(offsetof(Vertex2D, texUV))); + vao->linkAttrib(vbo, 2, 3, GL_FLOAT, sizeof(Vertex2D), reinterpret_cast(offsetof(Vertex2D, color))); + // Unbind all to prevent accidentally modifying them + vao->unBind(); + Vbo::unBind(); + ebo.unBind(); + } + +} // ModelUtils \ No newline at end of file diff --git a/Renderer/Opengl/Model/Utils/Mesh2D.h b/Renderer/Opengl/Model/Utils/Mesh2D.h new file mode 100644 index 00000000..edc7c7c9 --- /dev/null +++ b/Renderer/Opengl/Model/Utils/Mesh2D.h @@ -0,0 +1,35 @@ +#ifndef SNAKE3_MESH2D_H +#define SNAKE3_MESH2D_H + +#include "Vao.h" +#include "Ebo.h" +#include +#include + +namespace ModelUtils { + class Mesh2D { + public: + Mesh2D(const vector &vertices, const vector &indices, string name = ""); + + virtual ~Mesh2D(); + + [[nodiscard]] const vector &getIndices() const; + [[nodiscard]] const vector &getVertices() const; + [[nodiscard]] const glm::mat4 &getGlobalTransformation() const; + [[nodiscard]] const string &getName() const; + void setGlobalTransformation(const glm::mat4 &globalTransformation); + + void initialize(); + + void bind(); + + protected: + vector vertices; + vector indices; + Vao *vao; + glm::mat4 globalTransformation{}; + string name; + }; +} // ModelUtils + +#endif //SNAKE3_MESH2D_H \ No newline at end of file diff --git a/Renderer/Opengl/Model/Utils/TextMesh.cpp b/Renderer/Opengl/Model/Utils/TextMesh.cpp new file mode 100644 index 00000000..205d8867 --- /dev/null +++ b/Renderer/Opengl/Model/Utils/TextMesh.cpp @@ -0,0 +1,82 @@ +#include "TextMesh.h" + +namespace ModelUtils { + TextMesh::TextMesh() { + vao = nullptr; + vbo = nullptr; + } + + void TextMesh::initialize() { + if (vao != nullptr) { + return; + } + + vao = make_shared(); + vbo = make_shared(); + } + + void TextMesh::update(const std::string &text, const shared_ptr &font) { + if (vao == nullptr) { + initialize(); + } + + vertices.clear(); + float x = 0.0f; + const float y = font->getAscenderPixels(); + + for (const char c: text) { + const auto ch = font->getCharacter(c); + + float xpos = x + static_cast(ch->bearing.x); + const auto w = static_cast(ch->size.x); + const auto h = static_cast(ch->size.y); + + float u0 = ch->uvOffset.x, v0 = ch->uvOffset.y; + float u1 = u0 + ch->uvSize.x, v1 = v0 + ch->uvSize.y; + + const auto bearingY = static_cast(ch->bearing.y); + const float sizeY = h; + const bool exclude = c == '-' || c == '`' || c == '~' || c == '\'' || c == '='; + const float air_below = (bearingY > sizeY && !exclude) ? (bearingY - sizeY) : 0.0f; + float ypos = y - bearingY + air_below; + + vertices.insert(vertices.end(), { + xpos, ypos + h, u0, v1, // Top-Left + xpos, ypos, u0, v0, // Bottom-Left + xpos + w, ypos, u1, v0, // Bottom-Right + + xpos, ypos + h, u0, v1, // Top-Left + xpos + w, ypos, u1, v0, // Bottom-Right + xpos + w, ypos + h, u1, v1 // Top-Right + }); + + x += static_cast(ch->advance); + } + + width = x; + maxSizeY = 0.8f * (font->getAscenderPx() - font->getDescenderPx()); + + vao->bind(); + vbo->bind(); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 4,GL_FLOAT,GL_FALSE, 4 * sizeof(float), static_cast(nullptr)); + glBufferData(GL_ARRAY_BUFFER, static_cast(vertices.size() * sizeof(float)), vertices.data(), + GL_DYNAMIC_DRAW); + } + + void TextMesh::bind() const { + vao->bind(); + } + + std::vector TextMesh::getVertices() const { + return vertices; + } + + float TextMesh::getSizeY() const { + return maxSizeY; + } + + float TextMesh::getWidth() const { + return width; + } +} // ModelUtils diff --git a/Renderer/Opengl/Model/Utils/TextMesh.h b/Renderer/Opengl/Model/Utils/TextMesh.h new file mode 100644 index 00000000..190ebaf5 --- /dev/null +++ b/Renderer/Opengl/Model/Utils/TextMesh.h @@ -0,0 +1,40 @@ +#ifndef SNAKE3_TEXTMESH_H +#define SNAKE3_TEXTMESH_H + +#include +#include + +#include "Vao.h" +#include "Ebo.h" +#include "../../Material/2D/Font.h" + +using namespace std; +using namespace Material; + +namespace ModelUtils { + class TextMesh { + public: + TextMesh(); + + void initialize(); + + void bind() const; + + void update(const std::string &text, const shared_ptr &font); + + [[nodiscard]] std::vector getVertices() const; + + [[nodiscard]] float getSizeY() const; + + [[nodiscard]] float getWidth() const; + + protected: + shared_ptr vao{}; + shared_ptr vbo{}; + std::vector vertices; + float maxSizeY = 0.0f; + float width = 0.0f; + }; +} // ModelUtils + +#endif //SNAKE3_TEXTMESH_H diff --git a/Renderer/Opengl/Model/Utils/Tree.h b/Renderer/Opengl/Model/Utils/Tree.h index 1f7f95c2..0171ca5c 100644 --- a/Renderer/Opengl/Model/Utils/Tree.h +++ b/Renderer/Opengl/Model/Utils/Tree.h @@ -3,12 +3,13 @@ #include +using namespace std; + namespace ModelUtils { template class Tree { - private: T data; - std::vector> children; + std::vector children; public: explicit Tree(T data) noexcept : data{std::move(data)} {} diff --git a/Renderer/Opengl/Model/Utils/Vao.h b/Renderer/Opengl/Model/Utils/Vao.h index f1dafbec..2df51d16 100644 --- a/Renderer/Opengl/Model/Utils/Vao.h +++ b/Renderer/Opengl/Model/Utils/Vao.h @@ -3,6 +3,7 @@ #include #include + #include "Vbo.h" namespace ModelUtils { diff --git a/Renderer/Opengl/Model/Utils/Vbo.cpp b/Renderer/Opengl/Model/Utils/Vbo.cpp index c1b0e323..025a3669 100644 --- a/Renderer/Opengl/Model/Utils/Vbo.cpp +++ b/Renderer/Opengl/Model/Utils/Vbo.cpp @@ -1,10 +1,21 @@ #include "Vbo.h" namespace ModelUtils { - Vbo::Vbo(vector &vertices) { + Vbo::Vbo(const vector &vertices) { glGenBuffers(1, &ID); glBindBuffer(GL_ARRAY_BUFFER, ID); - glBufferData(GL_ARRAY_BUFFER, (long)(vertices.size() * sizeof(Vertex)), vertices.data(), GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, static_cast(vertices.size() * sizeof(Vertex)), vertices.data(), GL_STATIC_DRAW); + } + + Vbo::Vbo(const vector &vertices) { + glGenBuffers(1, &ID); + glBindBuffer(GL_ARRAY_BUFFER, ID); + glBufferData(GL_ARRAY_BUFFER, static_cast(vertices.size() * sizeof(Vertex)), vertices.data(), GL_STATIC_DRAW); + } + + Vbo::Vbo() { + glGenBuffers(1, &ID); + glBindBuffer(GL_ARRAY_BUFFER,ID); } void Vbo::bind() const { @@ -15,7 +26,7 @@ namespace ModelUtils { glBindBuffer(GL_ARRAY_BUFFER, 0); } - void Vbo::clear() { + void Vbo::clear() const { glDeleteBuffers(1, &ID); } } // ModelUtils \ No newline at end of file diff --git a/Renderer/Opengl/Model/Utils/Vbo.h b/Renderer/Opengl/Model/Utils/Vbo.h index a0963e2f..2aaa27b7 100644 --- a/Renderer/Opengl/Model/Utils/Vbo.h +++ b/Renderer/Opengl/Model/Utils/Vbo.h @@ -1,15 +1,33 @@ #ifndef SNAKE3_VBO_H #define SNAKE3_VBO_H +#include #include -#include #include #include +#include +#include "../../../../Manager/TextureManager.h" + +using namespace Manager; using namespace std; namespace ModelUtils { + enum class TextureType { + Diffuse, // Barva (Albedo) + Specular, // Lesk (nebo Metallic/Roughness v PBR) + Normal, // Bump mapa + Emissive, // Záře + MetalRough + }; + + struct TextureInfo { + std::string path; // Cesta k souboru nebo klíč pro embedded texturu + TextureType type; + shared_ptr texture; + }; + // Structure to standardize the vertices used in the meshes struct Vertex { @@ -23,18 +41,26 @@ namespace ModelUtils { float Weights[4]; }; + struct Vertex2D { + glm::vec3 position{}; + glm::vec2 texUV{}; + glm::vec3 color = {1.0f, 1.0f, 1.0f}; + }; + class Vbo { public: // Constructor that generates a Vertex Buffer Object and links it to vertices - explicit Vbo(vector& vertices); + explicit Vbo(const vector& vertices); + explicit Vbo(const vector& vertices); + explicit Vbo(); // Reference ID of the Vertex Buffer Object GLuint ID{}; // Binds the VBO void bind() const; // Unbinds the VBO - void unBind(); + static void unBind(); // Deletes the VBO - void clear(); + void clear() const; }; } // ModelUtils diff --git a/Renderer/Opengl/Node2DRenderer.cpp b/Renderer/Opengl/Node2DRenderer.cpp new file mode 100644 index 00000000..847f519e --- /dev/null +++ b/Renderer/Opengl/Node2DRenderer.cpp @@ -0,0 +1,40 @@ +#include "Node2DRenderer.h" + +#include + +namespace Renderer { + Node2DRenderer::Node2DRenderer(const shared_ptr &camera, const int width, const int height) + : camera(camera) { + ortho = glm::ortho(0.0f, static_cast(width), static_cast(height), 0.0f, -1.0f, 1000.0f); + } + + void Node2DRenderer::render3D(float dt, uint64_t frameId) { + throw std::runtime_error("Not implemented. Use render2D() instead."); + } + + void Node2DRenderer::render2D(const float dt, const uint64_t frameId) { + rootNode->update(dt, frameId); + this->beforeRender(standard); + renderScene(); + this->afterRender(); + } + + void Node2DRenderer::beforeRender(const MODE mode) { + this->mode = mode; + } + + void Node2DRenderer::afterRender() { + } + + void Node2DRenderer::setRootNode(const shared_ptr &rootNode) { + this->rootNode = rootNode; + } + + shared_ptr Node2DRenderer::getRootNode() { + return rootNode; + } + + void Node2DRenderer::renderScene() const { + rootNode->render(camera, ortho, 1, glm::mat4(1)); + } +} // Renderer \ No newline at end of file diff --git a/Renderer/Opengl/Node2DRenderer.h b/Renderer/Opengl/Node2DRenderer.h new file mode 100644 index 00000000..cba0482c --- /dev/null +++ b/Renderer/Opengl/Node2DRenderer.h @@ -0,0 +1,36 @@ +#ifndef SNAKE3_NODE2DRENDERER_H +#define SNAKE3_NODE2DRENDERER_H + +#include + +#include "BaseRenderer.h" +#include "../../Manager/Camera.h" +#include "Model/Standard/2D/MeshNode2D.h" + +using namespace Manager; +using namespace Model; +using namespace std; + +namespace Renderer { + class Node2DRenderer final : public BaseRenderer { // BaseRenderer2D + public: + Node2DRenderer(const shared_ptr &camera, int width, int height); + ~Node2DRenderer() override = default; + void render3D(float dt, uint64_t frameId) override; + void render2D(float dt, uint64_t frameId) override; + void beforeRender(MODE mode) override; + void afterRender() override; + void renderShadowMap() override {}; + void setRootNode(const shared_ptr &rootNode); + shared_ptr getRootNode(); + + protected: + void renderScene() const; + + shared_ptr camera; + shared_ptr rootNode; + glm::mat4 ortho{}; + }; +} // Renderer + +#endif //SNAKE3_NODE2DRENDERER_H \ No newline at end of file diff --git a/Renderer/Opengl/Node3DRenderer.cpp b/Renderer/Opengl/Node3DRenderer.cpp new file mode 100644 index 00000000..2bb7a708 --- /dev/null +++ b/Renderer/Opengl/Node3DRenderer.cpp @@ -0,0 +1,52 @@ +#include "Node3DRenderer.h" + +namespace Renderer { + Node3DRenderer::Node3DRenderer(shared_ptr camera, + const glm::mat4 &projection, + shared_ptr rootNode) + : camera(std::move(camera)), + rootNode(std::move(rootNode)), + projection(projection) { + } + + Node3DRenderer::Node3DRenderer(shared_ptr camera, const glm::mat4 &projection) + : camera(std::move(camera)), + projection(projection) { + } + + Node3DRenderer::~Node3DRenderer() = default; + + void Node3DRenderer::render3D(const float dt, const uint64_t frameId) { + rootNode->update(dt, frameId); + renderScene(); + } + + void Node3DRenderer::beforeRender(const MODE mode) { + this->mode = mode; + } + + void Node3DRenderer::afterRender() { + } + + void Node3DRenderer::renderShadowMap() { + if (shadows) { + rootNode->renderShadows(camera, projection, 1, glm::mat4(1)); + } + } + + void Node3DRenderer::setRootNode(const shared_ptr &rootNode) { + this->rootNode = rootNode; + } + + shared_ptr Node3DRenderer::getRootNode() { + return rootNode; + } + + void Node3DRenderer::renderScene() const { + if (mode == reflection && !rootNode->isIncludeInPlanarReflection()) { + return; + } + + rootNode->render(camera, projection, 1, glm::mat4(1), shadows); + } +} // Renderer diff --git a/Renderer/Opengl/Node3DRenderer.h b/Renderer/Opengl/Node3DRenderer.h new file mode 100644 index 00000000..375b0d70 --- /dev/null +++ b/Renderer/Opengl/Node3DRenderer.h @@ -0,0 +1,49 @@ +#ifndef SNAKE3_STANDARDMESHRENDERER_H +#define SNAKE3_STANDARDMESHRENDERER_H + +#include +#include "BaseRenderer.h" +#include "Model/Standard/StandardMesh.h" +#include "../../Manager/ResourceManager.h" +#include "../../Manager/Camera.h" +#include "Model/Standard/MeshNode3D.h" + +using namespace Model; +using namespace std; + +namespace Renderer { + class Node3DRenderer final : public BaseRenderer { + public: + explicit Node3DRenderer(shared_ptr camera, + const glm::mat4 &projection, + shared_ptr rootNode); + + Node3DRenderer(shared_ptr camera, + const glm::mat4 &projection); + + ~Node3DRenderer() override; + + void render3D(float dt, uint64_t frameId) override; + + void beforeRender(MODE mode) override; + + void afterRender() override; + + void renderShadowMap() override; + + void setMesh(const shared_ptr &mesh); + + void setRootNode(const shared_ptr &rootNode); + + shared_ptr getRootNode(); + + protected: + void renderScene() const; + + shared_ptr camera; + shared_ptr rootNode; + glm::mat4 projection; + }; +} // Renderer + +#endif //SNAKE3_STANDARDMESHRENDERER_H diff --git a/Renderer/Opengl/ObjWallRenderer.cpp b/Renderer/Opengl/ObjWallRenderer.cpp deleted file mode 100644 index bdacd99d..00000000 --- a/Renderer/Opengl/ObjWallRenderer.cpp +++ /dev/null @@ -1,110 +0,0 @@ -#include "ObjWallRenderer.h" - -namespace Renderer { - ObjWallRenderer::ObjWallRenderer(ObjWall *item, Camera* camera, glm::mat4 proj, ResourceManager* resManager) - : wall(item), resourceManager(resManager), camera(camera), projection(proj), parallax(false), heightScale(0.1f) { - mesh = resourceManager->getModel("cube")->getMesh(); - shader = resourceManager->getShader("normalShader"); - texture1 = resourceManager->getTexture("brickwork-texture.jpg"); - texture2 = resourceManager->getTexture("brickwork_normal-map.jpg"); - texture3 = resourceManager->getTexture("brickwork-bump-map.jpg"); - texture4 = resourceManager->getTexture("bricks2_disp.jpg"); - } - - ObjWallRenderer::~ObjWallRenderer() { - delete wall; - } - - void ObjWallRenderer::render() { - shader->use(); - shader->setMat4("view", camera->getViewMatrix()); - shader->setMat4("projection", this->projection); - shader->setInt("diffuseMap", 0); - shader->setInt("normalMap", 1); - shader->setInt("specularMap", 2); - shader->setInt("depthMap", 2); - shader->setBool("parallaxEnable", parallax); - shader->setFloat("alpha", 1.0); - shader->setFloat("heightScale", heightScale); - shader->setBool("fogEnable", fog); - - // lighting info - // ------------- - glm::vec3 lightPos(camera->getPosition().x - 26, camera->getPosition().y - 26, 36.3f); - - for (auto item: wall->getItems()) { - glLoadIdentity(); - - if (parallax) { - texture1->bind(0); - texture2->bind(1); - texture4->bind(2); - } else { - texture1->bind(0); - texture2->bind(1); - texture3->bind(2); - } - - glm::vec3 position = item->getPosition(); - - // Initialize matrices - glm::mat4 model = glm::mat4(1.0f); - // Transform the matrices to their correct form - model = glm::translate(model, {0.0, 0.0, 0.0}); - model = glm::scale(model, {0.041666667f, 0.041666667f, 0.041666667f}); - model = glm::translate(model, position); - - shader->setMat4("model", model); - shader->setVec3("viewPos", camera->getPosition()); - shader->setVec3("lightPos", lightPos); - shader->setBool("shadowEnable", true); - - mesh->bind(); - glDrawElements(GL_TRIANGLES, (int)mesh->getIndices().size(), GL_UNSIGNED_INT, nullptr); - } - - glEnable(GL_TEXTURE0); - } - - void ObjWallRenderer::beforeRender() { - glEnable(GL_DEPTH_TEST); - } - - void ObjWallRenderer::afterRender() { - } - - void ObjWallRenderer::renderShadowMap() { - - } - - void ObjWallRenderer::toggleParallax() { - ObjWallRenderer::parallax = !parallax; - - if (parallax) { - texture1 = resourceManager->getTexture("bricks2.jpg"); - texture2 = resourceManager->getTexture("bricks2_normal.jpg"); - texture4 = resourceManager->getTexture("bricks2_disp.jpg"); - } else { - texture1 = resourceManager->getTexture("brickwork-texture.jpg"); - texture2 = resourceManager->getTexture("brickwork_normal-map.jpg"); - texture3 = resourceManager->getTexture("brickwork-bump-map.jpg"); - } - } - - void ObjWallRenderer::downScale() { - if (heightScale > 0.0f) { - heightScale -= 0.05f; - } else { - heightScale = 0.0f; - } - } - - void ObjWallRenderer::upScale() { - if (heightScale < 1.0f) { - heightScale += 0.05f; - } else { - heightScale = 1.0f; - } - } - -} // Renderer \ No newline at end of file diff --git a/Renderer/Opengl/ObjWallRenderer.h b/Renderer/Opengl/ObjWallRenderer.h deleted file mode 100644 index 70074f7c..00000000 --- a/Renderer/Opengl/ObjWallRenderer.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef SNAKE3_OBJWALLRENDERER_H -#define SNAKE3_OBJWALLRENDERER_H - -#include "../../Manager/ShaderManager.h" -#include "../../Manager/ResourceManager.h" -#include "BaseRenderer.h" -#include -#include -#include "../../ItemsDto/ObjWall.h" - -using namespace ItemsDto; -using namespace Manager; - -namespace Renderer { - - class ObjWallRenderer : public BaseRenderer { - public: - explicit ObjWallRenderer(ObjWall *item, Camera* camera, glm::mat4 proj, ResourceManager* resManager); - ~ObjWallRenderer() override; - void render() override; - void beforeRender() override; - void afterRender() override; - void renderShadowMap() override; - void toggleParallax(); - void downScale(); - void upScale(); - - protected: - ObjWall* wall; - Mesh* mesh; - Camera* camera; - glm::mat4 projection{}; - ResourceManager* resourceManager; - ShaderManager* shader; - TextureManager* texture1; - TextureManager* texture2; - TextureManager* texture3; - TextureManager* texture4; - bool parallax; - float heightScale; - }; - -} // Renderer - -#endif //SNAKE3_OBJWALLRENDERER_H diff --git a/Renderer/Opengl/PlanarReflectionRenderer.cpp b/Renderer/Opengl/PlanarReflectionRenderer.cpp new file mode 100644 index 00000000..ee51f53e --- /dev/null +++ b/Renderer/Opengl/PlanarReflectionRenderer.cpp @@ -0,0 +1,110 @@ +#include "PlanarReflectionRenderer.h" +#include "../../Manager/RenderManager.h" +#include +#include +#include "Model/Standard/PlaneMesh.h" + +namespace Renderer { + PlanarReflectionRenderer::PlanarReflectionRenderer( + const shared_ptr &contextState, + const shared_ptr &resManager, + const shared_ptr &camera, + const glm::mat4 &projection, + const int width, const int height) + : contextState(contextState), resourceManager(resManager), camera(camera), projection(projection), width(width), height(height) { + // Framebuffer setup + glGenFramebuffers(1, &reflectionFBO); + glBindFramebuffer(GL_FRAMEBUFFER, reflectionFBO); + + glGenTextures(1, &reflectionTexture); + glBindTexture(GL_TEXTURE_2D, reflectionTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, reflectionTexture, 0); + + glGenRenderbuffers(1, &depthBuffer); + glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + std::cout << "Reflection Framebuffer not complete!" << std::endl; + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + const auto textureRes = make_shared(reflectionTexture); + resourceManager->addTexture("PlanarReflectionTexture", textureRes); + } + + PlanarReflectionRenderer::~PlanarReflectionRenderer() { + glDeleteFramebuffers(1, &reflectionFBO); + glDeleteTextures(1, &reflectionTexture); + glDeleteRenderbuffers(1, &depthBuffer); + } + + void PlanarReflectionRenderer::updateRenderers(const vector &renderers) { + this->renderers = renderers; + } + + void PlanarReflectionRenderer::render3D(const float dt, const uint64_t frameId) { + // Vypočet zrcadlené kamery + glm::vec3 originalPos = camera->getPosition(); + const glm::vec3 originalFront = camera->getFront(); + const glm::vec3 originalUp = camera->getUp(); + + // Zrcadlíme pozici přes rovinu Z + const float dist = 2.0f * (originalPos.z - planeZ); + camera->setPosition({originalPos.x, originalPos.y, originalPos.z - dist}); + + // Zrcadlíme front vektor: X a Y zůstávají, Z se obrací + glm::vec3 reflectedFront = originalFront; + reflectedFront.z = -reflectedFront.z; + camera->setFront(reflectedFront); + + glm::vec3 reflectedUp = originalUp; + reflectedUp.z = -reflectedUp.z; + camera->setUp(reflectedUp); + + glBindFramebuffer(GL_FRAMEBUFFER, reflectionFBO); + glViewport(0, 0, width, height); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Změna winding order kvůli zrcadlení + glFrontFace(GL_CW); + + // Vykreslíme ostatní renderery (např. oheň) + for (auto &entry: renderers) { + if (entry.renderer.get() == this) continue; + + entry.renderer->beforeRender(reflection); + entry.renderer->render3D(dt, frameId); + entry.renderer->afterRender(); + } + + contextState->setDepthTest(true); + contextState->setDepthWrite(true); + glFrontFace(GL_CCW); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // Camera restore + camera->setPosition(originalPos); + camera->setFront(originalFront); + camera->setUp(originalUp); + } + + void PlanarReflectionRenderer::beforeRender(const MODE mode) { + this->mode = mode; + } + + void PlanarReflectionRenderer::afterRender() { + } + + void PlanarReflectionRenderer::renderShadowMap() { + } + + void PlanarReflectionRenderer::setPlaneZ(const float z) { + planeZ = z; + } +} diff --git a/Renderer/Opengl/PlanarReflectionRenderer.h b/Renderer/Opengl/PlanarReflectionRenderer.h new file mode 100644 index 00000000..1927da76 --- /dev/null +++ b/Renderer/Opengl/PlanarReflectionRenderer.h @@ -0,0 +1,49 @@ +#ifndef SNAKE3_PLANARREFLECTIONRENDERER_H +#define SNAKE3_PLANARREFLECTIONRENDERER_H + +#include +#include +#include +#include "BaseRenderer.h" +#include "../../Manager/ResourceManager.h" +#include "../../Manager/Camera.h" +#include "Scene/SceneRenderer.h" + +using namespace std; +using namespace Manager; + +namespace Renderer { + class PlanarReflectionRenderer final : public BaseRenderer { + public: + PlanarReflectionRenderer(const shared_ptr &contextState, + const shared_ptr &resManager, + const shared_ptr &camera, + const glm::mat4 &projection, + int width, int height); + ~PlanarReflectionRenderer() override; + + void updateRenderers(const vector &renderers); + void render3D(float dt, uint64_t frameId) override; + void beforeRender(MODE mode) override; + void afterRender() override; + void renderShadowMap() override; + void setPlaneZ(float z); + + protected: + shared_ptr contextState; + shared_ptr resourceManager; + shared_ptr camera; + glm::mat4 projection; + vector nodes3d; + vector renderers; + + unsigned int reflectionFBO{}; + unsigned int reflectionTexture{}; + unsigned int depthBuffer{}; + int width; + int height; + float planeZ = -1.0f; + }; +} + +#endif //SNAKE3_PLANARREFLECTIONRENDERER_H diff --git a/Renderer/Opengl/RadarRenderer.cpp b/Renderer/Opengl/RadarRenderer.cpp deleted file mode 100644 index 1c0a4848..00000000 --- a/Renderer/Opengl/RadarRenderer.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include "RadarRenderer.h" - -namespace Renderer { - RadarRenderer::RadarRenderer(Radar *radar, Camera *camera, glm::mat4 proj, - ResourceManager *resManager) : radar(radar), camera(camera) { - resourceManager = resManager; - projection = proj; - model = new RadarModel(radar); - shader = resourceManager->getShader("radarShader"); - frameTexture = resourceManager->getTexture("red_screen.bmp"); - } - - RadarRenderer::~RadarRenderer() { - delete model; - delete radar; - } - - void RadarRenderer::render() { - if (radar->isVisible()) { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_DST_COLOR); - shader->use(); - shader->setMat4("projection", projection); - shader->setInt("texture_diffuse1", 0); - shader->setFloat("alpha", 1.0); - - glLoadIdentity(); - - frameTexture->bind(); - - glm::vec3 position = radar->getPosition(); - glm::vec3 zoom = radar->getZoom(); - - // Initialize matrices - glm::mat4 model = glm::mat4(1.0f); - - model = glm::translate(model, position); - model = glm::scale(model, glm::vec3(zoom.x, zoom.y, zoom.z)); - - shader->setMat4("model", model); - shader->setBool("useMaterial", false); - - auto radarMesh = this->model->getMesh(); - - radarMesh->bind(); - glDrawElements(GL_TRIANGLES, (int) radarMesh->getIndices().size(), GL_UNSIGNED_INT, nullptr); - glDisable(GL_BLEND); - - int index = 0; - radar->updatePositions(); - - for (const auto& radarItem: radar->getItems()) { - if (radarItem.item->isVisible()) { - glLoadIdentity(); - - shader->setBool("useMaterial", true); - shader->setVec3("Color", radarItem.color); - - glm::vec3 position = radarItem.radarPresent->getPosition(); - glm::vec3 zoom = radarItem.radarPresent->getZoom(); - - // Initialize matrices - glm::mat4 model = glm::mat4(1.0f); - - model = glm::translate(model, position); - model = glm::scale(model, glm::vec3(zoom.x, zoom.y, zoom.z)); - - shader->setMat4("model", model); - - glDrawElements(GL_TRIANGLES, (int) radarMesh->getIndices().size(), GL_UNSIGNED_INT, nullptr); - index++; - } - } - } - } - - void RadarRenderer::beforeRender() { - glDepthMask(GL_TRUE); - glDepthFunc(GL_LEQUAL); // change depth function so depth test passes when values are equal to depth buffer's content - } - - void RadarRenderer::afterRender() { - glDisable(GL_BLEND); - glEnable(GL_DEPTH_TEST); - glDepthMask(1); - glDepthFunc(GL_LESS); // set depth function back to default - } - -} // Renderer \ No newline at end of file diff --git a/Renderer/Opengl/RadarRenderer.h b/Renderer/Opengl/RadarRenderer.h deleted file mode 100644 index 081eb0e7..00000000 --- a/Renderer/Opengl/RadarRenderer.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef SNAKE3_RADARRENDERER_H -#define SNAKE3_RADARRENDERER_H - -#include "BaseRenderer.h" -#include "../../ItemsDto/Radar.h" -#include "../../Manager/ResourceManager.h" -#include "../../Manager/ShaderManager.h" -#include "Model/RadarModel.h" - -using namespace Model; - -namespace Renderer { - - class RadarRenderer : public BaseRenderer { - public: - explicit RadarRenderer(Radar* radar, Camera* camera, glm::mat4 proj, ResourceManager* resManager); - ~RadarRenderer() override; - - public: - void render() override; - void beforeRender() override; - void afterRender() override; - void renderShadowMap() override {}; - - protected: - Radar* radar; - RadarModel* model; - ResourceManager* resourceManager; - ShaderManager* shader; - TextureManager* frameTexture; - Camera* camera; - glm::mat4 projection{}; - }; - -} // Renderer - -#endif //SNAKE3_RADARRENDERER_H diff --git a/Renderer/Opengl/RainDropRenderer.cpp b/Renderer/Opengl/RainDropRenderer.cpp index 3ab69bb6..19bb238e 100644 --- a/Renderer/Opengl/RainDropRenderer.cpp +++ b/Renderer/Opengl/RainDropRenderer.cpp @@ -4,9 +4,9 @@ namespace Renderer { RainDropRenderer::RainDropRenderer(BaseItem *item, Camera *camera, glm::mat4 proj, ResourceManager *resourceManager) - : BaseRenderer(item), resourceManager(resourceManager), camera(camera), projection(proj), enable(false) { - baseShader = resourceManager->getShader("rainDrop"); - texture = resourceManager->getTexture("raindrops_nor.png"); + : resourceManager(resourceManager), camera(camera), projection(proj), enable(false) { + baseShader = resourceManager->getShader("rainDrop").get(); + texture = resourceManager->getTexture("raindrops_nor.png").get(); model = new MeshModel(item); } @@ -14,7 +14,7 @@ namespace Renderer { delete model; } - void RainDropRenderer::render() { + void RainDropRenderer::render3D(float dt, uint64_t frameId) { if (enable) { baseShader->use(); baseShader->setMat4("view", camera->getViewMatrix()); @@ -53,10 +53,11 @@ namespace Renderer { } - void RainDropRenderer::beforeRender() { + void RainDropRenderer::beforeRender(MODE mode) { + this->mode = mode; glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE); - auto currentFrame = (float)glfwGetTime(); + const auto currentFrame = static_cast(glfwGetTime()); if (currentFrame > lastFrame) { lastFrame = currentFrame; } @@ -67,7 +68,7 @@ namespace Renderer { glDisable(GL_BLEND); } - void RainDropRenderer::setEnable(bool enable) { + void RainDropRenderer::setEnable(const bool enable) { RainDropRenderer::enable = enable; } } // Renderer \ No newline at end of file diff --git a/Renderer/Opengl/RainDropRenderer.h b/Renderer/Opengl/RainDropRenderer.h index cf626ddb..26db8ead 100644 --- a/Renderer/Opengl/RainDropRenderer.h +++ b/Renderer/Opengl/RainDropRenderer.h @@ -6,6 +6,7 @@ #include "../../Manager/ResourceManager.h" #include "../../Manager/ShaderManager.h" #include "Model/MeshModel.h" +#include "../../Manager/Camera.h" using namespace ItemsDto; using namespace Model; @@ -17,9 +18,9 @@ namespace Renderer { public: RainDropRenderer(BaseItem *item, Camera *camera, glm::mat4 proj, ResourceManager *resourceManager); ~RainDropRenderer() override; - void render() override; + void render3D(float dt, uint64_t frameId) override; void renderShadowMap() override; - void beforeRender() override; + void beforeRender(MODE mode) override; void afterRender() override; void setEnable(bool enable); diff --git a/Renderer/Opengl/RainRenderer.cpp b/Renderer/Opengl/RainRenderer.cpp deleted file mode 100644 index 620993f3..00000000 --- a/Renderer/Opengl/RainRenderer.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include "RainRenderer.h" - -Renderer::RainRenderer::RainRenderer(BaseItem *item, Camera *camera, glm::mat4 proj, ResourceManager *resourceManager) - : BaseRenderer(item), resourceManager(resourceManager), camera(camera), projection(proj), enable(false) { - baseShader = resourceManager->getShader("rain"); - texture = resourceManager->getTexture("rain.jpg"); - model = new RainModel(item, 6000); -} - -Renderer::RainRenderer::~RainRenderer() { - delete model; -} - -void Renderer::RainRenderer::render() { - if (enable) { - baseShader->use(); - for (Particle particle: model->getParticles()) { - if (particle.Position.z >= -2.0f) { - baseShader->setVec2("offset", particle.Position); - baseShader->setVec4("color", particle.Color); - baseShader->setInt("sprite", 0); - baseShader->setMat4("projection", projection); - glm::mat4 tr = glm::mat4(1.0f); - // Transform the matrices to their correct form - tr = glm::translate(tr, {0.0, 0.0, 0.0}); - tr = glm::translate(tr, particle.Position); - tr = glm::rotate(tr, glm::radians(90.0f), {1.0, 0.0, 0.0f}); - baseShader->setMat4("model", tr); - baseShader->setMat4("view", camera->getViewMatrix()); - texture->bind(0); - - model->getMesh()->bind(); - glDrawElements(GL_TRIANGLES, (int) model->getMesh()->getIndices().size(), GL_UNSIGNED_INT, nullptr); - glBindVertexArray(0); - } - } - } -} - -void Renderer::RainRenderer::renderShadowMap() { - -} - -void Renderer::RainRenderer::beforeRender() { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE); -// glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_DST_COLOR); - auto currentFrame = (float)glfwGetTime(); - deltaTime = currentFrame - lastFrame; - if (currentFrame > lastFrame) { - item->setPosition({0.0, 0, -2.5}); - model->update(deltaTime, 130); - lastFrame = currentFrame; - } -} - -void Renderer::RainRenderer::afterRender() { - // don't forget to reset to default blending mode - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDisable(GL_BLEND); -} - -void Renderer::RainRenderer::toggle() { - enable = !enable; -} - -bool Renderer::RainRenderer::isEnable() const { - return enable; -} diff --git a/Renderer/Opengl/RainRenderer.h b/Renderer/Opengl/RainRenderer.h deleted file mode 100644 index 9ea2966a..00000000 --- a/Renderer/Opengl/RainRenderer.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef SNAKE3_RAINRENDERER_H -#define SNAKE3_RAINRENDERER_H - -#include "../../ItemsDto/BaseItem.h" -#include "../../Manager/ResourceManager.h" -#include "../../Manager/ShaderManager.h" -#include "Model/RainModel.h" -#include "BaseRenderer.h" - -using namespace ItemsDto; -using namespace Model; -using namespace Manager; - -namespace Renderer { - class RainRenderer : public BaseRenderer { - public: - RainRenderer(BaseItem *item, Camera *camera, glm::mat4 proj, ResourceManager *resourceManager); - ~RainRenderer() override; - void render() override; - void renderShadowMap() override; - void beforeRender() override; - void afterRender() override; - void toggle(); - [[nodiscard]] bool isEnable() const; - - protected: - ResourceManager *resourceManager; - ShaderManager *baseShader; - Camera *camera; - RainModel* model; - TextureManager* texture; - glm::mat4 projection{}; - float deltaTime = 0.0f; - float lastFrame = 0.0f; - bool enable; - }; -} - -#endif //SNAKE3_RAINRENDERER_H diff --git a/Renderer/Opengl/Scene/Scene.cpp b/Renderer/Opengl/Scene/Scene.cpp new file mode 100644 index 00000000..145456ef --- /dev/null +++ b/Renderer/Opengl/Scene/Scene.cpp @@ -0,0 +1,105 @@ +#include "Scene.h" + +namespace Scenes { + Scene::Scene( + const shared_ptr &directionalLight, + const vector > &spotLights, + const vector > &pointLights, + const shared_ptr &rendererManager, + const shared_ptr &camera, const glm::mat4 &projection, + const shared_ptr &rm, const int width, const int height) + : resourceManager(rm), rendererManager(rendererManager), camera(camera), + projection(projection), width(width), height(height) { + this->directionalLight = directionalLight; + if (directionalLight == nullptr) { + this->directionalLight = make_shared(); + this->directionalLight->setPosition({0.0f, 0.0f, 0.0f}); + this->directionalLight->setDirection({-0.410001, -0.82, 0.84}); + this->directionalLight->setAmbient({0.07f, 0.07f, 0.07f}); + //this->directionalLight->setDiffuse({0.0f, 0.0f, 0.0f}); + this->directionalLight->setDiffuse({0.01f, 0.01f, 0.01f}); + this->directionalLight->setSpecular({.051f, .051f, .051f}); + } + + this->spotLights = spotLights; + this->pointLights = pointLights; + + keyboardManager = make_unique(); + sceneRenderer = make_shared(camera, projection, width, height); + contextState = rendererManager->getContextState(); + } + + void Scene::init(const int priority) { + positionHandler = make_shared(camera); + keyboardManager->addEventHandler(positionHandler); + rendererManager->addRenderer(sceneRenderer, priority); + rendererManager->updateDirectionalLight(directionalLight); + } + + Scene::~Scene() { + meshNode3d.clear(); + } + + void Scene::update() { + keyboardManager->runDefault(); + if (collisionSystem != nullptr) { + collisionSystem->update(); + } + sceneRenderer->update(meshNode3d, meshNode2d); + for (const auto &node: nodes) { + node->update(); + } + } + + void Scene::render() { + static float lastFrame = 0.0f; + const auto currentFrame = static_cast(glfwGetTime()); + float deltaTime = currentFrame - lastFrame; + lastFrame = currentFrame; + deltaTime = std::min(deltaTime, 0.05f); + + rendererManager->render(deltaTime); + } + + void Scene::addNode(const std::shared_ptr &node) { + node->parent = shared_from_this(); + node->depth = this->depth + 1; + if (this->depth > 10) { + throw std::runtime_error("Depth limit reached. Maximum nesting scene nodes is 10"); + } + nodes.push_back(node); + } + + void Scene::keyboardInput(GLFWwindow *window, const int keyCode, const int scancode, const int action, + const int mods) const { + keyboardManager->onKeyPress(keyCode, scancode, action, mods); + for (const auto &node: nodes) { + node->keyboardInput(window, keyCode, scancode, action, mods); + } + } + + void Scene::addMeshNode3D(shared_ptr node, const int priority) { + meshNode3d.push_back({std::move(node), priority}); + ranges::stable_sort(meshNode3d, + [](auto &a, auto &b) { return a.priority > b.priority; }); + } + + void Scene::addMeshNode2D(shared_ptr node, const int priority) { + meshNode2d.push_back({std::move(node), priority}); + ranges::stable_sort(meshNode2d, + [](auto &a, auto &b) { return a.priority > b.priority; }); + } + + vector Scene::getAllMeshNodes3D() const { + vector allNodes = meshNode3d; + for (const auto &node : nodes) { + auto childNodes = node->getAllMeshNodes3D(); + allNodes.insert(allNodes.end(), childNodes.begin(), childNodes.end()); + } + return allNodes; + } + + void Scene::setCollisionSystem(const shared_ptr &collisionSystem) { + this->collisionSystem = collisionSystem; + } +} // Scene diff --git a/Renderer/Opengl/Scene/Scene.h b/Renderer/Opengl/Scene/Scene.h new file mode 100644 index 00000000..4128ae8d --- /dev/null +++ b/Renderer/Opengl/Scene/Scene.h @@ -0,0 +1,75 @@ +#ifndef SNAKE3_SCENE_H +#define SNAKE3_SCENE_H + +#include +#include +#include "SceneRenderer.h" +#include "../../../Handler/Debug/PositionHandler.h" +#include "../../../Manager/KeyboardManager.h" +#include "../../../Manager/RenderManager.h" +#include "../../../Manager/ResourceManager.h" +#include "../../../Physic/CollisionSystem3D.h" + +using namespace std; +using namespace Model; +using namespace Manager; + +namespace Scenes { + class Scene : public enable_shared_from_this { + public: + virtual ~Scene(); + + explicit Scene( + const shared_ptr &directionalLight, + const vector > &spotLights, + const vector > &pointLights, + const shared_ptr &rendererManager, + const shared_ptr &camera, const glm::mat4 &projection, + const shared_ptr &rm, int width, int height); + + virtual void init(int priority); + + virtual void update(); + + virtual void render(); + + void addNode(const std::shared_ptr& node); + + virtual void keyboardInput(GLFWwindow *window, int keyCode, int scancode, int action, int mods) const; + + void addMeshNode3D(shared_ptr node, int priority = 0); + void addMeshNode2D(shared_ptr node, int priority = 0); + + vector getAllMeshNodes3D() const; + + void setEnvironment(const shared_ptr &environment) { + this->environment = environment; + } + + void setCollisionSystem(const shared_ptr &collisionSystem); + + protected: + vector meshNode3d; + vector meshNode2d; + shared_ptr environment; + shared_ptr directionalLight; + vector > spotLights; + vector > pointLights; + shared_ptr resourceManager; + shared_ptr sceneRenderer; + shared_ptr rendererManager; + unique_ptr keyboardManager; + shared_ptr camera; + shared_ptr contextState; + shared_ptr positionHandler; + shared_ptr collisionSystem; + glm::mat4 projection; + vector> nodes; + weak_ptr parent; + int depth = 0; + int width; + int height; + }; +} // Scene + +#endif //SNAKE3_SCENE_H diff --git a/Renderer/Opengl/Scene/SceneRenderer.cpp b/Renderer/Opengl/Scene/SceneRenderer.cpp new file mode 100644 index 00000000..a3851cbb --- /dev/null +++ b/Renderer/Opengl/Scene/SceneRenderer.cpp @@ -0,0 +1,53 @@ +#include "SceneRenderer.h" + +namespace Scenes { + SceneRenderer::SceneRenderer(const shared_ptr &camera, const glm::mat4 &projection, int width, int height) + : camera(camera), projection(projection) { + meshNode3DRenderer = make_unique(camera, projection); + meshNode2DRenderer = make_unique(camera, width, height); + } + + SceneRenderer::~SceneRenderer() = default; + + void SceneRenderer::update(const vector &nodes, const vector &nodes2d) { + this->nodes2d = nodes2d; + this->nodes3d = nodes; + } + + void SceneRenderer::render3D(const float dt, const uint64_t frameId) { + for (auto &node : nodes3d) { + this->meshNode3DRenderer->beforeRender(this->mode); + this->meshNode3DRenderer->setRootNode(node.node); + this->meshNode3DRenderer->render3D(dt, frameId); + } + } + + void SceneRenderer::render2D(const float dt, const uint64_t frameId) { + BaseRenderer::render2D(dt, frameId); + + for (auto &node : nodes2d) { + this->meshNode2DRenderer->beforeRender(this->mode); + this->meshNode2DRenderer->setRootNode(node.node); + this->meshNode2DRenderer->render2D(dt, frameId); + } + } + + void SceneRenderer::renderShadowMap() { + for (auto &node : nodes3d) { + this->meshNode3DRenderer->setRootNode(node.node); + this->meshNode3DRenderer->renderShadowMap(); + } + } + + void SceneRenderer::beforeRender(const MODE mode) { + this->mode = mode; + } + + void SceneRenderer::afterRender() { + } + + void SceneRenderer::setShadow(const bool shadow) { + BaseRenderer::setShadow(shadow); + this->meshNode3DRenderer->setShadow(shadow); + } +} // Scene diff --git a/Renderer/Opengl/Scene/SceneRenderer.h b/Renderer/Opengl/Scene/SceneRenderer.h new file mode 100644 index 00000000..5ef8a806 --- /dev/null +++ b/Renderer/Opengl/Scene/SceneRenderer.h @@ -0,0 +1,58 @@ +#ifndef SNAKE3_SCENERENDERER_H +#define SNAKE3_SCENERENDERER_H + +#include +#include +#include +#include "../Model/Standard/StandardMesh.h" +#include "../../../Manager/Camera.h" +#include "../BaseRenderer.h" +#include "../Node2DRenderer.h" +#include "../Node3DRenderer.h" +#include "../Model/Standard/2D/MeshNode2D.h" + +using namespace std; +using namespace Renderer; +using namespace Model; +using namespace Manager; + +namespace Scenes { + struct RendererEntry3D { + shared_ptr node; + int priority; + }; + struct RendererEntry2D { + shared_ptr node; + int priority; + }; + class SceneRenderer final : public BaseRenderer { + public: + SceneRenderer(const shared_ptr &camera, const glm::mat4 &projection, int width, int height); + + ~SceneRenderer() override; + + void update(const vector &nodes, const vector &nodes2d); + + void render3D(float dt, uint64_t frameId) override; + + void render2D(float dt, uint64_t frameId) override; + + void renderShadowMap() override; + + void beforeRender(MODE mode) override; + + void afterRender() override; + + void setShadow(bool shadow) override; + + private: + shared_ptr camera; + glm::mat4 projection; + vector nodes3d; + vector nodes2d; + unique_ptr meshNode3DRenderer; + unique_ptr meshNode2DRenderer; + }; +} // Scene + +#endif //SNAKE3_SCENERENDERER_H diff --git a/Renderer/Opengl/SkyboxRenderer.cpp b/Renderer/Opengl/SkyboxRenderer.cpp deleted file mode 100644 index 5cefe937..00000000 --- a/Renderer/Opengl/SkyboxRenderer.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "SkyboxRenderer.h" - -namespace Renderer { - SkyboxRenderer::SkyboxRenderer(Cube *cube, Camera *camera, const glm::mat4 &projection, - ResourceManager *resourceManager) : cube(cube), camera(camera), - projection(projection), - resourceManager(resourceManager) { - mesh = resourceManager->getModel("cube")->getMesh(); - shader = resourceManager->getShader("skyboxShader"); - texture = resourceManager->getTexture("skybox"); - } - - void SkyboxRenderer::render() { - if (cube->isVisible()) { - shader->use(); - shader->setInt("skybox", 0); - - glm::mat4 view = camera->getViewMatrix(); - view = glm::mat4(glm::mat3(camera->getViewMatrix())); // remove translation from the view matrix - shader->setMat4("view", view); - shader->setVec3("viewPos", camera->getPosition()); - shader->setMat4("projection", projection); - - // skybox cube - mesh->bind(); - texture->cubeBind(); - glDrawElements(GL_TRIANGLES, (int) mesh->getIndices().size(), GL_UNSIGNED_INT, nullptr); - glBindVertexArray(0); - } - } - - void SkyboxRenderer::beforeRender() { - glEnable(GL_DEPTH_TEST); - glDepthMask(GL_TRUE); - glDisable(GL_BLEND); - glDepthFunc(GL_LEQUAL); // change depth function so depth test passes when values are equal to depth buffer's content - } - - void SkyboxRenderer::afterRender() { - glDepthFunc(GL_LESS); // set depth function back to default - } -} // Renderer \ No newline at end of file diff --git a/Renderer/Opengl/SkyboxRenderer.h b/Renderer/Opengl/SkyboxRenderer.h deleted file mode 100644 index 7a02b36b..00000000 --- a/Renderer/Opengl/SkyboxRenderer.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef SNAKE3_SKYBOXRENDERER_H -#define SNAKE3_SKYBOXRENDERER_H - -#include -#include -#include "BaseRenderer.h" -#include "../../ItemsDto/Cube.h" -#include "../../Manager/ShaderManager.h" -#include "../../Manager/Camera.h" -#include "../../Manager/ResourceManager.h" -#include - -using namespace std; -using namespace Manager; - -namespace Renderer { - - class SkyboxRenderer : public BaseRenderer { - public: - SkyboxRenderer(Cube *cube, Camera *camera, const glm::mat4 &projection, - ResourceManager *resourceManager); - - void render() override; - void beforeRender() override; - void afterRender() override; - void renderShadowMap() override {}; - protected: - Cube* cube; - Mesh* mesh; - Camera* camera; - glm::mat4 projection; - ResourceManager* resourceManager; - ShaderManager* shader; - TextureManager* texture; - }; - -} // Renderer - -#endif //SNAKE3_SKYBOXRENDERER_H diff --git a/Renderer/Opengl/SnakeRenderer.cpp b/Renderer/Opengl/SnakeRenderer.cpp deleted file mode 100644 index 31b46777..00000000 --- a/Renderer/Opengl/SnakeRenderer.cpp +++ /dev/null @@ -1,101 +0,0 @@ -#include "SnakeRenderer.h" - -namespace Renderer { - - SnakeRenderer::SnakeRenderer(Snake *snake, Camera *camera, const glm::mat4 &projection, ResourceManager *resManager) - : snake(snake), camera(camera), projection(projection), resourceManager(resManager), blur(false), renderStyle(2) { - mesh = (*resourceManager->getAnimationModel("tile")->getMeshes().begin()); - baseShader = resourceManager->getShader("basicShader"); - shadowShader = resourceManager->getShader("shadowDepthShader"); - shaderLight = resourceManager->getShader("bloomLight"); - snakeTileTexture = resourceManager->getTexture("snake.bmp"); - snakeHeadTexture = resourceManager->getTexture("head.bmp"); - } - - SnakeRenderer::~SnakeRenderer() { - delete snake; - } - - void SnakeRenderer::render() { - if (blur) { - shaderLight->use(); - shaderLight->setMat4("projection", projection); - shaderLight->setMat4("view", camera->getViewMatrix()); - renderScene(shaderLight); - } else { - baseShader->use(); - baseShader->setMat4("view", camera->getViewMatrix()); - baseShader->setMat4("projection", projection); - baseShader->setVec3("viewPos", camera->getPosition()); - baseShader->setBool("useMaterial", false); - baseShader->setBool("useBones", false); - renderScene(baseShader); - } - baseShader->setBool("fogEnable", fog); - } - - void SnakeRenderer::renderShadowMap() { - shadowShader->use(); - renderScene(shadowShader); - } - - void SnakeRenderer::renderScene(ShaderManager *shader) { - for (auto snakeTileIter = snake->getItems().end()-1; snakeTileIter >= snake->getItems().begin(); snakeTileIter--) { - if ((*snakeTileIter)->tile->isVisible()) { - glLoadIdentity(); - - if (snakeTileIter == this->snake->getItems().begin()) { - snakeTileTexture->bind(); - } else { - if (renderStyle == 2) { - baseShader->setBool("useMaterial", true); - } else { - snakeHeadTexture->bind(); - } - } - - glm::vec3 position = (*snakeTileIter)->tile->getPosition(); - glm::mat4 model = glm::mat4(1.0f); - model = glm::translate(model, {0.0, 0.0, 0.0}); - if (snakeTileIter == this->snake->getItems().begin()) { - model = glm::scale(model, {0.041667f, 0.041667f, 0.041667f}); - } else { - model = glm::scale(model, {0.041666667f, 0.041666667f, 0.041666667f}); - } - model = glm::translate(model, position); - - shader->setMat4("model", model); - if (blur) { - if (snakeTileIter == this->snake->getItems().begin()) { - shader->setVec3("lightColor", {10.0f, 0.0f, 0.0f}); - } else { - shader->setVec3("lightColor", {0.0f, 5.0f, 0.0f}); - } - } - mesh->bind(); - - glDrawElements(GL_TRIANGLES, (int) mesh->getIndices().size(), GL_UNSIGNED_INT, nullptr); - } - } - } - - void SnakeRenderer::beforeRender() { - } - - void SnakeRenderer::afterRender() { - } - - void SnakeRenderer::toggleBlur() { - blur = !blur; - } - - void SnakeRenderer::toggleStyle(int style) { - if (style == 1) { - mesh = resourceManager->getModel("cube")->getMesh(); - renderStyle = 1; - } else { - mesh = (*resourceManager->getAnimationModel("tile")->getMeshes().begin()); - renderStyle = 2; - } - } -} // Renderer \ No newline at end of file diff --git a/Renderer/Opengl/SnakeRenderer.h b/Renderer/Opengl/SnakeRenderer.h deleted file mode 100644 index 04a02c05..00000000 --- a/Renderer/Opengl/SnakeRenderer.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef SNAKE3_SNAKERENDERER_H -#define SNAKE3_SNAKERENDERER_H - -#include "BaseRenderer.h" -#include "../../ItemsDto/Snake.h" -#include "../../Manager/ShaderManager.h" -#include "../../Manager/Camera.h" -#include "../../Manager/ResourceManager.h" - -using namespace Manager; - -namespace Renderer { - - class SnakeRenderer : public BaseRenderer { - public: - SnakeRenderer(Snake *snake, Camera *camera, const glm::mat4 &projection, ResourceManager* resManager); - ~SnakeRenderer() override; - void render() override; - void renderShadowMap() override; - void beforeRender() override; - void afterRender() override; - void toggleBlur(); - void toggleStyle(int style); - - protected: - void renderScene(ShaderManager* shader); - Snake* snake; - Camera* camera; - glm::mat4 projection; - ShaderManager* baseShader; - ShaderManager* shadowShader; - ShaderManager* shaderLight; - TextureManager* snakeTileTexture; - TextureManager* snakeHeadTexture; - ResourceManager* resourceManager; - Mesh* mesh; - bool blur; - int renderStyle; - }; - -} // Renderer - -#endif //SNAKE3_SNAKERENDERER_H diff --git a/Renderer/Opengl/TextRenderer.cpp b/Renderer/Opengl/TextRenderer.cpp deleted file mode 100644 index a0c3459b..00000000 --- a/Renderer/Opengl/TextRenderer.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "TextRenderer.h" -#include -#include FT_FREETYPE_H - -namespace Renderer { - TextRenderer::TextRenderer(int width, int height) { - this->width = width; - this->height = height; - } - - TextRenderer::~TextRenderer() { - release(); - } - - void TextRenderer::render() { - for (auto Iter = texts.begin(); Iter < texts.end(); Iter++) { - auto text = (*Iter)->getText(); - if (text->isVisible()) { - (*Iter)->render(); - if (text->isStartFade()) { - text->fadeStep(); - } - } - } - } - - void TextRenderer::beforeRender() { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - - void TextRenderer::afterRender() { - glDisable(GL_BLEND); - } - - void TextRenderer::addText(ItemsDto::Text *text, ShaderManager* shader) { - auto textModel = new Model::TextModel(width, height, shader); - textModel->load(text); - texts.push_back(textModel); - } - - void TextRenderer::release() { - for (auto Iter = texts.begin(); Iter < texts.end(); Iter++) { - delete (*Iter); - } - - texts.clear(); - } - -} // Renderer \ No newline at end of file diff --git a/Renderer/Opengl/TextRenderer.h b/Renderer/Opengl/TextRenderer.h deleted file mode 100644 index 553cd4d8..00000000 --- a/Renderer/Opengl/TextRenderer.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef SNAKE3_TEXTRENDERER_H -#define SNAKE3_TEXTRENDERER_H - -#include -#include -#include "BaseRenderer.h" -#include "../../ItemsDto/Text.h" -#include "../../Manager/ShaderManager.h" -#include "Model/TextModel.h" -#include - -using namespace std; -using namespace Manager; - -namespace Renderer { - - class TextRenderer : public BaseRenderer { - public: - TextRenderer(int width, int height); - ~TextRenderer() override; - void render() override; - void beforeRender() override; - void afterRender() override; - void addText(ItemsDto::Text* text, ShaderManager* shader); - void renderShadowMap() override {}; - - protected: - void release(); - vector texts; - int width; - int height; - }; - -} // Renderer - -#endif //SNAKE3_TEXTRENDERER_H diff --git a/Renderer/Opengl/TorchRenderer.cpp b/Renderer/Opengl/TorchRenderer.cpp new file mode 100644 index 00000000..1834b24a --- /dev/null +++ b/Renderer/Opengl/TorchRenderer.cpp @@ -0,0 +1,799 @@ +#include "TorchRenderer.h" + +using namespace ItemsDto; +using namespace Manager; + +namespace Renderer { + TorchRenderer::TorchRenderer(Cube *cube, Camera *camera, const glm::mat4 &projection, + ResourceManager *resManager): cube(cube), camera(camera), + projection(projection), + resourceManager(resManager) { + //mesh = resourceManager->getModel("torch")->getMesh(); + baseShader = resourceManager->getShader("normalShader").get(); + texture = resourceManager->getTexture("torch.png").get(); + texture2 = resourceManager->getTexture("torch_normal.png").get(); + + constexpr float quad[] = { + -1.0f, -1.0f, + 1.0f, -1.0f, + -1.0f, 1.0f, + 1.0f, 1.0f, + }; + glGenVertexArrays(1, &quadVAO); + glGenBuffers(1, &quadVBO); + glBindVertexArray(quadVAO); + glBindBuffer(GL_ARRAY_BUFFER, quadVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void *) 0); + glBindVertexArray(0); + + // Linie od originu po jednotku na ose + trojúhelníková špička + std::vector gizmoVerts = { + // X osa (hřídel) + {0, 0, 0}, {1, 0, 0}, + // Y osa + {0, 0, 0}, {0, 1, 0}, + // Z osa + {0, 0, 0}, {0, 0, 1}, + }; + + glGenVertexArrays(1, &gizmoVAO); + glGenBuffers(1, &gizmoVBO); + glBindVertexArray(gizmoVAO); + glBindBuffer(GL_ARRAY_BUFFER, gizmoVBO); + glBufferData(GL_ARRAY_BUFFER, gizmoVerts.size() * sizeof(glm::vec3), gizmoVerts.data(), GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(glm::vec3), static_cast(nullptr)); + glBindVertexArray(0); + + // X + auto circleX = makeCircleForAxis(glm::vec3(1, 0, 0), 64); + glGenVertexArrays(1, &circleVAOX); + glGenBuffers(1, &circleVBOX); + glBindVertexArray(circleVAOX); + glBindBuffer(GL_ARRAY_BUFFER, circleVBOX); + glBufferData(GL_ARRAY_BUFFER, circleX.size() * sizeof(glm::vec3), circleX.data(), GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3,GL_FLOAT,GL_FALSE, sizeof(glm::vec3), (void *) 0); + + // Y + auto circleY = makeCircleForAxis(glm::vec3(0, 1, 0), 64); + glGenVertexArrays(1, &circleVAOY); + glGenBuffers(1, &circleVBOY); + glBindVertexArray(circleVAOY); + glBindBuffer(GL_ARRAY_BUFFER, circleVBOY); + glBufferData(GL_ARRAY_BUFFER, circleY.size() * sizeof(glm::vec3), circleY.data(), GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3,GL_FLOAT,GL_FALSE, sizeof(glm::vec3), (void *) 0); + + // Z + auto circleZ = makeCircleForAxis(glm::vec3(0, 0, 1), 64); + glGenVertexArrays(1, &circleVAOZ); + glGenBuffers(1, &circleVBOZ); + glBindVertexArray(circleVAOZ); + glBindBuffer(GL_ARRAY_BUFFER, circleVBOZ); + glBufferData(GL_ARRAY_BUFFER, circleZ.size() * sizeof(glm::vec3), circleZ.data(), GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3,GL_FLOAT,GL_FALSE, sizeof(glm::vec3), (void *) 0); + + glBindVertexArray(0); + } + + TorchRenderer::~TorchRenderer() = default; + + void TorchRenderer::render3D(float dt) { + baseShader->use(); + baseShader->setMat4("view", camera->getViewMatrix()); + baseShader->setMat4("projection", projection); + baseShader->setInt("diffuseMap", 0); + baseShader->setInt("normalMap", 1); + baseShader->setInt("specularMap", 2); + baseShader->setFloat("alpha", 1.0); + baseShader->setVec3("viewPos", camera->getPosition()); + baseShader->setBool("parallaxEnable", false); + baseShader->setBool("fogEnable", fog); + texture->bind(0); + texture2->bind(1); + texture->bind(2); + + glm::vec3 lightPos(cube->getPosition().x, cube->getPosition().y - 6, cube->getPosition().z + 5); + baseShader->setVec3("lightPos", lightPos); + + texture->bind(0); + texture2->bind(1); + texture->bind(2); + + renderScene(baseShader); + } + + void TorchRenderer::renderShadowMap() { + } + + void TorchRenderer::beforeRender() { + } + + void TorchRenderer::afterRender() { + } + + void TorchRenderer::renderScene(const ShaderManager *shader) { + glLoadIdentity(); + const glm::mat4 model = cube->getModelMatrix(); + const auto time = static_cast(glfwGetTime()); + const float pulse = 0.5f + 0.5f * sin(time * 4.0f); // osciluje 0..1 + + auto meshVertices = mesh->getVertices(); + + // 1. Lokální AABB + glm::vec3 localMin, localMax; + computeLocalAABB(meshVertices, localMin, localMax); + + // 2. Světový AABB (bere v potaz scale/rotation/translation) + glm::vec3 worldMin, worldMax; + computeWorldAABB(model, localMin, localMax, worldMin, worldMax); + + // 3. Center objektu ve světě + currentWorldCenter = (worldMin + worldMax) * 0.5f; + currentWorldMin = worldMin; + currentWorldMax = worldMax; + + // 4. Ring center: pod objektem (Z-up) — malý offset dolů, aby nebyl v konfliktu + glm::vec3 ringCenter = currentWorldCenter; + ringCenter.z = worldMin.z - 0.01f; + + // float scaleX = glm::length(glm::vec3(model[0])); // první sloupec + // float scaleY = glm::length(glm::vec3(model[1])); + // float scaleZ = glm::length(glm::vec3(model[2])); + // auto extractScale = glm::vec3(scaleX, scaleY, scaleZ); + + // 5. Radius: vezmeme extenty v X/Y z world AABB + float extentX = (worldMax.x - worldMin.x) * 0.7f; + float extentY = (worldMax.y - worldMin.y) * 0.7f; + float radius = glm::max(extentX, extentY) * 1.18f; + + const auto ringShader = resourceManager->getShader("colorShader"); + // bind shader, nastav uniformy + ringShader->use(); + ringShader->setVec3("ringCenter", ringCenter); + ringShader->setMat4("view", camera->getViewMatrix()); + ringShader->setMat4("proj", projection); + ringShader->setFloat("radius", radius); // např. 1.0f nebo podle velikosti objektu + ringShader->setFloat("pulse", pulse); + ringShader->setVec3("color", glm::vec3(1.0f, 1.0f, 0.0f)); // žlutá + + // vykresli billboard + glBindVertexArray(quadVAO); + // můžeš zapnout blending pro jemnost + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glDisable(GL_BLEND); + glBindVertexArray(0); + + glLoadIdentity(); + shader->use(); + shader->setMat4("model", model); + + mesh->bind(); + glDrawElements(GL_TRIANGLES, (int) mesh->getIndices().size(), GL_UNSIGNED_INT, nullptr); + + // glm::vec4 centerClip = projection * camera->getViewMatrix() * glm::vec4(ringCenter, 1.0f); + // glm::vec3 centerNDC = glm::vec3(centerClip) / centerClip.w; + // + // glm::vec3 offsetWorld = ringCenter + glm::vec3(radius, 0.0f, 0.0f); + // glm::vec4 offsetClip = projection * camera->getViewMatrix() * glm::vec4(offsetWorld, 1.0f); + // glm::vec3 offsetNDC = glm::vec3(offsetClip) / offsetClip.w; + // + // float ndcDiff = fabs(offsetNDC.x - centerNDC.x); // polovina průměru v NDC + // float desiredNDC = 0.08f; // chtěný minimální poloměr v NDC (nastav podle toho, jak velké chces) + // if (ndcDiff < desiredNDC) { + // // zvětšíme radius tak, aby v NDC bylo aspoň desiredNDC + // float scaleFactor = desiredNDC / ndcDiff; + // radius *= scaleFactor; + // } + + drawGizmoAxes(currentWorldCenter, camera->getPosition(), camera->getViewMatrix(), projection, 1080); + drawRotationGizmo(currentWorldCenter, camera->getPosition(), camera->getViewMatrix(), projection, 1080, 64); + } + + void TorchRenderer::computeLocalAABB(const std::vector &verts, glm::vec3 &outMin, glm::vec3 &outMax) { + if (verts.empty()) { + outMin = outMax = glm::vec3(0.0f); + return; + } + outMin = verts[0].position; + outMax = verts[0].position; + for (const auto &v: verts) { + outMin = glm::min(outMin, v.position); + outMax = glm::max(outMax, v.position); + } + } + + void TorchRenderer::computeWorldAABB(const glm::mat4 &model, const glm::vec3 &localMin, const glm::vec3 &localMax, + glm::vec3 &outWorldMin, glm::vec3 &outWorldMax) { + glm::vec3 corners[8] = { + {localMin.x, localMin.y, localMin.z}, + {localMax.x, localMin.y, localMin.z}, + {localMin.x, localMax.y, localMin.z}, + {localMax.x, localMax.y, localMin.z}, + {localMin.x, localMin.y, localMax.z}, + {localMax.x, localMin.y, localMax.z}, + {localMin.x, localMax.y, localMax.z}, + {localMax.x, localMax.y, localMax.z}, + }; + glm::vec3 wMin(std::numeric_limits::infinity()); + glm::vec3 wMax(-std::numeric_limits::infinity()); + for (int i = 0; i < 8; ++i) { + glm::vec4 transformed = model * glm::vec4(corners[i], 1.0f); + glm::vec3 p = glm::vec3(transformed); + wMin = glm::min(wMin, p); + wMax = glm::max(wMax, p); + } + outWorldMin = wMin; + outWorldMax = wMax; + } + + // worldCenter = pozice gizma + // cameraPos je pozice kamery ve světě + // view, proj jsou matice + // viewportHeight je výška okna v pixelech + float TorchRenderer::computeGizmoScale(const glm::vec3 &worldCenter, const glm::vec3 &cameraPos, + float viewportHeight, + float desiredPixelSize /*unused*/ ) { + glm::vec3 objectSize = (currentWorldMax - currentWorldMin); + float maxExtent = glm::compMax(objectSize); + + return maxExtent * 1.1f; + } + + void TorchRenderer::drawGizmoAxes(const glm::vec3 &worldCenter, + const glm::vec3 &cameraPos, + const glm::mat4 &view, + const glm::mat4 &proj, + float viewportHeight) { + const auto gizmoShader = resourceManager->getShader("gizmoShader"); + if (!gizmoShader) return; + + const float axisLength = computeGizmoScale(worldCenter, cameraPos, viewportHeight, 80.0f); + + gizmoShader->use(); + gizmoShader->setMat4("view", view); + gizmoShader->setMat4("proj", proj); + gizmoShader->setFloat("thickness", 3.0f); + gizmoShader->setVec2("viewportSize", glm::vec2(1920, viewportHeight)); + gizmoShader->setVec3("gizmoCenter", worldCenter); + gizmoShader->setFloat("scale", 1.0f); + + glBindVertexArray(gizmoVAO); + glBindBuffer(GL_ARRAY_BUFFER, gizmoVBO); + + // Nastavení vertex atributu pro pozice + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(glm::vec3), (void *) 0); + + std::vector vertices; + vertices.push_back({0, 0, 0}); + vertices.push_back({axisLength, 0, 0}); + vertices.push_back({0, 0, 0}); + vertices.push_back({0, axisLength, 0}); + vertices.push_back({0, 0, 0}); + vertices.push_back({0, 0, axisLength}); + + glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), vertices.data(), GL_STATIC_DRAW); + + // Vykreslení čar + gizmoShader->setBool("isArrowHead", false); + for (int i = 0; i < 3; i++) { + gizmoShader->setInt("axisType", i); + + glm::vec3 baseColor = i == 0 ? glm::vec3(1, 0, 0) : (i == 1 ? glm::vec3(0, 1, 0) : glm::vec3(0, 0, 1)); + + // Zvýraznění při hoveru + if (hoveredAxis == Axis::X && i == 0) + baseColor = glm::vec3(1, 0.6f, 0.6f); + + if (hoveredAxis == Axis::Y && i == 1) + baseColor = glm::vec3(0.6f, 0.6f, 1); + + if (hoveredAxis == Axis::Z && i == 2) + baseColor = glm::vec3(0.6f, 0.6f, 1); + + gizmoShader->setVec3("color", baseColor); + glDrawArrays(GL_LINES, i * 2, 2); + } + + // Vykreslení šipek + gizmoShader->setBool("isArrowHead", true); + for (int i = 0; i < 3; i++) { + glm::vec3 baseColor = i == 0 ? glm::vec3(1, 0, 0) : (i == 1 ? glm::vec3(0, 1, 0) : glm::vec3(0, 0, 1)); + // Zvýraznění při hoveru + if ((hoveredAxis == Axis::X || activeAxis == Axis::X) && i == 0) + baseColor = glm::vec3(1, 0.6f, 0.6f); + + if (hoveredAxis == Axis::Y && i == 1) + baseColor = glm::vec3(0.6f, 0.6f, 1); + + if (hoveredAxis == Axis::Z && i == 2) + baseColor = glm::vec3(0.6f, 0.6f, 1); + gizmoShader->setInt("axisType", i); + gizmoShader->setVec3("color", baseColor); + glDrawArrays(GL_LINES, i * 2, 2); + } + + glBindVertexArray(0); + } + + void TorchRenderer::drawRotationGizmo(const glm::vec3 &worldCenter, + const glm::vec3 &cameraPos, + const glm::mat4 &view, + const glm::mat4 &proj, + float viewportHeight, + int circleVertexCount) { + float baseScale = computeGizmoScale(worldCenter, cameraPos, viewportHeight, 80.0f); + float circleScale = baseScale * 0.8f; + + const auto gizmoShader = resourceManager->getShader("gizmoShader"); + gizmoShader->use(); + gizmoShader->setMat4("view", view); + gizmoShader->setMat4("proj", proj); + gizmoShader->setVec3("gizmoCenter", worldCenter); + gizmoShader->setFloat("scale", circleScale); + gizmoShader->setFloat("thickness", 2.0f); + gizmoShader->setVec2("viewportSize", glm::vec2(1080, viewportHeight)); + gizmoShader->setBool("isArrowHead", false); + + // X kruh (gap: přeskočíme pár vrcholů kolem 0 úhlu) + bool hoverX = (hoveredAxis == Axis::RotateX); + bool activeX = (activeAxis == Axis::RotateX); + glm::vec3 baseColorX = activeX + ? glm::vec3(1, 0.3f, 0.3f) + : hoverX + ? glm::vec3(1, 0.6f, 0.6f) + : glm::vec3(1, 0, 0); + gizmoShader->setVec3("color", baseColorX); + int gap = circleVertexCount / 16; // velikost gapu + // vykreslíme dva segmenty: od gap do poloviny a od poloviny+gap do end-gap (dají pocit přerušení) + glBindVertexArray(circleVAOX); + glDrawArrays(GL_LINE_STRIP, gap, circleVertexCount / 2 - gap); + glDrawArrays(GL_LINE_STRIP, circleVertexCount / 2 + gap, circleVertexCount / 2 - gap); + + // Y kruh + bool hoverY = (hoveredAxis == Axis::RotateY); + bool activeY = (activeAxis == Axis::RotateY); + glm::vec3 baseColorY = activeY + ? glm::vec3(0.3f, 1, 0.3f) + : hoverY + ? glm::vec3(0.6f, 1, 0.6f) + : glm::vec3(0, 1, 0); + gizmoShader->setVec3("color", baseColorY); + glBindVertexArray(circleVAOY); + glDrawArrays(GL_LINE_STRIP, gap, circleVertexCount / 2 - gap); + glDrawArrays(GL_LINE_STRIP, circleVertexCount / 2 + gap, circleVertexCount / 2 - gap); + + // Z kruh + bool hoverZ = (hoveredAxis == Axis::RotateZ); + bool activeZ = (activeAxis == Axis::RotateZ); + + glm::vec3 baseColorZ = activeZ + ? glm::vec3(0.3f, 0.3f, 1) + : hoverZ + ? glm::vec3(0.6f, 0.6f, 1) + : glm::vec3(0, 0, 1); + gizmoShader->setVec3("color", baseColorZ); + glBindVertexArray(circleVAOZ); + glDrawArrays(GL_LINE_STRIP, gap, circleVertexCount / 2 - gap); + glDrawArrays(GL_LINE_STRIP, circleVertexCount / 2 + gap, circleVertexCount / 2 - gap); + + glBindVertexArray(0); + } + + // vrátí vertexy kruhu v rovině kolmé na axis (normalized axis: e.g. {1,0,0} for X) + std::vector TorchRenderer::makeCircleForAxis(const glm::vec3 &axis, int segments = 64) { + std::vector verts; + // najdi dvě ortogonální vektory k axis pro parametrizaci kruhu + glm::vec3 up = glm::abs(axis.y) < 0.99f ? glm::vec3(0, 1, 0) : glm::vec3(1, 0, 0); + glm::vec3 tangent = glm::normalize(glm::cross(axis, up)); + glm::vec3 bitangent = glm::normalize(glm::cross(axis, tangent)); + + for (int i = 0; i <= segments; ++i) { + float theta = (float) i / (float) segments * glm::two_pi(); + float c = cos(theta); + float s = sin(theta); + glm::vec3 point = tangent * c + bitangent * s; // leží v rovině kolmé na axis + verts.push_back(point); // bude škálováno a posouváno při kreslení + } + return verts; + } + + glm::vec3 TorchRenderer::screenToWorldRay(const glm::vec2 &screenPos, const glm::mat4 &view, const glm::mat4 &proj, + int viewportWidth, int viewportHeight) { + float x = (2.0f * screenPos.x) / viewportWidth - 1.0f; + float y = 1.0f - (2.0f * screenPos.y) / viewportHeight; + glm::vec4 clip = glm::vec4(x, y, -1.0f, 1.0f); + glm::vec4 eye = glm::inverse(proj) * clip; + eye.z = -1.0f; + eye.w = 0.0f; + glm::vec4 worldDir4 = glm::inverse(view) * eye; + glm::vec3 worldDir = glm::normalize(glm::vec3(worldDir4)); + + return worldDir; + } + + bool TorchRenderer::closestPointsBetweenLines(const glm::vec3 &p1, const glm::vec3 &d1, + const glm::vec3 &p2, const glm::vec3 &d2, + glm::vec3 &outPoint1, glm::vec3 &outPoint2) { + float a = glm::dot(d1, d1); + float b = glm::dot(d1, d2); + float c = glm::dot(d2, d2); + glm::vec3 r = p1 - p2; + float d = glm::dot(d1, r); + float e = glm::dot(d2, r); + float denom = a * c - b * b; + if (fabs(denom) < 1e-6f) { + // téměř rovnoběžné: použij projekci p1 na osu2 jako aproximaci + outPoint1 = p1; + float t = glm::dot(d2, p1 - p2) / glm::dot(d2, d2); + outPoint2 = p2 + d2 * t; + return false; + } + float s = (b * e - c * d) / denom; + float t = (a * e - b * d) / denom; + outPoint1 = p1 + d1 * s; + outPoint2 = p2 + d2 * t; + return true; + } + + void TorchRenderer::updateHover(const glm::vec2 &cursor, int width, int height) { + // glm::vec3 rayDir = screenToWorldRay(cursor, camera->getViewMatrix(), projection, width, height); + // glm::vec3 rayOrigin = camera->getPosition(); + // float gizmoScale = computeGizmoScale(currentWorldCenter, camera->getPosition(), height); + // Axis hover = pickTranslateAxis(currentWorldCenter, rayOrigin, rayDir, gizmoScale, cursor, width, height, 12.0f); + // hoveredAxis = hover; + glm::mat4 view = camera->getViewMatrix(); + auto rayDir = screenToWorldRay(cursor, view, projection, width, height); + auto rayOrigin = camera->getPosition(); + + float gizmoScale = computeGizmoScale(currentWorldCenter, camera->getPosition(), height, 80.0f); + + // Nejdřív zkusíme translační osy + Axis translationAxis = pickTranslateAxis(currentWorldCenter, rayOrigin, rayDir, + gizmoScale, cursor, width, height, 16.0f); + + if (translationAxis != Axis::None) { + hoveredAxis = translationAxis; + return; + } + + // Pokud není vybrána translační osa, zkusíme rotační + Axis rotationAxis = pickRotationAxis(currentWorldCenter, cursor, width, height); + hoveredAxis = rotationAxis; + } + + TorchRenderer::Axis TorchRenderer::pickTranslateAxis(const glm::vec3 &worldCenter, + const glm::vec3 &rayOrigin, + const glm::vec3 &rayDir, + float gizmoScale, + const glm::vec2 &cursor, // nové: kurzor + int viewportWidth, + int viewportHeight, + float pixelThreshold) { + struct AxisTest { + Axis axis; + glm::vec3 dir; + float screenDist; + float worldDist; + glm::vec3 closestOnAxis; + glm::vec3 segmentStart; + glm::vec3 segmentEnd; + }; + std::vector tests = { + {Axis::X, glm::vec3(1, 0, 0), FLT_MAX, FLT_MAX, {}, {}, {}}, + {Axis::Y, glm::vec3(0, 1, 0), FLT_MAX, FLT_MAX, {}, {}, {}}, + {Axis::Z, glm::vec3(0, 0, 1), FLT_MAX, FLT_MAX, {}, {}, {}} + }; + + float worldThreshold = gizmoScale * 0.25f; // trochu větší tolerance + + for (auto &test: tests) { + test.segmentStart = worldCenter; + test.segmentEnd = worldCenter + test.dir * gizmoScale; + + glm::vec3 pointOnRay, pointOnAxisInfinite; + closestPointsBetweenLines(rayOrigin, rayDir, test.segmentStart, test.dir, pointOnRay, pointOnAxisInfinite); + + glm::vec3 toAxis = pointOnAxisInfinite - test.segmentStart; + float projLen = glm::dot(toAxis, test.dir); + projLen = glm::clamp(projLen, 0.0f, gizmoScale); + glm::vec3 pointOnAxisSegment = test.segmentStart + test.dir * projLen; + test.closestOnAxis = pointOnAxisSegment; + + test.worldDist = glm::length(pointOnRay - pointOnAxisSegment); + + glm::vec2 screenA = worldToScreen(test.segmentStart, camera->getViewMatrix(), projection, viewportWidth, + viewportHeight); + glm::vec2 screenB = worldToScreen(test.segmentEnd, camera->getViewMatrix(), projection, viewportWidth, + viewportHeight); + test.screenDist = pointToSegmentDistance2D(cursor, screenA, screenB); + } + + // primární filtr: musí být blízko v screen-space i world-space + Axis bestAxis = Axis::None; + float bestScreen = FLT_MAX; + for (auto &t: tests) { + if (t.screenDist < pixelThreshold && t.worldDist < worldThreshold) { + if (t.screenDist < bestScreen) { + bestScreen = t.screenDist; + bestAxis = t.axis; + } + } + } + if (bestAxis != Axis::None) { + return bestAxis; + } + + // fallback: kombinované skóre (normalizované) + struct ScoreEntry { + Axis axis; + float score; + }; + std::vector scoreList; + for (auto &t: tests) { + float normScreen = t.screenDist / pixelThreshold; + float normWorld = t.worldDist / worldThreshold; + float combined = normScreen * normScreen + normWorld * normWorld; + scoreList.push_back({t.axis, combined}); + } + std::sort(scoreList.begin(), scoreList.end(), [](const ScoreEntry &a, const ScoreEntry &b) { + return a.score < b.score; + }); + if (!scoreList.empty() && scoreList[0].score < 1.0f) { + return scoreList[0].axis; + } + + // poslední fallback: nejmenší screenDist, i když worldDist bude větší + auto bestScreenIt = std::min_element(tests.begin(), tests.end(), + [](const AxisTest &a, const AxisTest &b) { + return a.screenDist < b.screenDist; + }); + if (bestScreenIt != tests.end() && bestScreenIt->screenDist < pixelThreshold * 2.0f) { + return bestScreenIt->axis; + } + + return Axis::None; + } + + void TorchRenderer::onMouseDown(const glm::vec2 &cursor, int width, int height) { + glm::vec3 rayDir = screenToWorldRay(cursor, camera->getViewMatrix(), projection, width, height); + glm::vec3 rayOrigin = camera->getPosition(); + + float gizmoScale = computeGizmoScale(currentWorldCenter, camera->getPosition(), height, 80.0f); + + Axis picked = pickTranslateAxis(currentWorldCenter, rayOrigin, rayDir, gizmoScale, cursor, width, height, + 16.0f); + Axis pickedRotate = pickRotationAxis(currentWorldCenter, cursor, width, height); + if (picked == Axis::None && pickedRotate == Axis::None) { + return; + } + + if (hoveredAxis == Axis::X || hoveredAxis == Axis::Y || + hoveredAxis == Axis::Z) { + mode = InteractionMode::Translating; + activeAxis = picked; + axisDir = (picked == Axis::X) + ? glm::vec3(1, 0, 0) + : (picked == Axis::Y) + ? glm::vec3(0, 1, 0) + : glm::vec3(0, 0, 1); + axisDir = glm::normalize(axisDir); + + // referenční začátek osy: pozice objektu při startu + dragLineOrigin = cube->getPosition(); + startObjectPos = cube->getPosition(); + + // spočítat nejbližší bod na ose (closestOnAxis) k paprsku -> to je startGrabPoint + glm::vec3 pointOnRay, closestOnAxis; + bool valid = closestPointsBetweenLines(rayOrigin, rayDir, dragLineOrigin, axisDir, pointOnRay, + closestOnAxis); + if (!valid) { + // fallback: projekce rayOrigin na osu + glm::vec3 toOrigin = rayOrigin - dragLineOrigin; + float proj = glm::dot(toOrigin, axisDir); + closestOnAxis = dragLineOrigin + axisDir * proj; + } + + startGrabPoint = closestOnAxis; + // parametr podél osy od dragLineOrigin k místu uchopení + startAxisParam = glm::dot(startGrabPoint - dragLineOrigin, axisDir); + } else if (hoveredAxis == Axis::RotateX || hoveredAxis == Axis::RotateY || + hoveredAxis == Axis::RotateZ) { + mode = InteractionMode::Rotating; + activeAxis = pickedRotate; + + originalRotation[0] = cube->getRotationX(); + originalRotation[1] = cube->getRotationY(); + originalRotation[2] = cube->getRotationZ(); + + // Výpočet počátečního úhlu pro rotaci + glm::vec2 centerScreen = worldToScreen(currentWorldCenter, + camera->getViewMatrix(), + projection, width, height); + glm::vec2 toMouse = cursor - centerScreen; + rotationStartAngle = atan2(toMouse.y, toMouse.x); + } + + + // std::cout << "startGrabPoint: " << vec3_to_string(startGrabPoint) + // << " startObjectPos: " << vec3_to_string(startObjectPos) + // << " startAxisParam: " << startAxisParam << std::endl; + } + + // pomocná: promítne světový bod do screen space (pixelů) + glm::vec3 TorchRenderer::worldToScreen(const glm::vec3 &worldPos, + const glm::mat4 &view, + const glm::mat4 &proj, + int width, + int height) { + glm::vec4 clipSpace = projection * view * glm::vec4(worldPos, 1.0f); + glm::vec3 ndc = glm::vec3(clipSpace) / clipSpace.w; + glm::vec2 screenPos = glm::vec2( + (ndc.x + 1.0f) * 0.5f * width, + (1.0f - ndc.y) * 0.5f * height + ); + + return glm::vec3(screenPos, ndc.z); + } + + void TorchRenderer::onMouseMove(const glm::vec2 &cursor, int width, int height) { + updateHover(cursor, width, height); + if (activeAxis == Axis::None) { + return; + } + + if (mode == InteractionMode::Translating) { + glm::vec3 rayDir = screenToWorldRay(cursor, camera->getViewMatrix(), projection, width, height); + glm::vec3 rayOrigin = camera->getPosition(); + + // najdi aktuální nejbližší bod na nekonečné ose (procházející dragLineOrigin) + glm::vec3 pointOnRay, closestOnAxis; + bool valid = closestPointsBetweenLines(rayOrigin, rayDir, dragLineOrigin, axisDir, pointOnRay, + closestOnAxis); + if (!valid) { + // fallback: projekce rayOrigin na osu + glm::vec3 toOrigin = rayOrigin - dragLineOrigin; + float proj = glm::dot(toOrigin, axisDir); + closestOnAxis = dragLineOrigin + axisDir * proj; + } + + // aktuální parametr podél osy + float currentParam = glm::dot(closestOnAxis - dragLineOrigin, axisDir); + + // Korekce pro citlivost osy podle délky projekce na obrazovku + glm::vec3 axisScreenA = worldToScreen(dragLineOrigin, camera->getViewMatrix(), projection, width, height); + glm::vec3 axisScreenB = worldToScreen(dragLineOrigin + axisDir, camera->getViewMatrix(), projection, width, height); + float axisScreenLength = glm::length(axisScreenB - axisScreenA); + + // Základní délka pro srovnání (např. osa X) + float referenceScreenLength = 100.0f; // experimentuj s hodnotou + float scale = (axisScreenLength > 0.0001f) ? (referenceScreenLength / axisScreenLength) : 1.0f; + float deltaParam = (currentParam - startAxisParam) * scale; + + // float deltaParam = currentParam - startAxisParam; + + // nová pozice objektu: startovní + posun podél osy + glm::vec3 newPos = startObjectPos + axisDir * deltaParam; + cube->setPosition(newPos); + currentWorldCenter = newPos; + } else if (mode == InteractionMode::Rotating) { + glm::vec2 centerScreen = worldToScreen(currentWorldCenter, + camera->getViewMatrix(), + projection, width, height); + glm::vec2 toMouse = cursor - centerScreen; + float currentAngle = atan2(toMouse.y, toMouse.x); + float deltaAngle = currentAngle - rotationStartAngle; + + // Kopírujeme původní rotace + float newRotateX = originalRotation[0]; + float newRotateY = originalRotation[1]; + float newRotateZ = originalRotation[2]; + + // Upravíme příslušnou rotační komponentu + switch (activeAxis) { + case Axis::RotateX: + newRotateX += glm::degrees(deltaAngle); + break; + case Axis::RotateY: + newRotateY += glm::degrees(deltaAngle); + break; + case Axis::RotateZ: + newRotateZ += glm::degrees(deltaAngle); + break; + default: + break; + } + + // Aplikujeme nové rotace + cube->setRotationX(newRotateX); + cube->setRotationY(newRotateY); + cube->setRotationZ(newRotateZ); + } + + // debug výpisy + // std::cout << "closestOnAxis: " << vec3_to_string(closestOnAxis) + // << " deltaParam: " << deltaParam + // << " newPos: " << vec3_to_string(newPos) << std::endl; + } + + void TorchRenderer::onMouseUp() { + mode = InteractionMode::None; + activeAxis = Axis::None; + } + + float TorchRenderer::pointToSegmentDistance2D(const glm::vec2 &p, const glm::vec2 &a, const glm::vec2 &b) { + glm::vec2 ab = b - a; + float t = glm::dot(p - a, ab) / glm::dot(ab, ab); + t = glm::clamp(t, 0.0f, 1.0f); + glm::vec2 projection = a + ab * t; + return glm::length(p - projection); + } + + + TorchRenderer::Axis TorchRenderer::pickRotationAxis(const glm::vec3 &worldCenter, + const glm::vec2 &cursor, + int viewportWidth, + int viewportHeight, + float pixelThreshold) { + glm::mat4 view = camera->getViewMatrix(); + float gizmoScale = computeGizmoScale(worldCenter, camera->getPosition(), viewportHeight, 80.0f); + + // Vytvořit projekce kružnic pro každou osu + std::vector screenPointsX; + std::vector screenPointsY; + std::vector screenPointsZ; + + // Získat body kružnic + auto circleX = makeCircleForAxis(glm::vec3(1, 0, 0), 32); + auto circleY = makeCircleForAxis(glm::vec3(0, 1, 0), 32); + auto circleZ = makeCircleForAxis(glm::vec3(0, 0, 1), 32); + + // Převést body do screen space + for (const auto &point: circleX) { + glm::vec3 worldPoint = worldCenter + point * gizmoScale; + screenPointsX.push_back(worldToScreen(worldPoint, view, projection, viewportWidth, viewportHeight)); + } + for (const auto &point: circleY) { + glm::vec3 worldPoint = worldCenter + point * gizmoScale; + screenPointsY.push_back(worldToScreen(worldPoint, view, projection, viewportWidth, viewportHeight)); + } + for (const auto &point: circleZ) { + glm::vec3 worldPoint = worldCenter + point * gizmoScale; + screenPointsZ.push_back(worldToScreen(worldPoint, view, projection, viewportWidth, viewportHeight)); + } + + // Najít nejbližší vzdálenost pro každou osu + float minDistX = std::numeric_limits::max(); + float minDistY = std::numeric_limits::max(); + float minDistZ = std::numeric_limits::max(); + + // Kontrola vzdálenosti pro každou kružnici + for (size_t i = 0; i < screenPointsX.size(); i++) { + size_t next = (i + 1) % screenPointsX.size(); + minDistX = std::min(minDistX, + pointToSegmentDistance2D(cursor, screenPointsX[i], screenPointsX[next])); + minDistY = std::min(minDistY, + pointToSegmentDistance2D(cursor, screenPointsY[i], screenPointsY[next])); + minDistZ = std::min(minDistZ, + pointToSegmentDistance2D(cursor, screenPointsZ[i], screenPointsZ[next])); + } + + // Najít nejbližší osu + float minDist = std::min({minDistX, minDistY, minDistZ}); + + if (minDist > pixelThreshold) { + return Axis::None; + } + + if (minDist == minDistX) return Axis::RotateX; + if (minDist == minDistY) return Axis::RotateY; + return Axis::RotateZ; + } +} // Renderer diff --git a/Renderer/Opengl/TorchRenderer.h b/Renderer/Opengl/TorchRenderer.h new file mode 100644 index 00000000..88dbcabc --- /dev/null +++ b/Renderer/Opengl/TorchRenderer.h @@ -0,0 +1,111 @@ +#ifndef TORCHRENDERER_H +#define TORCHRENDERER_H + +#include "BaseRenderer.h" +#include "../../ItemsDto/Cube.h" +#include "../../Manager/ResourceManager.h" +#include "../../Manager/ShaderManager.h" +#include "../../Manager/Camera.h" + +using namespace ItemsDto; +using namespace Manager; + +namespace Renderer { + +class TorchRenderer : public BaseRenderer { + + enum class Axis { None, X, Y, Z, RotateX, RotateY, RotateZ }; + + Axis pickTranslateAxis(const glm::vec3 &worldCenter, + const glm::vec3 &rayOrigin, + const glm::vec3 &rayDir, + float gizmoScale, + const glm::vec2 &cursor, + int viewportWidth, + int viewportHeight, + float pixelThreshold = 16.0f); + Axis pickRotationAxis(const glm::vec3 &worldCenter, + const glm::vec2 &cursor, + int viewportWidth, + int viewportHeight, + float pixelThreshold = 16.0f); + enum class InteractionMode { None, Translating, Rotating }; + +public: + TorchRenderer(Cube *cube, Camera *camera, const glm::mat4 &projection, ResourceManager* resManager); + ~TorchRenderer() override; + void render3D(float dt) override; + void renderShadowMap() override; + void beforeRender() override; + void afterRender() override; + + glm::vec3 screenToWorldRay(const glm::vec2& screenPos, const glm::mat4& view, const glm::mat4& proj, + int viewportWidth, int viewportHeight); + // glm::vec3 projectRayOntoAxis(const glm::vec3& rayOrigin, const glm::vec3& rayDir, + // const glm::vec3& axisOrigin, const glm::vec3& axisDir); + void onMouseDown(const glm::vec2& cursor, int width, int height); + void onMouseMove(const glm::vec2& cursor, int width, int height); + void onMouseUp(); +protected: + void renderScene(const ShaderManager* shader); + void computeLocalAABB(const std::vector& verts, glm::vec3& outMin, glm::vec3& outMax); + void computeWorldAABB(const glm::mat4& model, const glm::vec3& localMin, const glm::vec3& localMax, + glm::vec3& outWorldMin, glm::vec3& outWorldMax); + float computeGizmoScale(const glm::vec3& worldCenter, const glm::vec3& cameraPos, + float viewportHeight, + float desiredPixelSize); + void drawGizmoAxes(const glm::vec3& worldCenter, + const glm::vec3& cameraPos, + const glm::mat4& view, + const glm::mat4& proj, + float viewportHeight); + void drawRotationGizmo(const glm::vec3& worldCenter, + const glm::vec3& cameraPos, + const glm::mat4& view, + const glm::mat4& proj, + float viewportHeight, + int circleVertexCount); + std::vector makeCircleForAxis(const glm::vec3& axis, int segments); + bool closestPointsBetweenLines(const glm::vec3& p1, const glm::vec3& d1, + const glm::vec3& p2, const glm::vec3& d2, + glm::vec3& outPoint1, glm::vec3& outPoint2); + glm::vec3 worldToScreen(const glm::vec3 &worldPos, + const glm::mat4 &view, + const glm::mat4 &proj, + int width, + int height); + float pointToSegmentDistance2D(const glm::vec2& p, const glm::vec2& a, const glm::vec2& b); + void updateHover(const glm::vec2 &cursor, int width, int height); + Cube* cube; + Mesh* mesh; + Camera* camera; + glm::mat4 projection; + ResourceManager* resourceManager; + ShaderManager* baseShader; + TextureManager* texture; + TextureManager* texture2; + unsigned int quadVAO, quadVBO, gizmoVAO, gizmoVBO; + unsigned int circleVAOX, circleVBOX; + unsigned int circleVAOY, circleVBOY; + unsigned int circleVAOZ, circleVBOZ; + glm::vec3 currentWorldCenter; + Axis activeAxis = Axis::None; + Axis hoveredAxis = Axis::None; + InteractionMode mode = InteractionMode::None; + float startAxisParam = 0.0f; + float currentAxisParam = 0.0f; + float rotationStartAngle = 0.0f; + float originalRotation[3]; + glm::vec3 dragLineOrigin = {}; + + glm::vec3 startObjectPos; + glm::vec3 startGrabPoint; // projekce kurzorového raye na osu při mouse down + glm::vec3 axisDir; // směr aktuální osy (unit) + float gizmoBaseScale = 1.0f; // Derived from object size (world AABB) + glm::vec3 currentWorldMin; + glm::vec3 currentWorldMax; +}; + +} // Renderer + +#endif //TORCHRENDERER_H diff --git a/Resource/AnimLoader.cpp b/Resource/AnimLoader.cpp index d77ef874..79174a05 100644 --- a/Resource/AnimLoader.cpp +++ b/Resource/AnimLoader.cpp @@ -1,10 +1,22 @@ #include "AnimLoader.h" +#include +#include + +#include "../Renderer/Opengl/Model/Standard/Animation/AnimationPlayer.h" namespace Resource { - shared_ptr AnimLoader::loadObj(const fs::path &path) { + shared_ptr AnimLoader::loadObj(const fs::path &path) { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_GenSmoothNormals | - aiProcess_FlipUVs | aiProcess_CalcTangentSpace); + // Increase smoothing angle to better smooth low-poly assets and join duplicate vertices + importer.SetPropertyFloat(AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE, 80.0f); + constexpr unsigned int pp_flags = aiProcess_Triangulate | + aiProcess_GenSmoothNormals | + aiProcess_FlipUVs | + aiProcess_CalcTangentSpace | + aiProcess_JoinIdenticalVertices | + aiProcess_ImproveCacheLocality | + aiProcess_OptimizeMeshes; + const aiScene *scene = importer.ReadFile(path, pp_flags); // check for errors if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) // if is Not Zero { @@ -12,38 +24,38 @@ namespace Resource { exit(1); } - vector meshes; - vector bones; + vector > meshes; + vector > bones; unordered_map bone_map; - processNode(scene->mRootNode, scene, &meshes, glm::mat4(1.0f), bone_map, bones); + processNode(scene->mRootNode, scene, meshes, glm::mat4(1.0f), bone_map, bones); bones.reserve(bones.size() + 100); auto animations = loadAnimations(scene, bones, bone_map); auto animation_tree = loadAnimationTree(scene, bones, bone_map, animations); - auto global_matrix = convert(scene->mRootNode->mTransformation); + const auto global_matrix = convert(scene->mRootNode->mTransformation); - cout << "Loading OBJ file " << path << " success" << endl; importer.FreeScene(); - return std::make_shared(new BaseItem(), meshes, std::move(animations), std::move(bones), + return std::make_shared(std::move(meshes), std::move(animations), bones, std::move(animation_tree), std::move(bone_map), glm::inverse(global_matrix)); } - std::vector AnimLoader::loadAnimations(const aiScene* scene, std::vector& bones, std::unordered_map& bone_map) { - std::vector animations; + map > AnimLoader::loadAnimations( + const aiScene* scene, std::vector > &bones, const unordered_map& bone_map) { + map > animations; for (uint32_t i = 0; i < scene->mNumAnimations; ++i) { const auto* anim = scene->mAnimations[i]; std::string anim_name(anim->mName.C_Str()); - std::vector anim_nodes; + std::vector > anim_nodes; for (uint32_t j = 0; j < anim->mNumChannels; ++j) { const auto* channel = anim->mChannels[j]; - auto bi = bone_map.find(channel->mNodeName.C_Str()); + // auto bi = bone_map.find(channel->mNodeName.C_Str()); // transformace na objektu bez kosti (pohnu-li v animaci objektem a ne kosti) - zatim nepodporovano // if (bi == bone_map.end()) { // bones.emplace_back("", channel->mNodeName.C_Str(), glm::mat4(1.f)); @@ -70,34 +82,35 @@ namespace Resource { scale_frames.emplace_back(vec, channel->mScalingKeys[k].mTime); } - anim_nodes.emplace_back(pos_frames, rot_frames, scale_frames, bone); + anim_nodes.emplace_back(make_shared(pos_frames, rot_frames, scale_frames, bone)); } - animations.emplace_back(anim_name, anim->mDuration, anim->mTicksPerSecond > 0 ? anim->mTicksPerSecond : 25, anim_nodes); + animations.emplace(anim_name, make_shared( + anim_name, anim->mDuration, anim->mTicksPerSecond > 0 ? anim->mTicksPerSecond : 25, anim_nodes)); } return animations; } - Tree AnimLoader::loadAnimationTree(const aiScene* scene, std::vector& bones, std::unordered_map& bone_map, std::vector& anim) { - auto bone_finder = [&] (const std::string& str, std::vector& anim) { - if (auto bi = bone_map.find(str); bi != bone_map.end()) { + Tree AnimLoader::loadAnimationTree(const aiScene* scene, vector > &bones, + unordered_map& bone_map, map >& anim) { + auto bone_finder = [&] (const std::string& str, map >&) { + if (const auto bi = bone_map.find(str); bi != bone_map.end()) { return bi->second; - } else { - bones.emplace_back(str, "", glm::mat4(1.f)); - bone_map.emplace(str, bones.size() - 1); - return static_cast(bones.size() - 1); } + bones.emplace_back(make_shared(str, "", glm::mat4(1.f))); + bone_map.emplace(str, bones.size() - 1); + return static_cast(bones.size() - 1); }; Tree tree(bone_finder(scene->mRootNode->mName.C_Str(), anim)); - std::function& tree, const aiNode*, int)> dfs; - dfs = [&] (Tree& tree, const aiNode* node, int depth) { - bones[*tree].node_transform = convert(node->mTransformation); + function& tree, const aiNode*, int)> dfs; + dfs = [&] (Tree& treeDfs, const aiNode* node, const int depth) { + bones[*treeDfs]->node_transform = convert(node->mTransformation); for (uint32_t i = 0; i < node->mNumChildren; ++i) { - auto& child = tree.add(bone_finder(node->mChildren[i]->mName.C_Str(), anim)); + auto& child = treeDfs.add(bone_finder(node->mChildren[i]->mName.C_Str(), anim)); dfs(child, node->mChildren[i], depth + 1); } }; @@ -107,15 +120,17 @@ namespace Resource { return tree; } - void AnimLoader::processNode(aiNode *node, const aiScene *scene, vector* meshes, glm::mat4 parentTransformation, unordered_map& bone_map, vector& bones) { - glm::mat4 transformation = AiMatrix4x4ToGlm(&node->mTransformation); - glm::mat4 globalTransformation = parentTransformation * transformation; + void AnimLoader::processNode(const aiNode *node, const aiScene *scene, vector> &meshes, const glm::mat4 &parentTransformation, + unordered_map& bone_map, vector >& bones) + { + const glm::mat4 transformation = AiMatrix4x4ToGlm(&node->mTransformation); + const glm::mat4 globalTransformation = parentTransformation * transformation; for (unsigned int i = 0; i < node->mNumMeshes; i++) { aiMesh *mesh = scene->mMeshes[node->mMeshes[i]]; - Mesh* myMesh = processMesh(mesh, scene, bone_map, bones); + auto myMesh = processMesh(mesh, scene, bone_map, bones); myMesh->setGlobalTransformation(globalTransformation); - meshes->push_back(myMesh); + meshes.push_back(myMesh); } for (unsigned int i = 0; i < node->mNumChildren; i++) { processNode(node->mChildren[i], scene, meshes, globalTransformation, bone_map, bones); @@ -134,7 +149,8 @@ namespace Resource { return to; } - Mesh* AnimLoader::processMesh(aiMesh *mesh, const aiScene *scene, unordered_map& bone_map, vector& bones) { + shared_ptr AnimLoader::processMesh(aiMesh *mesh, const aiScene *scene, unordered_map& bone_map, vector >& bones) { std::vector bone_weights; @@ -148,8 +164,7 @@ namespace Resource { auto offset_mat = convert(mesh->mBones[j]->mOffsetMatrix); uint32_t bone_index; if (bi == bone_map.end()) { -// bones.emplace_back(bone_name, offset_mat); - bones.emplace_back(bone_name, mesh_name, offset_mat); + bones.emplace_back(make_shared(bone_name, mesh_name, offset_mat)); bone_index = bones.size() - 1; bone_map.insert({bone_name, bone_index}); } else { @@ -178,8 +193,7 @@ namespace Resource { // walk through each of the mesh's vertices for (unsigned int i = 0; i < mesh->mNumVertices; i++) { Vertex vertex{}; - glm::vec3 vector; // we declare a placeholder vector since assimp uses its own vector class that doesn't directly convert to glm's vec3 class so we transfer the data to this placeholder glm::vec3 first. - // positions + glm::vec3 vector; vector.x = mesh->mVertices[i].x; vector.y = mesh->mVertices[i].y; vector.z = mesh->mVertices[i].z; @@ -220,10 +234,10 @@ namespace Resource { vertex.Weights[1] = (bone_weights.begin() + i)->weight[1]; vertex.Weights[2] = (bone_weights.begin() + i)->weight[2]; vertex.Weights[3] = (bone_weights.begin() + i)->weight[3]; - vertex.BoneIDs[0] = (int)(bone_weights.begin() + i)->bone_index[0]; - vertex.BoneIDs[1] = (int)(bone_weights.begin() + i)->bone_index[1]; - vertex.BoneIDs[2] = (int)(bone_weights.begin() + i)->bone_index[2]; - vertex.BoneIDs[3] = (int)(bone_weights.begin() + i)->bone_index[3]; + vertex.BoneIDs[0] = static_cast((bone_weights.begin() + i)->bone_index[0]); + vertex.BoneIDs[1] = static_cast((bone_weights.begin() + i)->bone_index[1]); + vertex.BoneIDs[2] = static_cast((bone_weights.begin() + i)->bone_index[2]); + vertex.BoneIDs[3] = static_cast((bone_weights.begin() + i)->bone_index[3]); } vertices.push_back(vertex); } @@ -235,6 +249,39 @@ namespace Resource { indices.push_back(face.mIndices[j]); } + // Fallback: if mesh has no normals (or Assimp failed to generate), compute smooth normals + if (!mesh->HasNormals()) { + // Initialize normals to zero + for (auto &v : vertices) { + v.normal = glm::vec3(0.0f); + } + // Accumulate face normals (area-weighted by triangle area) + for (size_t i = 0; i + 2 < indices.size(); i += 3) { + const auto ia = indices[i]; + const auto ib = indices[i + 1]; + const auto ic = indices[i + 2]; + const glm::vec3 &a = vertices[ia].position; + const glm::vec3 &b = vertices[ib].position; + const glm::vec3 &c = vertices[ic].position; + glm::vec3 n = glm::normalize(glm::cross(b - a, c - a)); + // Use triangle area (length of cross) as weight + float area = glm::length(glm::cross(b - a, c - a)); + if (area > 0.0f) { + vertices[ia].normal += n * area; + vertices[ib].normal += n * area; + vertices[ic].normal += n * area; + } + } + // Normalize accumulated normals + for (auto &v : vertices) { + if (glm::length2(v.normal) > 0.0f) { + v.normal = glm::normalize(v.normal); + } else { + v.normal = glm::vec3(0.0f, 1.0f, 0.0f); + } + } + } + // TODO: implementovat ??? // for (int i = 0; i < material->GetTextureCount(aiTextureType_DIFFUSE); i++) // { @@ -255,6 +302,6 @@ namespace Resource { // std::vector heightMaps = loadMaterialTextures(material, aiTextureType_AMBIENT, "texture_height"); // textures.insert(textures.end(), heightMaps.begin(), heightMaps.end()); - return new Mesh(vertices, indices, mesh->HasBones(), mesh->mName.C_Str()); + return make_shared(vertices, indices, mesh->HasBones(), mesh->mName.C_Str()); } } // Resource \ No newline at end of file diff --git a/Resource/AnimLoader.h b/Resource/AnimLoader.h index c3abc42b..66026343 100644 --- a/Resource/AnimLoader.h +++ b/Resource/AnimLoader.h @@ -1,17 +1,17 @@ #ifndef SNAKE3_ANIMLOADER_H #define SNAKE3_ANIMLOADER_H -#include "../Renderer/Opengl/Model/AnimationModel.h" -#include "../ItemsDto/ObjItem.h" #include "../ItemsDto/AnimItem.h" #include #include +#include #include #include -#include + +#include "../Renderer/Opengl/Model/Standard/Animation/AnimationPlayer.h" using namespace std; -using namespace Model; +using namespace Animations; namespace fs = std::filesystem; @@ -38,14 +38,16 @@ namespace Resource { class AnimLoader { public: - static shared_ptr loadObj(const fs::path &path); + static shared_ptr loadObj(const fs::path &path); protected: - static void processNode(aiNode *node, const aiScene *scene, vector* meshes, glm::mat4 parentTransformation, unordered_map& bone_map, vector& bones); - static Mesh* processMesh(aiMesh *mesh, const aiScene *scene, unordered_map& bone_map, vector& bones); + static void processNode(const aiNode *node, const aiScene *scene, vector> &meshes, const glm::mat4 &parentTransformation, + unordered_map& bone_map, vector >& bones); + static shared_ptr processMesh(aiMesh *mesh, const aiScene *scene, unordered_map& bone_map, vector >& bones); static glm::mat4 AiMatrix4x4ToGlm(const aiMatrix4x4 *from); - static std::vector loadAnimations(const aiScene* scene, std::vector& bones, std::unordered_map& bone_map); - static Tree loadAnimationTree(const aiScene* scene, std::vector& bones, std::unordered_map& bone_map, std::vector& anim); - static std::vector animations2; + static map > loadAnimations(const aiScene* scene, vector>& bones, + const unordered_map& bone_map); + static Tree loadAnimationTree(const aiScene* scene, vector> &bones, unordered_map &bone_map, + map > &anim); }; } // Resource diff --git a/Resource/ObjModelLoader.cpp b/Resource/ObjModelLoader.cpp index 73dca7d5..78fc15bc 100644 --- a/Resource/ObjModelLoader.cpp +++ b/Resource/ObjModelLoader.cpp @@ -1,86 +1,193 @@ +#include +#include + +// Assimp includes +#include +#include +#include + #include "ObjModelLoader.h" -#include "../Thirdparty/tinyobj/tiny_obj_loader.h" +#include "TextureLoader.h" namespace Resource { - shared_ptr ObjModelLoader::loadObj(const fs::path &path) { - std::vector vertexIndices, uvIndices, normalIndices; - std::vector temp_vertices; - std::vector temp_uvs; - std::vector temp_normals; + std::vector loadMaterialTextures(const aiMaterial *mat, const aiTextureType type, + const TextureType typeName, const aiScene *scene) { + std::vector textures; + + for (unsigned int i = 0; i < mat->GetTextureCount(type); i++) { + aiString str; + mat->GetTexture(type, i, &str); + + TextureInfo texture; + texture.type = typeName; + texture.path = str.C_Str(); + + // Pokud cesta začíná hvězdičkou (např. "*0", "*1"), znamená to, + // že textura je EMBEDDED (zabalená) přímo v binárním souboru. + const aiTexture *embeddedTexture = scene->GetEmbeddedTexture(str.C_Str()); + unsigned int dataSize = 0; + if (embeddedTexture->mHeight == 0) { + dataSize = embeddedTexture->mWidth; + } else { + dataSize = embeddedTexture->mWidth * embeddedTexture->mHeight * 4; // 4 bajty na pixel (BGRA) + } + if (embeddedTexture) { + // Zde musíš texturu načíst z paměti (viz vysvětlení pod kódem) + // texture.texture = std::make_shared( + // TextureLoader::bindFromBuffer(embeddedTexture->pcData, embeddedTexture->mWidth)); + texture.texture = std::make_shared(embeddedTexture->pcData, dataSize); + std::cout << "Nalezen embedded texture: " << str.C_Str() << std::endl; + } else { + // Textura je externí soubor na disku + texture.texture = std::make_shared(TextureLoader::loadTexture(str.C_Str())); + std::cout << "Nalezen file texture: " << str.C_Str() << std::endl; + } + + textures.push_back(texture); + } + return textures; + } + + std::vector> processAssimpScene(const aiScene* scene) { + + std::vector> meshes; + + for (unsigned int m = 0; m < scene->mNumMeshes; m++) { + aiMesh* mesh = scene->mMeshes[m]; - tinyobj::ObjReaderConfig reader_config; - reader_config.mtl_search_path = "./"; // Path to material files + std::vector vertices; + std::vector indices; + std::vector textures; - tinyobj::ObjReader reader; + // 1. VRCHOLY (Zůstává stejné jako tvoje, jen bez offsetu, protože každý mesh je teď zvlášť) + for (unsigned int i = 0; i < mesh->mNumVertices; i++) { + Vertex vertex{}; + vertex.position = { mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z }; - if (!reader.ParseFromFile(path, reader_config)) { - if (!reader.Error().empty()) { - std::cerr << "TinyObjReader: " << reader.Error(); + if (mesh->HasNormals()) { + vertex.normal = { mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z }; + } + + if (mesh->mTextureCoords[0]) { + vertex.texUV = { mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y }; + } else { + vertex.texUV = {0.0f, 0.0f}; + } + + if (mesh->HasTangentsAndBitangents()) { + vertex.tangents = { mesh->mTangents[i].x, mesh->mTangents[i].y, mesh->mTangents[i].z }; + } + + vertices.push_back(vertex); } - exit(1); - } - if (!reader.Warning().empty()) { - std::cout << "TinyObjReader: " << reader.Warning(); - } + // 2. INDEXY + for (unsigned int i = 0; i < mesh->mNumFaces; i++) { + aiFace face = mesh->mFaces[i]; + for (unsigned int j = 0; j < face.mNumIndices; j++) { + indices.push_back(face.mIndices[j]); + } + } + + // 3. MATERIÁLY A TEXTURY (To hlavní!) + if (mesh->mMaterialIndex >= 0) { + aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex]; + + // a) Diffuse mapy (Base Color) + // V novějším Assimp a GLTF se Base Color mapuje často jako aiTextureType_BASE_COLOR nebo DIFFUSE + std::vector diffuseMaps = loadMaterialTextures(material, aiTextureType_DIFFUSE, TextureType::Diffuse, scene); + textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end()); + + // b) Normal mapy + std::vector normalMaps = loadMaterialTextures(material, aiTextureType_NORMALS, TextureType::Normal, scene); + textures.insert(textures.end(), normalMaps.begin(), normalMaps.end()); + + // c) EMISSIVE + std::vector emissiveMaps = loadMaterialTextures(material, aiTextureType_EMISSIVE, TextureType::Emissive, scene); + textures.insert(textures.end(), emissiveMaps.begin(), emissiveMaps.end()); - auto& attrib = reader.GetAttrib(); - auto& shapes = reader.GetShapes(); - auto& materials = reader.GetMaterials(); - vector vertices; - vector uvs; - vector normals; - - // Loop over shapes - for (const auto & shape : shapes) { - // Loop over faces(polygon) - size_t index_offset = 0; - for (size_t f = 0; f < shape.mesh.num_face_vertices.size(); f++) { - auto fv = size_t(shape.mesh.num_face_vertices[f]); - - // Loop over vertices in the face. - for (size_t v = 0; v < fv; v++) { - // access to vertex - tinyobj::index_t idx = shape.mesh.indices[index_offset + v]; - tinyobj::real_t vx = attrib.vertices[3*size_t(idx.vertex_index)+0]; - tinyobj::real_t vy = attrib.vertices[3*size_t(idx.vertex_index)+1]; - tinyobj::real_t vz = attrib.vertices[3*size_t(idx.vertex_index)+2]; - - glm::vec3 vertex = {vx, vy, vz}; - vertices.push_back(vertex); - - // Check if `normal_index` is zero or positive. negative = no normal data - if (idx.normal_index >= 0) { - tinyobj::real_t nx = attrib.normals[3*size_t(idx.normal_index)+0]; - tinyobj::real_t ny = attrib.normals[3*size_t(idx.normal_index)+1]; - tinyobj::real_t nz = attrib.normals[3*size_t(idx.normal_index)+2]; - glm::vec3 normal = {nx, ny, nz}; - normals.push_back(normal); - } - - // Check if `texcoord_index` is zero or positive. negative = no texcoord data - if (idx.texcoord_index >= 0) { - tinyobj::real_t tx = attrib.texcoords[2*size_t(idx.texcoord_index)+0]; - tinyobj::real_t ty = attrib.texcoords[2*size_t(idx.texcoord_index)+1]; - glm::vec2 uv = {tx, ty}; - uvs.push_back(uv); - } - - // Optional: vertex colors - // tinyobj::real_t red = attrib.colors[3*size_t(idx.vertex_index)+0]; - // tinyobj::real_t green = attrib.colors[3*size_t(idx.vertex_index)+1]; - // tinyobj::real_t blue = attrib.colors[3*size_t(idx.vertex_index)+2]; + // d) REFLEXION + std::vector pbrMaps = loadMaterialTextures(material, aiTextureType_METALNESS, TextureType::MetalRough, scene); + + // 2. Pokud nic nenašel (což je u GLTF běžné), zkusíme UNKNOWN + if (pbrMaps.empty()) { + // V GLTF je MetalRoughness textura často mapovaná jako UNKNOWN_0 + pbrMaps = loadMaterialTextures(material, aiTextureType_UNKNOWN, TextureType::MetalRough, scene); } - index_offset += fv; - // per-face material - //shape.mesh.material_ids[f]; + textures.insert(textures.end(), pbrMaps.begin(), pbrMaps.end()); + + // aiColor3D color(0.f, 0.f, 0.f); + // if (material->Get(AI_MATKEY_COLOR_EMISSIVE, color) == AI_SUCCESS) { + // const auto meshEmissiveColor = glm::vec3(color.r, color.g, color.b); + // + // if (meshEmissiveColor.r > 0 || meshEmissiveColor.g > 0 || meshEmissiveColor.b > 0) { + // std::cout << "Mesh [" << m << "] sviti barvou: " + // << meshEmissiveColor.r << ", " + // << meshEmissiveColor.g << ", " + // << meshEmissiveColor.b << std::endl; + // } + // } + // tady si emissive color ulozit a pracovat s nim dal + + // Poznámka: PBR Metallic/Roughness je složitější, Assimp to často dává do aiTextureType_UNKNOWN + // nebo specifických PBR flagů, ale pro začátek stačí tyto tři. } + + meshes.push_back(std::make_shared(vertices, indices, textures)); + } + + return meshes; + } + + // Definice flagů pro Assimp: + // aiProcess_Triangulate: Převede polygony na trojúhelníky + // aiProcess_GenSmoothNormals: Vypočítá normály, pokud chybí + // aiProcess_FlipUVs: Otočí Y souřadnici textury (záleží na vašem enginu, v OpenGL často potřeba) + // aiProcess_CalcTangentSpace: Vypočítá Tangenty/Bitangenty (nahrazuje VboIndexer) + // aiProcess_JoinIdenticalVertices: Optimalizuje mesh a vytváří indexy (nahrazuje VboIndexer) + static constexpr unsigned int ASSIMP_FLAGS = + aiProcess_Triangulate | + aiProcess_GenSmoothNormals | + aiProcess_FlipUVs | + aiProcess_CalcTangentSpace | + aiProcess_JoinIdenticalVertices; + + std::vector> ObjModelLoader::loadObj(const fs::path &path) { + Assimp::Importer importer; + + // Načtení souboru + const aiScene* scene = importer.ReadFile(path.string(), ASSIMP_FLAGS); + + // Kontrola chyb + if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) { + std::cerr << "Assimp Error: " << importer.GetErrorString() << std::endl; + exit(1); } - cout << "Loading OBJ file " << path << " success" << endl; + return processAssimpScene(scene); + } + + std::vector> ObjModelLoader::loadObjFromStr(const fs::path &path, const string &str) { + Assimp::Importer importer; + + std::string extension = path.extension().string(); + if(extension.empty()) extension = ".obj"; - return std::make_shared(vertices, uvs, normals); + const aiScene* scene = importer.ReadFileFromMemory( + str.c_str(), + str.length(), + ASSIMP_FLAGS, + extension.c_str() + ); + + if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) { + std::cerr << "Assimp Error (from string): " << importer.GetErrorString() << std::endl; + exit(1); + } + + return processAssimpScene(scene); } + } // Resource \ No newline at end of file diff --git a/Resource/ObjModelLoader.h b/Resource/ObjModelLoader.h index d2cf1517..f6159386 100644 --- a/Resource/ObjModelLoader.h +++ b/Resource/ObjModelLoader.h @@ -2,12 +2,13 @@ #define SNAKE3_OBJMODELLOADER_H #define TINYOBJLOADER_IMPLEMENTATION // define this in only *one* .cc -#include "../ItemsDto/ObjItem.h" #include #include +#include "../Renderer/Opengl/Model/Utils/Mesh.h" + using namespace std; -using namespace ItemsDto; +using namespace ModelUtils; namespace fs = std::filesystem; @@ -15,7 +16,8 @@ namespace Resource { class ObjModelLoader { public: - static shared_ptr loadObj(const fs::path& path); + static vector> loadObj(const fs::path& path); + static vector> loadObjFromStr(const fs::path& path, const string& str); }; } // Resource diff --git a/Resource/ResourceLoader.cpp b/Resource/ResourceLoader.cpp new file mode 100644 index 00000000..40dd6c34 --- /dev/null +++ b/Resource/ResourceLoader.cpp @@ -0,0 +1,197 @@ +#include "ResourceLoader.h" +#include +#include "AnimLoader.h" +#include "ObjModelLoader.h" +#include "ShaderLoader.h" +#include "TextureLoader.h" + +namespace Resource { + ResourceLoader::ResourceLoader() { + worker = std::thread([this]() { workerLoop(); }); + std::thread([this]() { workerLoopAnim(); }).detach(); + std::thread([this]() { workerLoopTexture(); }).detach(); + std::thread([this]() { workerLoopShader(); }).detach(); + } + + ResourceLoader::~ResourceLoader() { + stop(); + } + + void ResourceLoader::stop() { + { + std::lock_guard lock(queueMutex); + running = false; + } + cv.notify_all(); // probudí worker, pokud spí + if (worker.joinable()) + worker.join(); + } + + void ResourceLoader::enqueue(const std::filesystem::path& path, Callback callback) { + { + std::lock_guard lock(queueMutex); + jobs.push({path, std::move(callback)}); + } + cv.notify_one(); + } + + void ResourceLoader::enqueueAnimation(const std::filesystem::path &path, AnimCallback callback) { + { + std::lock_guard lock(queueMutex); + animJobs.push({path, std::move(callback)}); + } + cv.notify_all(); + } + + void ResourceLoader::enqueueTexture(const std::filesystem::path &path, const bool albedo, TextureCallback callback) { + { + std::lock_guard lock(queueMutex); + textureJobs.push({path, albedo, std::move(callback)}); + } + cv.notify_all(); + } + + void ResourceLoader::enqueueShader( + const std::filesystem::path &vertexPath, + const std::filesystem::path &geometryPath, + const std::filesystem::path &fragmentPath, + ShaderCallback callback) { + { + std::lock_guard lock(queueMutex); + shaderJobs.push({vertexPath, geometryPath, fragmentPath, std::move(callback)}); + } + cv.notify_all(); + } + + void ResourceLoader::workerLoop() { + while (true) { + Job job; + + { + std::unique_lock lock(queueMutex); + cv.wait(lock, [this]() { return !jobs.empty() || !running; }); + + if (!running && jobs.empty()) { + break; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + job = jobs.front(); + jobs.pop(); + } + + loadSemaphore.acquire(); + + // Spustíme job asynchronně + std::thread([this, job]() { + try { + const auto obj = ObjModelLoader::loadObj(job.path); + job.callback(obj); + } catch (const std::exception &e) { + std::cerr << "[ResourceLoader] Failed to load " << job.path << ": " << e.what() << "\n"; + } + loadSemaphore.release(); + cv.notify_one(); + }).detach(); + } + } + + void ResourceLoader::workerLoopAnim() { + while (true) { + AnimJob job; + + { + std::unique_lock lock(queueMutex); + cv.wait(lock, [this]() { return !animJobs.empty() || !running; }); + + if (!running && animJobs.empty()) { + break; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + job = animJobs.front(); + animJobs.pop(); + } + + loadSemaphore.acquire(); + std::thread([this, job]() { + try { + const auto anim = AnimLoader::loadObj(job.path); + job.callback(anim); + } catch (const std::exception &e) { + std::cerr << "[ResourceLoader] Failed to load ANIM " << job.path << ": " << e.what() << "\n"; + } + loadSemaphore.release(); + cv.notify_one(); + }).detach(); + } + } + + void ResourceLoader::workerLoopTexture() { + while (true) { + TextureJob job; + + { + std::unique_lock lock(queueMutex); + cv.wait(lock, [this]() { return !textureJobs.empty() || !running; }); + + if (!running && textureJobs.empty()) { + break; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + job = textureJobs.front(); + textureJobs.pop(); + } + + loadSemaphore.acquire(); + std::thread([this, job]() { + try { + const vector data = TextureLoader::loadTextureToBuffer(job.path); + job.callback(data, job.albedo); + } catch (const std::exception &e) { + std::cerr << "[ResourceLoader] Failed to load TEXTURE " << job.path << ": " << e.what() << "\n"; + } + loadSemaphore.release(); + cv.notify_one(); + }).detach(); + } + } + + void ResourceLoader::workerLoopShader() { + while (true) { + ShaderJob job; + + { + std::unique_lock lock(queueMutex); + cv.wait(lock, [this]() { return !shaderJobs.empty() || !running; }); + + if (!running && shaderJobs.empty()) { + break; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + job = shaderJobs.front(); + shaderJobs.pop(); + } + + loadSemaphore.acquire(); + std::thread([this, job]() { + try { + if (job.geometryPath.empty()) { + const fvShader shader = ShaderLoader::loadShaderToBuffer(job.vertexPath, job.fragmentPath); + const vector geom; + job.callback(shader.vertex, shader.fragment, geom); + } else { + const fgvShader shader = ShaderLoader::loadShaderToBuffer(job.vertexPath, job.geometryPath, job.fragmentPath); + job.callback(shader.vertex, shader.fragment, shader.geometry); + } + } catch (const std::exception &e) { + std::cerr << "[ResourceLoader] Failed to load SHADER " << job.vertexPath << ": " << e.what() << "\n"; + } + loadSemaphore.release(); + cv.notify_one(); + }).detach(); + } + } +} // Resource \ No newline at end of file diff --git a/Resource/ResourceLoader.h b/Resource/ResourceLoader.h new file mode 100644 index 00000000..16fdc19a --- /dev/null +++ b/Resource/ResourceLoader.h @@ -0,0 +1,78 @@ +#ifndef SNAKE3_RESOURCELOADER_H +#define SNAKE3_RESOURCELOADER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../Manager/TextureManager.h" +#include "../Renderer/Opengl/Model/Standard/Animation/AnimationPlayer.h" + +using namespace Manager; +using namespace Animations; + +namespace Resource { + class ResourceLoader { + public: + using Callback = std::function>)>; + using AnimCallback = std::function)>; + using TextureCallback = std::function, bool albedo)>; + using ShaderCallback = std::function, vector, vector)>; + + ResourceLoader(); + ~ResourceLoader(); + + void enqueue(const std::filesystem::path& path, Callback callback); + void enqueueAnimation(const std::filesystem::path &path, AnimCallback callback); + void enqueueTexture(const std::filesystem::path &path, bool albedo, TextureCallback callback); + void enqueueShader( + const std::filesystem::path &vertexPath, + const std::filesystem::path &geometryPath, + const std::filesystem::path &fragmentPath, + ShaderCallback callback + ); + void stop(); + private: + static constexpr int MAX_CONCURRENT_LOADS = 1; + struct Job { + std::filesystem::path path; + Callback callback; + }; + struct AnimJob { + std::filesystem::path path; + AnimCallback callback; + }; + struct TextureJob { + std::filesystem::path path; + bool albedo; + TextureCallback callback; + }; + struct ShaderJob { + std::filesystem::path vertexPath; + std::filesystem::path geometryPath; + std::filesystem::path fragmentPath; + ShaderCallback callback; + }; + + std::queue jobs; + std::queue animJobs; + std::queue textureJobs; + std::queue shaderJobs; + mutable std::mutex queueMutex; + std::thread worker; + std::atomic running = true; + std::condition_variable cv; + std::counting_semaphore loadSemaphore{MAX_CONCURRENT_LOADS}; + + void workerLoop(); + void workerLoopAnim(); + void workerLoopTexture(); + void workerLoopShader(); + }; +} // Resource + +#endif //SNAKE3_RESOURCELOADER_H \ No newline at end of file diff --git a/Resource/ShaderLoader.cpp b/Resource/ShaderLoader.cpp index 05769818..eeaa5107 100644 --- a/Resource/ShaderLoader.cpp +++ b/Resource/ShaderLoader.cpp @@ -1,62 +1,84 @@ #include "ShaderLoader.h" +#include +#include namespace Resource { + unsigned int ShaderLoader::loadShader(const fs::path &vertexPath) { + const auto vertex = loadShaderToBuffer(vertexPath); + const string vertexStr(vertex.vertex.begin(), vertex.vertex.end()); + + return compileShader(vertexStr); + } + unsigned int ShaderLoader::loadShader(const fs::path &vertexPath, const fs::path &fragmentPath) { - GLuint vertShader = glCreateShader(GL_VERTEX_SHADER); - GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER); + const auto [fragment, vertex] = loadShaderToBuffer(vertexPath, fragmentPath); + const string vertexStr(vertex.begin(), vertex.end()); + const string fragmentStr(fragment.begin(), fragment.end()); - // Read shaders - string vertShaderStr = readFile(vertexPath); - string fragShaderStr = readFile(fragmentPath); - cout << fragmentPath.parent_path() << endl; - replaceIncludes(fragmentPath.parent_path(), fragmentPath, fragShaderStr); - replaceIncludes(vertexPath.parent_path(), vertexPath, vertShaderStr); - const char *vertShaderSrc = vertShaderStr.c_str(); - const char *fragShaderSrc = fragShaderStr.c_str(); + return compileShader(vertexStr, fragmentStr); + } - GLint result = GL_FALSE; - int logLength; + unsigned int ShaderLoader::loadShader(const fs::path &vertexPath, + const fs::path &geometryPath, + const fs::path &fragmentPath) { + const fgvShader shader = loadShaderToBuffer(vertexPath, geometryPath, fragmentPath); + const string vertexStr(shader.vertex.begin(), shader.vertex.end()); + const string fragmentStr(shader.fragment.begin(), shader.fragment.end()); + const string geomStr(shader.geometry.begin(), shader.geometry.end()); - // Compile vertex shader - cout << "Compiling vertex shader." << endl; - glShaderSource(vertShader, 1, &vertShaderSrc, nullptr); - glCompileShader(vertShader); - checkCompileErrors(vertShader, "VERTEX"); + return compileShader(vertexStr, fragmentStr, geomStr); + } - // Check vertex shader - glGetShaderiv(vertShader, GL_COMPILE_STATUS, &result); - glGetShaderiv(vertShader, GL_INFO_LOG_LENGTH, &logLength); + vShader ShaderLoader::loadShaderToBuffer(const fs::path &vertexPath) { + vShader shader; + string vertex = readFile(vertexPath); + replaceIncludes(vertexPath.parent_path(), vertexPath, vertex); + shader.vertex.insert(shader.vertex.end(), vertex.begin(), vertex.end()); - // Compile fragment shader - cout << "Compiling fragment shader." << endl; - glShaderSource(fragShader, 1, &fragShaderSrc, nullptr); - glCompileShader(fragShader); - checkCompileErrors(fragShader, "FRAGMENT"); + return shader; + } - // Check fragment shader - glGetShaderiv(fragShader, GL_COMPILE_STATUS, &result); - glGetShaderiv(fragShader, GL_INFO_LOG_LENGTH, &logLength); + fvShader ShaderLoader::loadShaderToBuffer(const fs::path &vertexPath, const fs::path &fragmentPath) { + fvShader shader; + string vertex = readFile(vertexPath); + string fragment = readFile(fragmentPath); + replaceIncludes(vertexPath.parent_path(), vertexPath, vertex); + replaceIncludes(fragmentPath.parent_path(), fragmentPath, fragment); + shader.vertex.insert(shader.vertex.end(), vertex.begin(), vertex.end()); + shader.fragment.insert(shader.fragment.end(), fragment.begin(), fragment.end()); - cout << "Linking program" << endl; - GLuint program = glCreateProgram(); - glAttachShader(program, vertShader); - glAttachShader(program, fragShader); - glLinkProgram(program); - checkCompileErrors(program, "PROGRAM"); + return shader; + } - glGetProgramiv(program, GL_LINK_STATUS, &result); - glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength); + fgvShader ShaderLoader::loadShaderToBuffer(const fs::path &vertexPath, const fs::path &geometryPath, + const fs::path &fragmentPath) { + fgvShader shader; + string vertex = readFile(vertexPath); + string fragment = readFile(fragmentPath); + string geom = readFile(geometryPath); + replaceIncludes(vertexPath.parent_path(), vertexPath, vertex); + replaceIncludes(fragmentPath.parent_path(), fragmentPath, fragment); + replaceIncludes(geometryPath.parent_path(), geometryPath, geom); + shader.vertex.insert(shader.vertex.end(), vertex.begin(), vertex.end()); + shader.fragment.insert(shader.fragment.end(), fragment.begin(), fragment.end()); + shader.geometry.insert(shader.geometry.end(), geom.begin(), geom.end()); - glDetachShader(program, vertShader); - glDetachShader(program, fragShader); + return shader; + } - glDeleteShader(vertShader); - glDeleteShader(fragShader); + unsigned int ShaderLoader::bindFromBuffer(const string &vertexStr, const string &fragmentStr) { + return compileShader(vertexStr, fragmentStr); + } - return program; + unsigned int ShaderLoader::bindFromBuffer( + const string &vertexStr, + const string &geometryStr, + const string &fragmentStr) + { + return compileShader(vertexStr, fragmentStr, geometryStr); } - void ShaderLoader::checkCompileErrors(unsigned int shader, const string &type) { + void ShaderLoader::checkCompileErrors(const unsigned int shader, const string &type) { GLint success; GLchar infoLog[1024]; if (type != "PROGRAM") { @@ -64,18 +86,124 @@ namespace Resource { if (!success) { glGetShaderInfoLog(shader, 1024, nullptr, infoLog); cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog - << "\n -- --------------------------------------------------- -- " << endl; + << "\n -- --------------------------------------------------- -- " << endl; } } else { glGetProgramiv(shader, GL_LINK_STATUS, &success); if (!success) { glGetProgramInfoLog(shader, 1024, nullptr, infoLog); cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog - << "\n -- --------------------------------------------------- -- " << endl; + << "\n -- --------------------------------------------------- -- " << endl; } } } + unsigned int ShaderLoader::compileShader(const string &vertexStr) { + const GLuint vertShader = glCreateShader(GL_VERTEX_SHADER); + + // Compile vertex shader + const char* vertexSource = vertexStr.c_str(); + glShaderSource(vertShader, 1, &vertexSource, nullptr); + glCompileShader(vertShader); + checkCompileErrors(vertShader, "VERTEX"); + + const GLuint program = glCreateProgram(); + glAttachShader(program, vertShader); + + // Transform Feedback pro stav částic (interleaved do jednoho VBO) + const char* varyings[] = { + "outPos", + "outVel", + "outLife", + "outSeed" + }; + + glTransformFeedbackVaryings(program, 4, varyings, GL_INTERLEAVED_ATTRIBS); + glLinkProgram(program); + + glLinkProgram(program); + checkCompileErrors(program, "PROGRAM"); + + glDetachShader(program, vertShader); + glDeleteShader(vertShader); + + return program; + } + + unsigned int ShaderLoader::compileShader(const string &vertexStr, const string &fragmentStr) { + const GLuint vertShader = glCreateShader(GL_VERTEX_SHADER); + const GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER); + + // Compile vertex shader + const char* vertexSource = vertexStr.c_str(); + glShaderSource(vertShader, 1, &vertexSource, nullptr); + glCompileShader(vertShader); + checkCompileErrors(vertShader, "VERTEX"); + + // Compile fragment shader + const char* fragmentSource = fragmentStr.c_str(); + glShaderSource(fragShader, 1, &fragmentSource, nullptr); + glCompileShader(fragShader); + checkCompileErrors(fragShader, "FRAGMENT"); + + const GLuint program = glCreateProgram(); + glAttachShader(program, vertShader); + glAttachShader(program, fragShader); + glLinkProgram(program); + checkCompileErrors(program, "PROGRAM"); + + glDetachShader(program, vertShader); + glDetachShader(program, fragShader); + + glDeleteShader(vertShader); + glDeleteShader(fragShader); + + return program; + } + + unsigned int ShaderLoader::compileShader(const string &vertexStr, const string &fragmentStr, + const string &geometryStr) { + const GLuint vertShader = glCreateShader(GL_VERTEX_SHADER); + const GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER); + const GLuint geomShader = glCreateShader(GL_GEOMETRY_SHADER); + + // Compile vertex shader + const char* vertexSource = vertexStr.c_str(); + glShaderSource(vertShader, 1, &vertexSource, nullptr); + glCompileShader(vertShader); + checkCompileErrors(vertShader, "VERTEX"); + + // Compile fragment shader + const char* fragmentSource = fragmentStr.c_str(); + glShaderSource(fragShader, 1, &fragmentSource, nullptr); + glCompileShader(fragShader); + checkCompileErrors(fragShader, "FRAGMENT"); + + // Compile fragment shader + const char* geometrySource = geometryStr.c_str(); + glShaderSource(geomShader, 1, &geometrySource, nullptr); + glCompileShader(geomShader); + checkCompileErrors(geomShader, "GEOMETRY"); + + const GLuint program = glCreateProgram(); + glAttachShader(program, vertShader); + glAttachShader(program, fragShader); + glAttachShader(program, geomShader); + glLinkProgram(program); + checkCompileErrors(program, "PROGRAM"); + + glDetachShader(program, vertShader); + glDetachShader(program, fragShader); + glDetachShader(program, geomShader); + + glDeleteShader(vertShader); + glDeleteShader(fragShader); + glDeleteShader(geomShader); + + return program; + + } + string ShaderLoader::readFile(const string &filePath) { string content; ifstream fileStream(filePath, ios::in); @@ -99,7 +227,7 @@ namespace Resource { void ShaderLoader::replaceIncludes(const fs::path &base_dir, const string &path, string &source) { try { resolveIncludes(base_dir, source); - } catch (const shader_file_not_found& not_found) { + } catch (const shader_file_not_found ¬_found) { throw shader_include_not_found("Failed to resolve include for " + path + ": " + not_found.what()); } } @@ -107,7 +235,7 @@ namespace Resource { void ShaderLoader::resolveIncludes(const fs::path &base_dir, string &src) { static constexpr std::string_view include = "#include"; - std::size_t found {}; + std::size_t found{}; while (true) { found = src.find(include, found); @@ -115,9 +243,9 @@ namespace Resource { return; } - size_t beg = found + include.length() + 2; - size_t end = src.find('"', beg); - size_t name_length = end - beg; + const size_t beg = found + include.length() + 2; + const size_t end = src.find('"', beg); + const size_t name_length = end - beg; fs::path file_name = src.substr(beg, name_length); @@ -128,5 +256,4 @@ namespace Resource { src.replace(found, include.length() + 3 + name_length, include_src); } } - -} // Resource \ No newline at end of file +} // Resource diff --git a/Resource/ShaderLoader.h b/Resource/ShaderLoader.h index d1413bdb..aeb40ffc 100644 --- a/Resource/ShaderLoader.h +++ b/Resource/ShaderLoader.h @@ -1,33 +1,57 @@ #ifndef SNAKE3_SHADERLOADER_H #define SNAKE3_SHADERLOADER_H -#include "../stdafx.h" #include -#include -#include #include +#include using namespace std; namespace fs = std::filesystem; namespace Resource { - class shader_file_not_found : public std::runtime_error { + struct vShader { + vector vertex; + }; + struct fvShader { + vector fragment; + vector vertex; + }; + struct fgvShader { + vector fragment; + vector geometry; + vector vertex; + }; + class shader_file_not_found final : public std::runtime_error { public: explicit shader_file_not_found(const std::string& error) : std::runtime_error(error) {} }; - class shader_include_not_found : public std::runtime_error { + class shader_include_not_found final : public std::runtime_error { public: explicit shader_include_not_found(const std::string& error) : std::runtime_error(error) {} }; class ShaderLoader { public: + static unsigned int loadShader(const fs::path& vertexPath); static unsigned int loadShader(const fs::path& vertexPath, const fs::path& fragmentPath); + static unsigned int loadShader(const fs::path& vertexPath, + const fs::path& geometryPath, + const fs::path& fragmentPath); + static vShader loadShaderToBuffer(const fs::path& vertexPath); + static fvShader loadShaderToBuffer(const fs::path& vertexPath, const fs::path& fragmentPath); + static fgvShader loadShaderToBuffer(const fs::path& vertexPath, + const fs::path& geometryPath, + const fs::path& fragmentPath); + static unsigned int bindFromBuffer(const string& vertexStr, const string& fragmentStr); + static unsigned int bindFromBuffer(const string& vertexStr, const string& geometryStr, const string& fragmentStr); protected: static void replaceIncludes(const fs::path& base_dir, const string &path, string &source); static void resolveIncludes(const fs::path& base_dir, std::string& src); static void checkCompileErrors(unsigned int shader, const string &type); + static unsigned int compileShader(const string &vertexStr); + static unsigned int compileShader(const string &vertexStr, const string &fragmentStr); + static unsigned int compileShader(const string &vertexStr, const string &fragmentStr, const string &geometryStr); static string readFile(const string &filePath); }; diff --git a/Resource/TextureLoader.cpp b/Resource/TextureLoader.cpp index df493c88..9c860e49 100644 --- a/Resource/TextureLoader.cpp +++ b/Resource/TextureLoader.cpp @@ -2,10 +2,14 @@ #define STB_IMAGE_IMPLEMENTATION +#include +#include +#include + #include "../Thirdparty/stbimage/stb_image.h" namespace Resource { - unsigned int TextureLoader::loadTexture(const fs::path &path) { + unsigned int TextureLoader::loadTexture(const fs::path &path, const bool isAlbedo) { unsigned int textureID; glGenTextures(1, &textureID); @@ -13,67 +17,156 @@ namespace Resource { unsigned char *data = stbi_load(path.c_str(), &widthImg, &heightImg, &numColCh, 0); if (data) { glBindTexture(GL_TEXTURE_2D, textureID); + if (isAlbedo) { + // Albedo = barevná textura, musí být sRGB + if (numColCh == 3) + glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB, widthImg, heightImg, 0, GL_RGB, GL_UNSIGNED_BYTE, data); + else if (numColCh == 4) + glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB_ALPHA, widthImg, heightImg, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + else { + throw std::invalid_argument("Automatic Texture type recognition failed"); + } + } else { + // Metallic, roughness, ao, normal = data, lineární + if (numColCh == 1) + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, widthImg, heightImg, 0, GL_RED, GL_UNSIGNED_BYTE, data); + else if (numColCh == 3) + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, widthImg, heightImg, 0, GL_RGB, GL_UNSIGNED_BYTE, data); + else if (numColCh == 4) + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, widthImg, heightImg, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + else { + throw std::invalid_argument("Automatic Texture type recognition failed"); + } + } + + glGenerateMipmap(GL_TEXTURE_2D); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } else { + throw std::runtime_error(std::string("Texture failed to load at path: ") + path.c_str()); + } + + stbi_image_free(data); + + return textureID; + } + + unsigned int TextureLoader::bindFromBuffer(const vector &buffer, const bool isAlbedo) { + return bindFromBuffer(buffer.data(), buffer.size(), isAlbedo); + } - // Check what type of color channels the baseColor has and load it accordingly - if (numColCh == 4) - glTexImage2D - ( - GL_TEXTURE_2D, - 0, - GL_RGBA, - widthImg, - heightImg, - 0, - GL_RGBA, - GL_UNSIGNED_BYTE, - data - ); - else if (numColCh == 3) - glTexImage2D - ( - GL_TEXTURE_2D, - 0, - GL_RGBA, - widthImg, - heightImg, - 0, - GL_RGB, - GL_UNSIGNED_BYTE, - data - ); - else if (numColCh == 1) - glTexImage2D - ( - GL_TEXTURE_2D, - 0, - GL_RGBA, - widthImg, - heightImg, - 0, - GL_RED, - GL_UNSIGNED_BYTE, - data - ); - else { - throw std::invalid_argument("Automatic Texture type recognition failed"); + unsigned int TextureLoader::bindFromBuffer(const void *buffer, const unsigned int length, const bool isAlbedo) { + unsigned int textureID; + int widthImg, heightImg, numColCh; + glGenTextures(1, &textureID); + unsigned char* data = stbi_load_from_memory( + static_cast(buffer), + static_cast(length), + &widthImg, + &heightImg, + &numColCh, + 0 // zachovej původní počet kanálů + ); + if (data) { + glBindTexture(GL_TEXTURE_2D, textureID); + if (isAlbedo) { + // Albedo = barevná textura, musí být sRGB + if (numColCh == 3) + glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB, widthImg, heightImg, 0, GL_RGB, GL_UNSIGNED_BYTE, data); + else if (numColCh == 4) + glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB_ALPHA, widthImg, heightImg, 0, GL_RGBA, GL_UNSIGNED_BYTE,data); + else { + throw std::invalid_argument("Automatic Texture type recognition failed"); + } + } else { + // Metallic, roughness, ao, normal = data, lineární + if (numColCh == 1) + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, widthImg, heightImg, 0, GL_RED, GL_UNSIGNED_BYTE, data); + else if (numColCh == 3) + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, widthImg, heightImg, 0, GL_RGB, GL_UNSIGNED_BYTE, data); + else if (numColCh == 4) + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, widthImg, heightImg, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + else { + throw std::invalid_argument("Automatic Texture type recognition failed"); + } } glGenerateMipmap(GL_TEXTURE_2D); - - stbi_image_free(data); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { - cout << "Texture failed to load at path: " << path << std::endl; - stbi_image_free(data); + throw std::runtime_error("Texture failed to load from memory."); } + stbi_image_free(data); + return textureID; } - unsigned int TextureLoader::loadSkyboxTexture(vector faces) { + vector TextureLoader::loadTextureToBuffer(const fs::path &path) { + std::vector out; + namespace fs = std::filesystem; + std::error_code ec; + + if (!fs::exists(path, ec)) { + std::cerr << "[loadFile] file not found: " << path << " (" << ec.message() << ")\n"; + return out; + } + + auto filesize = fs::file_size(path, ec); + if (ec) { + std::cerr << "[loadFile] file_size failed: " << ec.message() << ". Budu fallback číst streamem.\n"; + // fallback: přečíst přes stream iterator níže + } else { + if (filesize == 0) { + std::cerr << "[loadFile] file is empty: " << path << "\n"; + return out; + } + // kontrola proti přetečení / příliš velkému požadavku + if (filesize > std::vector().max_size()) { + std::cerr << "[loadFile] file is too large to fit into vector: " << filesize << "\n"; + return out; + } + + // otevři a čti přesně 'filesize' bajtů + std::ifstream ifs(path, std::ios::binary); + if (!ifs) { + std::cerr << "[loadFile] cannot open file: " << path << "\n"; + return out; + } + + out.clear(); + out.resize(static_cast(filesize)); + ifs.read(reinterpret_cast(out.data()), static_cast(filesize)); + if (!ifs) { + // read může selhat + std::cerr << "[loadFile] read failed (gcount=" << ifs.gcount() << ")\n"; + return out; + } + } + + // --- fallback: pokud file_size nebo jiné kroky selhaly, načti přes istreambuf_iterator + std::ifstream ifs(path, std::ios::binary); + if (!ifs) { + std::cerr << "[loadFile] fallback: cannot open file: " << path << "\n"; + return out; + } + + out.assign(std::istreambuf_iterator(ifs), std::istreambuf_iterator()); + + if (out.empty()) { + std::cerr << "[loadFile] fallback read produced empty buffer (maybe file empty or read error)\n"; + return out; + } + + return out; + } + + unsigned int TextureLoader::loadSkyboxTexture(const vector &faces) { unsigned int textureID; glGenTextures(1, &textureID); glBindTexture(GL_TEXTURE_CUBE_MAP, textureID); @@ -85,13 +178,16 @@ namespace Resource { glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data ); - stbi_image_free(data); } else { std::cout << "Texture failed to load at path: " << faces[i] << std::endl; - stbi_image_free(data); } + stbi_image_free(data); } - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glGenerateMipmap(GL_TEXTURE_CUBE_MAP); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + + //glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); diff --git a/Resource/TextureLoader.h b/Resource/TextureLoader.h index 2b4779b7..5186919c 100644 --- a/Resource/TextureLoader.h +++ b/Resource/TextureLoader.h @@ -1,10 +1,7 @@ #ifndef SNAKE3_TEXTURELOADER_H #define SNAKE3_TEXTURELOADER_H -#include "../stdafx.h" #include -#include -#include #include using namespace std; @@ -15,8 +12,11 @@ namespace Resource { class TextureLoader { public: - static unsigned int loadTexture(const fs::path& path); - static unsigned int loadSkyboxTexture(vector faces); + static unsigned int loadTexture(const fs::path& path, bool isAlbedo = true); + static unsigned int bindFromBuffer(const vector &buffer, bool isAlbedo = true); + static unsigned int bindFromBuffer(const void* buffer, unsigned int length, bool isAlbedo = true); + static vector loadTextureToBuffer(const fs::path& path); + static unsigned int loadSkyboxTexture(const vector &faces); }; } // Resource diff --git a/Scenes/BarriersScene.cpp b/Scenes/BarriersScene.cpp new file mode 100644 index 00000000..2e844703 --- /dev/null +++ b/Scenes/BarriersScene.cpp @@ -0,0 +1,84 @@ +#include "BarriersScene.h" +#include "../Renderer/Opengl/Model/Standard/BoxMesh.h" + +namespace Scenes { + BarriersScene::BarriersScene( + const shared_ptr &directionalLight, + const vector > &spotLights, + const vector > &pointLights, + const shared_ptr &rendererManager, const shared_ptr &camera, + const glm::mat4 &projection, const shared_ptr &rm, const int width, const int height) + : Scene(directionalLight, spotLights, pointLights, rendererManager, camera, projection, rm, width, height) { + } + + void BarriersScene::init(const int priority) { + Scene::init(priority); + initBarriers(); + initLevelManager(); + } + + shared_ptr BarriersScene::getLevelManager() const { + return levelManager; + } + + shared_ptr BarriersScene::getLevelBoxes() const { + return levelBoxes; + } + + void BarriersScene::initBarriers() { + const auto shader = resourceManager->getShader("basicShader"); + const auto shadowsShader = resourceManager->getShader("shadowDepthShader"); + const auto boxMesh = make_shared(shader, 2.0, 2.0, 2.0); + const auto brickWall = resourceManager->getTexture("brickwork-texture.jpg"); + const auto brickWallNormal = resourceManager->getTexture("brickwork_normal-map.jpg"); + const auto brickWallSpecular = resourceManager->getTexture("brickwork-bump-map.jpg"); + const auto boxMaterial = make_shared(StandardMaterial(shader, shadowsShader)); + boxMaterial->setNormalEnabled(true); + boxMaterial->setAlbedo(brickWall); + boxMaterial->setNormal(brickWallNormal); + boxMaterial->setSpecular(brickWallSpecular); + boxMaterial->setAmbientLightColorIntensity(0.1); + boxMaterial->setDirectionalLight(directionalLight); + boxMaterial->setSpotLights(spotLights); + boxMaterial->setPointLights(pointLights); + + boxMesh->setMaterial(boxMaterial); + + const auto boxNode3D = make_shared(contextState, boxMesh, resourceManager); + boxNode3D->setPosition(glm::vec3{-25.0, -25.0, -23.0}); + boxNode3D->setScale({0.041666667f, 0.041666667f, 0.041666667f}); + + for (int x = 2; x <= 98; x += 2) { + const auto boxNode3D_2 = make_shared(contextState, boxMesh, resourceManager); + boxNode3D_2->setPosition(glm::vec3{x, 0.0, 0.0}); + boxNode3D->addNode(boxNode3D_2); + } + + for (int x = 0; x <= 98; x += 2) { + const auto boxNode3D_2 = make_shared(contextState, boxMesh, resourceManager); + boxNode3D_2->setPosition(glm::vec3{x, 98.0, 0.0}); + boxNode3D->addNode(boxNode3D_2); + } + + for (int y = 2; y <= 96; y += 2) { + const auto boxNode3D_2 = make_shared(contextState, boxMesh, resourceManager); + boxNode3D_2->setPosition(glm::vec3{0, y, 0.0}); + boxNode3D->addNode(boxNode3D_2); + } + + for (int y = 2; y <= 96; y += 2) { + const auto boxNode3D_2 = make_shared(contextState, boxMesh, resourceManager); + boxNode3D_2->setPosition(glm::vec3{98, y, 0.0}); + boxNode3D->addNode(boxNode3D_2); + } + + addMeshNode3D(boxNode3D, 100); + } + + void BarriersScene::initLevelManager() { + levelManager = make_shared(contextState, 1, MAX_LIVES, resourceManager); + // levelManager->createLevel(START_LEVEL); + levelBoxes = levelManager->createLevel(START_LEVEL, directionalLight, spotLights, pointLights); + addMeshNode3D(levelBoxes, 3001); + } +} // Scenes \ No newline at end of file diff --git a/Scenes/BarriersScene.h b/Scenes/BarriersScene.h new file mode 100644 index 00000000..fa00ae7d --- /dev/null +++ b/Scenes/BarriersScene.h @@ -0,0 +1,33 @@ +#ifndef SNAKE3_BARRIERSSCENE_H +#define SNAKE3_BARRIERSSCENE_H + +#include "../Manager/LevelManager.h" +#include "../Renderer/Opengl/Scene/Scene.h" + +namespace Scenes { + class BarriersScene final : public Scene { + public: + BarriersScene( + const shared_ptr &directionalLight, + const vector > &spotLights, + const vector > &pointLights, + const shared_ptr &rendererManager, const shared_ptr &camera, + const glm::mat4 &projection, const shared_ptr &rm, int width, int height); + + void init(int priority) override; + + [[nodiscard]] shared_ptr getLevelManager() const; + + [[nodiscard]] shared_ptr getLevelBoxes() const; + + protected: + void initBarriers(); + + void initLevelManager(); + + shared_ptr levelManager; + shared_ptr levelBoxes; + }; +} // Scenes + +#endif //SNAKE3_BARRIERSSCENE_H diff --git a/Scenes/CoinScene.cpp b/Scenes/CoinScene.cpp new file mode 100644 index 00000000..451c60d5 --- /dev/null +++ b/Scenes/CoinScene.cpp @@ -0,0 +1,119 @@ +#include "CoinScene.h" + +#include "../Renderer/Opengl/Model/Standard/ArrayMesh.h" + +namespace Scenes { + CoinScene::CoinScene( + const shared_ptr &directionalLight, + const vector > &spotLights, + const vector > &pointLights, + const shared_ptr &rendererManager, const shared_ptr &camera, + const glm::mat4 &projection, const shared_ptr &rm, const int width, const int height) + : Scene(directionalLight, spotLights, pointLights, rendererManager, camera, projection, rm, width, height) { + } + + void CoinScene::init(const int priority) { + Scene::init(priority); + initCoin(); + } + + shared_ptr CoinScene::getCoin() const { + return coin; + } + + shared_ptr CoinScene::getRemoveCoin() const { + return removeCoin; + } + + void CoinScene::initCoin() { + const auto shader = resourceManager->getShader("basicShader"); + const auto shadowsShader = resourceManager->getShader("shadowDepthShader"); + const auto coinMesh = make_shared(shader); + coinMesh->fromMesh(resourceManager->getModel("coin")); + + coin = make_shared(spotLights[4], contextState, coinMesh, resourceManager); + coin->setPosition({-69.0, -69, -70.0f}); + coin->setScale({0.013888889, 0.013888889, 0.013888889}); + coin->setRotationX(90); + coin->setVisible(false); + + const auto coinAlbedo = resourceManager->getTexture("Coin_Gold_albedo.png"); + const auto coinNormal = resourceManager->getTexture("Coin_Gold_nm.png"); + const auto coinMetalness = resourceManager->getTexture("Coin_Gold_metalness.png"); + auto coinRoughness = resourceManager->getTexture("Coin_Gold_rough.png"); + const auto coinMaterial = make_shared(shader, shadowsShader); + coinMaterial->setAlbedo(coinAlbedo); + coinMaterial->setNormal(coinNormal); + coinMaterial->setSpecular(coinMetalness); + coinMaterial->setNormalEnabled(true); + coinMaterial->setDirectionalLight(directionalLight); + coinMaterial->setBlending(Blending::Translucent); + + int index = 0; + for (auto &spotLight : spotLights) { + coinMaterial->addSpotLight(spotLight); + index++; + // u indexu 5 vytvorit objekt co bude drzet pozici a direction + // takovou jako vlastni lampicku + } + + coinMesh->setMaterial(coinMaterial); + + // CREATE rotation animation + std::vector> rot_frames; + rot_frames.emplace_back(glm::angleAxis(glm::radians(0.f), glm::vec3(0,1,0)), 0.f); + rot_frames.emplace_back(glm::angleAxis(glm::radians(90.f), glm::vec3(0,1,0)), 8.f); + rot_frames.emplace_back(glm::angleAxis(glm::radians(180.f), glm::vec3(0,1,0)), 16.f); + rot_frames.emplace_back(glm::angleAxis(glm::radians(270.f), glm::vec3(0,1,0)), 24.f); + rot_frames.emplace_back(glm::angleAxis(glm::radians(0.f), glm::vec3(0,1,0)), 32.f); + + std::vector> pos_frames; + std::vector> scale_frames; + const auto animationNode = make_shared(pos_frames, rot_frames, scale_frames, nullptr); + + // CREATE eaten up animationi + std::vector> rot_frames2; + rot_frames2.emplace_back(glm::angleAxis(glm::radians(0.f), glm::vec3(0,1,0)), 0.f); + rot_frames2.emplace_back(glm::angleAxis(glm::radians(90.f), glm::vec3(0,1,0)), 8.f); + rot_frames2.emplace_back(glm::angleAxis(glm::radians(180.f), glm::vec3(0,1,0)), 16.f); + + std::vector> pos_frames2; + pos_frames2.emplace_back(glm::vec3(0.0f, 0.0, 0.0), 0); + pos_frames2.emplace_back(glm::vec3(0.0f, 6.0, 0.0), 8); + pos_frames2.emplace_back(glm::vec3(0.0f, 12.0, 0.0), 16); + + std::vector> alpha_frames; + alpha_frames.emplace_back(1.0f, 0); + alpha_frames.emplace_back(0.5f, 8); + alpha_frames.emplace_back(0.0f, 16); + + const auto animationNode2 = make_shared(pos_frames2, rot_frames2, scale_frames, nullptr); + animationNode2->setAlphaFrames(alpha_frames); + + const auto coinAnimation = make_shared(); + coinAnimation->createAnimation("coinRotation"); + coinAnimation->addAnimationNode("coinRotation", animationNode, 32); + coinMesh->setAnimationPlayer(coinAnimation); + + const auto coinAnimation2 = make_shared(); + coinAnimation2->createAnimation("eatenUp"); + coinAnimation2->addAnimationNode("eatenUp", animationNode2, 16); + const auto coinMesh2 = make_shared(ArrayMesh(shader)); + coinMesh2->fromMesh(resourceManager->getModel("coin")); + coinMesh2->setAnimationPlayer(coinAnimation2); + coinMesh2->setMaterial(coinMaterial); + removeCoin = make_shared(nullptr, contextState, coinMesh2, resourceManager); + removeCoin->setPosition({-69.0, -69, -70.0f}); + removeCoin->setScale({0.013888889, 0.013888889, 0.013888889}); + removeCoin->setRotationX(90); + removeCoin->setVisible(false); + + coinAnimation2->setCompletedCallback([this](AnimationPlayer * player) { + removeCoin->setVisible(false); + player->reset("eatenUp"); + }); + + addMeshNode3D(coin); + addMeshNode3D(removeCoin); + } +} // Scenes \ No newline at end of file diff --git a/Scenes/CoinScene.h b/Scenes/CoinScene.h new file mode 100644 index 00000000..df320dfa --- /dev/null +++ b/Scenes/CoinScene.h @@ -0,0 +1,36 @@ +#ifndef SNAKE3_COINSCENE_H +#define SNAKE3_COINSCENE_H + +#include + +#include "../Renderer/Opengl/Model/Game/CoinMeshNode3D.h" +#include "../Renderer/Opengl/Model/Standard/Animation/AnimationPlayer.h" +#include "../Renderer/Opengl/Scene/Scene.h" + +using namespace Model; +using namespace Animations; + +namespace Scenes { + class CoinScene final : public Scene { + public: + CoinScene( + const shared_ptr &directionalLight, + const vector > &spotLights, + const vector > &pointLights, + const shared_ptr &rendererManager, const shared_ptr &camera, + const glm::mat4 &projection, const shared_ptr &rm, int width, int height); + + void init(int priority) override; + + [[nodiscard]] shared_ptr getCoin() const; + [[nodiscard]] shared_ptr getRemoveCoin() const; + + protected: + void initCoin(); + + shared_ptr coin; + shared_ptr removeCoin; + }; +} // Scenes + +#endif //SNAKE3_COINSCENE_H diff --git a/Scenes/MainScene.cpp b/Scenes/MainScene.cpp new file mode 100644 index 00000000..cfcf00f9 --- /dev/null +++ b/Scenes/MainScene.cpp @@ -0,0 +1,462 @@ +#include "MainScene.h" + +#include + +#include "PlayerScene.h" +#include "TorchScene.h" +#include "WeatherScene.h" +#include "../Resource/ShaderLoader.h" +#include "../Renderer/Opengl/Material/ShaderMaterial.h" +#include "../Renderer/Opengl/Material/PlanarReflectionMaterial.h" +#include "../Renderer/Opengl/Material/Uniform/FadeOutUniform.h" +#include "../Renderer/Opengl/Model/Debug/DirectionalLightNode3D.h" +#include "../Renderer/Opengl/Model/Game/RadarMeshNode2D.h" +#include "../Renderer/Opengl/Model/Standard/ArrayMesh.h" +#include "../Renderer/Opengl/Model/Standard/PlaneMesh.h" +#include "../Renderer/Opengl/Model/Standard/SkyboxNode3D.h" +#include "../Renderer/Opengl/Model/Standard/2D/LabelNode2D.h" +#include "../Renderer/Opengl/Model/Standard/2D/QuadNode2D.h" + +namespace Scenes { + MainScene::MainScene( + const shared_ptr &directionalLight, + const vector > &spotLights, + const vector > &pointLights, + const shared_ptr &rendererManager, const shared_ptr &camera, + const glm::mat4 &projection, const shared_ptr &rm, const int width, const int height) + : Scene(directionalLight, spotLights, pointLights, rendererManager, camera, projection, rm, width, height) { + ortho = glm::ortho(0.0f, static_cast(width), static_cast(height), 0.0f, -1.0f, 1000.0f); + collisionSystem = make_shared(); + } + + void MainScene::init(const int priority) { + Scene::init(priority); + + collisionDetector = make_shared(); + initLights(); + initPlayerScene(); + initBarriersScene(); + initCoinScene(); + initTorchScene(); + initWeatherScene(); + + initSkybox(); + initPlane(); + initRadar(); + initEatManager(); + initLabels(); + + buildStartMoveCallback(); + buildEatenUpCallback(); + + // DEBUG + // ==================== + const auto shader = resourceManager->getShader("arrowGizmo"); + const auto dirLightNode = make_shared(contextState, shader, resourceManager); + dirLightNode->setDirectionalLight(directionalLight); + addMeshNode3D(dirLightNode); + + if (positionHandler != nullptr) { + // positionHandler->addItem(directionalLight); + // for (auto &spotLight : spotLights) { + // positionHandler->addItem(spotLight); + // } + // for (auto &pointLight : pointLights) { + // positionHandler->addItem(pointLight); + // } + } + } + + void MainScene::keyboardInput(GLFWwindow *window, const int keyCode, const int scancode, const int action, const int mods) const { + Scene::keyboardInput(window, keyCode, scancode, action, mods); + + switch (keyCode) { + case GLFW_KEY_V: + rendererManager->toggleShadows(); + break; + case GLFW_KEY_B: + rendererManager->toggleBloom(); + break; + case GLFW_KEY_F2: + if (planeMaterial) { + rendererManager->toggleReflections(); + planeMaterial->setReflectionEnabled(rendererManager->isReflectionsEnabled()); + } + break; + case GLFW_KEY_F: + rendererManager->toggleFog(); + break; + case GLFW_KEY_M: + if (playerScene) { + playerScene->getSnake()->respawn(); + } + break; + case GLFW_KEY_R: + if (radarMeshNode) { + if (radarMeshNode->isVisible()) { + radarNode->setMaterial(radarExpansionOut); + radarFadeOutUniform->start(); + radarMeshNode->hideItems(); + } else { + radarFadeOutUniform->setAlpha(1.0); + radarMeshNode->setVisible(true); + radarNode->setMaterial(radarExpansionIn); + radarFadeInUniform->start(); + } + } + break; + default: + break; + } + } + + void MainScene::initLights() { + constexpr auto spotAmbientColor = glm::vec3(0.5f, 0.5f, 0.5f); + constexpr auto spotSpecularColor = glm::vec3(0.1f, 0.1f, 0.1f); + const auto spotLight = make_shared(); + spotLight->setPosition({-1.7521, -0.75, -1.5f}); + spotLight->setDirection({0.0, -1.0, 0.0f}); + spotLight->setAmbient(spotAmbientColor); + spotLight->setDiffuse({0.0f, 0.0f, 0.0f}); + spotLight->setSpecular(spotSpecularColor); + spotLight->setCutOff(12.5); + spotLight->setOuterCutOff(17.5); + spotLight->setPulse(true); + + const auto spotLight2 = make_shared(); + spotLight2->setPosition({3.67, 2.81, -1.5f}); + spotLight2->setDirection({1.9, 3.0, 0.0f}); + spotLight2->setAmbient(spotAmbientColor); + spotLight2->setDiffuse({0.0f, 0.0f, 0.0f}); + spotLight2->setSpecular(spotSpecularColor); + spotLight2->setCutOff(12.5); + spotLight2->setOuterCutOff(17.5); + spotLight2->setPulse(true); + + const auto spotLight3 = make_shared(); + spotLight3->setPosition({-1.7521, 2.81, -1.5f}); + spotLight3->setDirection({0.0, 3.0, 0.0f}); + spotLight3->setAmbient(spotAmbientColor); + spotLight3->setDiffuse({0.0f, 0.0f, 0.0f}); + spotLight3->setSpecular(spotSpecularColor); + spotLight3->setCutOff(12.5); + spotLight3->setOuterCutOff(17.5); + spotLight3->setPulse(true); + + const auto spotLight4 = make_shared(); + spotLight4->setPosition({3.67, -0.75, -1.5f}); + spotLight4->setDirection({2.0f, -1.0, 0.0f}); + spotLight4->setAmbient(spotAmbientColor); + spotLight4->setDiffuse({0.0f, 0.0f, 0.0f}); + spotLight4->setSpecular(spotSpecularColor); + spotLight4->setCutOff(12.5); + spotLight4->setOuterCutOff(17.5); + spotLight4->setPulse(true); + + const auto spotLight5 = make_shared(); + spotLight5->setPosition({1.93, 0.43, -1.5f}); + spotLight5->setDirection({1.93f, 0.43, 0.0f}); + spotLight5->setAmbient(glm::vec3(1.0f, 0.95f, 0.8f)); + spotLight5->setDiffuse({0.991f, 0.982f, 0.305f}); + spotLight5->setSpecular({0.4f, 0.4f, 0.4f}); + spotLight5->setConstant(0.9f); + spotLight5->setCutOff(7.5); + spotLight5->setOuterCutOff(13.5); + spotLight5->setPulse(true); + spotLight5->setVisible(false); + + const auto pointLight1 = make_shared(); + pointLight1->setPosition({-0.03, 0.201801, -0.656399}); + pointLight1->setAmbient({0.05f, 0.05f, 0.05f}); + pointLight1->setDiffuse({1.0f, 0.95f, 0.8f}); + pointLight1->setSpecular({0.01f, 0.01f, 0.01f}); + pointLight1->setConstant(1.0f); + pointLight1->setLinear(8.09f); + pointLight1->setQuadratic(0.032f); + + const auto pointLight2 = make_shared(); + pointLight2->setPosition({2.15, 1.2218, -0.656399}); + pointLight2->setAmbient({0.1f, 0.0f, 0.0f}); + pointLight2->setDiffuse({0.88f, 0.0f, 0.00f}); + pointLight2->setSpecular({0.0f, 0.0f, 0.0f}); + pointLight2->setConstant(1.0f); + pointLight2->setLinear(0.7f); + pointLight2->setQuadratic(20.8f); + + this->spotLights.push_back(spotLight); + this->spotLights.push_back(spotLight2); + this->spotLights.push_back(spotLight3); + this->spotLights.push_back(spotLight4); + this->spotLights.push_back(spotLight5); + + this->pointLights.push_back(pointLight1); + this->pointLights.push_back(pointLight2); + } + + void MainScene::initSkybox() { + const auto skyboxNode = make_shared(contextState, resourceManager, camera); + addMeshNode3D(skyboxNode, 1000); + } + + void MainScene::initPlane() { + auto basicShader = resourceManager->getShader("basicShader"); + auto shadowDepthShader = resourceManager->getShader("shadowDepthShader"); + const auto shadowMap = resourceManager->getTexture("depth"); + const auto gamefieldAlbedo = resourceManager->getTexture("tile.png"); + const auto gamefieldNormal = resourceManager->getTexture("gamefield_normal.jpg"); + const auto gamefieldSpecular = resourceManager->getTexture("gamefield_specular.jpg"); + planeMaterial = make_shared(basicShader, shadowDepthShader); + planeMaterial->setReflectionTexture(resourceManager->getTexture("PlanarReflectionTexture")); + planeMaterial->setReflectionEnabled(rendererManager->isReflectionsEnabled()); + + planeMaterial->setDirectionalLight(directionalLight); + planeMaterial->setColor(glm::vec3(0.0f, 0.0f, 0.0f)); + planeMaterial->setShadow(shadowMap); + planeMaterial->setNormalEnabled(true); + planeMaterial->setAlbedo(gamefieldAlbedo); + planeMaterial->setNormal(gamefieldNormal); + planeMaterial->setSpecular(gamefieldSpecular); + planeMaterial->set_uv_scale(glm::vec2(48.0f, 48.0f)); + planeMaterial->setSpotLights(spotLights); + planeMaterial->setPointLights(pointLights); + + auto planeMesh = make_shared(basicShader, 4, 4); + planeMesh->setMaterial(planeMaterial); + const auto node3d = make_shared(contextState, planeMesh, resourceManager); + node3d->disablePlanarReflection(); + node3d->setRotationX(90); + node3d->setPosition({1.0, 1.0, -1.0}); + + addMeshNode3D(node3d, 200); + } + + void MainScene::initPlayerScene() { + playerScene = make_shared(directionalLight, spotLights, pointLights, rendererManager, camera, projection, resourceManager, width, height); + playerScene->setCollisionSystem(collisionSystem); + playerScene->init(2); + snakeMoveHandler = playerScene->getSnakeMoveHandler(); + snakeMoveHandler->setCollisionDetector(collisionDetector); + addNode(playerScene); + } + + void MainScene::initBarriersScene() { + barriersScene = make_shared(directionalLight, spotLights, pointLights, rendererManager, camera, projection, resourceManager, width, height); + barriersScene->init(3); + levelManager = barriersScene->getLevelManager(); + addNode(barriersScene); + } + + void MainScene::initCoinScene() { + coinScene = make_shared(directionalLight, spotLights, pointLights, rendererManager, camera, projection, resourceManager, width, height); + coinScene->init(4); + addNode(coinScene); + + collisionDetector->addStaticItem(coinScene->getCoin()); + } + + void MainScene::initTorchScene() { + const auto torchScene = make_shared(directionalLight, spotLights, pointLights, rendererManager, camera, projection, resourceManager, width, height); + torchScene->setCollisionSystem(collisionSystem); + torchScene->init(1); + addNode(torchScene); + } + + void MainScene::initWeatherScene() { + const auto weatherScene = make_shared(directionalLight, spotLights, pointLights, rendererManager, camera, projection, resourceManager, width, height); + weatherScene->init(0); + addNode(weatherScene); + } + + void MainScene::initEatManager() { + auto eatLocationHandler = make_shared( + barriersScene->getLevelBoxes(), + playerScene->getSnake(), + coinScene->getCoin() + ); + eatManager = make_unique(eatLocationHandler); + } + + void MainScene::initRadar() { + radarFadeInUniform = make_shared(); + radarFadeInUniform->setStep(5.0f); + radarExpansionIn = make_shared(resourceManager->getShader("quadCorner")); + radarExpansionIn->addUniform("quadSize", glm::vec2(200, 200)); + radarExpansionIn->addUniform("borderColor", glm::vec3(1.0,0.0,0.0)); + radarExpansionIn->addUniform("borderWidth", 11.9f); + radarExpansionIn->addUniform("radius", 8.0f); + radarExpansionIn->addUniform("expansion", radarFadeInUniform); + radarFadeInUniform->setFinishedCallback([this]() { + radarMeshNode->showItems(); + if (coinScene->getCoin()->isVisible() == false) { + radarMeshNode->hideItem("coin"); + } + }); + radarFadeInUniform->start(); + + radarFadeOutUniform = make_shared(); + radarFadeOutUniform->setStep(5.0f); + radarExpansionOut = make_shared(resourceManager->getShader("quadCorner")); + radarExpansionOut->addUniform("quadSize", glm::vec2(200, 200)); + radarExpansionOut->addUniform("borderColor", glm::vec3(1.0,0.0,0.0)); + radarExpansionOut->addUniform("borderWidth", 11.9f); + radarExpansionOut->addUniform("radius", 8.0f); + radarExpansionOut->addUniform("expansion", radarFadeOutUniform); + + radarNode = make_shared(220, 220, nullptr); + radarNode->setColor(glm::vec3(0.0f, 0.0f, 0.0f)); + radarNode->setMaterial(radarExpansionIn); + radarMeshNode = make_shared(contextState, radarNode, resourceManager); + radarNode->setBlending(Blending::Translucent); + radarMeshNode->setPosition({width - 240 + 100, 30.0 + 110, 0.0}); // + 100 kvuli tomu, ze stred neni 0,0 ale stred quadu + radarMeshNode->addItem(playerScene->getSnake(), glm::vec3(0.0,1.0,0.0), "snake"); + radarMeshNode->addItem(coinScene->getCoin(), glm::vec3(1.0,1.0,0.0), "coin"); + radarMeshNode->addItem(barriersScene->getLevelBoxes(), glm::vec3(1.0,0.0,0.0), "barriers"); + radarMeshNode->hideItems(); + + addMeshNode2D(radarMeshNode); + } + + void MainScene::initLabels() { + const auto shader = resourceManager->getShader("textShader"); + const auto font = make_shared("Assets/Fonts/OCRAEXT.TTF", 26); + const auto settings = make_shared(font); + const auto label = make_shared("Press start I, K or L...", shader, settings); + label->alignVerticalCenter(static_cast(width), static_cast(height)); + + fadeOutUniform = make_shared(); + const auto shaderMaterial = make_shared(shader); + shaderMaterial->addUniform("alpha", fadeOutUniform); + shaderMaterial->addUniform("textColor", glm::vec3(1.0f)); + shaderMaterial->addUniform("textTexture", 0); + fadeOutUniform->setFinishedCallback([this]() { + helpText->setVisible(false); + }); + + label->setMaterial(shaderMaterial); + helpText = make_shared(contextState, label, resourceManager); + + tilesCounterText = make_shared("", shader, settings); + fadeInUniform = make_shared(); + const auto shaderMaterial2 = make_shared(shader); + shaderMaterial2->addUniform("alpha", fadeInUniform); + shaderMaterial2->addUniform("textColor", glm::vec3(1.0f)); + shaderMaterial2->addUniform("textTexture", 0); + + tilesCounterText->setMaterial(shaderMaterial2); + tilesCounterNode = make_shared(contextState, tilesCounterText, resourceManager); + tilesCounterNode->setVisible(false); + + addMeshNode2D(helpText); + addMeshNode2D(tilesCounterNode); + } + + void MainScene::buildEatenUpCallback() const { + snakeMoveHandler->setEatenUpCallback([this]() { + if (this->levelManager) { + // alSourcePlay (coinSource); + // + // if (const ALCenum error = alGetError(); error != AL_NO_ERROR) { + // cout << "Sound error" << endl; + // } + + coinScene->getRemoveCoin()->setTransform(coinScene->getCoin()); + coinScene->getRemoveCoin()->setVisible(true); + coinScene->getRemoveCoin()->animationStart("eatenUp", false); + + this->levelManager->setEatCounter(this->levelManager->getEatCounter() + 1); + + if (this->levelManager->getEatCounter() == MAX_POINT) { + fadeOutUniform->setAlpha(1.0f); + coinScene->getCoin()->setVisible(false); + // playerScene->getSnake()->respawn(); + // this->eat->setVisible(false); + // this->levelManager->createLevel(this->levelManager->getLevel() + 1); + // this->eatManager->run(Manager::EatManager::clean); + } else { + this->eatManager->run(EatManager::eatenUp); + } + + char buff[100]; + snprintf(buff, sizeof(buff), + "%s %d, %s %d, %s %d", + "Level:", + this->levelManager->getLevel(), + "Lives:", + this->levelManager->getLive(), + "Points left:", + MAX_POINT - this->levelManager->getEatCounter() + ); + const std::string buffAsStdStr = buff; + tilesCounterText->setText(buffAsStdStr); + } + }); + } + + void MainScene::buildStartMoveCallback() const { + snakeMoveHandler->addStartMoveCallback([this]() { + if (this->levelManager) { + this->eatManager->run(EatManager::firstPlace); + fadeOutUniform->start(); + char buff[100]; + snprintf(buff, sizeof(buff), + "%s %d, %s %d, %s %d", + "Level:", + this->levelManager->getLevel(), + "Lives:", + this->levelManager->getLive(), + "Points left:", + MAX_POINT - this->levelManager->getEatCounter() + ); + const std::string buffAsStdStr = buff; + tilesCounterNode->setVisible(true); + tilesCounterText->setText(buffAsStdStr); + fadeInUniform->start(); + radarMeshNode->showItem("coin"); + coinScene->getCoin()->animationStart("coinRotation"); + playerScene->getSnake()->animationStart("KostraAction", true); + } + }); + } + + void MainScene::buildCrashCallback() { + snakeMoveHandler->setCrashCallback([this]() { + if (this->levelManager) { + playerScene->getSnake()->respawn(); + this->levelManager->setLive(this->levelManager->getLive() - 1); + this->levelManager->setEatCounter(0); + char buff[100]; + snprintf(buff, sizeof(buff), + "%s %d, %s %d, %s %d", + "Level:", + this->levelManager->getLevel(), + "Lives:", + this->levelManager->getLive(), + "Points left:", + MAX_POINT - this->levelManager->getEatCounter() + ); + const std::string buffAsStdStr = buff; + tilesCounterText->setText(buffAsStdStr); + coinScene->getCoin()->setVisible(false); + if (this->levelManager->getLive() == 0) { + // Game Over + this->levelManager->createLevel(1, directionalLight, spotLights, pointLights); + fadeOutUniform->setAlpha(1.0f); + this->levelManager->setLive(3); + cout << "crash callback call" << endl; + } + } + }); + } + + void MainScene::update() { + Scene::update(); + + if (radarMeshNode->isVisible() && radarFadeOutUniform->getAlpha() <= 0) { + radarMeshNode->setVisible(false); + } + + if (!helpText->isVisible()) { + eatManager->run(EatManager::checkPlace); + } + } +} // Scenes diff --git a/Scenes/MainScene.h b/Scenes/MainScene.h new file mode 100644 index 00000000..ed85454e --- /dev/null +++ b/Scenes/MainScene.h @@ -0,0 +1,92 @@ +#ifndef SNAKE3_MAINSCENE_H +#define SNAKE3_MAINSCENE_H + +#include + +#include "BarriersScene.h" +#include "CoinScene.h" +#include "PlayerScene.h" +#include "../Manager/EatManager.h" +#include "../Renderer/Opengl/Material/PlanarReflectionMaterial.h" +#include "../Renderer/Opengl/Material/Uniform/FadeInUniform.h" +#include "../Renderer/Opengl/Material/Uniform/FadeOutUniform.h" +#include "../Renderer/Opengl/Scene/Scene.h" +#include "../Renderer/Opengl/Model/Game/RadarMeshNode2D.h" +#include "../Renderer/Opengl/Model/Standard/2D/LabelNode2D.h" +#include "../Renderer/Opengl/Model/Standard/2D/QuadNode2D.h" + +using namespace Uniform; +using namespace Physic; +using namespace Scenes; +using namespace std; + +namespace Scenes { + class MainScene final : public Scene { + public: + explicit MainScene( + const shared_ptr &directionalLight, + const vector > &spotLights, + const vector > &pointLights, + const shared_ptr &rendererManager, + const shared_ptr &camera, const glm::mat4 &projection, + const shared_ptr &rm, int width, int height); + + void init(int priority) override; + + void update() override; + + void keyboardInput(GLFWwindow *window, int keyCode, int scancode, int action, int mods) const override; + + private: + void initLights(); + + void initPlayerScene(); + + void initBarriersScene(); + + void initCoinScene(); + + void initTorchScene(); + + void initWeatherScene(); + + void initSkybox(); + + void initPlane(); + + void initEatManager(); + + void initRadar(); + + void initLabels(); + + void buildEatenUpCallback() const; + + void buildStartMoveCallback() const; + + void buildCrashCallback(); + + shared_ptr playerScene; + shared_ptr coinScene; + shared_ptr barriersScene; + unique_ptr eatManager; + shared_ptr levelManager; + shared_ptr collisionDetector; + shared_ptr snakeMoveHandler; + shared_ptr fadeOutUniform; + shared_ptr fadeInUniform; + shared_ptr tilesCounterText; + shared_ptr helpText; + shared_ptr tilesCounterNode; + shared_ptr radarExpansionIn; + shared_ptr radarExpansionOut; + shared_ptr radarFadeInUniform; + shared_ptr radarFadeOutUniform; + shared_ptr radarNode; + shared_ptr radarMeshNode; + shared_ptr planeMaterial; + glm::mat4 ortho{}; + }; +} // Scenes + +#endif //SNAKE3_MAINSCENE_H diff --git a/Scenes/PlayerScene.cpp b/Scenes/PlayerScene.cpp new file mode 100644 index 00000000..daf9af2d --- /dev/null +++ b/Scenes/PlayerScene.cpp @@ -0,0 +1,92 @@ +#include "PlayerScene.h" + +#include "../Physic/BoxShape.h" +#include "../Physic/SphereShape.h" +#include "../Renderer/Opengl/Model/Standard/AnimationArrayMesh.h" + +namespace Scenes { + PlayerScene::PlayerScene( + const shared_ptr &directionalLight, + const vector > &spotLights, + const vector > &pointLights, + const shared_ptr &rendererManager, const shared_ptr &camera, + const glm::mat4 &projection, const shared_ptr &rm, const int width, const int height) + : Scene(directionalLight, spotLights, pointLights, rendererManager, camera, projection, rm, width, height) { + } + + void PlayerScene::init(const int priority) { + Scene::init(priority); + initSnake(); + initSnakeMoveHandler(); + } + + shared_ptr PlayerScene::getSnake() const { + return snake; + } + + shared_ptr PlayerScene::getSnakeMoveHandler() const { + return snakeMoveHandler; + } + + void PlayerScene::initSnake() { + const auto shader = resourceManager->getShader("basicShader"); + const auto shadowsShader = resourceManager->getShader("shadowDepthShader"); + const auto pacmanMesh = make_shared(resourceManager->getAnimationModel("pacman"), shader, "KostraAction"); + + const auto directionalLight = make_shared(); + directionalLight->setDirection({1, 1.0, -3}); + directionalLight->setAmbient({0.2f, 0.2f, 0.2f}); + directionalLight->setDiffuse({0.1f, 0.1f, 0.1f}); + directionalLight->setSpecular({.091f, .091f, .091f}); + + const auto material = make_shared(shader, shadowsShader); + material->setShadow(resourceManager->getTexture("depth")); + material->setNormalEnabled(true); + material->setDirectionalLight(directionalLight); + material->setSpotLights(spotLights); + material->setPointLights(pointLights); + pacmanMesh->setMaterial(material); + pacmanMesh->getAnimationPlayer()->setAcceleration(2.5f); + + snake = make_shared(contextState, pacmanMesh, resourceManager, collisionSystem); + snake->setDirectionalLight(directionalLight); + snake->setScale({0.041667f, 0.041667f, 0.041667f}); + snake->setSpotLights(spotLights); + snake->setPointLights(pointLights); + snake->respawn(); + + camera->setStickyPoint(snake); + + const auto sphereShape = make_shared(resourceManager, contextState,0.77f); + const auto shape = make_shared(contextState, resourceManager, sphereShape); + + snake->setCollisionShape(shape); + + addMeshNode3D(snake); + collisionSystem->addCollider(snake); + } + + void PlayerScene::initSnakeMoveHandler() { + snakeMoveHandler = make_shared(snake); + keyboardManager->addEventHandler(snakeMoveHandler); + + buildStartMoveCallback(); + buildStopMoveCallback(); + } + + void PlayerScene::buildStartMoveCallback() const { + snakeMoveHandler->addStartMoveCallback([this]() { + this->snake->animationStart("KostraAction"); + }); + } + + void PlayerScene::buildStopMoveCallback() const { + snakeMoveHandler->setStopMoveCallback([this](const bool stop) { + if (stop) { + this->snake->animationPause("KostraAction"); + } else { + this->snake->animationResume("KostraAction"); + } + }); + } +} // Scenes \ No newline at end of file diff --git a/Scenes/PlayerScene.h b/Scenes/PlayerScene.h new file mode 100644 index 00000000..5b29da69 --- /dev/null +++ b/Scenes/PlayerScene.h @@ -0,0 +1,43 @@ +#ifndef SNAKE3_PLAYERSCENE_H +#define SNAKE3_PLAYERSCENE_H + +#include + +#include "../Renderer/Opengl/Model/Game/SnakeMeshNode3D.h" +#include "../Renderer/Opengl/Model/Standard/Animation/AnimationPlayer.h" +#include "../Renderer/Opengl/Scene/Scene.h" + +using namespace Model; +using namespace Animations; + +namespace Scenes { + class PlayerScene final : public Scene { + public: + PlayerScene( + const shared_ptr &directionalLight, + const vector > &spotLights, + const vector > &pointLights, + const shared_ptr &rendererManager, const shared_ptr &camera, + const glm::mat4 &projection, const shared_ptr &rm, int width, int height); + + void init(int priority) override; + + [[nodiscard]] shared_ptr getSnake() const; + + [[nodiscard]] shared_ptr getSnakeMoveHandler() const; + + protected: + void initSnake(); + + void initSnakeMoveHandler(); + + void buildStartMoveCallback() const; + + void buildStopMoveCallback() const; + + shared_ptr snake; + shared_ptr snakeMoveHandler; + }; +} // Scenes + +#endif //SNAKE3_PLAYERSCENE_H diff --git a/Scenes/PreloaderScene.cpp b/Scenes/PreloaderScene.cpp new file mode 100644 index 00000000..9d8ba606 --- /dev/null +++ b/Scenes/PreloaderScene.cpp @@ -0,0 +1,29 @@ +#include "PreloaderScene.h" + +#include "../Renderer/Opengl/Model/SpinnerMesh.h" + +namespace Scenes { + PreloaderScene::PreloaderScene( + const shared_ptr &directionalLight, + const vector > &spotLights, + const vector > &pointLights, + const shared_ptr &rendererManager, const shared_ptr &camera, + const glm::mat4 &projection, const shared_ptr &rm, const int width, const int height) + : Scene(directionalLight, spotLights, pointLights, rendererManager, camera, projection, rm, width, height) { + } + + void PreloaderScene::init(const int priority) { + Scene::init(priority); + + const auto preLoader = initPreloader(); + camera->setStickyPoint(preLoader); + addMeshNode3D(preLoader); + } + + shared_ptr PreloaderScene::initPreloader() const { + auto shader = resourceManager->getShader("preloadShader"); + auto shadowDepthShader = resourceManager->getShader("shadowDepthShader"); + + return make_shared(contextState, make_shared(shader), resourceManager); + } +} // Scenes \ No newline at end of file diff --git a/Scenes/PreloaderScene.h b/Scenes/PreloaderScene.h new file mode 100644 index 00000000..20607a0f --- /dev/null +++ b/Scenes/PreloaderScene.h @@ -0,0 +1,23 @@ +#ifndef SNAKE3_PRELOADERSCENE_H +#define SNAKE3_PRELOADERSCENE_H + +#include "../Renderer/Opengl/Scene/Scene.h" + +namespace Scenes { + class PreloaderScene : public Scene { + public: + PreloaderScene( + const shared_ptr &directionalLight, + const vector > &spotLights, + const vector > &pointLights, + const shared_ptr &rendererManager, const shared_ptr &camera, + const glm::mat4 &projection, const shared_ptr &rm, int width, int height); + + void init(int priority) override; + + protected: + [[nodiscard]] shared_ptr initPreloader() const; + }; +} // Scenes + +#endif //SNAKE3_PRELOADERSCENE_H diff --git a/Scenes/TorchScene.cpp b/Scenes/TorchScene.cpp new file mode 100644 index 00000000..ab322c2d --- /dev/null +++ b/Scenes/TorchScene.cpp @@ -0,0 +1,199 @@ +#include "TorchScene.h" + +#include + +#include "../Handler/Debug/PositionHandler.h" +#include "../Physic/BoxShape.h" +#include "../Physic/CylinderShape.h" +#include "../Renderer/Opengl/Model/Collision/CollisionShape3D.h" +#include "../Renderer/Opengl/Model/Game/BarrelNode3D.h" +#include "../Renderer/Opengl/Model/Game/StreetLampNode3D.h" +#include "../Renderer/Opengl/Model/Standard/ArrayMesh.h" +#include "../Renderer/Opengl/Model/Standard/QuadMesh3D.h" + +namespace Scenes { + TorchScene::TorchScene( + const shared_ptr &directionalLight, + const vector > &spotLights, + const vector > &pointLights, + const shared_ptr &rendererManager, const shared_ptr &camera, + const glm::mat4 &projection, const shared_ptr &rm, const int width, const int height) + : Scene(directionalLight, spotLights, pointLights, rendererManager, camera, projection, rm, width, height) { + } + + void TorchScene::init(const int priority) { + Scene::init(priority); + + quad = make_shared(resourceManager->getShader("basicShader"), 1.7, 1.7); + quad->setBlending(Blending::AlphaAdditive); + quad->setDepthWrite(false); + + initTorch(); + + const auto streetLamp = make_shared(contextState, resourceManager); + streetLamp->setDirectionalLight(directionalLight); + streetLamp->setPointLights(pointLights); + streetLamp->init(); + addMeshNode3D(streetLamp); + + const auto barrel = make_shared(contextState, resourceManager); + barrel->setDirectionalLight(directionalLight); + barrel->setPointLights(pointLights); + barrel->init(); + + const auto shape = make_shared(resourceManager, contextState, 0.3f, 0.90f); + const auto collisionShape = make_shared(contextState, resourceManager, shape); + collisionShape->setPosition(glm::vec3(0, 0.40f, 0)); + + barrel->addNode(collisionShape); + + if (collisionSystem != nullptr) { + collisionSystem->addCollider(barrel); + } + + addMeshNode3D(barrel); + } + + void TorchScene::initTorch() { + const auto torch = make_shared(resourceManager->getShader("basicShader")); + torch->fromMesh(resourceManager->getModel("torch")); + + const auto torchAlbedo = resourceManager->getTexture("torch.png"); + const auto torchNormal = resourceManager->getTexture("torch_normal.png"); + const auto torchMaterial = make_shared( + resourceManager->getShader("basicShader"), + resourceManager->getShader("shadowDepthShader") + ); + torchMaterial->setAlbedo(torchAlbedo); + torchMaterial->setNormal(torchNormal); + torchMaterial->setNormalEnabled(true); + torchMaterial->setDirectionalLight(directionalLight); + torchMaterial->setBlending(Blending::Opaque); + + for (auto &spotLight : spotLights) { + torchMaterial->addSpotLight(spotLight); + } + + torch->setMaterial(torchMaterial); + + const auto torchNode = make_shared(contextState, torch, resourceManager); + const auto torchNode2 = make_shared(contextState, torch, resourceManager); + const auto torchNode3 = make_shared(contextState, torch, resourceManager); + const auto torchNode4 = make_shared(contextState, torch, resourceManager); + + torchNode->setRotationX(90); + torchNode2->setRotationX(90); + torchNode3->setRotationX(90); + torchNode4->setRotationX(90); + torchNode->setScale({0.2, 0.2, 0.2}); + torchNode2->setScale({0.2, 0.2, 0.2}); + torchNode3->setScale({0.2, 0.2, 0.2}); + torchNode4->setScale({0.2, 0.2, 0.2}); + torchNode->setPosition({-5.07928, -5.47677, -3.48698}); + torchNode2->setPosition({15.2239, -5.47677, -3.48698}); + torchNode3->setPosition({15.2753, 15.4487, -3.48698}); + torchNode4->setPosition({-5.07928, 15.4487, -3.48698}); + + const auto smoke = initSmoke(); + torchNode->addNode(smoke); + torchNode2->addNode(smoke); + torchNode3->addNode(smoke); + torchNode4->addNode(smoke); + + const auto fire = initFire(); + torchNode->addNode(fire); + torchNode2->addNode(fire); + torchNode3->addNode(fire); + torchNode4->addNode(fire); + + const auto boxShape = make_shared(resourceManager, contextState,glm::vec3(0.88, 1.9, 0.9)); + const auto boxShape2 = make_shared(resourceManager, contextState,glm::vec3(0.88, 1.9, 0.9)); + const auto boxShape3 = make_shared(resourceManager, contextState,glm::vec3(1, 2, 1)); + const auto boxShape4 = make_shared(resourceManager, contextState,glm::vec3(1, 2, 1)); + const auto shape = make_shared(contextState, resourceManager, boxShape); + const auto shape2 = make_shared(contextState, resourceManager, boxShape2); + const auto shape3 = make_shared(contextState, resourceManager, boxShape3); + const auto shape4 = make_shared(contextState, resourceManager, boxShape4); + shape->setPosition(glm::vec3(0, -0.5f, 0)); + shape2->setPosition(glm::vec3(0, -0.5f, 0)); + shape3->setPosition(glm::vec3(0, -0.5f, 0)); + shape4->setPosition(glm::vec3(0, -0.5f, 0)); + //torchNode->addNode(shape); + //torchNode2->addNode(shape2); + //torchNode3->addNode(shape3); + //torchNode4->addNode(shape4); + collisionSystem->addCollider(torchNode); + collisionSystem->addCollider(torchNode2); + collisionSystem->addCollider(torchNode3); + collisionSystem->addCollider(torchNode4); + + addMeshNode3D(torchNode, 1); + addMeshNode3D(torchNode2, 1); + addMeshNode3D(torchNode3, 1); + addMeshNode3D(torchNode4, 1); + + positionHandler->addItem(torchNode); + } + + shared_ptr TorchScene::initFire() { + const auto gravity = glm::vec3( + glm::linearRand(-0.005f, 0.005f), + glm::linearRand(0.01f, 0.001f), + glm::linearRand(0.005f, 0.009f) + ); + + const auto material = make_shared(resourceManager); + material->set_texture("fire.png"); + material->set_mode(Stretched); + + material->set_life_min(0.5f); + material->set_life_max(1.0f); + material->set_size_min(0.008f); + material->set_size_max(0.042f); + material->set_stretch(0.105f); + material->set_vel_min({-0.005f, 0.000f, 0.100f}); + material->set_vel_max({ 0.005f, 0.010f, 0.200f}); + material->set_gravity(gravity); + material->set_emitter_radius(0.03f); + material->set_emitter_y_offset(0.24f); + material->set_color_start({6.0f, 3.5f, 1.0f, 1.0f}); + material->set_color_end({7.0f, 4.5f, 1.5f, 0.0f}); + material->set_min_radius(0.05f); + material->set_spawn_per_frame(0.6f); + + const auto fire = make_shared(material, contextState, camera, quad, resourceManager, 1000); + fire->setPosition(glm::vec3(0.0, -0.1, 0.0)); + fire->setScale({2.2, 2.2, 2.2}); + fire->setRotationX(-90); + + return fire; + } + + shared_ptr TorchScene::initSmoke() { + const auto material = make_shared(resourceManager); + material->set_texture("smoke.png"); + material->set_mode(Stretched); + + material->set_life_min(2.0f); + material->set_life_max(0.5f); + material->set_size_min(0.08f); + material->set_size_max(0.042f); + material->set_stretch(0.15f); + material->set_vel_min({-0.005f, 0.000f, 0.100f}); + material->set_vel_max({ 0.005f, 0.010f, 0.200f}); + material->set_gravity(glm::vec3(0.0f, -0.05f, 0.0f)); + material->set_emitter_radius(0.03f); + material->set_emitter_y_offset(0.24f); + material->set_color_start({0.4f, 0.4f, 0.4f, 0.8f}); + material->set_color_end({0.2f, 0.2f, 0.2f, 0.0f}); + material->set_min_radius(0.05f); + material->set_spawn_per_frame(0.6f); + + const auto smoke = make_shared(material, contextState, camera, quad, resourceManager, 10); + smoke->setPosition(glm::vec3(0.0, 0.067, 0.0)); + smoke->setScale({2.0, 2.0, 2.0}); + smoke->setRotationX(-90); + + return smoke; + } +} // Scenes \ No newline at end of file diff --git a/Scenes/TorchScene.h b/Scenes/TorchScene.h new file mode 100644 index 00000000..d6f16fde --- /dev/null +++ b/Scenes/TorchScene.h @@ -0,0 +1,36 @@ +#ifndef SNAKE3_TORCHSCENE_H +#define SNAKE3_TORCHSCENE_H + +#include + +#include "../Renderer/Opengl/Scene/Scene.h" +#include "../Renderer/Opengl/Model/Standard/GPUParticle3D.h" +#include "../Renderer/Opengl/Model/Standard/QuadMesh3D.h" + +using namespace Model; +using namespace std; + +namespace Scenes { + class TorchScene final : public Scene { + public: + TorchScene( + const shared_ptr &directionalLight, + const vector > &spotLights, + const vector > &pointLights, + const shared_ptr &rendererManager, const shared_ptr &camera, + const glm::mat4 &projection, const shared_ptr &rm, int width, int height); + + void init(int priority) override; + + protected: + void initTorch(); + + shared_ptr initFire(); + shared_ptr initSmoke(); + + shared_ptr quad; + shared_ptr torchNode; + }; +} // Scenes + +#endif //SNAKE3_TORCHSCENE_H diff --git a/Scenes/WeatherScene.cpp b/Scenes/WeatherScene.cpp new file mode 100644 index 00000000..8cee059b --- /dev/null +++ b/Scenes/WeatherScene.cpp @@ -0,0 +1,145 @@ +#include "WeatherScene.h" + +#include "../Renderer/Opengl/Model/Standard/GPUParticle3D.h" +#include "../Renderer/Opengl/Model/Standard/QuadMesh3D.h" +#include "../Renderer/Opengl/Model/Standard/2D/GPUParticle2D.h" +#include "../Renderer/Opengl/Model/Standard/2D/QuadNode2D.h" + +namespace Scenes { + WeatherScene::WeatherScene( + const shared_ptr &directionalLight, + const vector > &spotLights, + const vector > &pointLights, + const shared_ptr &rendererManager, const shared_ptr &camera, const glm::mat4 &projection, + const shared_ptr &rm, const int width, const int height) + : Scene(directionalLight, spotLights, pointLights, rendererManager, camera, projection, rm, width, height) { + } + + void WeatherScene::init(const int priority) { + Scene::init(priority); + + quad = make_shared(resourceManager->getShader("basicShader"), 1.7, 1.7); + quad->setBlending(Blending::AlphaAdditive); + quad->setDepthTest(false); + quad->setDepthWrite(false); + + // initRain(); + // initRainDrop(); + // initSnow(); + // initExplosion(); + } + + void WeatherScene::initRain() { + const auto material = make_shared(resourceManager); + material->set_texture("rain.png"); + material->set_mode(Billboard); + material->set_spawn_shape(1); + material->set_respawn_mode(1); + material->set_turbulence({0.0f, 0.0f}); + material->set_min_radius(3.0f); + material->set_max_radius(30.0f); + material->set_spawn_height(25.0f); + material->set_color_start({0.25f, 0.35f, 0.8f, 0.45f}); + material->set_color_end({0.25f, 0.35f, 0.8f, 0.45f}); + material->set_color_sensitivity(26.0f); + material->set_gravity({0.0f, 0.0f, -2.8f}); + material->set_emitter_radius(25.0f); + material->set_emitter_y_offset(15.0f); + material->set_vel_min({-0.2f, -0.2f, -15.0f}); + material->set_vel_max({0.2f, 0.2f, -25.0f}); + material->set_life_min(1.0f); + material->set_life_max(1.8f); + material->set_size_min(0.012f); + material->set_size_max(0.012f); + material->set_stretch(0.003f); + + const auto rain = make_shared(material, contextState, camera, quad, resourceManager, 6000); + + addMeshNode3D(rain); + } + + void WeatherScene::initRainDrop() { + const auto material = make_shared(resourceManager); + material->set_texture("drop_normal.png"); + material->set_spawn_shape(1); + material->set_respawn_mode(1); + material->set_turbulence({1.0f, 0.0f}); + material->set_gravity({0.0f, -0.5f, 0.0f}); + material->set_vel_min({0.0f, -0.1f, 0.0f}); + material->set_vel_max({0.0f, -0.8f, 0.0f}); + material->set_life_min(1.0f); + material->set_life_max(5.0f); + material->set_size_min(0.009f); + material->set_size_max(0.03f); + material->set_color_start({0.8f, 0.9f, 1.0f, 0.3f}); + material->set_color_end({0.8f, 0.9f, 1.0f, 0.0f}); + material->set_color_sensitivity(1.0f); + material->set_drag(0.5f); + material->set_emitter_size({0.9f, 1.2f}); + material->set_spawn_per_frame(1.0f); + + const auto quad2D = make_shared(0.9, 1.2); + const auto rainDrop2D = make_shared(material, contextState, quad2D, resourceManager, 5); + + addMeshNode2D(rainDrop2D, 1); + } + + void WeatherScene::initSnow() { + const auto material = make_shared(resourceManager); + material->set_texture("snow.png"); + material->set_mode(Billboard); + material->set_spawn_shape(1); + material->set_respawn_mode(1); + material->set_turbulence({0.5f, 0.8f}); + material->set_min_radius(4.0f); + material->set_max_radius(25.0f); + material->set_spawn_height(20.0f); + material->set_color_start({1.0f, 1.0f, 1.0f, 1.0f}); + material->set_color_end({1.0f, 1.0f, 1.0f, 0.0f}); + material->set_color_sensitivity(2.0f); + material->set_gravity({0.0f, 0.0f, -0.5f}); + material->set_emitter_radius(30.0f); + material->set_emitter_y_offset(10.0f); + material->set_vel_min({-1.5f, -1.5f, -0.8f}); + material->set_vel_max({1.5f, 1.5f, -1.8f}); + material->set_life_min(1.0f); + material->set_life_max(5.0f); + material->set_size_min(0.04f); + material->set_size_max(0.12f); + material->set_stretch(0.0f); + material->set_spawn_per_frame(0); + + const auto snow = make_shared(material, contextState, camera, quad, resourceManager, 6000); + + addMeshNode3D(snow); + } + + void WeatherScene::initExplosion() { + const auto material = make_shared(resourceManager); + material->set_texture("explosion.png"); + material->set_mode(Stretched); + material->set_spawn_shape(2); + material->set_respawn_mode(0); + material->set_life_min(0.1f); + material->set_life_max(0.6f); + material->set_size_max(0.01f); + material->set_size_min(0.002f); + material->set_stretch(0.0f); + material->set_vel_min({ 0.5f, 0.0f, 0.0f }); + material->set_vel_max({ 1.3f, 0.0f, 0.0f }); + material->set_gravity({0.0f, 0.0f, -0.9f}); + material->set_emitter_radius(0.05f); + material->set_color_start({8.0f, 4.0f, 1.0f, 1.0f}); + material->set_color_end({0.1f, 0.1f, 0.1f, 0.0f}); + + const auto explosion1 = make_shared(material, contextState, camera, quad, resourceManager, 500); + const auto explosion2 = make_shared(material, contextState, camera, quad, resourceManager, 500); + + explosion1->setPosition(glm::vec3(0.0, 0.6, 0.0)); + explosion2->setPosition(glm::vec3(2.0, 0.6, 0.0)); + explosion2->setTimeOffset(0.2f); + + addMeshNode3D(explosion1); + addMeshNode3D(explosion2); + } +} // Scenes \ No newline at end of file diff --git a/Scenes/WeatherScene.h b/Scenes/WeatherScene.h new file mode 100644 index 00000000..1c217c52 --- /dev/null +++ b/Scenes/WeatherScene.h @@ -0,0 +1,32 @@ +#ifndef SNAKE3_WEATHERSCENE_H +#define SNAKE3_WEATHERSCENE_H + +#include + +#include "../Renderer/Opengl/Model/Standard/QuadMesh3D.h" +#include "../Renderer/Opengl/Scene/Scene.h" + +using namespace std; + +namespace Scenes { + class WeatherScene final : public Scene { + public: + WeatherScene( + const shared_ptr &directionalLight, + const vector > &spotLights, + const vector > &pointLights, + const shared_ptr &rendererManager, const shared_ptr &camera, + const glm::mat4 &projection, const shared_ptr &rm, int width, int height); + + void init(int priority) override; + protected: + void initRain(); + void initRainDrop(); + void initSnow(); + void initExplosion(); + + shared_ptr quad; + }; +} // Scenes + +#endif //SNAKE3_WEATHERSCENE_H \ No newline at end of file diff --git a/Tests/main.cpp b/Tests/main.cpp index 90796847..30cd5e65 100644 --- a/Tests/main.cpp +++ b/Tests/main.cpp @@ -1,27 +1,244 @@ -#include "../App.h" -#include #define CATCH_CONFIG_MAIN -#include -#include - -using namespace Handler; -using namespace Manager; - -TEST_CASE( "Checking if field is empty to place food..." ) { - - auto snake = new Snake; - snake->init(); - snake->reset(); - auto eat = new Eat; - auto barriers = new Barriers; - auto radar = new Radar; - auto eatLocation = new EatLocationHandler(barriers, snake, eat, radar); - auto levelManager = new LevelManager(1, 3, barriers); - levelManager->createLevel(2); - - REQUIRE( barriers->getItems().size() == 40 ); - REQUIRE( eatLocation->isFieldEmpty(4, 24) == false ); - REQUIRE( eatLocation->isFieldEmpty(5, 24) == false ); - REQUIRE( eatLocation->isFieldEmpty(4, 25) == true ); - REQUIRE( eatLocation->isFieldEmpty(5, 25) == true ); +#include +#include "../Physic/Algorithms/CollisionAlgorithms.h" +#include "../Physic/BoxShape.h" +#include "../Physic/SphereShape.h" +#include "../Physic/CapsuleShape.h" +#include "../Physic/CylinderShape.h" + +using namespace Physic; + +// Mock CollisionEntry for testing +struct MockCollisionEntry : public CollisionEntry { + explicit MockCollisionEntry(const shared_ptr& shape) { + shapeNode = make_shared(nullptr, nullptr, shape); + parentObject = make_shared(nullptr, nullptr, nullptr); + } +}; + +TEST_CASE("BoxVsSphere Collision Detection") { + auto boxShape = make_shared(nullptr, nullptr, glm::vec3(2.0f)); // 2x2x2 box, halfExtents 1,1,1 + auto sphereShape = make_shared(nullptr, nullptr, 0.5f); // radius 0.5 + + MockCollisionEntry entBox(boxShape); + MockCollisionEntry entSphere(sphereShape); + + SECTION("No collision - sphere far away") { + entSphere.parentObject->setPosition({3.0f, 0.0f, 0.0f}); // Center at 3, box ends at 1. Gap 1.5. + CHECK_FALSE(Algorithms::BoxVsSphere(entBox, entSphere)); + } + + SECTION("Collision - sphere touching box face") { + entSphere.parentObject->setPosition({1.4f, 0.0f, 0.0f}); // Center at 1.4, radius 0.5 -> edge at 0.9. Box edge at 1.0. + CHECK(Algorithms::BoxVsSphere(entBox, entSphere)); + } + + SECTION("No collision - sphere just outside") { + entSphere.parentObject->setPosition({1.6f, 0.0f, 0.0f}); // Center at 1.6, radius 0.5 -> edge at 1.1. Box edge at 1.0. + CHECK_FALSE(Algorithms::BoxVsSphere(entBox, entSphere)); + } + + SECTION("Collision - sphere at corner") { + // Box corner at (1, 1, 1) + // Sphere center at (1.3, 1.3, 1.3), radius 0.5. + // Distance from corner to center: sqrt(0.3^2 + 0.3^2 + 0.3^2) = sqrt(0.27) approx 0.5196 + // 0.5196 > 0.5, so NO collision. + entSphere.parentObject->setPosition({1.3f, 1.3f, 1.3f}); + CHECK_FALSE(Algorithms::BoxVsSphere(entBox, entSphere)); + + // Sphere center at (1.2, 1.2, 1.2), radius 0.5. + // Distance: sqrt(0.2^2 + 0.2^2 + 0.2^2) = sqrt(0.12) approx 0.346 + // 0.346 < 0.5, so YES collision. + entSphere.parentObject->setPosition({1.2f, 1.2f, 1.2f}); + CHECK(Algorithms::BoxVsSphere(entBox, entSphere)); + } + + SECTION("Collision - with rotation and scale") { + boxShape = make_shared(nullptr, nullptr, glm::vec3(2.0f)); + // Transformace je S * T * R + // Pro box (0,0,0) s pozicí (0,0,0) je to jedno. + // Ale pokud dáme pozici a scale: + entBox.parentObject->setPosition({0.0f, 2.0f, 0.0f}); + entBox.parentObject->setScale({1.0f, 2.0f, 1.0f}); + // Světová pozice = scale * position = (1,2,1) * (0,2,0) = (0, 4, 0). + // Velikost boxu = size * scale = (2,2,2) * (1,2,1) = (2, 4, 2). + // Střed v (0,4,0), half-extents (1, 2, 1). + // Horní hrana Y = 4 + 2 = 6. Spodní hrana Y = 4 - 2 = 2. + + // Koule poloměr 0.5. Chceme kolizi na spodní hraně (Y=2). + // Pozice koule (0, 1.6, 0). Scale koule (1,1,1). + // Světová pozice koule = (1,1,1) * (0, 1.6, 0) = (0, 1.6, 0). + // Koule zasahuje do Y = 1.6 + 0.5 = 2.1. Mělo by kolidovat s Y=2. + entSphere.parentObject->setPosition({0.0f, 1.6f, 0.0f}); + CHECK(Algorithms::BoxVsSphere(entBox, entSphere)); + + entSphere.parentObject->setPosition({0.0f, 1.4f, 0.0f}); // Vrchol koule na 1.9, box končí na 2.0. + CHECK_FALSE(Algorithms::BoxVsSphere(entBox, entSphere)); + } +} + +TEST_CASE("BoxVsBox SAT Collision Detection") { + auto boxA = make_shared(nullptr, nullptr, glm::vec3(2.0f)); + auto boxB = make_shared(nullptr, nullptr, glm::vec3(2.0f)); + + MockCollisionEntry entA(boxA); + MockCollisionEntry entB(boxB); + + SECTION("Collision - overlapping") { + entB.parentObject->setPosition({1.5f, 0.0f, 0.0f}); + CHECK(Algorithms::BoxVsBox(entA, entB)); + } + + SECTION("No collision - separated") { + entB.parentObject->setPosition({2.5f, 0.0f, 0.0f}); + CHECK_FALSE(Algorithms::BoxVsBox(entA, entB)); + } + + SECTION("Collision - rotated") { + entB.parentObject->setPosition({1.5f, 1.5f, 0.0f}); + entB.parentObject->setRotationZ(45.0f); + // Box A: [-1, 1] + // Box B: center (1.5, 1.5), half-diagonal ~1.414. + // 1.5 - 1.414 = 0.086. Corner of B is at (0.086, 0.086) inside A. + CHECK(Algorithms::BoxVsBox(entA, entB)); + } +} + +TEST_CASE("Capsule Collision Detection") { + auto capsuleShape = make_shared(nullptr, nullptr, 0.5f, 2.0f); // radius 0.5, total height 2 (segment height 1) + MockCollisionEntry entCap(capsuleShape); + + SECTION("Capsule vs Sphere") { + auto sphereShape = make_shared(nullptr, nullptr, 0.5f); + MockCollisionEntry entSphere(sphereShape); + + // Capsule at (0,0,0), oriented along Y. Segment is from (0, -0.5, 0) to (0, 0.5, 0). + // Sphere at (0, 1.2, 0). Distance to p1 is 0.7. Radii sum is 1.0. COLLISION. + entSphere.parentObject->setPosition({0.0f, 1.2f, 0.0f}); + CHECK(Algorithms::SphereVsCapsule(entSphere, entCap)); + + // Sphere at (0, 1.6, 0). Distance to p1 is 1.1. Radii sum 1.0. NO COLLISION. + entSphere.parentObject->setPosition({0.0f, 1.6f, 0.0f}); + CHECK_FALSE(Algorithms::SphereVsCapsule(entSphere, entCap)); + + // Sphere at (1.2, 0, 0). Distance to segment is 1.2. Radii sum 1.0. NO COLLISION. + entSphere.parentObject->setPosition({1.2f, 0.0f, 0.0f}); + CHECK_FALSE(Algorithms::SphereVsCapsule(entSphere, entCap)); + + // Sphere at (0.8, 0, 0). Distance to segment is 0.8. Radii sum 1.0. COLLISION. + entSphere.parentObject->setPosition({0.8f, 0.0f, 0.0f}); + CHECK(Algorithms::SphereVsCapsule(entSphere, entCap)); + } + + SECTION("Capsule vs Capsule") { + auto capsuleShapeB = make_shared(nullptr, nullptr, 0.5f, 2.0f); + MockCollisionEntry entCapB(capsuleShapeB); + + // CapA: (0, -0.5, 0) to (0, 0.5, 0) + // CapB at (1.2, 0, 0): (1.2, -0.5, 0) to (1.2, 0.5, 0). Distance 1.2. Radii sum 1.0. NO. + entCapB.parentObject->setPosition({1.2f, 0.0f, 0.0f}); + CHECK_FALSE(Algorithms::CapsuleVsCapsule(entCap, entCapB)); + + // CapB at (0.8, 0, 0): Distance 0.8. Radii sum 1.0. YES. + entCapB.parentObject->setPosition({0.8f, 0.0f, 0.0f}); + CHECK(Algorithms::CapsuleVsCapsule(entCap, entCapB)); + + // Crossed capsules + entCapB.parentObject->setPosition({0.0f, 0.0f, 0.8f}); + entCapB.parentObject->setRotationX(90.0f); // Oriented along Z + // Segment B: (0, 0, 0.3) to (0, 0, 1.3). Distance between segments is at (0,0,0.5) and (0,0,0.3) -> 0.2? + // Wait, Segment A is Y:[-0.5, 0.5], Segment B is Z:[0.3, 1.3] (if rotation is around X 90deg and it was Y oriented). + // Closest points: A(0, 0.5, 0), B(0, 0, 0.8)? No. + // Let's just trust the math if it's close. + CHECK(Algorithms::CapsuleVsCapsule(entCap, entCapB)); + } + + SECTION("Box vs Capsule") { + auto boxShape = make_shared(nullptr, nullptr, glm::vec3(2.0f)); // halfExtents 1,1,1 + MockCollisionEntry entBox(boxShape); + + // Box [-1, 1] + // Capsule at (1.3, 0, 0), radius 0.5. Closest point on segment is (1.3, 0, 0). + // Distance to box face (1, 0, 0) is 0.3. Radius 0.5. YES. + entCap.parentObject->setPosition({1.3f, 0.0f, 0.0f}); + CHECK(Algorithms::BoxVsCapsule(entBox, entCap)); + + // Capsule at (1.6, 0, 0). Distance 0.6. Radius 0.5. NO. + entCap.parentObject->setPosition({1.6f, 0.0f, 0.0f}); + CHECK_FALSE(Algorithms::BoxVsCapsule(entBox, entCap)); + } + + SECTION("Box vs Capsule oriented along Z") { + auto boxShape = make_shared(nullptr, nullptr, glm::vec3(2.0f)); // halfExtents 1,1,1 + MockCollisionEntry entBox(boxShape); + + auto capsuleShape = make_shared(nullptr, nullptr, 0.5f, 2.0f); // radius 0.5, total height 2 (segment height 1) + MockCollisionEntry entCap(capsuleShape); + entCap.parentObject->setRotationX(90.0f); // Now oriented along Z + + // Box [-1, 1] + // Capsule at (0, 0, 1.3). Segment is Z: [0.8, 1.8]. Radius 0.5. + // Closest point on segment to box is (0, 0, 0.8). Box max Z is 1.0. + // (0, 0, 0.8) is INSIDE box in Z, but also X,Y are 0 which are inside. + // Actually closest point on segment to box (max Z=1) is (0, 0, 0.8)? No, it's a point on segment. + // The segment is from (0,0,0.8) to (0,0,1.8). + // Point (0,0,0.8) is inside the box. + entCap.parentObject->setPosition({0.0f, 0.0f, 1.3f}); + CHECK(Algorithms::BoxVsCapsule(entBox, entCap)); + + // Capsule at (0, 0, 1.6). Segment Z: [1.1, 2.1]. Radius 0.5. + // Closest point on segment to box is (0,0,1.1). + // Distance from (0,0,1.1) to box face (0,0,1.0) is 0.1. Radius 0.5. YES. + entCap.parentObject->setPosition({0.0f, 0.0f, 1.6f}); + CHECK(Algorithms::BoxVsCapsule(entBox, entCap)); + + // Capsule at (0, 0, 2.1). Segment Z: [1.6, 2.6]. Radius 0.5. + // Closest point on segment to box is (0,0,1.6). + // Distance to (0,0,1.0) is 0.6. Radius 0.5. NO. + entCap.parentObject->setPosition({0.0f, 0.0f, 2.1f}); + CHECK_FALSE(Algorithms::BoxVsCapsule(entBox, entCap)); + } +} + +TEST_CASE("Cylinder Collision Detection") { + auto cylinderShape = make_shared(nullptr, nullptr, 0.5f, 2.0f); // radius 0.5, total height 2 + MockCollisionEntry entCyl(cylinderShape); + + SECTION("Cylinder vs Sphere") { + auto sphereShape = make_shared(nullptr, nullptr, 0.5f); + MockCollisionEntry entSphere(sphereShape); + + // Cylinder at (0,0,0), oriented along Y. Segment is from (0, -1, 0) to (0, 1, 0). + // Sphere at (0, 1.4, 0). Distance to p1 is 0.4. Radius 0.5. + // Plane of top disk is at y=1. 1.4-1.0 = 0.4. 0.4 < 0.5. Inside radius 0.5. COLLISION. + entSphere.parentObject->setPosition({0.0f, 1.4f, 0.0f}); + CHECK(Algorithms::SphereVsCylinder(entSphere, entCyl)); + + // Sphere at (0, 1.6, 0). Distance to p1 is 0.6. Radius 0.5. NO COLLISION. + entSphere.parentObject->setPosition({0.0f, 1.6f, 0.0f}); + CHECK_FALSE(Algorithms::SphereVsCylinder(entSphere, entCyl)); + + // Sphere at (1.2, 0, 0). Distance to segment is 1.2. Radii sum 1.0. NO COLLISION. + entSphere.parentObject->setPosition({1.2f, 0.0f, 0.0f}); + CHECK_FALSE(Algorithms::SphereVsCylinder(entSphere, entCyl)); + + // Sphere at (0.8, 0, 0). Distance to segment is 0.8. Radii sum 1.0. COLLISION. + entSphere.parentObject->setPosition({0.8f, 0.0f, 0.0f}); + CHECK(Algorithms::SphereVsCylinder(entSphere, entCyl)); + } + + SECTION("Box vs Cylinder") { + auto boxShape = make_shared(nullptr, nullptr, glm::vec3(2.0f)); // halfExtents 1,1,1 + MockCollisionEntry entBox(boxShape); + + // Box [-1, 1] + // Cylinder at (1.3, 0, 0), radius 0.5. Closest point on segment is (1.3, 0, 0). + // Distance to box face (1, 0, 0) is 0.3. Radius 0.5. YES. + entCyl.parentObject->setPosition({1.3f, 0.0f, 0.0f}); + CHECK(Algorithms::BoxVsCylinder(entBox, entCyl)); + + // Cylinder at (1.6, 0, 0). Distance 0.6. Radius 0.5. NO. + entCyl.parentObject->setPosition({1.6f, 0.0f, 0.0f}); + CHECK_FALSE(Algorithms::BoxVsCylinder(entBox, entCyl)); + } } \ No newline at end of file diff --git a/Thirdparty/stbimage/stb_image_write.h b/Thirdparty/stbimage/stb_image_write.h new file mode 100644 index 00000000..023d71e4 --- /dev/null +++ b/Thirdparty/stbimage/stb_image_write.h @@ -0,0 +1,1724 @@ +/* stb_image_write - v1.16 - public domain - http://nothings.org/stb + writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 + no warranty implied; use at your own risk + + Before #including, + + #define STB_IMAGE_WRITE_IMPLEMENTATION + + in the file that you want to have the implementation. + + Will probably not work correctly with strict-aliasing optimizations. + +ABOUT: + + This header file is a library for writing images to C stdio or a callback. + + The PNG output is not optimal; it is 20-50% larger than the file + written by a decent optimizing implementation; though providing a custom + zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that. + This library is designed for source code compactness and simplicity, + not optimal image file size or run-time performance. + +BUILDING: + + You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. + You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace + malloc,realloc,free. + You can #define STBIW_MEMMOVE() to replace memmove() + You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function + for PNG compression (instead of the builtin one), it must have the following signature: + unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality); + The returned data will be freed with STBIW_FREE() (free() by default), + so it must be heap allocated with STBIW_MALLOC() (malloc() by default), + +UNICODE: + + If compiling for Windows and you wish to use Unicode filenames, compile + with + #define STBIW_WINDOWS_UTF8 + and pass utf8-encoded filenames. Call stbiw_convert_wchar_to_utf8 to convert + Windows wchar_t filenames to utf8. + +USAGE: + + There are five functions, one for each image file format: + + int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality); + int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); + + void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically + + There are also five equivalent functions that use an arbitrary write function. You are + expected to open/close your file-equivalent before and after calling these: + + int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); + + where the callback is: + void stbi_write_func(void *context, void *data, int size); + + You can configure it with these global variables: + int stbi_write_tga_with_rle; // defaults to true; set to 0 to disable RLE + int stbi_write_png_compression_level; // defaults to 8; set to higher for more compression + int stbi_write_force_png_filter; // defaults to -1; set to 0..5 to force a filter mode + + + You can define STBI_WRITE_NO_STDIO to disable the file variant of these + functions, so the library will not use stdio.h at all. However, this will + also disable HDR writing, because it requires stdio for formatted output. + + Each function returns 0 on failure and non-0 on success. + + The functions create an image file defined by the parameters. The image + is a rectangle of pixels stored from left-to-right, top-to-bottom. + Each pixel contains 'comp' channels of data stored interleaved with 8-bits + per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is + monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. + The *data pointer points to the first byte of the top-left-most pixel. + For PNG, "stride_in_bytes" is the distance in bytes from the first byte of + a row of pixels to the first byte of the next row of pixels. + + PNG creates output files with the same number of components as the input. + The BMP format expands Y to RGB in the file format and does not + output alpha. + + PNG supports writing rectangles of data even when the bytes storing rows of + data are not consecutive in memory (e.g. sub-rectangles of a larger image), + by supplying the stride between the beginning of adjacent rows. The other + formats do not. (Thus you cannot write a native-format BMP through the BMP + writer, both because it is in BGR order and because it may have padding + at the end of the line.) + + PNG allows you to set the deflate compression level by setting the global + variable 'stbi_write_png_compression_level' (it defaults to 8). + + HDR expects linear float data. Since the format is always 32-bit rgb(e) + data, alpha (if provided) is discarded, and for monochrome data it is + replicated across all three channels. + + TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed + data, set the global variable 'stbi_write_tga_with_rle' to 0. + + JPEG does ignore alpha channels in input data; quality is between 1 and 100. + Higher quality looks better but results in a bigger image. + JPEG baseline (no JPEG progressive). + +CREDITS: + + + Sean Barrett - PNG/BMP/TGA + Baldur Karlsson - HDR + Jean-Sebastien Guay - TGA monochrome + Tim Kelsey - misc enhancements + Alan Hickman - TGA RLE + Emmanuel Julien - initial file IO callback implementation + Jon Olick - original jo_jpeg.cpp code + Daniel Gibson - integrate JPEG, allow external zlib + Aarni Koskela - allow choosing PNG filter + + bugfixes: + github:Chribba + Guillaume Chereau + github:jry2 + github:romigrou + Sergio Gonzalez + Jonas Karlsson + Filip Wasil + Thatcher Ulrich + github:poppolopoppo + Patrick Boettcher + github:xeekworx + Cap Petschulat + Simon Rodriguez + Ivan Tikhonov + github:ignotion + Adam Schackart + Andrew Kensler + +LICENSE + + See end of file for license information. + +*/ + +#ifndef INCLUDE_STB_IMAGE_WRITE_H +#define INCLUDE_STB_IMAGE_WRITE_H + +#include + +// if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline' +#ifndef STBIWDEF +#ifdef STB_IMAGE_WRITE_STATIC +#define STBIWDEF static +#else +#ifdef __cplusplus +#define STBIWDEF extern "C" +#else +#define STBIWDEF extern +#endif +#endif +#endif + +#ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations +STBIWDEF int stbi_write_tga_with_rle; +STBIWDEF int stbi_write_png_compression_level; +STBIWDEF int stbi_write_force_png_filter; +#endif + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); + +#ifdef STBIW_WINDOWS_UTF8 +STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif +#endif + +typedef void stbi_write_func(void *context, void *data, int size); + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); + +STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); + +#endif//INCLUDE_STB_IMAGE_WRITE_H + +#ifdef STB_IMAGE_WRITE_IMPLEMENTATION + +#ifdef _WIN32 + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif + #ifndef _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_NONSTDC_NO_DEPRECATE + #endif +#endif + +#ifndef STBI_WRITE_NO_STDIO +#include +#endif // STBI_WRITE_NO_STDIO + +#include +#include +#include +#include + +#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) +// ok +#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." +#endif + +#ifndef STBIW_MALLOC +#define STBIW_MALLOC(sz) malloc(sz) +#define STBIW_REALLOC(p,newsz) realloc(p,newsz) +#define STBIW_FREE(p) free(p) +#endif + +#ifndef STBIW_REALLOC_SIZED +#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) +#endif + + +#ifndef STBIW_MEMMOVE +#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) +#endif + + +#ifndef STBIW_ASSERT +#include +#define STBIW_ASSERT(x) assert(x) +#endif + +#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) + +#ifdef STB_IMAGE_WRITE_STATIC +static int stbi_write_png_compression_level = 8; +static int stbi_write_tga_with_rle = 1; +static int stbi_write_force_png_filter = -1; +#else +int stbi_write_png_compression_level = 8; +int stbi_write_tga_with_rle = 1; +int stbi_write_force_png_filter = -1; +#endif + +static int stbi__flip_vertically_on_write = 0; + +STBIWDEF void stbi_flip_vertically_on_write(int flag) +{ + stbi__flip_vertically_on_write = flag; +} + +typedef struct +{ + stbi_write_func *func; + void *context; + unsigned char buffer[64]; + int buf_used; +} stbi__write_context; + +// initialize a callback-based context +static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) +{ + s->func = c; + s->context = context; +} + +#ifndef STBI_WRITE_NO_STDIO + +static void stbi__stdio_write(void *context, void *data, int size) +{ + fwrite(data,1,size,(FILE*) context); +} + +#if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8) +#ifdef __cplusplus +#define STBIW_EXTERN extern "C" +#else +#define STBIW_EXTERN extern +#endif +STBIW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); + +STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbiw__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) + return 0; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + +static int stbi__start_write_file(stbi__write_context *s, const char *filename) +{ + FILE *f = stbiw__fopen(filename, "wb"); + stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); + return f != NULL; +} + +static void stbi__end_write_file(stbi__write_context *s) +{ + fclose((FILE *)s->context); +} + +#endif // !STBI_WRITE_NO_STDIO + +typedef unsigned int stbiw_uint32; +typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; + +static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) +{ + while (*fmt) { + switch (*fmt++) { + case ' ': break; + case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); + s->func(s->context,&x,1); + break; } + case '2': { int x = va_arg(v,int); + unsigned char b[2]; + b[0] = STBIW_UCHAR(x); + b[1] = STBIW_UCHAR(x>>8); + s->func(s->context,b,2); + break; } + case '4': { stbiw_uint32 x = va_arg(v,int); + unsigned char b[4]; + b[0]=STBIW_UCHAR(x); + b[1]=STBIW_UCHAR(x>>8); + b[2]=STBIW_UCHAR(x>>16); + b[3]=STBIW_UCHAR(x>>24); + s->func(s->context,b,4); + break; } + default: + STBIW_ASSERT(0); + return; + } + } +} + +static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) +{ + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); +} + +static void stbiw__write_flush(stbi__write_context *s) +{ + if (s->buf_used) { + s->func(s->context, &s->buffer, s->buf_used); + s->buf_used = 0; + } +} + +static void stbiw__putc(stbi__write_context *s, unsigned char c) +{ + s->func(s->context, &c, 1); +} + +static void stbiw__write1(stbi__write_context *s, unsigned char a) +{ + if ((size_t)s->buf_used + 1 > sizeof(s->buffer)) + stbiw__write_flush(s); + s->buffer[s->buf_used++] = a; +} + +static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) +{ + int n; + if ((size_t)s->buf_used + 3 > sizeof(s->buffer)) + stbiw__write_flush(s); + n = s->buf_used; + s->buf_used = n+3; + s->buffer[n+0] = a; + s->buffer[n+1] = b; + s->buffer[n+2] = c; +} + +static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) +{ + unsigned char bg[3] = { 255, 0, 255}, px[3]; + int k; + + if (write_alpha < 0) + stbiw__write1(s, d[comp - 1]); + + switch (comp) { + case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case + case 1: + if (expand_mono) + stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp + else + stbiw__write1(s, d[0]); // monochrome TGA + break; + case 4: + if (!write_alpha) { + // composite against pink background + for (k = 0; k < 3; ++k) + px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; + stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); + break; + } + /* FALLTHROUGH */ + case 3: + stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); + break; + } + if (write_alpha > 0) + stbiw__write1(s, d[comp - 1]); +} + +static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) +{ + stbiw_uint32 zero = 0; + int i,j, j_end; + + if (y <= 0) + return; + + if (stbi__flip_vertically_on_write) + vdir *= -1; + + if (vdir < 0) { + j_end = -1; j = y-1; + } else { + j_end = y; j = 0; + } + + for (; j != j_end; j += vdir) { + for (i=0; i < x; ++i) { + unsigned char *d = (unsigned char *) data + (j*x+i)*comp; + stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); + } + stbiw__write_flush(s); + s->func(s->context, &zero, scanline_pad); + } +} + +static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) +{ + if (y < 0 || x < 0) { + return 0; + } else { + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); + stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); + return 1; + } +} + +static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) +{ + if (comp != 4) { + // write RGB bitmap + int pad = (-x*3) & 3; + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, + "11 4 22 4" "4 44 22 444444", + 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header + 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header + } else { + // RGBA bitmaps need a v4 header + // use BI_BITFIELDS mode with 32bpp and alpha mask + // (straight BI_RGB with alpha mask doesn't work in most readers) + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *)data,1,0, + "11 4 22 4" "4 44 22 444444 4444 4 444 444 444 444", + 'B', 'M', 14+108+x*y*4, 0, 0, 14+108, // file header + 108, x,y, 1,32, 3,0,0,0,0,0, 0xff0000,0xff00,0xff,0xff000000u, 0, 0,0,0, 0,0,0, 0,0,0, 0,0,0); // bitmap V4 header + } +} + +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s = { 0 }; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_bmp_core(&s, x, y, comp, data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s = { 0 }; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_bmp_core(&s, x, y, comp, data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif //!STBI_WRITE_NO_STDIO + +static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) +{ + int has_alpha = (comp == 2 || comp == 4); + int colorbytes = has_alpha ? comp-1 : comp; + int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 + + if (y < 0 || x < 0) + return 0; + + if (!stbi_write_tga_with_rle) { + return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, + "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); + } else { + int i,j,k; + int jend, jdir; + + stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); + + if (stbi__flip_vertically_on_write) { + j = 0; + jend = y; + jdir = 1; + } else { + j = y-1; + jend = -1; + jdir = -1; + } + for (; j != jend; j += jdir) { + unsigned char *row = (unsigned char *) data + j * x * comp; + int len; + + for (i = 0; i < x; i += len) { + unsigned char *begin = row + i * comp; + int diff = 1; + len = 1; + + if (i < x - 1) { + ++len; + diff = memcmp(begin, row + (i + 1) * comp, comp); + if (diff) { + const unsigned char *prev = begin; + for (k = i + 2; k < x && len < 128; ++k) { + if (memcmp(prev, row + k * comp, comp)) { + prev += comp; + ++len; + } else { + --len; + break; + } + } + } else { + for (k = i + 2; k < x && len < 128; ++k) { + if (!memcmp(begin, row + k * comp, comp)) { + ++len; + } else { + break; + } + } + } + } + + if (diff) { + unsigned char header = STBIW_UCHAR(len - 1); + stbiw__write1(s, header); + for (k = 0; k < len; ++k) { + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); + } + } else { + unsigned char header = STBIW_UCHAR(len - 129); + stbiw__write1(s, header); + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); + } + } + } + stbiw__write_flush(s); + } + return 1; +} + +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s = { 0 }; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_tga_core(&s, x, y, comp, (void *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s = { 0 }; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR writer +// by Baldur Karlsson + +#define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) + +#ifndef STBI_WRITE_NO_STDIO + +static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) +{ + int exponent; + float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); + + if (maxcomp < 1e-32f) { + rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; + } else { + float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; + + rgbe[0] = (unsigned char)(linear[0] * normalize); + rgbe[1] = (unsigned char)(linear[1] * normalize); + rgbe[2] = (unsigned char)(linear[2] * normalize); + rgbe[3] = (unsigned char)(exponent + 128); + } +} + +static void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) +{ + unsigned char lengthbyte = STBIW_UCHAR(length+128); + STBIW_ASSERT(length+128 <= 255); + s->func(s->context, &lengthbyte, 1); + s->func(s->context, &databyte, 1); +} + +static void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) +{ + unsigned char lengthbyte = STBIW_UCHAR(length); + STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code + s->func(s->context, &lengthbyte, 1); + s->func(s->context, data, length); +} + +static void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) +{ + unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; + unsigned char rgbe[4]; + float linear[3]; + int x; + + scanlineheader[2] = (width&0xff00)>>8; + scanlineheader[3] = (width&0x00ff); + + /* skip RLE for images too small or large */ + if (width < 8 || width >= 32768) { + for (x=0; x < width; x++) { + switch (ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + s->func(s->context, rgbe, 4); + } + } else { + int c,r; + /* encode into scratch buffer */ + for (x=0; x < width; x++) { + switch(ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + scratch[x + width*0] = rgbe[0]; + scratch[x + width*1] = rgbe[1]; + scratch[x + width*2] = rgbe[2]; + scratch[x + width*3] = rgbe[3]; + } + + s->func(s->context, scanlineheader, 4); + + /* RLE each component separately */ + for (c=0; c < 4; c++) { + unsigned char *comp = &scratch[width*c]; + + x = 0; + while (x < width) { + // find first run + r = x; + while (r+2 < width) { + if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) + break; + ++r; + } + if (r+2 >= width) + r = width; + // dump up to first run + while (x < r) { + int len = r-x; + if (len > 128) len = 128; + stbiw__write_dump_data(s, len, &comp[x]); + x += len; + } + // if there's a run, output it + if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd + // find next byte after run + while (r < width && comp[r] == comp[x]) + ++r; + // output run up to r + while (x < r) { + int len = r-x; + if (len > 127) len = 127; + stbiw__write_run_data(s, len, comp[x]); + x += len; + } + } + } + } + } +} + +static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) +{ + if (y <= 0 || x <= 0 || data == NULL) + return 0; + else { + // Each component is stored separately. Allocate scratch space for full output scanline. + unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); + int i, len; + char buffer[128]; + char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; + s->func(s->context, header, sizeof(header)-1); + +#ifdef __STDC_LIB_EXT1__ + len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#else + len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#endif + s->func(s->context, buffer, len); + + for(i=0; i < y; i++) + stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i)); + STBIW_FREE(scratch); + return 1; + } +} + +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) +{ + stbi__write_context s = { 0 }; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_hdr_core(&s, x, y, comp, (float *) data); +} + +STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) +{ + stbi__write_context s = { 0 }; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif // STBI_WRITE_NO_STDIO + + +////////////////////////////////////////////////////////////////////////////// +// +// PNG writer +// + +#ifndef STBIW_ZLIB_COMPRESS +// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() +#define stbiw__sbraw(a) ((int *) (void *) (a) - 2) +#define stbiw__sbm(a) stbiw__sbraw(a)[0] +#define stbiw__sbn(a) stbiw__sbraw(a)[1] + +#define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) +#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) +#define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) + +#define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) +#define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) +#define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) + +static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) +{ + int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; + void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); + STBIW_ASSERT(p); + if (p) { + if (!*arr) ((int *) p)[1] = 0; + *arr = (void *) ((int *) p + 2); + stbiw__sbm(*arr) = m; + } + return *arr; +} + +static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) +{ + while (*bitcount >= 8) { + stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); + *bitbuffer >>= 8; + *bitcount -= 8; + } + return data; +} + +static int stbiw__zlib_bitrev(int code, int codebits) +{ + int res=0; + while (codebits--) { + res = (res << 1) | (code & 1); + code >>= 1; + } + return res; +} + +static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) +{ + int i; + for (i=0; i < limit && i < 258; ++i) + if (a[i] != b[i]) break; + return i; +} + +static unsigned int stbiw__zhash(unsigned char *data) +{ + stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) +#define stbiw__zlib_add(code,codebits) \ + (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) +#define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) +// default huffman tables +#define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) +#define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) +#define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) +#define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) +#define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) +#define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) + +#define stbiw__ZHASH 16384 + +#endif // STBIW_ZLIB_COMPRESS + +STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) +{ +#ifdef STBIW_ZLIB_COMPRESS + // user provided a zlib compress implementation, use that + return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality); +#else // use builtin + static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; + static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; + static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; + unsigned int bitbuf=0; + int i,j, bitcount=0; + unsigned char *out = NULL; + unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(unsigned char**)); + if (hash_table == NULL) + return NULL; + if (quality < 5) quality = 5; + + stbiw__sbpush(out, 0x78); // DEFLATE 32K window + stbiw__sbpush(out, 0x5e); // FLEVEL = 1 + stbiw__zlib_add(1,1); // BFINAL = 1 + stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman + + for (i=0; i < stbiw__ZHASH; ++i) + hash_table[i] = NULL; + + i=0; + while (i < data_len-3) { + // hash next 3 bytes of data to be compressed + int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; + unsigned char *bestloc = 0; + unsigned char **hlist = hash_table[h]; + int n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32768) { // if entry lies within window + int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); + if (d >= best) { best=d; bestloc=hlist[j]; } + } + } + // when hash table entry is too long, delete half the entries + if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { + STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); + stbiw__sbn(hash_table[h]) = quality; + } + stbiw__sbpush(hash_table[h],data+i); + + if (bestloc) { + // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal + h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); + hlist = hash_table[h]; + n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32767) { + int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); + if (e > best) { // if next match is better, bail on current match + bestloc = NULL; + break; + } + } + } + } + + if (bestloc) { + int d = (int) (data+i - bestloc); // distance back + STBIW_ASSERT(d <= 32767 && best <= 258); + for (j=0; best > lengthc[j+1]-1; ++j); + stbiw__zlib_huff(j+257); + if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); + for (j=0; d > distc[j+1]-1; ++j); + stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); + if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); + i += best; + } else { + stbiw__zlib_huffb(data[i]); + ++i; + } + } + // write out final bytes + for (;i < data_len; ++i) + stbiw__zlib_huffb(data[i]); + stbiw__zlib_huff(256); // end of block + // pad with 0 bits to byte boundary + while (bitcount) + stbiw__zlib_add(0,1); + + for (i=0; i < stbiw__ZHASH; ++i) + (void) stbiw__sbfree(hash_table[i]); + STBIW_FREE(hash_table); + + // store uncompressed instead if compression was worse + if (stbiw__sbn(out) > data_len + 2 + ((data_len+32766)/32767)*5) { + stbiw__sbn(out) = 2; // truncate to DEFLATE 32K window and FLEVEL = 1 + for (j = 0; j < data_len;) { + int blocklen = data_len - j; + if (blocklen > 32767) blocklen = 32767; + stbiw__sbpush(out, data_len - j == blocklen); // BFINAL = ?, BTYPE = 0 -- no compression + stbiw__sbpush(out, STBIW_UCHAR(blocklen)); // LEN + stbiw__sbpush(out, STBIW_UCHAR(blocklen >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(~blocklen)); // NLEN + stbiw__sbpush(out, STBIW_UCHAR(~blocklen >> 8)); + memcpy(out+stbiw__sbn(out), data+j, blocklen); + stbiw__sbn(out) += blocklen; + j += blocklen; + } + } + + { + // compute adler32 on input + unsigned int s1=1, s2=0; + int blocklen = (int) (data_len % 5552); + j=0; + while (j < data_len) { + for (i=0; i < blocklen; ++i) { s1 += data[j+i]; s2 += s1; } + s1 %= 65521; s2 %= 65521; + j += blocklen; + blocklen = 5552; + } + stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s2)); + stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s1)); + } + *out_len = stbiw__sbn(out); + // make returned pointer freeable + STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); + return (unsigned char *) stbiw__sbraw(out); +#endif // STBIW_ZLIB_COMPRESS +} + +static unsigned int stbiw__crc32(unsigned char *buffer, int len) +{ +#ifdef STBIW_CRC32 + return STBIW_CRC32(buffer, len); +#else + static unsigned int crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + unsigned int crc = ~0u; + int i; + for (i=0; i < len; ++i) + crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; + return ~crc; +#endif +} + +#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) +#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); +#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) + +static void stbiw__wpcrc(unsigned char **data, int len) +{ + unsigned int crc = stbiw__crc32(*data - len - 4, len+4); + stbiw__wp32(*data, crc); +} + +static unsigned char stbiw__paeth(int a, int b, int c) +{ + int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); + if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); + if (pb <= pc) return STBIW_UCHAR(b); + return STBIW_UCHAR(c); +} + +// @OPTIMIZE: provide an option that always forces left-predict or paeth predict +static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer) +{ + static int mapping[] = { 0,1,2,3,4 }; + static int firstmap[] = { 0,1,0,5,6 }; + int *mymap = (y != 0) ? mapping : firstmap; + int i; + int type = mymap[filter_type]; + unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y); + int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; + + if (type==0) { + memcpy(line_buffer, z, width*n); + return; + } + + // first loop isn't optimized since it's just one pixel + for (i = 0; i < n; ++i) { + switch (type) { + case 1: line_buffer[i] = z[i]; break; + case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break; + case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break; + case 5: line_buffer[i] = z[i]; break; + case 6: line_buffer[i] = z[i]; break; + } + } + switch (type) { + case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break; + case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break; + case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break; + case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break; + case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; + } +} + +STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) +{ + int force_filter = stbi_write_force_png_filter; + int ctype[5] = { -1, 0, 4, 2, 6 }; + unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; + unsigned char *out,*o, *filt, *zlib; + signed char *line_buffer; + int j,zlen; + + if (stride_bytes == 0) + stride_bytes = x * n; + + if (force_filter >= 5) { + force_filter = -1; + } + + filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; + line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } + for (j=0; j < y; ++j) { + int filter_type; + if (force_filter > -1) { + filter_type = force_filter; + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer); + } else { // Estimate the best filter by running through all of them: + int best_filter = 0, best_filter_val = 0x7fffffff, est, i; + for (filter_type = 0; filter_type < 5; filter_type++) { + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer); + + // Estimate the entropy of the line using this filter; the less, the better. + est = 0; + for (i = 0; i < x*n; ++i) { + est += abs((signed char) line_buffer[i]); + } + if (est < best_filter_val) { + best_filter_val = est; + best_filter = filter_type; + } + } + if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer); + filter_type = best_filter; + } + } + // when we get here, filter_type contains the filter type, and line_buffer contains the data + filt[j*(x*n+1)] = (unsigned char) filter_type; + STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); + } + STBIW_FREE(line_buffer); + zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level); + STBIW_FREE(filt); + if (!zlib) return 0; + + // each tag requires 12 bytes of overhead + out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); + if (!out) return 0; + *out_len = 8 + 12+13 + 12+zlen + 12; + + o=out; + STBIW_MEMMOVE(o,sig,8); o+= 8; + stbiw__wp32(o, 13); // header length + stbiw__wptag(o, "IHDR"); + stbiw__wp32(o, x); + stbiw__wp32(o, y); + *o++ = 8; + *o++ = STBIW_UCHAR(ctype[n]); + *o++ = 0; + *o++ = 0; + *o++ = 0; + stbiw__wpcrc(&o,13); + + stbiw__wp32(o, zlen); + stbiw__wptag(o, "IDAT"); + STBIW_MEMMOVE(o, zlib, zlen); + o += zlen; + STBIW_FREE(zlib); + stbiw__wpcrc(&o, zlen); + + stbiw__wp32(o,0); + stbiw__wptag(o, "IEND"); + stbiw__wpcrc(&o,0); + + STBIW_ASSERT(o == out + *out_len); + + return out; +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) +{ + FILE *f; + int len; + unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + + f = stbiw__fopen(filename, "wb"); + if (!f) { STBIW_FREE(png); return 0; } + fwrite(png, 1, len, f); + fclose(f); + STBIW_FREE(png); + return 1; +} +#endif + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) +{ + int len; + unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + func(context, png, len); + STBIW_FREE(png); + return 1; +} + + +/* *************************************************************************** + * + * JPEG writer + * + * This is based on Jon Olick's jo_jpeg.cpp: + * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html + */ + +static const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18, + 24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 }; + +static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) { + int bitBuf = *bitBufP, bitCnt = *bitCntP; + bitCnt += bs[1]; + bitBuf |= bs[0] << (24 - bitCnt); + while(bitCnt >= 8) { + unsigned char c = (bitBuf >> 16) & 255; + stbiw__putc(s, c); + if(c == 255) { + stbiw__putc(s, 0); + } + bitBuf <<= 8; + bitCnt -= 8; + } + *bitBufP = bitBuf; + *bitCntP = bitCnt; +} + +static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) { + float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p; + float z1, z2, z3, z4, z5, z11, z13; + + float tmp0 = d0 + d7; + float tmp7 = d0 - d7; + float tmp1 = d1 + d6; + float tmp6 = d1 - d6; + float tmp2 = d2 + d5; + float tmp5 = d2 - d5; + float tmp3 = d3 + d4; + float tmp4 = d3 - d4; + + // Even part + float tmp10 = tmp0 + tmp3; // phase 2 + float tmp13 = tmp0 - tmp3; + float tmp11 = tmp1 + tmp2; + float tmp12 = tmp1 - tmp2; + + d0 = tmp10 + tmp11; // phase 3 + d4 = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * 0.707106781f; // c4 + d2 = tmp13 + z1; // phase 5 + d6 = tmp13 - z1; + + // Odd part + tmp10 = tmp4 + tmp5; // phase 2 + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + // The rotator is modified from fig 4-8 to avoid extra negations. + z5 = (tmp10 - tmp12) * 0.382683433f; // c6 + z2 = tmp10 * 0.541196100f + z5; // c2-c6 + z4 = tmp12 * 1.306562965f + z5; // c2+c6 + z3 = tmp11 * 0.707106781f; // c4 + + z11 = tmp7 + z3; // phase 5 + z13 = tmp7 - z3; + + *d5p = z13 + z2; // phase 6 + *d3p = z13 - z2; + *d1p = z11 + z4; + *d7p = z11 - z4; + + *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6; +} + +static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { + int tmp1 = val < 0 ? -val : val; + val = val < 0 ? val-1 : val; + bits[1] = 1; + while(tmp1 >>= 1) { + ++bits[1]; + } + bits[0] = val & ((1<0)&&(DU[end0pos]==0); --end0pos) { + } + // end0pos = first element in reverse order !=0 + if(end0pos == 0) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + return DU[0]; + } + for(i = 1; i <= end0pos; ++i) { + int startpos = i; + int nrzeroes; + unsigned short bits[2]; + for (; DU[i]==0 && i<=end0pos; ++i) { + } + nrzeroes = i-startpos; + if ( nrzeroes >= 16 ) { + int lng = nrzeroes>>4; + int nrmarker; + for (nrmarker=1; nrmarker <= lng; ++nrmarker) + stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes); + nrzeroes &= 15; + } + stbiw__jpg_calcBits(DU[i], bits); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); + } + if(end0pos != 63) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + } + return DU[0]; +} + +static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) { + // Constants that don't pollute global namespace + static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0}; + static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d}; + static const unsigned char std_ac_luminance_values[] = { + 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, + 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, + 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, + 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, + 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, + 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, + 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa + }; + static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0}; + static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77}; + static const unsigned char std_ac_chrominance_values[] = { + 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, + 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, + 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, + 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, + 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, + 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, + 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa + }; + // Huffman tables + static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}}; + static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}}; + static const unsigned short YAC_HT[256][2] = { + {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const unsigned short UVAC_HT[256][2] = { + {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22, + 37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99}; + static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99, + 99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99}; + static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, + 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f }; + + int row, col, i, k, subsample; + float fdtbl_Y[64], fdtbl_UV[64]; + unsigned char YTable[64], UVTable[64]; + + if(!data || !width || !height || comp > 4 || comp < 1) { + return 0; + } + + quality = quality ? quality : 90; + subsample = quality <= 90 ? 1 : 0; + quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; + quality = quality < 50 ? 5000 / quality : 200 - quality * 2; + + for(i = 0; i < 64; ++i) { + int uvti, yti = (YQT[i]*quality+50)/100; + YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti); + uvti = (UVQT[i]*quality+50)/100; + UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti); + } + + for(row = 0, k = 0; row < 8; ++row) { + for(col = 0; col < 8; ++col, ++k) { + fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + } + } + + // Write Headers + { + static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 }; + static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 }; + const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width), + 3,1,(unsigned char)(subsample?0x22:0x11),0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; + s->func(s->context, (void*)head0, sizeof(head0)); + s->func(s->context, (void*)YTable, sizeof(YTable)); + stbiw__putc(s, 1); + s->func(s->context, UVTable, sizeof(UVTable)); + s->func(s->context, (void*)head1, sizeof(head1)); + s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1); + s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values)); + stbiw__putc(s, 0x10); // HTYACinfo + s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1); + s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values)); + stbiw__putc(s, 1); // HTUDCinfo + s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); + stbiw__putc(s, 0x11); // HTUACinfo + s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); + s->func(s->context, (void*)head2, sizeof(head2)); + } + + // Encode 8x8 macroblocks + { + static const unsigned short fillBits[] = {0x7F, 7}; + int DCY=0, DCU=0, DCV=0; + int bitBuf=0, bitCnt=0; + // comp == 2 is grey+alpha (alpha is ignored) + int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; + const unsigned char *dataR = (const unsigned char *)data; + const unsigned char *dataG = dataR + ofsG; + const unsigned char *dataB = dataR + ofsB; + int x, y, pos; + if(subsample) { + for(y = 0; y < height; y += 16) { + for(x = 0; x < width; x += 16) { + float Y[256], U[256], V[256]; + for(row = y, pos = 0; row < y+16; ++row) { + // row >= height => use last input row + int clamped_row = (row < height) ? row : height - 1; + int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; + for(col = x; col < x+16; ++col, ++pos) { + // if col >= width => use pixel from last input column + int p = base_p + ((col < width) ? col : (width-1))*comp; + float r = dataR[p], g = dataG[p], b = dataB[p]; + Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128; + U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b; + V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b; + } + } + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+0, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+8, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+128, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+136, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + + // subsample U,V + { + float subU[64], subV[64]; + int yy, xx; + for(yy = 0, pos = 0; yy < 8; ++yy) { + for(xx = 0; xx < 8; ++xx, ++pos) { + int j = yy*32+xx*2; + subU[pos] = (U[j+0] + U[j+1] + U[j+16] + U[j+17]) * 0.25f; + subV[pos] = (V[j+0] + V[j+1] + V[j+16] + V[j+17]) * 0.25f; + } + } + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subU, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subV, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + } + } + } + } else { + for(y = 0; y < height; y += 8) { + for(x = 0; x < width; x += 8) { + float Y[64], U[64], V[64]; + for(row = y, pos = 0; row < y+8; ++row) { + // row >= height => use last input row + int clamped_row = (row < height) ? row : height - 1; + int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; + for(col = x; col < x+8; ++col, ++pos) { + // if col >= width => use pixel from last input column + int p = base_p + ((col < width) ? col : (width-1))*comp; + float r = dataR[p], g = dataG[p], b = dataB[p]; + Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128; + U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b; + V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b; + } + } + + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + } + } + } + + // Do the bit alignment of the EOI marker + stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits); + } + + // EOI + stbiw__putc(s, 0xFF); + stbiw__putc(s, 0xD9); + + return 1; +} + +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s = { 0 }; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality); +} + + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s = { 0 }; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +#endif // STB_IMAGE_WRITE_IMPLEMENTATION + +/* Revision history + 1.16 (2021-07-11) + make Deflate code emit uncompressed blocks when it would otherwise expand + support writing BMPs with alpha channel + 1.15 (2020-07-13) unknown + 1.14 (2020-02-02) updated JPEG writer to downsample chroma channels + 1.13 + 1.12 + 1.11 (2019-08-11) + + 1.10 (2019-02-07) + support utf8 filenames in Windows; fix warnings and platform ifdefs + 1.09 (2018-02-11) + fix typo in zlib quality API, improve STB_I_W_STATIC in C++ + 1.08 (2018-01-29) + add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter + 1.07 (2017-07-24) + doc fix + 1.06 (2017-07-23) + writing JPEG (using Jon Olick's code) + 1.05 ??? + 1.04 (2017-03-03) + monochrome BMP expansion + 1.03 ??? + 1.02 (2016-04-02) + avoid allocating large structures on the stack + 1.01 (2016-01-16) + STBIW_REALLOC_SIZED: support allocators with no realloc support + avoid race-condition in crc initialization + minor compile issues + 1.00 (2015-09-14) + installable file IO function + 0.99 (2015-09-13) + warning fixes; TGA rle support + 0.98 (2015-04-08) + added STBIW_MALLOC, STBIW_ASSERT etc + 0.97 (2015-01-18) + fixed HDR asserts, rewrote HDR rle logic + 0.96 (2015-01-17) + add HDR output + fix monochrome BMP + 0.95 (2014-08-17) + add monochrome TGA output + 0.94 (2014-05-31) + rename private functions to avoid conflicts with stb_image.h + 0.93 (2014-05-27) + warning fixes + 0.92 (2010-08-01) + casts to unsigned char to fix warnings + 0.91 (2010-07-17) + first public release + 0.90 first internal release +*/ + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ \ No newline at end of file diff --git a/Thirdparty/tinyobj/tiny_obj_loader.cc b/Thirdparty/tinyobj/tiny_obj_loader.cc deleted file mode 100644 index 05ce41e5..00000000 --- a/Thirdparty/tinyobj/tiny_obj_loader.cc +++ /dev/null @@ -1,2 +0,0 @@ -#define TINYOBJLOADER_IMPLEMENTATION -#include "tiny_obj_loader.h" \ No newline at end of file diff --git a/Thirdparty/tinyobj/tiny_obj_loader.h b/Thirdparty/tinyobj/tiny_obj_loader.h deleted file mode 100644 index 6950f29d..00000000 --- a/Thirdparty/tinyobj/tiny_obj_loader.h +++ /dev/null @@ -1,3329 +0,0 @@ -/* -The MIT License (MIT) -Copyright (c) 2012-Present, Syoyo Fujita and many contributors. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -// -// version 2.0.0 : Add new object oriented API. 1.x API is still provided. -// * Support line primitive. -// * Support points primitive. -// * Support multiple search path for .mtl(v1 API). -// * Support vertex weight `vw`(as an tinyobj extension) -// * Support escaped whitespece in mtllib -// * Add robust triangulation using Mapbox earcut(TINYOBJLOADER_USE_MAPBOX_EARCUT). -// version 1.4.0 : Modifed ParseTextureNameAndOption API -// version 1.3.1 : Make ParseTextureNameAndOption API public -// version 1.3.0 : Separate warning and error message(breaking API of LoadObj) -// version 1.2.3 : Added color space extension('-colorspace') to tex opts. -// version 1.2.2 : Parse multiple group names. -// version 1.2.1 : Added initial support for line('l') primitive(PR #178) -// version 1.2.0 : Hardened implementation(#175) -// version 1.1.1 : Support smoothing groups(#162) -// version 1.1.0 : Support parsing vertex color(#144) -// version 1.0.8 : Fix parsing `g` tag just after `usemtl`(#138) -// version 1.0.7 : Support multiple tex options(#126) -// version 1.0.6 : Add TINYOBJLOADER_USE_DOUBLE option(#124) -// version 1.0.5 : Ignore `Tr` when `d` exists in MTL(#43) -// version 1.0.4 : Support multiple filenames for 'mtllib'(#112) -// version 1.0.3 : Support parsing texture options(#85) -// version 1.0.2 : Improve parsing speed by about a factor of 2 for large -// files(#105) -// version 1.0.1 : Fixes a shape is lost if obj ends with a 'usemtl'(#104) -// version 1.0.0 : Change data structure. Change license from BSD to MIT. -// - -// -// Use this in *one* .cc -// #define TINYOBJLOADER_IMPLEMENTATION -// #include "tiny_obj_loader.h" -// - -#ifndef TINY_OBJ_LOADER_H_ -#define TINY_OBJ_LOADER_H_ - -#include -#include -#include - -namespace tinyobj { - -// TODO(syoyo): Better C++11 detection for older compiler -#if __cplusplus > 199711L -#define TINYOBJ_OVERRIDE override -#else -#define TINYOBJ_OVERRIDE -#endif - -#ifdef __clang__ - #pragma clang diagnostic push -#if __has_warning("-Wzero-as-null-pointer-constant") -#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" -#endif - -#pragma clang diagnostic ignored "-Wpadded" - -#endif - -// https://en.wikipedia.org/wiki/Wavefront_.obj_file says ... -// -// -blendu on | off # set horizontal texture blending -// (default on) -// -blendv on | off # set vertical texture blending -// (default on) -// -boost real_value # boost mip-map sharpness -// -mm base_value gain_value # modify texture map values (default -// 0 1) -// # base_value = brightness, -// gain_value = contrast -// -o u [v [w]] # Origin offset (default -// 0 0 0) -// -s u [v [w]] # Scale (default -// 1 1 1) -// -t u [v [w]] # Turbulence (default -// 0 0 0) -// -texres resolution # texture resolution to create -// -clamp on | off # only render texels in the clamped -// 0-1 range (default off) -// # When unclamped, textures are -// repeated across a surface, -// # when clamped, only texels which -// fall within the 0-1 -// # range are rendered. -// -bm mult_value # bump multiplier (for bump maps -// only) -// -// -imfchan r | g | b | m | l | z # specifies which channel of the file -// is used to -// # create a scalar or bump texture. -// r:red, g:green, -// # b:blue, m:matte, l:luminance, -// z:z-depth.. -// # (the default for bump is 'l' and -// for decal is 'm') -// bump -imfchan r bumpmap.tga # says to use the red channel of -// bumpmap.tga as the bumpmap -// -// For reflection maps... -// -// -type sphere # specifies a sphere for a "refl" -// reflection map -// -type cube_top | cube_bottom | # when using a cube map, the texture -// file for each -// cube_front | cube_back | # side of the cube is specified -// separately -// cube_left | cube_right -// -// TinyObjLoader extension. -// -// -colorspace SPACE # Color space of the texture. e.g. -// 'sRGB` or 'linear' -// - -#ifdef TINYOBJLOADER_USE_DOUBLE - //#pragma message "using double" -typedef double real_t; -#else -//#pragma message "using float" - typedef float real_t; -#endif - - typedef enum { - TEXTURE_TYPE_NONE, // default - TEXTURE_TYPE_SPHERE, - TEXTURE_TYPE_CUBE_TOP, - TEXTURE_TYPE_CUBE_BOTTOM, - TEXTURE_TYPE_CUBE_FRONT, - TEXTURE_TYPE_CUBE_BACK, - TEXTURE_TYPE_CUBE_LEFT, - TEXTURE_TYPE_CUBE_RIGHT - } texture_type_t; - - struct texture_option_t { - texture_type_t type; // -type (default TEXTURE_TYPE_NONE) - real_t sharpness; // -boost (default 1.0?) - real_t brightness; // base_value in -mm option (default 0) - real_t contrast; // gain_value in -mm option (default 1) - real_t origin_offset[3]; // -o u [v [w]] (default 0 0 0) - real_t scale[3]; // -s u [v [w]] (default 1 1 1) - real_t turbulence[3]; // -t u [v [w]] (default 0 0 0) - int texture_resolution; // -texres resolution (No default value in the spec. - // We'll use -1) - bool clamp; // -clamp (default false) - char imfchan; // -imfchan (the default for bump is 'l' and for decal is 'm') - bool blendu; // -blendu (default on) - bool blendv; // -blendv (default on) - real_t bump_multiplier; // -bm (for bump maps only, default 1.0) - - // extension - std::string colorspace; // Explicitly specify color space of stored texel - // value. Usually `sRGB` or `linear` (default empty). - }; - - struct material_t { - std::string name; - - real_t ambient[3]; - real_t diffuse[3]; - real_t specular[3]; - real_t transmittance[3]; - real_t emission[3]; - real_t shininess; - real_t ior; // index of refraction - real_t dissolve; // 1 == opaque; 0 == fully transparent - // illumination model (see http://www.fileformat.info/format/material/) - int illum; - - int dummy; // Suppress padding warning. - - std::string ambient_texname; // map_Ka - std::string diffuse_texname; // map_Kd - std::string specular_texname; // map_Ks - std::string specular_highlight_texname; // map_Ns - std::string bump_texname; // map_bump, map_Bump, bump - std::string displacement_texname; // disp - std::string alpha_texname; // map_d - std::string reflection_texname; // refl - - texture_option_t ambient_texopt; - texture_option_t diffuse_texopt; - texture_option_t specular_texopt; - texture_option_t specular_highlight_texopt; - texture_option_t bump_texopt; - texture_option_t displacement_texopt; - texture_option_t alpha_texopt; - texture_option_t reflection_texopt; - - // PBR extension - // http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr - real_t roughness; // [0, 1] default 0 - real_t metallic; // [0, 1] default 0 - real_t sheen; // [0, 1] default 0 - real_t clearcoat_thickness; // [0, 1] default 0 - real_t clearcoat_roughness; // [0, 1] default 0 - real_t anisotropy; // aniso. [0, 1] default 0 - real_t anisotropy_rotation; // anisor. [0, 1] default 0 - real_t pad0; - std::string roughness_texname; // map_Pr - std::string metallic_texname; // map_Pm - std::string sheen_texname; // map_Ps - std::string emissive_texname; // map_Ke - std::string normal_texname; // norm. For normal mapping. - - texture_option_t roughness_texopt; - texture_option_t metallic_texopt; - texture_option_t sheen_texopt; - texture_option_t emissive_texopt; - texture_option_t normal_texopt; - - int pad2; - - std::map unknown_parameter; - -#ifdef TINY_OBJ_LOADER_PYTHON_BINDING - // For pybind11 - std::array GetDiffuse() { - std::array values; - values[0] = double(diffuse[0]); - values[1] = double(diffuse[1]); - values[2] = double(diffuse[2]); - - return values; - } - - std::array GetSpecular() { - std::array values; - values[0] = double(specular[0]); - values[1] = double(specular[1]); - values[2] = double(specular[2]); - - return values; - } - - std::array GetTransmittance() { - std::array values; - values[0] = double(transmittance[0]); - values[1] = double(transmittance[1]); - values[2] = double(transmittance[2]); - - return values; - } - - std::array GetEmission() { - std::array values; - values[0] = double(emission[0]); - values[1] = double(emission[1]); - values[2] = double(emission[2]); - - return values; - } - - std::array GetAmbient() { - std::array values; - values[0] = double(ambient[0]); - values[1] = double(ambient[1]); - values[2] = double(ambient[2]); - - return values; - } - - void SetDiffuse(std::array &a) { - diffuse[0] = real_t(a[0]); - diffuse[1] = real_t(a[1]); - diffuse[2] = real_t(a[2]); - } - - void SetAmbient(std::array &a) { - ambient[0] = real_t(a[0]); - ambient[1] = real_t(a[1]); - ambient[2] = real_t(a[2]); - } - - void SetSpecular(std::array &a) { - specular[0] = real_t(a[0]); - specular[1] = real_t(a[1]); - specular[2] = real_t(a[2]); - } - - void SetTransmittance(std::array &a) { - transmittance[0] = real_t(a[0]); - transmittance[1] = real_t(a[1]); - transmittance[2] = real_t(a[2]); - } - - std::string GetCustomParameter(const std::string &key) { - std::map::const_iterator it = - unknown_parameter.find(key); - - if (it != unknown_parameter.end()) { - return it->second; - } - return std::string(); - } - -#endif - }; - - struct tag_t { - std::string name; - - std::vector intValues; - std::vector floatValues; - std::vector stringValues; - }; - - struct joint_and_weight_t { - int joint_id; - real_t weight; - }; - - struct skin_weight_t { - int vertex_id; // Corresponding vertex index in `attrib_t::vertices`. - // Compared to `index_t`, this index must be positive and - // start with 0(does not allow relative indexing) - std::vector weightValues; - }; - -// Index struct to support different indices for vtx/normal/texcoord. -// -1 means not used. - struct index_t { - int vertex_index; - int normal_index; - int texcoord_index; - }; - - struct mesh_t { - std::vector indices; - std::vector - num_face_vertices; // The number of vertices per - // face. 3 = triangle, 4 = quad, - // ... Up to 255 vertices per face. - std::vector material_ids; // per-face material ID - std::vector smoothing_group_ids; // per-face smoothing group - // ID(0 = off. positive value - // = group id) - std::vector tags; // SubD tag - }; - -// struct path_t { -// std::vector indices; // pairs of indices for lines -//}; - - struct lines_t { - // Linear flattened indices. - std::vector indices; // indices for vertices(poly lines) - std::vector num_line_vertices; // The number of vertices per line. - }; - - struct points_t { - std::vector indices; // indices for points - }; - - struct shape_t { - std::string name; - mesh_t mesh; - lines_t lines; - points_t points; - }; - -// Vertex attributes - struct attrib_t { - std::vector vertices; // 'v'(xyz) - - // For backward compatibility, we store vertex weight in separate array. - std::vector vertex_weights; // 'v'(w) - std::vector normals; // 'vn' - std::vector texcoords; // 'vt'(uv) - - // For backward compatibility, we store texture coordinate 'w' in separate - // array. - std::vector texcoord_ws; // 'vt'(w) - std::vector colors; // extension: vertex colors - - // - // TinyObj extension. - // - - // NOTE(syoyo): array index is based on the appearance order. - // To get a corresponding skin weight for a specific vertex id `vid`, - // Need to reconstruct a look up table: `skin_weight_t::vertex_id` == `vid` - // (e.g. using std::map, std::unordered_map) - std::vector skin_weights; - - attrib_t() {} - - // - // For pybind11 - // - const std::vector &GetVertices() const { return vertices; } - - const std::vector &GetVertexWeights() const { return vertex_weights; } - }; - - struct callback_t { - // W is optional and set to 1 if there is no `w` item in `v` line - void (*vertex_cb)(void *user_data, real_t x, real_t y, real_t z, real_t w); - void (*normal_cb)(void *user_data, real_t x, real_t y, real_t z); - - // y and z are optional and set to 0 if there is no `y` and/or `z` item(s) in - // `vt` line. - void (*texcoord_cb)(void *user_data, real_t x, real_t y, real_t z); - - // called per 'f' line. num_indices is the number of face indices(e.g. 3 for - // triangle, 4 for quad) - // 0 will be passed for undefined index in index_t members. - void (*index_cb)(void *user_data, index_t *indices, int num_indices); - // `name` material name, `material_id` = the array index of material_t[]. -1 - // if - // a material not found in .mtl - void (*usemtl_cb)(void *user_data, const char *name, int material_id); - // `materials` = parsed material data. - void (*mtllib_cb)(void *user_data, const material_t *materials, - int num_materials); - // There may be multiple group names - void (*group_cb)(void *user_data, const char **names, int num_names); - void (*object_cb)(void *user_data, const char *name); - - callback_t() - : vertex_cb(NULL), - normal_cb(NULL), - texcoord_cb(NULL), - index_cb(NULL), - usemtl_cb(NULL), - mtllib_cb(NULL), - group_cb(NULL), - object_cb(NULL) {} - }; - - class MaterialReader { - public: - MaterialReader() {} - virtual ~MaterialReader(); - - virtual bool operator()(const std::string &matId, - std::vector *materials, - std::map *matMap, std::string *warn, - std::string *err) = 0; - }; - -/// -/// Read .mtl from a file. -/// - class MaterialFileReader : public MaterialReader { - public: - // Path could contain separator(';' in Windows, ':' in Posix) - explicit MaterialFileReader(const std::string &mtl_basedir) - : m_mtlBaseDir(mtl_basedir) {} - virtual ~MaterialFileReader() TINYOBJ_OVERRIDE {} - virtual bool operator()(const std::string &matId, - std::vector *materials, - std::map *matMap, std::string *warn, - std::string *err) TINYOBJ_OVERRIDE; - - private: - std::string m_mtlBaseDir; - }; - -/// -/// Read .mtl from a stream. -/// - class MaterialStreamReader : public MaterialReader { - public: - explicit MaterialStreamReader(std::istream &inStream) - : m_inStream(inStream) {} - virtual ~MaterialStreamReader() TINYOBJ_OVERRIDE {} - virtual bool operator()(const std::string &matId, - std::vector *materials, - std::map *matMap, std::string *warn, - std::string *err) TINYOBJ_OVERRIDE; - - private: - std::istream &m_inStream; - }; - -// v2 API - struct ObjReaderConfig { - bool triangulate; // triangulate polygon? - - // Currently not used. - // "simple" or empty: Create triangle fan - // "earcut": Use the algorithm based on Ear clipping - std::string triangulation_method; - - /// Parse vertex color. - /// If vertex color is not present, its filled with default value. - /// false = no vertex color - /// This will increase memory of parsed .obj - bool vertex_color; - - /// - /// Search path to .mtl file. - /// Default = "" = search from the same directory of .obj file. - /// Valid only when loading .obj from a file. - /// - std::string mtl_search_path; - - ObjReaderConfig() - : triangulate(true), triangulation_method("simple"), vertex_color(true) {} - }; - -/// -/// Wavefront .obj reader class(v2 API) -/// - class ObjReader { - public: - ObjReader() : valid_(false) {} - - /// - /// Load .obj and .mtl from a file. - /// - /// @param[in] filename wavefront .obj filename - /// @param[in] config Reader configuration - /// - bool ParseFromFile(const std::string &filename, - const ObjReaderConfig &config = ObjReaderConfig()); - - /// - /// Parse .obj from a text string. - /// Need to supply .mtl text string by `mtl_text`. - /// This function ignores `mtllib` line in .obj text. - /// - /// @param[in] obj_text wavefront .obj filename - /// @param[in] mtl_text wavefront .mtl filename - /// @param[in] config Reader configuration - /// - bool ParseFromString(const std::string &obj_text, const std::string &mtl_text, - const ObjReaderConfig &config = ObjReaderConfig()); - - /// - /// .obj was loaded or parsed correctly. - /// - bool Valid() const { return valid_; } - - const attrib_t &GetAttrib() const { return attrib_; } - - const std::vector &GetShapes() const { return shapes_; } - - const std::vector &GetMaterials() const { return materials_; } - - /// - /// Warning message(may be filled after `Load` or `Parse`) - /// - const std::string &Warning() const { return warning_; } - - /// - /// Error message(filled when `Load` or `Parse` failed) - /// - const std::string &Error() const { return error_; } - - private: - bool valid_; - - attrib_t attrib_; - std::vector shapes_; - std::vector materials_; - - std::string warning_; - std::string error_; - }; - -/// ==>>========= Legacy v1 API ============================================= - -/// Loads .obj from a file. -/// 'attrib', 'shapes' and 'materials' will be filled with parsed shape data -/// 'shapes' will be filled with parsed shape data -/// Returns true when loading .obj become success. -/// Returns warning message into `warn`, and error message into `err` -/// 'mtl_basedir' is optional, and used for base directory for .mtl file. -/// In default(`NULL'), .mtl file is searched from an application's working -/// directory. -/// 'triangulate' is optional, and used whether triangulate polygon face in .obj -/// or not. -/// Option 'default_vcols_fallback' specifies whether vertex colors should -/// always be defined, even if no colors are given (fallback to white). - bool LoadObj(attrib_t *attrib, std::vector *shapes, - std::vector *materials, std::string *warn, - std::string *err, const char *filename, - const char *mtl_basedir = NULL, bool triangulate = true, - bool default_vcols_fallback = true); - -/// Loads .obj from a file with custom user callback. -/// .mtl is loaded as usual and parsed material_t data will be passed to -/// `callback.mtllib_cb`. -/// Returns true when loading .obj/.mtl become success. -/// Returns warning message into `warn`, and error message into `err` -/// See `examples/callback_api/` for how to use this function. - bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, - void *user_data = NULL, - MaterialReader *readMatFn = NULL, - std::string *warn = NULL, std::string *err = NULL); - -/// Loads object from a std::istream, uses `readMatFn` to retrieve -/// std::istream for materials. -/// Returns true when loading .obj become success. -/// Returns warning and error message into `err` - bool LoadObj(attrib_t *attrib, std::vector *shapes, - std::vector *materials, std::string *warn, - std::string *err, std::istream *inStream, - MaterialReader *readMatFn = NULL, bool triangulate = true, - bool default_vcols_fallback = true); - -/// Loads materials into std::map - void LoadMtl(std::map *material_map, - std::vector *materials, std::istream *inStream, - std::string *warning, std::string *err); - -/// -/// Parse texture name and texture option for custom texture parameter through -/// material::unknown_parameter -/// -/// @param[out] texname Parsed texture name -/// @param[out] texopt Parsed texopt -/// @param[in] linebuf Input string -/// - bool ParseTextureNameAndOption(std::string *texname, texture_option_t *texopt, - const char *linebuf); - -/// =<<========== Legacy v1 API ============================================= - -} // namespace tinyobj - -#endif // TINY_OBJ_LOADER_H_ - -#ifdef TINYOBJLOADER_IMPLEMENTATION -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef TINYOBJLOADER_USE_MAPBOX_EARCUT - -#ifdef TINYOBJLOADER_DONOT_INCLUDE_MAPBOX_EARCUT -// Assume earcut.hpp is included outside of tiny_obj_loader.h -#else - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Weverything" -#endif - -#include -#include "mapbox/earcut.hpp" - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#endif - -#endif // TINYOBJLOADER_USE_MAPBOX_EARCUT - -namespace tinyobj { - -MaterialReader::~MaterialReader() {} - -struct vertex_index_t { - int v_idx, vt_idx, vn_idx; - vertex_index_t() : v_idx(-1), vt_idx(-1), vn_idx(-1) {} - explicit vertex_index_t(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {} - vertex_index_t(int vidx, int vtidx, int vnidx) - : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {} -}; - -// Internal data structure for face representation -// index + smoothing group. -struct face_t { - unsigned int - smoothing_group_id; // smoothing group id. 0 = smoothing groupd is off. - int pad_; - std::vector vertex_indices; // face vertex indices. - - face_t() : smoothing_group_id(0), pad_(0) {} -}; - -// Internal data structure for line representation -struct __line_t { - // l v1/vt1 v2/vt2 ... - // In the specification, line primitrive does not have normal index, but - // TinyObjLoader allow it - std::vector vertex_indices; -}; - -// Internal data structure for points representation -struct __points_t { - // p v1 v2 ... - // In the specification, point primitrive does not have normal index and - // texture coord index, but TinyObjLoader allow it. - std::vector vertex_indices; -}; - -struct tag_sizes { - tag_sizes() : num_ints(0), num_reals(0), num_strings(0) {} - int num_ints; - int num_reals; - int num_strings; -}; - -struct obj_shape { - std::vector v; - std::vector vn; - std::vector vt; -}; - -// -// Manages group of primitives(face, line, points, ...) -struct PrimGroup { - std::vector faceGroup; - std::vector<__line_t> lineGroup; - std::vector<__points_t> pointsGroup; - - void clear() { - faceGroup.clear(); - lineGroup.clear(); - pointsGroup.clear(); - } - - bool IsEmpty() const { - return faceGroup.empty() && lineGroup.empty() && pointsGroup.empty(); - } - - // TODO(syoyo): bspline, surface, ... -}; - -// See -// http://stackoverflow.com/questions/6089231/getting-std-ifstream-to-handle-lf-cr-and-crlf -static std::istream &safeGetline(std::istream &is, std::string &t) { - t.clear(); - - // The characters in the stream are read one-by-one using a std::streambuf. - // That is faster than reading them one-by-one using the std::istream. - // Code that uses streambuf this way must be guarded by a sentry object. - // The sentry object performs various tasks, - // such as thread synchronization and updating the stream state. - - std::istream::sentry se(is, true); - std::streambuf *sb = is.rdbuf(); - - if (se) { - for (;;) { - int c = sb->sbumpc(); - switch (c) { - case '\n': - return is; - case '\r': - if (sb->sgetc() == '\n') sb->sbumpc(); - return is; - case EOF: - // Also handle the case when the last line has no line ending - if (t.empty()) is.setstate(std::ios::eofbit); - return is; - default: - t += static_cast(c); - } - } - } - - return is; -} - -#define IS_SPACE(x) (((x) == ' ') || ((x) == '\t')) -#define IS_DIGIT(x) \ - (static_cast((x) - '0') < static_cast(10)) -#define IS_NEW_LINE(x) (((x) == '\r') || ((x) == '\n') || ((x) == '\0')) - -// Make index zero-base, and also support relative index. -static inline bool fixIndex(int idx, int n, int *ret) { - if (!ret) { - return false; - } - - if (idx > 0) { - (*ret) = idx - 1; - return true; - } - - if (idx == 0) { - // zero is not allowed according to the spec. - return false; - } - - if (idx < 0) { - (*ret) = n + idx; // negative value = relative - return true; - } - - return false; // never reach here. -} - -static inline std::string parseString(const char **token) { - std::string s; - (*token) += strspn((*token), " \t"); - size_t e = strcspn((*token), " \t\r"); - s = std::string((*token), &(*token)[e]); - (*token) += e; - return s; -} - -static inline int parseInt(const char **token) { - (*token) += strspn((*token), " \t"); - int i = atoi((*token)); - (*token) += strcspn((*token), " \t\r"); - return i; -} - -// Tries to parse a floating point number located at s. -// -// s_end should be a location in the string where reading should absolutely -// stop. For example at the end of the string, to prevent buffer overflows. -// -// Parses the following EBNF grammar: -// sign = "+" | "-" ; -// END = ? anything not in digit ? -// digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; -// integer = [sign] , digit , {digit} ; -// decimal = integer , ["." , integer] ; -// float = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ; -// -// Valid strings are for example: -// -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2 -// -// If the parsing is a success, result is set to the parsed value and true -// is returned. -// -// The function is greedy and will parse until any of the following happens: -// - a non-conforming character is encountered. -// - s_end is reached. -// -// The following situations triggers a failure: -// - s >= s_end. -// - parse failure. -// -static bool tryParseDouble(const char *s, const char *s_end, double *result) { - if (s >= s_end) { - return false; - } - - double mantissa = 0.0; - // This exponent is base 2 rather than 10. - // However the exponent we parse is supposed to be one of ten, - // thus we must take care to convert the exponent/and or the - // mantissa to a * 2^E, where a is the mantissa and E is the - // exponent. - // To get the final double we will use ldexp, it requires the - // exponent to be in base 2. - int exponent = 0; - - // NOTE: THESE MUST BE DECLARED HERE SINCE WE ARE NOT ALLOWED - // TO JUMP OVER DEFINITIONS. - char sign = '+'; - char exp_sign = '+'; - char const *curr = s; - - // How many characters were read in a loop. - int read = 0; - // Tells whether a loop terminated due to reaching s_end. - bool end_not_reached = false; - bool leading_decimal_dots = false; - - /* - BEGIN PARSING. - */ - - // Find out what sign we've got. - if (*curr == '+' || *curr == '-') { - sign = *curr; - curr++; - if ((curr != s_end) && (*curr == '.')) { - // accept. Somethig like `.7e+2`, `-.5234` - leading_decimal_dots = true; - } - } else if (IS_DIGIT(*curr)) { /* Pass through. */ - } else if (*curr == '.') { - // accept. Somethig like `.7e+2`, `-.5234` - leading_decimal_dots = true; - } else { - goto fail; - } - - // Read the integer part. - end_not_reached = (curr != s_end); - if (!leading_decimal_dots) { - while (end_not_reached && IS_DIGIT(*curr)) { - mantissa *= 10; - mantissa += static_cast(*curr - 0x30); - curr++; - read++; - end_not_reached = (curr != s_end); - } - - // We must make sure we actually got something. - if (read == 0) goto fail; - } - - // We allow numbers of form "#", "###" etc. - if (!end_not_reached) goto assemble; - - // Read the decimal part. - if (*curr == '.') { - curr++; - read = 1; - end_not_reached = (curr != s_end); - while (end_not_reached && IS_DIGIT(*curr)) { - static const double pow_lut[] = { - 1.0, 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, 0.0000001, - }; - const int lut_entries = sizeof pow_lut / sizeof pow_lut[0]; - - // NOTE: Don't use powf here, it will absolutely murder precision. - mantissa += static_cast(*curr - 0x30) * - (read < lut_entries ? pow_lut[read] : std::pow(10.0, -read)); - read++; - curr++; - end_not_reached = (curr != s_end); - } - } else if (*curr == 'e' || *curr == 'E') { - } else { - goto assemble; - } - - if (!end_not_reached) goto assemble; - - // Read the exponent part. - if (*curr == 'e' || *curr == 'E') { - curr++; - // Figure out if a sign is present and if it is. - end_not_reached = (curr != s_end); - if (end_not_reached && (*curr == '+' || *curr == '-')) { - exp_sign = *curr; - curr++; - } else if (IS_DIGIT(*curr)) { /* Pass through. */ - } else { - // Empty E is not allowed. - goto fail; - } - - read = 0; - end_not_reached = (curr != s_end); - while (end_not_reached && IS_DIGIT(*curr)) { - // To avoid annoying MSVC's min/max macro definiton, - // Use hardcoded int max value - if (exponent > (2147483647/10)) { // 2147483647 = std::numeric_limits::max() - // Integer overflow - goto fail; - } - exponent *= 10; - exponent += static_cast(*curr - 0x30); - curr++; - read++; - end_not_reached = (curr != s_end); - } - exponent *= (exp_sign == '+' ? 1 : -1); - if (read == 0) goto fail; - } - -assemble: - *result = (sign == '+' ? 1 : -1) * - (exponent ? std::ldexp(mantissa * std::pow(5.0, exponent), exponent) - : mantissa); - return true; -fail: - return false; -} - -static inline real_t parseReal(const char **token, double default_value = 0.0) { - (*token) += strspn((*token), " \t"); - const char *end = (*token) + strcspn((*token), " \t\r"); - double val = default_value; - tryParseDouble((*token), end, &val); - real_t f = static_cast(val); - (*token) = end; - return f; -} - -static inline bool parseReal(const char **token, real_t *out) { - (*token) += strspn((*token), " \t"); - const char *end = (*token) + strcspn((*token), " \t\r"); - double val; - bool ret = tryParseDouble((*token), end, &val); - if (ret) { - real_t f = static_cast(val); - (*out) = f; - } - (*token) = end; - return ret; -} - -static inline void parseReal2(real_t *x, real_t *y, const char **token, - const double default_x = 0.0, - const double default_y = 0.0) { - (*x) = parseReal(token, default_x); - (*y) = parseReal(token, default_y); -} - -static inline void parseReal3(real_t *x, real_t *y, real_t *z, - const char **token, const double default_x = 0.0, - const double default_y = 0.0, - const double default_z = 0.0) { - (*x) = parseReal(token, default_x); - (*y) = parseReal(token, default_y); - (*z) = parseReal(token, default_z); -} - -static inline void parseV(real_t *x, real_t *y, real_t *z, real_t *w, - const char **token, const double default_x = 0.0, - const double default_y = 0.0, - const double default_z = 0.0, - const double default_w = 1.0) { - (*x) = parseReal(token, default_x); - (*y) = parseReal(token, default_y); - (*z) = parseReal(token, default_z); - (*w) = parseReal(token, default_w); -} - -// Extension: parse vertex with colors(6 items) -static inline bool parseVertexWithColor(real_t *x, real_t *y, real_t *z, - real_t *r, real_t *g, real_t *b, - const char **token, - const double default_x = 0.0, - const double default_y = 0.0, - const double default_z = 0.0) { - (*x) = parseReal(token, default_x); - (*y) = parseReal(token, default_y); - (*z) = parseReal(token, default_z); - - const bool found_color = - parseReal(token, r) && parseReal(token, g) && parseReal(token, b); - - if (!found_color) { - (*r) = (*g) = (*b) = 1.0; - } - - return found_color; -} - -static inline bool parseOnOff(const char **token, bool default_value = true) { - (*token) += strspn((*token), " \t"); - const char *end = (*token) + strcspn((*token), " \t\r"); - - bool ret = default_value; - if ((0 == strncmp((*token), "on", 2))) { - ret = true; - } else if ((0 == strncmp((*token), "off", 3))) { - ret = false; - } - - (*token) = end; - return ret; -} - -static inline texture_type_t parseTextureType( - const char **token, texture_type_t default_value = TEXTURE_TYPE_NONE) { - (*token) += strspn((*token), " \t"); - const char *end = (*token) + strcspn((*token), " \t\r"); - texture_type_t ty = default_value; - - if ((0 == strncmp((*token), "cube_top", strlen("cube_top")))) { - ty = TEXTURE_TYPE_CUBE_TOP; - } else if ((0 == strncmp((*token), "cube_bottom", strlen("cube_bottom")))) { - ty = TEXTURE_TYPE_CUBE_BOTTOM; - } else if ((0 == strncmp((*token), "cube_left", strlen("cube_left")))) { - ty = TEXTURE_TYPE_CUBE_LEFT; - } else if ((0 == strncmp((*token), "cube_right", strlen("cube_right")))) { - ty = TEXTURE_TYPE_CUBE_RIGHT; - } else if ((0 == strncmp((*token), "cube_front", strlen("cube_front")))) { - ty = TEXTURE_TYPE_CUBE_FRONT; - } else if ((0 == strncmp((*token), "cube_back", strlen("cube_back")))) { - ty = TEXTURE_TYPE_CUBE_BACK; - } else if ((0 == strncmp((*token), "sphere", strlen("sphere")))) { - ty = TEXTURE_TYPE_SPHERE; - } - - (*token) = end; - return ty; -} - -static tag_sizes parseTagTriple(const char **token) { - tag_sizes ts; - - (*token) += strspn((*token), " \t"); - ts.num_ints = atoi((*token)); - (*token) += strcspn((*token), "/ \t\r"); - if ((*token)[0] != '/') { - return ts; - } - - (*token)++; // Skip '/' - - (*token) += strspn((*token), " \t"); - ts.num_reals = atoi((*token)); - (*token) += strcspn((*token), "/ \t\r"); - if ((*token)[0] != '/') { - return ts; - } - (*token)++; // Skip '/' - - ts.num_strings = parseInt(token); - - return ts; -} - -// Parse triples with index offsets: i, i/j/k, i//k, i/j -static bool parseTriple(const char **token, int vsize, int vnsize, int vtsize, - vertex_index_t *ret) { - if (!ret) { - return false; - } - - vertex_index_t vi(-1); - - if (!fixIndex(atoi((*token)), vsize, &(vi.v_idx))) { - return false; - } - - (*token) += strcspn((*token), "/ \t\r"); - if ((*token)[0] != '/') { - (*ret) = vi; - return true; - } - (*token)++; - - // i//k - if ((*token)[0] == '/') { - (*token)++; - if (!fixIndex(atoi((*token)), vnsize, &(vi.vn_idx))) { - return false; - } - (*token) += strcspn((*token), "/ \t\r"); - (*ret) = vi; - return true; - } - - // i/j/k or i/j - if (!fixIndex(atoi((*token)), vtsize, &(vi.vt_idx))) { - return false; - } - - (*token) += strcspn((*token), "/ \t\r"); - if ((*token)[0] != '/') { - (*ret) = vi; - return true; - } - - // i/j/k - (*token)++; // skip '/' - if (!fixIndex(atoi((*token)), vnsize, &(vi.vn_idx))) { - return false; - } - (*token) += strcspn((*token), "/ \t\r"); - - (*ret) = vi; - - return true; -} - -// Parse raw triples: i, i/j/k, i//k, i/j -static vertex_index_t parseRawTriple(const char **token) { - vertex_index_t vi(static_cast(0)); // 0 is an invalid index in OBJ - - vi.v_idx = atoi((*token)); - (*token) += strcspn((*token), "/ \t\r"); - if ((*token)[0] != '/') { - return vi; - } - (*token)++; - - // i//k - if ((*token)[0] == '/') { - (*token)++; - vi.vn_idx = atoi((*token)); - (*token) += strcspn((*token), "/ \t\r"); - return vi; - } - - // i/j/k or i/j - vi.vt_idx = atoi((*token)); - (*token) += strcspn((*token), "/ \t\r"); - if ((*token)[0] != '/') { - return vi; - } - - // i/j/k - (*token)++; // skip '/' - vi.vn_idx = atoi((*token)); - (*token) += strcspn((*token), "/ \t\r"); - return vi; -} - -bool ParseTextureNameAndOption(std::string *texname, texture_option_t *texopt, - const char *linebuf) { - // @todo { write more robust lexer and parser. } - bool found_texname = false; - std::string texture_name; - - const char *token = linebuf; // Assume line ends with NULL - - while (!IS_NEW_LINE((*token))) { - token += strspn(token, " \t"); // skip space - if ((0 == strncmp(token, "-blendu", 7)) && IS_SPACE((token[7]))) { - token += 8; - texopt->blendu = parseOnOff(&token, /* default */ true); - } else if ((0 == strncmp(token, "-blendv", 7)) && IS_SPACE((token[7]))) { - token += 8; - texopt->blendv = parseOnOff(&token, /* default */ true); - } else if ((0 == strncmp(token, "-clamp", 6)) && IS_SPACE((token[6]))) { - token += 7; - texopt->clamp = parseOnOff(&token, /* default */ true); - } else if ((0 == strncmp(token, "-boost", 6)) && IS_SPACE((token[6]))) { - token += 7; - texopt->sharpness = parseReal(&token, 1.0); - } else if ((0 == strncmp(token, "-bm", 3)) && IS_SPACE((token[3]))) { - token += 4; - texopt->bump_multiplier = parseReal(&token, 1.0); - } else if ((0 == strncmp(token, "-o", 2)) && IS_SPACE((token[2]))) { - token += 3; - parseReal3(&(texopt->origin_offset[0]), &(texopt->origin_offset[1]), - &(texopt->origin_offset[2]), &token); - } else if ((0 == strncmp(token, "-s", 2)) && IS_SPACE((token[2]))) { - token += 3; - parseReal3(&(texopt->scale[0]), &(texopt->scale[1]), &(texopt->scale[2]), - &token, 1.0, 1.0, 1.0); - } else if ((0 == strncmp(token, "-t", 2)) && IS_SPACE((token[2]))) { - token += 3; - parseReal3(&(texopt->turbulence[0]), &(texopt->turbulence[1]), - &(texopt->turbulence[2]), &token); - } else if ((0 == strncmp(token, "-type", 5)) && IS_SPACE((token[5]))) { - token += 5; - texopt->type = parseTextureType((&token), TEXTURE_TYPE_NONE); - } else if ((0 == strncmp(token, "-texres", 7)) && IS_SPACE((token[7]))) { - token += 7; - // TODO(syoyo): Check if arg is int type. - texopt->texture_resolution = parseInt(&token); - } else if ((0 == strncmp(token, "-imfchan", 8)) && IS_SPACE((token[8]))) { - token += 9; - token += strspn(token, " \t"); - const char *end = token + strcspn(token, " \t\r"); - if ((end - token) == 1) { // Assume one char for -imfchan - texopt->imfchan = (*token); - } - token = end; - } else if ((0 == strncmp(token, "-mm", 3)) && IS_SPACE((token[3]))) { - token += 4; - parseReal2(&(texopt->brightness), &(texopt->contrast), &token, 0.0, 1.0); - } else if ((0 == strncmp(token, "-colorspace", 11)) && - IS_SPACE((token[11]))) { - token += 12; - texopt->colorspace = parseString(&token); - } else { -// Assume texture filename -#if 0 - size_t len = strcspn(token, " \t\r"); // untile next space - texture_name = std::string(token, token + len); - token += len; - - token += strspn(token, " \t"); // skip space -#else - // Read filename until line end to parse filename containing whitespace - // TODO(syoyo): Support parsing texture option flag after the filename. - texture_name = std::string(token); - token += texture_name.length(); -#endif - - found_texname = true; - } - } - - if (found_texname) { - (*texname) = texture_name; - return true; - } else { - return false; - } -} - -static void InitTexOpt(texture_option_t *texopt, const bool is_bump) { - if (is_bump) { - texopt->imfchan = 'l'; - } else { - texopt->imfchan = 'm'; - } - texopt->bump_multiplier = static_cast(1.0); - texopt->clamp = false; - texopt->blendu = true; - texopt->blendv = true; - texopt->sharpness = static_cast(1.0); - texopt->brightness = static_cast(0.0); - texopt->contrast = static_cast(1.0); - texopt->origin_offset[0] = static_cast(0.0); - texopt->origin_offset[1] = static_cast(0.0); - texopt->origin_offset[2] = static_cast(0.0); - texopt->scale[0] = static_cast(1.0); - texopt->scale[1] = static_cast(1.0); - texopt->scale[2] = static_cast(1.0); - texopt->turbulence[0] = static_cast(0.0); - texopt->turbulence[1] = static_cast(0.0); - texopt->turbulence[2] = static_cast(0.0); - texopt->texture_resolution = -1; - texopt->type = TEXTURE_TYPE_NONE; -} - -static void InitMaterial(material_t *material) { - InitTexOpt(&material->ambient_texopt, /* is_bump */ false); - InitTexOpt(&material->diffuse_texopt, /* is_bump */ false); - InitTexOpt(&material->specular_texopt, /* is_bump */ false); - InitTexOpt(&material->specular_highlight_texopt, /* is_bump */ false); - InitTexOpt(&material->bump_texopt, /* is_bump */ true); - InitTexOpt(&material->displacement_texopt, /* is_bump */ false); - InitTexOpt(&material->alpha_texopt, /* is_bump */ false); - InitTexOpt(&material->reflection_texopt, /* is_bump */ false); - InitTexOpt(&material->roughness_texopt, /* is_bump */ false); - InitTexOpt(&material->metallic_texopt, /* is_bump */ false); - InitTexOpt(&material->sheen_texopt, /* is_bump */ false); - InitTexOpt(&material->emissive_texopt, /* is_bump */ false); - InitTexOpt(&material->normal_texopt, - /* is_bump */ false); // @fixme { is_bump will be true? } - material->name = ""; - material->ambient_texname = ""; - material->diffuse_texname = ""; - material->specular_texname = ""; - material->specular_highlight_texname = ""; - material->bump_texname = ""; - material->displacement_texname = ""; - material->reflection_texname = ""; - material->alpha_texname = ""; - for (int i = 0; i < 3; i++) { - material->ambient[i] = static_cast(0.0); - material->diffuse[i] = static_cast(0.0); - material->specular[i] = static_cast(0.0); - material->transmittance[i] = static_cast(0.0); - material->emission[i] = static_cast(0.0); - } - material->illum = 0; - material->dissolve = static_cast(1.0); - material->shininess = static_cast(1.0); - material->ior = static_cast(1.0); - - material->roughness = static_cast(0.0); - material->metallic = static_cast(0.0); - material->sheen = static_cast(0.0); - material->clearcoat_thickness = static_cast(0.0); - material->clearcoat_roughness = static_cast(0.0); - material->anisotropy_rotation = static_cast(0.0); - material->anisotropy = static_cast(0.0); - material->roughness_texname = ""; - material->metallic_texname = ""; - material->sheen_texname = ""; - material->emissive_texname = ""; - material->normal_texname = ""; - - material->unknown_parameter.clear(); -} - -// code from https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html -template -static int pnpoly(int nvert, T *vertx, T *verty, T testx, T testy) { - int i, j, c = 0; - for (i = 0, j = nvert - 1; i < nvert; j = i++) { - if (((verty[i] > testy) != (verty[j] > testy)) && - (testx < - (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + - vertx[i])) - c = !c; - } - return c; -} - -// TODO(syoyo): refactor function. -static bool exportGroupsToShape(shape_t *shape, const PrimGroup &prim_group, - const std::vector &tags, - const int material_id, const std::string &name, - bool triangulate, const std::vector &v, - std::string *warn) { - if (prim_group.IsEmpty()) { - return false; - } - - shape->name = name; - - // polygon - if (!prim_group.faceGroup.empty()) { - // Flatten vertices and indices - for (size_t i = 0; i < prim_group.faceGroup.size(); i++) { - const face_t &face = prim_group.faceGroup[i]; - - size_t npolys = face.vertex_indices.size(); - - if (npolys < 3) { - // Face must have 3+ vertices. - if (warn) { - (*warn) += "Degenerated face found\n."; - } - continue; - } - - if (triangulate) { - if (npolys == 4) { - vertex_index_t i0 = face.vertex_indices[0]; - vertex_index_t i1 = face.vertex_indices[1]; - vertex_index_t i2 = face.vertex_indices[2]; - vertex_index_t i3 = face.vertex_indices[3]; - - size_t vi0 = size_t(i0.v_idx); - size_t vi1 = size_t(i1.v_idx); - size_t vi2 = size_t(i2.v_idx); - size_t vi3 = size_t(i3.v_idx); - - if (((3 * vi0 + 2) >= v.size()) || ((3 * vi1 + 2) >= v.size()) || - ((3 * vi2 + 2) >= v.size()) || ((3 * vi3 + 2) >= v.size())) { - // Invalid triangle. - // FIXME(syoyo): Is it ok to simply skip this invalid triangle? - if (warn) { - (*warn) += "Face with invalid vertex index found.\n"; - } - continue; - } - - real_t v0x = v[vi0 * 3 + 0]; - real_t v0y = v[vi0 * 3 + 1]; - real_t v0z = v[vi0 * 3 + 2]; - real_t v1x = v[vi1 * 3 + 0]; - real_t v1y = v[vi1 * 3 + 1]; - real_t v1z = v[vi1 * 3 + 2]; - real_t v2x = v[vi2 * 3 + 0]; - real_t v2y = v[vi2 * 3 + 1]; - real_t v2z = v[vi2 * 3 + 2]; - real_t v3x = v[vi3 * 3 + 0]; - real_t v3y = v[vi3 * 3 + 1]; - real_t v3z = v[vi3 * 3 + 2]; - - // There are two candidates to split the quad into two triangles. - // - // Choose the shortest edge. - // TODO: Is it better to determine the edge to split by calculating - // the area of each triangle? - // - // +---+ - // |\ | - // | \ | - // | \| - // +---+ - // - // +---+ - // | /| - // | / | - // |/ | - // +---+ - - real_t e02x = v2x - v0x; - real_t e02y = v2y - v0y; - real_t e02z = v2z - v0z; - real_t e13x = v3x - v1x; - real_t e13y = v3y - v1y; - real_t e13z = v3z - v1z; - - real_t sqr02 = e02x * e02x + e02y * e02y + e02z * e02z; - real_t sqr13 = e13x * e13x + e13y * e13y + e13z * e13z; - - index_t idx0, idx1, idx2, idx3; - - idx0.vertex_index = i0.v_idx; - idx0.normal_index = i0.vn_idx; - idx0.texcoord_index = i0.vt_idx; - idx1.vertex_index = i1.v_idx; - idx1.normal_index = i1.vn_idx; - idx1.texcoord_index = i1.vt_idx; - idx2.vertex_index = i2.v_idx; - idx2.normal_index = i2.vn_idx; - idx2.texcoord_index = i2.vt_idx; - idx3.vertex_index = i3.v_idx; - idx3.normal_index = i3.vn_idx; - idx3.texcoord_index = i3.vt_idx; - - if (sqr02 < sqr13) { - // [0, 1, 2], [0, 2, 3] - shape->mesh.indices.push_back(idx0); - shape->mesh.indices.push_back(idx1); - shape->mesh.indices.push_back(idx2); - - shape->mesh.indices.push_back(idx0); - shape->mesh.indices.push_back(idx2); - shape->mesh.indices.push_back(idx3); - } else { - // [0, 1, 3], [1, 2, 3] - shape->mesh.indices.push_back(idx0); - shape->mesh.indices.push_back(idx1); - shape->mesh.indices.push_back(idx3); - - shape->mesh.indices.push_back(idx1); - shape->mesh.indices.push_back(idx2); - shape->mesh.indices.push_back(idx3); - } - - // Two triangle faces - shape->mesh.num_face_vertices.push_back(3); - shape->mesh.num_face_vertices.push_back(3); - - shape->mesh.material_ids.push_back(material_id); - shape->mesh.material_ids.push_back(material_id); - - shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id); - shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id); - - } else { - vertex_index_t i0 = face.vertex_indices[0]; - vertex_index_t i1(-1); - vertex_index_t i2 = face.vertex_indices[1]; - - // find the two axes to work in - size_t axes[2] = {1, 2}; - for (size_t k = 0; k < npolys; ++k) { - i0 = face.vertex_indices[(k + 0) % npolys]; - i1 = face.vertex_indices[(k + 1) % npolys]; - i2 = face.vertex_indices[(k + 2) % npolys]; - size_t vi0 = size_t(i0.v_idx); - size_t vi1 = size_t(i1.v_idx); - size_t vi2 = size_t(i2.v_idx); - - if (((3 * vi0 + 2) >= v.size()) || ((3 * vi1 + 2) >= v.size()) || - ((3 * vi2 + 2) >= v.size())) { - // Invalid triangle. - // FIXME(syoyo): Is it ok to simply skip this invalid triangle? - continue; - } - real_t v0x = v[vi0 * 3 + 0]; - real_t v0y = v[vi0 * 3 + 1]; - real_t v0z = v[vi0 * 3 + 2]; - real_t v1x = v[vi1 * 3 + 0]; - real_t v1y = v[vi1 * 3 + 1]; - real_t v1z = v[vi1 * 3 + 2]; - real_t v2x = v[vi2 * 3 + 0]; - real_t v2y = v[vi2 * 3 + 1]; - real_t v2z = v[vi2 * 3 + 2]; - real_t e0x = v1x - v0x; - real_t e0y = v1y - v0y; - real_t e0z = v1z - v0z; - real_t e1x = v2x - v1x; - real_t e1y = v2y - v1y; - real_t e1z = v2z - v1z; - real_t cx = std::fabs(e0y * e1z - e0z * e1y); - real_t cy = std::fabs(e0z * e1x - e0x * e1z); - real_t cz = std::fabs(e0x * e1y - e0y * e1x); - const real_t epsilon = std::numeric_limits::epsilon(); - // std::cout << "cx " << cx << ", cy " << cy << ", cz " << cz << - // "\n"; - if (cx > epsilon || cy > epsilon || cz > epsilon) { - // std::cout << "corner\n"; - // found a corner - if (cx > cy && cx > cz) { - // std::cout << "pattern0\n"; - } else { - // std::cout << "axes[0] = 0\n"; - axes[0] = 0; - if (cz > cx && cz > cy) { - // std::cout << "axes[1] = 1\n"; - axes[1] = 1; - } - } - break; - } - } - -#ifdef TINYOBJLOADER_USE_MAPBOX_EARCUT - using Point = std::array; - - // first polyline define the main polygon. - // following polylines define holes(not used in tinyobj). - std::vector > polygon; - - std::vector polyline; - - // Fill polygon data(facevarying vertices). - for (size_t k = 0; k < npolys; k++) { - i0 = face.vertex_indices[k]; - size_t vi0 = size_t(i0.v_idx); - - assert(((3 * vi0 + 2) < v.size())); - - real_t v0x = v[vi0 * 3 + axes[0]]; - real_t v0y = v[vi0 * 3 + axes[1]]; - - polyline.push_back({v0x, v0y}); - } - - polygon.push_back(polyline); - std::vector indices = mapbox::earcut(polygon); - // => result = 3 * faces, clockwise - - assert(indices.size() % 3 == 0); - - // Reconstruct vertex_index_t - for (size_t k = 0; k < indices.size() / 3; k++) { - { - index_t idx0, idx1, idx2; - idx0.vertex_index = face.vertex_indices[indices[3 * k + 0]].v_idx; - idx0.normal_index = - face.vertex_indices[indices[3 * k + 0]].vn_idx; - idx0.texcoord_index = - face.vertex_indices[indices[3 * k + 0]].vt_idx; - idx1.vertex_index = face.vertex_indices[indices[3 * k + 1]].v_idx; - idx1.normal_index = - face.vertex_indices[indices[3 * k + 1]].vn_idx; - idx1.texcoord_index = - face.vertex_indices[indices[3 * k + 1]].vt_idx; - idx2.vertex_index = face.vertex_indices[indices[3 * k + 2]].v_idx; - idx2.normal_index = - face.vertex_indices[indices[3 * k + 2]].vn_idx; - idx2.texcoord_index = - face.vertex_indices[indices[3 * k + 2]].vt_idx; - - shape->mesh.indices.push_back(idx0); - shape->mesh.indices.push_back(idx1); - shape->mesh.indices.push_back(idx2); - - shape->mesh.num_face_vertices.push_back(3); - shape->mesh.material_ids.push_back(material_id); - shape->mesh.smoothing_group_ids.push_back( - face.smoothing_group_id); - } - } - -#else // Built-in ear clipping triangulation - - - face_t remainingFace = face; // copy - size_t guess_vert = 0; - vertex_index_t ind[3]; - real_t vx[3]; - real_t vy[3]; - - // How many iterations can we do without decreasing the remaining - // vertices. - size_t remainingIterations = face.vertex_indices.size(); - size_t previousRemainingVertices = - remainingFace.vertex_indices.size(); - - while (remainingFace.vertex_indices.size() > 3 && - remainingIterations > 0) { - // std::cout << "remainingIterations " << remainingIterations << - // "\n"; - - npolys = remainingFace.vertex_indices.size(); - if (guess_vert >= npolys) { - guess_vert -= npolys; - } - - if (previousRemainingVertices != npolys) { - // The number of remaining vertices decreased. Reset counters. - previousRemainingVertices = npolys; - remainingIterations = npolys; - } else { - // We didn't consume a vertex on previous iteration, reduce the - // available iterations. - remainingIterations--; - } - - for (size_t k = 0; k < 3; k++) { - ind[k] = remainingFace.vertex_indices[(guess_vert + k) % npolys]; - size_t vi = size_t(ind[k].v_idx); - if (((vi * 3 + axes[0]) >= v.size()) || - ((vi * 3 + axes[1]) >= v.size())) { - // ??? - vx[k] = static_cast(0.0); - vy[k] = static_cast(0.0); - } else { - vx[k] = v[vi * 3 + axes[0]]; - vy[k] = v[vi * 3 + axes[1]]; - } - } - - // - // area is calculated per face - // - real_t e0x = vx[1] - vx[0]; - real_t e0y = vy[1] - vy[0]; - real_t e1x = vx[2] - vx[1]; - real_t e1y = vy[2] - vy[1]; - real_t cross = e0x * e1y - e0y * e1x; - // std::cout << "axes = " << axes[0] << ", " << axes[1] << "\n"; - // std::cout << "e0x, e0y, e1x, e1y " << e0x << ", " << e0y << ", " - // << e1x << ", " << e1y << "\n"; - - real_t area = (vx[0] * vy[1] - vy[0] * vx[1]) * static_cast(0.5); - // std::cout << "cross " << cross << ", area " << area << "\n"; - // if an internal angle - if (cross * area < static_cast(0.0)) { - // std::cout << "internal \n"; - guess_vert += 1; - // std::cout << "guess vert : " << guess_vert << "\n"; - continue; - } - - // check all other verts in case they are inside this triangle - bool overlap = false; - for (size_t otherVert = 3; otherVert < npolys; ++otherVert) { - size_t idx = (guess_vert + otherVert) % npolys; - - if (idx >= remainingFace.vertex_indices.size()) { - // std::cout << "???0\n"; - // ??? - continue; - } - - size_t ovi = size_t(remainingFace.vertex_indices[idx].v_idx); - - if (((ovi * 3 + axes[0]) >= v.size()) || - ((ovi * 3 + axes[1]) >= v.size())) { - // std::cout << "???1\n"; - // ??? - continue; - } - real_t tx = v[ovi * 3 + axes[0]]; - real_t ty = v[ovi * 3 + axes[1]]; - if (pnpoly(3, vx, vy, tx, ty)) { - // std::cout << "overlap\n"; - overlap = true; - break; - } - } - - if (overlap) { - // std::cout << "overlap2\n"; - guess_vert += 1; - continue; - } - - // this triangle is an ear - { - index_t idx0, idx1, idx2; - idx0.vertex_index = ind[0].v_idx; - idx0.normal_index = ind[0].vn_idx; - idx0.texcoord_index = ind[0].vt_idx; - idx1.vertex_index = ind[1].v_idx; - idx1.normal_index = ind[1].vn_idx; - idx1.texcoord_index = ind[1].vt_idx; - idx2.vertex_index = ind[2].v_idx; - idx2.normal_index = ind[2].vn_idx; - idx2.texcoord_index = ind[2].vt_idx; - - shape->mesh.indices.push_back(idx0); - shape->mesh.indices.push_back(idx1); - shape->mesh.indices.push_back(idx2); - - shape->mesh.num_face_vertices.push_back(3); - shape->mesh.material_ids.push_back(material_id); - shape->mesh.smoothing_group_ids.push_back( - face.smoothing_group_id); - } - - // remove v1 from the list - size_t removed_vert_index = (guess_vert + 1) % npolys; - while (removed_vert_index + 1 < npolys) { - remainingFace.vertex_indices[removed_vert_index] = - remainingFace.vertex_indices[removed_vert_index + 1]; - removed_vert_index += 1; - } - remainingFace.vertex_indices.pop_back(); - } - - // std::cout << "remainingFace.vi.size = " << - // remainingFace.vertex_indices.size() << "\n"; - if (remainingFace.vertex_indices.size() == 3) { - i0 = remainingFace.vertex_indices[0]; - i1 = remainingFace.vertex_indices[1]; - i2 = remainingFace.vertex_indices[2]; - { - index_t idx0, idx1, idx2; - idx0.vertex_index = i0.v_idx; - idx0.normal_index = i0.vn_idx; - idx0.texcoord_index = i0.vt_idx; - idx1.vertex_index = i1.v_idx; - idx1.normal_index = i1.vn_idx; - idx1.texcoord_index = i1.vt_idx; - idx2.vertex_index = i2.v_idx; - idx2.normal_index = i2.vn_idx; - idx2.texcoord_index = i2.vt_idx; - - shape->mesh.indices.push_back(idx0); - shape->mesh.indices.push_back(idx1); - shape->mesh.indices.push_back(idx2); - - shape->mesh.num_face_vertices.push_back(3); - shape->mesh.material_ids.push_back(material_id); - shape->mesh.smoothing_group_ids.push_back( - face.smoothing_group_id); - } - } -#endif - } // npolys - } else { - for (size_t k = 0; k < npolys; k++) { - index_t idx; - idx.vertex_index = face.vertex_indices[k].v_idx; - idx.normal_index = face.vertex_indices[k].vn_idx; - idx.texcoord_index = face.vertex_indices[k].vt_idx; - shape->mesh.indices.push_back(idx); - } - - shape->mesh.num_face_vertices.push_back( - static_cast(npolys)); - shape->mesh.material_ids.push_back(material_id); // per face - shape->mesh.smoothing_group_ids.push_back( - face.smoothing_group_id); // per face - } - } - - shape->mesh.tags = tags; - } - - // line - if (!prim_group.lineGroup.empty()) { - // Flatten indices - for (size_t i = 0; i < prim_group.lineGroup.size(); i++) { - for (size_t j = 0; j < prim_group.lineGroup[i].vertex_indices.size(); - j++) { - const vertex_index_t &vi = prim_group.lineGroup[i].vertex_indices[j]; - - index_t idx; - idx.vertex_index = vi.v_idx; - idx.normal_index = vi.vn_idx; - idx.texcoord_index = vi.vt_idx; - - shape->lines.indices.push_back(idx); - } - - shape->lines.num_line_vertices.push_back( - int(prim_group.lineGroup[i].vertex_indices.size())); - } - } - - // points - if (!prim_group.pointsGroup.empty()) { - // Flatten & convert indices - for (size_t i = 0; i < prim_group.pointsGroup.size(); i++) { - for (size_t j = 0; j < prim_group.pointsGroup[i].vertex_indices.size(); - j++) { - const vertex_index_t &vi = prim_group.pointsGroup[i].vertex_indices[j]; - - index_t idx; - idx.vertex_index = vi.v_idx; - idx.normal_index = vi.vn_idx; - idx.texcoord_index = vi.vt_idx; - - shape->points.indices.push_back(idx); - } - } - } - - return true; -} - -// Split a string with specified delimiter character and escape character. -// https://rosettacode.org/wiki/Tokenize_a_string_with_escaping#C.2B.2B -static void SplitString(const std::string &s, char delim, char escape, - std::vector &elems) { - std::string token; - - bool escaping = false; - for (size_t i = 0; i < s.size(); ++i) { - char ch = s[i]; - if (escaping) { - escaping = false; - } else if (ch == escape) { - escaping = true; - continue; - } else if (ch == delim) { - if (!token.empty()) { - elems.push_back(token); - } - token.clear(); - continue; - } - token += ch; - } - - elems.push_back(token); -} - -static std::string JoinPath(const std::string &dir, - const std::string &filename) { - if (dir.empty()) { - return filename; - } else { - // check '/' - char lastChar = *dir.rbegin(); - if (lastChar != '/') { - return dir + std::string("/") + filename; - } else { - return dir + filename; - } - } -} - -void LoadMtl(std::map *material_map, - std::vector *materials, std::istream *inStream, - std::string *warning, std::string *err) { - (void)err; - - // Create a default material anyway. - material_t material; - InitMaterial(&material); - - // Issue 43. `d` wins against `Tr` since `Tr` is not in the MTL specification. - bool has_d = false; - bool has_tr = false; - - // has_kd is used to set a default diffuse value when map_Kd is present - // and Kd is not. - bool has_kd = false; - - std::stringstream warn_ss; - - size_t line_no = 0; - std::string linebuf; - while (inStream->peek() != -1) { - safeGetline(*inStream, linebuf); - line_no++; - - // Trim trailing whitespace. - if (linebuf.size() > 0) { - linebuf = linebuf.substr(0, linebuf.find_last_not_of(" \t") + 1); - } - - // Trim newline '\r\n' or '\n' - if (linebuf.size() > 0) { - if (linebuf[linebuf.size() - 1] == '\n') - linebuf.erase(linebuf.size() - 1); - } - if (linebuf.size() > 0) { - if (linebuf[linebuf.size() - 1] == '\r') - linebuf.erase(linebuf.size() - 1); - } - - // Skip if empty line. - if (linebuf.empty()) { - continue; - } - - // Skip leading space. - const char *token = linebuf.c_str(); - token += strspn(token, " \t"); - - assert(token); - if (token[0] == '\0') continue; // empty line - - if (token[0] == '#') continue; // comment line - - // new mtl - if ((0 == strncmp(token, "newmtl", 6)) && IS_SPACE((token[6]))) { - // flush previous material. - if (!material.name.empty()) { - material_map->insert(std::pair( - material.name, static_cast(materials->size()))); - materials->push_back(material); - } - - // initial temporary material - InitMaterial(&material); - - has_d = false; - has_tr = false; - - // set new mtl name - token += 7; - { - std::stringstream sstr; - sstr << token; - material.name = sstr.str(); - } - continue; - } - - // ambient - if (token[0] == 'K' && token[1] == 'a' && IS_SPACE((token[2]))) { - token += 2; - real_t r, g, b; - parseReal3(&r, &g, &b, &token); - material.ambient[0] = r; - material.ambient[1] = g; - material.ambient[2] = b; - continue; - } - - // diffuse - if (token[0] == 'K' && token[1] == 'd' && IS_SPACE((token[2]))) { - token += 2; - real_t r, g, b; - parseReal3(&r, &g, &b, &token); - material.diffuse[0] = r; - material.diffuse[1] = g; - material.diffuse[2] = b; - has_kd = true; - continue; - } - - // specular - if (token[0] == 'K' && token[1] == 's' && IS_SPACE((token[2]))) { - token += 2; - real_t r, g, b; - parseReal3(&r, &g, &b, &token); - material.specular[0] = r; - material.specular[1] = g; - material.specular[2] = b; - continue; - } - - // transmittance - if ((token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) || - (token[0] == 'T' && token[1] == 'f' && IS_SPACE((token[2])))) { - token += 2; - real_t r, g, b; - parseReal3(&r, &g, &b, &token); - material.transmittance[0] = r; - material.transmittance[1] = g; - material.transmittance[2] = b; - continue; - } - - // ior(index of refraction) - if (token[0] == 'N' && token[1] == 'i' && IS_SPACE((token[2]))) { - token += 2; - material.ior = parseReal(&token); - continue; - } - - // emission - if (token[0] == 'K' && token[1] == 'e' && IS_SPACE(token[2])) { - token += 2; - real_t r, g, b; - parseReal3(&r, &g, &b, &token); - material.emission[0] = r; - material.emission[1] = g; - material.emission[2] = b; - continue; - } - - // shininess - if (token[0] == 'N' && token[1] == 's' && IS_SPACE(token[2])) { - token += 2; - material.shininess = parseReal(&token); - continue; - } - - // illum model - if (0 == strncmp(token, "illum", 5) && IS_SPACE(token[5])) { - token += 6; - material.illum = parseInt(&token); - continue; - } - - // dissolve - if ((token[0] == 'd' && IS_SPACE(token[1]))) { - token += 1; - material.dissolve = parseReal(&token); - - if (has_tr) { - warn_ss << "Both `d` and `Tr` parameters defined for \"" - << material.name - << "\". Use the value of `d` for dissolve (line " << line_no - << " in .mtl.)\n"; - } - has_d = true; - continue; - } - if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) { - token += 2; - if (has_d) { - // `d` wins. Ignore `Tr` value. - warn_ss << "Both `d` and `Tr` parameters defined for \"" - << material.name - << "\". Use the value of `d` for dissolve (line " << line_no - << " in .mtl.)\n"; - } else { - // We invert value of Tr(assume Tr is in range [0, 1]) - // NOTE: Interpretation of Tr is application(exporter) dependent. For - // some application(e.g. 3ds max obj exporter), Tr = d(Issue 43) - material.dissolve = static_cast(1.0) - parseReal(&token); - } - has_tr = true; - continue; - } - - // PBR: roughness - if (token[0] == 'P' && token[1] == 'r' && IS_SPACE(token[2])) { - token += 2; - material.roughness = parseReal(&token); - continue; - } - - // PBR: metallic - if (token[0] == 'P' && token[1] == 'm' && IS_SPACE(token[2])) { - token += 2; - material.metallic = parseReal(&token); - continue; - } - - // PBR: sheen - if (token[0] == 'P' && token[1] == 's' && IS_SPACE(token[2])) { - token += 2; - material.sheen = parseReal(&token); - continue; - } - - // PBR: clearcoat thickness - if (token[0] == 'P' && token[1] == 'c' && IS_SPACE(token[2])) { - token += 2; - material.clearcoat_thickness = parseReal(&token); - continue; - } - - // PBR: clearcoat roughness - if ((0 == strncmp(token, "Pcr", 3)) && IS_SPACE(token[3])) { - token += 4; - material.clearcoat_roughness = parseReal(&token); - continue; - } - - // PBR: anisotropy - if ((0 == strncmp(token, "aniso", 5)) && IS_SPACE(token[5])) { - token += 6; - material.anisotropy = parseReal(&token); - continue; - } - - // PBR: anisotropy rotation - if ((0 == strncmp(token, "anisor", 6)) && IS_SPACE(token[6])) { - token += 7; - material.anisotropy_rotation = parseReal(&token); - continue; - } - - // ambient texture - if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) { - token += 7; - ParseTextureNameAndOption(&(material.ambient_texname), - &(material.ambient_texopt), token); - continue; - } - - // diffuse texture - if ((0 == strncmp(token, "map_Kd", 6)) && IS_SPACE(token[6])) { - token += 7; - ParseTextureNameAndOption(&(material.diffuse_texname), - &(material.diffuse_texopt), token); - - // Set a decent diffuse default value if a diffuse texture is specified - // without a matching Kd value. - if (!has_kd) { - material.diffuse[0] = static_cast(0.6); - material.diffuse[1] = static_cast(0.6); - material.diffuse[2] = static_cast(0.6); - } - - continue; - } - - // specular texture - if ((0 == strncmp(token, "map_Ks", 6)) && IS_SPACE(token[6])) { - token += 7; - ParseTextureNameAndOption(&(material.specular_texname), - &(material.specular_texopt), token); - continue; - } - - // specular highlight texture - if ((0 == strncmp(token, "map_Ns", 6)) && IS_SPACE(token[6])) { - token += 7; - ParseTextureNameAndOption(&(material.specular_highlight_texname), - &(material.specular_highlight_texopt), token); - continue; - } - - // bump texture - if ((0 == strncmp(token, "map_bump", 8)) && IS_SPACE(token[8])) { - token += 9; - ParseTextureNameAndOption(&(material.bump_texname), - &(material.bump_texopt), token); - continue; - } - - // bump texture - if ((0 == strncmp(token, "map_Bump", 8)) && IS_SPACE(token[8])) { - token += 9; - ParseTextureNameAndOption(&(material.bump_texname), - &(material.bump_texopt), token); - continue; - } - - // bump texture - if ((0 == strncmp(token, "bump", 4)) && IS_SPACE(token[4])) { - token += 5; - ParseTextureNameAndOption(&(material.bump_texname), - &(material.bump_texopt), token); - continue; - } - - // alpha texture - if ((0 == strncmp(token, "map_d", 5)) && IS_SPACE(token[5])) { - token += 6; - material.alpha_texname = token; - ParseTextureNameAndOption(&(material.alpha_texname), - &(material.alpha_texopt), token); - continue; - } - - // displacement texture - if ((0 == strncmp(token, "disp", 4)) && IS_SPACE(token[4])) { - token += 5; - ParseTextureNameAndOption(&(material.displacement_texname), - &(material.displacement_texopt), token); - continue; - } - - // reflection map - if ((0 == strncmp(token, "refl", 4)) && IS_SPACE(token[4])) { - token += 5; - ParseTextureNameAndOption(&(material.reflection_texname), - &(material.reflection_texopt), token); - continue; - } - - // PBR: roughness texture - if ((0 == strncmp(token, "map_Pr", 6)) && IS_SPACE(token[6])) { - token += 7; - ParseTextureNameAndOption(&(material.roughness_texname), - &(material.roughness_texopt), token); - continue; - } - - // PBR: metallic texture - if ((0 == strncmp(token, "map_Pm", 6)) && IS_SPACE(token[6])) { - token += 7; - ParseTextureNameAndOption(&(material.metallic_texname), - &(material.metallic_texopt), token); - continue; - } - - // PBR: sheen texture - if ((0 == strncmp(token, "map_Ps", 6)) && IS_SPACE(token[6])) { - token += 7; - ParseTextureNameAndOption(&(material.sheen_texname), - &(material.sheen_texopt), token); - continue; - } - - // PBR: emissive texture - if ((0 == strncmp(token, "map_Ke", 6)) && IS_SPACE(token[6])) { - token += 7; - ParseTextureNameAndOption(&(material.emissive_texname), - &(material.emissive_texopt), token); - continue; - } - - // PBR: normal map texture - if ((0 == strncmp(token, "norm", 4)) && IS_SPACE(token[4])) { - token += 5; - ParseTextureNameAndOption(&(material.normal_texname), - &(material.normal_texopt), token); - continue; - } - - // unknown parameter - const char *_space = strchr(token, ' '); - if (!_space) { - _space = strchr(token, '\t'); - } - if (_space) { - std::ptrdiff_t len = _space - token; - std::string key(token, static_cast(len)); - std::string value = _space + 1; - material.unknown_parameter.insert( - std::pair(key, value)); - } - } - // flush last material. - material_map->insert(std::pair( - material.name, static_cast(materials->size()))); - materials->push_back(material); - - if (warning) { - (*warning) = warn_ss.str(); - } -} - -bool MaterialFileReader::operator()(const std::string &matId, - std::vector *materials, - std::map *matMap, - std::string *warn, std::string *err) { - if (!m_mtlBaseDir.empty()) { -#ifdef _WIN32 - char sep = ';'; -#else - char sep = ':'; -#endif - - // https://stackoverflow.com/questions/5167625/splitting-a-c-stdstring-using-tokens-e-g - std::vector paths; - std::istringstream f(m_mtlBaseDir); - - std::string s; - while (getline(f, s, sep)) { - paths.push_back(s); - } - - for (size_t i = 0; i < paths.size(); i++) { - std::string filepath = JoinPath(paths[i], matId); - - std::ifstream matIStream(filepath.c_str()); - if (matIStream) { - LoadMtl(matMap, materials, &matIStream, warn, err); - - return true; - } - } - - std::stringstream ss; - ss << "Material file [ " << matId - << " ] not found in a path : " << m_mtlBaseDir << "\n"; - if (warn) { - (*warn) += ss.str(); - } - return false; - - } else { - std::string filepath = matId; - std::ifstream matIStream(filepath.c_str()); - if (matIStream) { - LoadMtl(matMap, materials, &matIStream, warn, err); - - return true; - } - - std::stringstream ss; - ss << "Material file [ " << filepath - << " ] not found in a path : " << m_mtlBaseDir << "\n"; - if (warn) { - (*warn) += ss.str(); - } - - return false; - } -} - -bool MaterialStreamReader::operator()(const std::string &matId, - std::vector *materials, - std::map *matMap, - std::string *warn, std::string *err) { - (void)err; - (void)matId; - if (!m_inStream) { - std::stringstream ss; - ss << "Material stream in error state. \n"; - if (warn) { - (*warn) += ss.str(); - } - return false; - } - - LoadMtl(matMap, materials, &m_inStream, warn, err); - - return true; -} - -bool LoadObj(attrib_t *attrib, std::vector *shapes, - std::vector *materials, std::string *warn, - std::string *err, const char *filename, const char *mtl_basedir, - bool triangulate, bool default_vcols_fallback) { - attrib->vertices.clear(); - attrib->normals.clear(); - attrib->texcoords.clear(); - attrib->colors.clear(); - shapes->clear(); - - std::stringstream errss; - - std::ifstream ifs(filename); - if (!ifs) { - errss << "Cannot open file [" << filename << "]\n"; - if (err) { - (*err) = errss.str(); - } - return false; - } - - std::string baseDir = mtl_basedir ? mtl_basedir : ""; - if (!baseDir.empty()) { -#ifndef _WIN32 - const char dirsep = '/'; -#else - const char dirsep = '\\'; -#endif - if (baseDir[baseDir.length() - 1] != dirsep) baseDir += dirsep; - } - MaterialFileReader matFileReader(baseDir); - - return LoadObj(attrib, shapes, materials, warn, err, &ifs, &matFileReader, - triangulate, default_vcols_fallback); -} - -bool LoadObj(attrib_t *attrib, std::vector *shapes, - std::vector *materials, std::string *warn, - std::string *err, std::istream *inStream, - MaterialReader *readMatFn /*= NULL*/, bool triangulate, - bool default_vcols_fallback) { - std::stringstream errss; - - std::vector v; - std::vector vn; - std::vector vt; - std::vector vc; - std::vector vw; - std::vector tags; - PrimGroup prim_group; - std::string name; - - // material - std::set material_filenames; - std::map material_map; - int material = -1; - - // smoothing group id - unsigned int current_smoothing_id = - 0; // Initial value. 0 means no smoothing. - - int greatest_v_idx = -1; - int greatest_vn_idx = -1; - int greatest_vt_idx = -1; - - shape_t shape; - - bool found_all_colors = true; - - size_t line_num = 0; - std::string linebuf; - while (inStream->peek() != -1) { - safeGetline(*inStream, linebuf); - - line_num++; - - // Trim newline '\r\n' or '\n' - if (linebuf.size() > 0) { - if (linebuf[linebuf.size() - 1] == '\n') - linebuf.erase(linebuf.size() - 1); - } - if (linebuf.size() > 0) { - if (linebuf[linebuf.size() - 1] == '\r') - linebuf.erase(linebuf.size() - 1); - } - - // Skip if empty line. - if (linebuf.empty()) { - continue; - } - - // Skip leading space. - const char *token = linebuf.c_str(); - token += strspn(token, " \t"); - - assert(token); - if (token[0] == '\0') continue; // empty line - - if (token[0] == '#') continue; // comment line - - // vertex - if (token[0] == 'v' && IS_SPACE((token[1]))) { - token += 2; - real_t x, y, z; - real_t r, g, b; - - found_all_colors &= parseVertexWithColor(&x, &y, &z, &r, &g, &b, &token); - - v.push_back(x); - v.push_back(y); - v.push_back(z); - - if (found_all_colors || default_vcols_fallback) { - vc.push_back(r); - vc.push_back(g); - vc.push_back(b); - } - - continue; - } - - // normal - if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) { - token += 3; - real_t x, y, z; - parseReal3(&x, &y, &z, &token); - vn.push_back(x); - vn.push_back(y); - vn.push_back(z); - continue; - } - - // texcoord - if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { - token += 3; - real_t x, y; - parseReal2(&x, &y, &token); - vt.push_back(x); - vt.push_back(y); - continue; - } - - // skin weight. tinyobj extension - if (token[0] == 'v' && token[1] == 'w' && IS_SPACE((token[2]))) { - token += 3; - - // vw ... - // example: - // vw 0 0 0.25 1 0.25 2 0.5 - - // TODO(syoyo): Add syntax check - int vid = 0; - vid = parseInt(&token); - - skin_weight_t sw; - - sw.vertex_id = vid; - - while (!IS_NEW_LINE(token[0])) { - real_t j, w; - // joint_id should not be negative, weight may be negative - // TODO(syoyo): # of elements check - parseReal2(&j, &w, &token, -1.0); - - if (j < static_cast(0)) { - if (err) { - std::stringstream ss; - ss << "Failed parse `vw' line. joint_id is negative. " - "line " - << line_num << ".)\n"; - (*err) += ss.str(); - } - return false; - } - - joint_and_weight_t jw; - - jw.joint_id = int(j); - jw.weight = w; - - sw.weightValues.push_back(jw); - - size_t n = strspn(token, " \t\r"); - token += n; - } - - vw.push_back(sw); - } - - // line - if (token[0] == 'l' && IS_SPACE((token[1]))) { - token += 2; - - __line_t line; - - while (!IS_NEW_LINE(token[0])) { - vertex_index_t vi; - if (!parseTriple(&token, static_cast(v.size() / 3), - static_cast(vn.size() / 3), - static_cast(vt.size() / 2), &vi)) { - if (err) { - std::stringstream ss; - ss << "Failed parse `l' line(e.g. zero value for vertex index. " - "line " - << line_num << ".)\n"; - (*err) += ss.str(); - } - return false; - } - - line.vertex_indices.push_back(vi); - - size_t n = strspn(token, " \t\r"); - token += n; - } - - prim_group.lineGroup.push_back(line); - - continue; - } - - // points - if (token[0] == 'p' && IS_SPACE((token[1]))) { - token += 2; - - __points_t pts; - - while (!IS_NEW_LINE(token[0])) { - vertex_index_t vi; - if (!parseTriple(&token, static_cast(v.size() / 3), - static_cast(vn.size() / 3), - static_cast(vt.size() / 2), &vi)) { - if (err) { - std::stringstream ss; - ss << "Failed parse `p' line(e.g. zero value for vertex index. " - "line " - << line_num << ".)\n"; - (*err) += ss.str(); - } - return false; - } - - pts.vertex_indices.push_back(vi); - - size_t n = strspn(token, " \t\r"); - token += n; - } - - prim_group.pointsGroup.push_back(pts); - - continue; - } - - // face - if (token[0] == 'f' && IS_SPACE((token[1]))) { - token += 2; - token += strspn(token, " \t"); - - face_t face; - - face.smoothing_group_id = current_smoothing_id; - face.vertex_indices.reserve(3); - - while (!IS_NEW_LINE(token[0])) { - vertex_index_t vi; - if (!parseTriple(&token, static_cast(v.size() / 3), - static_cast(vn.size() / 3), - static_cast(vt.size() / 2), &vi)) { - if (err) { - std::stringstream ss; - ss << "Failed parse `f' line(e.g. zero value for face index. line " - << line_num << ".)\n"; - (*err) += ss.str(); - } - return false; - } - - greatest_v_idx = greatest_v_idx > vi.v_idx ? greatest_v_idx : vi.v_idx; - greatest_vn_idx = - greatest_vn_idx > vi.vn_idx ? greatest_vn_idx : vi.vn_idx; - greatest_vt_idx = - greatest_vt_idx > vi.vt_idx ? greatest_vt_idx : vi.vt_idx; - - face.vertex_indices.push_back(vi); - size_t n = strspn(token, " \t\r"); - token += n; - } - - // replace with emplace_back + std::move on C++11 - prim_group.faceGroup.push_back(face); - - continue; - } - - // use mtl - if ((0 == strncmp(token, "usemtl", 6))) { - token += 6; - std::string namebuf = parseString(&token); - - int newMaterialId = -1; - std::map::const_iterator it = - material_map.find(namebuf); - if (it != material_map.end()) { - newMaterialId = it->second; - } else { - // { error!! material not found } - if (warn) { - (*warn) += "material [ '" + namebuf + "' ] not found in .mtl\n"; - } - } - - if (newMaterialId != material) { - // Create per-face material. Thus we don't add `shape` to `shapes` at - // this time. - // just clear `faceGroup` after `exportGroupsToShape()` call. - exportGroupsToShape(&shape, prim_group, tags, material, name, - triangulate, v, warn); - prim_group.faceGroup.clear(); - material = newMaterialId; - } - - continue; - } - - // load mtl - if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) { - if (readMatFn) { - token += 7; - - std::vector filenames; - SplitString(std::string(token), ' ', '\\', filenames); - - if (filenames.empty()) { - if (warn) { - std::stringstream ss; - ss << "Looks like empty filename for mtllib. Use default " - "material (line " - << line_num << ".)\n"; - - (*warn) += ss.str(); - } - } else { - bool found = false; - for (size_t s = 0; s < filenames.size(); s++) { - if (material_filenames.count(filenames[s]) > 0) { - found = true; - continue; - } - - std::string warn_mtl; - std::string err_mtl; - bool ok = (*readMatFn)(filenames[s].c_str(), materials, - &material_map, &warn_mtl, &err_mtl); - if (warn && (!warn_mtl.empty())) { - (*warn) += warn_mtl; - } - - if (err && (!err_mtl.empty())) { - (*err) += err_mtl; - } - - if (ok) { - found = true; - material_filenames.insert(filenames[s]); - break; - } - } - - if (!found) { - if (warn) { - (*warn) += - "Failed to load material file(s). Use default " - "material.\n"; - } - } - } - } - - continue; - } - - // group name - if (token[0] == 'g' && IS_SPACE((token[1]))) { - // flush previous face group. - bool ret = exportGroupsToShape(&shape, prim_group, tags, material, name, - triangulate, v, warn); - (void)ret; // return value not used. - - if (shape.mesh.indices.size() > 0) { - shapes->push_back(shape); - } - - shape = shape_t(); - - // material = -1; - prim_group.clear(); - - std::vector names; - - while (!IS_NEW_LINE(token[0])) { - std::string str = parseString(&token); - names.push_back(str); - token += strspn(token, " \t\r"); // skip tag - } - - // names[0] must be 'g' - - if (names.size() < 2) { - // 'g' with empty names - if (warn) { - std::stringstream ss; - ss << "Empty group name. line: " << line_num << "\n"; - (*warn) += ss.str(); - name = ""; - } - } else { - std::stringstream ss; - ss << names[1]; - - // tinyobjloader does not support multiple groups for a primitive. - // Currently we concatinate multiple group names with a space to get - // single group name. - - for (size_t i = 2; i < names.size(); i++) { - ss << " " << names[i]; - } - - name = ss.str(); - } - - continue; - } - - // object name - if (token[0] == 'o' && IS_SPACE((token[1]))) { - // flush previous face group. - bool ret = exportGroupsToShape(&shape, prim_group, tags, material, name, - triangulate, v, warn); - (void)ret; // return value not used. - - if (shape.mesh.indices.size() > 0 || shape.lines.indices.size() > 0 || - shape.points.indices.size() > 0) { - shapes->push_back(shape); - } - - // material = -1; - prim_group.clear(); - shape = shape_t(); - - // @todo { multiple object name? } - token += 2; - std::stringstream ss; - ss << token; - name = ss.str(); - - continue; - } - - if (token[0] == 't' && IS_SPACE(token[1])) { - const int max_tag_nums = 8192; // FIXME(syoyo): Parameterize. - tag_t tag; - - token += 2; - - tag.name = parseString(&token); - - tag_sizes ts = parseTagTriple(&token); - - if (ts.num_ints < 0) { - ts.num_ints = 0; - } - if (ts.num_ints > max_tag_nums) { - ts.num_ints = max_tag_nums; - } - - if (ts.num_reals < 0) { - ts.num_reals = 0; - } - if (ts.num_reals > max_tag_nums) { - ts.num_reals = max_tag_nums; - } - - if (ts.num_strings < 0) { - ts.num_strings = 0; - } - if (ts.num_strings > max_tag_nums) { - ts.num_strings = max_tag_nums; - } - - tag.intValues.resize(static_cast(ts.num_ints)); - - for (size_t i = 0; i < static_cast(ts.num_ints); ++i) { - tag.intValues[i] = parseInt(&token); - } - - tag.floatValues.resize(static_cast(ts.num_reals)); - for (size_t i = 0; i < static_cast(ts.num_reals); ++i) { - tag.floatValues[i] = parseReal(&token); - } - - tag.stringValues.resize(static_cast(ts.num_strings)); - for (size_t i = 0; i < static_cast(ts.num_strings); ++i) { - tag.stringValues[i] = parseString(&token); - } - - tags.push_back(tag); - - continue; - } - - if (token[0] == 's' && IS_SPACE(token[1])) { - // smoothing group id - token += 2; - - // skip space. - token += strspn(token, " \t"); // skip space - - if (token[0] == '\0') { - continue; - } - - if (token[0] == '\r' || token[1] == '\n') { - continue; - } - - if (strlen(token) >= 3 && token[0] == 'o' && token[1] == 'f' && - token[2] == 'f') { - current_smoothing_id = 0; - } else { - // assume number - int smGroupId = parseInt(&token); - if (smGroupId < 0) { - // parse error. force set to 0. - // FIXME(syoyo): Report warning. - current_smoothing_id = 0; - } else { - current_smoothing_id = static_cast(smGroupId); - } - } - - continue; - } // smoothing group id - - // Ignore unknown command. - } - - // not all vertices have colors, no default colors desired? -> clear colors - if (!found_all_colors && !default_vcols_fallback) { - vc.clear(); - } - - if (greatest_v_idx >= static_cast(v.size() / 3)) { - if (warn) { - std::stringstream ss; - ss << "Vertex indices out of bounds (line " << line_num << ".)\n\n"; - (*warn) += ss.str(); - } - } - if (greatest_vn_idx >= static_cast(vn.size() / 3)) { - if (warn) { - std::stringstream ss; - ss << "Vertex normal indices out of bounds (line " << line_num << ".)\n\n"; - (*warn) += ss.str(); - } - } - if (greatest_vt_idx >= static_cast(vt.size() / 2)) { - if (warn) { - std::stringstream ss; - ss << "Vertex texcoord indices out of bounds (line " << line_num << ".)\n\n"; - (*warn) += ss.str(); - } - } - - bool ret = exportGroupsToShape(&shape, prim_group, tags, material, name, - triangulate, v, warn); - // exportGroupsToShape return false when `usemtl` is called in the last - // line. - // we also add `shape` to `shapes` when `shape.mesh` has already some - // faces(indices) - if (ret || shape.mesh.indices - .size()) { // FIXME(syoyo): Support other prims(e.g. lines) - shapes->push_back(shape); - } - prim_group.clear(); // for safety - - if (err) { - (*err) += errss.str(); - } - - attrib->vertices.swap(v); - attrib->vertex_weights.swap(v); - attrib->normals.swap(vn); - attrib->texcoords.swap(vt); - attrib->texcoord_ws.swap(vt); - attrib->colors.swap(vc); - attrib->skin_weights.swap(vw); - - return true; -} - -bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, - void *user_data /*= NULL*/, - MaterialReader *readMatFn /*= NULL*/, - std::string *warn, /* = NULL*/ - std::string *err /*= NULL*/) { - std::stringstream errss; - - // material - std::set material_filenames; - std::map material_map; - int material_id = -1; // -1 = invalid - - std::vector indices; - std::vector materials; - std::vector names; - names.reserve(2); - std::vector names_out; - - std::string linebuf; - while (inStream.peek() != -1) { - safeGetline(inStream, linebuf); - - // Trim newline '\r\n' or '\n' - if (linebuf.size() > 0) { - if (linebuf[linebuf.size() - 1] == '\n') - linebuf.erase(linebuf.size() - 1); - } - if (linebuf.size() > 0) { - if (linebuf[linebuf.size() - 1] == '\r') - linebuf.erase(linebuf.size() - 1); - } - - // Skip if empty line. - if (linebuf.empty()) { - continue; - } - - // Skip leading space. - const char *token = linebuf.c_str(); - token += strspn(token, " \t"); - - assert(token); - if (token[0] == '\0') continue; // empty line - - if (token[0] == '#') continue; // comment line - - // vertex - if (token[0] == 'v' && IS_SPACE((token[1]))) { - token += 2; - // TODO(syoyo): Support parsing vertex color extension. - real_t x, y, z, w; // w is optional. default = 1.0 - parseV(&x, &y, &z, &w, &token); - if (callback.vertex_cb) { - callback.vertex_cb(user_data, x, y, z, w); - } - continue; - } - - // normal - if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) { - token += 3; - real_t x, y, z; - parseReal3(&x, &y, &z, &token); - if (callback.normal_cb) { - callback.normal_cb(user_data, x, y, z); - } - continue; - } - - // texcoord - if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { - token += 3; - real_t x, y, z; // y and z are optional. default = 0.0 - parseReal3(&x, &y, &z, &token); - if (callback.texcoord_cb) { - callback.texcoord_cb(user_data, x, y, z); - } - continue; - } - - // face - if (token[0] == 'f' && IS_SPACE((token[1]))) { - token += 2; - token += strspn(token, " \t"); - - indices.clear(); - while (!IS_NEW_LINE(token[0])) { - vertex_index_t vi = parseRawTriple(&token); - - index_t idx; - idx.vertex_index = vi.v_idx; - idx.normal_index = vi.vn_idx; - idx.texcoord_index = vi.vt_idx; - - indices.push_back(idx); - size_t n = strspn(token, " \t\r"); - token += n; - } - - if (callback.index_cb && indices.size() > 0) { - callback.index_cb(user_data, &indices.at(0), - static_cast(indices.size())); - } - - continue; - } - - // use mtl - if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) { - token += 7; - std::stringstream ss; - ss << token; - std::string namebuf = ss.str(); - - int newMaterialId = -1; - std::map::const_iterator it = - material_map.find(namebuf); - if (it != material_map.end()) { - newMaterialId = it->second; - } else { - // { warn!! material not found } - if (warn && (!callback.usemtl_cb)) { - (*warn) += "material [ " + namebuf + " ] not found in .mtl\n"; - } - } - - if (newMaterialId != material_id) { - material_id = newMaterialId; - } - - if (callback.usemtl_cb) { - callback.usemtl_cb(user_data, namebuf.c_str(), material_id); - } - - continue; - } - - // load mtl - if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) { - if (readMatFn) { - token += 7; - - std::vector filenames; - SplitString(std::string(token), ' ', '\\', filenames); - - if (filenames.empty()) { - if (warn) { - (*warn) += - "Looks like empty filename for mtllib. Use default " - "material. \n"; - } - } else { - bool found = false; - for (size_t s = 0; s < filenames.size(); s++) { - if (material_filenames.count(filenames[s]) > 0) { - found = true; - continue; - } - - std::string warn_mtl; - std::string err_mtl; - bool ok = (*readMatFn)(filenames[s].c_str(), &materials, - &material_map, &warn_mtl, &err_mtl); - - if (warn && (!warn_mtl.empty())) { - (*warn) += warn_mtl; // This should be warn message. - } - - if (err && (!err_mtl.empty())) { - (*err) += err_mtl; - } - - if (ok) { - found = true; - material_filenames.insert(filenames[s]); - break; - } - } - - if (!found) { - if (warn) { - (*warn) += - "Failed to load material file(s). Use default " - "material.\n"; - } - } else { - if (callback.mtllib_cb) { - callback.mtllib_cb(user_data, &materials.at(0), - static_cast(materials.size())); - } - } - } - } - - continue; - } - - // group name - if (token[0] == 'g' && IS_SPACE((token[1]))) { - names.clear(); - - while (!IS_NEW_LINE(token[0])) { - std::string str = parseString(&token); - names.push_back(str); - token += strspn(token, " \t\r"); // skip tag - } - - assert(names.size() > 0); - - if (callback.group_cb) { - if (names.size() > 1) { - // create const char* array. - names_out.resize(names.size() - 1); - for (size_t j = 0; j < names_out.size(); j++) { - names_out[j] = names[j + 1].c_str(); - } - callback.group_cb(user_data, &names_out.at(0), - static_cast(names_out.size())); - - } else { - callback.group_cb(user_data, NULL, 0); - } - } - - continue; - } - - // object name - if (token[0] == 'o' && IS_SPACE((token[1]))) { - // @todo { multiple object name? } - token += 2; - - std::stringstream ss; - ss << token; - std::string object_name = ss.str(); - - if (callback.object_cb) { - callback.object_cb(user_data, object_name.c_str()); - } - - continue; - } - -#if 0 // @todo - if (token[0] == 't' && IS_SPACE(token[1])) { - tag_t tag; - - token += 2; - std::stringstream ss; - ss << token; - tag.name = ss.str(); - - token += tag.name.size() + 1; - - tag_sizes ts = parseTagTriple(&token); - - tag.intValues.resize(static_cast(ts.num_ints)); - - for (size_t i = 0; i < static_cast(ts.num_ints); ++i) { - tag.intValues[i] = atoi(token); - token += strcspn(token, "/ \t\r") + 1; - } - - tag.floatValues.resize(static_cast(ts.num_reals)); - for (size_t i = 0; i < static_cast(ts.num_reals); ++i) { - tag.floatValues[i] = parseReal(&token); - token += strcspn(token, "/ \t\r") + 1; - } - - tag.stringValues.resize(static_cast(ts.num_strings)); - for (size_t i = 0; i < static_cast(ts.num_strings); ++i) { - std::stringstream ss; - ss << token; - tag.stringValues[i] = ss.str(); - token += tag.stringValues[i].size() + 1; - } - - tags.push_back(tag); - } -#endif - - // Ignore unknown command. - } - - if (err) { - (*err) += errss.str(); - } - - return true; -} - -bool ObjReader::ParseFromFile(const std::string &filename, - const ObjReaderConfig &config) { - std::string mtl_search_path; - - if (config.mtl_search_path.empty()) { - // - // split at last '/'(for unixish system) or '\\'(for windows) to get - // the base directory of .obj file - // - size_t pos = filename.find_last_of("/\\"); - if (pos != std::string::npos) { - mtl_search_path = filename.substr(0, pos); - } - } else { - mtl_search_path = config.mtl_search_path; - } - - valid_ = LoadObj(&attrib_, &shapes_, &materials_, &warning_, &error_, - filename.c_str(), mtl_search_path.c_str(), - config.triangulate, config.vertex_color); - - return valid_; -} - -bool ObjReader::ParseFromString(const std::string &obj_text, - const std::string &mtl_text, - const ObjReaderConfig &config) { - std::stringbuf obj_buf(obj_text); - std::stringbuf mtl_buf(mtl_text); - - std::istream obj_ifs(&obj_buf); - std::istream mtl_ifs(&mtl_buf); - - MaterialStreamReader mtl_ss(mtl_ifs); - - valid_ = LoadObj(&attrib_, &shapes_, &materials_, &warning_, &error_, - &obj_ifs, &mtl_ss, config.triangulate, config.vertex_color); - - return valid_; -} - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif -} // namespace tinyobj - -#endif \ No newline at end of file diff --git a/Tools/BlendFactor.h b/Tools/BlendFactor.h new file mode 100644 index 00000000..58956ab5 --- /dev/null +++ b/Tools/BlendFactor.h @@ -0,0 +1,20 @@ +#ifndef SNAKE3_BLEND_FACTOR_H +#define SNAKE3_BLEND_FACTOR_H + +#include + +namespace Tools { + enum class BlendFactor { + None, + Zero = GL_ZERO, + One = GL_ONE, + DstColor = GL_DST_COLOR, + SrcColor = GL_SRC_COLOR, + SrcAlpha = GL_SRC_ALPHA, + DstAlpha = GL_DST_ALPHA, + OneMinusSrcAlpha = GL_ONE_MINUS_SRC_ALPHA, + OneMinusDstAlpha = GL_ONE_MINUS_DST_ALPHA, + BlendColor = GL_CONSTANT_COLOR + }; +} +#endif \ No newline at end of file diff --git a/Tools/Blending.h b/Tools/Blending.h new file mode 100644 index 00000000..30519c97 --- /dev/null +++ b/Tools/Blending.h @@ -0,0 +1,46 @@ +#ifndef SNAKE3_BLENDING_H +#define SNAKE3_BLENDING_H + +namespace Tools { + enum class Blending { + /** + * Opaque material + */ + Opaque, + + /** + * Mix based on alpha channel + * + * color.rgb * color.a + background.rgb * (1 - color.a) + */ + Translucent, + + /** + * Mix colors with addition + * + * color.rgb + background.rgb + */ + Additive, + + /** + * Mix colors with alpha addition + * + * color.rgb + background.rgba + */ + AlphaAdditive, + + /** + * Mix colors with multiplication + * + * color.rgb * background.rgb + */ + Modulate, + + /** + * Used for text rendering + */ + Text + }; +} // Tools + +#endif //SNAKE3_BLENDING_H diff --git a/Tools/Capabilities.h b/Tools/Capabilities.h new file mode 100644 index 00000000..013b482d --- /dev/null +++ b/Tools/Capabilities.h @@ -0,0 +1,17 @@ +#ifndef SNAKE3_CAPABILITIES_H +#define SNAKE3_CAPABILITIES_H + +#include + +namespace Tools { + enum class Capabilities { + DepthTest = GL_DEPTH_TEST, + Blending = GL_BLEND, + ProgramPointSize = GL_PROGRAM_POINT_SIZE, + ScissorTest = GL_SCISSOR_TEST, + StencilTest = GL_STENCIL_TEST, + CullFace = GL_CULL_FACE + }; +} + +#endif \ No newline at end of file diff --git a/Tools/Clear.h b/Tools/Clear.h new file mode 100644 index 00000000..721e6a98 --- /dev/null +++ b/Tools/Clear.h @@ -0,0 +1,16 @@ +#ifndef SNAKE3_CLEAR_H +#define SNAKE3_CLEAR_H + +#include + +namespace Tools { + enum class Clear { + Color = GL_COLOR_BUFFER_BIT, + Depth = GL_DEPTH_BUFFER_BIT, + Stencil = GL_STENCIL_BUFFER_BIT, + ColorDepth = Color | Depth, + ColorDepthStencil = ColorDepth | Stencil + }; +} + +#endif //SNAKE3_CLEAR_H \ No newline at end of file diff --git a/Tools/ContextState.cpp b/Tools/ContextState.cpp new file mode 100644 index 00000000..6ff3246b --- /dev/null +++ b/Tools/ContextState.cpp @@ -0,0 +1,93 @@ +#include "ContextState.h" + +namespace Tools { + ContextState::ContextState() { + init(); + } + + void ContextState::setBlendingMode(const Blending blending) noexcept { + switch (blending) { + case Blending::Opaque: + this->disable(Capabilities::Blending); + break; + case Blending::Additive: + this->enable(Capabilities::Blending); + this->setBlendFunc(BlendFactor::One, BlendFactor::One); + break; + case Blending::Modulate: + this->enable(Capabilities::Blending); + this->setBlendFunc(BlendFactor::DstColor, BlendFactor::Zero); + break; + case Blending::Translucent: + this->enable(Capabilities::Blending); + this->setBlendFunc(BlendFactor::SrcAlpha, BlendFactor::OneMinusSrcAlpha); + break; + case Blending::AlphaAdditive: + this->enable(Capabilities::Blending); + this->setBlendFunc(BlendFactor::SrcAlpha, BlendFactor::One); + break; + case Blending::Text: + this->enable(Capabilities::Blending); + this->setBlendFunc(BlendFactor::SrcAlpha, BlendFactor::OneMinusSrcAlpha); + break; + } + } + + void ContextState::setDepthTest(const bool depthTest) { + if (depthTest) { + this->enable(Capabilities::DepthTest); + this->setDepthFunc(DepthFunc::Less); + return; + } + this->disable(Capabilities::DepthTest); + } + + void ContextState::setDepthWrite(const bool depthWrite) { + this->setDepthMask(depthWrite ? DepthMask::True : DepthMask::False); + } + + void ContextState::disable(Capabilities func) noexcept { + if (capability_map[func]) { + glDisable(static_cast(func)); + capability_map[func] = false; + } + } + + void ContextState::enable(Capabilities func) noexcept { + if (!capability_map[func]) { + glEnable(static_cast(func)); + capability_map[func] = true; + } + } + + void ContextState::setBlendFunc(const BlendFactor src, const BlendFactor dst) noexcept { + if (src_factor != src || dst_factor != dst) { + src_factor = src; + dst_factor = dst; + glBlendFunc(static_cast(src_factor), static_cast(dst_factor)); + } + } + + void ContextState::setDepthFunc(DepthFunc func) noexcept { + if (depth_func != func) { + glDepthFunc(static_cast(func)); + depth_func = func; + } + } + + void ContextState::setDepthMask(DepthMask mask) noexcept { + if (depth_mask != mask) { + glDepthMask(static_cast(mask)); + depth_mask = mask; + } + } + + void ContextState::init() noexcept { + capability_map.emplace(Capabilities::DepthTest, false); + capability_map.emplace(Capabilities::Blending, false); + capability_map.emplace(Capabilities::ProgramPointSize, false); + capability_map.emplace(Capabilities::ScissorTest, false); + capability_map.emplace(Capabilities::StencilTest, false); + capability_map.emplace(Capabilities::CullFace, false); + } +} // Tools diff --git a/Tools/ContextState.h b/Tools/ContextState.h new file mode 100644 index 00000000..6157524d --- /dev/null +++ b/Tools/ContextState.h @@ -0,0 +1,37 @@ +#ifndef SNAKE3_CONTEXTSTATE_H +#define SNAKE3_CONTEXTSTATE_H + +#include + +#include "Blending.h" +#include "Capabilities.h" +#include "BlendFactor.h" +#include "DepthFunc.h" + +using namespace std; + +namespace Tools { + class ContextState { + public: + ContextState(); + ~ContextState() = default; + + void disable(Capabilities func) noexcept; + void enable(Capabilities func) noexcept; + void setBlendingMode(Blending blending) noexcept; + void setDepthTest(bool depthTest); + void setDepthWrite(bool depthWrite); + void setBlendFunc(BlendFactor src, BlendFactor dst) noexcept; + void setDepthFunc(DepthFunc func) noexcept; + void setDepthMask(DepthMask mask) noexcept; + protected: + void init() noexcept; + unordered_map capability_map; + BlendFactor src_factor {BlendFactor::None}; + BlendFactor dst_factor {BlendFactor::Zero}; + DepthFunc depth_func {DepthFunc::Less}; + DepthMask depth_mask {DepthMask::True}; + }; +} // Tools + +#endif //SNAKE3_CONTEXTSTATE_H \ No newline at end of file diff --git a/Tools/DepthFunc.h b/Tools/DepthFunc.h new file mode 100644 index 00000000..62a86fac --- /dev/null +++ b/Tools/DepthFunc.h @@ -0,0 +1,22 @@ +#ifndef SNAKE3_DEPTHFUNC_H +#define SNAKE3_DEPTHFUNC_H + +namespace Tools { + enum class DepthFunc { + Never = GL_NEVER, + Less = GL_LESS, + Equal = GL_EQUAL, + Lequal = GL_LEQUAL, + Greater = GL_GREATER, + Notequal = GL_NOTEQUAL, + Gequal = GL_GEQUAL, + Always = GL_ALWAYS + }; + + enum class DepthMask { + True = GL_TRUE, + False = GL_FALSE + }; +} // Tools + +#endif //SNAKE3_DEPTHFUNC_H \ No newline at end of file diff --git a/Tools/DrawElement.h b/Tools/DrawElement.h new file mode 100644 index 00000000..e2ba0ae1 --- /dev/null +++ b/Tools/DrawElement.h @@ -0,0 +1,18 @@ +#ifndef SNAKE3_DRAWELEMENT_H +#define SNAKE3_DRAWELEMENT_H + +#include + +namespace Tools { + enum class DrawElement { + Points = GL_POINTS, + Lines = GL_LINES, + LineLoop = GL_LINE_LOOP, + LineStrip = GL_LINE_STRIP, + Triangles = GL_TRIANGLES, + TriangleStrip = GL_TRIANGLE_STRIP, + TriangleFan = GL_TRIANGLE_FAN + }; +} + +#endif //SNAKE3_DRAWELEMENT_H \ No newline at end of file diff --git a/Tools/Environment.cpp b/Tools/Environment.cpp new file mode 100644 index 00000000..341bafa4 --- /dev/null +++ b/Tools/Environment.cpp @@ -0,0 +1,13 @@ +#include "Environment.h" + +namespace Tools { + Environment::Environment() { + ambientLight = AmbientLight(); + ambientLight.color = glm::vec3(1.0f, 1.0f, 1.0f); + ambientLight.intensity = 1; + } + + AmbientLight Environment::getAmbientLight() const { + return ambientLight; + } +} // Tools \ No newline at end of file diff --git a/Tools/Environment.h b/Tools/Environment.h new file mode 100644 index 00000000..2c17916b --- /dev/null +++ b/Tools/Environment.h @@ -0,0 +1,33 @@ +#ifndef SNAKE3_ENVIRONMENT_H +#define SNAKE3_ENVIRONMENT_H + +#include "Clear.h" +#include + +namespace Tools { + struct AmbientLight { + glm::vec3 color; + float intensity; + }; + + struct Background { + Clear mode; + }; + + class Environment { + // background + // -- mode + // -- color + + AmbientLight ambientLight{}; + + public: + Environment(); + + ~Environment() = default; + + [[nodiscard]] AmbientLight getAmbientLight() const; + }; +} // Tools + +#endif //SNAKE3_ENVIRONMENT_H diff --git a/Tools/Timer.cpp b/Tools/Timer.cpp new file mode 100644 index 00000000..0eddad08 --- /dev/null +++ b/Tools/Timer.cpp @@ -0,0 +1,74 @@ +#include "Timer.h" + +#include + +namespace Tools { + Timer::Timer(const bool autoStart) : autoStart(false), running(false), waitTime(0.0f), now(0), + lastTime(0), startTime(0), waitTimeLeave(false) { + if (autoStart) { + start(); + } + } + + void Timer::start() { + running = true; + now = lastTime = startTime = glfwGetTime(); + } + + void Timer::stop() { + running = false; + waitTimeLeave = false; + } + + void Timer::reset() { + now = lastTime = startTime = glfwGetTime(); + } + + bool Timer::isRunning() { + const bool isRunning = running && waitTimeLeave; + if (false == isRunning) { + getElapsedTime(); + } + + return isRunning; + } + + double Timer::getElapsedTime() { + const double elapsedTime = now - startTime; + if (!waitTimeLeave) { + if (elapsedTime < waitTime) { + return 0.0; + } + waitTimeLeave = true; + lastTime = now; + return 0.0; + } + + return elapsedTime; + } + + void Timer::update() { + if (!running) { + return; + } + + now = glfwGetTime(); + } + + double Timer::getDeltaTime() { + if (!running) return 0.0; + + const double delta = now - lastTime; + lastTime = now; + + return delta; + } + + double Timer::getWaitTime() const { + return waitTime; + } + + double Timer::getNow() const { + return now; + } +} // Tools diff --git a/Tools/Timer.h b/Tools/Timer.h new file mode 100644 index 00000000..65ca5df6 --- /dev/null +++ b/Tools/Timer.h @@ -0,0 +1,38 @@ +#ifndef SNAKE3_TIMER_H +#define SNAKE3_TIMER_H + +namespace Tools { + class Timer { + public: + explicit Timer(bool autoStart = false); + + void start(); + + void stop(); + + void reset(); + + [[nodiscard]] bool isRunning(); + + double getElapsedTime(); + + [[nodiscard]] double getDeltaTime(); + + void update(); + + [[nodiscard]] double getWaitTime() const; + + [[nodiscard]] double getNow() const; + + private: + bool autoStart; + bool running; + float waitTime; + double now; + double lastTime; + double startTime; + bool waitTimeLeave; + }; +} // Tools + +#endif //SNAKE3_TIMER_H diff --git a/Tools/WorldEnvironment.cpp b/Tools/WorldEnvironment.cpp new file mode 100644 index 00000000..6ee64325 --- /dev/null +++ b/Tools/WorldEnvironment.cpp @@ -0,0 +1,11 @@ +#include "WorldEnvironment.h" + +namespace Tools { + std::shared_ptr WorldEnvironment::getEnvironment() const { + return environment; + } + + void WorldEnvironment::setEnvironment(const std::shared_ptr &environment) { + this->environment = environment; + } +} // Tools \ No newline at end of file diff --git a/Tools/WorldEnvironment.h b/Tools/WorldEnvironment.h new file mode 100644 index 00000000..5d6ff9c8 --- /dev/null +++ b/Tools/WorldEnvironment.h @@ -0,0 +1,20 @@ +#ifndef SNAKE3_WORLDENVIRONMENT_H +#define SNAKE3_WORLDENVIRONMENT_H + +#include +#include "Environment.h" + +namespace Tools { + class WorldEnvironment { + std::shared_ptr environment; + + public: + WorldEnvironment(); + + [[nodiscard]] std::shared_ptr getEnvironment() const; + + void setEnvironment(const std::shared_ptr &environment); + }; +} // Tools + +#endif //SNAKE3_WORLDENVIRONMENT_H diff --git a/main.cpp b/main.cpp index a02389aa..a12f2f27 100644 --- a/main.cpp +++ b/main.cpp @@ -1,40 +1,41 @@ #include "stdafx.h" #include "App.h" #include +#include using namespace std; int W_WIDTH = 1920; int W_HEIGHT = 1080; -double lastX = (double)W_WIDTH / 2.0f; -double lastY = (double)W_HEIGHT / 2.0f; +double lastX = static_cast(W_WIDTH) / 2.0f; +double lastY = static_cast(W_HEIGHT) / 2.0f; bool firstMouse = true; void mouse_callback(GLFWwindow* window, double x, double y); +void mouse_button_callback(GLFWwindow* window, int button, int action, int mods); void scroll_callback(GLFWwindow* window, double offsetX, double offsetY); void framebuffer_size_callback(GLFWwindow* window, int width, int height); void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods); void processInput(GLFWwindow *window); -auto camera = new Camera(glm::vec3(0.0f, 0.0f, 3.0f)); -App *app = new App(camera, W_WIDTH, W_HEIGHT); +auto camera = make_shared(glm::vec3(0.0f, 0.0f, 3.0f)); +auto app = make_shared(camera, W_WIDTH, W_HEIGHT); int main(int argc, char *argv[]) { - GLFWwindow *window; if (!glfwInit()) { return 1; } glfwSetErrorCallback([](int error, const char *description) { std::cerr << "Error: " << description << '\n'; }); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, true); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE); //glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); - window = glfwCreateWindow(W_WIDTH, W_HEIGHT, "Snake 3", nullptr, nullptr); + GLFWwindow *window = glfwCreateWindow(W_WIDTH, W_HEIGHT, "Snake 3", nullptr, nullptr); if (!window) { glfwTerminate(); return 1; @@ -42,6 +43,7 @@ int main(int argc, char *argv[]) { glfwMakeContextCurrent(window); glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); glfwSetCursorPosCallback(window, mouse_callback); + glfwSetMouseButtonCallback(window, mouse_button_callback); glfwSetScrollCallback(window, scroll_callback); glfwSetKeyCallback(window, key_callback); glewInit(); @@ -52,10 +54,10 @@ int main(int argc, char *argv[]) { glViewport(0, 0, W_WIDTH, W_HEIGHT); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); - // Enables the Stencil Buffer glEnable(GL_STENCIL_TEST); - // Sets rules for outcomes of stecil tests glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + // ZAMKNE A SKRYJE KURZOR + //glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); GLenum err; while ((err = glGetError()) != GL_NO_ERROR) { @@ -81,36 +83,33 @@ int main(int argc, char *argv[]) { glfwDestroyWindow(window); glfwTerminate(); - delete app; alutExit(); return 0; } -// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly -// --------------------------------------------------------------------------------------------------------- +// process all inputs: query GLFW whether relevant keys are pressed/released this frame and react accordingly +// ---------------------------------------------------------------------------------------------------------- void processInput(GLFWwindow *window) { - if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) { - glfwSetWindowShouldClose(window, true); - } - - if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) { - camera->processKeyboard(Camera_Movement::FORWARD, 0.1); - } - if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) { - camera->processKeyboard(Camera_Movement::BACKWARD, 0.1); - } - if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) { - camera->processKeyboard(Camera_Movement::LEFT, 0.1); - } - if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) { - camera->processKeyboard(Camera_Movement::RIGHT, 0.1); - } + app->cameraProcessKeyboard(window); + + // if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) { + // camera->processKeyboard(Camera_Movement::FORWARD, 0.1); + // } + // if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) { + // camera->processKeyboard(Camera_Movement::BACKWARD, 0.1); + // } + // if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) { + // camera->processKeyboard(Camera_Movement::LEFT, 0.1); + // } + // if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) { + // camera->processKeyboard(Camera_Movement::RIGHT, 0.1); + // } } -// glfw: whenever the window size changed (by OS or user resize) this callback function executes -// --------------------------------------------------------------------------------------------- -void framebuffer_size_callback(GLFWwindow* window, int width, int height) +// glfw: whenever the window size changed (by OS or user resize), this callback function executes +// ---------------------------------------------------------------------------------------------- +void framebuffer_size_callback(GLFWwindow* window, const int width, const int height) { // make sure the viewport matches the new window dimensions; note that width and // height will be significantly larger than specified on retina displays. @@ -122,34 +121,45 @@ void framebuffer_size_callback(GLFWwindow* window, int width, int height) // ------------------------------------------------------- void mouse_callback(GLFWwindow* window, double x, double y) { - if (firstMouse) - { - lastX = x; - lastY = y; - firstMouse = false; - } - - double offsetX = x - lastX; - double offsetY = lastY - y; // reversed since y-coordinates go from bottom to top - - lastX = x; - lastY = y; + // if (firstMouse) + // { + // lastX = x; + // lastY = y; + // firstMouse = false; + // } + // + // const double offsetX = x - lastX; + // const double offsetY = lastY - y; // reversed since y-coordinates go from bottom to top + // + // lastX = x; + // lastY = y; + // + // camera->processMouseMovement(offsetX, offsetY); + app->mousePositionCallback(window, x, y); +} -// camera->processMouseMovement(offsetX, offsetY); +void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) { + // app->mouseButtonCallback(window, button, action, mods); } // glfw: whenever the mouse scroll wheel scrolls, this callback is called // ---------------------------------------------------------------------- void scroll_callback(GLFWwindow* window, double offsetX, double offsetY) { - //camera.processMouseScroll(offsetY); + // camera->processMouseScroll(offsetY); } // glfw: keyboard callback is called // ---------------------------------------------------------------------- -void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +void key_callback(GLFWwindow* window, const int key, const int scancode, const int action, const int mods) { if (action == GLFW_PRESS) { - app->processInput(key); + app->setKeyState(key, true); + } else if (action == GLFW_RELEASE) { + app->setKeyState(key, false); + } + + if (action == GLFW_PRESS || action == GLFW_REPEAT) { + app->processInput(window, key, scancode, action, mods); } } \ No newline at end of file diff --git a/manifest.go b/manifest.go new file mode 100644 index 00000000..4287674d --- /dev/null +++ b/manifest.go @@ -0,0 +1,60 @@ +package main + +import ( + "encoding/json" + "fmt" + "io/fs" + "os" + "path/filepath" +) + +type TextureEntry struct { + Name string `json:"name"` + Path string `json:"path"` + Category string `json:"category"` +} + +func main() { + paths := map[string]string{ + "Albedo": "Assets/Textures/Albedo", + "Others": "Assets/Textures/Others", + } + + var manifest []TextureEntry + + for category, folder := range paths { + err := filepath.WalkDir(folder, func(path string, d fs.DirEntry, err error) error { + if err != nil { + fmt.Fprintf(os.Stderr, "Chyba při čtení %s: %v\n", path, err) + return nil + } + if !d.IsDir() { + relPath, _ := filepath.Rel("Assets/Textures", path) + entry := TextureEntry{ + Name: d.Name(), + Path: filepath.ToSlash(relPath), + Category: category, + } + manifest = append(manifest, entry) + } + return nil + }) + if err != nil { + fmt.Fprintf(os.Stderr, "Chyba při průchodu složkou %s: %v\n", folder, err) + } + } + + file, err := os.Create("texture_manifest.json") + if err != nil { + panic(err) + } + defer file.Close() + + enc := json.NewEncoder(file) + enc.SetIndent("", " ") + if err := enc.Encode(manifest); err != nil { + panic(err) + } + + fmt.Printf("Manifest vytvořen: %d položek\n", len(manifest)) +} diff --git a/texture_manifest.json b/texture_manifest.json new file mode 100644 index 00000000..84d69fc2 --- /dev/null +++ b/texture_manifest.json @@ -0,0 +1,177 @@ +[ + { + "name": "Skeleton_Body.png", + "path": "Albedo/Skeleton_Body.png", + "category": "Albedo" + }, + { + "name": "brickwall.jpg", + "path": "Albedo/box/brickwall.jpg", + "category": "Albedo" + }, + { + "name": "bricks2.jpg", + "path": "Albedo/bricks2.jpg", + "category": "Albedo" + }, + { + "name": "bricks2_disp.jpg", + "path": "Albedo/bricks2_disp.jpg", + "category": "Albedo" + }, + { + "name": "brickwork-cavity.jpg", + "path": "Albedo/brickwork-cavity.jpg", + "category": "Albedo" + }, + { + "name": "brickwork-texture.jpg", + "path": "Albedo/brickwork-texture.jpg", + "category": "Albedo" + }, + { + "name": "Coin_Gold_albedo.png", + "path": "Albedo/coin/Coin_Gold_albedo.png", + "category": "Albedo" + }, + { + "name": "fast_noise.bmp", + "path": "Albedo/fast_noise.bmp", + "category": "Albedo" + }, + { + "name": "fire.png", + "path": "Albedo/fire/fire.png", + "category": "Albedo" + }, + { + "name": "smoke.png", + "path": "Albedo/fire/smoke.png", + "category": "Albedo" + }, + { + "name": "gamefield.bmp", + "path": "Albedo/gamefield/gamefield.bmp", + "category": "Albedo" + }, + { + "name": "gamefield_specular.jpg", + "path": "Albedo/gamefield/gamefield_specular.jpg", + "category": "Albedo" + }, + { + "name": "tile.png", + "path": "Albedo/gamefield/tile.png", + "category": "Albedo" + }, + { + "name": "head.bmp", + "path": "Albedo/head.bmp", + "category": "Albedo" + }, + { + "name": "raindrops.png", + "path": "Albedo/rain/raindrops.png", + "category": "Albedo" + }, + { + "name": "raindrops_nor.png", + "path": "Albedo/rain/raindrops_nor.png", + "category": "Albedo" + }, + { + "name": "rain.jpg", + "path": "Albedo/rain.jpg", + "category": "Albedo" + }, + { + "name": "rusted_albedo.png", + "path": "Albedo/rusted/rusted_albedo.png", + "category": "Albedo" + }, + { + "name": "snake.bmp", + "path": "Albedo/snake.bmp", + "category": "Albedo" + }, + { + "name": "tile.bmp", + "path": "Albedo/tile.bmp", + "category": "Albedo" + }, + { + "name": "torch.png", + "path": "Albedo/torch.png", + "category": "Albedo" + }, + { + "name": "Coin_Gold_metalness.png", + "path": "Others/Coin_Gold_metalness.png", + "category": "Others" + }, + { + "name": "Coin_Gold_nm.png", + "path": "Others/Coin_Gold_nm.png", + "category": "Others" + }, + { + "name": "Coin_Gold_rough.png", + "path": "Others/Coin_Gold_rough.png", + "category": "Others" + }, + { + "name": "Skeleton_Body_ORM.png", + "path": "Others/Skeleton_Body_ORM.png", + "category": "Others" + }, + { + "name": "ao.png", + "path": "Others/ao.png", + "category": "Others" + }, + { + "name": "bricks2_normal.jpg", + "path": "Others/bricks2_normal.jpg", + "category": "Others" + }, + { + "name": "brickwall_normal.jpg", + "path": "Others/brickwall_normal.jpg", + "category": "Others" + }, + { + "name": "brickwork-bump-map.jpg", + "path": "Others/brickwork-bump-map.jpg", + "category": "Others" + }, + { + "name": "brickwork_normal-map.jpg", + "path": "Others/brickwork_normal-map.jpg", + "category": "Others" + }, + { + "name": "gamefield_normal.jpg", + "path": "Others/gamefield_normal.jpg", + "category": "Others" + }, + { + "name": "rusted_metallic.png", + "path": "Others/rusted_metallic.png", + "category": "Others" + }, + { + "name": "rusted_normal.png", + "path": "Others/rusted_normal.png", + "category": "Others" + }, + { + "name": "rusted_roughness.png", + "path": "Others/rusted_roughness.png", + "category": "Others" + }, + { + "name": "torch_normal.png", + "path": "Others/torch_normal.png", + "category": "Others" + } +]