diff --git a/demo/inc/states/game/enemy/enemy.hpp b/demo/inc/states/game/enemy/enemy.hpp index be61eab..49b4b1e 100644 --- a/demo/inc/states/game/enemy/enemy.hpp +++ b/demo/inc/states/game/enemy/enemy.hpp @@ -12,12 +12,18 @@ #include #include +#include #include "states/game/renderer/renderer_dynamic_pair.hpp" #include "states/game/terrain/heightmap.hpp" #include +#include +#include "./enemy_info.hpp" +using Tyra::AnimationSequenceCallback; +using Tyra::Audio; using Tyra::DynamicMesh; using Tyra::DynPipOptions; +using Tyra::Engine; using Tyra::Renderer; using Tyra::TextureRepository; using Tyra::Vec4; @@ -26,7 +32,7 @@ namespace Demo { class Enemy { public: - Enemy(TextureRepository* repo); + Enemy(Engine* engine, const EnemyInfo& info); ~Enemy(); DynamicMesh* mesh; @@ -36,7 +42,19 @@ class Enemy { void update(const Heightmap& heightmap, const Vec4& playerPosition); private: + std::vector walkSequence; + std::vector fightSequence; + void allocateOptions(); + void walk(const Heightmap& heightmap, const Vec4& positionDiff); + void animationCallback(const AnimationSequenceCallback& callback); + void fight(); + + bool isWalking = true; + bool isFighting = false; + + Audio* audio; + EnemyInfo info; }; } // namespace Demo diff --git a/demo/inc/states/game/enemy/enemy_info.hpp b/demo/inc/states/game/enemy/enemy_info.hpp new file mode 100644 index 0000000..bb97c5c --- /dev/null +++ b/demo/inc/states/game/enemy/enemy_info.hpp @@ -0,0 +1,31 @@ +/* +# _____ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licensed under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include + +using Tyra::DynamicMesh; +using Tyra::Texture; +using Tyra::Vec4; + +namespace Demo { + +struct EnemyInfo { + u8 adpcmChannel; + audsrv_adpcm_t* adpcmSample; + DynamicMesh* motherMesh; + Texture* bodyTexture; + Texture* clothTexture; + Vec4 spawnPoint; +}; + +} // namespace Demo diff --git a/demo/inc/states/game/enemy/enemy_manager.hpp b/demo/inc/states/game/enemy/enemy_manager.hpp new file mode 100644 index 0000000..b78d0ef --- /dev/null +++ b/demo/inc/states/game/enemy/enemy_manager.hpp @@ -0,0 +1,43 @@ +/* +# _____ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licensed under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "states/game/terrain/heightmap.hpp" +#include "./enemy.hpp" +#include +#include + +using Tyra::DynamicMesh; +using Tyra::Engine; +using Tyra::Texture; +using Tyra::TextureRepository; +using Tyra::Vec4; + +namespace Demo { + +class EnemyManager { + public: + EnemyManager(Engine* engine, const Heightmap& heightmap); + ~EnemyManager(); + + void update(const Heightmap& heightmap, const Vec4& playerPosition); + + std::vector getPairs() const; + + private: + TextureRepository* textureRepo; + std::vector enemies; + DynamicMesh* motherMesh; + Texture* bodyTexture; + Texture* clothTexture; +}; + +} // namespace Demo diff --git a/demo/inc/states/game/game_state.hpp b/demo/inc/states/game/game_state.hpp index 4d818e1..af73d3f 100644 --- a/demo/inc/states/game/game_state.hpp +++ b/demo/inc/states/game/game_state.hpp @@ -17,7 +17,7 @@ #include "./terrain/terrain.hpp" #include "./skybox/skybox.hpp" #include "./debug_object.hpp" -#include "./enemy/enemy.hpp" +#include "./enemy/enemy_manager.hpp" namespace Demo { @@ -45,7 +45,7 @@ class GameState : public State { GameRenderer renderer; Player* player; - Enemy* enemy; + EnemyManager* enemyManager; Terrain* terrain; Skybox* skybox; DebugObject* dbgObj; diff --git a/demo/src/states/game/enemy/enemy.cpp b/demo/src/states/game/enemy/enemy.cpp index a0c42bb..8ba7308 100644 --- a/demo/src/states/game/enemy/enemy.cpp +++ b/demo/src/states/game/enemy/enemy.cpp @@ -9,38 +9,48 @@ */ #include "states/game/enemy/enemy.hpp" -#include "loaders/3d/obj_loader/obj_loader.hpp" -#include +#include +#include "debug/debug.hpp" -using Tyra::FileUtils; using Tyra::Math; -using Tyra::ObjLoader; -using Tyra::ObjLoaderOptions; namespace Demo { -Enemy::Enemy(TextureRepository* repo) { - ObjLoader loader; +Enemy::Enemy(Engine* engine, const EnemyInfo& t_info) { + info = t_info; - ObjLoaderOptions objOptions; - objOptions.animation.count = 6; - objOptions.flipUVs = true; - objOptions.scale = 250.0F; + mesh = new DynamicMesh(*info.motherMesh); - auto* data = loader.load(FileUtils::fromCwd("game/models/zombie/zombie.obj"), - objOptions); - data->normalsEnabled = false; - mesh = new DynamicMesh(*data); + auto* bodyMaterial = mesh->getMaterialByName("BodyMaterial"); + auto* clothMaterial = mesh->getMaterialByName("ClothMaterial"); - delete data; + TYRA_ASSERT(bodyMaterial != nullptr && clothMaterial != nullptr, + "Body and cloth materials not found!"); - mesh->playAnimation(0, mesh->frames.size() - 1); - mesh->setAnimSpeed(0.1F); + info.bodyTexture->addLink(bodyMaterial->id); + info.clothTexture->addLink(clothMaterial->id); - repo->addByMesh(mesh, FileUtils::fromCwd("game/models/zombie/"), "png"); + float r = Math::randomf(64.0F, 128.0F); + float g = Math::randomf(64.0F, 128.0F); + float b = Math::randomf(64.0F, 128.0F); + bodyMaterial->ambient.set(r, g, b); + clothMaterial->ambient.set(r, g, b); + + walkSequence = {0, 1, 2}; + fightSequence = {3, 4, 5}; + + mesh->setPosition(info.spawnPoint); + + mesh->animation.setSequence(walkSequence); + mesh->animation.loop = true; + mesh->animation.setCallback( + std::bind(&Enemy::animationCallback, this, std::placeholders::_1)); mesh->translation.translateY(-5.0F); + audio = &engine->audio; + audio->adpcm.setVolume(50, info.adpcmChannel); + allocateOptions(); pair = new RendererDynamicPair{mesh, options}; @@ -58,14 +68,10 @@ void Enemy::update(const Heightmap& heightmap, const Vec4& playerPosition) { auto diff = *enemyPosition - playerPosition; auto ang = Math::atan2(diff.x, diff.z); - if (diff.length() > 30.0F) { - diff.normalize(); - const float speed = 1.5F; - auto nextPos = *enemyPosition - diff * speed; - nextPos.y = heightmap.getHeightOffset(nextPos) - 60.0F; - - mesh->translation.identity(); - mesh->translation.translate(nextPos); + if (diff.length() > 110.0F) { + walk(heightmap, diff); + } else { + fight(); } const float naturalRotation = 3.1F; @@ -73,7 +79,43 @@ void Enemy::update(const Heightmap& heightmap, const Vec4& playerPosition) { ang += naturalRotation; mesh->rotation.rotateByAngle(ang, Vec4(0.0F, 1.0F, 0.0F, 0.0F)); - mesh->animate(); + mesh->update(); +} + +void Enemy::walk(const Heightmap& heightmap, const Vec4& positionDiff) { + if (isFighting) { + mesh->animation.setSequence(walkSequence); + } + + isFighting = false; + isWalking = true; + + auto* enemyPosition = mesh->getPosition(); + auto normalized = positionDiff; + normalized.normalize(); + const float speed = 2.5F; + auto nextPos = *enemyPosition - normalized * speed; + nextPos.y = heightmap.getHeightOffset(nextPos) - 60.0F; + + mesh->translation.identity(); + mesh->translation.translate(nextPos); +} + +void Enemy::fight() { + if (isWalking) { + mesh->animation.setSequence(fightSequence); + } + + isFighting = true; + isWalking = false; +} + +void Enemy::animationCallback(const AnimationSequenceCallback& callback) { + if (callback == AnimationSequenceCallback::AnimationSequenceCallback_Loop) { + if (isFighting) { + audio->adpcm.tryPlay(info.adpcmSample, info.adpcmChannel); + } + } } void Enemy::allocateOptions() { options = new DynPipOptions(); } diff --git a/demo/src/states/game/enemy/enemy_manager.cpp b/demo/src/states/game/enemy/enemy_manager.cpp new file mode 100644 index 0000000..1b4a8da --- /dev/null +++ b/demo/src/states/game/enemy/enemy_manager.cpp @@ -0,0 +1,92 @@ +/* +# _____ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licensed under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "states/game/enemy/enemy_manager.hpp" +#include "loaders/3d/obj_loader/obj_loader.hpp" +#include +#include +#include "debug/debug.hpp" + +using Tyra::FileUtils; +using Tyra::Math; +using Tyra::ObjLoader; +using Tyra::ObjLoaderOptions; + +namespace Demo { + +EnemyManager::EnemyManager(Engine* engine, const Heightmap& heightmap) { + textureRepo = &engine->renderer.core.texture.repository; + + ObjLoader loader; + ObjLoaderOptions objOptions; + objOptions.animation.count = 6; + objOptions.flipUVs = true; + objOptions.scale = 250.0F; + + auto* data = loader.load(FileUtils::fromCwd("game/models/zombie/zombie.obj"), + objOptions); + data->normalsEnabled = false; + motherMesh = new DynamicMesh(*data); + + delete data; + + bodyTexture = textureRepo->add( + FileUtils::fromCwd("game/models/zombie/BodyMaterial.png")); + + clothTexture = textureRepo->add( + FileUtils::fromCwd("game/models/zombie/ClothMaterial.png")); + + auto* sample = engine->audio.adpcm.load( + FileUtils::fromCwd("game/models/zombie/punch.adpcm")); + + const int enemyCount = 10; + for (int i = 0; i < enemyCount; i++) { + EnemyInfo info; + info.adpcmChannel = 9 + i; + info.adpcmSample = sample; + info.motherMesh = motherMesh; + info.clothTexture = clothTexture; + info.bodyTexture = bodyTexture; + info.spawnPoint = + Vec4(Math::randomf(heightmap.leftUp.x, heightmap.rightDown.x), 0.0F, + Math::randomf(heightmap.leftUp.z, heightmap.rightDown.z), 1.0F); + enemies.push_back(new Enemy(engine, info)); + } +} + +EnemyManager::~EnemyManager() { + for (auto* enemy : enemies) { + delete enemy; + } + + delete motherMesh; + + textureRepo->free(bodyTexture->id); + textureRepo->free(clothTexture->id); +} + +std::vector EnemyManager::getPairs() const { + std::vector result; + + for (auto* enemy : enemies) { + result.push_back(enemy->pair); + } + + return result; +} + +void EnemyManager::update(const Heightmap& heightmap, + const Vec4& playerPosition) { + for (auto* enemy : enemies) { + enemy->update(heightmap, playerPosition); + } +} + +} // namespace Demo diff --git a/demo/src/states/game/game_state.cpp b/demo/src/states/game/game_state.cpp index 307db6e..ea1b0a9 100644 --- a/demo/src/states/game/game_state.cpp +++ b/demo/src/states/game/game_state.cpp @@ -37,8 +37,8 @@ void GameState::onStart() { auto* repository = &engine->renderer.core.texture.repository; player = new Player(engine); - enemy = new Enemy(repository); terrain = new Terrain(repository); + enemyManager = new EnemyManager(engine, terrain->heightmap); skybox = new Skybox(repository); dbgObj = new DebugObject(repository); @@ -49,7 +49,7 @@ GlobalStateType GameState::onFinish() { if (!initialized) return STATE_EXIT; delete player; - delete enemy; + delete enemyManager; delete terrain; delete skybox; delete dbgObj; @@ -59,7 +59,7 @@ GlobalStateType GameState::onFinish() { } void GameState::update() { - if (fpsChecker++ > 100) { + if (fpsChecker++ > 50) { TYRA_LOG("FPS: ", engine->info.getFps()); fpsChecker = 0; } @@ -71,13 +71,13 @@ void GameState::update() { dbgObj->setPosition(*cameraInfo.looksAt); player->update(terrain->heightmap); - enemy->update(terrain->heightmap, player->getPosition()); + enemyManager->update(terrain->heightmap, player->getPosition()); renderer.clear(); { renderer.add(skybox->pair); renderer.add(player->pair); - renderer.add(enemy->pair); + renderer.add(enemyManager->getPairs()); renderer.add(terrain->pair); renderer.add(dbgObj->pair); } diff --git a/demo/src/states/game/player/player.cpp b/demo/src/states/game/player/player.cpp index fec31ea..0c50bad 100644 --- a/demo/src/states/game/player/player.cpp +++ b/demo/src/states/game/player/player.cpp @@ -18,7 +18,7 @@ Player::Player(Engine* engine) pad = &engine->pad; position = Vec4(3.0F, 50.0F, 0.0F); camera.lookAt = Vec4(0.0F, 0.0F, -30.0F); - speed = 3.5F; + speed = 5.0F; } Player::~Player() { delete pair; } diff --git a/demo/src/states/game/player/weapon.cpp b/demo/src/states/game/player/weapon.cpp index ae91c7b..e835419 100644 --- a/demo/src/states/game/player/weapon.cpp +++ b/demo/src/states/game/player/weapon.cpp @@ -115,59 +115,6 @@ void Weapon::update() { } } -// float tX = 0.0F; -// float tY = -3.0F; -// float tZ = -10.0F; - -// float rY = 0.1F; -// float rZ = -4.3F; - -// void Weapon::debugPosition(Pad* pad) { -// const auto& leftJoy = pad->getLeftJoyPad(); -// const auto& rightJoy = pad->getRightJoyPad(); -// const float offset = 0.05F; - -// if (leftJoy.v <= 100) { -// rY -= offset; -// } else if (leftJoy.v >= 200) { -// rY += offset; -// } -// if (leftJoy.h <= 100) { -// rZ -= offset; -// } else if (leftJoy.h >= 200) { -// rZ += offset; -// } - -// if (rightJoy.h <= 100) { -// tY -= offset; -// } else if (rightJoy.h >= 200) { -// tY += offset; -// } - -// if (rightJoy.v <= 100) { -// tZ -= offset; -// } else if (rightJoy.v >= 200) { -// tZ += offset; -// } - -// if (pad->getPressed().Circle) { -// tX -= offset; -// } else if (pad->getPressed().Cross) { -// tX += offset; -// } - -// mesh->rotation.identity(); -// mesh->rotation.rotateY(rY); -// mesh->rotation.rotateZ(rZ); - -// mesh->translation.identity(); -// mesh->translation.translateX(tX); -// mesh->translation.translateY(tY); -// mesh->translation.translateZ(tZ); - -// TYRA_LOG("tX: ", tX, " tY: ", tY, " tZ: ", tZ, " rY: ", rY, " rZ: ", rZ); -// } - void Weapon::allocateOptions() { options = new StaPipOptions(); options->textureMappingType = Tyra::TyraNearest; diff --git a/engine/inc/math/math.hpp b/engine/inc/math/math.hpp index 909a98e..2aa502a 100644 --- a/engine/inc/math/math.hpp +++ b/engine/inc/math/math.hpp @@ -27,11 +27,13 @@ class Math { static float cos(float x); static float asin(float x); static float atan2(float y, float x); - static float mod(float x, float y); - static float acos(float x); - static float sin(float x); - static float tan(float x); - static float invSqrt(float x); + static float mod(const float& x, const float& y); + static float acos(const float& x); + static float sin(const float& x); + static float tan(const float& x); + static float invSqrt(const float& x); + static float randomf(const float& min, const float& max); + static int randomi(const int& min, const int& max); static bool equalf(const float& a, const float& b, const float& epsilon = 0.00001F); diff --git a/engine/inc/renderer/3d/mesh/dynamic/animation_sequence_callback.hpp b/engine/inc/renderer/3d/mesh/dynamic/animation_sequence_callback.hpp new file mode 100644 index 0000000..25d0cb7 --- /dev/null +++ b/engine/inc/renderer/3d/mesh/dynamic/animation_sequence_callback.hpp @@ -0,0 +1,25 @@ + +/* +# _____ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licensed under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +namespace Tyra { + +enum AnimationSequenceCallback { + /** On frame change */ + AnimationSequenceCallback_NextFrame, + /** When animation in loop = false will end */ + AnimationSequenceCallback_End, + /** When animation in loop = true will start from beginning */ + AnimationSequenceCallback_Loop +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/mesh/dynamic/dynamic_mesh.hpp b/engine/inc/renderer/3d/mesh/dynamic/dynamic_mesh.hpp index 3054084..16cf9bf 100644 --- a/engine/inc/renderer/3d/mesh/dynamic/dynamic_mesh.hpp +++ b/engine/inc/renderer/3d/mesh/dynamic/dynamic_mesh.hpp @@ -11,8 +11,7 @@ #pragma once #include "../mesh_frame.hpp" -#include "../mesh_material_frame.hpp" -#include "./dynamic_mesh_anim_state.hpp" +#include "./dynamic_mesh_animation.hpp" #include "../mesh.hpp" namespace Tyra { @@ -25,36 +24,15 @@ class DynamicMesh : public Mesh { std::vector frames; - DynamicMeshAnimState animState; + DynamicMeshAnimation animation; /** @returns bounding box object of current frame. */ const BBox& getCurrentBoundingBox() const { - return *frames[animState.currentFrame]->bbox; + return *frames[animation.getState().currentFrame]->bbox; } - /** Loop in one frame */ - void playAnimation(const u32& t_frame); - - /** Play animation in loop from startFrame to endFrame */ - void playAnimation(const u32& t_startFrame, const u32& t_endFrame); - - /** Play animation from startFrame to endFrame and after loop in stayFrame - */ - void playAnimation(const u32& t_startFrame, const u32& t_endFrame, - const u32& t_stayFrame); - - void setAnimSpeed(const float& t_value) { animState.speed = t_value; } - - void animate(); - - /** - * Check if this class loaded mesh data first. - * Meshes which use loadFrom() method have false there. - */ - const u8& isStayAnimationSet() const { return animState.isStayFrameSet; } - - private: - void initAnimation(); + /** Update animation */ + inline void update() { animation.update(); } }; } // namespace Tyra diff --git a/engine/inc/renderer/3d/mesh/dynamic/dynamic_mesh_anim_state.hpp b/engine/inc/renderer/3d/mesh/dynamic/dynamic_mesh_anim_state.hpp index 05f67a9..6b528f9 100644 --- a/engine/inc/renderer/3d/mesh/dynamic/dynamic_mesh_anim_state.hpp +++ b/engine/inc/renderer/3d/mesh/dynamic/dynamic_mesh_anim_state.hpp @@ -15,16 +15,10 @@ namespace Tyra { -typedef struct { - u32 startFrame; - u32 endFrame; - u32 stayFrame; - u8 isStayFrameSet; - float speed; +struct DynamicMeshAnimState { float interpolation; - u32 animType; u32 currentFrame; u32 nextFrame; -} DynamicMeshAnimState; +}; } // namespace Tyra diff --git a/engine/inc/renderer/3d/mesh/dynamic/dynamic_mesh_animation.hpp b/engine/inc/renderer/3d/mesh/dynamic/dynamic_mesh_animation.hpp new file mode 100644 index 0000000..3e4016e --- /dev/null +++ b/engine/inc/renderer/3d/mesh/dynamic/dynamic_mesh_animation.hpp @@ -0,0 +1,72 @@ +/* +# _____ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licensed under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include "./dynamic_mesh_anim_state.hpp" +#include "./animation_sequence_callback.hpp" +#include "../mesh_frame.hpp" +#include +#include + +namespace Tyra { + +class DynamicMeshAnimation { + public: + DynamicMeshAnimation(); + ~DynamicMeshAnimation(); + + bool loop; + float speed; + + /** + * Update animation. + * Should be called every frame if you want animation running. + */ + void update(); + + /** Set animation sequence (indices of frames) */ + void setSequence(const std::vector& sequence); + + /** Start animation from beginning */ + void restart(); + + /** + * @brief Set animation callback. + * It will push animation info on every update() call. + */ + void setCallback( + const std::function& callback); + + const DynamicMeshAnimState& getState() const; + + void resetAll(const std::vector& frames); + + private: + u32 framesCount; + u32 currentFrameSequenceIndex; + DynamicMeshAnimState state; + + std::function callback; + std::vector sequence; + + void updateLoopState(AnimationSequenceCallback* callbackInfo); + + /** + * @return true Frame changed + * @return false No changes + */ + bool updateNoLoopState(AnimationSequenceCallback* callbackInfo); + + u32 getNextSequenceIndexFrom(const u32& index) const; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/mesh/mesh.hpp b/engine/inc/renderer/3d/mesh/mesh.hpp index 02394de..7d6b0f1 100644 --- a/engine/inc/renderer/3d/mesh/mesh.hpp +++ b/engine/inc/renderer/3d/mesh/mesh.hpp @@ -29,6 +29,9 @@ class Mesh { M4x4 translation, rotation, scale; + /** nullptr if not found */ + MeshMaterial* getMaterialByName(const std::string& name); + std::vector materials; M4x4 getModelMatrix() const; diff --git a/engine/inc/renderer/3d/pipeline/dynamic/dynamic_pipeline.hpp b/engine/inc/renderer/3d/pipeline/dynamic/dynamic_pipeline.hpp index 074bd56..ee51ce7 100644 --- a/engine/inc/renderer/3d/pipeline/dynamic/dynamic_pipeline.hpp +++ b/engine/inc/renderer/3d/pipeline/dynamic/dynamic_pipeline.hpp @@ -66,7 +66,7 @@ class DynamicPipeline : public Renderer3DPipeline { DynPipColorBag* getColorBag(MeshMaterial* material) const; - DynPipTextureBag* getTextureBag(MeshMaterial* material, + DynPipTextureBag* getTextureBag(Texture* texture, MeshMaterialFrame* materialFrameFrom, MeshMaterialFrame* materialFrameTo, const u32& startIndex); diff --git a/engine/inc/renderer/core/texture/models/texture.hpp b/engine/inc/renderer/core/texture/models/texture.hpp index e3be0fc..e60aa27 100644 --- a/engine/inc/renderer/core/texture/models/texture.hpp +++ b/engine/inc/renderer/core/texture/models/texture.hpp @@ -55,7 +55,7 @@ class Texture { * Returns index of link. * -1 if not found. * @param t_id - * For 3D: MeshMaterial material id. + * For 3D: MeshMaterial id. * For 2D: Sprite id. */ const s32 getIndexOfLink(const u32& t_id) const; @@ -68,11 +68,8 @@ class Texture { // Other // ---- - /** Assign texture to mesh and mesh material. */ - void addLink(const u32& t_meshId, const u32& t_materialId); - - /** Assign texture to sprite. */ - void addLink(const u32& t_spriteId); + /** Assign texture to Sprite.Id or MeshMaterial.Id. */ + void addLink(const u32& t_id); u32 getTextureSize() const; diff --git a/engine/inc/tyra b/engine/inc/tyra new file mode 100644 index 0000000..9bf9f36 --- /dev/null +++ b/engine/inc/tyra @@ -0,0 +1,27 @@ +/* +# _____ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licensed under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "./engine.hpp" +#include "./debug/debug.hpp" +#include "./file/file_utils.hpp" +#include "./loaders/3d/md2_loader/md2_loader.hpp" +#include "./loaders/3d/obj_loader/obj_loader.hpp" +#include "./loaders/texture/png_loader.hpp" +#include "./packet2/packet2_tyra_utils.hpp" +#include "./physics/ray.hpp" +#include "./renderer/3d/pipeline/dynamic/dynamic_pipeline.hpp" +#include "./renderer/3d/pipeline/static/static_pipeline.hpp" +#include "./renderer/3d/pipeline/minecraft/minecraft_pipeline.hpp" +#include "./renderer/3d/mesh/dynamic/dynamic_mesh.hpp" +#include "./renderer/3d/mesh/static/static_mesh.hpp" +#include "./thread/threading.hpp" +#include "./time/timer.hpp" diff --git a/engine/src/math/math.cpp b/engine/src/math/math.cpp index e2ec2aa..7546e45 100644 --- a/engine/src/math/math.cpp +++ b/engine/src/math/math.cpp @@ -13,6 +13,7 @@ #endif #include "math/math.hpp" +#include extern volatile const u32 TYRA_MATH_ATAN_TABLE[9] alignas(sizeof(float)) = { 0x3f7ffff5, 0xbeaaa61c, 0x3e4c40a6, 0xbe0e6c63, 0x3dc577df, @@ -91,7 +92,7 @@ float Math::cos(float x) { return r; } -float Math::invSqrt(float x) { return 1.0F / sqrt(x); } +float Math::invSqrt(const float& x) { return 1.0F / sqrt(x); } float Math::atan2(float y, float x) { float r; @@ -185,6 +186,17 @@ float Math::atan2(float y, float x) { return r; } +float Math::randomf(const float& min, const float& max) { + float random = ((float)rand()) / (float)RAND_MAX; + float diff = max - min; + float r = random * diff; + return min + r; +} + +int Math::randomi(const int& min, const int& max) { + return rand() % (max - min + 1) + min; +} + float Math::asin(float x) { float r; asm volatile( @@ -248,7 +260,7 @@ bool Math::equalf(const float& a, const float& b, const float& epsilon) { return fabs(a - b) < epsilon; } -float Math::mod(float x, float y) { +float Math::mod(const float& x, const float& y) { /* * Portable fmod(x,y) implementation for systems * that don't have it. Adapted from code found here: @@ -270,14 +282,14 @@ float Math::mod(float x, float y) { return f; } -float Math::acos(float x) { +float Math::acos(const float& x) { float y = sqrt(1.0f - x * x); float t = atan2(y, x); return t; } -float Math::sin(float x) { return cos(x - HALF_PI); } +float Math::sin(const float& x) { return cos(x - HALF_PI); } -float Math::tan(float x) { return sin(x) / cos(x); } +float Math::tan(const float& x) { return sin(x) / cos(x); } } // namespace Tyra diff --git a/engine/src/renderer/3d/mesh/dynamic/dynamic_mesh.cpp b/engine/src/renderer/3d/mesh/dynamic/dynamic_mesh.cpp index 92dfe18..1817d9d 100644 --- a/engine/src/renderer/3d/mesh/dynamic/dynamic_mesh.cpp +++ b/engine/src/renderer/3d/mesh/dynamic/dynamic_mesh.cpp @@ -26,7 +26,7 @@ DynamicMesh::DynamicMesh(const MeshBuilderData& data) : Mesh(data) { frames.push_back(new MeshFrame(data, i)); } - initAnimation(); + animation.resetAll(frames); } DynamicMesh::DynamicMesh(const DynamicMesh& mesh) : Mesh(mesh) { @@ -34,7 +34,7 @@ DynamicMesh::DynamicMesh(const DynamicMesh& mesh) : Mesh(mesh) { frames.push_back(new MeshFrame(*mesh.frames[i])); } - initAnimation(); + animation.resetAll(frames); } DynamicMesh::~DynamicMesh() { @@ -43,71 +43,4 @@ DynamicMesh::~DynamicMesh() { } } -void DynamicMesh::initAnimation() { - animState.startFrame = 0; - animState.endFrame = 0; - animState.interpolation = 0.0F; - animState.animType = 0; - animState.currentFrame = 0; - animState.stayFrame = 0; - animState.isStayFrameSet = false; - animState.nextFrame = 0; - animState.speed = 0.1F; -} - -void DynamicMesh::playAnimation(const u32& t_frame) { - playAnimation(t_frame, t_frame); -} - -void DynamicMesh::playAnimation(const u32& t_startFrame, - const u32& t_endFrame) { - TYRA_ASSERT(frames.size() > 0, - "Cant play animation, because no mesh data was loaded!"); - TYRA_ASSERT(frames.size() != 1, - "Cant play animation, because this mesh have only one frame."); - TYRA_ASSERT( - t_endFrame < frames.size(), - "End frame value is too high. Valid range: (0, getFramesCount()-1)"); - animState.startFrame = t_startFrame; - animState.endFrame = t_endFrame; - if (animState.currentFrame == t_startFrame) - animState.nextFrame = t_endFrame; - else - animState.nextFrame = t_startFrame; -} - -void DynamicMesh::playAnimation(const u32& t_startFrame, const u32& t_endFrame, - const u32& t_stayFrame) { - TYRA_ASSERT(frames.size() > 0, - "Cant play animation, because no mesh data was loaded!"); - TYRA_ASSERT(frames.size() != 1, - "Cant play animation, because this mesh have only one frame."); - TYRA_ASSERT( - t_endFrame < frames.size(), - "End frame value is too high. Valid range: (0, getFramesCount()-1)"); - animState.startFrame = t_startFrame; - animState.endFrame = t_endFrame; - animState.isStayFrameSet = true; - animState.stayFrame = t_stayFrame; - animState.nextFrame = t_startFrame; -} - -void DynamicMesh::animate() { - animState.interpolation += animState.speed; - if (animState.interpolation >= 1.0F) { - animState.interpolation = 0.0F; - animState.currentFrame = animState.nextFrame; - if (++animState.nextFrame > animState.endFrame) { - if (animState.isStayFrameSet) { - animState.isStayFrameSet = false; - animState.nextFrame = animState.stayFrame; - animState.startFrame = animState.stayFrame; - animState.endFrame = animState.stayFrame; - } else { - animState.nextFrame = animState.startFrame; - } - } - } -} - } // namespace Tyra diff --git a/engine/src/renderer/3d/mesh/dynamic/dynamic_mesh_animation.cpp b/engine/src/renderer/3d/mesh/dynamic/dynamic_mesh_animation.cpp new file mode 100644 index 0000000..aa8fb85 --- /dev/null +++ b/engine/src/renderer/3d/mesh/dynamic/dynamic_mesh_animation.cpp @@ -0,0 +1,145 @@ + +/* +# _____ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licensed under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "math/m4x4.hpp" +#include "renderer/3d/mesh/dynamic/dynamic_mesh_animation.hpp" +#include "debug/debug.hpp" + +namespace Tyra { + +DynamicMeshAnimation::DynamicMeshAnimation() { + callback = nullptr; + currentFrameSequenceIndex = 0; + framesCount = 0; +} + +DynamicMeshAnimation::~DynamicMeshAnimation() {} + +void DynamicMeshAnimation::resetAll(const std::vector& frames) { + framesCount = frames.size(); + speed = 0.1F; + loop = true; + + state.interpolation = 0.0F; + state.currentFrame = 0; + state.nextFrame = 0; + + sequence.clear(); + for (std::size_t i = 0; i < frames.size(); i++) { + sequence.push_back(i); + } + + restart(); +} + +void DynamicMeshAnimation::setSequence(const std::vector& t_sequence) { + TYRA_ASSERT(framesCount != 1, + "Cant set sequence, because this mesh have only one frame."); + + TYRA_ASSERT(sequence.size() > 0, + "Cant set sequence, because sequence is empty."); + + for (std::size_t i = 0; i < t_sequence.size(); i++) { + TYRA_ASSERT(t_sequence[i] < framesCount && t_sequence[i] >= 0, + "Cant set sequence, because value: ", t_sequence[i], + " At index: ", i, " Is out of range!"); + } + + sequence = t_sequence; + + restart(); +} + +const DynamicMeshAnimState& DynamicMeshAnimation::getState() const { + return state; +} + +void DynamicMeshAnimation::update() { + AnimationSequenceCallback callbackInfo = + AnimationSequenceCallback::AnimationSequenceCallback_NextFrame; + bool sendCallback = true; + + state.interpolation += speed; + + if (state.interpolation >= 1.0F) { + state.interpolation = 0.0F; + + if (loop) { + updateLoopState(&callbackInfo); + } else { + auto anythingChanged = updateNoLoopState(&callbackInfo); + if (!anythingChanged) { + sendCallback = false; + } + } + } + + if (callback != nullptr && sendCallback) callback(callbackInfo); +} + +void DynamicMeshAnimation::restart() { + currentFrameSequenceIndex = 0; + + auto firstIndex = sequence[0]; + auto secondIndex = sequence.size() > 1 ? sequence[1] : firstIndex; + + state.currentFrame = sequence[firstIndex]; + state.nextFrame = sequence[secondIndex]; +} + +void DynamicMeshAnimation::updateLoopState( + AnimationSequenceCallback* callbackInfo) { + auto n1SequenceIndex = getNextSequenceIndexFrom(currentFrameSequenceIndex); + + state.currentFrame = state.nextFrame; + + auto isN1SequenceIndexEndOfSequence = n1SequenceIndex == sequence.size() - 1; + if (isN1SequenceIndexEndOfSequence) { + *callbackInfo = AnimationSequenceCallback::AnimationSequenceCallback_Loop; + } + + state.nextFrame = sequence[getNextSequenceIndexFrom(n1SequenceIndex)]; + currentFrameSequenceIndex = n1SequenceIndex; +} + +bool DynamicMeshAnimation::updateNoLoopState( + AnimationSequenceCallback* callbackInfo) { + if (state.currentFrame == state.nextFrame) { + return false; // Animation is finished. + } + + auto n1SequenceIndex = getNextSequenceIndexFrom(currentFrameSequenceIndex); + + state.currentFrame = state.nextFrame; + + auto isN1SequenceIndexEndOfSequence = n1SequenceIndex == sequence.size() - 1; + if (isN1SequenceIndexEndOfSequence) { + state.nextFrame = sequence[sequence.size() - 1]; + *callbackInfo = AnimationSequenceCallback::AnimationSequenceCallback_End; + currentFrameSequenceIndex = sequence.size() - 1; + } else { + state.nextFrame = sequence[getNextSequenceIndexFrom(n1SequenceIndex)]; + currentFrameSequenceIndex = n1SequenceIndex; + } + + return true; +} + +u32 DynamicMeshAnimation::getNextSequenceIndexFrom(const u32& index) const { + return index == sequence.size() - 1 ? 0 : index + 1; +} + +void DynamicMeshAnimation::setCallback( + const std::function& t_callback) { + callback = t_callback; +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/mesh/mesh.cpp b/engine/src/renderer/3d/mesh/mesh.cpp index 0e416c4..347e295 100644 --- a/engine/src/renderer/3d/mesh/mesh.cpp +++ b/engine/src/renderer/3d/mesh/mesh.cpp @@ -46,6 +46,16 @@ Mesh::Mesh(const Mesh& mesh) { M4x4 Mesh::getModelMatrix() const { return translation * rotation * scale; } +MeshMaterial* Mesh::getMaterialByName(const std::string& name) { + for (auto* material : materials) { + if (material->name == name) { + return material; + } + } + + return nullptr; +} + Mesh::~Mesh() { for (u32 i = 0; i < materials.size(); i++) { delete materials[i]; diff --git a/engine/src/renderer/3d/pipeline/dynamic/dynamic_pipeline.cpp b/engine/src/renderer/3d/pipeline/dynamic/dynamic_pipeline.cpp index 19287d8..ea1665b 100644 --- a/engine/src/renderer/3d/pipeline/dynamic/dynamic_pipeline.cpp +++ b/engine/src/renderer/3d/pipeline/dynamic/dynamic_pipeline.cpp @@ -62,7 +62,7 @@ void DynamicPipeline::render(DynamicMesh* mesh, const DynPipOptions* options) { PipelineDirLightsBag* dirLights = nullptr; if (options->frustumCulling == PipelineFrustumCulling_Simple) { - auto* frameTo = mesh->frames[mesh->animState.nextFrame]; + auto* frameTo = mesh->frames[mesh->animation.getState().nextFrame]; if (frameTo->bbox->isInFrustum( rendererCore->renderer3D.frustumPlanes.getAll(), model) == CoreBBoxFrustum::OUTSIDE_FRUSTUM) { @@ -84,8 +84,8 @@ void DynamicPipeline::render(DynamicMesh* mesh, const DynPipOptions* options) { for (u32 i = 0; i < mesh->materials.size(); i++) { auto* material = mesh->materials[i]; - auto* frameFrom = material->frames[mesh->animState.currentFrame]; - auto* frameTo = material->frames[mesh->animState.nextFrame]; + auto* frameFrom = material->frames[mesh->animation.getState().currentFrame]; + auto* frameTo = material->frames[mesh->animation.getState().nextFrame]; auto partSize = core.getMaxVertCountByParams( options && options->lighting, material->frames[0]->textureCoords); @@ -97,6 +97,13 @@ void DynamicPipeline::render(DynamicMesh* mesh, const DynPipOptions* options) { setBuffersColorBag(buffers, colorBag); u8 isPartInitialized = false; + auto* texture = + rendererCore->texture.repository.getBySpriteOrMesh(material->id); + + TYRA_ASSERT( + texture, "Texture for material: ", material->name, "Id: ", material->id, + "Was not found in texture repository! Did you forget to add texture?"); + for (u32 k = 0; k < partsCount; k++) { auto& buffer = buffers[bufferIndex]; @@ -107,7 +114,7 @@ void DynamicPipeline::render(DynamicMesh* mesh, const DynPipOptions* options) { u32 startIndex = k * partSize; addVertices(frameFrom, frameTo, &buffer, startIndex); - buffer.texture = getTextureBag(material, frameFrom, frameTo, startIndex); + buffer.texture = getTextureBag(texture, frameFrom, frameTo, startIndex); buffer.lighting = getLightingBag(frameFrom, frameTo, &model, options, dirLights, startIndex); @@ -180,7 +187,7 @@ void DynamicPipeline::setBuffersDefaultVars(DynPipBag* buffers, DynPipInfoBag* infoBag) { for (u32 i = 0; i < buffersCount; i++) { buffers[i].info = infoBag; - buffers[i].interpolation = mesh->animState.interpolation; + buffers[i].interpolation = mesh->animation.getState().interpolation; } } @@ -234,19 +241,13 @@ DynPipColorBag* DynamicPipeline::getColorBag(MeshMaterial* material) const { } DynPipTextureBag* DynamicPipeline::getTextureBag( - MeshMaterial* material, MeshMaterialFrame* materialFrameFrom, + Texture* texture, MeshMaterialFrame* materialFrameFrom, MeshMaterialFrame* materialFrameTo, const u32& startIndex) { if (!materialFrameFrom->textureCoords) return nullptr; auto* result = new DynPipTextureBag(); - result->texture = - rendererCore->texture.repository.getBySpriteOrMesh(material->id); - - TYRA_ASSERT( - result->texture, "Texture for material: ", material->name, - "Id: ", material->id, - "Was not found in texture repository! Did you forget to add texture?"); + result->texture = texture; result->coordinatesFrom = &materialFrameFrom->textureCoords[startIndex]; result->coordinatesTo = &materialFrameTo->textureCoords[startIndex];