Compare commits
13 Commits
e967844558
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| e0db570581 | |||
| 6eed5063e6 | |||
| 5b014dcb65 | |||
| cd82fb0eed | |||
| 3f0484e87c | |||
| 9c4bea5983 | |||
| 3645557520 | |||
| 19a1275a8a | |||
| 25280a9cbe | |||
| 7e06da700a | |||
| 9e5d08bfc6 | |||
| a62d781aa0 | |||
| fea7c71788 |
63
.vscode/settings.json
vendored
@@ -10,6 +10,67 @@
|
||||
"variant": "cpp",
|
||||
"tuple": "cpp",
|
||||
"iostream": "cpp",
|
||||
"string_view": "cpp"
|
||||
"string_view": "cpp",
|
||||
"atomic": "cpp",
|
||||
"memory_resource": "cpp",
|
||||
"stop_token": "cpp",
|
||||
"random": "cpp",
|
||||
"future": "cpp",
|
||||
"array": "cpp",
|
||||
"deque": "cpp",
|
||||
"list": "cpp",
|
||||
"string": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"unordered_set": "cpp",
|
||||
"vector": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"span": "cpp",
|
||||
"chrono": "cpp",
|
||||
"format": "cpp",
|
||||
"cctype": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"cstring": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"bit": "cpp",
|
||||
"bitset": "cpp",
|
||||
"charconv": "cpp",
|
||||
"compare": "cpp",
|
||||
"concepts": "cpp",
|
||||
"condition_variable": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"map": "cpp",
|
||||
"set": "cpp",
|
||||
"exception": "cpp",
|
||||
"algorithm": "cpp",
|
||||
"functional": "cpp",
|
||||
"iterator": "cpp",
|
||||
"memory": "cpp",
|
||||
"optional": "cpp",
|
||||
"ratio": "cpp",
|
||||
"system_error": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"utility": "cpp",
|
||||
"fstream": "cpp",
|
||||
"iomanip": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"limits": "cpp",
|
||||
"mutex": "cpp",
|
||||
"new": "cpp",
|
||||
"numbers": "cpp",
|
||||
"numeric": "cpp",
|
||||
"ostream": "cpp",
|
||||
"semaphore": "cpp",
|
||||
"sstream": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"thread": "cpp",
|
||||
"typeinfo": "cpp"
|
||||
}
|
||||
}
|
||||
@@ -7,9 +7,6 @@
|
||||
|
||||
#include "Ogre.h"
|
||||
#include "OgreApplicationContext.h"
|
||||
#include "Bullet/OgreBullet.h"
|
||||
#include "BulletCollision/CollisionDispatch/btGhostObject.h"
|
||||
#include "LinearMath/btTransform.h"
|
||||
#include "OgrePageManager.h"
|
||||
|
||||
#define CAM_HEIGHT 1.6f // height of camera above character's center of mass
|
||||
@@ -21,11 +18,15 @@ using Real = Ogre::Real;
|
||||
using Math = Ogre::Math;
|
||||
|
||||
class WorldData {
|
||||
#if 0
|
||||
std::unique_ptr<Ogre::Bullet::DynamicsWorld> mDynWorld;
|
||||
std::unique_ptr<Ogre::Bullet::DebugDrawer> mDbgDraw;
|
||||
#endif
|
||||
std::unique_ptr<Ogre::Root> mRoot;
|
||||
std::unique_ptr<Ogre::SceneManager> mScnMgr;
|
||||
#if 0
|
||||
std::unique_ptr<btDynamicsWorld> mbtWorld;
|
||||
#endif
|
||||
std::unique_ptr<Ogre::PageManager> mPageManager;
|
||||
Ogre::PagedWorld *mPagedWorld;
|
||||
|
||||
@@ -61,13 +62,14 @@ private:
|
||||
DummyPageProvider mDummyPageProvider;
|
||||
|
||||
WorldData(Ogre::Root *root, Ogre::SceneManager *scnMgr)
|
||||
: mDynWorld(new Ogre::Bullet::DynamicsWorld(
|
||||
: /*mDynWorld(new Ogre::Bullet::DynamicsWorld(
|
||||
Ogre::Vector3(0, -9.8, 0)))
|
||||
, mDbgDraw(new Ogre::Bullet::DebugDrawer(
|
||||
scnMgr->getRootSceneNode(), mDynWorld->getBtWorld()))
|
||||
, mRoot(root)
|
||||
, */
|
||||
mRoot(root)
|
||||
, mScnMgr(scnMgr)
|
||||
, mbtWorld(mDynWorld->getBtWorld())
|
||||
/*, mbtWorld(mDynWorld->getBtWorld()) */
|
||||
, mPageManager(nullptr)
|
||||
, mPagedWorld(nullptr)
|
||||
{
|
||||
@@ -122,6 +124,7 @@ public:
|
||||
#endif
|
||||
return ghost;
|
||||
}
|
||||
#if 0
|
||||
btRigidBody *addRigidBody(float mass, Ogre::Entity *ent,
|
||||
Ogre::Bullet::ColliderType ct, int group = 1,
|
||||
int mask = 0xFFFF)
|
||||
@@ -182,11 +185,14 @@ public:
|
||||
{
|
||||
return mDynWorld.get();
|
||||
}
|
||||
#endif
|
||||
void update(float delta)
|
||||
{
|
||||
#if 0
|
||||
WorldData::get_singleton()->getBtWorld()->stepSimulation(delta,
|
||||
10);
|
||||
mDbgDraw->update();
|
||||
#endif
|
||||
}
|
||||
void initPagedWorld(Ogre::Camera *camera)
|
||||
{
|
||||
@@ -200,7 +206,7 @@ public:
|
||||
WorldData *WorldData::singleton = nullptr;
|
||||
|
||||
class MainWorld : public Ogre::FrameListener {
|
||||
btRigidBody *mFloorBody;
|
||||
// btRigidBody *mFloorBody;
|
||||
|
||||
public:
|
||||
void setup()
|
||||
@@ -219,14 +225,18 @@ public:
|
||||
WorldData::get_singleton()->getSceneManager();
|
||||
Ogre::Entity *floor = scnMgr->createEntity("Floor", "floor");
|
||||
scnMgr->getRootSceneNode()->attachObject(floor);
|
||||
#if 0
|
||||
mFloorBody = WorldData::get_singleton()->addRigidBody(
|
||||
0, floor, Ogre::Bullet::CT_TRIMESH);
|
||||
#endif
|
||||
}
|
||||
#if 0
|
||||
btRigidBody *addCharacter(Ogre::Entity *ent, float mass)
|
||||
{
|
||||
return WorldData::get_singleton()->addKinematicRigidBody(
|
||||
mass, ent, Ogre::Bullet::CT_COMPOUND);
|
||||
}
|
||||
#endif
|
||||
bool frameStarted(const Ogre::FrameEvent &evt) override;
|
||||
};
|
||||
class CharacterController : public OgreBites::InputListener,
|
||||
@@ -265,8 +275,10 @@ class CharacterController : public OgreBites::InputListener,
|
||||
Ogre::Vector3 rootMotion;
|
||||
Ogre::Quaternion rootRotation;
|
||||
// btRigidBody *mRigidBody;
|
||||
#if 0
|
||||
btCompoundShape *mCollisionShape;
|
||||
btPairCachingGhostObject *mGhostObject;
|
||||
#endif
|
||||
|
||||
public:
|
||||
CharacterController(Ogre::SceneNode *camNode, Ogre::Camera *cam,
|
||||
@@ -315,6 +327,7 @@ private:
|
||||
recoverResult *recover_result,
|
||||
const std::set<btCollisionObject *> &exclude);
|
||||
#endif
|
||||
#if 0
|
||||
inline btQuaternion convert(const Ogre::Quaternion &q)
|
||||
{
|
||||
return btQuaternion(q.x, q.y, q.z, q.w);
|
||||
@@ -344,6 +357,7 @@ private:
|
||||
q = convert(from.getRotation());
|
||||
v = convert(from.getOrigin());
|
||||
}
|
||||
#endif
|
||||
};
|
||||
CharacterController::CharacterController(Ogre::SceneNode *camNode,
|
||||
Ogre::Camera *cam,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.13.0)
|
||||
project(world2)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(BLENDER "${CMAKE_SOURCE_DIR}/../../blender-bin/bin/blender" CACHE STRING "Blender path")
|
||||
set(CREATE_DIRECTORIES
|
||||
${CMAKE_BINARY_DIR}/assets/blender/shapes/male
|
||||
@@ -27,11 +27,10 @@ file(GLOB WATER_SRC ${CMAKE_SOURCE_DIR}/water/*.cpp)
|
||||
|
||||
# The COMPONENTS part checks that OGRE was built the way we need it
|
||||
# The CONFIG flag makes sure we get OGRE instead of OGRE-next
|
||||
find_package(OGRE REQUIRED COMPONENTS Bites Bullet Paging Terrain CONFIG)
|
||||
find_package(OGRE REQUIRED COMPONENTS Bites Paging Terrain CONFIG)
|
||||
find_package(ZLIB)
|
||||
find_package(SDL2)
|
||||
find_package(assimp REQUIRED CONFIG)
|
||||
find_package(Bullet)
|
||||
find_package(OgreProcedural REQUIRED CONFIG)
|
||||
find_package(pugixml REQUIRED CONFIG)
|
||||
find_package(flecs REQUIRED CONFIG)
|
||||
@@ -66,41 +65,25 @@ set_target_properties(fix::pugixml PROPERTIES
|
||||
|
||||
|
||||
add_subdirectory(src/lua)
|
||||
add_subdirectory(src/characters)
|
||||
add_subdirectory(src/gamedata)
|
||||
add_subdirectory(src/miniaudio)
|
||||
add_subdirectory(src/sound)
|
||||
add_subdirectory(src/sceneloader)
|
||||
add_subdirectory(audio/gui)
|
||||
add_subdirectory(tests)
|
||||
add_subdirectory(lua-scripts)
|
||||
add_subdirectory(morph)
|
||||
add_subdirectory(src/world)
|
||||
add_subdirectory(src/tests)
|
||||
add_subdirectory(src/physics)
|
||||
add_subdirectory(src/editor)
|
||||
|
||||
# add the source files as usual
|
||||
add_executable(0_Bootstrap Bootstrap.cpp)
|
||||
|
||||
# this also sets the includes and pulls third party dependencies
|
||||
target_link_libraries(0_Bootstrap OgreBites OgreBullet OgrePaging ${BULLET_DYNAMICS_LIBRARY} ${BULLET_COLLISION_LIBRARY} ${BULLET_MATH_LIBRARY} ${ASSIMP_LIBRARIES}
|
||||
-Wl,--as-needed
|
||||
)
|
||||
if(OGRE_STATIC)
|
||||
target_link_options(0_Bootstrap PRIVATE -static-libstdc++ -static-libgcc)
|
||||
endif()
|
||||
add_dependencies(0_Bootstrap stage_files import_vrm)
|
||||
|
||||
add_executable(Editor Editor.cpp ${WATER_SRC})
|
||||
target_link_libraries(Editor OgreBites OgreBullet OgrePaging OgreTerrain OgreMeshLodGenerator OgreProcedural::OgreProcedural ${BULLET_DYNAMICS_LIBRARY} ${BULLET_COLLISION_LIBRARY} ${BULLET_MATH_LIBRARY}
|
||||
-Wl,--as-needed
|
||||
)
|
||||
if(OGRE_STATIC)
|
||||
target_link_options(Editor PRIVATE -static-libstdc++ -static-libgcc)
|
||||
endif()
|
||||
add_dependencies(Editor stage_files import_buildings import_water_stuff import_vehicles import_vrm)
|
||||
add_executable(Game Game.cpp ${WATER_SRC})
|
||||
target_include_directories(Game PRIVATE src/gamedata)
|
||||
target_link_libraries(Game OgreBites OgreBullet OgrePaging OgreTerrain OgreMeshLodGenerator
|
||||
OgreProcedural::OgreProcedural ${BULLET_DYNAMICS_LIBRARY}
|
||||
${BULLET_COLLISION_LIBRARY} ${BULLET_MATH_LIBRARY}
|
||||
target_link_libraries(Game OgreBites OgrePaging OgreTerrain OgreMeshLodGenerator
|
||||
OgreProcedural::OgreProcedural
|
||||
GameData
|
||||
sound
|
||||
sceneloader physics
|
||||
flecs::flecs_static
|
||||
-Wl,--as-needed
|
||||
)
|
||||
@@ -110,9 +93,8 @@ endif()
|
||||
add_dependencies(Game stage_files import_buildings import_water_stuff import_vehicles import_vrm audio_data_gui)
|
||||
|
||||
add_executable(Procedural Procedural.cpp)
|
||||
target_link_libraries(Procedural OgreBites OgreBullet OgrePaging OgreTerrain
|
||||
OgreProcedural::OgreProcedural ${BULLET_DYNAMICS_LIBRARY}
|
||||
${BULLET_COLLISION_LIBRARY} ${BULLET_MATH_LIBRARY}
|
||||
target_link_libraries(Procedural OgreBites OgrePaging OgreTerrain
|
||||
OgreProcedural::OgreProcedural
|
||||
-Wl,--as-needed
|
||||
)
|
||||
if(OGRE_STATIC)
|
||||
@@ -168,6 +150,25 @@ foreach(VEHICLE_FILE ${VEHICLES_SRC})
|
||||
endforeach()
|
||||
add_custom_target(import_vehicles ALL DEPENDS ${VEHICLE_OUTPUT_FILES})
|
||||
|
||||
set(CHARACTER_SHAPES_SRC edited-normal-male-base.blend edited-shape-test-male.blend)
|
||||
set(CHARACTER_SHAPES_OUTPUT_FILES)
|
||||
foreach(CHARACTER_SHAPE_FILE ${CHARACTER_SHAPES_SRC})
|
||||
get_filename_component(FILE_NAME ${CHARACTER_SHAPE_FILE} NAME_WE)
|
||||
set(CHARACTER_SHAPE_OUTPUT_FILE ${CMAKE_BINARY_DIR}/characters/shapes/male/${FILE_NAME}/${FILE_NAME}.scene)
|
||||
add_custom_command(
|
||||
OUTPUT ${CHARACTER_SHAPE_OUTPUT_FILE}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/assets/blender
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/characters/shapes/male/${FILE_NAME}
|
||||
COMMAND ${BLENDER} ${CMAKE_SOURCE_DIR}/assets/blender/${CHARACTER_SHAPE_FILE}
|
||||
-b -Y -P
|
||||
${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_characters_ogre.py
|
||||
-- ${CHARACTER_SHAPE_OUTPUT_FILE}
|
||||
COMMAND touch ${CHARACTER_SHAPE_OUTPUT_FILE}
|
||||
DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/${CHARACTER_SHAPE_FILE})
|
||||
list(APPEND CHARACTER_SHAPES_OUTPUT_FILES ${CHARACTER_SHAPE_OUTPUT_FILE})
|
||||
endforeach()
|
||||
add_custom_target(import_character_shapes ALL DEPENDS ${CHARACTER_SHAPES_OUTPUT_FILES})
|
||||
|
||||
set(WATER_STUFF)
|
||||
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/water/sea.glb
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/water
|
||||
@@ -181,7 +182,7 @@ list(APPEND WATER_STUFF ${CMAKE_BINARY_DIR}/water/sea.glb)
|
||||
add_custom_target(import_water_stuff ALL DEPENDS ${WATER_STUFF})
|
||||
|
||||
add_executable(TerrainTest terrain.cpp)
|
||||
target_link_libraries(TerrainTest OgreBites OgreBullet OgrePaging OgreTerrain lua ${BULLET_DYNAMICS_LIBRARY} ${BULLET_COLLISION_LIBRARY} ${BULLET_MATH_LIBRARY}
|
||||
target_link_libraries(TerrainTest OgreBites OgrePaging OgreTerrain lua
|
||||
-Wl,--as-needed
|
||||
)
|
||||
target_include_directories(TerrainTest PRIVATE . src/terrain src/lua src/lua/lua-5.4.8/src)
|
||||
@@ -189,8 +190,6 @@ if(OGRE_STATIC)
|
||||
target_link_libraries(TerrainTest fix::assimp pugixml)
|
||||
target_link_options(TerrainTest PRIVATE -static-libstdc++ -static-libgcc)
|
||||
target_link_libraries(Procedural fix::assimp pugixml)
|
||||
target_link_libraries(0_Bootstrap fix::assimp pugixml)
|
||||
target_link_libraries(Editor fix::assimp pugixml)
|
||||
endif()
|
||||
|
||||
add_dependencies(TerrainTest stage_lua_scripts stage_files)
|
||||
@@ -330,6 +329,23 @@ list(APPEND EDITED_BLEND_TARGETS ${CMAKE_BINARY_DIR}/assets/blender/edited-norma
|
||||
list(APPEND CHARACTER_GLBS ${CMAKE_BINARY_DIR}/characters/${EDITED_BLEND}/normal-${EDITED_BLEND}.glb)
|
||||
endforeach()
|
||||
|
||||
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/characters/shapes/male/chibi/vroid-normal-male-chibi.glb
|
||||
DEPENDS ${CMAKE_BINARY_DIR}/assets/blender/characters/shapes/male/chibi/vroid-normal-male-chibi.glb
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/assets/blender/characters/shapes/male/chibi/vroid-normal-male-chibi.glb
|
||||
${CMAKE_BINARY_DIR}/characters/shapes/male/chibi/vroid-normal-male-chibi.glb)
|
||||
add_custom_target(morph ALL DEPENDS MorphTargetsResearch ${CMAKE_BINARY_DIR}/characters/shapes/male/chibi/vroid-normal-male-chibi.glb)
|
||||
|
||||
set(COPY_BLENDS edited-shape-test-male.blend edited-normal-male-base.blend)
|
||||
foreach (COPY_BLEND_FILE ${COPY_BLENDS})
|
||||
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/assets/blender/${COPY_BLEND_FILE}
|
||||
DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/${COPY_BLEND_FILE}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_SOURCE_DIR}/assets/blender/${COPY_BLEND_FILE}
|
||||
${CMAKE_BINARY_DIR}/assets/blender/${COPY_BLEND_FILE}
|
||||
)
|
||||
list(APPEND EDITED_BLEND_TARGETS ${CMAKE_BINARY_DIR}/assets/blender/${COPY_BLEND_FILE})
|
||||
endforeach()
|
||||
|
||||
add_custom_target(edited-blends ALL DEPENDS ${EDITED_BLEND_TARGETS})
|
||||
|
||||
add_custom_command(
|
||||
@@ -366,6 +382,8 @@ add_custom_target(stage_files ALL DEPENDS ${CMAKE_BINARY_DIR}/resources.cfg ${MA
|
||||
add_custom_target(remove_scenes COMMAND rm -f ${VRM_SOURCE} ${VRM_IMPORTED_BLENDS} ${CHARACTER_GLBS})
|
||||
|
||||
add_custom_target(import_vrm DEPENDS ${CHARACTER_GLBS})
|
||||
target_compile_definitions(Game PRIVATE FLECS_CPP_NO_AUTO_REGISTRATION)
|
||||
|
||||
install(TARGETS Game DESTINATION bin)
|
||||
install(TARGETS Editor DESTINATION bin)
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include <iostream>
|
||||
|
||||
#include <Ogre.h>
|
||||
#include <OgreBullet.h>
|
||||
#include <OgreApplicationContext.h>
|
||||
#include <OgreOverlaySystem.h>
|
||||
#include <OgreOverlayManager.h>
|
||||
@@ -245,8 +244,6 @@ public:
|
||||
void initGui();
|
||||
};
|
||||
class App : public OgreBites::ApplicationContext {
|
||||
std::unique_ptr<Ogre::Bullet::DynamicsWorld> mDynWorld;
|
||||
std::unique_ptr<Ogre::Bullet::DebugDrawer> mDbgDraw;
|
||||
Ogre::SceneNode *mCameraNode, *mCameraPivot, *mCameraGoal;
|
||||
Ogre::Camera *mCamera;
|
||||
Ogre::Real mPivotPitch;
|
||||
@@ -376,8 +373,6 @@ public:
|
||||
: OgreBites::ApplicationContext("ChoroEditor")
|
||||
, mKbd(this)
|
||||
, m_edit_ui(this)
|
||||
, mDynWorld(new Ogre::Bullet::DynamicsWorld(
|
||||
Ogre::Vector3(0, -9.8, 0)))
|
||||
{
|
||||
}
|
||||
virtual ~App()
|
||||
@@ -393,8 +388,6 @@ public:
|
||||
mScnMgr->addRenderQueueListener(pOverlaySystem);
|
||||
// mTrayMgr = new OgreBites::TrayManager("AppTrays",
|
||||
// getRenderWindow());
|
||||
mDbgDraw.reset(new Ogre::Bullet::DebugDrawer(
|
||||
mScnMgr->getRootSceneNode(), mDynWorld->getBtWorld()));
|
||||
}
|
||||
void locateResources() override
|
||||
{
|
||||
@@ -455,8 +448,10 @@ public:
|
||||
std::cout << "Init camera" << "\n";
|
||||
initCamera();
|
||||
std::cout << "Set up water" << "\n";
|
||||
#if 0
|
||||
m_water.createWater(getRenderWindow(), mCamera,
|
||||
mDynWorld.get());
|
||||
#endif
|
||||
std::cout << "Set up cursor" << "\n";
|
||||
Ogre::ResourceGroupManager::getSingleton()
|
||||
.initialiseAllResourceGroups();
|
||||
|
||||
19
Game.cpp
@@ -16,6 +16,7 @@
|
||||
#include "CharacterModule.h"
|
||||
#include "TerrainModule.h"
|
||||
#include "GUIModule.h"
|
||||
#include "AppModule.h"
|
||||
#include "sound.h"
|
||||
class App;
|
||||
class SkyRenderer : public Ogre::SceneManager::Listener {
|
||||
@@ -228,8 +229,12 @@ public:
|
||||
control |= 8;
|
||||
else if (key == OgreBites::SDLK_LSHIFT)
|
||||
control |= 16;
|
||||
else if (key == 'e')
|
||||
control |= 32;
|
||||
else if (key == 'f')
|
||||
control |= 64;
|
||||
if (key == 'w' || key == 'a' || key == 's' || key == 'd' ||
|
||||
key == OgreBites::SDLK_LSHIFT)
|
||||
key == 'e' || key == OgreBites::SDLK_LSHIFT)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@@ -285,8 +290,6 @@ public:
|
||||
void frameRendered(const Ogre::FrameEvent &evt) override;
|
||||
};
|
||||
class App : public OgreBites::ApplicationContext {
|
||||
std::unique_ptr<Ogre::Bullet::DynamicsWorld> mDynWorld;
|
||||
std::unique_ptr<Ogre::Bullet::DebugDrawer> mDbgDraw;
|
||||
Ogre::SceneNode *mCameraNode, *mCameraPivot, *mCameraGoal;
|
||||
Ogre::Camera *mCamera;
|
||||
Ogre::Real mPivotPitch;
|
||||
@@ -300,8 +303,6 @@ public:
|
||||
App()
|
||||
: OgreBites::ApplicationContext("ChoroGame")
|
||||
, mKbd(this)
|
||||
, mDynWorld(new Ogre::Bullet::DynamicsWorld(
|
||||
Ogre::Vector3(0, -9.8, 0)))
|
||||
, mGrab(false)
|
||||
{
|
||||
}
|
||||
@@ -318,8 +319,6 @@ public:
|
||||
mScnMgr->addRenderQueueListener(pOverlaySystem);
|
||||
// mTrayMgr = new OgreBites::TrayManager("AppTrays",
|
||||
// getRenderWindow());
|
||||
mDbgDraw.reset(new Ogre::Bullet::DebugDrawer(
|
||||
mScnMgr->getRootSceneNode(), mDynWorld->getBtWorld()));
|
||||
}
|
||||
bool isWindowGrab()
|
||||
{
|
||||
@@ -428,7 +427,9 @@ public:
|
||||
}
|
||||
void updateWorld(float delta)
|
||||
{
|
||||
#if 0
|
||||
mDynWorld->getBtWorld()->stepSimulation(delta, 3);
|
||||
#endif
|
||||
if (!ECS::get().has<ECS::GUI>())
|
||||
return;
|
||||
/* Update window grab */
|
||||
@@ -440,8 +441,10 @@ public:
|
||||
}
|
||||
ECS::update(delta);
|
||||
|
||||
#if 0
|
||||
if (ECS::get<ECS::EngineData>().enableDbgDraw)
|
||||
mDbgDraw->update();
|
||||
#endif
|
||||
}
|
||||
class InputListenerChainFlexible : public OgreBites::InputListener {
|
||||
protected:
|
||||
@@ -594,7 +597,7 @@ public:
|
||||
"Skybox/Dynamic", "General");
|
||||
OgreAssert(m, "Sky box material not found.");
|
||||
m->load();
|
||||
ECS::setup(mScnMgr, mDynWorld.get(), mCameraNode, mCamera,
|
||||
ECS::setup(mScnMgr, /*mDynWorld.get(), */ mCameraNode, mCamera,
|
||||
getRenderWindow());
|
||||
ECS::get().set<ECS::RenderWindow>(
|
||||
{ getRenderWindow(), getDisplayDPI() });
|
||||
|
||||
209
Procedural.cpp
@@ -7,9 +7,6 @@
|
||||
|
||||
#include "Ogre.h"
|
||||
#include "OgreApplicationContext.h"
|
||||
#include "Bullet/OgreBullet.h"
|
||||
#include "BulletCollision/CollisionDispatch/btGhostObject.h"
|
||||
#include "LinearMath/btTransform.h"
|
||||
#include "OgrePageManager.h"
|
||||
#include "Procedural.h"
|
||||
|
||||
@@ -22,11 +19,8 @@ using Real = Ogre::Real;
|
||||
using Math = Ogre::Math;
|
||||
|
||||
class WorldData {
|
||||
std::unique_ptr<Ogre::Bullet::DynamicsWorld> mDynWorld;
|
||||
std::unique_ptr<Ogre::Bullet::DebugDrawer> mDbgDraw;
|
||||
std::unique_ptr<Ogre::Root> mRoot;
|
||||
std::unique_ptr<Ogre::SceneManager> mScnMgr;
|
||||
std::unique_ptr<btDynamicsWorld> mbtWorld;
|
||||
std::unique_ptr<Ogre::PageManager> mPageManager;
|
||||
Ogre::PagedWorld *mPagedWorld;
|
||||
|
||||
@@ -62,13 +56,8 @@ private:
|
||||
DummyPageProvider mDummyPageProvider;
|
||||
|
||||
WorldData(Ogre::Root *root, Ogre::SceneManager *scnMgr)
|
||||
: mDynWorld(new Ogre::Bullet::DynamicsWorld(
|
||||
Ogre::Vector3(0, -9.8, 0)))
|
||||
, mDbgDraw(new Ogre::Bullet::DebugDrawer(
|
||||
scnMgr->getRootSceneNode(), mDynWorld->getBtWorld()))
|
||||
, mRoot(root)
|
||||
: mRoot(root)
|
||||
, mScnMgr(scnMgr)
|
||||
, mbtWorld(mDynWorld->getBtWorld())
|
||||
, mPageManager(nullptr)
|
||||
, mPagedWorld(nullptr)
|
||||
{
|
||||
@@ -100,94 +89,8 @@ public:
|
||||
void createTrimesh(Ogre::Entity *entity)
|
||||
{
|
||||
}
|
||||
btPairCachingGhostObject *addGhostObject(Ogre::Entity *ent,
|
||||
btCollisionShape *shape,
|
||||
int group = 1,
|
||||
int mask = 0xFFFF)
|
||||
{
|
||||
btDynamicsWorld *world = mDynWorld->getBtWorld();
|
||||
Ogre::SceneNode *node = ent->getParentSceneNode();
|
||||
btPairCachingGhostObject *ghost =
|
||||
new btPairCachingGhostObject();
|
||||
ghost->setCollisionShape(shape);
|
||||
ghost->setCollisionFlags(
|
||||
ghost->getCollisionFlags() |
|
||||
btCollisionObject::CF_NO_CONTACT_RESPONSE |
|
||||
btCollisionObject::CF_CHARACTER_OBJECT);
|
||||
getWorld()->attachCollisionObject(ghost, ent, group, mask);
|
||||
#if 0
|
||||
getBtWorld()
|
||||
->getBroadphase()->getOverlappingPairCache()
|
||||
->setInternalGhostPairCallback(new btGhostPairCallback());
|
||||
ghost->setUserPointer(new EntityCollisionListener{ent, nullptr});
|
||||
#endif
|
||||
return ghost;
|
||||
}
|
||||
btRigidBody *addRigidBody(float mass, Ogre::Entity *ent,
|
||||
Ogre::Bullet::ColliderType ct, int group = 1,
|
||||
int mask = 0xFFFF)
|
||||
{
|
||||
btDynamicsWorld *world = mDynWorld->getBtWorld();
|
||||
Ogre::SceneNode *node = ent->getParentSceneNode();
|
||||
Ogre::Bullet::RigidBodyState *state =
|
||||
new Ogre::Bullet::RigidBodyState(node);
|
||||
btCollisionShape *cs;
|
||||
btCollisionShape *shape;
|
||||
btVector3 inertia(0, 0, 0);
|
||||
switch (ct) {
|
||||
case Ogre::Bullet::CT_TRIMESH: {
|
||||
cs = Ogre::Bullet::createTrimeshCollider(ent);
|
||||
if (mass != 0)
|
||||
cs->calculateLocalInertia(mass, inertia);
|
||||
} break;
|
||||
case Ogre::Bullet::CT_CAPSULE: {
|
||||
cs = new btCompoundShape(false);
|
||||
btScalar height = 1.0f;
|
||||
btScalar radius = 0.3f;
|
||||
shape = new btCapsuleShape(radius,
|
||||
2 * height - 2 * radius);
|
||||
btTransform transform;
|
||||
transform.setIdentity();
|
||||
transform.setOrigin(btVector3(0, 1, 0));
|
||||
static_cast<btCompoundShape *>(cs)->addChildShape(
|
||||
transform, shape);
|
||||
btScalar masses[1] = { mass };
|
||||
btTransform principal;
|
||||
static_cast<btCompoundShape *>(cs)
|
||||
->calculatePrincipalAxisTransform(
|
||||
masses, principal, inertia);
|
||||
} break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
btRigidBody *body = new btRigidBody(mass, state, cs, inertia);
|
||||
getWorld()->attachRigidBody(body, ent, nullptr, group, mask);
|
||||
#if 0
|
||||
body->setUserPointer(new EntityCollisionListener{ent, nullptr});
|
||||
// btRigidBody *body = mDynWorld->addRigidBody(0, ent, Ogre::Bullet::CT_TRIMESH);
|
||||
#endif
|
||||
return body;
|
||||
}
|
||||
btRigidBody *addKinematicRigidBody(float mass, Ogre::Entity *ent,
|
||||
Ogre::Bullet::ColliderType ct,
|
||||
int group = 1, int mask = 0xFFFF)
|
||||
{
|
||||
return mDynWorld->addKinematicRigidBody(ent, ct, group, mask);
|
||||
}
|
||||
btDynamicsWorld *getBtWorld()
|
||||
{
|
||||
return mDynWorld->getBtWorld();
|
||||
}
|
||||
Ogre::Bullet::DynamicsWorld *getWorld()
|
||||
{
|
||||
return mDynWorld.get();
|
||||
}
|
||||
void update(float delta)
|
||||
{
|
||||
WorldData::get_singleton()->getBtWorld()->stepSimulation(delta,
|
||||
10);
|
||||
mDbgDraw->update();
|
||||
}
|
||||
void initPagedWorld(Ogre::Camera *camera)
|
||||
{
|
||||
@@ -201,8 +104,6 @@ public:
|
||||
WorldData *WorldData::singleton = nullptr;
|
||||
|
||||
class MainWorld : public Ogre::FrameListener {
|
||||
btRigidBody *mFloorBody;
|
||||
|
||||
public:
|
||||
void setup()
|
||||
{
|
||||
@@ -220,13 +121,6 @@ public:
|
||||
WorldData::get_singleton()->getSceneManager();
|
||||
Ogre::Entity *floor = scnMgr->createEntity("Floor", "floor");
|
||||
scnMgr->getRootSceneNode()->attachObject(floor);
|
||||
mFloorBody = WorldData::get_singleton()->addRigidBody(
|
||||
0, floor, Ogre::Bullet::CT_TRIMESH);
|
||||
}
|
||||
btRigidBody *addCharacter(Ogre::Entity *ent, float mass)
|
||||
{
|
||||
return WorldData::get_singleton()->addKinematicRigidBody(
|
||||
mass, ent, Ogre::Bullet::CT_COMPOUND);
|
||||
}
|
||||
bool frameStarted(const Ogre::FrameEvent &evt) override;
|
||||
};
|
||||
@@ -266,8 +160,6 @@ class CharacterController : public OgreBites::InputListener,
|
||||
Ogre::Vector3 rootMotion;
|
||||
Ogre::Quaternion rootRotation;
|
||||
// btRigidBody *mRigidBody;
|
||||
btCompoundShape *mCollisionShape;
|
||||
btPairCachingGhostObject *mGhostObject;
|
||||
|
||||
public:
|
||||
CharacterController(Ogre::SceneNode *camNode, Ogre::Camera *cam,
|
||||
@@ -316,35 +208,6 @@ private:
|
||||
recoverResult *recover_result,
|
||||
const std::set<btCollisionObject *> &exclude);
|
||||
#endif
|
||||
inline btQuaternion convert(const Ogre::Quaternion &q)
|
||||
{
|
||||
return btQuaternion(q.x, q.y, q.z, q.w);
|
||||
}
|
||||
inline btVector3 convert(const Ogre::Vector3 &v)
|
||||
{
|
||||
return btVector3(v.x, v.y, v.z);
|
||||
}
|
||||
inline btTransform convert(const Ogre::Quaternion &q,
|
||||
const Ogre::Vector3 &v)
|
||||
{
|
||||
btQuaternion mq = convert(q);
|
||||
btVector3 mv = convert(v);
|
||||
return btTransform(mq, mv);
|
||||
}
|
||||
inline Ogre::Quaternion convert(const btQuaternion &q)
|
||||
{
|
||||
return Ogre::Quaternion(q.w(), q.x(), q.y(), q.z());
|
||||
}
|
||||
inline Ogre::Vector3 convert(const btVector3 &v)
|
||||
{
|
||||
return Ogre::Vector3(v.x(), v.y(), v.z());
|
||||
}
|
||||
inline void convert(const btTransform &from, Ogre::Quaternion &q,
|
||||
Ogre::Vector3 &v)
|
||||
{
|
||||
q = convert(from.getRotation());
|
||||
v = convert(from.getOrigin());
|
||||
}
|
||||
};
|
||||
CharacterController::CharacterController(Ogre::SceneNode *camNode,
|
||||
Ogre::Camera *cam,
|
||||
@@ -358,8 +221,6 @@ CharacterController::CharacterController(Ogre::SceneNode *camNode,
|
||||
, mAnimID(ANIM_NONE)
|
||||
, mRunning(false)
|
||||
, world(world)
|
||||
, mCollisionShape(nullptr)
|
||||
, mGhostObject(nullptr)
|
||||
{
|
||||
setupBody();
|
||||
setupCamera();
|
||||
@@ -374,72 +235,6 @@ void CharacterController::setupBody()
|
||||
mBodyNode = mScnMgr->getRootSceneNode()->createChildSceneNode();
|
||||
mBodyNode->attachObject(mBodyEnt);
|
||||
mSkeleton = mBodyEnt->getSkeleton();
|
||||
// mRigidBody = world->addCharacter(mBodyEnt, 0);
|
||||
// mCollisionShape = static_cast<btCompoundShape *>(mRigidBody->getCollisionShape());
|
||||
mGhostObject = new btPairCachingGhostObject();
|
||||
mCollisionShape = new btCompoundShape(false);
|
||||
mGhostObject->setCollisionShape(mCollisionShape);
|
||||
|
||||
{
|
||||
btVector3 inertia(0, 0, 0);
|
||||
// mCollisionShape = new btCompoundShape();
|
||||
btScalar height = 1.0f;
|
||||
btScalar radius = 0.3f;
|
||||
btCapsuleShape *shape =
|
||||
new btCapsuleShape(radius, 2 * height - 2 * radius);
|
||||
btTransform transform;
|
||||
transform.setIdentity();
|
||||
transform.setOrigin(btVector3(0, 1, 0));
|
||||
static_cast<btCompoundShape *>(mCollisionShape)
|
||||
->addChildShape(transform, shape);
|
||||
btScalar masses[1] = { 0 };
|
||||
btTransform principal;
|
||||
static_cast<btCompoundShape *>(mCollisionShape)
|
||||
->calculatePrincipalAxisTransform(masses, principal,
|
||||
inertia);
|
||||
}
|
||||
mGhostObject->setCollisionFlags(
|
||||
btCollisionObject::CF_KINEMATIC_OBJECT |
|
||||
btCollisionObject::CF_NO_CONTACT_RESPONSE);
|
||||
mGhostObject->setActivationState(DISABLE_DEACTIVATION);
|
||||
Ogre::Bullet::KinematicMotionSimple *controller =
|
||||
new Ogre::Bullet::KinematicMotionSimple(mGhostObject,
|
||||
mBodyNode);
|
||||
WorldData::get_singleton()->getWorld()->attachCollisionObject(
|
||||
mGhostObject, mBodyEnt, btBroadphaseProxy::AllFilter,
|
||||
btBroadphaseProxy::AllFilter);
|
||||
WorldData::get_singleton()->getBtWorld()->addAction(controller);
|
||||
|
||||
assert(mCollisionShape);
|
||||
#if 0
|
||||
if (mRigidBody->getMass() == 0) {
|
||||
#if 0
|
||||
mRigidBody->setCollisionFlags(mRigidBody->getCollisionFlags()
|
||||
| btCollisionObject::CF_KINEMATIC_OBJECT
|
||||
| btCollisionObject::CF_NO_CONTACT_RESPONSE
|
||||
);
|
||||
#endif
|
||||
#if 0
|
||||
mGhostObject->setWorldTransform(mRigidBody->getWorldTransform());
|
||||
WorldData::get_singleton()->getBtWorld()
|
||||
->getBroadphase()->getOverlappingPairCache()
|
||||
->setInternalGhostPairCallback(new btGhostPairCallback());
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#if 0
|
||||
mRigidBody->setActivationState(DISABLE_DEACTIVATION);
|
||||
#endif
|
||||
#if 0
|
||||
{
|
||||
Ogre::Entity *e2 = mScnMgr->createEntity("normal-male.glb");
|
||||
Ogre::SceneNode *e2node = mScnMgr->getRootSceneNode()->createChildSceneNode();
|
||||
e2node->attachObject(e2);
|
||||
mGhostObject = WorldData::get_singleton()->addGhostObject(e2, mCollisionShape);
|
||||
mController = new btKinematicCharacterController(mGhostObject, mCollisionShape, 0.5f);
|
||||
WorldData::get_singleton()->getBtWorld()->addAction(mController);
|
||||
}
|
||||
#endif
|
||||
assert(mSkeleton->hasBone("Root"));
|
||||
mRootBone = mSkeleton->getBone("Root");
|
||||
assert(mRootBone);
|
||||
@@ -740,8 +535,6 @@ void CharacterController::updateRootMotion(Real delta)
|
||||
Ogre::Vector3 velocity = rot * boneMotion / delta;
|
||||
velocity += gravity * delta;
|
||||
Ogre::Vector3 rotMotion = velocity * delta;
|
||||
btTransform from(convert(mBodyNode->getOrientation()),
|
||||
convert(mBodyNode->getPosition()));
|
||||
mBodyNode->setPosition(mBodyNode->getPosition() + rotMotion);
|
||||
// WorldData::get_singleton()->getWorld()->testBodyMotion(mRigidBody, from, Ogre::Bullet::convert(rotMotion), true,
|
||||
// nullptr, false, std::set<btCollisionObject *>());
|
||||
|
||||
BIN
assets/blender/edited-normal-female.blend
(Stored with Git LFS)
BIN
assets/blender/edited-normal-male-base.blend
(Stored with Git LFS)
Normal file
BIN
assets/blender/edited-normal-male.blend
(Stored with Git LFS)
BIN
assets/blender/edited-shape-test-male.blend
(Stored with Git LFS)
Normal file
BIN
assets/blender/mixamo/female/hanging-climb.fbx
(Stored with Git LFS)
Normal file
BIN
assets/blender/mixamo/female/hanging-idle.fbx
(Stored with Git LFS)
Normal file
BIN
assets/blender/mixamo/female/sitting.fbx
(Stored with Git LFS)
Normal file
BIN
assets/blender/mixamo/male/hanging-climb.fbx
(Stored with Git LFS)
Normal file
BIN
assets/blender/mixamo/male/hanging-idle.fbx
(Stored with Git LFS)
Normal file
BIN
assets/blender/mixamo/male/sitting.fbx
(Stored with Git LFS)
Normal file
@@ -92,8 +92,8 @@ class OgreCollisionOp(bpy.types.Operator):
|
||||
def get_subcollisions( self, ob, create=True ):
|
||||
r = get_subcollisions( ob )
|
||||
if not r and create:
|
||||
method = getattr(self, 'create_%s'%ob.collision_mode)
|
||||
p = method(ob)
|
||||
# method = getattr(self, 'create_%s'%ob.collision_mode)
|
||||
# p = method(ob)
|
||||
p.name = '%s.%s' %(ob.collision_mode, ob.name)
|
||||
p.subcollision = True
|
||||
r.append( p )
|
||||
|
||||
@@ -52,7 +52,7 @@ def dot_scene(path, scene_name=None):
|
||||
linkedgroups = []
|
||||
invalidnamewarnings = []
|
||||
for ob in bpy.context.scene.objects:
|
||||
if ob.subcollision:
|
||||
if ob.subcollision or "collision_type" in ob:
|
||||
continue
|
||||
if ((config.get("EXPORT_HIDDEN") is False) and (ob not in bpy.context.visible_objects)):
|
||||
continue
|
||||
@@ -184,6 +184,7 @@ def dot_scene(path, scene_name=None):
|
||||
|
||||
mesh_collision_prims = {}
|
||||
mesh_collision_files = {}
|
||||
compound_collision_shapes = {}
|
||||
|
||||
# Export the objects in the scene
|
||||
for root in roots:
|
||||
@@ -193,6 +194,7 @@ def dot_scene(path, scene_name=None):
|
||||
meshes = meshes,
|
||||
mesh_collision_prims = mesh_collision_prims,
|
||||
mesh_collision_files = mesh_collision_files,
|
||||
compound_collision_shapes = compound_collision_shapes,
|
||||
exported_armatures = exported_armatures,
|
||||
prefix = prefix,
|
||||
objects = objects,
|
||||
@@ -655,14 +657,16 @@ def dot_scene_skybox_export( path ):
|
||||
|
||||
# Recursive Node export
|
||||
def dot_scene_node_export( ob, path, doc=None, rex=None,
|
||||
exported_meshes=[], meshes=[], mesh_collision_prims={}, mesh_collision_files={},
|
||||
exported_meshes=[], meshes=[], mesh_collision_prims={}, mesh_collision_files={}, compound_collision_shapes={},
|
||||
exported_armatures=[], prefix='', objects=[], xmlparent=None ):
|
||||
print("Nodes... " + ob.name)
|
||||
|
||||
o = _ogre_node_helper( doc, ob )
|
||||
xmlparent.appendChild(o)
|
||||
|
||||
# if config.get('EXPORT_USER') is True:
|
||||
# Custom user props
|
||||
user = None
|
||||
if len(ob.items()) > 0:
|
||||
user = doc.createElement('userData')
|
||||
o.appendChild(user)
|
||||
@@ -672,46 +676,116 @@ def dot_scene_node_export( ob, path, doc=None, rex=None,
|
||||
if not propname.startswith('_'):
|
||||
_property_helper(doc, user, propname, propvalue)
|
||||
|
||||
if ob.type == 'MESH' or ob.type == 'EMPTY':
|
||||
if not user:
|
||||
user = doc.createElement('userData')
|
||||
o.appendChild(user)
|
||||
if ob.rigid_body:
|
||||
_property_helper(doc, user, "blenderCollisionType", ob.rigid_body.type)
|
||||
_property_helper(doc, user, "blenderCollisionShape", ob.rigid_body.collision_shape)
|
||||
_property_helper(doc, user, "blenderCollisionEnabled", ob.rigid_body.enabled)
|
||||
_property_helper(doc, user, "blenderCollisionKinematic", ob.rigid_body.kinematic)
|
||||
_property_helper(doc, user, "blenderCollisionMass", ob.rigid_body.mass)
|
||||
if ob.rigid_body.collision_shape == "CONVEX_HULL":
|
||||
_property_helper(doc, user, "collisionType", "convexHull")
|
||||
elif ob.rigid_body.collision_shape == "MESH":
|
||||
_property_helper(doc, user, "collisionType", "mesh")
|
||||
elif ob.rigid_body.collision_shape == "COMPOUND":
|
||||
_property_helper(doc, user, "collisionType", "compound")
|
||||
_property_helper(doc, user, "collisionBodyMass", ob.rigid_body.mass)
|
||||
if ob.type == 'MESH':
|
||||
if (not ob.parent) or (ob.parent and not ob.parent.rigid_body) or (ob.parent and ob.parent.rigid_body and ob.parent.rigid_body.collision_shape != "COMPOUND"):
|
||||
# no compound parent
|
||||
if ob.rigid_body.collision_shape in ["CONVEX_HULL", "MESH"]:
|
||||
mesh.dot_mesh(ob, path, force_name='%s_collision_%s' % (prefix, ob.data.name) )
|
||||
skeleton.dot_skeleton(ob, path)
|
||||
_property_helper(doc, user, "collisionFile", '%s_collision_%s.mesh' % (prefix, ob.data.name))
|
||||
|
||||
elif ob.parent and ob.parent.rigid_body.collision_shape == "COMPOUND":
|
||||
# has compound parent
|
||||
mesh.dot_mesh(ob, path, force_name='%s_collision_%s_%s' % (prefix, ob.parent.data.name, ob.data.name) )
|
||||
skeleton.dot_skeleton(ob, path)
|
||||
_property_helper(doc, user, "collisionFile", '%s_collision_%s_%s.mesh' % (prefix, ob.parent.data.name, ob.data.name))
|
||||
if (not ob.parent) or (ob.parent and not ob.parent.rigid_body) or (ob.parent and ob.parent.rigid_body and ob.parent.rigid_body.collision_shape != "COMPOUND"):
|
||||
if ob.rigid_body.type == 'ACTIVE':
|
||||
if not ob.rigid_body.kinematic:
|
||||
_property_helper(doc, user, "collisionBodyType", "dynamic")
|
||||
else:
|
||||
_property_helper(doc, user, "collisionBodyType", "ghost")
|
||||
else:
|
||||
if not ob.rigid_body.kinematic:
|
||||
_property_helper(doc, user, "collisionBodyType", "static")
|
||||
else:
|
||||
_property_helper(doc, user, "collisionBodyType", "kinematic")
|
||||
|
||||
|
||||
if ob.type == 'MESH':
|
||||
# ob.data.tessfaces is empty. always until the following call
|
||||
ob.data.update()
|
||||
ob.data.calc_loop_triangles()
|
||||
# if it has no faces at all, the object itself will not be exported, BUT
|
||||
# it might have children
|
||||
|
||||
if ob.type == 'MESH' and len(ob.data.loop_triangles):
|
||||
print("Loopie..." + ob.data.name + " ", ob.data.loop_triangles)
|
||||
compoundParent = False
|
||||
hasPhysics = False
|
||||
if ob.rigid_body:
|
||||
hasPhysics = True
|
||||
if ob.parent:
|
||||
if ob.parent.rigid_body:
|
||||
if ob.parent.rigid_body.collision_shape == "COMPOUND":
|
||||
compoundParent = True
|
||||
createEntity = not (compoundParent and hasPhysics)
|
||||
if ob.type == 'MESH' and len(ob.data.loop_triangles) and createEntity:
|
||||
collisionFile = None
|
||||
collisionPrim = None
|
||||
compoundColliders = None
|
||||
if ob.data.name in mesh_collision_prims:
|
||||
collisionPrim = mesh_collision_prims[ ob.data.name ]
|
||||
if ob.data.name in mesh_collision_files:
|
||||
collisionFile = mesh_collision_files[ ob.data.name ]
|
||||
|
||||
e = doc.createElement('entity')
|
||||
o.appendChild(e); e.setAttribute('name', ob.name)
|
||||
prefix = ''
|
||||
e.setAttribute('meshFile', '%s%s.mesh' % (prefix, clean_object_name(ob.data.name)) )
|
||||
|
||||
# Set the instancing attribute if the object belongs to the correct group
|
||||
_mesh_instance_helper(e, ob, "static")
|
||||
_mesh_instance_helper(e, ob, "instanced")
|
||||
if ob.data.name in compound_collision_shapes:
|
||||
compoundColliders = compound_collision_shapes[ ob.data.name ]
|
||||
print("Meshie..." + ob.data.name)
|
||||
if True:
|
||||
e = doc.createElement('entity')
|
||||
o.appendChild(e); e.setAttribute('name', ob.name)
|
||||
prefix = ''
|
||||
e.setAttribute('meshFile', '%s%s.mesh' % (prefix, clean_object_name(ob.data.name)) )
|
||||
|
||||
# Set the instancing attribute if the object belongs to the correct group
|
||||
_mesh_instance_helper(e, ob, "static")
|
||||
_mesh_instance_helper(e, ob, "instanced")
|
||||
if not collisionPrim and not collisionFile:
|
||||
print("Collisions..." + ob.data.name)
|
||||
for child in ob.children:
|
||||
if child.type == 'MESH':
|
||||
print("\tCollisions... " + child.data.name)
|
||||
#if child.rigid_body:
|
||||
# print("physics body")
|
||||
# if child.rigid_body.collision_shape == 'CONVEX_HULL':
|
||||
# collisionFile = '%s_collision_%s.mesh' % (prefix, ob.data.name)
|
||||
# elif child.rigid_body.collision_shape == 'MESH':
|
||||
# collisionFile = '%s_collision_%s.mesh' % (prefix, ob.data.name)
|
||||
# break
|
||||
if child.subcollision and child.name.startswith('DECIMATE'):
|
||||
collisionFile = '%s_collision_%s.mesh' % (prefix, ob.data.name)
|
||||
break
|
||||
elif "collision_type" in child and child.name.startswith('DECIMATE'):
|
||||
collisionFile = '%s_collision_%s.mesh' % (prefix, ob.data.name)
|
||||
break
|
||||
elif "collision_type" in child:
|
||||
collisionFile = '%s_collision_%s.mesh' % (prefix, ob.data.name)
|
||||
break
|
||||
if collisionFile:
|
||||
print("CollisionFile:")
|
||||
mesh_collision_files[ ob.data.name ] = collisionFile
|
||||
mesh.dot_mesh(child, path, force_name='%s_collision_%s' % (prefix, ob.data.name) )
|
||||
skeleton.dot_skeleton(child, path)
|
||||
|
||||
if not collisionPrim and not collisionFile:
|
||||
for child in ob.children:
|
||||
if child.subcollision and child.name.startswith('DECIMATE'):
|
||||
collisionFile = '%s_collision_%s.mesh' % (prefix, ob.data.name)
|
||||
break
|
||||
if child.name.endswith("-collision")
|
||||
collisionFile = '%s_collision_%s.mesh' % (prefix, ob.data.name)
|
||||
if collisionFile:
|
||||
mesh_collision_files[ ob.data.name ] = collisionFile
|
||||
mesh.dot_mesh(child, path, force_name='%s_collision_%s' % (prefix, ob.data.name) )
|
||||
skeleton.dot_skeleton(child, path)
|
||||
|
||||
if collisionPrim:
|
||||
e.setAttribute('collisionPrim', collisionPrim )
|
||||
elif collisionFile:
|
||||
e.setAttribute('collisionFile', collisionFile )
|
||||
if collisionPrim:
|
||||
e.setAttribute('collisionPrim', collisionPrim )
|
||||
elif collisionFile:
|
||||
e.setAttribute('collisionFile', collisionFile )
|
||||
|
||||
#if config.get('EXPORT_USER') is True:
|
||||
_mesh_entity_helper( doc, ob, e )
|
||||
|
||||
@@ -769,14 +769,21 @@ def get_subcollision_meshes():
|
||||
''' returns all collision meshes found in the scene '''
|
||||
r = []
|
||||
for ob in bpy.context.scene.objects:
|
||||
if ob.type=='MESH' and ob.subcollision: r.append( ob )
|
||||
if ob.type=='MESH' and (ob.subcollision or "collision_type" in ob):
|
||||
r.append( ob )
|
||||
return r
|
||||
|
||||
def get_objects_with_subcollision():
|
||||
''' returns objects that have active sub-collisions '''
|
||||
r = []
|
||||
for ob in bpy.context.scene.objects:
|
||||
if ob.type=='MESH' and ob.collision_mode not in ('NONE', 'PRIMITIVE'):
|
||||
if ob.type != 'MESH':
|
||||
continue
|
||||
if not ob.rigid_body:
|
||||
continue
|
||||
if ob.rigid_body.collision_shape in ('CONVEX_HULL', 'MESH'):
|
||||
r.append( ob )
|
||||
elif ob.type=='MESH' and not ob.collision_mode in ('NONE', 'PRIMITIVE'):
|
||||
r.append( ob )
|
||||
return r
|
||||
|
||||
@@ -784,7 +791,7 @@ def get_subcollisions(ob):
|
||||
prefix = '%s.' %ob.collision_mode
|
||||
r = []
|
||||
for child in ob.children:
|
||||
if child.subcollision and child.name.startswith( prefix ):
|
||||
if (child.subcollision or "collision_type" in ob) and child.name.startswith( prefix ):
|
||||
r.append( child )
|
||||
return r
|
||||
|
||||
|
||||
@@ -11,13 +11,18 @@ from math import radians, pi
|
||||
argv = sys.argv
|
||||
argv = argv[argv.index("--") + 1:]
|
||||
|
||||
sys.path.insert(0, os.getcwd() + "/assets/blender/scripts")
|
||||
sys.path.insert(1, os.getcwd() + "/assets/blender/scripts/blender2ogre")
|
||||
incpath = os.path.dirname(__file__)
|
||||
|
||||
sys.path.insert(0, incpath)
|
||||
sys.path.insert(1, incpath + "/blender2ogre")
|
||||
|
||||
|
||||
import io_ogre
|
||||
io_ogre.register()
|
||||
|
||||
gltf_file = argv[0]
|
||||
print("Exporting to " + gltf_file)
|
||||
basepath = os.getcwd()
|
||||
basepath = incpath
|
||||
# bpy.ops.export_scene.gltf(filepath="", check_existing=True,
|
||||
# export_import_convert_lighting_mode='SPEC', gltf_export_id="",
|
||||
# export_format='GLB', ui_tab='GENERAL', export_copyright="", export_image_format='AUTO',
|
||||
@@ -47,6 +52,7 @@ for obj in bpy.data.objects:
|
||||
bpy.data.objects.remove(obj)
|
||||
|
||||
scene_file = gltf_file.replace(".glb", "").replace(".gltf", "") + ".scene"
|
||||
|
||||
bpy.ops.ogre.export(filepath=scene_file,
|
||||
EX_SWAP_AXIS='xz-y',
|
||||
EX_V2_MESH_TOOL_VERSION='v2',
|
||||
@@ -58,7 +64,7 @@ bpy.ops.ogre.export(filepath=scene_file,
|
||||
EX_FORCE_LIGHTS=False,
|
||||
EX_NODE_ANIMATION=True,
|
||||
EX_MATERIALS=True,
|
||||
EX_SEPARATE_MATERIALS=False,
|
||||
EX_SEPARATE_MATERIALS=True,
|
||||
EX_COPY_SHADER_PROGRAMS=True,
|
||||
EX_MESH=True,
|
||||
EX_LOD_LEVELS=3,
|
||||
|
||||
88
assets/blender/scripts/export_characters_ogre.py
Normal file
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os, sys, time
|
||||
import bpy
|
||||
from math import pi
|
||||
import glob
|
||||
import shutil
|
||||
from mathutils import Vector, Matrix
|
||||
from math import radians, pi
|
||||
|
||||
argv = sys.argv
|
||||
argv = argv[argv.index("--") + 1:]
|
||||
|
||||
sys.path.insert(0, os.getcwd() + "/assets/blender/scripts")
|
||||
sys.path.insert(1, os.getcwd() + "/assets/blender/scripts/blender2ogre")
|
||||
import io_ogre
|
||||
|
||||
output_file = argv[0]
|
||||
print("Exporting to " + output_file)
|
||||
basepath = os.getcwd()
|
||||
# bpy.ops.export_scene.gltf(filepath="", check_existing=True,
|
||||
# export_import_convert_lighting_mode='SPEC', gltf_export_id="",
|
||||
# export_format='GLB', ui_tab='GENERAL', export_copyright="", export_image_format='AUTO',
|
||||
# export_texture_dir="", export_jpeg_quality=75, export_keep_originals=False,
|
||||
# export_texcoords=True, export_normals=True, export_draco_mesh_compression_enable=False,
|
||||
# export_draco_mesh_compression_level=6, export_draco_position_quantization=14,
|
||||
# export_draco_normal_quantization=10, export_draco_texcoord_quantization=12,
|
||||
# export_draco_color_quantization=10, export_draco_generic_quantization=12, export_tangents=False,
|
||||
# export_materials='EXPORT', export_original_specular=False, export_colors=True,
|
||||
# export_attributes=False, use_mesh_edges=False, use_mesh_vertices=False, export_cameras=False,
|
||||
# use_selection=False, use_visible=False, use_renderable=False,
|
||||
# use_active_collection_with_nested=True, use_active_collection=False, use_active_scene=False,
|
||||
# export_extras=False, export_yup=True, export_apply=False, export_animations=True,
|
||||
# export_frame_range=False, export_frame_step=1, export_force_sampling=True, export_animation_mode='ACTIONS',
|
||||
# export_nla_strips_merged_animation_name="Animation", export_def_bones=False,
|
||||
# export_hierarchy_flatten_bones=False, export_optimize_animation_size=True,
|
||||
# export_optimize_animation_keep_anim_armature=True, export_optimize_animation_keep_anim_object=False,
|
||||
# export_negative_frame='SLIDE', export_anim_slide_to_zero=False, export_bake_animation=False,
|
||||
# export_anim_single_armature=True, export_reset_pose_bones=True, export_current_frame=False,
|
||||
# export_rest_position_armature=True, export_anim_scene_split_object=True, export_skins=True,
|
||||
# export_all_influences=False, export_morph=True, export_morph_normal=True,
|
||||
# export_morph_tangent=False, export_morph_animation=True, export_morph_reset_sk_data=True,
|
||||
# export_lights=False, export_nla_strips=True, will_save_settings=False, filter_glob="*.glb")
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
if obj.name.endswith("-col"):
|
||||
bpy.data.objects.remove(obj)
|
||||
|
||||
scene_file = output_file
|
||||
#bpy.ops.ogre.export(filepath=scene_file,
|
||||
# EX_SWAP_AXIS='xz-y',
|
||||
# EX_V2_MESH_TOOL_VERSION='v2',
|
||||
# EX_EXPORT_XML_DELETE=True,
|
||||
# EX_SCENE=True,
|
||||
# EX_SELECTED_ONLY=False,
|
||||
# EX_EXPORT_HIDDEN=False,
|
||||
# EX_FORCE_CAMERA=False,
|
||||
# EX_FORCE_LIGHTS=False,
|
||||
# EX_NODE_ANIMATION=True,
|
||||
# EX_MATERIALS=True,
|
||||
# EX_SEPARATE_MATERIALS=True,
|
||||
# EX_COPY_SHADER_PROGRAMS=True,
|
||||
# EX_MESH=True,
|
||||
# EX_LOD_LEVELS=3,
|
||||
# EX_LOD_DISTANCE=100,
|
||||
# EX_LOD_PERCENT=40
|
||||
#)
|
||||
#
|
||||
bpy.ops.ogre.export(filepath=scene_file,
|
||||
EX_SWAP_AXIS='xz-y',
|
||||
EX_V2_MESH_TOOL_VERSION='v2',
|
||||
EX_EXPORT_XML_DELETE=True,
|
||||
EX_SCENE=True,
|
||||
EX_SELECTED_ONLY=False,
|
||||
EX_EXPORT_HIDDEN=False,
|
||||
EX_FORCE_CAMERA=False,
|
||||
EX_FORCE_LIGHTS=False,
|
||||
EX_NODE_ANIMATION=True,
|
||||
EX_MATERIALS=True,
|
||||
EX_SEPARATE_MATERIALS=True,
|
||||
EX_COPY_SHADER_PROGRAMS=True,
|
||||
EX_MESH=True
|
||||
)
|
||||
|
||||
|
||||
bpy.ops.wm.read_homefile(use_empty=True)
|
||||
time.sleep(2)
|
||||
bpy.ops.wm.quit_blender()
|
||||
@@ -9,7 +9,7 @@ from mathutils import Vector, Matrix
|
||||
from math import radians, pi
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
from settings import ExportMappingFemale, ExportMappingMale, ExportMappingMaleBabyShape, ExportMappingMaleEdited, ExportMappingFemaleEdited
|
||||
from settings import ExportMappingFemale, ExportMappingMale, ExportMappingMaleBabyShape, ExportMappingMaleEdited, ExportMappingFemaleEdited, ExportMappingMaleTestShapeEdited, ExportMappingMaleBaseShapeEdited
|
||||
|
||||
basepath = os.getcwd()
|
||||
def check_bone(bname):
|
||||
@@ -179,7 +179,7 @@ def extra_linear(angle, offset):
|
||||
ret += offt
|
||||
return ret
|
||||
|
||||
for mapping in [ExportMappingFemale(), ExportMappingMale(), ExportMappingMaleBabyShape(), ExportMappingMaleEdited(), ExportMappingFemaleEdited()]:
|
||||
for mapping in [ExportMappingFemale(), ExportMappingMale(), ExportMappingMaleBabyShape(), ExportMappingMaleEdited(), ExportMappingFemaleEdited(), ExportMappingMaleTestShapeEdited(), ExportMappingMaleBaseShapeEdited()]:
|
||||
if not os.path.exists(mapping.blend_path):
|
||||
print("Skipping mapping: " + mapping.blend_path)
|
||||
continue
|
||||
|
||||
@@ -6,7 +6,7 @@ class VRMDataFemale:
|
||||
editfile = "modelling-vroid-normal-female.blend"
|
||||
armature_name = "female"
|
||||
mixamo_animation_path = basepath + "/assets/blender/mixamo/female/"
|
||||
mixamo_animations = ["idle", "walking", "running", "jump", "left_turn", "right_turn", "swimming", "treading_water"]
|
||||
mixamo_animations = ["idle", "walking", "running", "jump", "left_turn", "right_turn", "swimming", "treading_water", "sitting", "hanging-climb", "hanging-idle"]
|
||||
fbx_scale = 0.89
|
||||
class VRMDataMale:
|
||||
path = "buch1.vrm"
|
||||
@@ -14,7 +14,7 @@ class VRMDataMale:
|
||||
editfile = "modelling-vroid-normal-male.blend"
|
||||
armature_name = "male"
|
||||
mixamo_animation_path = basepath + "/assets/blender/mixamo/male/"
|
||||
mixamo_animations = ["idle", "walking", "running", "jump", "left_turn", "right_turn", "swimming", "treading_water"]
|
||||
mixamo_animations = ["idle", "walking", "running", "jump", "left_turn", "right_turn", "swimming", "treading_water", "sitting", "hanging-climb", "hanging-idle"]
|
||||
fbx_scale = 1.0
|
||||
class VRMDataMaleBabyShape:
|
||||
path = "buch1-chibi.vrm"
|
||||
@@ -101,7 +101,35 @@ class ExportMappingMaleBabyShape:
|
||||
gltf_path = "assets/blender/" + "characters/shapes/male/chibi/vroid-normal-male-chibi.gltf"
|
||||
ogre_scene = "characters/shapes/male/chibi/vroid-normal-male-chibi.scene"
|
||||
inner_path = "Object"
|
||||
objs = ["male", "Body", "Hair", "Face"]
|
||||
objs = ["male", "Body"]
|
||||
armature_name = "male"
|
||||
outfile = "tmp-male-chibi.blend"
|
||||
default_action = 'default'
|
||||
def __init__(self):
|
||||
self.files = []
|
||||
for fobj in self.objs:
|
||||
self.files.append({"name": fobj})
|
||||
|
||||
class ExportMappingMaleTestShapeEdited:
|
||||
blend_path = "assets/blender/" + "edited-shape-test-male.blend"
|
||||
gltf_path = "characters/shapes/male/edited-shape-test-male.gltf"
|
||||
ogre_scene = "characters/shapes/male/chibi/edited-shape-test-male.scene"
|
||||
inner_path = "Object"
|
||||
objs = ["male", "Body"]
|
||||
armature_name = "male"
|
||||
outfile = "tmp-male-chibi.blend"
|
||||
default_action = 'default'
|
||||
def __init__(self):
|
||||
self.files = []
|
||||
for fobj in self.objs:
|
||||
self.files.append({"name": fobj})
|
||||
|
||||
class ExportMappingMaleBaseShapeEdited:
|
||||
blend_path = "assets/blender/" + "edited-normal-male-base.blend"
|
||||
gltf_path = "characters/shapes/male/edited-normal-male-base.gltf"
|
||||
ogre_scene = "characters/shapes/male/chibi/edited-normal-male-base.scene"
|
||||
inner_path = "Object"
|
||||
objs = ["male", "Body"]
|
||||
armature_name = "male"
|
||||
outfile = "tmp-male-chibi.blend"
|
||||
default_action = 'default'
|
||||
|
||||
127
assets/blender/scripts/uv_unwrap.py
Normal file
@@ -0,0 +1,127 @@
|
||||
import bpy
|
||||
import bmesh
|
||||
import math
|
||||
import os, sys, time
|
||||
|
||||
argv = sys.argv
|
||||
argv = argv[argv.index("--") + 1:]
|
||||
|
||||
obj_name = argv[0]
|
||||
basepath = os.getcwd()
|
||||
print("Object: " + obj_name)
|
||||
|
||||
target_obj = None
|
||||
for obj in bpy.data.objects:
|
||||
if obj.name == obj_name:
|
||||
target_obj = obj
|
||||
break
|
||||
|
||||
target_obj.select_set(True)
|
||||
bpy.context.view_layer.objects.active = target_obj
|
||||
l = target_obj.data.uv_layers.new(name="UVMapCustom")
|
||||
target_obj.data.uv_layers.active = l
|
||||
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
#bpy.ops.mesh.select_mode(type="FACE")
|
||||
#bpy.ops.mesh.select_all(action='SELECT')
|
||||
#bpy.ops.uv.unwrap(method='UNWRAP', fill_holes=True, correct_aspect=True, use_subsurf_data=False, margin=0.002)
|
||||
## bpy.ops.uv.smart_project(angle_limit=66.0, island_margin=0.0, user_area_weight=0.0, use_aspect=True, stretch_to_bounds=True)
|
||||
# bpy.ops.uv.unwrap()
|
||||
bpy.ops.mesh.select_all(action='SELECT')
|
||||
# bpy.ops.uv.unwrap()
|
||||
bm = bmesh.new()
|
||||
bm = bmesh.from_edit_mesh(obj.data)
|
||||
uv_layer = bm.loops.layers.uv["UVMapCustom"]
|
||||
for f in bm.faces:
|
||||
f.select = True
|
||||
bpy.ops.uv.unwrap(method='ANGLE_BASED', fill_holes=True, correct_aspect=True, margin=0.001)
|
||||
#uv_layer = bm.loops.layers.uv.verify()
|
||||
if False:
|
||||
for f in bm.faces:
|
||||
for l in f.loops:
|
||||
vert = l.vert
|
||||
if vert.co.x >= 0.0:
|
||||
if vert.co.x < 0.225:
|
||||
if vert.co.y >= 0.0:
|
||||
l[uv_layer].uv.x = vert.co.x
|
||||
if vert.co.z < 1.04:
|
||||
#legs
|
||||
px = vert.co.x - 0.1
|
||||
pd = vert.co.y
|
||||
a = (math.atan2(-pd, px) + math.pi) / (2.0 * math.pi)
|
||||
l[uv_layer].uv.y = vert.co.z * 0.4
|
||||
l[uv_layer].uv.x = a * 0.27
|
||||
elif vert.co.z < 1.3477:
|
||||
l[uv_layer].uv.y = vert.co.z * 0.5 - 0.1
|
||||
elif vert.co.z < 1.5497:
|
||||
#chest
|
||||
l[uv_layer].uv.y = vert.co.z * 0.9 - 0.65
|
||||
# elif vert.co.z < 1.5531:
|
||||
# l[uv_layer].uv.y = vert.co.z * 0.99 - 0.8
|
||||
else:
|
||||
# head
|
||||
pd = vert.co.y
|
||||
px = vert.co.x
|
||||
a = (math.atan2(-pd, px) + math.pi) / (2.0 * math.pi)
|
||||
l[uv_layer].uv.y = vert.co.z * 0.8 - 0.47
|
||||
l[uv_layer].uv.x = a * 0.8 - 0.2
|
||||
else:
|
||||
l[uv_layer].uv.x = 0.5 - vert.co.x
|
||||
if vert.co.z < 1.04:
|
||||
#legs
|
||||
px = vert.co.x - 0.1
|
||||
pd = vert.co.y
|
||||
a = (math.atan2(-pd, px) + math.pi) / (2.0 * math.pi)
|
||||
l[uv_layer].uv.y = vert.co.z * 0.4
|
||||
l[uv_layer].uv.x = 0.195 + a * 0.3
|
||||
elif vert.co.z < 1.3477:
|
||||
l[uv_layer].uv.y = vert.co.z * 0.5 - 0.1
|
||||
elif vert.co.z < 1.5497:
|
||||
#chest
|
||||
l[uv_layer].uv.y = vert.co.z * 0.9 - 0.65
|
||||
else:
|
||||
# head
|
||||
pd = vert.co.y
|
||||
px = vert.co.x
|
||||
a = (math.atan2(-pd, px) + math.pi) / (2.0 * math.pi)
|
||||
l[uv_layer].uv.y = vert.co.z * 0.8 - 0.47
|
||||
l[uv_layer].uv.x = -0.2 + a * 0.8
|
||||
else:
|
||||
# arms
|
||||
px = (vert.co.z - 1.395)
|
||||
py = (-vert.co.x + 0.915)
|
||||
pd = vert.co.y
|
||||
a = (math.atan2(-pd, px - 0.049) + math.pi) / (2.0 * math.pi)
|
||||
print(a)
|
||||
l[uv_layer].uv.x = a * 0.14 + 0.18
|
||||
l[uv_layer].uv.y = (-vert.co.x + 0.915) * 0.7 + 0.05
|
||||
else:
|
||||
if vert.co.x < 0.225:
|
||||
#not arms
|
||||
l[uv_layer].uv.x = vert.co.x - 5.0
|
||||
l[uv_layer].uv.y = vert.co.z
|
||||
else:
|
||||
#arms
|
||||
l[uv_layer].uv.x = vert.co.x - 5.0
|
||||
l[uv_layer].uv.y = vert.co.z
|
||||
for f in bm.faces:
|
||||
for l in f.loops:
|
||||
vert = l.vert
|
||||
if vert.co.z < 1.04:
|
||||
#legs
|
||||
px = vert.co.x - 0.1
|
||||
pd = vert.co.y
|
||||
a = (math.atan2(-pd, px) + math.pi) / (2.0 * math.pi)
|
||||
l[uv_layer].uv.y = vert.co.z * 0.4
|
||||
if vert.co.x >= 0.0:
|
||||
l[uv_layer].uv.x = a * 0.27
|
||||
else:
|
||||
l[uv_layer].uv.x = a * 0.27 - 1.0
|
||||
|
||||
bmesh.update_edit_mesh(obj.data)
|
||||
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
bpy.ops.wm.save_as_mainfile(filepath="tmp.blend")
|
||||
|
||||
|
||||
|
||||
BIN
assets/blender/vehicles/boat-big.blend
(Stored with Git LFS)
Normal file
BIN
assets/blender/vehicles/boat-gobbot.blend
(Stored with Git LFS)
Normal file
BIN
assets/blender/vehicles/boat.blend
(Stored with Git LFS)
BIN
assets/blender/vehicles/raft.blend
(Stored with Git LFS)
Normal file
BIN
assets/blender/vehicles/tiny-boat.blend
(Stored with Git LFS)
Normal file
@@ -132,7 +132,7 @@ function Quest(name, book)
|
||||
this.story:begin()
|
||||
this:_narration()
|
||||
end,
|
||||
event = function(this, event)
|
||||
event = function(this, event, event_data)
|
||||
if not this.active then
|
||||
return
|
||||
end
|
||||
@@ -228,13 +228,10 @@ function StartGameQuest()
|
||||
quest.activate = function(this)
|
||||
print('activate...')
|
||||
local mc_is_free = function()
|
||||
this.boat_id = ecs_vehicle_set("boat", 0, 0, -20, 1.75)
|
||||
this.trigger_id = ecs_child_character_trigger(this.boat_id, "entered_boat", 0, 0, 0, 3, 3)
|
||||
this.boat = true
|
||||
local npc = ecs_npc_set("normal-female.glb", 0, 2, -20, 1.75)
|
||||
ecs_character_physics_control(npc, false)
|
||||
ecs_character_params_set("player", "gravity", true)
|
||||
ecs_character_params_set("player", "buoyancy", true)
|
||||
this.boat = true
|
||||
local ent = ecs_get_player_entity()
|
||||
ecs_character("params-set", ent, "gravity", true)
|
||||
ecs_character("params-set", ent, "buoyancy", true)
|
||||
end
|
||||
this.story:bind('mc_is_free', mc_is_free)
|
||||
this.base.activate(this)
|
||||
@@ -248,31 +245,413 @@ function StartGameQuest()
|
||||
end
|
||||
return quest
|
||||
end
|
||||
function BoatControlQuest()
|
||||
-- Parse a book from the Ink file.
|
||||
local book = narrator.parse_file('stories.boat_control')
|
||||
local quest = Quest('boat control', book)
|
||||
quest.base = {}
|
||||
quest.base.activate = quest.activate
|
||||
quest.base.complete = quest.complete
|
||||
quest.boat = false
|
||||
quest.activate = function(this)
|
||||
print('activate...')
|
||||
local ent = ecs_get_player_entity()
|
||||
ecs_set_slot(this.boat.boat_id, ent, "seat1")
|
||||
-- ecs_character_set_actuator(ent, "sitting")
|
||||
ecs_character("set-actuator", ent, "idle")
|
||||
local boat_activated = function()
|
||||
-- local ent = ecs_get_player_entity()
|
||||
-- ecs_character_set_actuator(ent, "idle")
|
||||
-- ecs_character_physics_control(ent, false)
|
||||
-- ecs_character_params_set(ent, "gravity", false)
|
||||
-- ecs_character_params_set(ent, "buoyancy", false)
|
||||
end
|
||||
this.story:bind('boat_activated', boat_activated)
|
||||
this.base.activate(this)
|
||||
end
|
||||
quest.complete = function(this)
|
||||
this.base.complete(this)
|
||||
this.active = false
|
||||
local ent = ecs_get_player_entity()
|
||||
ecs_set_slot(this.boat.boat_id, this.boat.passengers[1], "seat0")
|
||||
ecs_character("set-actuator", ent, "sitting")
|
||||
ecs_set_slot(this.boat.boat_id, ent, "seat1")
|
||||
ecs_character("set-actuator", ent, "sitting")
|
||||
print("SLOT", "captain_seat", "boo1")
|
||||
ecs_set_slot(this.boat.boat_id, ent, "captain_seat")
|
||||
ecs_character("set-actuator", ent, "sitting")
|
||||
end
|
||||
return quest
|
||||
end
|
||||
function create_actuator()
|
||||
return {
|
||||
is_complete = false,
|
||||
complete = function(this)
|
||||
return this.is_complete
|
||||
end,
|
||||
finish = function(this)
|
||||
this.is_complete = true
|
||||
ecs_character("set-actuator", this.entity, "")
|
||||
ecs_character("physics-control", this.entity, true)
|
||||
print("COMPLETE")
|
||||
end,
|
||||
forward = function(this)
|
||||
if (this.forward_animation) then
|
||||
this:animation(this.forward_animation)
|
||||
end
|
||||
end,
|
||||
animation = function(this, animation)
|
||||
print("ANIMATION: ", animation)
|
||||
ecs_character("set-actuator", this.entity, animation)
|
||||
end,
|
||||
event = function(this, event, trigger_entity, what_entity)
|
||||
print("actuator events: ", event)
|
||||
if event == "actuator_forward" then
|
||||
this:forward()
|
||||
return true
|
||||
elseif event == "_in_actuator_forward" then
|
||||
this:forward()
|
||||
return true
|
||||
elseif event == "actuator_exit" then
|
||||
this:finish()
|
||||
return true
|
||||
end
|
||||
if this.finish_events then
|
||||
for i, p in ipairs(this.finish_events) do
|
||||
if p == event then
|
||||
this:finish()
|
||||
break
|
||||
end
|
||||
end
|
||||
if this.is_complete then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end,
|
||||
}
|
||||
end
|
||||
quests = {}
|
||||
local actuator = nil
|
||||
function check_actuator_event(event, trigger_entity, what_entity)
|
||||
print("check_actuator_event: ", event)
|
||||
--[[
|
||||
if event == "actuator_enter" then
|
||||
if not ecs_character("is-player", what_entity) then
|
||||
return
|
||||
end
|
||||
ecs_character("physics-control", what_entity, false)
|
||||
local animation = ecs_trigger_get_animation(trigger_entity)
|
||||
ecs_character("set-actuator", what_entity, animation)
|
||||
ecs_trigger_set_position(trigger_entity, what_entity)
|
||||
local ent = ecs_get_entity(what_entity)
|
||||
if (ent.is_character()) then
|
||||
print("character")
|
||||
end
|
||||
if (ent.is_player()) then
|
||||
print("player")
|
||||
end
|
||||
-- crash()
|
||||
actuator = create_actuator()
|
||||
actuator.trigger = trigger_entity
|
||||
actuator.entity = what_entity
|
||||
actuator.forward = function(this)
|
||||
this:animation("swimming-edge-climb")
|
||||
local ent = ecs_get_player_entity()
|
||||
ecs_character("params-set", ent, "gravity", true)
|
||||
ecs_character("params-set", ent, "buoyancy", true)
|
||||
end
|
||||
actuator.base_event = actuator.event
|
||||
actuator.finish_events = {"animation:swimming-edge-climb:end"}
|
||||
actuator.event = function(this, event, te, we)
|
||||
print("actuator events 1: ", event)
|
||||
if this.base_event(this, event, te, we) then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
]]--
|
||||
if event == "character_enter" then
|
||||
if not ecs_character("is-player", trigger_entity) then
|
||||
return
|
||||
end
|
||||
actuator = create_actuator()
|
||||
actuator.trigger = trigger_entity
|
||||
actuator.entity = trigger_entity
|
||||
actuator.other_entity = what_entity
|
||||
actuator.forward_animation = "pass-character"
|
||||
actuator.finish_events = {"animation:pass-character:end"}
|
||||
actuator.base_event = actuator.event
|
||||
actuator.event = function(this, event, te, we)
|
||||
print("actuator events 2: ", event)
|
||||
if event == "actuator_exit" then
|
||||
this:animation("idle")
|
||||
return true
|
||||
end
|
||||
if this.base_event(this, event, te, we) then
|
||||
return true
|
||||
end
|
||||
if event == "character_enter" then
|
||||
-- why?
|
||||
-- ecs_character_set_actuator(this.entity, "idle")
|
||||
-- ecs_character_set_actuator(this.entity, "idle")
|
||||
return true
|
||||
elseif event == "actuator_enter" then
|
||||
-- why?
|
||||
-- ecs_character_set_actuator(this.entity, "idle")
|
||||
-- ecs_character_set_actuator(this.entity, "idle")
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
actuator:animation("idle")
|
||||
end
|
||||
end
|
||||
-- ecs_set_debug_drawing(true)
|
||||
setup_handler(function(event)
|
||||
|
||||
local vehicles = {}
|
||||
local player_vehicles = {}
|
||||
local actuators = {}
|
||||
|
||||
|
||||
function create_vehicle(vtype, passengers, position)
|
||||
local vehicle = {}
|
||||
vehicle.id = ecs_vehicle_set(vtype, position[1], position[2], position[3], position[4])
|
||||
vehicle.passengers = {}
|
||||
vehicle.actuators = {}
|
||||
vehicle.add_passenger = function(this, data)
|
||||
local npc_id = ecs_npc_set(data.model, data.position[1], data.position[2], data.position[3], data.position[4])
|
||||
table.insert(this.passengers, npc_id)
|
||||
ecs_character("physics-control", npc_id, false)
|
||||
ecs_character("params-set", npc_id, "gravity", false)
|
||||
ecs_character("params-set", npc_id, "buoyancy", false)
|
||||
ecs_set_slot(this.id, npc_id, data.slot)
|
||||
ecs_character("set-actuator", npc_id, data.animation)
|
||||
end
|
||||
for i, v in ipairs(passengers) do
|
||||
vehicle:add_passenger(v)
|
||||
end
|
||||
return vehicle
|
||||
end
|
||||
function create_boat()
|
||||
local boat = create_vehicle("boat", {
|
||||
model = "normal-female.glb",
|
||||
position = {0, 2, -10, 1.75},
|
||||
slot = "captain_seat",
|
||||
animation = "sitting"}, {0, 0, -10, 1.75})
|
||||
--[[
|
||||
boat.id = ecs_vehicle_set("boat", 0, 0, -10, 1.75)
|
||||
local npc_id = ecs_npc_set("normal-female.glb", 0, 2, -10, 1.75)
|
||||
boat.passengers = {npc_id}
|
||||
ecs_character("physics-control", npc_id, false)
|
||||
ecs_character("params-set", npc_id, "gravity", false)
|
||||
ecs_character("params-set", npc_id, "buoyancy", false)
|
||||
print("SLOT", "captain_seat", "boo")
|
||||
ecs_set_slot(boat.boat_id, npc_id, "captain_seat")
|
||||
ecs_character("set-actuator", npc_id, "sitting")
|
||||
-- ecs_set_animation_state(npc_id, "main", "actuator", true)
|
||||
-- ecs_set_animation_state(npc_id, "actuator-state", "sitting", true)
|
||||
]]--
|
||||
boat.event = function(this, event, event_data)
|
||||
print("boat: ", event)
|
||||
if event == "boat_control_enter" then
|
||||
local quest = BoatControlQuest()
|
||||
quest.boat = this
|
||||
quests[quest.name] = quest
|
||||
quest:activate()
|
||||
return true
|
||||
end
|
||||
if event == "boat_control_exit" then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
return boat
|
||||
end
|
||||
function create_actuator2(ent)
|
||||
print("create actuator")
|
||||
local act = {
|
||||
id = ent,
|
||||
activated = false,
|
||||
contained = {},
|
||||
enter = function(this, what)
|
||||
print(this.id, "visited by", what)
|
||||
if not ecs_character("is-player", what) then
|
||||
-- actuators are only for players
|
||||
return
|
||||
end
|
||||
ecs_character("physics-control", what, false)
|
||||
local animation = ecs_trigger_get_animation(this.id)
|
||||
print(animation)
|
||||
ecs_character("set-actuator", what, animation)
|
||||
ecs_trigger_set_position(this.id, what)
|
||||
local ent = ecs_get_entity(what)
|
||||
if (ent.is_character()) then
|
||||
print("character")
|
||||
end
|
||||
if (ent.is_player()) then
|
||||
print("player")
|
||||
end
|
||||
table.insert(this.contained, what)
|
||||
this.activated = true
|
||||
print("position: ", _ecs.position(what).y)
|
||||
end,
|
||||
exit = function(this, what)
|
||||
print(this.id, "left by", what)
|
||||
if this.contained[1] ~= what then
|
||||
crash()
|
||||
end
|
||||
ecs_character("set-actuator", this.contained[1], "")
|
||||
ecs_character("physics-control", this.contained[1], true)
|
||||
this.activated = false
|
||||
print("COMPLETE")
|
||||
end,
|
||||
event = function(this, event, trigger, what)
|
||||
if this.activated then
|
||||
print("!!!", event)
|
||||
if event == "actuator_forward" then
|
||||
this:animation("swimming-edge-climb")
|
||||
local ent = this.contained[1]
|
||||
ecs_character("params-set", ent, "gravity", true)
|
||||
ecs_character("params-set", ent, "buoyancy", true)
|
||||
return true
|
||||
elseif event == "actuator_backward" then
|
||||
return true
|
||||
elseif event == "animation:swimming-edge-climb:end" then
|
||||
this:exit(this.contained[1])
|
||||
return true
|
||||
end
|
||||
|
||||
end
|
||||
return false
|
||||
end,
|
||||
animation = function(this, animation)
|
||||
if this.activated then
|
||||
print("ANIMATION: ", animation)
|
||||
local ent = this.contained[1]
|
||||
ecs_character("set-actuator", ent, animation)
|
||||
end
|
||||
end,
|
||||
}
|
||||
return act
|
||||
end
|
||||
|
||||
function endswith(s, suffix)
|
||||
return string.sub(s, -#suffix) == suffix
|
||||
end
|
||||
|
||||
function startswith(s, prefix)
|
||||
return string.sub(s, 1, #prefix) == prefix
|
||||
end
|
||||
|
||||
|
||||
setup_handler(function(event, trigger_entity, what_entity)
|
||||
print(event)
|
||||
local event_handled = false
|
||||
if startswith(event, "actuator_") and event ~= "actuator_created" then
|
||||
for i, act in ipairs(actuators) do
|
||||
if act.id == trigger_entity then
|
||||
if event == "actuator_enter" then
|
||||
if not act.activated then
|
||||
act:enter(what_entity)
|
||||
event_handled = true
|
||||
break
|
||||
end
|
||||
elseif event == "actuator_exit" then
|
||||
if act.activated then
|
||||
act:exit(what_entity)
|
||||
event_handled = true
|
||||
break
|
||||
end
|
||||
else
|
||||
if act.activated then
|
||||
if act:event(event, trigger_entity, what_entity) then
|
||||
event_handled = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
event_handled = true
|
||||
end
|
||||
end
|
||||
elseif startswith(event, "animation:") and endswith(event, ":end") then
|
||||
for i, act in ipairs(actuators) do
|
||||
if act.activated then
|
||||
if act:event(event, trigger_entity, what_entity) then
|
||||
event_handled = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if event_handled then
|
||||
return
|
||||
end
|
||||
if event == "actuator_enter" or event == "actuator_exit" or event == "actuator_forward" then
|
||||
print("bad event:", event, trigger_entity, what_entity)
|
||||
crash()
|
||||
end
|
||||
|
||||
for k, v in pairs(quests) do
|
||||
if v.active then
|
||||
v:event(event)
|
||||
local event_data = {}
|
||||
event_data.trigger_entity = trigger_entity
|
||||
event_data.object_entity = what_entity
|
||||
v:event(event, event_data)
|
||||
end
|
||||
end
|
||||
for i, vehicle in ipairs(vehicles) do
|
||||
print(i, vehicle)
|
||||
if vehicle.event then
|
||||
local event_data = {}
|
||||
event_data.trigger_entity = trigger_entity
|
||||
event_data.object_entity = what_entity
|
||||
if vehicle:event(event, event_data) then
|
||||
event_handled = true
|
||||
end
|
||||
end
|
||||
end
|
||||
if event_handled then
|
||||
return
|
||||
end
|
||||
if event == "startup" then
|
||||
main_menu()
|
||||
elseif event == "narration_progress" then
|
||||
print("narration progress!")
|
||||
elseif event == "narration_answered" then
|
||||
local answer = narration_get_answer()
|
||||
story:choose(answer)
|
||||
print("answered:", answer)
|
||||
elseif event == "new_game" then
|
||||
ecs_character_params_set("player", "gravity", true)
|
||||
ecs_character_params_set("player", "buoyancy", false)
|
||||
local ent = ecs_get_player_entity()
|
||||
ecs_character("params-set", ent, "gravity", true)
|
||||
ecs_character("params-set", ent, "buoyancy", false)
|
||||
local quest = StartGameQuest()
|
||||
quests[quest.name] = quest
|
||||
for k, v in pairs(quests) do
|
||||
print(k, v.active)
|
||||
end
|
||||
quest:activate()
|
||||
local start_boat = create_boat()
|
||||
table.insert(vehicles, start_boat)
|
||||
table.insert(player_vehicles, start_boat)
|
||||
elseif event == "actuator_created" then
|
||||
print(trigger_entity)
|
||||
local act = create_actuator2(trigger_entity)
|
||||
table.insert(actuators, act)
|
||||
--[[
|
||||
else
|
||||
if not actuator then
|
||||
check_actuator_event(event, trigger_entity, what_entity)
|
||||
else
|
||||
if not actuator:event(event, trigger_entity, what_entity) then
|
||||
crash()
|
||||
end
|
||||
if actuator:complete() then
|
||||
print("ACTUATOR COMPLETE")
|
||||
print("EXIT ACTUATOR")
|
||||
actuator = nil
|
||||
end
|
||||
end
|
||||
--]]
|
||||
end
|
||||
end)
|
||||
|
||||
6
lua-scripts/stories/boat_control.ink
Normal file
@@ -0,0 +1,6 @@
|
||||
- Are we going to set sail?
|
||||
* Yes
|
||||
~ boat_activated()
|
||||
* Not now
|
||||
- ->END
|
||||
|
||||
9
morph/CMakeLists.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
project(morph)
|
||||
add_executable(MorphTargetsResearch MorphTargetsResearch.cpp)
|
||||
target_link_libraries(MorphTargetsResearch OgreBites OgrePaging ${BULLET_DYNAMICS_LIBRARY} ${BULLET_COLLISION_LIBRARY} ${BULLET_MATH_LIBRARY} ${ASSIMP_LIBRARIES}
|
||||
-Wl,--as-needed
|
||||
)
|
||||
if(OGRE_STATIC)
|
||||
target_link_options(MorphTargetsResearch PRIVATE -static-libstdc++ -static-libgcc)
|
||||
endif()
|
||||
add_dependencies(MorphTargetsResearch stage_files import_vrm import_character_shapes)
|
||||
901
morph/MorphTargetsResearch.cpp
Normal file
@@ -0,0 +1,901 @@
|
||||
#include <iostream>
|
||||
#include <Ogre.h>
|
||||
#include <OgreApplicationContext.h>
|
||||
|
||||
class App;
|
||||
struct Vertex2D {
|
||||
float x, y;
|
||||
Ogre::ColourValue color;
|
||||
|
||||
Vertex2D(float x, float y, const Ogre::ColourValue &color)
|
||||
: x(x)
|
||||
, y(y)
|
||||
, color(color)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
static bool calculateBarycentricCoordinates(int px, int py, const Vertex2D &v0,
|
||||
const Vertex2D &v1,
|
||||
const Vertex2D &v2, float &u,
|
||||
float &v, float &w)
|
||||
{
|
||||
float area = 0.5f * ((v1.x - v0.x) * (v2.y - v0.y) -
|
||||
(v2.x - v0.x) * (v1.y - v0.y));
|
||||
if (area == 0)
|
||||
return false; // Degenerate triangle
|
||||
|
||||
u = 0.5f * ((v1.x - px) * (v2.y - py) - (v2.x - px) * (v1.y - py)) /
|
||||
area;
|
||||
v = 0.5f * ((v2.x - px) * (v0.y - py) - (v0.x - px) * (v2.y - py)) /
|
||||
area;
|
||||
w = 1.0f - u - v;
|
||||
|
||||
return u >= 0 && v >= 0 && w >= 0;
|
||||
}
|
||||
|
||||
static void drawColoredTriangle(Ogre::Image &image, const Vertex2D &v0,
|
||||
const Vertex2D &v1, const Vertex2D &v2)
|
||||
{
|
||||
// Determine bounding box of the triangle
|
||||
int minX = std::min({ v0.x, v1.x, v2.x });
|
||||
int minY = std::min({ v0.y, v1.y, v2.y });
|
||||
int maxX = std::max({ v0.x, v1.x, v2.x });
|
||||
int maxY = std::max({ v0.y, v1.y, v2.y });
|
||||
|
||||
// Clamp to image bounds
|
||||
minX = std::max(0, minX);
|
||||
minY = std::max(0, minY);
|
||||
maxX = std::min((int)image.getWidth() - 1, maxX);
|
||||
maxY = std::min((int)image.getHeight() - 1, maxY);
|
||||
|
||||
for (int y = minY; y <= maxY; ++y) {
|
||||
for (int x = minX; x <= maxX; ++x) {
|
||||
float u, v, w;
|
||||
if (calculateBarycentricCoordinates(x, y, v0, v1, v2, u,
|
||||
v, w)) {
|
||||
// Interpolate colors
|
||||
Ogre::ColourValue color = u * v0.color +
|
||||
v * v1.color +
|
||||
w * v2.color;
|
||||
|
||||
image.setColourAt(color, x, y, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void getSubmeshNormals(const Ogre::Mesh *mesh,
|
||||
const Ogre::SubMesh *submesh,
|
||||
std::vector<Ogre::Vector3> &normals)
|
||||
{
|
||||
int j;
|
||||
float *pReal;
|
||||
int vertex_count = 0;
|
||||
if (submesh->useSharedVertices)
|
||||
vertex_count += mesh->sharedVertexData->vertexCount;
|
||||
else
|
||||
vertex_count += submesh->vertexData->vertexCount;
|
||||
Ogre::HardwareVertexBufferSharedPtr vbuf;
|
||||
Ogre::VertexData *vertex_data = submesh->useSharedVertices ?
|
||||
mesh->sharedVertexData :
|
||||
submesh->vertexData;
|
||||
const Ogre::VertexElement *normalsElem =
|
||||
vertex_data->vertexDeclaration->findElementBySemantic(
|
||||
Ogre::VES_NORMAL);
|
||||
if (!normalsElem)
|
||||
return;
|
||||
OgreAssert(normals.size() == 0 || normals.size() == vertex_count,
|
||||
"bad vertex count");
|
||||
normals.resize(vertex_count);
|
||||
vbuf = vertex_data->vertexBufferBinding->getBuffer(
|
||||
normalsElem->getSource());
|
||||
unsigned char *vertex = static_cast<unsigned char *>(
|
||||
vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
|
||||
for (j = 0; j < vertex_data->vertexCount;
|
||||
++j, vertex += vbuf->getVertexSize()) {
|
||||
normalsElem->baseVertexPointerToElement(vertex, &pReal);
|
||||
normals[j] = Ogre::Vector3(pReal[0], pReal[1], pReal[2]);
|
||||
}
|
||||
vbuf->unlock();
|
||||
}
|
||||
static void getSubmeshUVs(const Ogre::Mesh *mesh, const Ogre::SubMesh *submesh,
|
||||
std::vector<Ogre::Vector2> &uvs, int index)
|
||||
{
|
||||
int j;
|
||||
float *pReal;
|
||||
uvs.resize(0);
|
||||
Ogre::HardwareVertexBufferSharedPtr vbuf;
|
||||
Ogre::VertexData *vertex_data = submesh->useSharedVertices ?
|
||||
mesh->sharedVertexData :
|
||||
submesh->vertexData;
|
||||
const Ogre::VertexElement *uvElem =
|
||||
vertex_data->vertexDeclaration->findElementBySemantic(
|
||||
Ogre::VES_TEXTURE_COORDINATES, index);
|
||||
int vertex_count = vertex_data->vertexCount;
|
||||
if (!uvElem)
|
||||
return;
|
||||
OgreAssert(uvs.size() == 0 || uvs.size() == vertex_count,
|
||||
"bad vertex count");
|
||||
uvs.resize(vertex_count);
|
||||
vbuf = vertex_data->vertexBufferBinding->getBuffer(uvElem->getSource());
|
||||
unsigned char *uv = static_cast<unsigned char *>(
|
||||
vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
|
||||
for (j = 0; j < vertex_data->vertexCount; ++j) {
|
||||
uvElem->baseVertexPointerToElement(uv, &pReal);
|
||||
uvs[j] = Ogre::Vector2(pReal[0], pReal[1]);
|
||||
std::cout << "buffer UV: " << index << ": " << j << " " << uvs[j] << " " << (void *)pReal << " " << (void *)uv << std::endl;
|
||||
uv += vbuf->getVertexSize();
|
||||
}
|
||||
vbuf->unlock();
|
||||
}
|
||||
static void getSubmeshVertices(const Ogre::Mesh *mesh,
|
||||
const Ogre::SubMesh *submesh,
|
||||
std::vector<Ogre::Vector3> &vertices)
|
||||
{
|
||||
int j;
|
||||
float *pReal;
|
||||
int vertex_count = 0;
|
||||
if (submesh->useSharedVertices)
|
||||
vertex_count += mesh->sharedVertexData->vertexCount;
|
||||
else
|
||||
vertex_count += submesh->vertexData->vertexCount;
|
||||
Ogre::HardwareVertexBufferSharedPtr vbuf;
|
||||
Ogre::VertexData *vertex_data = submesh->useSharedVertices ?
|
||||
mesh->sharedVertexData :
|
||||
submesh->vertexData;
|
||||
const Ogre::VertexElement *posElem =
|
||||
vertex_data->vertexDeclaration->findElementBySemantic(
|
||||
Ogre::VES_POSITION);
|
||||
if (!posElem)
|
||||
return;
|
||||
OgreAssert(vertices.size() == 0 || vertices.size() == vertex_count,
|
||||
"bad vertex count");
|
||||
vertices.resize(vertex_count);
|
||||
vbuf = vertex_data->vertexBufferBinding->getBuffer(
|
||||
posElem->getSource());
|
||||
unsigned char *vertex = static_cast<unsigned char *>(
|
||||
vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
|
||||
for (j = 0; j < vertex_data->vertexCount;
|
||||
++j, vertex += vbuf->getVertexSize()) {
|
||||
posElem->baseVertexPointerToElement(vertex, &pReal);
|
||||
vertices[j] = Ogre::Vector3(pReal[0], pReal[1], pReal[2]);
|
||||
}
|
||||
vbuf->unlock();
|
||||
}
|
||||
static void getSubmeshIndices(const Ogre::Mesh *mesh,
|
||||
const Ogre::SubMesh *submesh,
|
||||
std::vector<unsigned long> &indices)
|
||||
{
|
||||
int index_count = 0;
|
||||
index_count += submesh->indexData->indexCount;
|
||||
int index_offset = 0;
|
||||
indices.resize(index_count);
|
||||
Ogre::IndexData *index_data = submesh->indexData;
|
||||
size_t numTris = index_data->indexCount / 3;
|
||||
Ogre::HardwareIndexBufferSharedPtr ibuf = index_data->indexBuffer;
|
||||
|
||||
bool use32bitindexes =
|
||||
(ibuf->getType() == Ogre::HardwareIndexBuffer::IT_32BIT);
|
||||
|
||||
unsigned long *pLong = static_cast<unsigned long *>(
|
||||
ibuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
|
||||
unsigned short *pShort = reinterpret_cast<unsigned short *>(pLong);
|
||||
|
||||
size_t offset = 0;
|
||||
|
||||
if (use32bitindexes) {
|
||||
for (size_t k = 0; k < numTris * 3; ++k) {
|
||||
indices[index_offset++] =
|
||||
pLong[k] + static_cast<unsigned long>(offset);
|
||||
}
|
||||
} else {
|
||||
for (size_t k = 0; k < numTris * 3; ++k) {
|
||||
indices[index_offset++] =
|
||||
static_cast<unsigned long>(pShort[k]) +
|
||||
static_cast<unsigned long>(offset);
|
||||
}
|
||||
}
|
||||
|
||||
ibuf->unlock();
|
||||
}
|
||||
struct SubMeshInformation {
|
||||
Ogre::String materialName;
|
||||
bool sharedVertices;
|
||||
std::vector<Ogre::Vector3> vertices;
|
||||
std::vector<Ogre::Vector3> normals;
|
||||
std::vector<Ogre::Vector2> uvs;
|
||||
std::vector<Ogre::Vector2> uv2s;
|
||||
std::vector<unsigned long> indices;
|
||||
Ogre::SubMesh::LODFaceList lodFaceList;
|
||||
Ogre::Mesh::VertexBoneAssignmentList boneList;
|
||||
void createSubmesh(Ogre::Mesh *mesh)
|
||||
{
|
||||
int i;
|
||||
Ogre::SubMesh *out = mesh->createSubMesh();
|
||||
out->useSharedVertices = false;
|
||||
out->vertexData = new Ogre::VertexData();
|
||||
size_t currOffset = 0;
|
||||
Ogre::VertexDeclaration *vertexDecl =
|
||||
out->vertexData->vertexDeclaration;
|
||||
vertexDecl->addElement(0, currOffset, Ogre::VET_FLOAT3,
|
||||
Ogre::VES_POSITION);
|
||||
currOffset +=
|
||||
Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);
|
||||
vertexDecl->addElement(0, currOffset, Ogre::VET_FLOAT3,
|
||||
Ogre::VES_NORMAL);
|
||||
currOffset +=
|
||||
Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);
|
||||
vertexDecl->addElement(0, currOffset, Ogre::VET_FLOAT2,
|
||||
Ogre::VES_TEXTURE_COORDINATES, 0);
|
||||
currOffset +=
|
||||
Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2);
|
||||
out->vertexData->vertexCount = vertices.size();
|
||||
Ogre::HardwareVertexBufferSharedPtr vbuf =
|
||||
Ogre::HardwareBufferManager::getSingleton()
|
||||
.createVertexBuffer(
|
||||
vertexDecl->getVertexSize(0),
|
||||
out->vertexData->vertexCount,
|
||||
Ogre::HardwareBuffer::
|
||||
HBU_STATIC_WRITE_ONLY, // only GPU side
|
||||
false);
|
||||
Ogre::VertexBufferBinding *binding =
|
||||
out->vertexData->vertexBufferBinding;
|
||||
binding->setBinding(0, vbuf);
|
||||
float *pvertex = static_cast<float *>(
|
||||
vbuf->lock(Ogre::HardwareBuffer::HBL_DISCARD));
|
||||
out->indexData->indexCount = indices.size();
|
||||
out->indexData->indexBuffer =
|
||||
Ogre::HardwareBufferManager::getSingleton().createIndexBuffer(
|
||||
vertices.size() < 32768 ?
|
||||
Ogre::HardwareIndexBuffer::IT_16BIT :
|
||||
Ogre::HardwareIndexBuffer::IT_32BIT,
|
||||
out->indexData->indexCount,
|
||||
Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY,
|
||||
false);
|
||||
OgreAssert(normals.size() == vertices.size(), "bad normals");
|
||||
for (i = 0; i < vertices.size(); i++) {
|
||||
*pvertex++ = vertices[i].x;
|
||||
*pvertex++ = vertices[i].y;
|
||||
*pvertex++ = vertices[i].z;
|
||||
if (normals.size() > 0) {
|
||||
*pvertex++ = normals[i].x;
|
||||
*pvertex++ = normals[i].y;
|
||||
*pvertex++ = normals[i].z;
|
||||
} else {
|
||||
*pvertex++ = 0.0f;
|
||||
*pvertex++ = 1.0f;
|
||||
*pvertex++ = 0.0f;
|
||||
}
|
||||
if (uvs.size() > 0) {
|
||||
*pvertex++ = uvs[i].x;
|
||||
*pvertex++ = uvs[i].y;
|
||||
} else {
|
||||
*pvertex++ = 0.0f;
|
||||
*pvertex++ = 1.0f;
|
||||
}
|
||||
}
|
||||
vbuf->unlock();
|
||||
Ogre::HardwareIndexBufferSharedPtr ibuf =
|
||||
out->indexData->indexBuffer;
|
||||
if (vertices.size() < 32768) {
|
||||
unsigned short *pindices =
|
||||
static_cast<unsigned short *>(ibuf->lock(
|
||||
Ogre::HardwareBuffer::HBL_DISCARD));
|
||||
for (i = 0; i < indices.size(); i++)
|
||||
*pindices++ = (unsigned short)indices[i];
|
||||
ibuf->unlock();
|
||||
} else {
|
||||
unsigned long *pindices = static_cast<unsigned long *>(
|
||||
ibuf->lock(Ogre::HardwareBuffer::HBL_DISCARD));
|
||||
for (i = 0; i < indices.size(); i++)
|
||||
*pindices++ = indices[i];
|
||||
ibuf->unlock();
|
||||
}
|
||||
out->clearBoneAssignments();
|
||||
auto vbass_it = boneList.begin();
|
||||
while (vbass_it != boneList.end()) {
|
||||
out->addBoneAssignment(vbass_it->second);
|
||||
vbass_it++;
|
||||
}
|
||||
out->setMaterialName(materialName);
|
||||
}
|
||||
};
|
||||
|
||||
static void getSubMeshInformation(const Ogre::Mesh *mesh,
|
||||
const Ogre::SubMesh *submesh,
|
||||
struct SubMeshInformation *info)
|
||||
{
|
||||
int i;
|
||||
info->materialName = submesh->getMaterialName();
|
||||
std::cout << "material name: " << info->materialName << std::endl;
|
||||
info->sharedVertices = submesh->useSharedVertices;
|
||||
info->lodFaceList = submesh->mLodFaceList;
|
||||
info->boneList = submesh->getBoneAssignments();
|
||||
getSubmeshIndices(mesh, submesh, info->indices);
|
||||
getSubmeshVertices(mesh, submesh, info->vertices);
|
||||
getSubmeshNormals(mesh, submesh, info->normals);
|
||||
getSubmeshUVs(mesh, submesh, info->uvs, 0);
|
||||
std::cout << "UV0: " << info->uvs.size() << std::endl;
|
||||
getSubmeshUVs(mesh, submesh, info->uv2s, 1);
|
||||
std::cout << "UV1: " << info->uv2s.size() << std::endl;
|
||||
for (i = 0; i < info->uvs.size(); i++)
|
||||
std::cout << "UV0: " << i << " " << info->uvs[i] << std::endl;
|
||||
for (i = 0; i < info->uv2s.size(); i++)
|
||||
std::cout << "UV1: " << i << " " << info->uv2s[i] << std::endl;
|
||||
}
|
||||
|
||||
struct MeshMorphTarget {
|
||||
Ogre::SkeletonPtr skelp;
|
||||
Ogre::Image image;
|
||||
struct DataChange {
|
||||
int index;
|
||||
int submesh;
|
||||
Ogre::Vector3 vertex;
|
||||
Ogre::Vector3 normal;
|
||||
};
|
||||
std::vector<DataChange> vertices;
|
||||
float weight;
|
||||
};
|
||||
struct MeshInformation {
|
||||
std::map<Ogre::String, struct MeshMorphTarget> targets;
|
||||
std::vector<struct SubMeshInformation> submesh;
|
||||
Ogre::SkeletonPtr skelp;
|
||||
Ogre::String skelName;
|
||||
MeshInformation(Ogre::Mesh *prototype)
|
||||
{
|
||||
int i;
|
||||
skelName = prototype->getSkeletonName();
|
||||
skelp = Ogre::SkeletonManager::getSingleton().getByName(
|
||||
prototype->getSkeletonName(), "Characters");
|
||||
OgreAssert(skelp, "Could not load skeleton " +
|
||||
prototype->getSkeletonName());
|
||||
submesh.resize(prototype->getNumSubMeshes());
|
||||
for (i = 0; i < submesh.size(); i++) {
|
||||
getSubMeshInformation(prototype,
|
||||
prototype->getSubMesh(i),
|
||||
&submesh[i]);
|
||||
}
|
||||
}
|
||||
Ogre::MeshPtr create(const Ogre::String &meshName)
|
||||
{
|
||||
int j;
|
||||
Ogre::MeshPtr baseMesh =
|
||||
Ogre::MeshManager::getSingleton().createManual(
|
||||
meshName, Ogre::ResourceGroupManager::
|
||||
DEFAULT_RESOURCE_GROUP_NAME);
|
||||
applyMorphTargets();
|
||||
for (j = 0; j < submesh.size(); j++)
|
||||
submesh[j].createSubmesh(baseMesh.get());
|
||||
//define a extreme boundary values
|
||||
Ogre::Real max_x = -1e+8;
|
||||
Ogre::Real min_x = 1e+8;
|
||||
Ogre::Real max_y = -1e+8;
|
||||
Ogre::Real min_y = 1e+8;
|
||||
Ogre::Real max_z = -1e+8;
|
||||
Ogre::Real min_z = +1e+8;
|
||||
// Setting bounding box
|
||||
Ogre::Mesh::SubMeshList mlist = baseMesh->getSubMeshes();
|
||||
for (j = 0; j < mlist.size(); j++) {
|
||||
Ogre::SubMesh *in = mlist[j];
|
||||
Ogre::VertexData *vertex_data = in->vertexData;
|
||||
const Ogre::VertexElement *posElem =
|
||||
vertex_data->vertexDeclaration
|
||||
->findElementBySemantic(
|
||||
Ogre::VES_POSITION);
|
||||
Ogre::HardwareVertexBufferSharedPtr hwvb =
|
||||
in->vertexData->vertexBufferBinding->getBuffer(
|
||||
posElem->getSource());
|
||||
unsigned char *hbuff =
|
||||
static_cast<unsigned char *>(hwvb->lock(
|
||||
Ogre::HardwareBuffer::HBL_READ_ONLY));
|
||||
Ogre::Real *pValue;
|
||||
Ogre::Real value;
|
||||
|
||||
for (size_t idx = 0; idx < vertex_data->vertexCount;
|
||||
++idx, hbuff += hwvb->getVertexSize()) {
|
||||
posElem->baseVertexPointerToElement(hbuff,
|
||||
&pValue);
|
||||
value = (*pValue++);
|
||||
if (value > max_x)
|
||||
max_x = value;
|
||||
if (value < min_x)
|
||||
min_x = value;
|
||||
value = (*pValue++);
|
||||
|
||||
if (value > max_y)
|
||||
max_y = value;
|
||||
if (value < min_y)
|
||||
min_y = value;
|
||||
value = (*pValue++);
|
||||
|
||||
if (value > max_z)
|
||||
max_z = value;
|
||||
if (value < min_z)
|
||||
min_z = value;
|
||||
}
|
||||
hwvb->unlock();
|
||||
}
|
||||
baseMesh->setSkeletonName(skelName);
|
||||
baseMesh->_setBounds(Ogre::AxisAlignedBox(min_x, min_y, min_z,
|
||||
max_x, max_y, max_z));
|
||||
return baseMesh;
|
||||
}
|
||||
std::pair<int, int> findVertex(const Ogre::Vector2 &uv)
|
||||
{
|
||||
int i, j;
|
||||
float d = 1.5f;
|
||||
int ret_submesh = -1, ret_index = -1;
|
||||
for (i = 0; i < submesh.size(); i++) {
|
||||
if (submesh[i].uv2s.size() != submesh[i].normals.size())
|
||||
continue;
|
||||
for (j = 0; j < submesh[i].uv2s.size(); j++) {
|
||||
float l =
|
||||
submesh[i].uv2s[j].squaredDistance(uv);
|
||||
if (l < d) {
|
||||
d = l;
|
||||
ret_submesh = i;
|
||||
ret_index = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
OgreAssert(ret_submesh >= 0 && ret_index >= 0,
|
||||
"Vertex not found");
|
||||
return { ret_submesh, ret_index };
|
||||
}
|
||||
void createTarget(const Ogre::String &targetName,
|
||||
const Ogre::Mesh *target)
|
||||
{
|
||||
MeshMorphTarget mtarget;
|
||||
int i, j, k, l;
|
||||
std::unordered_map<int, SubMeshInformation> targetInfo;
|
||||
mtarget.skelp = Ogre::SkeletonManager::getSingleton().getByName(
|
||||
target->getSkeletonName(), "Characters");
|
||||
int count = 0;
|
||||
for (i = 0; i < target->getNumSubMeshes(); i++) {
|
||||
SubMeshInformation info;
|
||||
getSubMeshInformation(target, target->getSubMesh(i),
|
||||
&info);
|
||||
std::cout << "-- processed submesh: " << i << std::endl;
|
||||
std::cout << "-- processed submesh: "
|
||||
<< info.materialName << std::endl;
|
||||
if (info.uv2s.size() == 0)
|
||||
continue;
|
||||
targetInfo[i] = info;
|
||||
count++;
|
||||
}
|
||||
OgreAssert(count > 0, "Bad target mesh count");
|
||||
struct VertexIndex {
|
||||
int base_submesh;
|
||||
int base_index;
|
||||
};
|
||||
std::vector<VertexIndex> indices;
|
||||
Ogre::Image image(Ogre::PF_R8G8B8A8, 512, 512);
|
||||
Ogre::Image image_normals(Ogre::PF_R8G8B8A8, 512, 512);
|
||||
float vertexMul = 0.0f, normalMul = 0.0f;
|
||||
struct DeltaData {
|
||||
std::vector<Ogre::Vector3> vertexDeltas;
|
||||
std::vector<Ogre::Vector3> normalDeltas;
|
||||
std::vector<Ogre::Vector2> uvs;
|
||||
std::vector<unsigned long> indices;
|
||||
};
|
||||
std::vector<DeltaData> deltas;
|
||||
std::cout << "target start " << targetName << "\n";
|
||||
deltas.reserve(targetInfo.size());
|
||||
for (i = 0; i < targetInfo.size(); i++) {
|
||||
if (targetInfo.find(i) == targetInfo.end())
|
||||
continue;
|
||||
std::cout << "submesh: " << i << std::endl;
|
||||
OgreAssert(targetInfo[i].uv2s.size() == targetInfo[i].normals.size(), "bad UV2 count");
|
||||
OgreAssert(targetInfo[i].vertices.size() > 0, "bad vertex count");
|
||||
struct DeltaData dd;
|
||||
dd.vertexDeltas.resize(targetInfo[i].vertices.size());
|
||||
dd.normalDeltas.resize(targetInfo[i].vertices.size());
|
||||
dd.uvs.resize(targetInfo[i].vertices.size());
|
||||
std::cout
|
||||
<< "\tvertices: " << targetInfo[i].vertices.size()
|
||||
<< std::endl;
|
||||
for (j = 0; j < targetInfo[i].vertices.size(); j++) {
|
||||
const Ogre::Vector2 &target_uv =
|
||||
targetInfo[i].uv2s[j];
|
||||
auto ti = findVertex(target_uv);
|
||||
const Ogre::Vector3 &base_v =
|
||||
submesh[ti.first].vertices[ti.second];
|
||||
const Ogre::Vector3 &target_v =
|
||||
targetInfo[i].vertices[j];
|
||||
const Ogre::Vector3 &base_n =
|
||||
submesh[ti.first].normals[ti.second];
|
||||
const Ogre::Vector3 &target_n =
|
||||
targetInfo[i].normals[j];
|
||||
Ogre::Vector3 vertexDelta = target_v - base_v;
|
||||
Ogre::Vector3 normalDelta = target_n - base_n;
|
||||
dd.vertexDeltas[j] = vertexDelta;
|
||||
dd.normalDeltas[j] = normalDelta;
|
||||
dd.uvs[j] = target_uv;
|
||||
std::cout << "UV: " << j << " " << target_uv << " " << ti.second << std::endl;
|
||||
float lv = vertexDelta.length();
|
||||
if (vertexMul < lv)
|
||||
vertexMul = lv;
|
||||
float ln = normalDelta.length();
|
||||
if (normalMul < ln)
|
||||
normalMul = ln;
|
||||
}
|
||||
|
||||
dd.indices = targetInfo[i].indices;
|
||||
std::cout << "indices: " << dd.indices.size() << std::endl;
|
||||
deltas.push_back(dd);
|
||||
}
|
||||
std::cout << "multipliers: " << vertexMul << " " << normalMul
|
||||
<< "\n";
|
||||
OgreAssert(vertexMul > 0.0f && normalMul > 0.0f,
|
||||
"bad multipliers");
|
||||
// normalMul = 1.0f;
|
||||
// vertexMul = 1.0f;
|
||||
for (i = 0; i < deltas.size(); i++) {
|
||||
OgreAssert(deltas[i].normalDeltas.size() == deltas[i].vertexDeltas.size(), "bad delta count");
|
||||
for (j = 0; j < deltas[i].vertexDeltas.size(); j++) {
|
||||
Ogre::Vector3 d =
|
||||
deltas[i].normalDeltas[j] / normalMul *
|
||||
0.5f +
|
||||
Ogre::Vector3(0.5f, 0.5f, 0.5f);
|
||||
d.x = Ogre::Math::Clamp(d.x, 0.0f, 1.0f);
|
||||
d.y = Ogre::Math::Clamp(d.y, 0.0f, 1.0f);
|
||||
d.z = Ogre::Math::Clamp(d.z, 0.0f, 1.0f);
|
||||
deltas[i].normalDeltas[j] = d;
|
||||
d =
|
||||
deltas[i].vertexDeltas[j] / vertexMul *
|
||||
0.5f +
|
||||
Ogre::Vector3(0.5f, 0.5f, 0.5f);
|
||||
d.x = Ogre::Math::Clamp(d.x, 0.0f, 1.0f);
|
||||
d.y = Ogre::Math::Clamp(d.y, 0.0f, 1.0f);
|
||||
d.z = Ogre::Math::Clamp(d.z, 0.0f, 1.0f);
|
||||
deltas[i].vertexDeltas[j] = d;
|
||||
std::cout << " vertex delta: " << deltas[i].vertexDeltas[j];
|
||||
std::cout << " normal delta: " << deltas[i].normalDeltas[j];
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
image.setTo(Ogre::ColourValue(0.5, 0.5, 0.5, 1));
|
||||
image_normals.setTo(Ogre::ColourValue(0.5, 0.5, 0.5, 1));
|
||||
for (i = 0; i < deltas.size(); i++) {
|
||||
for (j = 0; j < deltas[i].indices.size(); j += 3) {
|
||||
int i1 = (int)deltas[i].indices[j];
|
||||
int i2 = (int)deltas[i].indices[j + 1];
|
||||
int i3 = (int)deltas[i].indices[j + 2];
|
||||
Ogre::Vector2 u0 = deltas[i].uvs[i1];
|
||||
Ogre::Vector2 u1 = deltas[i].uvs[i2];
|
||||
Ogre::Vector2 u2 = deltas[i].uvs[i3];
|
||||
Ogre::ColourValue c0(
|
||||
deltas[i].vertexDeltas[i1].x,
|
||||
deltas[i].vertexDeltas[i1].y,
|
||||
deltas[i].vertexDeltas[i1].z, 1.0f);
|
||||
Ogre::ColourValue c1(
|
||||
deltas[i].vertexDeltas[i2].x,
|
||||
deltas[i].vertexDeltas[i2].y,
|
||||
deltas[i].vertexDeltas[i2].z, 1.0f);
|
||||
Ogre::ColourValue c2(
|
||||
deltas[i].vertexDeltas[i3].x,
|
||||
deltas[i].vertexDeltas[i3].y,
|
||||
deltas[i].vertexDeltas[i3].z, 1.0f);
|
||||
Ogre::ColourValue x0(
|
||||
deltas[i].normalDeltas[i1].x,
|
||||
deltas[i].normalDeltas[i1].y,
|
||||
deltas[i].normalDeltas[i1].z, 1.0f);
|
||||
Ogre::ColourValue x1(
|
||||
deltas[i].normalDeltas[i2].x,
|
||||
deltas[i].normalDeltas[i2].y,
|
||||
deltas[i].normalDeltas[i2].z, 1.0f);
|
||||
Ogre::ColourValue x2(
|
||||
deltas[i].normalDeltas[i3].x,
|
||||
deltas[i].normalDeltas[i3].y,
|
||||
deltas[i].normalDeltas[i3].z, 1.0f);
|
||||
Vertex2D v0(u0.x * 512, u0.y * 512, c0);
|
||||
Vertex2D v1(u1.x * 512, u1.y * 512, c1);
|
||||
Vertex2D v2(u2.x * 512, u2.y * 512, c2);
|
||||
Vertex2D xv0(u0.x * 512, u0.y * 512, x0);
|
||||
Vertex2D xv1(u1.x * 512, u1.y * 512, x1);
|
||||
Vertex2D xv2(u2.x * 512, u2.y * 512, x2);
|
||||
std::cout << "triangle: ";
|
||||
std::cout << i1 << " " << i2 << " " << i3 << " ";
|
||||
std::cout << u0 << " " << u1 << " " << u2 << " ";
|
||||
std::cout << "(" << v0.x << ", " << v0.y << ") ";
|
||||
std::cout << "(" << v1.x << ", " << v1.y << ") ";
|
||||
std::cout << "(" << v2.x << ", " << v2.y << ") ";
|
||||
std::cout << std::endl;
|
||||
// SoftwareRenderer2D::drawTriangle(image, v0, v1, v2);
|
||||
drawColoredTriangle(image, v0, v1, v2);
|
||||
drawColoredTriangle(image_normals, xv0, xv1,
|
||||
xv2);
|
||||
}
|
||||
}
|
||||
std::cout << "output image\n";
|
||||
image.save(targetName + "_debug.png");
|
||||
image_normals.save(targetName + "_normals_debug.png");
|
||||
std::vector<Ogre::Vector3> vertices;
|
||||
std::vector<Ogre::Vector3> normals;
|
||||
std::vector<Ogre::Vector2> uvs;
|
||||
std::vector<int> uv_submeshes;
|
||||
std::vector<int> uv_indices;
|
||||
std::vector<Ogre::Vector2> change_uvs;
|
||||
std::vector<MeshMorphTarget::DataChange> changes;
|
||||
for (i = 0; i < targetInfo.size(); i++) {
|
||||
if (targetInfo[i].uv2s.size() !=
|
||||
targetInfo[i].normals.size())
|
||||
continue;
|
||||
OgreAssert(targetInfo[i].normals.size() ==
|
||||
targetInfo[i].uv2s.size(),
|
||||
"UV2 missing");
|
||||
const std::vector<Ogre::Vector2> &puv =
|
||||
targetInfo[i].uv2s;
|
||||
const std::vector<Ogre::Vector3> &pvertices =
|
||||
targetInfo[i].vertices;
|
||||
const std::vector<Ogre::Vector3> &pnormals =
|
||||
targetInfo[i].normals;
|
||||
for (j = 0; j < pvertices.size(); j++) {
|
||||
MeshMorphTarget::DataChange dc;
|
||||
dc.index = -1;
|
||||
dc.submesh = -1;
|
||||
change_uvs.push_back(puv[j]);
|
||||
dc.normal = pnormals[j];
|
||||
dc.vertex = pvertices[j];
|
||||
changes.push_back(dc);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < submesh.size(); i++) {
|
||||
int submesh_id = i;
|
||||
if (submesh[i].uv2s.size() != submesh[i].normals.size())
|
||||
continue;
|
||||
for (j = 0; j < submesh[i].vertices.size(); j++) {
|
||||
int index_id = j;
|
||||
const Ogre::Vector2 &xuv = submesh[i].uv2s[j];
|
||||
uvs.push_back(xuv);
|
||||
uv_submeshes.push_back(submesh_id);
|
||||
uv_indices.push_back(index_id);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < changes.size(); i++) {
|
||||
float l = 1.0f;
|
||||
int sub = -1, index = -1;
|
||||
for (j = 0; j < uvs.size(); j++) {
|
||||
float xl =
|
||||
change_uvs[i].squaredDistance(uvs[j]);
|
||||
if (xl < l) {
|
||||
l = xl;
|
||||
sub = uv_submeshes[j];
|
||||
index = uv_indices[j];
|
||||
}
|
||||
}
|
||||
OgreAssert(sub >= 0 && index >= 0, "Indexing error");
|
||||
if (l < 0.000000005f) {
|
||||
changes[i].submesh = sub;
|
||||
changes[i].index = index;
|
||||
}
|
||||
}
|
||||
mtarget.vertices = changes;
|
||||
|
||||
mtarget.weight = 0.0f;
|
||||
targets[targetName] = mtarget;
|
||||
}
|
||||
void setTargetWeight(const Ogre::String &targetName, float weight)
|
||||
{
|
||||
if (targets.find(targetName) == targets.end())
|
||||
return;
|
||||
targets[targetName].weight = weight;
|
||||
}
|
||||
void applyMorphTargets()
|
||||
{
|
||||
auto it = targets.begin();
|
||||
float sum = 0.0f;
|
||||
while (it != targets.end()) {
|
||||
sum += it->second.weight;
|
||||
it++;
|
||||
}
|
||||
if (sum > 1.0f) {
|
||||
it = targets.begin();
|
||||
while (it != targets.end()) {
|
||||
it->second.weight = it->second.weight / sum;
|
||||
it++;
|
||||
}
|
||||
}
|
||||
it = targets.begin();
|
||||
while (it != targets.end()) {
|
||||
if (it->second.weight > 0.001f) {
|
||||
int i;
|
||||
for (i = 0; i < it->second.vertices.size();
|
||||
i++) {
|
||||
int sub_id =
|
||||
it->second.vertices[i].submesh;
|
||||
int index_id =
|
||||
it->second.vertices[i].index;
|
||||
if (sub_id < 0 || index_id < 0)
|
||||
continue;
|
||||
Ogre::Vector3 orig_vertex =
|
||||
submesh[sub_id]
|
||||
.vertices[index_id];
|
||||
Ogre::Vector3 new_vertex =
|
||||
it->second.vertices[i].vertex;
|
||||
Ogre::Vector3 vertex_update =
|
||||
Ogre::Math::lerp(
|
||||
orig_vertex, new_vertex,
|
||||
it->second.weight);
|
||||
Ogre::Vector3 orig_normal =
|
||||
submesh[sub_id]
|
||||
.normals[index_id];
|
||||
Ogre::Vector3 new_normal =
|
||||
it->second.vertices[i].normal;
|
||||
Ogre::Vector3 normal_update =
|
||||
Ogre::Math::lerp(
|
||||
orig_normal, new_normal,
|
||||
it->second.weight);
|
||||
submesh[sub_id].vertices[index_id] =
|
||||
vertex_update;
|
||||
submesh[sub_id].normals[index_id] =
|
||||
normal_update;
|
||||
}
|
||||
}
|
||||
it++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct SimpleListener : public OgreBites::InputListener {
|
||||
App *mApp;
|
||||
SimpleListener(App *app)
|
||||
: mApp(app)
|
||||
{
|
||||
}
|
||||
void frameRendered(const Ogre::FrameEvent &evt) override;
|
||||
};
|
||||
|
||||
class App : public OgreBites::ApplicationContext {
|
||||
public:
|
||||
App()
|
||||
: OgreBites::ApplicationContext("MorphTargetsResearch")
|
||||
{
|
||||
}
|
||||
void locateResources() override
|
||||
{
|
||||
Ogre::ResourceGroupManager::getSingleton().createResourceGroup(
|
||||
"Characters", true);
|
||||
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
|
||||
"./characters", "FileSystem", "Characters", true, true);
|
||||
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
|
||||
"../characters", "FileSystem", "Characters", true,
|
||||
true);
|
||||
OgreBites::ApplicationContext::locateResources();
|
||||
}
|
||||
void loadResources() override
|
||||
{
|
||||
}
|
||||
Ogre::MeshPtr loadMesh(Ogre::String meshName)
|
||||
{
|
||||
Ogre::MeshPtr ret = Ogre::MeshManager::getSingleton().load(
|
||||
meshName, "Characters");
|
||||
return ret;
|
||||
}
|
||||
void meshProcessing()
|
||||
{
|
||||
int i;
|
||||
//code to test and create entity based off of sub-meshes
|
||||
|
||||
//vector of meshes
|
||||
std::vector<Ogre::MeshPtr> m_Meshes;
|
||||
|
||||
//define meshes
|
||||
// Ogre::MeshPtr base = Ogre::MeshManager::getSingleton().load(
|
||||
// "male/normal-male.glb", "Characters");
|
||||
Ogre::MeshPtr base = loadMesh("shapes/male/edited-normal-male-base.glb");
|
||||
m_Meshes.push_back(base);
|
||||
Ogre::MeshPtr shape = loadMesh("shapes/male/edited-shape-test-male.glb");
|
||||
OgreAssert(shape, "no shape");
|
||||
MeshInformation meshinfo(base.get());
|
||||
std::string m_ModelId = "MergedMesh";
|
||||
meshinfo.createTarget("shape", shape.get());
|
||||
meshinfo.setTargetWeight("shape", 1.0f);
|
||||
Ogre::MeshPtr m_BaseMesh = meshinfo.create(m_ModelId);
|
||||
Ogre::Entity *ent =
|
||||
mScnMgr->createEntity("entName", m_BaseMesh->getName());
|
||||
// ent->setMaterialName("TestMat");
|
||||
|
||||
//code here is temporary and only ment as a test for the modelers
|
||||
//Entity* testEntity = mSceneMgr->createEntity("cc", source->getName());
|
||||
//testEntity->setMaterialName("TestMat");
|
||||
Ogre::SceneNode *thisSceneNode =
|
||||
mScnMgr->getRootSceneNode()->createChildSceneNode();
|
||||
// thisSceneNode->setPosition(100, 100, 100);
|
||||
thisSceneNode->attachObject(ent);
|
||||
#if 0
|
||||
mIdle = ent->getAnimationState("idle");
|
||||
mIdle->setEnabled(true);
|
||||
mIdle->setLoop(true);
|
||||
mIdle->setWeight(1.0f);
|
||||
#endif
|
||||
}
|
||||
|
||||
void setup() override
|
||||
{
|
||||
OgreBites::ApplicationContext::setup();
|
||||
Ogre::Root *root = getRoot();
|
||||
Ogre::SceneManager *scnMgr = root->createSceneManager();
|
||||
Ogre::RTShader::ShaderGenerator *shadergen =
|
||||
Ogre::RTShader::ShaderGenerator::getSingletonPtr();
|
||||
shadergen->addSceneManager(scnMgr);
|
||||
// also need to tell where we are
|
||||
mScnMgr.reset(scnMgr);
|
||||
mCameraNode.reset(
|
||||
mScnMgr->getRootSceneNode()->createChildSceneNode());
|
||||
mCameraNode->setPosition(0, 2, 3);
|
||||
mCameraNode->lookAt(Ogre::Vector3(0, 1, -1),
|
||||
Ogre::Node::TS_PARENT);
|
||||
|
||||
Ogre::Light *light = mScnMgr->createLight("MainLight");
|
||||
Ogre::SceneNode *lightNode =
|
||||
mScnMgr->getRootSceneNode()->createChildSceneNode();
|
||||
// lightNode->setPosition(0, 10, 15);
|
||||
lightNode->setDirection(
|
||||
Ogre::Vector3(0.55, -0.3, 0.75).normalisedCopy());
|
||||
lightNode->attachObject(light);
|
||||
light->setType(Ogre::Light::LT_DIRECTIONAL);
|
||||
light->setDiffuseColour(Ogre::ColourValue::White);
|
||||
light->setSpecularColour(Ogre::ColourValue(0.4, 0.4, 0.4));
|
||||
|
||||
mScnMgr->setAmbientLight(
|
||||
Ogre::ColourValue(0.5f, 0.5f, 0.5f, 1.0f));
|
||||
|
||||
// create the camera
|
||||
Ogre::Camera *cam = mScnMgr->createCamera("normal_camera");
|
||||
cam->setNearClipDistance(0.1f); // specific to this sample
|
||||
cam->setAutoAspectRatio(true);
|
||||
mCameraNode->attachObject(cam);
|
||||
mCamera.reset(cam);
|
||||
|
||||
// and tell it to render into the main window
|
||||
getRenderWindow()->addViewport(cam);
|
||||
meshProcessing();
|
||||
#if 0
|
||||
Ogre::Entity *ent_base =
|
||||
mScnMgr->createEntity("male/normal-male.glb");
|
||||
Ogre::Entity *ent_shape = mScnMgr->createEntity(
|
||||
"shapes/male/chibi/vroid-normal-male-chibi.glb");
|
||||
Ogre::SceneNode *characterNode =
|
||||
mScnMgr->getRootSceneNode()->createChildSceneNode("ch");
|
||||
characterNode->attachObject(ent_base);
|
||||
#endif
|
||||
addInputListener(OGRE_NEW SimpleListener(this));
|
||||
Ogre::Image image(Ogre::PF_R8G8B8A8, 1024, 1024);
|
||||
image.setTo(Ogre::ColourValue(0, 0, 0, 1));
|
||||
Vertex2D v0(0.0f, 0.0f, Ogre::ColourValue(1, 0, 0, 1));
|
||||
Vertex2D v1(500.0f, 100.0f, Ogre::ColourValue(0, 1, 0, 1));
|
||||
Vertex2D v2(0.0f, 500.0f, Ogre::ColourValue(0, 0, 1, 1));
|
||||
// SoftwareRenderer2D::drawTriangle(image, v0, v1, v2);
|
||||
drawColoredTriangle(image, v0, v1, v2);
|
||||
image.save("checkup.png");
|
||||
}
|
||||
void animationTime(float time)
|
||||
{
|
||||
mIdle->addTime(time);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ogre::SceneNode> mCameraNode;
|
||||
std::unique_ptr<Ogre::Camera> mCamera;
|
||||
std::unique_ptr<Ogre::SceneManager> mScnMgr;
|
||||
Ogre::AnimationState *mIdle;
|
||||
};
|
||||
|
||||
void SimpleListener::frameRendered(const Ogre::FrameEvent &evt)
|
||||
{
|
||||
mApp->animationTime(evt.timeSinceLastFrame);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
App ctx;
|
||||
ctx.initApp();
|
||||
ctx.setWindowGrab(true);
|
||||
|
||||
ctx.getRoot()->startRendering();
|
||||
ctx.setWindowGrab(false);
|
||||
ctx.closeApp();
|
||||
return 0;
|
||||
}
|
||||
BIN
sims4-t1.png
Normal file
|
After Width: | Height: | Size: 569 KiB |
BIN
sims4-t2.png
Normal file
|
After Width: | Height: | Size: 708 KiB |
BIN
sims4-t3.png
Normal file
|
After Width: | Height: | Size: 840 KiB |
BIN
sims4-t4.png
Normal file
|
After Width: | Height: | Size: 569 KiB |
BIN
sims4-uv1-1.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
sims4-uv1-2.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
sims4-uv1-3.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
@@ -1,5 +0,0 @@
|
||||
project(characters)
|
||||
find_package(OGRE REQUIRED COMPONENTS Bites Bullet Paging Terrain CONFIG)
|
||||
# add_library(controller STATIC controller.cpp)
|
||||
# target_link_libraries(controller PUBLIC OgreMain OgreBites PRIVATE GameData)
|
||||
# target_include_directories(controller PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
@@ -1,369 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <Ogre.h>
|
||||
#include <OgreBullet.h>
|
||||
#include "BulletCollision/CollisionDispatch/btGhostObject.h"
|
||||
#include "LinearMath/btTransform.h"
|
||||
#include "character.h"
|
||||
|
||||
#define RUN_SPEED 17 // character running speed in units per second
|
||||
#define TURN_SPEED 500.0f // character turning in degrees per second
|
||||
#define ANIM_FADE_SPEED \
|
||||
7.5f // animation crossfade speed in % of full weight per second
|
||||
|
||||
Character::Character(Ogre::SceneManager *scnMgr, const Ogre::String &model,
|
||||
Ogre::Bullet::DynamicsWorld *world)
|
||||
: mModelName(model)
|
||||
, mScnMgr(scnMgr)
|
||||
, mPivotPitch(0)
|
||||
, mVerticalVelocity(0)
|
||||
, mAnimID(ANIM_NONE)
|
||||
, mRunning(false)
|
||||
, mCollisionShape(nullptr)
|
||||
, mGhostObject(nullptr)
|
||||
, mWorld(world)
|
||||
, mGoalDirection(0, 0, 0)
|
||||
, mUpdate(false)
|
||||
{
|
||||
setupBody();
|
||||
setupAnimations();
|
||||
}
|
||||
|
||||
Character::~Character()
|
||||
{
|
||||
}
|
||||
|
||||
bool Character::frameStarted(const Ogre::FrameEvent &evt)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Character::frameRenderingQueued(const Ogre::FrameEvent &evt)
|
||||
{
|
||||
if (mUpdate) {
|
||||
updateBody(evt.timeSinceLastFrame);
|
||||
updateAnimations(evt.timeSinceLastFrame);
|
||||
if (evt.timeSinceLastFrame > 0)
|
||||
updateRootMotion(evt.timeSinceLastFrame);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Character::frameEnded(const Ogre::FrameEvent &evt)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void Character::setupBody()
|
||||
{
|
||||
mBodyEnt = mScnMgr->createEntity(mModelName);
|
||||
mBodyNode = mScnMgr->getRootSceneNode()->createChildSceneNode();
|
||||
mBodyNode->attachObject(mBodyEnt);
|
||||
mSkeleton = mBodyEnt->getSkeleton();
|
||||
// mRigidBody = world->addCharacter(mBodyEnt, 0);
|
||||
// mCollisionShape = static_cast<btCompoundShape *>(mRigidBody->getCollisionShape());
|
||||
mGhostObject = new btPairCachingGhostObject();
|
||||
mCollisionShape = new btCompoundShape;
|
||||
mGhostObject->setCollisionShape(mCollisionShape);
|
||||
|
||||
{
|
||||
btVector3 inertia(0, 0, 0);
|
||||
// mCollisionShape = new btCompoundShape();
|
||||
btScalar height = 1.0f;
|
||||
btScalar radius = 0.3f;
|
||||
btCapsuleShape *shape =
|
||||
new btCapsuleShape(radius, 2 * height - 2 * radius);
|
||||
btTransform transform;
|
||||
transform.setIdentity();
|
||||
transform.setOrigin(btVector3(0, 1, 0));
|
||||
static_cast<btCompoundShape *>(mCollisionShape)
|
||||
->addChildShape(transform, shape);
|
||||
btScalar masses[1] = { 0 };
|
||||
btTransform principal;
|
||||
static_cast<btCompoundShape *>(mCollisionShape)
|
||||
->calculatePrincipalAxisTransform(masses, principal,
|
||||
inertia);
|
||||
}
|
||||
mGhostObject->setCollisionFlags(
|
||||
btCollisionObject::CF_KINEMATIC_OBJECT /* |
|
||||
btCollisionObject::CF_NO_CONTACT_RESPONSE */);
|
||||
mGhostObject->setActivationState(DISABLE_DEACTIVATION);
|
||||
Ogre::Bullet::KinematicMotionSimple *controller =
|
||||
new Ogre::Bullet::KinematicMotionSimple(mGhostObject,
|
||||
mBodyNode);
|
||||
mWorld->attachCollisionObject(mGhostObject, mBodyEnt,
|
||||
btBroadphaseProxy::AllFilter,
|
||||
btBroadphaseProxy::AllFilter);
|
||||
mWorld->getBtWorld()->addAction(controller);
|
||||
|
||||
assert(mCollisionShape);
|
||||
#if 0
|
||||
if (mRigidBody->getMass() == 0) {
|
||||
#if 0
|
||||
mRigidBody->setCollisionFlags(mRigidBody->getCollisionFlags()
|
||||
| btCollisionObject::CF_KINEMATIC_OBJECT
|
||||
| btCollisionObject::CF_NO_CONTACT_RESPONSE
|
||||
);
|
||||
#endif
|
||||
#if 0
|
||||
mGhostObject->setWorldTransform(mRigidBody->getWorldTransform());
|
||||
WorldData::get_singleton()->getBtWorld()
|
||||
->getBroadphase()->getOverlappingPairCache()
|
||||
->setInternalGhostPairCallback(new btGhostPairCallback());
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#if 0
|
||||
mRigidBody->setActivationState(DISABLE_DEACTIVATION);
|
||||
#endif
|
||||
#if 0
|
||||
{
|
||||
Ogre::Entity *e2 = mScnMgr->createEntity("normal-male.glb");
|
||||
Ogre::SceneNode *e2node = mScnMgr->getRootSceneNode()->createChildSceneNode();
|
||||
e2node->attachObject(e2);
|
||||
mGhostObject = WorldData::get_singleton()->addGhostObject(e2, mCollisionShape);
|
||||
mController = new btKinematicCharacterController(mGhostObject, mCollisionShape, 0.5f);
|
||||
WorldData::get_singleton()->getBtWorld()->addAction(mController);
|
||||
}
|
||||
#endif
|
||||
assert(mSkeleton->hasBone("Root"));
|
||||
mRootBone = mSkeleton->getBone("Root");
|
||||
assert(mRootBone);
|
||||
}
|
||||
void Character::setupAnimations()
|
||||
{
|
||||
int i, j;
|
||||
mSkeleton->setBlendMode(Ogre::ANIMBLEND_CUMULATIVE);
|
||||
Ogre::String animNames[NUM_ANIMS] = { "idle", "walking", "running" };
|
||||
for (i = 0; i < NUM_ANIMS; i++) {
|
||||
mAnims[i] = mBodyEnt->getAnimationState(animNames[i]);
|
||||
mAnims[i]->setLoop(true);
|
||||
mAnims[i]->setEnabled(true);
|
||||
mAnims[i]->setWeight(0);
|
||||
mFadingIn[i] = false;
|
||||
mFadingOut[i] = false;
|
||||
mSkelAnimations[i] = mSkeleton->getAnimation(animNames[i]);
|
||||
for (const auto &it : mSkelAnimations[i]->_getNodeTrackList()) {
|
||||
Ogre::NodeAnimationTrack *track = it.second;
|
||||
Ogre::String trackName =
|
||||
track->getAssociatedNode()->getName();
|
||||
if (trackName == "mixamorig:Hips") {
|
||||
mHipsTracks[i] = track;
|
||||
} else if (trackName == "Root") {
|
||||
mRootTracks[i] = track;
|
||||
// mRootTracks[i]->removeAllKeyFrames();
|
||||
}
|
||||
}
|
||||
Ogre::Vector3 delta = Ogre::Vector3::ZERO;
|
||||
Ogre::Vector3 motion = Ogre::Vector3::ZERO;
|
||||
for (j = 0; j < mRootTracks[i]->getNumKeyFrames(); j++) {
|
||||
Ogre::Vector3 trans = mRootTracks[i]
|
||||
->getNodeKeyFrame(j)
|
||||
->getTranslate();
|
||||
if (j == 0)
|
||||
delta = trans;
|
||||
else
|
||||
delta = trans - motion;
|
||||
mRootTracks[i]->getNodeKeyFrame(j)->setTranslate(delta);
|
||||
motion = trans;
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
for(i = 0; i < NUM_ANIMS - 1; i++) {
|
||||
// need to cache
|
||||
int j;
|
||||
Ogre::String animName = mAnims[i]->getAnimationName();
|
||||
Ogre::Animation *anim = mSkeleton->getAnimation(animName);
|
||||
Ogre::NodeAnimationTrack *hips_track = nullptr, *root_track = nullptr;
|
||||
Ogre::Node *root_node = nullptr;
|
||||
for (const auto& it : anim->_getNodeTrackList()) {
|
||||
Ogre::NodeAnimationTrack* track = it.second;
|
||||
Ogre::String trackName = track->getAssociatedNode()->getName();
|
||||
std::cout << animName << " track: " << trackName << "\n";
|
||||
if (trackName == "mixamorig:Hips")
|
||||
hips_track = track;
|
||||
else if (trackName == "Root") {
|
||||
root_track = track;
|
||||
root_node = track->getAssociatedNode();
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
root_track->removeAllKeyFrames();
|
||||
std::cout << hips_track << " " << root_track << "\n";
|
||||
std::cout << hips_track->getNumKeyFrames() << " " << root_track->getNumKeyFrames() << "\n";
|
||||
assert(hips_track && root_track);
|
||||
Ogre::Vector3 delta = Ogre::Vector3::ZERO;
|
||||
for(j = 0; j < hips_track->getNumKeyFrames(); j++) {
|
||||
float timePos = hips_track->getNodeKeyFrame(j)->getTime();
|
||||
Ogre::Vector3 trans = hips_track->getNodeKeyFrame(j)->getTranslate();
|
||||
Ogre::Vector3 hips_trans(0, 0, 0);
|
||||
Ogre::Vector3 root_trans(0, 0, 0);
|
||||
hips_track->getNodeKeyFrame(j)->setTranslate(hips_trans);
|
||||
Ogre::TransformKeyFrame *nk = root_track->createNodeKeyFrame(timePos);
|
||||
nk->setTranslate(root_trans - delta);
|
||||
nk->setScale(Ogre::Vector3(1, 1, 1));
|
||||
nk->setRotation(Ogre::Quaternion());
|
||||
std::cout << animName << " delta: " << j << " " << timePos << " " << root_trans - delta << "\n";
|
||||
delta = root_trans;
|
||||
}
|
||||
for(j = 0; j < root_track->getNumKeyFrames(); j++) {
|
||||
float timePos = hips_track->getNodeKeyFrame(j)->getTime();
|
||||
Ogre::Vector3 root_trans = hips_track->getNodeKeyFrame(j)->getTranslate();
|
||||
std::cout << animName << " delta: root: " << j << " " << timePos << " " << root_trans << "\n";
|
||||
}
|
||||
}
|
||||
// assert(false);
|
||||
#endif
|
||||
setAnimation(ANIM_IDLE);
|
||||
}
|
||||
void Character::updateBody(Ogre::Real delta)
|
||||
{
|
||||
Ogre::Quaternion toGoal =
|
||||
mBodyNode->getOrientation().zAxis().getRotationTo(
|
||||
mGoalDirection);
|
||||
// calculate how much the character has to turn to face goal direction
|
||||
Ogre::Real yawToGoal = toGoal.getYaw().valueDegrees();
|
||||
// this is how much the character CAN turn this frame
|
||||
Ogre::Real yawAtSpeed =
|
||||
yawToGoal / Ogre::Math::Abs(yawToGoal) * delta * TURN_SPEED;
|
||||
// reduce "turnability" if we're in midair
|
||||
// if (mBaseAnimID == ANIM_JUMP_LOOP) yawAtSpeed *= 0.2f;
|
||||
if (yawToGoal < 0)
|
||||
yawToGoal = std::min<Ogre::Real>(
|
||||
0,
|
||||
std::max<Ogre::Real>(
|
||||
yawToGoal,
|
||||
yawAtSpeed)); //yawToGoal = Math::Clamp<Real>(yawToGoal, yawAtSpeed, 0);
|
||||
else if (yawToGoal > 0)
|
||||
yawToGoal = std::max<Ogre::Real>(
|
||||
0,
|
||||
std::min<Ogre::Real>(
|
||||
yawToGoal,
|
||||
yawAtSpeed)); //yawToGoal = Math::Clamp<Real>(yawToGoal, 0, yawAtSpeed);
|
||||
mBodyNode->yaw(Ogre::Degree(yawToGoal));
|
||||
}
|
||||
void Character::updateAnimations(Ogre::Real delta)
|
||||
{
|
||||
int i, j, k;
|
||||
Ogre::Real animSpeed = 1;
|
||||
mTimer += delta;
|
||||
{
|
||||
Ogre::Quaternion rot = mBodyNode->getOrientation();
|
||||
OgreAssert(!Ogre::Math::isNaN(rot.x), "NaN");
|
||||
OgreAssert(!Ogre::Math::isNaN(rot.y), "NaN");
|
||||
OgreAssert(!Ogre::Math::isNaN(rot.z), "NaN");
|
||||
}
|
||||
if (mAnimID != ANIM_NONE) {
|
||||
if (mAnimID == ANIM_WALK)
|
||||
mAnims[mAnimID]->addTime(delta * 1.0f);
|
||||
else
|
||||
mAnims[mAnimID]->addTime(delta * animSpeed);
|
||||
}
|
||||
fadeAnimations(delta);
|
||||
}
|
||||
void Character::updateRootMotion(Ogre::Real delta)
|
||||
{
|
||||
Ogre::Vector3 boneMotion = mRootBone->getPosition();
|
||||
OgreAssert(delta > 0.0f, "Zero delta");
|
||||
Ogre::Vector3 motion = boneMotion - rootMotion;
|
||||
if (motion.squaredLength() > 0.1f * 0.1f)
|
||||
motion = Ogre::Vector3();
|
||||
rootMotion = boneMotion;
|
||||
#if 0
|
||||
float mass = mRigidBody->getMass();
|
||||
std::cout << "Root bone position: " << boneMotion << "\n";
|
||||
std::cout << "body mass: " << mass << "\n";
|
||||
#endif
|
||||
/* Kinematic motion */
|
||||
Ogre::Quaternion rot = mBodyNode->getOrientation();
|
||||
// Ogre::Vector3 gravity(0, -9.8, 0);
|
||||
Ogre::Vector3 gravity(0, 0, 0);
|
||||
Ogre::Vector3 velocity = rot * boneMotion / delta;
|
||||
velocity += gravity * delta;
|
||||
Ogre::Vector3 rotMotion = velocity * delta;
|
||||
btTransform from(convert(mBodyNode->getOrientation()),
|
||||
convert(mBodyNode->getPosition()));
|
||||
mBodyNode->setPosition(mBodyNode->getPosition() + rotMotion);
|
||||
// WorldData::get_singleton()->getWorld()->testBodyMotion(mRigidBody, from, Ogre::Bullet::convert(rotMotion), true,
|
||||
// nullptr, false, std::set<btCollisionObject *>());
|
||||
}
|
||||
void Character::fadeAnimations(Ogre::Real delta)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < NUM_ANIMS; i++) {
|
||||
if (mFadingIn[i]) {
|
||||
// slowly fade this animation in until it has full weight
|
||||
Ogre::Real newWeight = mAnims[i]->getWeight() +
|
||||
delta * ANIM_FADE_SPEED;
|
||||
mAnims[i]->setWeight(
|
||||
Ogre::Math::Clamp<Ogre::Real>(newWeight, 0, 1));
|
||||
if (newWeight >= 1)
|
||||
mFadingIn[i] = false;
|
||||
} else if (mFadingOut[i]) {
|
||||
// slowly fade this animation out until it has no weight, and then disable it
|
||||
Ogre::Real newWeight = mAnims[i]->getWeight() -
|
||||
delta * ANIM_FADE_SPEED;
|
||||
mAnims[i]->setWeight(
|
||||
Ogre::Math::Clamp<Ogre::Real>(newWeight, 0, 1));
|
||||
if (newWeight <= 0) {
|
||||
mAnims[i]->setEnabled(false);
|
||||
mFadingOut[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void Character::setAnimation(AnimID id, bool reset)
|
||||
{
|
||||
assert(id >= 0 && id < NUM_ANIMS);
|
||||
if (mAnimID != ANIM_NONE) {
|
||||
mFadingIn[mAnimID] = false;
|
||||
mFadingOut[mAnimID] = true;
|
||||
}
|
||||
mAnimID = id;
|
||||
if (id != ANIM_NONE) {
|
||||
mAnims[id]->setEnabled(true);
|
||||
mAnims[id]->setWeight(0);
|
||||
mFadingOut[id] = false;
|
||||
mFadingIn[id] = true;
|
||||
if (reset)
|
||||
mAnims[id]->setTimePosition(0);
|
||||
}
|
||||
}
|
||||
|
||||
bool Character::act_run()
|
||||
{
|
||||
if (mAnimID == ANIM_IDLE)
|
||||
setAnimation(ANIM_RUN, true);
|
||||
else if (mAnimID == ANIM_WALK)
|
||||
setAnimation(ANIM_RUN);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Character::act_walk()
|
||||
{
|
||||
if (mAnimID == ANIM_IDLE)
|
||||
setAnimation(ANIM_WALK, true);
|
||||
else if (mAnimID == ANIM_RUN)
|
||||
setAnimation(ANIM_WALK);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Character::act_idle()
|
||||
{
|
||||
setAnimation(ANIM_IDLE);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Character::isRunning()
|
||||
{
|
||||
return mAnimID == ANIM_RUN;
|
||||
}
|
||||
|
||||
bool Character::isWalking()
|
||||
{
|
||||
return mAnimID == ANIM_WALK;
|
||||
}
|
||||
|
||||
Ogre::Vector3 Character::getPosition()
|
||||
{
|
||||
return mBodyNode->_getDerivedPosition();
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
#include <Ogre.h>
|
||||
#include <OgreFrameListener.h>
|
||||
#include <OgreBullet.h>
|
||||
class btCompoundShape;
|
||||
class btPairCachingGhostObject;
|
||||
class Character : public Ogre::FrameListener {
|
||||
enum AnimID {
|
||||
ANIM_IDLE = 0,
|
||||
ANIM_WALK,
|
||||
ANIM_RUN,
|
||||
NUM_ANIMS,
|
||||
ANIM_NONE = NUM_ANIMS
|
||||
};
|
||||
Ogre::String mModelName;
|
||||
Ogre::Node *mRootBone;
|
||||
Ogre::SceneManager *mScnMgr;
|
||||
|
||||
Ogre::SceneNode *mCameraPivot;
|
||||
Ogre::SceneNode *mCameraGoal, *mBodyNode;
|
||||
Ogre::Entity *mBodyEnt;
|
||||
Ogre::Real mPivotPitch;
|
||||
Ogre::Real mVerticalVelocity;
|
||||
Ogre::Vector3 mGoalDirection; // actual intended direction in world-space
|
||||
Ogre::AnimationState *mAnims[NUM_ANIMS]; // master animation list
|
||||
Ogre::Animation *mSkelAnimations[NUM_ANIMS];
|
||||
Ogre::NodeAnimationTrack *mHipsTracks[NUM_ANIMS];
|
||||
Ogre::NodeAnimationTrack *mRootTracks[NUM_ANIMS];
|
||||
AnimID mAnimID;
|
||||
bool mFadingIn[NUM_ANIMS]; // which animations are fading in
|
||||
bool mFadingOut[NUM_ANIMS]; // which animations are fading out
|
||||
Ogre::Real
|
||||
mTimer; // general timer to see how long animations have been playing
|
||||
Ogre::Skeleton *mSkeleton;
|
||||
bool mRunning;
|
||||
Ogre::Vector3 rootMotion;
|
||||
Ogre::Quaternion rootRotation;
|
||||
// btRigidBody *mRigidBody;
|
||||
btCompoundShape *mCollisionShape;
|
||||
btPairCachingGhostObject *mGhostObject;
|
||||
Ogre::Bullet::DynamicsWorld *mWorld;
|
||||
bool mUpdate;
|
||||
|
||||
public:
|
||||
Character(Ogre::SceneManager *scnMgr, const Ogre::String &modelName,
|
||||
Ogre::Bullet::DynamicsWorld *world);
|
||||
~Character();
|
||||
|
||||
private:
|
||||
void setupBody();
|
||||
void setupAnimations();
|
||||
|
||||
public:
|
||||
bool frameStarted(const Ogre::FrameEvent &evt) override;
|
||||
bool frameEnded(const Ogre::FrameEvent &evt) override;
|
||||
bool frameRenderingQueued(const Ogre::FrameEvent &evt) override;
|
||||
|
||||
private:
|
||||
void updateBody(Ogre::Real deltaTime);
|
||||
void updateAnimations(Ogre::Real deltaTime);
|
||||
void updateRootMotion(Ogre::Real deltaTime);
|
||||
void fadeAnimations(Ogre::Real deltaTime);
|
||||
void setAnimation(AnimID id, bool reset = false);
|
||||
inline btQuaternion convert(const Ogre::Quaternion &q)
|
||||
{
|
||||
return btQuaternion(q.x, q.y, q.z, q.w);
|
||||
}
|
||||
inline btVector3 convert(const Ogre::Vector3 &v)
|
||||
{
|
||||
return btVector3(v.x, v.y, v.z);
|
||||
}
|
||||
inline btTransform convert(const Ogre::Quaternion &q,
|
||||
const Ogre::Vector3 &v)
|
||||
{
|
||||
btQuaternion mq = convert(q);
|
||||
btVector3 mv = convert(v);
|
||||
return btTransform(mq, mv);
|
||||
}
|
||||
inline Ogre::Quaternion convert(const btQuaternion &q)
|
||||
{
|
||||
return Ogre::Quaternion(q.w(), q.x(), q.y(), q.z());
|
||||
}
|
||||
inline Ogre::Vector3 convert(const btVector3 &v)
|
||||
{
|
||||
return Ogre::Vector3(v.x(), v.y(), v.z());
|
||||
}
|
||||
inline void convert(const btTransform &from, Ogre::Quaternion &q,
|
||||
Ogre::Vector3 &v)
|
||||
{
|
||||
q = convert(from.getRotation());
|
||||
v = convert(from.getOrigin());
|
||||
}
|
||||
|
||||
public:
|
||||
bool isIdle()
|
||||
{
|
||||
return mAnimID == ANIM_IDLE;
|
||||
}
|
||||
bool act_run();
|
||||
bool act_walk();
|
||||
bool act_idle();
|
||||
bool isRunning();
|
||||
bool isWalking();
|
||||
bool isMoving()
|
||||
{
|
||||
return isRunning() || isWalking();
|
||||
}
|
||||
void setGoalDirection(const Ogre::Vector3 &goalDirection)
|
||||
{
|
||||
mGoalDirection = goalDirection;
|
||||
}
|
||||
Ogre::Vector3 getGoalDirection()
|
||||
{
|
||||
return mGoalDirection;
|
||||
}
|
||||
Ogre::Vector3 getPosition();
|
||||
void enableUpdates()
|
||||
{
|
||||
mUpdate = true;
|
||||
}
|
||||
void disableUpdates()
|
||||
{
|
||||
mUpdate = false;
|
||||
}
|
||||
bool getUpdates()
|
||||
{
|
||||
return mUpdate;
|
||||
}
|
||||
};
|
||||
15
src/gamedata/AppModule.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "AppModule.h"
|
||||
namespace ECS
|
||||
{
|
||||
AppModule::AppModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.module<AppModule>();
|
||||
ecs.component<App>()
|
||||
.on_add([](App &app) {
|
||||
app.mInput = nullptr;
|
||||
app.mGuiOverlay = nullptr;
|
||||
app.listeners.clear();
|
||||
})
|
||||
.add(flecs::Singleton);
|
||||
}
|
||||
}
|
||||
25
src/gamedata/AppModule.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef _APP_MODULE_H_
|
||||
#define _APP_MODULE_H_
|
||||
#include <flecs.h>
|
||||
#include <vector>
|
||||
namespace Ogre
|
||||
{
|
||||
class ImGuiOverlay;
|
||||
}
|
||||
namespace OgreBites
|
||||
{
|
||||
class InputListenerChain;
|
||||
class InputListener;
|
||||
}
|
||||
namespace ECS
|
||||
{
|
||||
struct App {
|
||||
Ogre::ImGuiOverlay *mGuiOverlay;
|
||||
OgreBites::InputListenerChain *mInput;
|
||||
std::vector<OgreBites::InputListener *> listeners;
|
||||
};
|
||||
struct AppModule {
|
||||
AppModule(flecs::world &ecs);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
@@ -1,7 +1,15 @@
|
||||
#include <iostream>
|
||||
#include <Ogre.h>
|
||||
#include <OgreMeshManager.h>
|
||||
#include <pugixml.hpp>
|
||||
#include "loader.h"
|
||||
#include "Components.h"
|
||||
#include "GameData.h"
|
||||
#include "PhysicsModule.h"
|
||||
#include "EventModule.h"
|
||||
#include "EventTriggerModule.h"
|
||||
#include "CharacterModule.h"
|
||||
#include "WaterModule.h"
|
||||
#include "BoatModule.h"
|
||||
|
||||
namespace ECS
|
||||
@@ -11,13 +19,293 @@ BoatModule::BoatModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.module<BoatModule>();
|
||||
ecs.component<BoatBase>();
|
||||
ecs.component<BoatBody>();
|
||||
ecs.system<const EngineData, BoatType>("CreateBoat")
|
||||
ecs.component<BoatType>();
|
||||
ecs.component<SpawnBoat>();
|
||||
ecs.component<BoatCurrentActuator>();
|
||||
ecs.import <EventModule>();
|
||||
ecs.import <EventTriggerModule>();
|
||||
ecs.import <CharacterModule>();
|
||||
ecs.import <WaterModule>();
|
||||
ecs.observer<const EngineData, const BoatType>("CreateBoat")
|
||||
.event(flecs::OnSet)
|
||||
.without<BoatBase>()
|
||||
.without<SpawnBoat>()
|
||||
.each([&](flecs::entity e, const EngineData &eng,
|
||||
const BoatType &type) {
|
||||
e.add<SpawnBoat>();
|
||||
e.set<EventData>({});
|
||||
});
|
||||
ecs.system<const EngineData, const BoatType>("CreateBoatS")
|
||||
.kind(flecs::OnUpdate)
|
||||
.without<BoatBase>()
|
||||
.with<WaterReady>()
|
||||
.with<SpawnBoat>()
|
||||
.each([&](flecs::entity e, const EngineData &eng,
|
||||
const BoatType &type) {
|
||||
if (type.resourceName.find(".scene") !=
|
||||
std::string::npos) {
|
||||
BoatBase &boat = e.ensure<BoatBase>();
|
||||
int i;
|
||||
std::vector<Ogre::SceneNode *> colliderTarget;
|
||||
boat.mNode =
|
||||
ECS::get<EngineData>()
|
||||
.mScnMgr->getRootSceneNode()
|
||||
->createChildSceneNode(
|
||||
type.position,
|
||||
type.orientation);
|
||||
auto pnodes = boat.mNode->getChildren();
|
||||
for (i = 0; i < pnodes.size(); i++) {
|
||||
Ogre::SceneNode *pnode =
|
||||
static_cast<Ogre::SceneNode *>(
|
||||
pnodes[i]);
|
||||
Ogre::Any any =
|
||||
pnode->getUserObjectBindings()
|
||||
.getUserAny("type");
|
||||
if (!any.has_value())
|
||||
continue;
|
||||
Ogre::String obj_type =
|
||||
Ogre::any_cast<Ogre::String>(
|
||||
any);
|
||||
if (obj_type == "hull-collider") {
|
||||
/* FIXME */
|
||||
}
|
||||
}
|
||||
{
|
||||
Ogre::SceneNode *attachment =
|
||||
eng.mScnMgr->getRootSceneNode()
|
||||
->createChildSceneNode();
|
||||
Ogre::DataStreamPtr scene =
|
||||
Ogre::ResourceGroupManager::getSingleton()
|
||||
.openResource(
|
||||
type.resourceName,
|
||||
Ogre::ResourceGroupManager::
|
||||
AUTODETECT_RESOURCE_GROUP_NAME);
|
||||
SceneLoader loader;
|
||||
loader.load(scene, "General",
|
||||
attachment, e);
|
||||
// attachment->loadChildren(type.resourceName);
|
||||
std::vector<Ogre::Node *> v =
|
||||
attachment->getChildren();
|
||||
OgreAssert(v.size() == 1,
|
||||
"Bad root nodes count in " +
|
||||
type.resourceName);
|
||||
Ogre::Any any =
|
||||
static_cast<Ogre::SceneNode *>(
|
||||
v[0])
|
||||
->getUserObjectBindings()
|
||||
.getUserAny("type");
|
||||
OgreAssert(any.has_value(),
|
||||
"no \"type\" costom prop");
|
||||
Ogre::String obj_type =
|
||||
Ogre::any_cast<Ogre::String>(
|
||||
any);
|
||||
std::cout << "type: " << obj_type
|
||||
<< std::endl;
|
||||
OgreAssert(obj_type == "boat",
|
||||
"not a boat");
|
||||
boat.mNode =
|
||||
static_cast<Ogre::SceneNode *>(
|
||||
v[0]);
|
||||
boat.mNode->_setDerivedPosition(
|
||||
type.position);
|
||||
boat.mNode->_setDerivedOrientation(
|
||||
type.orientation);
|
||||
boat.mEnt = static_cast<Ogre::Entity *>(
|
||||
boat.mNode->getAttachedObject(
|
||||
std::to_string(
|
||||
(int)e.raw_id()) +
|
||||
"/boat"));
|
||||
loader.setupPhysicsBody(boat.mNode,
|
||||
boat.mEnt, e);
|
||||
e.remove<SpawnBoat>();
|
||||
e.modified<BoatBase>();
|
||||
}
|
||||
// BoatBody &body = e.ensure<BoatBody>();
|
||||
// e.modified<BoatBase>();
|
||||
}
|
||||
// ECS::get_mut<ECS::EngineData>().enableDbgDraw = true;
|
||||
// ECS::modified<ECS::EngineData>();
|
||||
// e.modified<BoatBody>();
|
||||
});
|
||||
ecs.observer<const BoatBase, EventData>("HandleBoatEvents")
|
||||
.event(flecs::OnSet)
|
||||
.each([](flecs::entity e, const BoatBase &boat,
|
||||
EventData &evt) {
|
||||
for (auto ev : evt.events) {
|
||||
std::cout << "event: " << ev.event << " "
|
||||
<< ev.sender.name() << " "
|
||||
<< ev.e1.name() << " " << ev.e2.name()
|
||||
<< std::endl;
|
||||
if (ev.event == "actuator_created") {
|
||||
continue;
|
||||
} else if (ev.event == "actuator_enter") {
|
||||
if (e.has<BoatCurrentActuator>())
|
||||
continue;
|
||||
if (ev.sender.has<EventTrigger>()) {
|
||||
if (ev.e2.has<Player>())
|
||||
PhysicsModule::
|
||||
controlPhysics(
|
||||
ev.e2,
|
||||
false);
|
||||
const EventTrigger &trigger =
|
||||
ev.sender.get<
|
||||
EventTrigger>();
|
||||
Ogre::SceneNode *node =
|
||||
trigger.node;
|
||||
Ogre::Any animationAny =
|
||||
node->getUserObjectBindings()
|
||||
.getUserAny(
|
||||
"trigger_animation");
|
||||
if (animationAny.has_value()) {
|
||||
Ogre::String animation =
|
||||
Ogre::any_cast<
|
||||
Ogre::String>(
|
||||
animationAny);
|
||||
ev.e2.set<
|
||||
CharacterVelocity>(
|
||||
{ { 0, 0, 0 },
|
||||
{ 0, 0,
|
||||
0 } });
|
||||
if (animation.length() >
|
||||
0)
|
||||
ev.e2.set<
|
||||
CharacterInActuator>(
|
||||
{ animation,
|
||||
{ 0,
|
||||
0,
|
||||
0 } });
|
||||
else
|
||||
ev.e2.remove<
|
||||
CharacterInActuator>();
|
||||
}
|
||||
Ogre::SceneNode *target_node =
|
||||
nullptr;
|
||||
Ogre::Any targetAny =
|
||||
node->getUserObjectBindings()
|
||||
.getUserAny(
|
||||
"target");
|
||||
if (targetAny.has_value()) {
|
||||
OgreAssert(
|
||||
targetAny
|
||||
.has_value(),
|
||||
"need target");
|
||||
target_node = Ogre::any_cast<
|
||||
Ogre::SceneNode
|
||||
*>(
|
||||
targetAny);
|
||||
Ogre::Vector3 position =
|
||||
target_node
|
||||
->_getDerivedPosition();
|
||||
Ogre::Quaternion orientation =
|
||||
target_node
|
||||
->_getDerivedOrientation();
|
||||
if (ev.e2.has<
|
||||
CharacterBase>()) {
|
||||
ev.e2.get_mut<
|
||||
CharacterBase>()
|
||||
.mBodyNode
|
||||
->_setDerivedPosition(
|
||||
position);
|
||||
ev.e2.get_mut<
|
||||
CharacterBase>()
|
||||
.mBodyNode
|
||||
->_setDerivedOrientation(
|
||||
orientation);
|
||||
}
|
||||
}
|
||||
e.set<BoatCurrentActuator>(
|
||||
{ ev.event,
|
||||
ev.sender });
|
||||
}
|
||||
} else if (ev.event == "actuator_forward") {
|
||||
/* */
|
||||
if (e.has<BoatCurrentActuator>()) {
|
||||
if (e.get<BoatCurrentActuator>()
|
||||
.actuator ==
|
||||
"actuator_enter")
|
||||
ev.e2.set<
|
||||
CharacterInActuator>(
|
||||
{ "swimming-edge-climb",
|
||||
{ 0, 0,
|
||||
0 } });
|
||||
}
|
||||
} else if (ev.event == "actuator_backward") {
|
||||
/* */
|
||||
} else if (ev.event ==
|
||||
"animation:swimming-edge-climb:end") {
|
||||
ev.e2.remove<CharacterInActuator>();
|
||||
PhysicsModule::controlPhysics(ev.e2,
|
||||
true);
|
||||
ev.e2.remove<InTrigger>(ev.e1);
|
||||
ev.e1.remove<TriggeredBy>(ev.e2);
|
||||
ev.e2.add<CharacterGravity>();
|
||||
ev.e2.add<CharacterBuoyancy>();
|
||||
e.remove<BoatCurrentActuator>();
|
||||
} else if (ev.event == "boat_control_enter") {
|
||||
Ogre::SceneNode *captainSeat = nullptr;
|
||||
std::vector<Ogre::Node *> children =
|
||||
boat.mNode->getChildren();
|
||||
for (auto child : children) {
|
||||
Ogre::Any nameAny =
|
||||
child->getUserObjectBindings()
|
||||
.getUserAny(
|
||||
"name");
|
||||
if (nameAny.has_value()) {
|
||||
Ogre::String name =
|
||||
Ogre::any_cast<
|
||||
Ogre::String>(
|
||||
nameAny);
|
||||
std::cout << "name: "
|
||||
<< name
|
||||
<< std::endl;
|
||||
if (name ==
|
||||
"captain_seat") {
|
||||
captainSeat = static_cast<
|
||||
Ogre::SceneNode
|
||||
*>(
|
||||
child);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
OgreAssert(captainSeat, "seats");
|
||||
PhysicsModule::controlPhysics(ev.e2,
|
||||
false);
|
||||
ev.e2.set<CharacterVelocity>(
|
||||
{ { 0, 0, 0 }, { 0, 0, 0 } });
|
||||
ev.e2.set<CharacterInActuator>(
|
||||
{ "sitting", { 0, 0, 0 } });
|
||||
Ogre::Vector3 position =
|
||||
captainSeat
|
||||
->_getDerivedPosition();
|
||||
Ogre::Quaternion orientation =
|
||||
captainSeat
|
||||
->_getDerivedOrientation();
|
||||
if (ev.e2.has<CharacterBase>()) {
|
||||
ev.e2.get_mut<CharacterBase>()
|
||||
.mBodyNode
|
||||
->_setDerivedPosition(
|
||||
position);
|
||||
ev.e2.get_mut<CharacterBase>()
|
||||
.mBodyNode
|
||||
->_setDerivedOrientation(
|
||||
orientation);
|
||||
}
|
||||
} else if (ev.event == "boat_control_exit") {
|
||||
} else if (ev.event == "actuator_exit") {
|
||||
/* */
|
||||
} else
|
||||
OgreAssert(false, "event");
|
||||
}
|
||||
evt.events.clear();
|
||||
});
|
||||
#if 0
|
||||
ecs.system<const EngineData, BoatType, Body2Entity>("CreateBoat")
|
||||
.kind(flecs::OnUpdate)
|
||||
.without<BoatBase>()
|
||||
.without<BoatBody>()
|
||||
.each([](flecs::entity e, const EngineData &eng,
|
||||
BoatType &type) {
|
||||
.each([](flecs::entity e, const EngineData &eng, BoatType &type,
|
||||
Body2Entity &b2e) {
|
||||
BoatBase &boat = e.ensure<BoatBase>();
|
||||
if (type.resourceName.find(".glb") !=
|
||||
std::string::npos) {
|
||||
@@ -30,35 +318,67 @@ BoatModule::BoatModule(flecs::world &ecs)
|
||||
type.orientation);
|
||||
boat.mNode->attachObject(boat.mEnt);
|
||||
|
||||
#if 0
|
||||
BoatBody &body = e.ensure<BoatBody>();
|
||||
body.body =
|
||||
ECS::get<EngineData>()
|
||||
.mWorld->addRigidBody(
|
||||
0, boat.mEnt,
|
||||
Ogre::Bullet::CT_HULL,
|
||||
Ogre::Bullet::CT_TRIMESH,
|
||||
nullptr, 2, 0x7fffffff);
|
||||
b2e.entities[body.body] = e;
|
||||
#endif
|
||||
} else if (type.resourceName.find(".scene") !=
|
||||
std::string::npos) {
|
||||
int i;
|
||||
std::vector<Ogre::SceneNode *> colliderTarget;
|
||||
boat.mNode =
|
||||
ECS::get<EngineData>()
|
||||
.mScnMgr->getRootSceneNode()
|
||||
->createChildSceneNode(
|
||||
type.position,
|
||||
type.orientation);
|
||||
auto pnodes = boat.mNode->getChildren();
|
||||
for (i = 0; i < pnodes.size(); i++) {
|
||||
Ogre::SceneNode *pnode =
|
||||
static_cast<Ogre::SceneNode *>(
|
||||
pnodes[i]);
|
||||
Ogre::Any any =
|
||||
pnode->getUserObjectBindings()
|
||||
.getUserAny("type");
|
||||
if (!any.has_value())
|
||||
continue;
|
||||
Ogre::String obj_type =
|
||||
Ogre::any_cast<Ogre::String>(
|
||||
any);
|
||||
if (obj_type == "hull-collider") {
|
||||
/* FIXME */
|
||||
}
|
||||
}
|
||||
|
||||
Ogre::SceneNode *attachment =
|
||||
eng.mScnMgr->getRootSceneNode()
|
||||
->createChildSceneNode();
|
||||
attachment->loadChildren(type.resourceName);
|
||||
Ogre::DataStreamPtr scene =
|
||||
Ogre::ResourceGroupManager::getSingleton()
|
||||
.openResource(
|
||||
type.resourceName,
|
||||
Ogre::ResourceGroupManager::
|
||||
AUTODETECT_RESOURCE_GROUP_NAME);
|
||||
SceneLoader loader;
|
||||
loader.load(scene, "General", attachment, e);
|
||||
// attachment->loadChildren(type.resourceName);
|
||||
std::vector<Ogre::Node *> v =
|
||||
attachment->getChildren();
|
||||
int i;
|
||||
OgreAssert(v.size() == 1, "Bad nodes count");
|
||||
OgreAssert(v.size() == 1,
|
||||
"Bad root nodes count in " +
|
||||
type.resourceName);
|
||||
Ogre::Any any =
|
||||
static_cast<Ogre::SceneNode *>(v[0])
|
||||
->getUserObjectBindings()
|
||||
.getUserAny("type");
|
||||
OgreAssert(any.has_value(),
|
||||
"bas node type costom prop");
|
||||
"no \"type\" costom prop");
|
||||
Ogre::String obj_type =
|
||||
Ogre::any_cast<Ogre::String>(any);
|
||||
std::cout << "type: " << obj_type << std::endl;
|
||||
@@ -69,23 +389,88 @@ BoatModule::BoatModule(flecs::world &ecs)
|
||||
boat.mNode->_setDerivedOrientation(
|
||||
type.orientation);
|
||||
boat.mEnt = static_cast<Ogre::Entity *>(
|
||||
boat.mNode->getAttachedObject("boat"));
|
||||
boat.mNode->getAttachedObject(
|
||||
std::to_string((int)e.raw_id()) +
|
||||
"/boat"));
|
||||
loader.setupPhysicsBody(boat.mNode, boat.mEnt);
|
||||
BoatBody &body = e.ensure<BoatBody>();
|
||||
#if 0
|
||||
Ogre::Any bodyA =
|
||||
boat.mNode->getUserObjectBindings()
|
||||
.getUserAny("bodyPointer");
|
||||
if (bodyA.has_value())
|
||||
body.body =
|
||||
Ogre::any_cast<btRigidBody *>(
|
||||
bodyA);
|
||||
#if 0
|
||||
body.body =
|
||||
ECS::get<EngineData>()
|
||||
.mWorld->addRigidBody(
|
||||
0, boat.mEnt,
|
||||
Ogre::Bullet::CT_HULL,
|
||||
Ogre::Bullet::CT_TRIMESH,
|
||||
nullptr, 2, 0x7fffffff);
|
||||
#if 0
|
||||
boat.mEnt = eng.mScnMgr->getEntity("boat");
|
||||
boat.mNode = boat.mEnt->get
|
||||
/* no need to attach anything */
|
||||
BoatBody &body =
|
||||
e.ensure<BoatBody>();
|
||||
#endif
|
||||
OgreAssert(body.body, "No body :()");
|
||||
b2e.entities[body.body] = e;
|
||||
#endif
|
||||
std::vector<Ogre::Node *> slots =
|
||||
boat.mNode->getChildren();
|
||||
for (i = 0; i < slots.size(); i++) {
|
||||
Ogre::Any any =
|
||||
static_cast<Ogre::SceneNode *>(
|
||||
slots[i])
|
||||
->getUserObjectBindings()
|
||||
.getUserAny("type");
|
||||
if (!any.has_value())
|
||||
continue;
|
||||
Ogre::String obj_type =
|
||||
Ogre::any_cast<Ogre::String>(
|
||||
any);
|
||||
std::cout << "child type: " << obj_type
|
||||
<< std::endl;
|
||||
}
|
||||
if (slots.size() > 0) {
|
||||
ObjectSlots &vs =
|
||||
e.ensure<ObjectSlots>();
|
||||
for (i = 0; i < slots.size(); i++) {
|
||||
Ogre::Any any =
|
||||
static_cast<Ogre::SceneNode
|
||||
*>(
|
||||
slots[i])
|
||||
->getUserObjectBindings()
|
||||
.getUserAny(
|
||||
"type");
|
||||
if (!any.has_value())
|
||||
continue;
|
||||
Ogre::String obj_type =
|
||||
Ogre::any_cast<
|
||||
Ogre::String>(
|
||||
any);
|
||||
any = static_cast<
|
||||
Ogre::SceneNode *>(
|
||||
slots[i])
|
||||
->getUserObjectBindings()
|
||||
.getUserAny(
|
||||
"name");
|
||||
if (!any.has_value())
|
||||
continue;
|
||||
Ogre::String obj_name =
|
||||
Ogre::any_cast<
|
||||
Ogre::String>(
|
||||
any);
|
||||
vs.slots[obj_name] = {
|
||||
obj_type,
|
||||
static_cast<Ogre::SceneNode
|
||||
*>(
|
||||
slots[i])
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
// ECS::get_mut<ECS::EngineData>().enableDbgDraw = true;
|
||||
// ECS::modified<ECS::EngineData>();
|
||||
e.modified<BoatBody>();
|
||||
});
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,9 +18,11 @@ struct BoatBase {
|
||||
Ogre::Entity *mEnt;
|
||||
Ogre::SceneNode *mNode;
|
||||
};
|
||||
struct BoatBody {
|
||||
btRigidBody *body;
|
||||
struct BoatCurrentActuator {
|
||||
Ogre::String actuator;
|
||||
flecs::entity actuator_e;
|
||||
};
|
||||
struct SpawnBoat {};
|
||||
struct BoatModule {
|
||||
BoatModule(flecs::world &ecs);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
project(gamedata)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
find_package(OGRE REQUIRED COMPONENTS Bites Bullet Paging Terrain Overlay CONFIG)
|
||||
add_library(GameData STATIC GameData.cpp CharacterModule.cpp WaterModule.cpp SunModule.cpp TerrainModule.cpp GUIModule.cpp LuaData.cpp WorldMapModule.cpp
|
||||
BoatModule.cpp EventTriggerModule.cpp)
|
||||
target_link_libraries(GameData PUBLIC OgreMain OgreBites OgreBullet OgrePaging OgreTerrain OgreOverlay flecs::flecs_static lua)
|
||||
target_include_directories(GameData PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
find_package(Bullet REQUIRED)
|
||||
add_library(GameData STATIC GameData.cpp CharacterModule.cpp WaterModule.cpp SunModule.cpp TerrainModule.cpp
|
||||
GUIModule.cpp LuaData.cpp WorldMapModule.cpp BoatModule.cpp EventTriggerModule.cpp
|
||||
CharacterAnimationModule.cpp PhysicsModule.cpp EventModule.cpp CharacterManagerModule.cpp
|
||||
VehicleManagerModule.cpp AppModule.cpp SmartObject.cpp SlotsModule.cpp goap.cpp)
|
||||
target_link_libraries(GameData PUBLIC lua flecs::flecs_static OgreMain OgreBites
|
||||
OgrePaging OgreTerrain OgreOverlay
|
||||
PRIVATE sceneloader world-build physics)
|
||||
target_include_directories(GameData PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${BULLET_INCLUDE_DIR} ../luaaa)
|
||||
target_compile_definitions(GameData PRIVATE FLECS_CPP_NO_AUTO_REGISTRATION)
|
||||
|
||||
744
src/gamedata/CharacterAnimationModule.cpp
Normal file
@@ -0,0 +1,744 @@
|
||||
#include <iostream>
|
||||
#include "Components.h"
|
||||
#include "EventTriggerModule.h"
|
||||
#include "CharacterModule.h"
|
||||
#include "PhysicsModule.h"
|
||||
#include "CharacterAnimationModule.h"
|
||||
#include "EventModule.h"
|
||||
#include "TerrainModule.h"
|
||||
#include "WaterModule.h"
|
||||
#include "world-build.h"
|
||||
namespace ECS
|
||||
{
|
||||
CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.module<CharacterAnimationModule>();
|
||||
ecs.component<AnimationControl>();
|
||||
ecs.import <EventModule>();
|
||||
ecs.import <TerrainModule>();
|
||||
ecs.import <WaterModule>();
|
||||
ecs.import <PhysicsModule>();
|
||||
ecs.system<const CharacterBase, AnimationControl>("HandleAnimations")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([this](flecs::entity e, const CharacterBase &ch,
|
||||
AnimationControl &anim) {
|
||||
if (!anim.configured && ch.mSkeleton) {
|
||||
int i, j;
|
||||
e.set<EventData>({});
|
||||
ch.mSkeleton->setBlendMode(
|
||||
Ogre::ANIMBLEND_CUMULATIVE);
|
||||
Ogre::String animNames[] = {
|
||||
"idle",
|
||||
"walking",
|
||||
"running",
|
||||
"treading_water",
|
||||
"swimming",
|
||||
"hanging-idle",
|
||||
"hanging-climb",
|
||||
"swimming-hold-edge",
|
||||
"swimming-edge-climb",
|
||||
"character-talk",
|
||||
"pass-character",
|
||||
"idle-act",
|
||||
"sitting-chair",
|
||||
"sitting-ground"
|
||||
};
|
||||
int state_count = sizeof(animNames) /
|
||||
sizeof(animNames[0]);
|
||||
anim.mAnimationSystem =
|
||||
new AnimationSystem(false);
|
||||
for (i = 0; i < state_count; i++) {
|
||||
Animation *animation = new Animation(
|
||||
ch.mSkeleton,
|
||||
ch.mBodyEnt->getAnimationState(
|
||||
animNames[i]),
|
||||
ch.mSkeleton->getAnimation(
|
||||
animNames[i]),
|
||||
e);
|
||||
#ifdef VDEBUG
|
||||
std::cout
|
||||
<< "animation: " << animNames[i]
|
||||
<< std::endl;
|
||||
#endif
|
||||
animation->setLoop(true);
|
||||
anim.mAnimationSystem->add_animation(
|
||||
animNames[i], animation);
|
||||
}
|
||||
anim.mAnimationSystem
|
||||
->builder()
|
||||
/* clang-format off */
|
||||
->output()
|
||||
->state_machine(ANIM_FADE_SPEED, "main")
|
||||
->state("locomotion")
|
||||
->state_machine(ANIM_FADE_SPEED, "locomotion-state")
|
||||
->state("idle")
|
||||
->animation("idle")
|
||||
->end()
|
||||
->state("walking")
|
||||
->animation("walking")
|
||||
->end()
|
||||
->state("running")
|
||||
->animation("running")
|
||||
->end()
|
||||
->state("treading_water")
|
||||
->animation("treading_water")
|
||||
->end()
|
||||
->state("swimming")
|
||||
->animation("swimming")
|
||||
->end()
|
||||
->state("swimming-fast")
|
||||
->speed(20.0f)
|
||||
->animation("swimming")
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->state("actuator")
|
||||
->state_machine(ANIM_FADE_SPEED * 10.0f, "actuator-state")
|
||||
->state("hanging-idle")
|
||||
->animation("hanging-idle")
|
||||
->end()
|
||||
->state("swimming-hold-edge")
|
||||
->animation("swimming-hold-edge")
|
||||
->end()
|
||||
->state("swimming-edge-climb")
|
||||
->animation("swimming-edge-climb")
|
||||
->trigger(e, "end_of_climb", 0.99f, "animation:swimming-edge-climb:end")
|
||||
->end()
|
||||
->state("hanging-climb")
|
||||
->animation("hanging-climb")
|
||||
->trigger(e, "end_of_climb2", 0.99f, "animation:hanging-climb:end")
|
||||
->end()
|
||||
->state("idle")
|
||||
->animation("idle-act")
|
||||
->end()
|
||||
->state("pass-character")
|
||||
->animation("pass-character")
|
||||
->trigger(e, "pass-character", 0.99f, "animation:pass-character:end")
|
||||
->end()
|
||||
->state("character-talk")
|
||||
->animation("character-talk")
|
||||
->end()
|
||||
->state("sitting")
|
||||
->animation("sitting-chair")
|
||||
->end()
|
||||
->state("sitting-ground")
|
||||
->animation("sitting-ground")
|
||||
->end()
|
||||
->transition_end("swimming-edge-climb", "idle")
|
||||
->transition_end("hanging-climb", "idle")
|
||||
->transition_end("pass-character", "idle")
|
||||
->end()
|
||||
->end()
|
||||
->end();
|
||||
/* clang-format on */
|
||||
|
||||
anim.mAnimationSystem
|
||||
->get<AnimationNodeStateMachine>("main")
|
||||
->setAnimation("locomotion", true);
|
||||
anim.mAnimationSystem
|
||||
->get<AnimationNodeStateMachine>(
|
||||
"locomotion-state")
|
||||
->setAnimation("idle", true);
|
||||
anim.configured = true;
|
||||
}
|
||||
});
|
||||
#if 0
|
||||
ecs.system<CharacterBase>("RootMotionStart")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([this](flecs::entity e, CharacterBase &ch) {
|
||||
ch.mBoneMotion = Ogre::Vector3::ZERO;
|
||||
});
|
||||
#endif
|
||||
ecs.system<const EngineData, CharacterBase, AnimationControl>(
|
||||
"HandleAnimations1")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([this](flecs::entity e, const EngineData &eng,
|
||||
CharacterBase &ch, AnimationControl &anim) {
|
||||
float delta = eng.delta;
|
||||
// ch.mBoneMotion = Ogre::Vector3::ZERO;
|
||||
bool result = anim.mAnimationSystem->addTime(delta);
|
||||
if (!ch.mRootBone)
|
||||
return;
|
||||
// The value we get is interpolated value. When result is true it is new step
|
||||
#if 0
|
||||
Ogre::Vector3 offset = ch.mRootBone->getPosition();
|
||||
ch.mBoneMotion = static_cast<RootMotionListener *>(
|
||||
anim.mListener)
|
||||
->getDeltaMotion();
|
||||
ch.mRootBone->setPosition(Ogre::Vector3::ZERO);
|
||||
Ogre::Vector3 d = offset - ch.mBonePrevMotion;
|
||||
ch.mBonePrevMotion = offset;
|
||||
|
||||
std::cout << "length: " << d.length() << std::endl;
|
||||
if (d.squaredLength() > 0.02f * 0.02f)
|
||||
d = offset;
|
||||
if (d.squaredLength() > 0.02f * 0.02f)
|
||||
d = Ogre::Vector3::ZERO;
|
||||
std::cout << "length2: " << d.length() << std::endl;
|
||||
OgreAssert(d.length() < 0.5f, "bad offset");
|
||||
ch.mBoneMotion = d;
|
||||
#endif
|
||||
#if 0
|
||||
if (result) {
|
||||
if (d.squaredLength() > 0.0f)
|
||||
ch.mBoneMotion =
|
||||
ch.mRootBone->getPosition() -
|
||||
ch.mBoneMotion;
|
||||
else
|
||||
ch.mBoneMotion =
|
||||
ch.mRootBone->getPosition();
|
||||
} else {
|
||||
ch.mBoneMotion = ch.mRootBone->getPosition() -
|
||||
ch.mBoneMotion;
|
||||
}
|
||||
#endif
|
||||
|
||||
#undef VDEBUG
|
||||
#ifdef VDEBUG
|
||||
std::cout << "root motion: " << delta << ": "
|
||||
<< ch.mBoneMotion << " - "
|
||||
<< ch.mRootBone->getPosition()
|
||||
<< " result: " << result << std::endl;
|
||||
#endif
|
||||
#undef VDEBUG
|
||||
#if 0
|
||||
// ch.mRootBone->setPosition(Ogre::Vector3::ZERO);
|
||||
ch.mBonePrevMotion = offset;
|
||||
#endif
|
||||
});
|
||||
ecs.system<const EngineData, CharacterBase, CharacterVelocity>(
|
||||
"HandleRootMotionVelocity")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<TerrainReady>()
|
||||
.with<WaterReady>()
|
||||
.each([this](flecs::entity e, const EngineData &eng,
|
||||
CharacterBase &ch, CharacterVelocity &v) {
|
||||
if (eng.delta < 0.0000001f)
|
||||
return;
|
||||
if (!ch.mBodyNode)
|
||||
return;
|
||||
Ogre::Quaternion rot = ch.mBodyNode->getOrientation();
|
||||
Ogre::Vector3 pos = ch.mBodyNode->getPosition();
|
||||
Ogre::Vector3 boneMotion = ch.mBoneMotion;
|
||||
v.velocity = Ogre::Vector3::ZERO;
|
||||
float safeDelta =
|
||||
Ogre::Math::Clamp(eng.delta, 0.001f, 0.99f);
|
||||
#if 0
|
||||
if (!e.has<CharacterInActuator>()) {
|
||||
v.velocity = Ogre::Math::lerp(
|
||||
v.velocity,
|
||||
rot * boneMotion / safeDelta, 0.99f);
|
||||
} else {
|
||||
// v.velocity = rot * boneMotion / safeDelta;
|
||||
v.velocity = Ogre::Math::lerp(
|
||||
v.velocity,
|
||||
rot * boneMotion / safeDelta, 0.99f);
|
||||
}
|
||||
#endif
|
||||
v.velocity = rot * boneMotion / safeDelta;
|
||||
#if 0
|
||||
if (!e.has<CharacterDisablePhysics>() &&
|
||||
!e.has<CharacterInActuator>()) {
|
||||
if (eng.startupDelay <= 0.0f)
|
||||
v.velocity += v.gvelocity;
|
||||
v.velocity.y = Ogre::Math::Clamp(v.velocity.y,
|
||||
-10.5f, 10.0f);
|
||||
}
|
||||
#endif
|
||||
// if (v.velocity.squaredLength() > 1.4f * 1.4f)
|
||||
// v.velocity = v.velocity.normalisedCopy() * 1.4f;
|
||||
// ch.mBoneMotion = Ogre::Vector3::ZERO;
|
||||
// safety
|
||||
// std::cout << "velocity: " << v.velocity << std::endl;
|
||||
v.velocity.x =
|
||||
Ogre::Math::Clamp(v.velocity.x, -16.0f, 16.0f);
|
||||
v.velocity.z =
|
||||
Ogre::Math::Clamp(v.velocity.z, -16.0f, 16.0f);
|
||||
v.velocity.y =
|
||||
Ogre::Math::Clamp(v.velocity.y, -10.5f, 10.0f);
|
||||
#if 0
|
||||
v.velocity.y = 0.0f;
|
||||
#endif
|
||||
});
|
||||
#if 0
|
||||
ecs.system<const EngineData, const AnimationControl,
|
||||
const CharacterBase, CharacterVelocity>("HandleSwimming")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<TerrainReady>()
|
||||
.with<WaterReady>()
|
||||
.with<InWater>()
|
||||
.with<CharacterBuoyancy>()
|
||||
.without<CharacterDisablePhysics>()
|
||||
.without<CharacterInActuator>()
|
||||
.each([this](flecs::entity e, const EngineData &eng,
|
||||
const AnimationControl &anim,
|
||||
const CharacterBase &ch, CharacterVelocity &gr) {
|
||||
if (anim.mAnimationSystem
|
||||
->get<AnimationNodeStateMachine>(
|
||||
"locomotion-state")
|
||||
->getCurrentState() == "swimming") {
|
||||
float h = Ogre::Math::Clamp(
|
||||
0.0f - ch.mBodyNode->getPosition().y,
|
||||
0.0f, 2000.0f);
|
||||
if (h > 0.05 && h < 2.0f)
|
||||
gr.gvelocity.y += 0.1f * (h + 1.0f) *
|
||||
h * eng.delta;
|
||||
}
|
||||
});
|
||||
#endif
|
||||
ecs.system<const EngineData, CharacterBase, AnimationControl,
|
||||
CharacterVelocity>("HandleRootMotion")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([this](flecs::entity e, const EngineData &eng,
|
||||
CharacterBase &ch, AnimationControl &anim,
|
||||
CharacterVelocity &v) {
|
||||
if (!ch.mBodyNode)
|
||||
return;
|
||||
if (eng.delta < 0.0000001f)
|
||||
return;
|
||||
OgreAssert(eng.delta > 0.0f, "Zero delta");
|
||||
int maxPen = 0;
|
||||
Ogre::Vector3 colNormal;
|
||||
bool is_on_floor = false;
|
||||
bool penetration = false;
|
||||
#if 0
|
||||
if (eng.startupDelay < 0.0f) {
|
||||
if (body.mController) {
|
||||
Ogre::Vector3 rotMotion =
|
||||
v.velocity * eng.delta;
|
||||
rotMotion.x = Ogre::Math::Clamp(
|
||||
rotMotion.x, -0.04f, 0.04f);
|
||||
rotMotion.y = Ogre::Math::Clamp(
|
||||
rotMotion.y, -0.025f, 0.1f);
|
||||
rotMotion.z = Ogre::Math::Clamp(
|
||||
rotMotion.z, -0.04f, 0.04f);
|
||||
btVector3 currentPosition =
|
||||
body.mGhostObject
|
||||
->getWorldTransform()
|
||||
.getOrigin();
|
||||
is_on_floor =
|
||||
body.mController->isOnFloor();
|
||||
penetration = body.mController
|
||||
->isPenetrating();
|
||||
if (is_on_floor)
|
||||
v.gvelocity =
|
||||
Ogre::Vector3::ZERO;
|
||||
|
||||
btTransform from(
|
||||
Ogre::Bullet::convert(
|
||||
ch.mBodyNode
|
||||
->getOrientation()),
|
||||
Ogre::Bullet::convert(
|
||||
ch.mBodyNode
|
||||
->getPosition()));
|
||||
ch.mBodyNode->_setDerivedPosition(
|
||||
ch.mBodyNode
|
||||
->_getDerivedPosition() +
|
||||
rotMotion);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
});
|
||||
#if 0
|
||||
ecs.system<CharacterVelocity, CharacterBase>("HandleRootMotionEnd")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([this](flecs::entity e, CharacterVelocity &v,
|
||||
CharacterBase &ch) {
|
||||
// zero the velocity;
|
||||
// v.velocity = Ogre::Vector3::ZERO;
|
||||
// ch.mBoneMotion = Ogre::Vector3::ZERO;
|
||||
});
|
||||
#endif
|
||||
|
||||
ecs.system<const Input, const CharacterBase, AnimationControl>(
|
||||
"HandleNPCAnimations")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<Character>()
|
||||
.without<Player>()
|
||||
.each([](flecs::entity e, const Input &input,
|
||||
const CharacterBase &ch, AnimationControl &anim) {
|
||||
if (!anim.configured)
|
||||
return;
|
||||
AnimationNodeStateMachine *state_machine =
|
||||
anim.mAnimationSystem
|
||||
->get<AnimationNodeStateMachine>(
|
||||
"locomotion-state");
|
||||
Ogre::String current_state =
|
||||
state_machine->getCurrentState();
|
||||
Ogre::String next_state = "idle";
|
||||
if (current_state != "treading_water" &&
|
||||
ch.is_submerged)
|
||||
next_state = "treading_water";
|
||||
if (current_state != "idle" && !ch.is_submerged)
|
||||
next_state = "idle";
|
||||
state_machine->setAnimation(next_state);
|
||||
});
|
||||
ecs.system<const CharacterBase, const CharacterInActuator,
|
||||
AnimationControl>("HandlePlayerAnimationsActuator")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<Character>()
|
||||
.each([](flecs::entity e, const CharacterBase &ch,
|
||||
const CharacterInActuator &inact,
|
||||
AnimationControl &anim) {
|
||||
if (!anim.configured)
|
||||
return;
|
||||
AnimationNodeStateMachine *main_sm =
|
||||
anim.mAnimationSystem
|
||||
->get<AnimationNodeStateMachine>(
|
||||
"main");
|
||||
AnimationNodeStateMachine *actuator_sm =
|
||||
anim.mAnimationSystem
|
||||
->get<AnimationNodeStateMachine>(
|
||||
"actuator-state");
|
||||
Ogre::String current_state = main_sm->getCurrentState();
|
||||
if (current_state != "actuator")
|
||||
main_sm->setAnimation("actuator", true);
|
||||
actuator_sm->setAnimation(inact.animationState, true);
|
||||
});
|
||||
ecs.system<const CharacterBase, AnimationControl>(
|
||||
"HandlePlayerAnimationsNoActuator")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<Character>()
|
||||
.without<CharacterInActuator>()
|
||||
.each([](flecs::entity e, const CharacterBase &ch,
|
||||
AnimationControl &anim) {
|
||||
if (!anim.configured)
|
||||
return;
|
||||
AnimationNodeStateMachine *main_sm =
|
||||
anim.mAnimationSystem
|
||||
->get<AnimationNodeStateMachine>(
|
||||
"main");
|
||||
Ogre::String current_state = main_sm->getCurrentState();
|
||||
if (current_state != "locomotion")
|
||||
main_sm->setAnimation("locomotion", true);
|
||||
});
|
||||
ecs.system<const Input, const CharacterBase, AnimationControl>(
|
||||
"HandlePlayerAnimations")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<Character>()
|
||||
.with<Player>()
|
||||
.without<CharacterInActuator>()
|
||||
.each([](flecs::entity e, const Input &input,
|
||||
const CharacterBase &ch, AnimationControl &anim) {
|
||||
if (!anim.configured)
|
||||
return;
|
||||
AnimationNodeStateMachine *state_machine =
|
||||
anim.mAnimationSystem
|
||||
->get<AnimationNodeStateMachine>(
|
||||
"locomotion-state");
|
||||
Ogre::String current_state =
|
||||
state_machine->getCurrentState();
|
||||
bool controls_idle = input.motion.zeroLength();
|
||||
bool anim_is_idle = current_state == "idle" ||
|
||||
current_state == "treading_water";
|
||||
bool anim_is_walking = current_state == "walking";
|
||||
bool anim_is_running = current_state == "running";
|
||||
bool anim_is_swimming_slow = current_state ==
|
||||
"swimming";
|
||||
bool anim_is_swimming_fast = current_state ==
|
||||
"swimming-fast";
|
||||
bool anim_is_swimming = anim_is_swimming_slow ||
|
||||
anim_is_swimming_fast;
|
||||
bool anim_is_motion = anim_is_walking ||
|
||||
anim_is_running ||
|
||||
anim_is_swimming;
|
||||
bool start_motion = !controls_idle && anim_is_idle;
|
||||
bool end_motion = controls_idle && !anim_is_idle;
|
||||
Ogre::String next_state = current_state;
|
||||
if (controls_idle && anim_is_idle) {
|
||||
if (current_state != "treading_water" &&
|
||||
ch.is_submerged)
|
||||
next_state = "treading_water";
|
||||
else if (current_state != "idle" &&
|
||||
!ch.is_submerged)
|
||||
next_state = "idle";
|
||||
state_machine->setAnimation(next_state);
|
||||
} else if (start_motion) {
|
||||
if (ch.is_submerged) {
|
||||
if (input.fast)
|
||||
next_state = "swimming-fast";
|
||||
else
|
||||
next_state = "swimming";
|
||||
} else {
|
||||
if (input.fast)
|
||||
next_state = "running";
|
||||
else
|
||||
next_state = "walking";
|
||||
}
|
||||
state_machine->setAnimation(next_state, true);
|
||||
} else if (end_motion) {
|
||||
if (ch.is_submerged)
|
||||
state_machine->setAnimation(
|
||||
"treading_water");
|
||||
else
|
||||
state_machine->setAnimation("idle");
|
||||
} else {
|
||||
if (ch.is_submerged) {
|
||||
if (input.fast &&
|
||||
!anim_is_swimming_fast) {
|
||||
next_state = "swimming-fast";
|
||||
} else if (!input.fast &&
|
||||
!anim_is_swimming_slow) {
|
||||
next_state = "swimming";
|
||||
}
|
||||
} else {
|
||||
if (input.fast && !anim_is_running)
|
||||
next_state = "running";
|
||||
else if (!input.fast &&
|
||||
!anim_is_walking)
|
||||
next_state = "walking";
|
||||
}
|
||||
if (current_state != next_state)
|
||||
state_machine->setAnimation(next_state);
|
||||
}
|
||||
});
|
||||
ecs.system<const Input, const CharacterBase, AnimationControl,
|
||||
CharacterInActuator>("HandlePlayerAnimations2")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<Character>()
|
||||
.with<Player>()
|
||||
.each([](flecs::entity e, const Input &input,
|
||||
const CharacterBase &ch, AnimationControl &anim,
|
||||
CharacterInActuator &act) {
|
||||
bool controls_idle = input.motion.zeroLength();
|
||||
if (!controls_idle) {
|
||||
std::cout << "motion.z: "
|
||||
<< Ogre::Math::Abs(input.motion.z -
|
||||
act.prevMotion.z)
|
||||
<< std::endl;
|
||||
bool trigger_event = false;
|
||||
e.each<InTrigger>([&](flecs::entity trig) {
|
||||
if (Ogre::Math::Abs(input.motion.z -
|
||||
act.prevMotion.z) >
|
||||
0.001f) {
|
||||
if (input.motion.z < 0) {
|
||||
trig.get_mut<EventData>()
|
||||
.add(e,
|
||||
"actuator_forward",
|
||||
trig, e);
|
||||
trig.modified<
|
||||
EventData>();
|
||||
}
|
||||
if (input.motion.z > 0) {
|
||||
trig.get_mut<EventData>()
|
||||
.add(e,
|
||||
"actuator_backward",
|
||||
trig, e);
|
||||
trig.modified<
|
||||
EventData>();
|
||||
}
|
||||
}
|
||||
if (input.act_pressed) {
|
||||
trig.get_mut<EventData>().add(
|
||||
e, "actuator_action",
|
||||
trig, e);
|
||||
trig.modified<EventData>();
|
||||
}
|
||||
// ECS::get_mut<LuaData>().call_handler(
|
||||
// "actuator_update", trig, e);
|
||||
trigger_event = true;
|
||||
});
|
||||
if (!trigger_event) {
|
||||
if (Ogre::Math::Abs(input.motion.z -
|
||||
act.prevMotion.z) >
|
||||
0.001f) {
|
||||
if (input.motion.z < 0) {
|
||||
e.get_mut<EventData>().add(
|
||||
e,
|
||||
"_in_actuator_forward",
|
||||
e, e);
|
||||
}
|
||||
if (input.motion.z > 0) {
|
||||
e.get_mut<EventData>().add(
|
||||
e,
|
||||
"_in_actuator_backward",
|
||||
e, e);
|
||||
}
|
||||
}
|
||||
if (input.act_pressed) {
|
||||
e.get_mut<EventData>().add(
|
||||
e,
|
||||
"_in_actuator_action",
|
||||
e, e);
|
||||
}
|
||||
}
|
||||
act.prevMotion.x = input.motion.x;
|
||||
act.prevMotion.y = input.motion.y;
|
||||
act.prevMotion.z = input.motion.z;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (!controls_idle) {
|
||||
if (Ogre::Math::Abs(input.motion.z - act.prevMotion.z) > 0.001f) {
|
||||
if (input.motion.z < 0)
|
||||
ECS::get_mut<LuaData>().call_handler("actuator_forward", e);
|
||||
}
|
||||
ECS::get_mut<LuaData>().call_handler("actuator_controls_update");
|
||||
}
|
||||
#endif
|
||||
act.prevMotion = input.motion;
|
||||
});
|
||||
ecs.system<EventData>("UpdateEvents")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<Character>()
|
||||
.each([](flecs::entity e, EventData &evt) {
|
||||
for (auto ev : evt.events) {
|
||||
std::cout << "character event: " << ev.event
|
||||
<< std::endl;
|
||||
/* parse character events */
|
||||
e.each<InTrigger>([&](flecs::entity trig) {
|
||||
/* if triggered, dispatch events to trigger */
|
||||
trig.get_mut<EventData>().add(
|
||||
ev.sender, ev.event,
|
||||
trig, // it is easier this way to identify trigger entity
|
||||
ev.e2);
|
||||
});
|
||||
}
|
||||
evt.events.clear();
|
||||
});
|
||||
|
||||
#ifdef VDEBUG
|
||||
ecs.system<const CharacterBase>("CharacterGravityStatus")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<Character>()
|
||||
.with<Player>()
|
||||
.each([](flecs::entity e, const CharacterBase &ch) {
|
||||
if (e.has<CharacterGravity>())
|
||||
std::cout << "gravity\n";
|
||||
else
|
||||
std::cout << "no gravity\n";
|
||||
if (e.has<InWater>())
|
||||
std::cout << "in water\n";
|
||||
else
|
||||
std::cout << "out of water\n";
|
||||
std::cout
|
||||
<< "h=" << ch.mBodyNode->_getDerivedPosition().y
|
||||
<< std::endl;
|
||||
});
|
||||
#endif
|
||||
#if 0
|
||||
ecs.system<const EngineData, const CharacterBase, CharacterBody>(
|
||||
"UpdateBodyCast")
|
||||
.kind(flecs::OnUpdate)
|
||||
.without<CharacterInActuator>()
|
||||
.each([](flecs::entity e, const EngineData &eng,
|
||||
const CharacterBase &ch, CharacterBody &body) {
|
||||
struct ResultCallback
|
||||
: public btCollisionWorld::RayResultCallback {
|
||||
btCollisionObject *m_me;
|
||||
btVector3 m_from, m_to, m_hitNormalWorld,
|
||||
m_hitPointWorld;
|
||||
ResultCallback(btCollisionObject *me,
|
||||
const btVector3 &from,
|
||||
const btVector3 &to)
|
||||
: m_me(me)
|
||||
, m_from(from)
|
||||
, m_to(to)
|
||||
{
|
||||
}
|
||||
btScalar addSingleResult(
|
||||
btCollisionWorld::LocalRayResult
|
||||
&rayResult,
|
||||
bool normalInWorldSpace) override
|
||||
{
|
||||
if (rayResult.m_collisionObject == m_me)
|
||||
return 1.0f;
|
||||
if (!btPairCachingGhostObject::upcast(
|
||||
rayResult.m_collisionObject))
|
||||
return 1.0f;
|
||||
if (!(rayResult.m_collisionObject
|
||||
->getCollisionFlags() &
|
||||
btCollisionObject::
|
||||
CF_CHARACTER_OBJECT))
|
||||
return 1.0f;
|
||||
m_closestHitFraction =
|
||||
rayResult.m_hitFraction;
|
||||
m_collisionObject =
|
||||
rayResult.m_collisionObject;
|
||||
if (normalInWorldSpace)
|
||||
m_hitNormalWorld =
|
||||
rayResult
|
||||
.m_hitNormalLocal;
|
||||
else
|
||||
m_hitNormalWorld =
|
||||
m_collisionObject
|
||||
->getWorldTransform()
|
||||
.getBasis() *
|
||||
rayResult
|
||||
.m_hitNormalLocal;
|
||||
m_hitPointWorld.setInterpolate3(
|
||||
m_from, m_to,
|
||||
rayResult.m_hitFraction);
|
||||
return rayResult.m_hitFraction;
|
||||
}
|
||||
};
|
||||
Ogre::Vector3 offset(0.0f, 0.5f, 0.0f);
|
||||
float dist = 0.5f;
|
||||
btVector3 a = Ogre::Bullet::convert(
|
||||
ch.mBodyNode->getPosition() + offset),
|
||||
b(Ogre::Bullet::convert(
|
||||
ch.mBodyNode->getPosition() +
|
||||
ch.mBodyNode->getOrientation() *
|
||||
Ogre::Vector3(0, 0, dist) +
|
||||
offset));
|
||||
ResultCallback result(body.mGhostObject, a, b);
|
||||
// body.mGhostObject->rayTest(a, b, result);
|
||||
eng.mWorld->getBtWorld()->rayTest(a, b, result);
|
||||
if (result.hasHit()) {
|
||||
std::cout << "Hit!!! " << result.m_hitPointWorld
|
||||
<< std::endl;
|
||||
e.set<CharacterInActuator>(
|
||||
{ "idle", { 0, 0, 0 } });
|
||||
ECS::get<LuaBase>().mLua->call_handler(
|
||||
"character_enter", e,
|
||||
ECS::get<Body2Entity>().entities.at(
|
||||
const_cast<btCollisionObject *>(
|
||||
result.m_collisionObject)));
|
||||
}
|
||||
});
|
||||
#endif
|
||||
struct AnimationSetCommand : public GameWorld::Command {
|
||||
int operator()(const std::vector<GameWorld::Parameter *> &args)
|
||||
override
|
||||
{
|
||||
GameWorld::ValueParameter<flecs::entity> *param_e =
|
||||
static_cast<GameWorld::ValueParameter<
|
||||
flecs::entity> *>(args[0]);
|
||||
OgreAssert(param_e->get().is_valid(), "bad entity");
|
||||
GameWorld::ValueParameter<std::string> *param_node =
|
||||
static_cast<GameWorld::ValueParameter<
|
||||
std::string> *>(args[1]);
|
||||
GameWorld::ValueParameter<std::string> *param_state =
|
||||
static_cast<GameWorld::ValueParameter<
|
||||
std::string> *>(args[2]);
|
||||
if (param_e->get().has<AnimationControl>() &&
|
||||
param_e->get().get<AnimationControl>().configured ==
|
||||
true) {
|
||||
const AnimationControl &control =
|
||||
param_e->get().get<AnimationControl>();
|
||||
AnimationNodeStateMachine *sm =
|
||||
control.mAnimationSystem
|
||||
->get<AnimationNodeStateMachine>(
|
||||
param_node->get());
|
||||
bool reset = false;
|
||||
if (args.size() == 4) {
|
||||
GameWorld::ValueParameter<bool>
|
||||
*param_reset = static_cast<
|
||||
GameWorld::ValueParameter<
|
||||
bool> *>(
|
||||
args[3]);
|
||||
reset = param_reset->get();
|
||||
}
|
||||
sm->setAnimation(param_state->get(), reset);
|
||||
std::cout << "animation switch: "
|
||||
<< param_node->get() << " "
|
||||
<< param_state->get() << std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
ECS::get_mut<GameWorld>().add_command<AnimationSetCommand>(
|
||||
"set_animation_state");
|
||||
}
|
||||
}
|
||||
903
src/gamedata/CharacterAnimationModule.h
Normal file
@@ -0,0 +1,903 @@
|
||||
#ifndef CHARACTER_ANIMATION_MODULE_H_
|
||||
#define CHARACTER_ANIMATION_MODULE_H_
|
||||
#include <Ogre.h>
|
||||
#include <flecs.h>
|
||||
#include "GameData.h"
|
||||
#include "CharacterModule.h"
|
||||
#include "LuaData.h"
|
||||
#include "EventModule.h"
|
||||
namespace ECS
|
||||
{
|
||||
class RootMotionListener : public Ogre::NodeAnimationTrack::Listener {
|
||||
Ogre::Vector3 prevTranslation;
|
||||
mutable Ogre::Vector3 deltaMotion;
|
||||
flecs::entity e;
|
||||
|
||||
public:
|
||||
RootMotionListener(flecs::entity e)
|
||||
: Ogre::NodeAnimationTrack::Listener()
|
||||
, e(e)
|
||||
, prevTranslation(Ogre::Vector3::ZERO)
|
||||
, deltaMotion(Ogre::Vector3::ZERO)
|
||||
{
|
||||
}
|
||||
bool getInterpolatedKeyFrame(const Ogre::AnimationTrack *t,
|
||||
const Ogre::TimeIndex &timeIndex,
|
||||
Ogre::KeyFrame *kf) override
|
||||
{
|
||||
Ogre::TransformKeyFrame *vkf =
|
||||
static_cast<Ogre::TransformKeyFrame *>(kf);
|
||||
Ogre::KeyFrame *kf1, *kf2;
|
||||
Ogre::TransformKeyFrame *k1, *k2;
|
||||
unsigned short firstKeyIndex;
|
||||
float tm = t->getKeyFramesAtTime(timeIndex, &kf1, &kf2,
|
||||
&firstKeyIndex);
|
||||
k1 = static_cast<Ogre::TransformKeyFrame *>(kf1);
|
||||
k2 = static_cast<Ogre::TransformKeyFrame *>(kf2);
|
||||
Ogre::Vector3 translation;
|
||||
Ogre::Quaternion rotation;
|
||||
if (tm == 0.0f) {
|
||||
rotation = k1->getRotation();
|
||||
translation = k1->getTranslate();
|
||||
deltaMotion = translation;
|
||||
|
||||
// vkf->setRotation(k1->getRotation());
|
||||
// vkf->setTranslate(k1->getTranslate());
|
||||
// vkf->setScale(k1->getScale());
|
||||
} else {
|
||||
rotation = Ogre::Quaternion::nlerp(
|
||||
tm, k1->getRotation(), k2->getRotation(), true);
|
||||
translation =
|
||||
k1->getTranslate() +
|
||||
(k2->getTranslate() - k1->getTranslate()) * tm;
|
||||
deltaMotion = translation - prevTranslation;
|
||||
if (deltaMotion.squaredLength() >
|
||||
translation.squaredLength())
|
||||
deltaMotion = translation;
|
||||
}
|
||||
#if 0
|
||||
std::cout << "time: " << tm
|
||||
<< " Position: " << deltaMotion;
|
||||
std::cout << " Quaternion: " << rotation;
|
||||
std::cout << std::endl;
|
||||
#endif
|
||||
vkf->setTranslate(deltaMotion);
|
||||
// vkf->setTranslate(translation);
|
||||
vkf->setRotation(rotation);
|
||||
vkf->setScale(Ogre::Vector3(1, 1, 1));
|
||||
prevTranslation = translation;
|
||||
e.get_mut<CharacterBase>().mBoneMotion = deltaMotion;
|
||||
e.get_mut<CharacterBase>().mBonePrevMotion = prevTranslation;
|
||||
e.modified<CharacterBase>();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
struct AnimationTrigger;
|
||||
struct AnimationTriggerSubscriber {
|
||||
virtual void operator()(const AnimationTrigger *trigger) = 0;
|
||||
};
|
||||
struct AnimationTrigger {
|
||||
Ogre::String name;
|
||||
float time;
|
||||
float weight;
|
||||
std::vector<AnimationTriggerSubscriber *> subscriber_list;
|
||||
float getTriggerTime() const
|
||||
{
|
||||
return time;
|
||||
}
|
||||
float getMinWeight() const
|
||||
{
|
||||
return weight;
|
||||
}
|
||||
const Ogre::String &getName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
void notify(float weight)
|
||||
{
|
||||
int i;
|
||||
if (weight < this->weight)
|
||||
return;
|
||||
for (i = 0; i < subscriber_list.size(); i++)
|
||||
(*subscriber_list[i])(this);
|
||||
}
|
||||
void addSubscriber(AnimationTriggerSubscriber *sub)
|
||||
{
|
||||
if (std::find(subscriber_list.begin(), subscriber_list.end(),
|
||||
sub) != subscriber_list.end())
|
||||
return;
|
||||
subscriber_list.push_back(sub);
|
||||
}
|
||||
void removeSubscriber(AnimationTriggerSubscriber *sub)
|
||||
{
|
||||
auto it = std::find(subscriber_list.begin(),
|
||||
subscriber_list.end(), sub);
|
||||
if (it != subscriber_list.end())
|
||||
subscriber_list.erase(it);
|
||||
}
|
||||
void clearSubscribers()
|
||||
{
|
||||
subscriber_list.clear();
|
||||
}
|
||||
AnimationTrigger(const Ogre::String name, float time, float weight)
|
||||
: name(name)
|
||||
, time(time)
|
||||
, weight(weight)
|
||||
{
|
||||
}
|
||||
};
|
||||
struct Animation {
|
||||
Ogre::AnimationState *mAnimationState;
|
||||
Ogre::Animation *mSkelAnimation;
|
||||
Ogre::NodeAnimationTrack *mHipsTrack;
|
||||
Ogre::NodeAnimationTrack *mRootTrack;
|
||||
RootMotionListener *mListener;
|
||||
float m_weight;
|
||||
float m_accWeight;
|
||||
Animation(Ogre::Skeleton *skeleton, Ogre::AnimationState *animState,
|
||||
Ogre::Animation *skelAnimation, flecs::entity e)
|
||||
: mAnimationState(animState)
|
||||
, mSkelAnimation(skelAnimation)
|
||||
, mListener(OGRE_NEW RootMotionListener(e))
|
||||
, m_weight(0)
|
||||
, m_accWeight(0)
|
||||
{
|
||||
int j;
|
||||
mRootTrack = nullptr;
|
||||
mHipsTrack = nullptr;
|
||||
for (const auto &it : mSkelAnimation->_getNodeTrackList()) {
|
||||
Ogre::NodeAnimationTrack *track = it.second;
|
||||
Ogre::String trackName =
|
||||
track->getAssociatedNode()->getName();
|
||||
if (trackName == "mixamorig:Hips") {
|
||||
mHipsTrack = track;
|
||||
} else if (trackName == "Root") {
|
||||
mRootTrack = track;
|
||||
// mRootTracks[i]->removeAllKeyFrames();
|
||||
}
|
||||
}
|
||||
if (!mRootTrack) {
|
||||
Ogre::Bone *bone = skeleton->getBone("Root");
|
||||
mRootTrack = mSkelAnimation->createNodeTrack(
|
||||
bone->getHandle(), bone);
|
||||
Ogre::TransformKeyFrame *kf =
|
||||
mRootTrack->createNodeKeyFrame(0.0f);
|
||||
kf->setTranslate(Ogre::Vector3::ZERO);
|
||||
kf->setRotation(Ogre::Quaternion::IDENTITY);
|
||||
}
|
||||
mRootTrack->setListener(mListener);
|
||||
}
|
||||
Ogre::String getName()
|
||||
{
|
||||
return mAnimationState->getAnimationName();
|
||||
}
|
||||
void setLoop(bool loop)
|
||||
{
|
||||
mAnimationState->setLoop(loop);
|
||||
}
|
||||
bool getLoop() const
|
||||
{
|
||||
return mAnimationState->getLoop();
|
||||
}
|
||||
void setEnabled(bool enabled)
|
||||
{
|
||||
mAnimationState->setEnabled(enabled);
|
||||
}
|
||||
bool getEnabled() const
|
||||
{
|
||||
return mAnimationState->getEnabled();
|
||||
}
|
||||
void setWeight(float weight)
|
||||
{
|
||||
bool enabled = weight > 0.001f;
|
||||
setEnabled(enabled);
|
||||
mAnimationState->setWeight(weight);
|
||||
m_weight = weight;
|
||||
}
|
||||
float getWeight() const
|
||||
{
|
||||
return m_weight;
|
||||
}
|
||||
bool addTime(float time)
|
||||
{
|
||||
bool result = mAnimationState->getEnabled();
|
||||
if (!result)
|
||||
return result;
|
||||
Ogre::TimeIndex index = mSkelAnimation->_getTimeIndex(
|
||||
mAnimationState->getTimePosition());
|
||||
Ogre::KeyFrame *kf1, *kf2;
|
||||
unsigned short prev_index, next_index;
|
||||
mRootTrack->getKeyFramesAtTime(index, &kf1, &kf2, &prev_index);
|
||||
unsigned int previous_frame = index.getKeyIndex();
|
||||
mAnimationState->addTime(time);
|
||||
index = mSkelAnimation->_getTimeIndex(
|
||||
mAnimationState->getTimePosition());
|
||||
mRootTrack->getKeyFramesAtTime(index, &kf1, &kf2, &next_index);
|
||||
return prev_index != next_index;
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
mAnimationState->setTimePosition(0);
|
||||
}
|
||||
void resetAccWeight()
|
||||
{
|
||||
m_accWeight = 0;
|
||||
}
|
||||
void increaseAccWeight(float weight)
|
||||
{
|
||||
m_accWeight += weight;
|
||||
}
|
||||
float getAccWeight() const
|
||||
{
|
||||
return m_accWeight;
|
||||
}
|
||||
float getLength() const
|
||||
{
|
||||
return mAnimationState->getLength();
|
||||
if (getEnabled())
|
||||
return mAnimationState->getLength();
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
float getTimePosition() const
|
||||
{
|
||||
return mAnimationState->getTimePosition();
|
||||
if (getEnabled())
|
||||
return mAnimationState->getTimePosition();
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
};
|
||||
|
||||
struct AnimationNode {
|
||||
std::vector<AnimationNode *> children;
|
||||
float m_weight;
|
||||
Ogre::String m_name;
|
||||
std::multimap<float, AnimationTrigger *> trigger_list;
|
||||
AnimationNode()
|
||||
: m_weight(0)
|
||||
{
|
||||
}
|
||||
virtual bool addTime(float time) = 0;
|
||||
virtual void setWeight(float weight) = 0;
|
||||
virtual void reset() = 0;
|
||||
virtual float getLength() const = 0;
|
||||
virtual float getTimePosition() const = 0;
|
||||
float getWeight()
|
||||
{
|
||||
return m_weight;
|
||||
}
|
||||
const Ogre::String &getName()
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
void setName(const Ogre::String &name)
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
virtual float getTime() const
|
||||
{
|
||||
float l = getLength();
|
||||
if (l > 0.0f)
|
||||
return getTimePosition() / l;
|
||||
return 0.0f;
|
||||
}
|
||||
void addTrigger(AnimationTrigger *trigger)
|
||||
{
|
||||
trigger_list.insert(std::pair<float, AnimationTrigger *>(
|
||||
trigger->getTriggerTime(), trigger));
|
||||
}
|
||||
void clearTriggers()
|
||||
{
|
||||
auto it = trigger_list.begin();
|
||||
while (it != trigger_list.end()) {
|
||||
delete it->second;
|
||||
it++;
|
||||
}
|
||||
trigger_list.clear();
|
||||
}
|
||||
float mpreUpdateTime;
|
||||
void preUpdateTriggers()
|
||||
{
|
||||
mpreUpdateTime = getTime();
|
||||
}
|
||||
void postUpdateTriggers(float delta)
|
||||
{
|
||||
float postUpdateTime = getTime();
|
||||
bool positive = delta >= 0.0f;
|
||||
if (positive)
|
||||
updateTriggers(mpreUpdateTime, postUpdateTime);
|
||||
else
|
||||
updateTriggers(postUpdateTime, mpreUpdateTime);
|
||||
}
|
||||
void updateTriggers(float currentTime, float nextTime)
|
||||
{
|
||||
int i;
|
||||
float weight = getWeight();
|
||||
if (currentTime <= nextTime) {
|
||||
auto it = trigger_list.lower_bound(currentTime);
|
||||
while (it != trigger_list.end()) {
|
||||
if (nextTime <=
|
||||
it->second->getTriggerTime()) // in future, sorrted by time
|
||||
return;
|
||||
it->second->notify(weight);
|
||||
it++;
|
||||
}
|
||||
} else {
|
||||
updateTriggers(currentTime, 1);
|
||||
updateTriggers(0, nextTime);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct AnimationNodeAnimation : AnimationNode {
|
||||
Animation *mAnimation;
|
||||
bool enabled;
|
||||
AnimationNodeAnimation(Animation *animation)
|
||||
: AnimationNode()
|
||||
, mAnimation(animation)
|
||||
{
|
||||
}
|
||||
bool addTime(float time)
|
||||
{
|
||||
bool ret;
|
||||
preUpdateTriggers();
|
||||
ret = mAnimation->addTime(time);
|
||||
postUpdateTriggers(time);
|
||||
return ret;
|
||||
}
|
||||
void setWeight(float weight)
|
||||
{
|
||||
m_weight = weight;
|
||||
enabled = weight > 0.001f;
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
mAnimation->reset();
|
||||
}
|
||||
float getLength() const
|
||||
{
|
||||
return mAnimation->getLength();
|
||||
if (enabled)
|
||||
return mAnimation->getLength();
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
float getTimePosition() const
|
||||
{
|
||||
return mAnimation->getTimePosition();
|
||||
if (enabled)
|
||||
return mAnimation->getTimePosition();
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
};
|
||||
struct AnimationNodeStateMachineState : AnimationNode {
|
||||
AnimationNodeStateMachineState()
|
||||
: AnimationNode()
|
||||
{
|
||||
}
|
||||
bool addTime(float time)
|
||||
{
|
||||
bool ret;
|
||||
preUpdateTriggers();
|
||||
ret = children[0]->addTime(time);
|
||||
postUpdateTriggers(time);
|
||||
return ret;
|
||||
}
|
||||
void setWeight(float weight)
|
||||
{
|
||||
m_weight = weight;
|
||||
bool enabled = weight > 0.001f;
|
||||
children[0]->setWeight(weight);
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
children[0]->reset();
|
||||
}
|
||||
float getLength() const
|
||||
{
|
||||
return children[0]->getLength();
|
||||
}
|
||||
float getTimePosition() const
|
||||
{
|
||||
return children[0]->getTimePosition();
|
||||
}
|
||||
};
|
||||
struct AnimationNodeSpeed : AnimationNode {
|
||||
float m_speed;
|
||||
bool enabled;
|
||||
AnimationNodeSpeed(float speed)
|
||||
: AnimationNode()
|
||||
, m_speed(speed)
|
||||
, enabled(false)
|
||||
{
|
||||
}
|
||||
bool addTime(float time)
|
||||
{
|
||||
bool ret;
|
||||
preUpdateTriggers();
|
||||
ret = children[0]->addTime(time * m_speed);
|
||||
postUpdateTriggers(time);
|
||||
return ret;
|
||||
}
|
||||
void setWeight(float weight)
|
||||
{
|
||||
m_weight = weight;
|
||||
children[0]->setWeight(weight);
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
children[0]->reset();
|
||||
}
|
||||
float getLength() const
|
||||
{
|
||||
if (m_speed > 0.0f || m_speed < 0.0f)
|
||||
return children[0]->getLength() / m_speed;
|
||||
return 0.0f;
|
||||
}
|
||||
float getTimePosition() const
|
||||
{
|
||||
if (m_speed > 0.0f || m_speed < 0.0f)
|
||||
return children[0]->getTimePosition() / m_speed;
|
||||
return 0.0f;
|
||||
}
|
||||
float getTime() const override
|
||||
{
|
||||
float l = children[0]->getLength();
|
||||
if (l > 0.0f)
|
||||
return children[0]->getTimePosition() / l;
|
||||
return 0.0f;
|
||||
}
|
||||
};
|
||||
struct AnimationNodeStateMachine : AnimationNode {
|
||||
std::map<Ogre::String, AnimationNode *> stateMap;
|
||||
std::set<AnimationNode *> fade_in, fade_out;
|
||||
AnimationNode *currentAnim, *nextAnim;
|
||||
float fade_speed;
|
||||
Ogre::String mCurrentStateName;
|
||||
bool configured;
|
||||
bool debug;
|
||||
AnimationNodeStateMachine(float fade_speed, bool debug = false)
|
||||
: AnimationNode()
|
||||
, currentAnim(nullptr)
|
||||
, nextAnim(nullptr)
|
||||
, fade_speed(fade_speed)
|
||||
, mCurrentStateName("")
|
||||
, configured(false)
|
||||
, debug(debug)
|
||||
{
|
||||
m_weight = 1.0f;
|
||||
}
|
||||
bool addTime(float time)
|
||||
{
|
||||
int i;
|
||||
preUpdateTriggers();
|
||||
if (!configured) {
|
||||
configure();
|
||||
configured = true;
|
||||
}
|
||||
#ifdef VDEBUG
|
||||
if (debug) {
|
||||
std::cout << "state machine addTime" << std::endl;
|
||||
std::cout
|
||||
<< "state machine children: " << children.size()
|
||||
<< std::endl;
|
||||
}
|
||||
#endif
|
||||
for (i = 0; i < children.size(); i++) {
|
||||
#ifdef VDEBUG
|
||||
if (debug)
|
||||
std::cout << "child weight: " << i << " "
|
||||
<< children[i]->getWeight()
|
||||
<< std::endl;
|
||||
#endif
|
||||
AnimationNode *child = children[i];
|
||||
if (fade_in.find(child) != fade_in.end()) {
|
||||
Ogre::Real newWeight =
|
||||
child->getWeight() + time * fade_speed;
|
||||
child->setWeight(Ogre::Math::Clamp<Ogre::Real>(
|
||||
newWeight * m_weight, 0, m_weight));
|
||||
#ifdef VDEBUG
|
||||
if (debug) {
|
||||
std::cout << "fade in: " << newWeight
|
||||
<< std::endl;
|
||||
std::cout << "m_weight: " << m_weight
|
||||
<< std::endl;
|
||||
}
|
||||
#endif
|
||||
if (newWeight >= 1)
|
||||
fade_in.erase(child);
|
||||
}
|
||||
if (fade_out.find(child) != fade_out.end()) {
|
||||
Ogre::Real newWeight =
|
||||
child->getWeight() - time * fade_speed;
|
||||
child->setWeight(Ogre::Math::Clamp<Ogre::Real>(
|
||||
newWeight * m_weight, 0, 1));
|
||||
if (newWeight <= 0)
|
||||
fade_out.erase(child);
|
||||
}
|
||||
}
|
||||
OgreAssert(currentAnim, "bad current anim");
|
||||
bool ret = false;
|
||||
if (currentAnim)
|
||||
ret = currentAnim->addTime(time);
|
||||
postUpdateTriggers(time);
|
||||
return ret;
|
||||
}
|
||||
void setWeight(float weight)
|
||||
{
|
||||
int i;
|
||||
if (weight > m_weight && currentAnim)
|
||||
fade_in.insert(currentAnim);
|
||||
if (weight < m_weight && currentAnim &&
|
||||
currentAnim->getWeight() > weight)
|
||||
currentAnim->setWeight(weight);
|
||||
m_weight = weight;
|
||||
bool enabled = weight > 0.001f;
|
||||
/* do not update child state yet */
|
||||
}
|
||||
void addState(AnimationNode *state)
|
||||
{
|
||||
const Ogre::String &name = state->getName();
|
||||
stateMap[name] = state;
|
||||
state->setWeight(0);
|
||||
fade_in.erase(state);
|
||||
fade_out.erase(state);
|
||||
std::cout << "registered state: " << name << std::endl;
|
||||
}
|
||||
void configure()
|
||||
{
|
||||
int i;
|
||||
if (debug)
|
||||
std::cout << "children: " << children.size()
|
||||
<< std::endl;
|
||||
for (i = 0; i < children.size(); i++)
|
||||
addState(children[i]);
|
||||
if (debug)
|
||||
std::cout << "configure called" << std::endl;
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < children.size(); i++)
|
||||
children[i]->reset();
|
||||
}
|
||||
void setAnimation(const Ogre::String &anim_state, bool reset = false)
|
||||
{
|
||||
if (!configured) {
|
||||
configure();
|
||||
configured = true;
|
||||
}
|
||||
OgreAssert(stateMap.find(anim_state) != stateMap.end(),
|
||||
"Bad animation state: " + anim_state);
|
||||
nextAnim = stateMap[anim_state];
|
||||
if (nextAnim == currentAnim)
|
||||
return;
|
||||
if (currentAnim != nullptr) {
|
||||
fade_out.insert(currentAnim);
|
||||
fade_in.erase(currentAnim);
|
||||
}
|
||||
fade_out.erase(nextAnim);
|
||||
fade_in.insert(nextAnim);
|
||||
nextAnim->setWeight(0);
|
||||
if (reset)
|
||||
nextAnim->reset();
|
||||
currentAnim = nextAnim;
|
||||
mCurrentStateName = anim_state;
|
||||
}
|
||||
const Ogre::String &getCurrentState() const
|
||||
{
|
||||
return mCurrentStateName;
|
||||
}
|
||||
float getLength() const
|
||||
{
|
||||
if (currentAnim)
|
||||
return currentAnim->getLength();
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
float getTimePosition() const
|
||||
{
|
||||
if (currentAnim)
|
||||
return currentAnim->getTimePosition();
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
};
|
||||
|
||||
#define ANIM_FADE_SPEED \
|
||||
7.5f // animation crossfade speed in % of full weight per second
|
||||
|
||||
struct AnimationNodeOutput : AnimationNode {
|
||||
float m_weight;
|
||||
float m_speed;
|
||||
AnimationNodeOutput()
|
||||
: AnimationNode()
|
||||
, m_weight(1.0f)
|
||||
, m_speed(1.0f)
|
||||
{
|
||||
}
|
||||
bool addTime(float time)
|
||||
{
|
||||
bool ret;
|
||||
preUpdateTriggers();
|
||||
ret = children[0]->addTime(time * m_speed);
|
||||
postUpdateTriggers(time);
|
||||
return ret;
|
||||
}
|
||||
void setWeight(float weight)
|
||||
{
|
||||
m_weight = weight;
|
||||
bool enabled = weight > 0.001f;
|
||||
children[0]->setWeight(weight);
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
children[0]->reset();
|
||||
}
|
||||
float getLength() const
|
||||
{
|
||||
return children[0]->getLength();
|
||||
}
|
||||
float getTimePosition() const
|
||||
{
|
||||
return children[0]->getTimePosition();
|
||||
}
|
||||
};
|
||||
|
||||
struct AnimationSystem : AnimationNode {
|
||||
bool debug;
|
||||
AnimationSystem(bool debug = false)
|
||||
: debug(debug)
|
||||
, m_builder(this, debug)
|
||||
{
|
||||
}
|
||||
std::unordered_map<Ogre::String, Animation *> animation_list;
|
||||
std::vector<Animation *> vanimation_list;
|
||||
void add_animation(const Ogre::String &name, Animation *animation)
|
||||
{
|
||||
OgreAssert(animation, "No animation " + name);
|
||||
animation_list[name] = animation;
|
||||
vanimation_list.push_back(animation);
|
||||
}
|
||||
void clear_animations()
|
||||
{
|
||||
animation_list.clear();
|
||||
vanimation_list.clear();
|
||||
}
|
||||
struct AnimationSystemBuilder {
|
||||
AnimationSystem *mAnimationSystem;
|
||||
std::vector<AnimationNode *> animation_nodes;
|
||||
AnimationNode *parent;
|
||||
std::list<AnimationNode *> parent_stack;
|
||||
std::unordered_map<Ogre::String, AnimationNode *> nodeMap;
|
||||
std::vector<AnimationNodeAnimation *> animationNodeList;
|
||||
bool debug;
|
||||
AnimationSystemBuilder(AnimationSystem *animationSystem,
|
||||
bool debug = false)
|
||||
: mAnimationSystem(animationSystem)
|
||||
, debug(debug)
|
||||
{
|
||||
}
|
||||
AnimationSystemBuilder *output()
|
||||
{
|
||||
AnimationNodeOutput *onode = new AnimationNodeOutput();
|
||||
animation_nodes.push_back(onode);
|
||||
parent = onode;
|
||||
return this;
|
||||
}
|
||||
AnimationSystemBuilder *
|
||||
animation(const Ogre::String &animation_name)
|
||||
{
|
||||
OgreAssert(parent, "bad parent");
|
||||
Animation *animation =
|
||||
mAnimationSystem->animation_list[animation_name];
|
||||
OgreAssert(animation,
|
||||
"bad animation " + animation_name);
|
||||
AnimationNodeAnimation *onode =
|
||||
new AnimationNodeAnimation(animation);
|
||||
OgreAssert(onode, "bad animation");
|
||||
OgreAssert(onode->mAnimation, "bad animation");
|
||||
animation_nodes.push_back(onode);
|
||||
parent->children.push_back(onode);
|
||||
animationNodeList.push_back(onode);
|
||||
return this;
|
||||
}
|
||||
AnimationSystemBuilder *trigger(flecs::entity e,
|
||||
const Ogre::String &name,
|
||||
float time,
|
||||
const Ogre::String &event)
|
||||
{
|
||||
struct EventSubscriber : AnimationTriggerSubscriber {
|
||||
flecs::entity ent;
|
||||
Ogre::String event;
|
||||
void operator()(const AnimationTrigger *trigger)
|
||||
{
|
||||
ent.get_mut<EventData>().add(ent, event,
|
||||
ent, ent);
|
||||
}
|
||||
EventSubscriber(flecs::entity e,
|
||||
const Ogre::String &event)
|
||||
: ent(e)
|
||||
, event(event)
|
||||
{
|
||||
}
|
||||
};
|
||||
OgreAssert(parent, "bad parent");
|
||||
AnimationTrigger *trigger =
|
||||
new AnimationTrigger(name, time, 0.1f);
|
||||
EventSubscriber *sub = new EventSubscriber(e, event);
|
||||
trigger->addSubscriber(sub);
|
||||
parent->addTrigger(trigger);
|
||||
return this;
|
||||
} // leaf too...
|
||||
AnimationSystemBuilder *
|
||||
transition_end(const Ogre::String &state_from,
|
||||
const Ogre::String &state_to)
|
||||
{
|
||||
struct EndTransitionSubscriber
|
||||
: AnimationTriggerSubscriber {
|
||||
AnimationNodeStateMachine *sm;
|
||||
Ogre::String next_state;
|
||||
bool reset;
|
||||
void operator()(const AnimationTrigger *trigger)
|
||||
{
|
||||
sm->setAnimation(next_state, reset);
|
||||
}
|
||||
EndTransitionSubscriber(
|
||||
AnimationNodeStateMachine *sm,
|
||||
const Ogre::String &next_state,
|
||||
bool reset = true)
|
||||
: sm(sm)
|
||||
, next_state(next_state)
|
||||
, reset(reset)
|
||||
{
|
||||
}
|
||||
};
|
||||
OgreAssert(parent, "no parent");
|
||||
AnimationNodeStateMachine *sm =
|
||||
static_cast<AnimationNodeStateMachine *>(
|
||||
parent);
|
||||
OgreAssert(sm, "no state machine");
|
||||
AnimationTrigger *trigger = new AnimationTrigger(
|
||||
"transition:" + state_from + "_" + state_to,
|
||||
0.99f, 0.1f);
|
||||
EndTransitionSubscriber *sub =
|
||||
new EndTransitionSubscriber(sm, state_to);
|
||||
int i;
|
||||
bool ok = false;
|
||||
for (i = 0; i < sm->children.size(); i++) {
|
||||
if (sm->children[i]->getName() == state_from) {
|
||||
trigger->addSubscriber(sub);
|
||||
sm->children[i]->addTrigger(trigger);
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
OgreAssert(ok, "Failed to set transition");
|
||||
return this;
|
||||
}
|
||||
AnimationSystemBuilder *speed(float speed,
|
||||
const Ogre::String &anchor = "")
|
||||
{
|
||||
OgreAssert(parent, "bad parent");
|
||||
AnimationNodeSpeed *onode =
|
||||
new AnimationNodeSpeed(speed);
|
||||
animation_nodes.push_back(onode);
|
||||
parent->children.push_back(onode);
|
||||
parent_stack.push_back(parent);
|
||||
parent = onode;
|
||||
if (anchor.length() > 0)
|
||||
nodeMap[anchor] = onode;
|
||||
return this;
|
||||
}
|
||||
AnimationSystemBuilder *
|
||||
state_machine(float fade_time = ANIM_FADE_SPEED,
|
||||
const Ogre::String &anchor = "")
|
||||
{
|
||||
OgreAssert(parent, "bad parent");
|
||||
AnimationNodeStateMachine *onode =
|
||||
new AnimationNodeStateMachine(fade_time, debug);
|
||||
animation_nodes.push_back(onode);
|
||||
parent->children.push_back(onode);
|
||||
parent_stack.push_back(parent);
|
||||
parent = onode;
|
||||
if (anchor.length() > 0)
|
||||
nodeMap[anchor] = onode;
|
||||
return this;
|
||||
}
|
||||
AnimationSystemBuilder *state(const Ogre::String &state_name)
|
||||
{
|
||||
OgreAssert(parent, "bad parent");
|
||||
AnimationNodeStateMachineState *onode =
|
||||
new AnimationNodeStateMachineState;
|
||||
animation_nodes.push_back(onode);
|
||||
parent->children.push_back(onode);
|
||||
parent_stack.push_back(parent);
|
||||
parent = onode;
|
||||
onode->setName(state_name);
|
||||
return this;
|
||||
}
|
||||
AnimationSystemBuilder *end()
|
||||
{
|
||||
parent = parent_stack.back();
|
||||
parent_stack.pop_back();
|
||||
return this;
|
||||
}
|
||||
};
|
||||
AnimationSystemBuilder m_builder;
|
||||
bool addTime(float time)
|
||||
{
|
||||
int i;
|
||||
preUpdateTriggers();
|
||||
bool ret = m_builder.animation_nodes[0]->addTime(time);
|
||||
for (i = 0; i < m_builder.animationNodeList.size(); i++) {
|
||||
AnimationNodeAnimation *anim =
|
||||
m_builder.animationNodeList[i];
|
||||
OgreAssert(anim->mAnimation, "No animation");
|
||||
float weight = anim->getWeight();
|
||||
anim->mAnimation->increaseAccWeight(weight);
|
||||
#ifdef VDEBUG
|
||||
if (debug)
|
||||
std::cout << i << " node: "
|
||||
<< anim->mAnimation->getName() << " "
|
||||
<< weight << std::endl;
|
||||
#endif
|
||||
}
|
||||
for (i = 0; i < vanimation_list.size(); i++) {
|
||||
float weight = vanimation_list[i]->getAccWeight();
|
||||
vanimation_list[i]->setWeight(weight);
|
||||
vanimation_list[i]->resetAccWeight();
|
||||
#define VDEBUG
|
||||
#ifdef VDEBUG
|
||||
if (debug && vanimation_list[i]->getEnabled())
|
||||
std::cout << i << " animation: "
|
||||
<< vanimation_list[i]->getName()
|
||||
<< " " << weight << std::endl;
|
||||
#endif
|
||||
#undef VDEBUG
|
||||
}
|
||||
postUpdateTriggers(time);
|
||||
return ret;
|
||||
}
|
||||
void setWeight(float weight)
|
||||
{
|
||||
m_builder.animation_nodes[0]->setWeight(weight);
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
m_builder.animation_nodes[0]->reset();
|
||||
}
|
||||
AnimationSystemBuilder *builder()
|
||||
{
|
||||
m_builder.animation_nodes.reserve(8);
|
||||
m_builder.parent = nullptr;
|
||||
return &m_builder;
|
||||
}
|
||||
template <class T> T *get(const Ogre::String &name)
|
||||
{
|
||||
return static_cast<T *>(m_builder.nodeMap[name]);
|
||||
}
|
||||
float getLength() const
|
||||
{
|
||||
return m_builder.animation_nodes[0]->getLength();
|
||||
}
|
||||
float getTimePosition() const
|
||||
{
|
||||
return m_builder.animation_nodes[0]->getTimePosition();
|
||||
}
|
||||
};
|
||||
|
||||
struct AnimationControl {
|
||||
bool configured;
|
||||
AnimationSystem *mAnimationSystem;
|
||||
};
|
||||
struct DefaultAnimation {
|
||||
std::vector<std::pair<std::string, std::string> > animations;
|
||||
};
|
||||
struct CharacterAnimationModule {
|
||||
CharacterAnimationModule(flecs::world &ecs);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
42
src/gamedata/CharacterManagerModule.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#include <Ogre.h>
|
||||
#include <OgreConfigFile.h>
|
||||
#include "GameData.h"
|
||||
#include "Components.h"
|
||||
#include "CharacterModule.h"
|
||||
#include "CharacterAnimationModule.h"
|
||||
#include "CharacterManagerModule.h"
|
||||
|
||||
namespace ECS
|
||||
{
|
||||
CharacterManagerModule::CharacterManagerModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.module<CharacterManagerModule>();
|
||||
ecs.import <CharacterModule>();
|
||||
ecs.import <CharacterAnimationModule>();
|
||||
}
|
||||
flecs::entity
|
||||
CharacterManagerModule::createPlayer(const Ogre::Vector3 &position,
|
||||
const Ogre::Quaternion &rotation)
|
||||
{
|
||||
player = ECS::get().entity("player");
|
||||
Ogre::Vector3 playerPos(0, 0, 4);
|
||||
player.set<CharacterLocation>({ rotation, position });
|
||||
player.set<CharacterConf>({ "normal-male.glb" });
|
||||
player.add<Character>();
|
||||
player.add<Player>();
|
||||
return player;
|
||||
}
|
||||
flecs::entity
|
||||
CharacterManagerModule::createCharacterData(const Ogre::String model,
|
||||
const Ogre::Vector3 &position,
|
||||
const Ogre::Quaternion &rotation)
|
||||
{
|
||||
flecs::entity e =
|
||||
ECS::get()
|
||||
.entity()
|
||||
.set<CharacterLocation>({ rotation, position })
|
||||
.set<CharacterConf>({ model })
|
||||
.add<Character>();
|
||||
return e;
|
||||
}
|
||||
}
|
||||
17
src/gamedata/CharacterManagerModule.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef _CHARACTER_MANAGER_MODULE_
|
||||
#define _CHARACTER_MANAGER_MODULE_
|
||||
#include <flecs.h>
|
||||
namespace ECS
|
||||
{
|
||||
struct CharacterManagerModule {
|
||||
std::set<flecs::entity> characters;
|
||||
CharacterManagerModule(flecs::world &ecs);
|
||||
flecs::entity createPlayer(const Ogre::Vector3 &position,
|
||||
const Ogre::Quaternion &rotation);
|
||||
flecs::entity createCharacterData(const Ogre::String model,
|
||||
const Ogre::Vector3 &position,
|
||||
const Ogre::Quaternion &rotation);
|
||||
void removeCharacterData(int id);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
@@ -1,11 +1,13 @@
|
||||
#include <iostream>
|
||||
#include <Ogre.h>
|
||||
#include <OgreBullet.h>
|
||||
#include "GameData.h"
|
||||
#include "CharacterModule.h"
|
||||
#include "WaterModule.h"
|
||||
#include "TerrainModule.h"
|
||||
#include "Components.h"
|
||||
#include "PhysicsModule.h"
|
||||
#include "CharacterAnimationModule.h"
|
||||
#include "CharacterModule.h"
|
||||
#include "goap.h"
|
||||
namespace ECS
|
||||
{
|
||||
CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
@@ -15,10 +17,22 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
ecs.component<Character>();
|
||||
ecs.component<Player>();
|
||||
ecs.component<CharacterBase>();
|
||||
ecs.component<CharacterVelocity>();
|
||||
ecs.component<CharacterBody>();
|
||||
ecs.component<CharacterGravity>();
|
||||
ecs.component<CharacterLocation>();
|
||||
ecs.component<CharacterBuoyancy>();
|
||||
ecs.component<CharacterConf>();
|
||||
ecs.component<CharacterDisablePhysics>();
|
||||
ecs.component<CharacterUpdatePhysicsState>();
|
||||
ecs.component<CharacterInActuator>();
|
||||
ecs.component<Blackboard>();
|
||||
ecs.component<ActionTarget>();
|
||||
ecs.component<Plan>();
|
||||
ecs.component<Male>();
|
||||
ecs.component<Female>();
|
||||
ecs.component<Planner>().add(flecs::Singleton);
|
||||
ecs.import <TerrainModule>();
|
||||
ecs.import <WaterModule>();
|
||||
|
||||
ecs.system<EngineData, CharacterBase>("UpdateTimer")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([this](EngineData &eng, CharacterBase &ch) {
|
||||
@@ -49,6 +63,22 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
zaxis = 0.0f;
|
||||
else
|
||||
zaxis = Ogre::Math::Sign(zaxis);
|
||||
if (active & 32)
|
||||
input.act = true;
|
||||
else
|
||||
input.act = false;
|
||||
if (pressed & 32)
|
||||
input.act_pressed = true;
|
||||
else
|
||||
input.act_pressed = false;
|
||||
if (active & 64)
|
||||
input.act2 = true;
|
||||
else
|
||||
input.act2 = false;
|
||||
if (pressed & 64)
|
||||
input.act2_pressed = true;
|
||||
else
|
||||
input.act2_pressed = false;
|
||||
input.motion.z = zaxis;
|
||||
float xaxis = input.motion.x;
|
||||
xaxis *= 0.9f;
|
||||
@@ -81,111 +111,6 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
}
|
||||
ECS::get().modified<ECS::Input>();
|
||||
});
|
||||
ecs.system<const CharacterBase, AnimationControl>("HandleAnimations")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([this](flecs::entity e, const CharacterBase &ch,
|
||||
AnimationControl &anim) {
|
||||
if (!anim.configured && ch.mSkeleton) {
|
||||
int i, j;
|
||||
ch.mSkeleton->setBlendMode(
|
||||
Ogre::ANIMBLEND_CUMULATIVE);
|
||||
Ogre::String
|
||||
animNames[AnimationControl::NUM_ANIMS] = {
|
||||
"idle", "walking", "running",
|
||||
"treading_water", "swimming"
|
||||
};
|
||||
for (i = 0; i < AnimationControl::NUM_ANIMS;
|
||||
i++) {
|
||||
anim.mAnims[i] =
|
||||
ch.mBodyEnt->getAnimationState(
|
||||
animNames[i]);
|
||||
anim.mAnims[i]->setLoop(true);
|
||||
anim.mAnims[i]->setEnabled(true);
|
||||
anim.mAnims[i]->setWeight(0);
|
||||
anim.mFadingIn[i] = false;
|
||||
anim.mFadingOut[i] = false;
|
||||
anim.mSkelAnimations[i] =
|
||||
ch.mSkeleton->getAnimation(
|
||||
animNames[i]);
|
||||
for (const auto &it :
|
||||
anim.mSkelAnimations[i]
|
||||
->_getNodeTrackList()) {
|
||||
Ogre::NodeAnimationTrack *track =
|
||||
it.second;
|
||||
Ogre::String trackName =
|
||||
track->getAssociatedNode()
|
||||
->getName();
|
||||
if (trackName ==
|
||||
"mixamorig:Hips") {
|
||||
anim.mHipsTracks[i] =
|
||||
track;
|
||||
} else if (trackName ==
|
||||
"Root") {
|
||||
anim.mRootTracks[i] =
|
||||
track;
|
||||
// mRootTracks[i]->removeAllKeyFrames();
|
||||
}
|
||||
}
|
||||
Ogre::Vector3 delta =
|
||||
Ogre::Vector3::ZERO;
|
||||
Ogre::Vector3 motion =
|
||||
Ogre::Vector3::ZERO;
|
||||
for (j = 0;
|
||||
j < anim.mRootTracks[i]
|
||||
->getNumKeyFrames();
|
||||
j++) {
|
||||
Ogre::Vector3 trans =
|
||||
anim.mRootTracks[i]
|
||||
->getNodeKeyFrame(
|
||||
j)
|
||||
->getTranslate();
|
||||
if (j == 0)
|
||||
delta = trans;
|
||||
else
|
||||
delta = trans - motion;
|
||||
anim.mRootTracks[i]
|
||||
->getNodeKeyFrame(j)
|
||||
->setTranslate(delta);
|
||||
motion = trans;
|
||||
}
|
||||
}
|
||||
anim.nextAnim = AnimationControl::ANIM_IDLE;
|
||||
setAnimation(anim);
|
||||
anim.configured = true;
|
||||
}
|
||||
});
|
||||
ecs.system<AnimationControl>("HandleAnimations0")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([this](flecs::entity e, AnimationControl &anim) {
|
||||
if (anim.currentAnim != anim.nextAnim)
|
||||
setAnimation(anim);
|
||||
});
|
||||
ecs.system<const EngineData, const Input, CharacterBase,
|
||||
AnimationControl>("HandleAnimations1")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([this](const EngineData &eng, const Input &input,
|
||||
CharacterBase &ch, AnimationControl &anim) {
|
||||
float delta = eng.delta;
|
||||
Ogre::Real animSpeed = 1;
|
||||
if (anim.currentAnim != AnimationControl::ANIM_NONE) {
|
||||
if (anim.currentAnim ==
|
||||
AnimationControl::ANIM_WALK)
|
||||
anim.mAnims[anim.currentAnim]->addTime(
|
||||
delta * 1.0f);
|
||||
else if (anim.currentAnim ==
|
||||
AnimationControl::ANIM_SWIMMING &&
|
||||
input.fast)
|
||||
anim.mAnims[anim.currentAnim]->addTime(
|
||||
delta * 20.0f);
|
||||
else
|
||||
anim.mAnims[anim.currentAnim]->addTime(
|
||||
delta * animSpeed);
|
||||
}
|
||||
fadeAnimations(anim, delta);
|
||||
if (!ch.mRootBone)
|
||||
return;
|
||||
ch.mBoneMotion = ch.mRootBone->getPosition();
|
||||
});
|
||||
ecs.system<CharacterBase>()
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<TerrainReady>()
|
||||
@@ -204,6 +129,7 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
else if (current_subm < 0.8f)
|
||||
ch.is_submerged = false;
|
||||
});
|
||||
#if 0
|
||||
ecs.system<const EngineData, const CharacterBase, CharacterVelocity>(
|
||||
"HandleGravityBouyanceWater")
|
||||
.kind(flecs::OnUpdate)
|
||||
@@ -211,6 +137,7 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
.with<WaterReady>()
|
||||
.with<InWater>()
|
||||
.without<CharacterDisablePhysics>()
|
||||
.without<CharacterUpdatePhysicsState>()
|
||||
.each([this](flecs::entity e, const EngineData &eng,
|
||||
const CharacterBase &ch, CharacterVelocity &gr) {
|
||||
Ogre::Vector3 gravity(0, -9.8f, 0);
|
||||
@@ -242,6 +169,8 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
gr.gvelocity *= (1.0 - eng.delta);
|
||||
gr.velocity.y *= (1.0 - eng.delta);
|
||||
});
|
||||
#endif
|
||||
#if 0
|
||||
ecs.system<const EngineData, const CharacterBase, CharacterVelocity>(
|
||||
"HandleGravityNoWater")
|
||||
.kind(flecs::OnUpdate)
|
||||
@@ -261,191 +190,13 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
gr.gvelocity *= (1.0 - eng.delta);
|
||||
gr.velocity.y *= (1.0 - eng.delta);
|
||||
});
|
||||
ecs.system<const EngineData, const AnimationControl,
|
||||
const CharacterBase, CharacterVelocity>("HandleSwimming")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<TerrainReady>()
|
||||
.with<WaterReady>()
|
||||
.with<InWater>()
|
||||
.with<CharacterBuoyancy>()
|
||||
.each([this](flecs::entity e, const EngineData &eng,
|
||||
const AnimationControl &anim,
|
||||
const CharacterBase &ch, CharacterVelocity &gr) {
|
||||
if (anim.currentAnim ==
|
||||
AnimationControl::ANIM_SWIMMING) {
|
||||
float h = Ogre::Math::Clamp(
|
||||
0.0f - ch.mBodyNode->getPosition().y,
|
||||
0.0f, 2000.0f);
|
||||
if (h > 0.05 && h < 2.0f)
|
||||
gr.gvelocity.y += 0.1f * (h + 1.0f) *
|
||||
h * eng.delta;
|
||||
}
|
||||
});
|
||||
ecs.system<const EngineData, const CharacterBase, CharacterVelocity>(
|
||||
"HandleRootMotionVelocity")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<TerrainReady>()
|
||||
.with<WaterReady>()
|
||||
.each([this](flecs::entity e, const EngineData &eng,
|
||||
const CharacterBase &ch, CharacterVelocity &v) {
|
||||
if (eng.delta < 0.0000001f)
|
||||
return;
|
||||
if (!ch.mBodyNode)
|
||||
return;
|
||||
Ogre::Quaternion rot = ch.mBodyNode->getOrientation();
|
||||
Ogre::Vector3 pos = ch.mBodyNode->getPosition();
|
||||
Ogre::Vector3 boneMotion = ch.mBoneMotion;
|
||||
v.velocity = rot * boneMotion / eng.delta;
|
||||
if (eng.startupDelay <= 0.0f)
|
||||
v.velocity += v.gvelocity;
|
||||
v.velocity.y = Ogre::Math::Clamp(v.velocity.y, -10.5f,
|
||||
1000000.0f);
|
||||
});
|
||||
ecs.system<const EngineData, CharacterBase, CharacterBody,
|
||||
AnimationControl, CharacterVelocity>("HandleRootMotion")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([this](flecs::entity e, const EngineData &eng,
|
||||
CharacterBase &ch, CharacterBody &body,
|
||||
AnimationControl &anim, CharacterVelocity &v) {
|
||||
if (!ch.mBodyNode)
|
||||
return;
|
||||
if (eng.delta < 0.0000001f)
|
||||
return;
|
||||
OgreAssert(eng.delta > 0.0f, "Zero delta");
|
||||
int maxPen = 0;
|
||||
Ogre::Vector3 colNormal;
|
||||
bool is_on_floor = false;
|
||||
bool penetration = false;
|
||||
if (eng.startupDelay < 0.0f) {
|
||||
if (body.mController) {
|
||||
Ogre::Vector3 rotMotion =
|
||||
v.velocity * eng.delta;
|
||||
btVector3 currentPosition =
|
||||
body.mGhostObject
|
||||
->getWorldTransform()
|
||||
.getOrigin();
|
||||
is_on_floor =
|
||||
body.mController->isOnFloor();
|
||||
penetration = body.mController
|
||||
->isPenetrating();
|
||||
if (is_on_floor)
|
||||
v.gvelocity = Ogre::Vector3(
|
||||
0.0f, 0.0f, 0.0f);
|
||||
|
||||
btTransform from(
|
||||
Ogre::Bullet::convert(
|
||||
ch.mBodyNode
|
||||
->getOrientation()),
|
||||
Ogre::Bullet::convert(
|
||||
ch.mBodyNode
|
||||
->getPosition()));
|
||||
ch.mBodyNode->setPosition(
|
||||
ch.mBodyNode->getPosition() +
|
||||
rotMotion);
|
||||
ch.mBoneMotion = Ogre::Vector3(0, 0, 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
ecs.system<const Input, const CharacterBase, AnimationControl>(
|
||||
"HandlePlayerAnimations")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<Character>()
|
||||
.with<Player>()
|
||||
.each([](flecs::entity e, const Input &input,
|
||||
const CharacterBase &ch, AnimationControl &anim) {
|
||||
if (!anim.configured)
|
||||
return;
|
||||
bool controls_idle = input.motion.zeroLength();
|
||||
bool anim_is_idle =
|
||||
anim.currentAnim ==
|
||||
AnimationControl::ANIM_IDLE ||
|
||||
anim.currentAnim ==
|
||||
AnimationControl::ANIM_TREADING_WATER;
|
||||
bool anim_is_walking = anim.currentAnim ==
|
||||
AnimationControl::ANIM_WALK;
|
||||
bool anim_is_running = anim.currentAnim ==
|
||||
AnimationControl::ANIM_RUN;
|
||||
bool anim_is_swimming = anim.currentAnim ==
|
||||
AnimationControl::ANIM_SWIMMING;
|
||||
bool anim_is_motion = anim_is_walking ||
|
||||
anim_is_running ||
|
||||
anim_is_swimming;
|
||||
if (controls_idle) {
|
||||
if (anim.currentAnim ==
|
||||
AnimationControl::ANIM_IDLE &&
|
||||
ch.is_submerged)
|
||||
anim.nextAnim = AnimationControl::
|
||||
ANIM_TREADING_WATER;
|
||||
else if (anim.currentAnim ==
|
||||
AnimationControl::
|
||||
ANIM_TREADING_WATER &&
|
||||
!ch.is_submerged)
|
||||
anim.nextAnim =
|
||||
AnimationControl::ANIM_IDLE;
|
||||
}
|
||||
if (!controls_idle && anim_is_idle) {
|
||||
anim.reset = true;
|
||||
if (ch.is_submerged) {
|
||||
if (input.fast)
|
||||
anim.nextAnim =
|
||||
AnimationControl::
|
||||
ANIM_SWIMMING;
|
||||
else
|
||||
anim.nextAnim =
|
||||
AnimationControl::
|
||||
ANIM_SWIMMING;
|
||||
} else {
|
||||
if (input.fast)
|
||||
anim.nextAnim =
|
||||
AnimationControl::ANIM_RUN;
|
||||
else
|
||||
anim.nextAnim =
|
||||
AnimationControl::
|
||||
ANIM_WALK;
|
||||
}
|
||||
} else
|
||||
anim.reset = false;
|
||||
if (controls_idle && anim_is_motion)
|
||||
if (ch.is_submerged)
|
||||
anim.nextAnim = AnimationControl::
|
||||
ANIM_TREADING_WATER;
|
||||
else
|
||||
anim.nextAnim =
|
||||
AnimationControl::ANIM_IDLE;
|
||||
else if (!controls_idle && anim_is_motion) {
|
||||
if (input.fast && anim_is_walking)
|
||||
anim.nextAnim =
|
||||
AnimationControl::ANIM_RUN;
|
||||
else if (!input.fast && anim_is_running)
|
||||
anim.nextAnim =
|
||||
AnimationControl::ANIM_WALK;
|
||||
if ((anim_is_walking || anim_is_running) &&
|
||||
ch.is_submerged) {
|
||||
if (input.fast)
|
||||
anim.nextAnim =
|
||||
AnimationControl::
|
||||
ANIM_SWIMMING;
|
||||
else
|
||||
anim.nextAnim =
|
||||
AnimationControl::
|
||||
ANIM_SWIMMING;
|
||||
} else if ((anim_is_swimming) &&
|
||||
!ch.is_submerged) {
|
||||
if (input.fast)
|
||||
anim.nextAnim =
|
||||
AnimationControl::ANIM_RUN;
|
||||
else
|
||||
anim.nextAnim =
|
||||
AnimationControl::
|
||||
ANIM_WALK;
|
||||
}
|
||||
}
|
||||
});
|
||||
#endif
|
||||
#define TURN_SPEED 500.0f // character turning in degrees per second
|
||||
ecs.system<const Input, const Camera, CharacterBase>("UpdateBody")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<Character>()
|
||||
.with<Player>()
|
||||
.without<CharacterInActuator>()
|
||||
.each([](flecs::entity e, const Input &input,
|
||||
const Camera &camera, CharacterBase &ch) {
|
||||
ch.mGoalDirection = Ogre::Vector3::ZERO;
|
||||
@@ -492,6 +243,7 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
ch.mBodyNode->yaw(Ogre::Degree(yawToGoal));
|
||||
}
|
||||
});
|
||||
#if 0
|
||||
ecs.system<const EngineData, CharacterLocation, CharacterBase,
|
||||
CharacterBody>("UpdateCharacterBase")
|
||||
.kind(flecs::OnUpdate)
|
||||
@@ -508,15 +260,49 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
ch.mBodyNode->_getDerivedPosition();
|
||||
}
|
||||
});
|
||||
#endif
|
||||
ecs.observer<const EngineData, const CharacterLocation,
|
||||
const CharacterConf>("SetupCharacterM")
|
||||
.event(flecs::OnSet)
|
||||
.with<Character>()
|
||||
.without<CharacterBase>()
|
||||
.each([](flecs::entity e, const EngineData &eng,
|
||||
const CharacterLocation &loc,
|
||||
const CharacterConf &conf) {
|
||||
CharacterBase &ch = e.ensure<CharacterBase>();
|
||||
AnimationControl &anim = e.ensure<AnimationControl>();
|
||||
ch.mBodyEnt = eng.mScnMgr->createEntity(conf.type);
|
||||
ch.mBodyNode = eng.mScnMgr->getRootSceneNode()
|
||||
->createChildSceneNode();
|
||||
ch.mBodyNode->setOrientation(loc.orientation);
|
||||
ch.mBodyNode->setPosition(loc.position);
|
||||
ch.mBodyNode->attachObject(ch.mBodyEnt);
|
||||
ch.mSkeleton = ch.mBodyEnt->getSkeleton();
|
||||
OgreAssert(ch.mBodyEnt->getSkeleton()->hasBone("Root"),
|
||||
"No root bone");
|
||||
OgreAssert(ch.mSkeleton->hasBone("Root"),
|
||||
"No root bone");
|
||||
ch.mRootBone = ch.mSkeleton->getBone("Root");
|
||||
OgreAssert(ch.mRootBone, "No root bone");
|
||||
// body.mController = nullptr;
|
||||
ch.mBoneMotion = Ogre::Vector3::ZERO;
|
||||
ch.mBonePrevMotion = Ogre::Vector3::ZERO;
|
||||
e.set<CharacterVelocity>(
|
||||
{ { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } });
|
||||
e.add<CharacterGravity>();
|
||||
e.add<CharacterBuoyancy>();
|
||||
anim.configured = false;
|
||||
});
|
||||
#if 0
|
||||
ecs.system<const EngineData, const CharacterLocation,
|
||||
const CharacterConf>("SetupCharacter")
|
||||
const CharacterConf, Body2Entity>("SetupCharacter")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<Character>()
|
||||
.without<CharacterBase>()
|
||||
.without<CharacterBody>()
|
||||
.each([](flecs::entity e, const EngineData &eng,
|
||||
const CharacterLocation &loc,
|
||||
const CharacterConf &conf) {
|
||||
const CharacterConf &conf, Body2Entity &b2e) {
|
||||
CharacterBase &ch = e.ensure<CharacterBase>();
|
||||
CharacterBody &body = e.ensure<CharacterBody>();
|
||||
AnimationControl &anim = e.ensure<AnimationControl>();
|
||||
@@ -527,19 +313,25 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
ch.mBodyNode->setPosition(loc.position);
|
||||
ch.mBodyNode->attachObject(ch.mBodyEnt);
|
||||
ch.mSkeleton = ch.mBodyEnt->getSkeleton();
|
||||
OgreAssert(ch.mBodyEnt->getSkeleton()->hasBone("Root"),
|
||||
"No root bone");
|
||||
OgreAssert(ch.mSkeleton->hasBone("Root"),
|
||||
"No root bone");
|
||||
ch.mRootBone = ch.mSkeleton->getBone("Root");
|
||||
OgreAssert(ch.mRootBone, "No root bone");
|
||||
body.mController = nullptr;
|
||||
// body.mController = nullptr;
|
||||
ch.mBoneMotion = Ogre::Vector3::ZERO;
|
||||
ch.mBonePrevMotion = Ogre::Vector3::ZERO;
|
||||
e.set<CharacterVelocity>(
|
||||
{ { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } });
|
||||
body.checkGround = false;
|
||||
body.checkGroundResult = false;
|
||||
#if 0
|
||||
body.mCollisionShape = nullptr;
|
||||
body.mGhostObject = nullptr;
|
||||
body.mController = nullptr;
|
||||
body.mGhostObject = new btPairCachingGhostObject();
|
||||
b2e.entities[body.mGhostObject] = e;
|
||||
body.mCollisionShape = new btCompoundShape(false);
|
||||
body.mGhostObject->setCollisionShape(
|
||||
body.mCollisionShape);
|
||||
@@ -563,8 +355,8 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
->calculatePrincipalAxisTransform(
|
||||
masses, principal, inertia);
|
||||
}
|
||||
body.mGhostObject->setCollisionFlags(
|
||||
btCollisionObject::CF_KINEMATIC_OBJECT /*|
|
||||
body.mGhostObject->setCollisionFlags(body.mGhostObject->getCollisionFlags() | btCollisionObject::CF_CHARACTER_OBJECT | btCollisionObject::CF_KINEMATIC_OBJECT
|
||||
/*btCollisionObject::CF_KINEMATIC_OBJECT |
|
||||
btCollisionObject::CF_NO_CONTACT_RESPONSE */);
|
||||
body.mGhostObject->setActivationState(
|
||||
DISABLE_DEACTIVATION);
|
||||
@@ -572,13 +364,15 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
body.mGhostObject, ch.mBodyEnt, 1, 0x7FFFFFFF);
|
||||
OgreAssert(body.mGhostObject, "Need GhostObject");
|
||||
OgreAssert(body.mCollisionShape, "No collision shape");
|
||||
#endif
|
||||
e.add<CharacterGravity>();
|
||||
e.add<CharacterBuoyancy>();
|
||||
anim.currentAnim = AnimationControl::ANIM_NONE;
|
||||
anim.nextAnim = AnimationControl::ANIM_NONE;
|
||||
anim.reset = false;
|
||||
anim.configured = false;
|
||||
// OgreAssert(body.mGhostObject->hasContactResponse(),
|
||||
// "need contact response");
|
||||
});
|
||||
#endif
|
||||
#if 0
|
||||
ecs.system<const EngineData, CharacterBase, CharacterBody>(
|
||||
"UpdateCharacterPhysics")
|
||||
.kind(flecs::OnUpdate)
|
||||
@@ -587,17 +381,21 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
.with<WaterReady>()
|
||||
.each([](const EngineData &eng, CharacterBase &ch,
|
||||
CharacterBody &body) {
|
||||
#if 0
|
||||
if (ch.mBodyNode && !body.mController &&
|
||||
eng.startupDelay < 0.0f) {
|
||||
body.mController =
|
||||
new Ogre::Bullet::KinematicMotionSimple(
|
||||
body.mGhostObject,
|
||||
ch.mBodyNode);
|
||||
body.mController->enableManualNarrowPhase(true);
|
||||
eng.mWorld->getBtWorld()->addAction(
|
||||
body.mController);
|
||||
OgreAssert(body.mController, "Need controller");
|
||||
}
|
||||
#endif
|
||||
});
|
||||
#endif
|
||||
#define CAM_HEIGHT 1.6f // height of camera above character's center of mass
|
||||
ecs.system<const EngineData, Camera, const CharacterBase>(
|
||||
"UpdateCamera")
|
||||
@@ -647,6 +445,7 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
Ogre::Node::TS_PARENT);
|
||||
}
|
||||
});
|
||||
#if 0
|
||||
class ClosestNotMeRayResultCallback
|
||||
: public btCollisionWorld::ClosestRayResultCallback {
|
||||
btCollisionObject *mMe;
|
||||
@@ -669,12 +468,14 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
rayResult, normalInWorldSpace);
|
||||
}
|
||||
};
|
||||
ecs.system<const EngineData, CharacterBody>("CheckGround")
|
||||
#endif
|
||||
ecs.system<const EngineData, CharacterBase>("CheckGround")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<Character>()
|
||||
.with<Player>()
|
||||
.without<GroundCheckReady>()
|
||||
.each([](const EngineData &eng, CharacterBody &body) {
|
||||
.each([](const EngineData &eng, CharacterBase &ch) {
|
||||
#if 0
|
||||
if (body.mGhostObject) {
|
||||
btVector3 from =
|
||||
body.mGhostObject->getWorldTransform()
|
||||
@@ -690,7 +491,10 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
if (resultCallback.hasHit())
|
||||
ECS::get().add<GroundCheckReady>();
|
||||
}
|
||||
#endif
|
||||
ECS::get().add<GroundCheckReady>();
|
||||
});
|
||||
#if 0
|
||||
ecs.system<const WaterBody, const CharacterBase, CharacterBody>(
|
||||
"CharacterWater1")
|
||||
.kind(flecs::OnUpdate)
|
||||
@@ -698,11 +502,13 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
.without<InWater>()
|
||||
.each([](flecs::entity e, const WaterBody &waterb,
|
||||
const CharacterBase &ch, CharacterBody &body) {
|
||||
#if 0
|
||||
if (waterb.isInWater(body.mGhostObject) &&
|
||||
ch.mBodyNode->_getDerivedPosition().y < -0.05f) {
|
||||
e.add<InWater>();
|
||||
std::cout << "Big Splash\n";
|
||||
}
|
||||
#endif
|
||||
#if 0
|
||||
if (waterb.mInWater.find(body.mGhostObject) ==
|
||||
waterb.mInWater.end())
|
||||
@@ -710,6 +516,8 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
std::cout << waterb.mInWater.size() << " InWater\n";
|
||||
#endif
|
||||
});
|
||||
#endif
|
||||
#if 0
|
||||
ecs.system<const WaterBody, const CharacterBase, CharacterBody>(
|
||||
"CharacterWater2")
|
||||
.kind(flecs::OnUpdate)
|
||||
@@ -717,10 +525,16 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
.with<InWater>()
|
||||
.each([](flecs::entity e, const WaterBody &waterb,
|
||||
const CharacterBase &ch, CharacterBody &body) {
|
||||
if (waterb.isInWater(body.mGhostObject) &&
|
||||
ch.mBodyNode->_getDerivedPosition().y > 0.05f)
|
||||
float h = ch.mBodyNode->_getDerivedPosition().y;
|
||||
#if 0
|
||||
if (waterb.isInWater(body.mGhostObject) && h > 0.05f)
|
||||
e.remove<InWater>();
|
||||
else if (!waterb.isInWater(body.mGhostObject) &&
|
||||
h > 0.05f)
|
||||
e.remove<InWater>();
|
||||
#endif
|
||||
});
|
||||
#endif
|
||||
#if 0
|
||||
ecs.system<const EngineData, CharacterBase, CharacterBody>(
|
||||
"DisplayPlayerPos")
|
||||
@@ -733,71 +547,10 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
<< "\n";
|
||||
});
|
||||
#endif
|
||||
ecs.system<const EngineData, const CharacterBody>("UpdatePhysics")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<CharacterUpdatePhysicsState>()
|
||||
.write<CharacterUpdatePhysicsState>()
|
||||
.each([](flecs::entity e, const EngineData &eng,
|
||||
const CharacterBody &body) {
|
||||
if (e.has<CharacterDisablePhysics>()) {
|
||||
eng.mWorld->getBtWorld()->removeAction(
|
||||
body.mController);
|
||||
} else {
|
||||
eng.mWorld->getBtWorld()->addAction(
|
||||
body.mController);
|
||||
}
|
||||
e.remove<CharacterUpdatePhysicsState>();
|
||||
});
|
||||
#if 0
|
||||
#endif
|
||||
}
|
||||
|
||||
void CharacterModule::setAnimation(AnimationControl &anim)
|
||||
{
|
||||
OgreAssert(anim.nextAnim >= 0 &&
|
||||
anim.nextAnim < AnimationControl::NUM_ANIMS,
|
||||
"Bad animation");
|
||||
if (anim.currentAnim != AnimationControl::ANIM_NONE) {
|
||||
anim.mFadingIn[anim.currentAnim] = false;
|
||||
anim.mFadingOut[anim.currentAnim] = true;
|
||||
}
|
||||
if (anim.nextAnim != AnimationControl::ANIM_NONE) {
|
||||
anim.mAnims[anim.nextAnim]->setEnabled(true);
|
||||
anim.mAnims[anim.nextAnim]->setWeight(0);
|
||||
anim.mFadingOut[anim.nextAnim] = false;
|
||||
anim.mFadingIn[anim.nextAnim] = true;
|
||||
if (anim.reset)
|
||||
anim.mAnims[anim.nextAnim]->setTimePosition(0);
|
||||
}
|
||||
anim.currentAnim = anim.nextAnim;
|
||||
anim.reset = false;
|
||||
}
|
||||
#define ANIM_FADE_SPEED \
|
||||
7.5f // animation crossfade speed in % of full weight per second
|
||||
|
||||
void CharacterModule::fadeAnimations(AnimationControl &anim, Ogre::Real delta)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < AnimationControl::NUM_ANIMS; i++) {
|
||||
if (anim.mFadingIn[i]) {
|
||||
// slowly fade this animation in until it has full weight
|
||||
Ogre::Real newWeight = anim.mAnims[i]->getWeight() +
|
||||
delta * ANIM_FADE_SPEED;
|
||||
anim.mAnims[i]->setWeight(
|
||||
Ogre::Math::Clamp<Ogre::Real>(newWeight, 0, 1));
|
||||
if (newWeight >= 1)
|
||||
anim.mFadingIn[i] = false;
|
||||
} else if (anim.mFadingOut[i]) {
|
||||
// slowly fade this animation out until it has no weight, and then disable it
|
||||
Ogre::Real newWeight = anim.mAnims[i]->getWeight() -
|
||||
delta * ANIM_FADE_SPEED;
|
||||
anim.mAnims[i]->setWeight(
|
||||
Ogre::Math::Clamp<Ogre::Real>(newWeight, 0, 1));
|
||||
if (newWeight <= 0) {
|
||||
anim.mAnims[i]->setEnabled(false);
|
||||
anim.mFadingOut[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void CharacterModule::updateCameraGoal(Camera &camera, Ogre::Real deltaYaw,
|
||||
Ogre::Real deltaPitch,
|
||||
Ogre::Real deltaZoom)
|
||||
@@ -819,4 +572,182 @@ void CharacterModule::updateCameraGoal(Camera &camera, Ogre::Real deltaYaw,
|
||||
camera.mCameraGoal->translate(0, 0, distChange,
|
||||
Ogre::Node::TS_LOCAL);
|
||||
}
|
||||
}
|
||||
CharacterAIModule::CharacterAIModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.module<CharacterAIModule>();
|
||||
ecs.system<Blackboard>("UpdateCharacters")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([&](flecs::entity e, Blackboard &bb) {
|
||||
bb.flags &=
|
||||
~(Blackboard::LOW_HEALTH |
|
||||
Blackboard::FULL_HEALTH |
|
||||
Blackboard::LOW_STAMINA |
|
||||
Blackboard::FULL_STAMINA |
|
||||
Blackboard::LOW_LUST | Blackboard::HIGH_LUST |
|
||||
Blackboard::FULL_LUST);
|
||||
if (bb.health < 5)
|
||||
bb.flags |= Blackboard::LOW_HEALTH;
|
||||
else if (bb.health >= 100)
|
||||
bb.flags |= Blackboard::FULL_HEALTH;
|
||||
if (bb.stamina < 5)
|
||||
bb.flags |= Blackboard::LOW_STAMINA;
|
||||
if (bb.stamina >= 100)
|
||||
bb.flags |= Blackboard::FULL_STAMINA;
|
||||
if (bb.lust >= 100)
|
||||
bb.flags |= Blackboard::FULL_LUST;
|
||||
if (bb.lust > 10)
|
||||
bb.flags |= Blackboard::HIGH_LUST;
|
||||
if (bb.lust < 5)
|
||||
bb.flags |= Blackboard::LOW_LUST;
|
||||
if (bb.stamina < 0)
|
||||
bb.stamina = 0;
|
||||
else if (bb.stamina > 100)
|
||||
bb.stamina = 100;
|
||||
if (bb.lust < 0)
|
||||
bb.lust = 0;
|
||||
else if (bb.lust > 100)
|
||||
bb.lust = 100;
|
||||
});
|
||||
ecs.system<Blackboard, Plan>("UpdateCharactersPlan2")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([&](flecs::entity e, Blackboard &bb, Plan &p) {
|
||||
int i;
|
||||
bool ok_plan = true;
|
||||
|
||||
for (i = p.position; i < p.length; i++) {
|
||||
if (!p.actions[i]->can_run(bb, true)) {
|
||||
ok_plan = false;
|
||||
break;
|
||||
}
|
||||
int ret = p.actions[i]->execute(bb);
|
||||
p.position = i;
|
||||
if (ret == BaseAction::BUSY)
|
||||
break;
|
||||
else if (ret == BaseAction::ABORT) {
|
||||
ok_plan = false;
|
||||
break;
|
||||
}
|
||||
if (ret == BaseAction::OK && i == p.length - 1)
|
||||
ok_plan = false;
|
||||
// stop_this = true;
|
||||
}
|
||||
if (!ok_plan) {
|
||||
std::cout << e.name() << ": invalidated plan"
|
||||
<< " step: " << i << std::endl;
|
||||
for (i = 0; i < p.length; i++)
|
||||
p.actions[i]->stop(bb);
|
||||
e.remove<Plan>();
|
||||
}
|
||||
});
|
||||
ecs.system<Blackboard, Planner>("UpdateCharacters2")
|
||||
.kind(flecs::OnUpdate)
|
||||
.without<Plan>()
|
||||
.each([&](flecs::entity e, Blackboard &bb, Planner &planner) {
|
||||
int i;
|
||||
std::vector<BaseAction *> actions;
|
||||
actions.insert(actions.end(), planner.actions.begin(),
|
||||
planner.actions.end());
|
||||
e.world()
|
||||
.query_builder<const ActionTarget>()
|
||||
.build()
|
||||
.each([&](flecs::entity me,
|
||||
const ActionTarget &t) {
|
||||
actions.insert(actions.end(),
|
||||
t.actions.begin(),
|
||||
t.actions.end());
|
||||
#if 0
|
||||
auto it = t.actions.begin();
|
||||
while (it != t.actions.end()) {
|
||||
if (me != bb.me)
|
||||
actions.push_back(*it);
|
||||
// std::cout << (*it)->get_name()
|
||||
// << std::endl;
|
||||
it++;
|
||||
}
|
||||
#endif
|
||||
});
|
||||
#if 0
|
||||
for (i = 0; i < actions.size(); i++)
|
||||
std::cout << "action: " << i << " "
|
||||
<< actions[i]->get_name()
|
||||
<< std::endl;
|
||||
#endif
|
||||
if (actions.size() == 0)
|
||||
return;
|
||||
int len = -1;
|
||||
OgreAssert(
|
||||
bb.stamina < 100 ||
|
||||
bb.stamina >= 100 &&
|
||||
!bb.get_flag(
|
||||
Blackboard::LOW_STAMINA),
|
||||
"bad thing");
|
||||
for (i = 0; i < planner.goals.size(); i++) {
|
||||
if (planner.goals[i]->distance_to(bb) > 1000000)
|
||||
continue;
|
||||
len = planner.planner.plan(
|
||||
bb, *planner.goals[i], actions.data(),
|
||||
actions.size(), planner.path.data(),
|
||||
100);
|
||||
std::cout << bb.me.name() << ": goal: " << len
|
||||
<< " " << planner.goals[i]->get_name()
|
||||
<< std::endl;
|
||||
if (len > 0)
|
||||
break;
|
||||
}
|
||||
// std::cout << "plan length: " << len << std::endl;
|
||||
#if 0
|
||||
if (len > 0)
|
||||
stop_this = true;
|
||||
if (len < 0)
|
||||
stop_this = true;
|
||||
#endif
|
||||
if (len > 0) {
|
||||
Plan &p = e.ensure<Plan>();
|
||||
p.actions = planner.path;
|
||||
p.position = 0;
|
||||
p.length = len;
|
||||
for (i = 0; i < len; i++)
|
||||
std::cout
|
||||
<< i << " "
|
||||
<< planner.path[i]->get_name()
|
||||
<< " "
|
||||
<< planner.path[i]->get_cost(bb)
|
||||
<< std::endl;
|
||||
bool ok_plan = true;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!planner.path[i]->can_run(bb,
|
||||
true)) {
|
||||
ok_plan = false;
|
||||
break;
|
||||
}
|
||||
int ret = planner.path[i]->execute(bb);
|
||||
p.position = i;
|
||||
std::cout << "exec: " << i << " "
|
||||
<< planner.path[i]->get_name()
|
||||
<< std::endl;
|
||||
if (ret == BaseAction::BUSY)
|
||||
break;
|
||||
else if (ret == BaseAction::ABORT) {
|
||||
ok_plan = false;
|
||||
} else if (ret == BaseAction::OK)
|
||||
std::cout
|
||||
<< "exec: complete "
|
||||
<< i << " "
|
||||
<< planner.path[i]
|
||||
->get_name()
|
||||
<< std::endl;
|
||||
}
|
||||
e.modified<Plan>();
|
||||
if (!ok_plan) {
|
||||
std::cout << e.name()
|
||||
<< ": invalidate plan"
|
||||
<< " step: " << i
|
||||
<< std::endl;
|
||||
for (i = 0; i < len; i++)
|
||||
planner.path[i]->stop(bb);
|
||||
e.remove<Plan>();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef CHARACTER_MODULE_H_
|
||||
#define CHARACTER_MODULE_H_
|
||||
#include <flecs.h>
|
||||
#include <Ogre.h>
|
||||
#include "goap.h"
|
||||
namespace ECS
|
||||
{
|
||||
struct Camera;
|
||||
@@ -11,6 +13,9 @@ struct CharacterGravity {};
|
||||
struct CharacterBuoyancy {};
|
||||
struct CharacterDisablePhysics {};
|
||||
struct CharacterUpdatePhysicsState {};
|
||||
struct Male {};
|
||||
struct Female {};
|
||||
|
||||
struct CharacterBase {
|
||||
Ogre::String type;
|
||||
float mTimer;
|
||||
@@ -19,6 +24,7 @@ struct CharacterBase {
|
||||
Ogre::Skeleton *mSkeleton;
|
||||
Ogre::Node *mRootBone;
|
||||
Ogre::Vector3 mBoneMotion;
|
||||
Ogre::Vector3 mBonePrevMotion;
|
||||
Ogre::Vector3 mGoalDirection; // actual intended direction in world-space
|
||||
bool is_submerged;
|
||||
};
|
||||
@@ -29,44 +35,32 @@ struct CharacterLocation {
|
||||
struct CharacterConf {
|
||||
Ogre::String type;
|
||||
};
|
||||
struct CharacterBody {
|
||||
btPairCachingGhostObject *mGhostObject;
|
||||
btCompoundShape *mCollisionShape;
|
||||
Ogre::Bullet::KinematicMotionSimple *mController;
|
||||
bool checkGround;
|
||||
bool checkGroundResult;
|
||||
struct CharacterInActuator {
|
||||
Ogre::String animationState;
|
||||
Vector3 prevMotion;
|
||||
};
|
||||
struct CharacterVelocity {
|
||||
Ogre::Vector3 gvelocity;
|
||||
Ogre::Vector3 velocity;
|
||||
struct ActionTarget {
|
||||
std::vector<BaseAction *> actions;
|
||||
};
|
||||
struct AnimationControl {
|
||||
enum AnimID {
|
||||
ANIM_IDLE = 0,
|
||||
ANIM_WALK,
|
||||
ANIM_RUN,
|
||||
ANIM_TREADING_WATER,
|
||||
ANIM_SWIMMING,
|
||||
NUM_ANIMS,
|
||||
ANIM_NONE = NUM_ANIMS
|
||||
};
|
||||
AnimID currentAnim;
|
||||
AnimID nextAnim;
|
||||
bool reset;
|
||||
bool configured;
|
||||
Ogre::AnimationState *mAnims[NUM_ANIMS]; // master animation list
|
||||
Ogre::Animation *mSkelAnimations[NUM_ANIMS];
|
||||
bool mFadingIn[NUM_ANIMS]; // which animations are fading in
|
||||
bool mFadingOut[NUM_ANIMS]; // which animations are fading out
|
||||
Ogre::NodeAnimationTrack *mHipsTracks[NUM_ANIMS];
|
||||
Ogre::NodeAnimationTrack *mRootTracks[NUM_ANIMS];
|
||||
struct Plan {
|
||||
std::vector<BaseAction *> actions;
|
||||
int position;
|
||||
int length;
|
||||
};
|
||||
struct Planner {
|
||||
BasePlanner<ECS::Blackboard, BaseAction> planner;
|
||||
std::vector<BaseAction *> path;
|
||||
std::vector<BasePlanner<ECS::Blackboard, BaseAction>::BaseGoal *> goals;
|
||||
std::vector<BaseAction *> actions;
|
||||
};
|
||||
|
||||
struct CharacterModule {
|
||||
CharacterModule(flecs::world &ecs);
|
||||
void setAnimation(AnimationControl &anim);
|
||||
void fadeAnimations(AnimationControl &anim, Ogre::Real deltaTime);
|
||||
void updateCameraGoal(Camera &camera, Ogre::Real deltaYaw,
|
||||
Ogre::Real deltaPitch, Ogre::Real deltaZoom);
|
||||
};
|
||||
struct CharacterAIModule {
|
||||
CharacterAIModule(flecs::world &ecs);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef COMPONENTS_H_
|
||||
#define COMPONENTS_H_
|
||||
#include <flecs.h>
|
||||
#include <Ogre.h>
|
||||
#include <OgreBullet.h>
|
||||
namespace Ogre
|
||||
{
|
||||
class ImGuiOverlay;
|
||||
@@ -19,7 +19,6 @@ struct GameData {
|
||||
};
|
||||
struct EngineData {
|
||||
Ogre::SceneManager *mScnMgr;
|
||||
Ogre::Bullet::DynamicsWorld *mWorld;
|
||||
float delta;
|
||||
float startupDelay;
|
||||
int width;
|
||||
@@ -44,6 +43,10 @@ struct Input {
|
||||
bool mouse_moved;
|
||||
bool wheel_moved;
|
||||
bool fast;
|
||||
bool act;
|
||||
bool act_pressed;
|
||||
bool act2;
|
||||
bool act2_pressed;
|
||||
Input()
|
||||
: control(0)
|
||||
, control_prev(0)
|
||||
@@ -64,17 +67,12 @@ struct RenderWindow {
|
||||
Ogre::RenderWindow *window;
|
||||
float dpi;
|
||||
};
|
||||
struct App {
|
||||
Ogre::ImGuiOverlay *mGuiOverlay;
|
||||
OgreBites::InputListenerChain *mInput;
|
||||
std::vector<OgreBites::InputListener *> listeners;
|
||||
};
|
||||
struct CollisionShape {
|
||||
btCollisionShape *shape;
|
||||
void *shape;
|
||||
};
|
||||
struct InWater {};
|
||||
struct TerrainReady {};
|
||||
struct WaterReady {};
|
||||
struct GroundCheckReady {};
|
||||
struct Body2Entity {
|
||||
/* std::unordered_map<btCollisionObject *, flecs::entity> entities; */
|
||||
};
|
||||
}
|
||||
#endif
|
||||
13
src/gamedata/EventModule.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#include "EventModule.h"
|
||||
namespace ECS
|
||||
{
|
||||
EventModule::EventModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.module<EventModule>();
|
||||
ecs.component<EventData>();
|
||||
}
|
||||
void EventModule::send_event(flecs::entity from, const Ogre::String &event,
|
||||
flecs::entity to)
|
||||
{
|
||||
}
|
||||
}
|
||||
29
src/gamedata/EventModule.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef _EVENT_MODULE_H_
|
||||
#define _EVENT_MODULE_H_
|
||||
#include <flecs.h>
|
||||
#include <Ogre.h>
|
||||
namespace ECS
|
||||
{
|
||||
struct EventData {
|
||||
struct EventArgs {
|
||||
int type;
|
||||
};
|
||||
struct Event {
|
||||
flecs::entity sender;
|
||||
Ogre::String event;
|
||||
flecs::entity e1, e2;
|
||||
};
|
||||
std::list<Event> events;
|
||||
void add(flecs::entity sender, const Ogre::String &event,
|
||||
flecs::entity e1, flecs::entity e2)
|
||||
{
|
||||
events.push_back({ sender, event, e1, e2 });
|
||||
}
|
||||
};
|
||||
struct EventModule {
|
||||
EventModule(flecs::world &ecs);
|
||||
void send_event(flecs::entity from, const Ogre::String &event,
|
||||
flecs::entity to);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
@@ -1,11 +1,15 @@
|
||||
#include <iostream>
|
||||
#include <OgreBullet.h>
|
||||
#include <OgreMeshManager.h>
|
||||
#include "Components.h"
|
||||
#include "GameData.h"
|
||||
#include "LuaData.h"
|
||||
#include "EventModule.h"
|
||||
#include "EventTriggerModule.h"
|
||||
|
||||
struct TriggerBody {
|
||||
void *data;
|
||||
};
|
||||
#if 0
|
||||
struct TriggerBody {
|
||||
btPairCachingGhostObject *mBody;
|
||||
btCylinderShape *shape;
|
||||
@@ -58,11 +62,50 @@ struct DeepPenetrationContactResultCallback : public btManifoldResult {
|
||||
}
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
ECS::EventTriggerModule::EventTriggerModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.module<EventTriggerModule>();
|
||||
ecs.component<EventTriggerExit>();
|
||||
ecs.component<EventTrigger>();
|
||||
ecs.component<EventTriggerData>();
|
||||
ecs.component<InTrigger>();
|
||||
ecs.component<TriggeredBy>();
|
||||
ecs.import <EventModule>();
|
||||
ecs.import <LuaModule>();
|
||||
ecs.observer<const EngineData, const EventTrigger>("CreateTrigger")
|
||||
.event(flecs::OnSet)
|
||||
.each([](flecs::entity e, const EngineData &eng,
|
||||
const EventTrigger &trigger) {
|
||||
e.set<EventTriggerData>({});
|
||||
e.set<EventData>({});
|
||||
});
|
||||
ecs.observer<const EventTrigger, EventData>("CreateTriggerEvent")
|
||||
.event(flecs::OnSet)
|
||||
.each([](flecs::entity e, const EventTrigger &trigger,
|
||||
EventData &evt) {
|
||||
evt.add(e, "actuator_created", e, e);
|
||||
});
|
||||
ecs.system<const EventTrigger, EventData>("HandleEventSystem")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([](flecs::entity e, const EventTrigger &trigger,
|
||||
EventData &evt) {
|
||||
if (e.parent().is_valid() &&
|
||||
e.parent().has<EventData>()) {
|
||||
bool added = false;
|
||||
for (auto ev : evt.events) {
|
||||
e.parent().get_mut<EventData>().add(
|
||||
ev.sender, ev.event, ev.e1,
|
||||
ev.e2);
|
||||
added = true;
|
||||
}
|
||||
evt.events.clear();
|
||||
if (added)
|
||||
e.parent().modified<EventData>();
|
||||
}
|
||||
});
|
||||
#if 0
|
||||
ecs.component<EventTriggerData>();
|
||||
ecs.component<TriggerBody>().on_add([](flecs::entity e,
|
||||
TriggerBody &body) {
|
||||
bool kinematic = false;
|
||||
@@ -81,14 +124,6 @@ ECS::EventTriggerModule::EventTriggerModule(flecs::world &ecs)
|
||||
e.get<EventTrigger>().position,
|
||||
Ogre::Quaternion(0, 0, 0, 1));
|
||||
}
|
||||
/*
|
||||
Ogre::MeshPtr mesh =
|
||||
Ogre::MeshManager::getSingleton().createManual(
|
||||
"trigger", "General");
|
||||
Ogre::Entity *ent =
|
||||
ECS::get<EngineData>().mScnMgr->createEntity(mesh);
|
||||
body.mSceneNode->attachObject(ent);
|
||||
*/
|
||||
body.mBody = new btPairCachingGhostObject();
|
||||
body.mBody->getWorldTransform().setOrigin(Ogre::Bullet::convert(
|
||||
body.mSceneNode->_getDerivedPosition()));
|
||||
@@ -102,10 +137,6 @@ ECS::EventTriggerModule::EventTriggerModule(flecs::world &ecs)
|
||||
if (kinematic)
|
||||
flags |= btCollisionObject::CF_STATIC_OBJECT;
|
||||
body.mBody->setCollisionFlags(flags);
|
||||
/*
|
||||
ECS::get<EngineData>().mWorld->attachCollisionObject(
|
||||
body.mBody, ent, 16, 0x1);
|
||||
*/
|
||||
ECS::get<EngineData>().mWorld->getBtWorld()->addCollisionObject(
|
||||
body.mBody, 16, 0x1);
|
||||
struct EntityCollisionListener {
|
||||
@@ -139,15 +170,19 @@ ECS::EventTriggerModule::EventTriggerModule(flecs::world &ecs)
|
||||
body.mSceneNode->getUserObjectBindings().setUserAny(
|
||||
"BtCollisionObject", objWrapper);
|
||||
});
|
||||
ecs.component<EventTrigger>().on_set(
|
||||
[](flecs::entity e, EventTrigger &ev) {
|
||||
e.add<TriggerBody>();
|
||||
});
|
||||
ecs.system<const EngineData, const EventTrigger, TriggerBody>(
|
||||
"CheckCollisions")
|
||||
ecs.component<EventTrigger>().on_set([](flecs::entity e,
|
||||
EventTrigger &ev) {
|
||||
e.add<TriggerBody>();
|
||||
e.set<EventTriggerData>({});
|
||||
ECS::get<LuaBase>().mLua->call_handler("actuator_created", e,
|
||||
e);
|
||||
});
|
||||
ecs.system<const EngineData, const EventTrigger, TriggerBody,
|
||||
EventTriggerData>("CheckCollisions")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([](flecs::entity e, const EngineData &eng,
|
||||
const EventTrigger &evt, TriggerBody &body) {
|
||||
const EventTrigger &evt, TriggerBody &body,
|
||||
EventTriggerData &data) {
|
||||
btDispatcher *dispatch =
|
||||
eng.mWorld->getBtWorld()->getDispatcher();
|
||||
btHashedOverlappingPairCache *cache =
|
||||
@@ -255,11 +290,40 @@ ECS::EventTriggerModule::EventTriggerModule(flecs::world &ecs)
|
||||
.end()) {
|
||||
body.contactBodies
|
||||
.insert(other);
|
||||
OgreAssert(
|
||||
ECS::get<
|
||||
Body2Entity>()
|
||||
.entities
|
||||
.find(const_cast<
|
||||
btCollisionObject
|
||||
*>(
|
||||
other)) !=
|
||||
ECS::get<
|
||||
Body2Entity>()
|
||||
.entities
|
||||
.end(),
|
||||
"No body to entity mapping");
|
||||
flecs::entity obj_e =
|
||||
ECS::get<
|
||||
Body2Entity>()
|
||||
.entities
|
||||
.at(const_cast<
|
||||
btCollisionObject
|
||||
*>(
|
||||
other));
|
||||
ECS::get<
|
||||
LuaBase>()
|
||||
.mLua
|
||||
->call_handler(
|
||||
evt.event);
|
||||
evt.event +
|
||||
"_enter",
|
||||
e,
|
||||
obj_e);
|
||||
obj_e.add<
|
||||
InTrigger>(
|
||||
e);
|
||||
e.add<TriggeredBy>(
|
||||
obj_e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -271,21 +335,24 @@ ECS::EventTriggerModule::EventTriggerModule(flecs::world &ecs)
|
||||
while (it != body.contactBodies.end()) {
|
||||
if (currentContactBodies.find(*it) ==
|
||||
currentContactBodies.end()) {
|
||||
if (e.has<EventTriggerExit>()) {
|
||||
const Ogre::String &exit_event =
|
||||
ECS::get<
|
||||
EventTriggerExit>()
|
||||
.event;
|
||||
ECS::get<LuaBase>()
|
||||
.mLua->call_handler(
|
||||
exit_event);
|
||||
} else {
|
||||
std::cout << "body exited"
|
||||
<< std::endl;
|
||||
}
|
||||
body.contactBodies.erase(*it);
|
||||
flecs::entity obj_e =
|
||||
ECS::get<Body2Entity>()
|
||||
.entities
|
||||
.at(const_cast<
|
||||
btCollisionObject
|
||||
*>(
|
||||
*it));
|
||||
ECS::get<LuaBase>().mLua->call_handler(
|
||||
evt.event + "_exit", e, obj_e);
|
||||
obj_e.remove<InTrigger>(e);
|
||||
e.remove<TriggeredBy>(obj_e);
|
||||
std::cout << "body exited" << std::endl;
|
||||
it = body.contactBodies.erase(it);
|
||||
if (it == body.contactBodies.end())
|
||||
break;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -10,10 +10,14 @@ struct EventTrigger {
|
||||
float halfheight;
|
||||
float radius;
|
||||
Ogre::String event;
|
||||
Ogre::SceneNode *node;
|
||||
bool once;
|
||||
};
|
||||
struct EventTriggerExit {
|
||||
Ogre::String event;
|
||||
struct EventTriggerData {
|
||||
std::set<flecs::entity_t> entities;
|
||||
};
|
||||
struct InTrigger {};
|
||||
struct TriggeredBy {};
|
||||
struct EventTriggerModule {
|
||||
EventTriggerModule(flecs::world &ecs);
|
||||
};
|
||||
|
||||
@@ -11,15 +11,22 @@
|
||||
#include "GameData.h"
|
||||
#include "Components.h"
|
||||
#include "LuaData.h"
|
||||
#include "AppModule.h"
|
||||
#include "GUIModule.h"
|
||||
namespace ECS
|
||||
{
|
||||
struct GUIListener;
|
||||
struct EditorGUIListener;
|
||||
struct GUIData {
|
||||
Ogre::ImGuiOverlay *mGuiOverlay;
|
||||
std::vector<Ogre::String> glb_names;
|
||||
GUIListener *mGUIListener;
|
||||
};
|
||||
struct EditorGUIData {
|
||||
Ogre::ImGuiOverlay *mGuiOverlay;
|
||||
std::vector<Ogre::String> glb_names;
|
||||
EditorGUIListener *mGUIListener;
|
||||
};
|
||||
struct GUIListener : public Ogre::RenderTargetListener {
|
||||
float panel_width;
|
||||
bool enableEditor;
|
||||
@@ -459,8 +466,537 @@ struct GUIListener : public Ogre::RenderTargetListener {
|
||||
}
|
||||
};
|
||||
|
||||
struct EditorGUIListener : public Ogre::RenderTargetListener {
|
||||
float panel_width;
|
||||
bool enableEditor;
|
||||
bool enableMapEditor;
|
||||
ImFont *smallFont, *midFont, *bigFont;
|
||||
Ogre::FontPtr _smallFont, _midFont, _bigFont;
|
||||
EditorGUIListener(Ogre::ImGuiOverlay *overlay)
|
||||
: Ogre::RenderTargetListener()
|
||||
{
|
||||
std::cout << "WAAAAAAA!!!!!" << std::endl;
|
||||
_midFont = createFont("midFont", "General",
|
||||
"Jupiteroid-Regular.ttf", 18.0f);
|
||||
_smallFont = createFont("smallFont", "General",
|
||||
"Jupiteroid-Regular.ttf", 13.0f);
|
||||
_bigFont = createFont("bigFont", "General", "Kenney Bold.ttf",
|
||||
32.0f);
|
||||
smallFont = overlay->addFont("smallFont", "General");
|
||||
OgreAssert(smallFont, "Could not load font");
|
||||
midFont = overlay->addFont("midFont", "General");
|
||||
OgreAssert(midFont, "Could not load font");
|
||||
bigFont = overlay->addFont("bigFont", "General");
|
||||
OgreAssert(bigFont, "Could not load font");
|
||||
#if 0
|
||||
Ogre::FontPtr _midFont = createFont("midFont", "General",
|
||||
"Kenney Bold.ttf", 28.0f);
|
||||
#endif
|
||||
#if 0
|
||||
ImGui::GetIO().Fonts->Build();
|
||||
#endif
|
||||
}
|
||||
Ogre::FontPtr createFont(const Ogre::String &name,
|
||||
const Ogre::String &group,
|
||||
const Ogre::String &ttfname, float fontSize)
|
||||
{
|
||||
Ogre::FontPtr ret =
|
||||
Ogre::FontManager::getSingleton().create(name, group);
|
||||
ret->setType(Ogre::FontType::FT_TRUETYPE);
|
||||
ret->setSource(ttfname);
|
||||
ret->setTrueTypeSize(fontSize);
|
||||
ret->setTrueTypeResolution(75);
|
||||
ret->addCodePointRange(Ogre::Font::CodePointRange(30, 128));
|
||||
ret->load();
|
||||
return ret;
|
||||
}
|
||||
void
|
||||
preViewportUpdate(const Ogre::RenderTargetViewportEvent &evt) override
|
||||
{
|
||||
preview(evt);
|
||||
}
|
||||
void buttons_panel()
|
||||
{
|
||||
ImVec2 size = ImGui::GetMainViewport()->Size;
|
||||
float window_width = size.x * 0.2f;
|
||||
if (window_width > panel_width)
|
||||
window_width = panel_width;
|
||||
float window_height = size.y * 0.5f - 20;
|
||||
ImGui::SetNextWindowPos(ImVec2(0, size.y * 0.5f + 20),
|
||||
ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImVec2(window_width, window_height),
|
||||
ImGuiCond_Always);
|
||||
ImGui::Begin("Control");
|
||||
// if (ECS::get().get<GUI>().enabled)
|
||||
// ECS::get().get<App>().app->setWindowGrab(true);
|
||||
if (ImGui::Button("Quit"))
|
||||
Ogre::Root::getSingleton().queueEndRendering();
|
||||
if (ImGui::Button("Return"))
|
||||
ECS::get().get<GUI>().finish();
|
||||
if (ImGui::Button("Enable/Disable item editor")) {
|
||||
enableEditor ^= true;
|
||||
if (enableEditor)
|
||||
enableMapEditor = false;
|
||||
}
|
||||
if (ImGui::Button("Enable/Disable map editor")) {
|
||||
enableMapEditor ^= true;
|
||||
if (enableMapEditor)
|
||||
enableEditor = false;
|
||||
}
|
||||
ImGui::Text("Text message...");
|
||||
ImGui::End();
|
||||
}
|
||||
void create_entity_node(const Ogre::String &name, int key)
|
||||
{
|
||||
Ogre::Entity *ent =
|
||||
ECS::get().get<EngineData>().mScnMgr->createEntity(
|
||||
name);
|
||||
Ogre::SceneNode *pnode =
|
||||
ECS::get()
|
||||
.get<EngineData>()
|
||||
.mScnMgr->getRootSceneNode()
|
||||
->createChildSceneNode(
|
||||
"ent:" + name +
|
||||
Ogre::StringConverter::toString(
|
||||
key),
|
||||
ECS::get()
|
||||
.get<Camera>()
|
||||
.mCameraPivot->getPosition(),
|
||||
ECS::get()
|
||||
.get<Camera>()
|
||||
.mCameraPivot->getOrientation());
|
||||
pnode->attachObject(ent);
|
||||
Ogre::Quaternion q = pnode->getOrientation();
|
||||
Ogre::Radian yaw = q.getYaw();
|
||||
Ogre::Quaternion nq(yaw, Ogre::Vector3(0, 1, 0));
|
||||
pnode->setOrientation(nq);
|
||||
|
||||
ECS::get().get<GUI>().finish();
|
||||
}
|
||||
void buildings_editor()
|
||||
{
|
||||
int i;
|
||||
ImVec2 size = ImGui::GetMainViewport()->Size;
|
||||
float window_width = size.x * 0.2f;
|
||||
if (window_width > panel_width)
|
||||
window_width = panel_width;
|
||||
float window_height = size.y * 0.5 - 20;
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImVec2(window_width, window_height),
|
||||
ImGuiCond_Always);
|
||||
ImGui::Begin("Droppings...");
|
||||
for (i = 0;
|
||||
i < ECS::get().get<EditorGUIData>().glb_names.size();
|
||||
i++) {
|
||||
Ogre::String id_button =
|
||||
"Create entity: " +
|
||||
ECS::get().get<EditorGUIData>().glb_names[i] +
|
||||
"##ent:" +
|
||||
ECS::get().get<EditorGUIData>().glb_names[i];
|
||||
if (ImGui::Button(id_button.c_str())) {
|
||||
create_entity_node(ECS::get()
|
||||
.get<EditorGUIData>()
|
||||
.glb_names[i],
|
||||
i);
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
void position_editor(Ogre::SceneNode *node)
|
||||
{
|
||||
Ogre::Vector3 position = node->getPosition();
|
||||
float v[3] = { position.x, position.y, position.z };
|
||||
ImGui::InputFloat3("position", v);
|
||||
position.x = v[0];
|
||||
position.y = v[1];
|
||||
position.z = v[2];
|
||||
node->setPosition(position);
|
||||
}
|
||||
void orientation_editor(Ogre::SceneNode *node)
|
||||
{
|
||||
Ogre::Quaternion q = node->getOrientation();
|
||||
float yaw = Ogre::Radian(q.getYaw()).valueDegrees();
|
||||
float pitch = Ogre::Radian(q.getPitch()).valueDegrees();
|
||||
float roll = Ogre::Radian(q.getRoll()).valueDegrees();
|
||||
bool m1 = ImGui::InputFloat("yaw", &yaw);
|
||||
bool m2 = ImGui::InputFloat("pitch", &pitch);
|
||||
bool m3 = ImGui::InputFloat("roll", &roll);
|
||||
if (m1 || m2 || m3) {
|
||||
Ogre::Quaternion q1(Ogre::Radian(Ogre::Degree(yaw)),
|
||||
Ogre::Vector3::UNIT_Y);
|
||||
Ogre::Quaternion q2(Ogre::Degree(pitch),
|
||||
Ogre::Vector3::UNIT_X);
|
||||
Ogre::Quaternion q3(Ogre::Degree(roll),
|
||||
Ogre::Vector3::UNIT_Z);
|
||||
node->setOrientation(q1 * q2 * q3);
|
||||
}
|
||||
}
|
||||
void attachments_editor(Ogre::SceneNode *node)
|
||||
{
|
||||
const Ogre::SceneNode::ObjectMap &pmap =
|
||||
node->getAttachedObjects();
|
||||
int i;
|
||||
for (i = 0; i < pmap.size(); i++) {
|
||||
const Ogre::MovableObject *mobj = pmap[i];
|
||||
const Ogre::String &pname = mobj->getName();
|
||||
ImGui::Text("Name: %s", pname.c_str());
|
||||
}
|
||||
}
|
||||
void map_editor()
|
||||
{
|
||||
}
|
||||
void preview(const Ogre::RenderTargetViewportEvent &evt)
|
||||
{
|
||||
int i;
|
||||
Ogre::ImGuiOverlay::NewFrame();
|
||||
std::cout << "GUI enabled: " << ECS::get().get<GUI>().enabled
|
||||
<< std::endl;
|
||||
if (ECS::get().get<GUI>().enabled) {
|
||||
buttons_panel();
|
||||
buildings_editor();
|
||||
map_editor();
|
||||
ImVec2 size = ImGui::GetMainViewport()->Size;
|
||||
float window_width = size.x * 0.2f;
|
||||
if (window_width > panel_width)
|
||||
window_width = panel_width;
|
||||
float window_height = size.y * 0.5f - 20;
|
||||
ImGui::SetNextWindowPos(ImVec2(size.x - window_width,
|
||||
size.y * 0.5f + 20),
|
||||
ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImVec2(window_width,
|
||||
window_height),
|
||||
ImGuiCond_Always);
|
||||
// ImGui::Begin("Dumb and Stupid", &mKbd.gui_active);
|
||||
ImGui::Begin("Panel...");
|
||||
std::deque<Ogre::SceneNode *> tree_input_queue,
|
||||
tree_output_queue;
|
||||
std::vector<Ogre::SceneNode *> tree_list;
|
||||
tree_input_queue.push_back(
|
||||
ECS::get()
|
||||
.get<EngineData>()
|
||||
.mScnMgr->getRootSceneNode());
|
||||
tree_input_queue.push_back(nullptr);
|
||||
std::set<Ogre::SceneNode *> visited;
|
||||
while (true) {
|
||||
int new_nodes_count = 0;
|
||||
while (!tree_input_queue.empty()) {
|
||||
int child;
|
||||
Ogre::SceneNode *item =
|
||||
tree_input_queue.front();
|
||||
tree_input_queue.pop_front();
|
||||
if (item &&
|
||||
visited.find(item) ==
|
||||
visited.end()) { // new node
|
||||
new_nodes_count++;
|
||||
tree_output_queue.push_back(
|
||||
item);
|
||||
visited.insert(item);
|
||||
const Ogre::Node::ChildNodeMap
|
||||
&children =
|
||||
item->getChildren();
|
||||
for (child = 0;
|
||||
child < children.size();
|
||||
child++) {
|
||||
tree_output_queue.push_back(
|
||||
static_cast<
|
||||
Ogre::SceneNode
|
||||
*>(
|
||||
children[child]));
|
||||
tree_output_queue
|
||||
.push_back(
|
||||
nullptr);
|
||||
}
|
||||
} else
|
||||
tree_output_queue.push_back(
|
||||
item);
|
||||
}
|
||||
if (new_nodes_count == 0)
|
||||
break;
|
||||
tree_input_queue = tree_output_queue;
|
||||
tree_output_queue.clear();
|
||||
}
|
||||
tree_list.insert(tree_list.begin(),
|
||||
tree_output_queue.begin(),
|
||||
tree_output_queue.end());
|
||||
int count = 0;
|
||||
int depth = 0;
|
||||
std::vector<int> check_depth;
|
||||
int max_depth = 0;
|
||||
check_depth.push_back(0);
|
||||
for (count = 0; count < tree_list.size(); count++) {
|
||||
int t;
|
||||
|
||||
Ogre::SceneNode *node = tree_list[count];
|
||||
if (node && max_depth >= depth) {
|
||||
Ogre::String name = node->getName();
|
||||
if (name.length() == 0) {
|
||||
name = "Node #" +
|
||||
Ogre::StringConverter::
|
||||
toString(count);
|
||||
}
|
||||
if (ImGui::TreeNode(name.c_str())) {
|
||||
check_depth.push_back(
|
||||
max_depth);
|
||||
max_depth++;
|
||||
ImGui::Text("%s",
|
||||
(name + "##caption")
|
||||
.c_str());
|
||||
position_editor(node);
|
||||
ImGui::Separator();
|
||||
orientation_editor(node);
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Attachments");
|
||||
attachments_editor(node);
|
||||
}
|
||||
} else if (!node && max_depth >= depth) {
|
||||
max_depth = check_depth.back();
|
||||
check_depth.pop_back();
|
||||
ImGui::TreePop();
|
||||
}
|
||||
if (tree_list[count])
|
||||
depth++;
|
||||
else
|
||||
depth--;
|
||||
}
|
||||
ImGui::Spacing();
|
||||
ImGui::End();
|
||||
#if 0
|
||||
if (ECS::get().get<GUI>().narrationBox) {
|
||||
ImVec2 size = ImGui::GetMainViewport()->Size;
|
||||
ImGui::SetNextWindowPos(ImVec2(0,
|
||||
size.y * 0.75f),
|
||||
ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImVec2(size.x,
|
||||
size.y * 0.25f),
|
||||
ImGuiCond_Always);
|
||||
ImGui::Begin("Narration...", NULL,
|
||||
ImGuiWindowFlags_NoTitleBar);
|
||||
ImGui::PushFont(midFont);
|
||||
ImVec2 p = ImGui::GetCursorScreenPos();
|
||||
ImGui::TextWrapped(
|
||||
"%s", ECS::get()
|
||||
.get<GUI>()
|
||||
.narrationText.c_str());
|
||||
if (ECS::get().get<GUI>().choices.size() == 0) {
|
||||
ImGui::SetCursorScreenPos(p);
|
||||
if (ImGui::InvisibleButton(
|
||||
"Background",
|
||||
ImGui::GetWindowSize()))
|
||||
ECS::get<LuaBase>().mLua->call_handler(
|
||||
"narration_progress");
|
||||
} else {
|
||||
int i;
|
||||
for (i = 0; i < ECS::get()
|
||||
.get<GUI>()
|
||||
.choices.size();
|
||||
i++) {
|
||||
if (ImGui::Button(
|
||||
ECS::get()
|
||||
.get<GUI>()
|
||||
.choices[i]
|
||||
.c_str())) {
|
||||
ECS::get()
|
||||
.get_mut<GUI>()
|
||||
.narration_answer =
|
||||
i + 1;
|
||||
std::cout << "answer: "
|
||||
<< i + 1
|
||||
<< std::endl;
|
||||
ECS::modified<GUI>();
|
||||
ECS::get<LuaBase>()
|
||||
.mLua
|
||||
->call_handler(
|
||||
"narration_answered");
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::Spacing();
|
||||
ImGui::PopFont();
|
||||
ImGui::End();
|
||||
} else if (ECS::get().get<GUI>().mainMenu) {
|
||||
ImVec2 size = ImGui::GetMainViewport()->Size;
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0),
|
||||
ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImVec2(size.x, size.y),
|
||||
ImGuiCond_Always);
|
||||
ImGui::Begin(
|
||||
"MainMenu", nullptr,
|
||||
ImGuiWindowFlags_NoTitleBar |
|
||||
ImGuiWindowFlags_NoDecoration |
|
||||
|
||||
ImGuiWindowFlags_NoResize |
|
||||
ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoFocusOnAppearing |
|
||||
0);
|
||||
// if (ECS::get().get<GUI>().enabled)
|
||||
// ECS::get().get<App>().app->setWindowGrab(true);
|
||||
ImGui::PushFont(bigFont);
|
||||
ImGui::TextWrapped("%s", "Booo!!!!");
|
||||
bool pressed = false;
|
||||
bool new_game = false, cont = false,
|
||||
load_game = false, opts = false,
|
||||
quit = false;
|
||||
ImGui::SetCursorPosY(size.y / 2.0f - 300.0f);
|
||||
ImGui::SetCursorPosX(size.x / 2.0f - 300.0f);
|
||||
new_game = ImGui::Button("New Game");
|
||||
ImGui::SetCursorPosX(size.x / 2.0f - 300.0f);
|
||||
cont = ImGui::Button("Continue");
|
||||
ImGui::SetCursorPosX(size.x / 2.0f - 300.0f);
|
||||
load_game = ImGui::Button("Load Game");
|
||||
ImGui::SetCursorPosX(size.x / 2.0f - 300.0f);
|
||||
opts = ImGui::Button("Options");
|
||||
ImGui::SetCursorPosX(size.x / 2.0f - 300.0f);
|
||||
quit = ImGui::Button("Quit###quit");
|
||||
pressed = new_game || cont || load_game ||
|
||||
opts || quit;
|
||||
ImGui::PopFont();
|
||||
ImGui::Spacing();
|
||||
ImGui::End();
|
||||
if (quit)
|
||||
Ogre::Root::getSingleton()
|
||||
.queueEndRendering();
|
||||
if (pressed)
|
||||
ECS::get().get<GUI>().finish();
|
||||
if (new_game) {
|
||||
ECS::get<LuaBase>().mLua->call_handler(
|
||||
"new_game");
|
||||
}
|
||||
} else {
|
||||
buttons_panel();
|
||||
if (enableEditor)
|
||||
buildings_editor();
|
||||
if (enableMapEditor)
|
||||
map_editor();
|
||||
ImVec2 size = ImGui::GetMainViewport()->Size;
|
||||
float window_width = size.x * 0.2f;
|
||||
if (window_width > panel_width)
|
||||
window_width = panel_width;
|
||||
float window_height = size.y * 0.5f - 20;
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(size.x - window_width,
|
||||
size.y * 0.5f + 20),
|
||||
ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImVec2(window_width,
|
||||
window_height),
|
||||
ImGuiCond_Always);
|
||||
// ImGui::Begin("Dumb and Stupid", &mKbd.gui_active);
|
||||
ImGui::Begin("Panel...");
|
||||
std::deque<Ogre::SceneNode *> tree_input_queue,
|
||||
tree_output_queue;
|
||||
std::vector<Ogre::SceneNode *> tree_list;
|
||||
tree_input_queue.push_back(
|
||||
ECS::get()
|
||||
.get<EngineData>()
|
||||
.mScnMgr->getRootSceneNode());
|
||||
tree_input_queue.push_back(nullptr);
|
||||
std::set<Ogre::SceneNode *> visited;
|
||||
while (true) {
|
||||
int new_nodes_count = 0;
|
||||
while (!tree_input_queue.empty()) {
|
||||
int child;
|
||||
Ogre::SceneNode *item =
|
||||
tree_input_queue.front();
|
||||
tree_input_queue.pop_front();
|
||||
if (item &&
|
||||
visited.find(item) ==
|
||||
visited.end()) { // new node
|
||||
new_nodes_count++;
|
||||
tree_output_queue
|
||||
.push_back(
|
||||
item);
|
||||
visited.insert(item);
|
||||
const Ogre::Node::ChildNodeMap
|
||||
&children =
|
||||
item->getChildren();
|
||||
for (child = 0;
|
||||
child <
|
||||
children.size();
|
||||
child++) {
|
||||
tree_output_queue
|
||||
.push_back(static_cast<
|
||||
Ogre::SceneNode
|
||||
*>(
|
||||
children[child]));
|
||||
tree_output_queue
|
||||
.push_back(
|
||||
nullptr);
|
||||
}
|
||||
} else
|
||||
tree_output_queue
|
||||
.push_back(
|
||||
item);
|
||||
}
|
||||
if (new_nodes_count == 0)
|
||||
break;
|
||||
tree_input_queue = tree_output_queue;
|
||||
tree_output_queue.clear();
|
||||
}
|
||||
tree_list.insert(tree_list.begin(),
|
||||
tree_output_queue.begin(),
|
||||
tree_output_queue.end());
|
||||
int count = 0;
|
||||
int depth = 0;
|
||||
std::vector<int> check_depth;
|
||||
int max_depth = 0;
|
||||
check_depth.push_back(0);
|
||||
for (count = 0; count < tree_list.size();
|
||||
count++) {
|
||||
int t;
|
||||
|
||||
Ogre::SceneNode *node =
|
||||
tree_list[count];
|
||||
if (node && max_depth >= depth) {
|
||||
Ogre::String name =
|
||||
node->getName();
|
||||
if (name.length() == 0) {
|
||||
name = "Node #" +
|
||||
Ogre::StringConverter::
|
||||
toString(
|
||||
count);
|
||||
}
|
||||
if (ImGui::TreeNode(
|
||||
name.c_str())) {
|
||||
check_depth.push_back(
|
||||
max_depth);
|
||||
max_depth++;
|
||||
ImGui::Text(
|
||||
"%s",
|
||||
(name +
|
||||
"##caption")
|
||||
.c_str());
|
||||
position_editor(node);
|
||||
ImGui::Separator();
|
||||
orientation_editor(
|
||||
node);
|
||||
ImGui::Separator();
|
||||
ImGui::Text(
|
||||
"Attachments");
|
||||
attachments_editor(
|
||||
node);
|
||||
}
|
||||
} else if (!node &&
|
||||
max_depth >= depth) {
|
||||
max_depth = check_depth.back();
|
||||
check_depth.pop_back();
|
||||
ImGui::TreePop();
|
||||
}
|
||||
if (tree_list[count])
|
||||
depth++;
|
||||
else
|
||||
depth--;
|
||||
}
|
||||
ImGui::Spacing();
|
||||
ImGui::End();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
};
|
||||
GUIModule::GUIModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.module<GUIModule>();
|
||||
ecs.import <AppModule>();
|
||||
ecs.component<GUI>()
|
||||
.on_add([](GUI &gui) {
|
||||
gui.enabled = false;
|
||||
@@ -522,4 +1058,46 @@ GUIModule::GUIModule(flecs::world &ecs)
|
||||
}
|
||||
});
|
||||
}
|
||||
EditorGUIModule::EditorGUIModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.module<EditorGUIModule>();
|
||||
ecs.import <AppModule>();
|
||||
ecs.component<GUI>()
|
||||
.on_add([](GUI &gui) {
|
||||
gui.enabled = true;
|
||||
gui.grab = false;
|
||||
gui.grabChanged = false;
|
||||
})
|
||||
.add(flecs::Singleton);
|
||||
ecs.component<EditorGUIData>()
|
||||
.on_add([](EditorGUIData &priv) {
|
||||
priv.glb_names.clear();
|
||||
priv.mGUIListener = nullptr;
|
||||
priv.mGuiOverlay = nullptr;
|
||||
})
|
||||
.add(flecs::Singleton);
|
||||
ecs.observer<const RenderWindow, const App, GUI>("SupportEditorGUI")
|
||||
.event(flecs::OnSet)
|
||||
.without<EditorGUIData>()
|
||||
.each([](const RenderWindow &window, const App &app, GUI &gui) {
|
||||
float vpScale = window.dpi / 96 *
|
||||
window.window->getWidth() / 1600.0f;
|
||||
Ogre::OverlayManager::getSingleton().setPixelRatio(
|
||||
vpScale);
|
||||
std::cout << "Editor GUI configure\n";
|
||||
OgreAssert(app.mGuiOverlay, "No ImGUI overlay");
|
||||
Ogre::ImGuiOverlay *guiOverlay = app.mGuiOverlay;
|
||||
EditorGUIListener *guiListener =
|
||||
new EditorGUIListener(guiOverlay);
|
||||
guiOverlay->setZOrder(300);
|
||||
guiOverlay->show();
|
||||
guiListener->panel_width = 300.0f;
|
||||
guiListener->enableEditor = false;
|
||||
window.window->addListener(guiListener);
|
||||
|
||||
ECS::get().set<EditorGUIData>(
|
||||
{ app.mGuiOverlay, {}, guiListener });
|
||||
std::cout << "Editor GUI configure finished\n";
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -17,11 +17,11 @@ struct GUI {
|
||||
int narration_answer;
|
||||
static void setWindowGrab(bool g = true)
|
||||
{
|
||||
ECS::GUI &gui = ECS::get().get_mut<ECS::GUI>();
|
||||
ECS::GUI &gui = ECS::get().get_mut<GUI>();
|
||||
if (gui.grab != g) {
|
||||
gui.grab = g;
|
||||
gui.grabChanged = true;
|
||||
ECS::get().modified<ECS::GUI>();
|
||||
ECS::get().modified<GUI>();
|
||||
}
|
||||
}
|
||||
static void finish()
|
||||
@@ -38,5 +38,8 @@ struct GUIModule {
|
||||
flecs::entity ui_wait;
|
||||
GUIModule(flecs::world &ecs);
|
||||
};
|
||||
struct EditorGUIModule {
|
||||
EditorGUIModule(flecs::world &ecs);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
@@ -11,42 +11,50 @@
|
||||
#include "WorldMapModule.h"
|
||||
#include "BoatModule.h"
|
||||
#include "EventTriggerModule.h"
|
||||
#include "CharacterAnimationModule.h"
|
||||
#include "PhysicsModule.h"
|
||||
#include "EventModule.h"
|
||||
#include "CharacterManagerModule.h"
|
||||
#include "VehicleManagerModule.h"
|
||||
#include "AppModule.h"
|
||||
#include "world-build.h"
|
||||
|
||||
namespace ECS
|
||||
{
|
||||
static flecs::world ecs;
|
||||
flecs::entity player;
|
||||
void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world,
|
||||
Ogre::SceneNode *cameraNode, Ogre::Camera *camera,
|
||||
Ogre::RenderWindow *window)
|
||||
void setup_minimal()
|
||||
{
|
||||
std::cout << "Setup GameData\n";
|
||||
ecs.component<EngineData>().add(flecs::Singleton);
|
||||
ecs.component<GameData>().add(flecs::Singleton);
|
||||
ecs.component<Input>().add(flecs::Singleton);
|
||||
ecs.component<Camera>().add(flecs::Singleton);
|
||||
ecs.component<InWater>();
|
||||
ecs.component<WaterReady>().add(flecs::Singleton);
|
||||
ecs.component<GroundCheckReady>().add(flecs::Singleton);
|
||||
ecs.component<App>()
|
||||
.on_add([](App &app) {
|
||||
app.mInput = nullptr;
|
||||
app.mGuiOverlay = nullptr;
|
||||
app.listeners.clear();
|
||||
})
|
||||
.add(flecs::Singleton);
|
||||
/* lots of things depend on it */
|
||||
ecs.component<TerrainReady>().add(flecs::Singleton);
|
||||
ecs.import <GameWorldModule>();
|
||||
ecs.import <EventModule>();
|
||||
ecs.import <CharacterManagerModule>();
|
||||
ecs.import <VehicleManagerModule>();
|
||||
ecs.import <WaterModule>();
|
||||
ecs.import <AppModule>();
|
||||
/* lots of things depend on it */
|
||||
ecs.component<Body2Entity>().add(flecs::Singleton);
|
||||
}
|
||||
void setup(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
|
||||
Ogre::Camera *camera, Ogre::RenderWindow *window)
|
||||
{
|
||||
std::cout << "Setup GameData\n";
|
||||
setup_minimal();
|
||||
ecs.component<RenderWindow>().add(flecs::Singleton);
|
||||
ecs.import <CharacterModule>();
|
||||
ecs.import <TerrainModule>();
|
||||
ecs.import <BoatModule>();
|
||||
ecs.import <PhysicsModule>();
|
||||
ecs.import <WaterModule>();
|
||||
ecs.import <SunModule>();
|
||||
ecs.import <TerrainModule>();
|
||||
ecs.import <GUIModule>();
|
||||
ecs.import <EventTriggerModule>();
|
||||
ecs.import <LuaModule>();
|
||||
ecs.import <WorldMapModule>();
|
||||
ecs.import <LuaModule>();
|
||||
ecs.import <BoatModule>();
|
||||
ecs.import <EventTriggerModule>();
|
||||
ecs.import <CharacterAnimationModule>();
|
||||
|
||||
ecs.system<EngineData>("UpdateDelta")
|
||||
.kind(flecs::OnUpdate)
|
||||
@@ -78,9 +86,8 @@ void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world,
|
||||
std::cout << "ground check ready\n";
|
||||
#endif
|
||||
});
|
||||
ecs.set<EngineData>({ scnMgr, world, 0.0f, 5.0f,
|
||||
(int)window->getWidth(), (int)window->getHeight(),
|
||||
false });
|
||||
ecs.set<EngineData>({ scnMgr, 0.0f, 5.0f, (int)window->getWidth(),
|
||||
(int)window->getHeight(), false });
|
||||
ecs.set<Camera>({ cameraNode, camera, false });
|
||||
ecs.add<GameData>();
|
||||
ecs.add<Input>();
|
||||
@@ -94,16 +101,92 @@ void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world,
|
||||
nullptr,
|
||||
false,
|
||||
{ 0, 0, 0 } });
|
||||
if (!ecs.has<LuaBase>())
|
||||
ecs.add<LuaBase>();
|
||||
if (!ecs.has<LuaEvent>())
|
||||
ecs.set<LuaEvent>({});
|
||||
|
||||
// ecs.set<Body2Entity>({});
|
||||
std::cout << "Setup GameData done\n";
|
||||
|
||||
/* Create player */
|
||||
player = ecs.entity("player");
|
||||
Ogre::Vector3 playerPos(0, 0, 4);
|
||||
player.set<CharacterLocation>({ { 0, 0, 0, 1 }, playerPos });
|
||||
player.set<CharacterConf>({ "normal-male.glb" });
|
||||
player.add<Character>();
|
||||
player.add<Player>();
|
||||
player = ecs.get_mut<CharacterManagerModule>().createPlayer(
|
||||
{ 0, 0, 4 }, Ogre::Quaternion(Ogre::Radian(Ogre::Math::PI),
|
||||
Ogre::Vector3::UNIT_Y));
|
||||
}
|
||||
void setupEditor(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
|
||||
Ogre::Camera *camera, Ogre::RenderWindow *window)
|
||||
{
|
||||
std::cout << "Setup Editor\n";
|
||||
setup_minimal();
|
||||
ecs.component<RenderWindow>().add(flecs::Singleton);
|
||||
ecs.import <CharacterModule>();
|
||||
ecs.import <BoatModule>();
|
||||
ecs.import <PhysicsModule>();
|
||||
ecs.import <WaterModule>();
|
||||
ecs.import <SunModule>();
|
||||
ecs.import <TerrainModule>();
|
||||
ecs.import <EditorGUIModule>();
|
||||
ecs.import <EventTriggerModule>();
|
||||
ecs.import <LuaModule>();
|
||||
// ecs.import <WorldMapModule>();
|
||||
ecs.import <CharacterAnimationModule>();
|
||||
|
||||
ecs.system<EngineData>("UpdateDelta")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([](EngineData &eng) {
|
||||
eng.delta = ECS::get().delta_time();
|
||||
});
|
||||
ecs.system<EngineData>("UpdateDelay")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<TerrainReady>()
|
||||
.with<WaterReady>()
|
||||
.with<GroundCheckReady>()
|
||||
.each([](EngineData &eng) {
|
||||
if (eng.startupDelay >= 0.0f)
|
||||
eng.startupDelay -= eng.delta;
|
||||
#ifdef VDEBUG
|
||||
if (ECS::get().has<GroundCheckReady>())
|
||||
std::cout << "ground check ready\n";
|
||||
#endif
|
||||
});
|
||||
ecs.system<EngineData>("CheckStatus")
|
||||
.kind(flecs::OnUpdate)
|
||||
.run([](flecs::iter &it) {
|
||||
#ifdef VDEBUG
|
||||
if (ECS::get().has<WaterReady>())
|
||||
std::cout << "water ready\n";
|
||||
if (ECS::get().has<TerrainReady>())
|
||||
std::cout << "terrain ready\n";
|
||||
if (ECS::get().has<GroundCheckReady>())
|
||||
std::cout << "ground check ready\n";
|
||||
#endif
|
||||
});
|
||||
ecs.set<EngineData>({ scnMgr, 0.0f, 5.0f, (int)window->getWidth(),
|
||||
(int)window->getHeight(), false });
|
||||
ecs.set<Camera>({ cameraNode, camera, false });
|
||||
ecs.add<GameData>();
|
||||
ecs.add<Input>();
|
||||
ecs.add<WaterSurface>();
|
||||
ecs.set<Sun>({ nullptr, nullptr, nullptr, nullptr });
|
||||
ecs.set<Terrain>({ nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
false,
|
||||
{ 0, 0, 0 } });
|
||||
ecs.set<GUI>({ true, true, true, false, false, "", {}, -1 });
|
||||
ecs.get_mut<GUI>().enabled = true;
|
||||
ecs.get_mut<GUI>().setWindowGrab(false);
|
||||
ecs.modified<GUI>();
|
||||
ecs.get_mut<GUI>().setWindowGrab(true);
|
||||
ecs.modified<GUI>();
|
||||
ecs.get_mut<GUI>().enabled = true;
|
||||
ecs.modified<GUI>();
|
||||
}
|
||||
|
||||
void update(float delta)
|
||||
{
|
||||
ecs.progress(delta);
|
||||
@@ -117,4 +200,4 @@ bool Vector3::zeroLength() const
|
||||
float l = x * x + y * y + z * z;
|
||||
return (l < 1e-06 * 1e-06);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
#ifndef GAMEDATA_H
|
||||
#define GAMEDATA_H
|
||||
#include <OgreBullet.h>
|
||||
#include <flecs.h>
|
||||
namespace ECS
|
||||
{
|
||||
extern flecs::entity player;
|
||||
void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world,
|
||||
Ogre::SceneNode *cameraNode, Ogre::Camera *camera,
|
||||
Ogre::RenderWindow *window);
|
||||
void setup_minimal();
|
||||
void setup(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
|
||||
Ogre::Camera *camera, Ogre::RenderWindow *window);
|
||||
void setupEditor(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
|
||||
Ogre::Camera *camera, Ogre::RenderWindow *window);
|
||||
void update(float delta);
|
||||
flecs::world get();
|
||||
template <class T> const T &get()
|
||||
@@ -23,4 +24,4 @@ template <class T> void modified()
|
||||
ECS::get().modified<T>();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -2,21 +2,83 @@
|
||||
#include "GameData.h"
|
||||
#include "Components.h"
|
||||
#include "GUIModule.h"
|
||||
#include "PhysicsModule.h"
|
||||
#include "CharacterModule.h"
|
||||
#include "CharacterAnimationModule.h"
|
||||
#include "CharacterManagerModule.h"
|
||||
#include "VehicleManagerModule.h"
|
||||
#include "BoatModule.h"
|
||||
#include "EventTriggerModule.h"
|
||||
#include "SlotsModule.h"
|
||||
#include "world-build.h"
|
||||
#include "LuaData.h"
|
||||
|
||||
#include "luaaa.hpp"
|
||||
extern "C" {
|
||||
int luaopen_lpeg(lua_State *L);
|
||||
}
|
||||
struct FooPosition {
|
||||
Ogre::Vector3 value;
|
||||
Ogre::Vector3 &get()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
namespace luaaa
|
||||
{
|
||||
template <> struct LuaStack<FooPosition> {
|
||||
inline static FooPosition get(lua_State *L, int idx)
|
||||
{
|
||||
FooPosition result;
|
||||
if (lua_istable(L, idx)) {
|
||||
lua_pushnil(L);
|
||||
while (0 != lua_next(L, idx)) {
|
||||
const int top = lua_gettop(L);
|
||||
const char *name =
|
||||
LuaStack<const char *>::get(L, top - 1);
|
||||
if (strncmp(name, "x", 1) == 0)
|
||||
result.value.x =
|
||||
LuaStack<float>::get(L, top);
|
||||
else if (strncmp(name, "y", 1) == 0)
|
||||
result.value.y =
|
||||
LuaStack<float>::get(L, top);
|
||||
else if (strncmp(name, "z", 1) == 0)
|
||||
result.value.z =
|
||||
LuaStack<float>::get(L, top);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
inline static void put(lua_State *L, const FooPosition &v)
|
||||
{
|
||||
lua_newtable(L);
|
||||
LuaStack<const char *>::put(L, "x");
|
||||
LuaStack<float>::put(L, v.value.x);
|
||||
lua_rawset(L, -3);
|
||||
LuaStack<const char *>::put(L, "y");
|
||||
LuaStack<float>::put(L, v.value.y);
|
||||
lua_rawset(L, -3);
|
||||
LuaStack<const char *>::put(L, "z");
|
||||
LuaStack<float>::put(L, v.value.z);
|
||||
lua_rawset(L, -3);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
namespace ECS
|
||||
{
|
||||
struct LuaEcsEntity {
|
||||
int id;
|
||||
flecs::entity e;
|
||||
};
|
||||
struct idmap {
|
||||
std::unordered_map<int, flecs::entity> id2entity;
|
||||
std::unordered_map<flecs::entity_t, int> entity2id;
|
||||
int next_id;
|
||||
idmap()
|
||||
: id2entity({})
|
||||
, entity2id({})
|
||||
, next_id(0)
|
||||
{
|
||||
}
|
||||
@@ -27,8 +89,11 @@ struct idmap {
|
||||
}
|
||||
int add_entity(flecs::entity e)
|
||||
{
|
||||
if (entity2id.find(e.id()) != entity2id.end())
|
||||
return entity2id[e.id()];
|
||||
int id = get_next_id();
|
||||
id2entity[id] = e;
|
||||
entity2id[e.id()] = id;
|
||||
return id;
|
||||
}
|
||||
flecs::entity get_entity(int id)
|
||||
@@ -53,12 +118,36 @@ int LuaData::call_handler(const Ogre::String &event)
|
||||
for (i = 0; i < setup_handlers.size(); i++) {
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, setup_handlers[i]);
|
||||
lua_pushstring(L, event.c_str());
|
||||
lua_pcall(L, 1, 0, 0);
|
||||
lua_pushinteger(L, -1);
|
||||
lua_pushinteger(L, -1);
|
||||
if (lua_pcall(L, 3, 0, 0)) {
|
||||
Ogre::LogManager::getSingleton().stream()
|
||||
<< lua_tostring(L, -1);
|
||||
OgreAssert(false, "Lua error");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int luaLibraryLoader(lua_State *L)
|
||||
int LuaData::call_handler(const Ogre::String &event, flecs::entity e,
|
||||
flecs::entity o)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < setup_handlers.size(); i++) {
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, setup_handlers[i]);
|
||||
lua_pushstring(L, event.c_str());
|
||||
lua_pushinteger(L, idmap.add_entity(e));
|
||||
lua_pushinteger(L, idmap.add_entity(o));
|
||||
if (lua_pcall(L, 3, 0, 0)) {
|
||||
Ogre::LogManager::getSingleton().stream()
|
||||
<< lua_tostring(L, -1);
|
||||
OgreAssert(false, "Lua error");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int luaLibraryLoader(lua_State *L)
|
||||
{
|
||||
int i;
|
||||
if (!lua_isstring(L, 1)) {
|
||||
@@ -159,6 +248,46 @@ LuaData::LuaData()
|
||||
#endif
|
||||
installLibraryLoader(L);
|
||||
lua_pop(L, 1);
|
||||
|
||||
#if 0
|
||||
luaaa::LuaClass<Ogre::Vector3> luaVector3(L, "Vector3");
|
||||
luaVector3.ctor<float, float, float>();
|
||||
luaVector3.get("x", [](Ogre::Vector3 &v) -> float { return v.x; });
|
||||
luaVector3.set("x", [](Ogre::Vector3 &v, float value) -> void {
|
||||
v.x = value;
|
||||
});
|
||||
luaVector3.get("y", [](Ogre::Vector3 &v) -> float { return v.y; });
|
||||
luaVector3.set("y", [](Ogre::Vector3 &v, float value) -> void {
|
||||
v.y = value;
|
||||
});
|
||||
luaVector3.get("z", [](Ogre::Vector3 &v) -> float { return v.z; });
|
||||
luaVector3.set("z", [](Ogre::Vector3 &v, float value) -> void {
|
||||
v.z = value;
|
||||
});
|
||||
luaVector3.fun("squaredLength", &Ogre::Vector3::squaredLength);
|
||||
luaVector3.fun("length", &Ogre::Vector3::length);
|
||||
luaVector3.fun("squaredDistance", &Ogre::Vector3::squaredDistance);
|
||||
luaVector3.fun("distance", &Ogre::Vector3::distance);
|
||||
#endif
|
||||
luaaa::LuaModule luaECS(L, "_ecs");
|
||||
luaECS.fun("position", [](int id) -> FooPosition {
|
||||
#if 0
|
||||
flecs::entity e = idmap.get_entity(id);
|
||||
if (e.has<CharacterBase>()) {
|
||||
const CharacterBase &cb = e.get<CharacterBase>();
|
||||
return cb.mBodyNode->_getDerivedPosition();
|
||||
} else if (e.has<BoatBase>()) {
|
||||
const BoatBase &boat = e.get<BoatBase>();
|
||||
return boat.mNode->_getDerivedPosition();
|
||||
} else if (e.has<EventTrigger>()) {
|
||||
const EventTrigger &trigger = e.get<EventTrigger>();
|
||||
return trigger.position;
|
||||
} else
|
||||
return Ogre::Vector3(0, 0, 0);
|
||||
#endif
|
||||
return FooPosition();
|
||||
});
|
||||
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
OgreAssert(false, "Crash function called");
|
||||
return 0;
|
||||
@@ -225,35 +354,6 @@ LuaData::LuaData()
|
||||
return 0;
|
||||
});
|
||||
lua_setglobal(L, "main_menu");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
OgreAssert(lua_gettop(L) == 3, "Invalid parameters");
|
||||
luaL_checktype(L, 1, LUA_TSTRING);
|
||||
luaL_checktype(L, 2, LUA_TSTRING);
|
||||
luaL_checktype(L, 3, LUA_TBOOLEAN);
|
||||
bool enable = lua_toboolean(L, 3);
|
||||
flecs::entity e = ECS::get().lookup(lua_tostring(L, 1));
|
||||
Ogre::String what = lua_tostring(L, 2);
|
||||
OgreAssert(e.is_valid(), "Invalid character");
|
||||
OgreAssert(e.has<Character>(), "Not a character");
|
||||
if (what == "gravity") {
|
||||
/* clear momentum */
|
||||
e.get_mut<CharacterVelocity>().gvelocity.y = 0.0f;
|
||||
e.get_mut<CharacterVelocity>().velocity.y = 0.0f;
|
||||
e.modified<CharacterVelocity>();
|
||||
if (enable)
|
||||
e.add<CharacterGravity>();
|
||||
else
|
||||
e.remove<CharacterGravity>();
|
||||
} else if (what == "buoyancy") {
|
||||
if (enable)
|
||||
e.add<CharacterBuoyancy>();
|
||||
else
|
||||
e.remove<CharacterBuoyancy>();
|
||||
} else
|
||||
OgreAssert(false, "Bad parameter " + what);
|
||||
return 0;
|
||||
});
|
||||
lua_setglobal(L, "ecs_character_params_set");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
luaL_checktype(L, 1, LUA_TSTRING);
|
||||
luaL_checktype(L, 2, LUA_TNUMBER);
|
||||
@@ -266,16 +366,35 @@ LuaData::LuaData()
|
||||
float x = lua_tonumber(L, 2);
|
||||
float y = lua_tonumber(L, 3);
|
||||
float z = lua_tonumber(L, 4);
|
||||
flecs::entity e = ECS::get().entity();
|
||||
Ogre::Quaternion orientation(Ogre::Radian(yaw),
|
||||
Ogre::Vector3(0, 1, 0));
|
||||
Ogre::Vector3 position(x, y, z);
|
||||
e.set<BoatType>(
|
||||
{ "boat.scene", position, orientation });
|
||||
flecs::entity e =
|
||||
ECS::get_mut<VehicleManagerModule>()
|
||||
.createVehicleAtPosition(
|
||||
"boat", position, orientation);
|
||||
ECS::modified<VehicleManagerModule>();
|
||||
int ret = idmap.add_entity(e);
|
||||
lua_pushinteger(L, ret);
|
||||
std::cout << "boat created: " << ret << std::endl;
|
||||
return 1;
|
||||
} else if (what == "raft") {
|
||||
float yaw = lua_tonumber(L, 5);
|
||||
float x = lua_tonumber(L, 2);
|
||||
float y = lua_tonumber(L, 3);
|
||||
float z = lua_tonumber(L, 4);
|
||||
Ogre::Quaternion orientation(Ogre::Radian(yaw),
|
||||
Ogre::Vector3(0, 1, 0));
|
||||
Ogre::Vector3 position(x, y, z);
|
||||
flecs::entity e =
|
||||
ECS::get_mut<VehicleManagerModule>()
|
||||
.createVehicleAtPosition(
|
||||
"raft", position, orientation);
|
||||
ECS::modified<VehicleManagerModule>();
|
||||
int ret = idmap.add_entity(e);
|
||||
lua_pushinteger(L, ret);
|
||||
std::cout << "raft created: " << ret << std::endl;
|
||||
return 1;
|
||||
}
|
||||
lua_pushinteger(L, -1);
|
||||
return 1;
|
||||
@@ -302,6 +421,15 @@ LuaData::LuaData()
|
||||
return 1;
|
||||
});
|
||||
lua_setglobal(L, "ecs_character_trigger");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
luaL_checktype(L, 1, LUA_TNUMBER); // object
|
||||
int object = lua_tointeger(L, 1);
|
||||
flecs::entity object_e = idmap.get_entity(object);
|
||||
flecs::entity parent_e = object_e.parent();
|
||||
lua_pushinteger(L, idmap.add_entity(parent_e));
|
||||
return 1;
|
||||
});
|
||||
lua_setglobal(L, "ecs_parent");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
OgreAssert(lua_gettop(L) == 7, "bad parameters");
|
||||
luaL_checktype(L, 1, LUA_TNUMBER); // parent
|
||||
@@ -328,6 +456,49 @@ LuaData::LuaData()
|
||||
return 1;
|
||||
});
|
||||
lua_setglobal(L, "ecs_child_character_trigger");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
luaL_checktype(L, 1, LUA_TNUMBER); // trigger
|
||||
luaL_checktype(L, 2, LUA_TNUMBER); // object
|
||||
int trigger = lua_tointeger(L, 1);
|
||||
int object = lua_tointeger(L, 2);
|
||||
flecs::entity trigger_e = idmap.get_entity(trigger);
|
||||
flecs::entity object_e = idmap.get_entity(object);
|
||||
Ogre::SceneNode *target_node = nullptr;
|
||||
Ogre::Any targetAny = trigger_e.get<EventTrigger>()
|
||||
.node->getUserObjectBindings()
|
||||
.getUserAny("target");
|
||||
OgreAssert(targetAny.has_value(), "need target");
|
||||
target_node = Ogre::any_cast<Ogre::SceneNode *>(targetAny);
|
||||
Ogre::Vector3 position = target_node->_getDerivedPosition();
|
||||
Ogre::Quaternion orientation =
|
||||
target_node->_getDerivedOrientation();
|
||||
if (object_e.has<CharacterBase>()) {
|
||||
object_e.get_mut<CharacterBase>()
|
||||
.mBodyNode->_setDerivedPosition(position);
|
||||
object_e.get_mut<CharacterBase>()
|
||||
.mBodyNode->_setDerivedOrientation(orientation);
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
lua_setglobal(L, "ecs_trigger_set_position");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
luaL_checktype(L, 1, LUA_TNUMBER); // trigger
|
||||
int trigger = lua_tointeger(L, 1);
|
||||
flecs::entity trigger_e = idmap.get_entity(trigger);
|
||||
Ogre::SceneNode *node = trigger_e.get<EventTrigger>().node;
|
||||
Ogre::Any animationAny =
|
||||
node->getUserObjectBindings().getUserAny(
|
||||
"trigger_animation");
|
||||
if (animationAny.has_value()) {
|
||||
Ogre::String animation =
|
||||
Ogre::any_cast<Ogre::String>(animationAny);
|
||||
lua_pushstring(L, animation.c_str());
|
||||
return 1;
|
||||
}
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
});
|
||||
lua_setglobal(L, "ecs_trigger_get_animation");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
OgreAssert(lua_gettop(L) == 5, "Invalid parameters");
|
||||
luaL_checktype(L, 1, LUA_TSTRING); // type
|
||||
@@ -340,13 +511,13 @@ LuaData::LuaData()
|
||||
float x = lua_tonumber(L, 2);
|
||||
float y = lua_tonumber(L, 3);
|
||||
float z = lua_tonumber(L, 4);
|
||||
flecs::entity e = ECS::get().entity();
|
||||
Ogre::Quaternion orientation(Ogre::Radian(yaw),
|
||||
Ogre::Vector3(0, 1, 0));
|
||||
Ogre::Vector3::UNIT_Y);
|
||||
Ogre::Vector3 npcPos(x, y, z);
|
||||
e.set<CharacterLocation>({ orientation, npcPos });
|
||||
e.set<CharacterConf>({ type });
|
||||
e.add<Character>();
|
||||
flecs::entity e =
|
||||
ECS::get_mut<CharacterManagerModule>()
|
||||
.createCharacterData(type, npcPos, orientation);
|
||||
ECS::modified<CharacterManagerModule>();
|
||||
lua_pushinteger(L, idmap.add_entity(e));
|
||||
return 1;
|
||||
});
|
||||
@@ -385,20 +556,247 @@ LuaData::LuaData()
|
||||
});
|
||||
lua_setglobal(L, "ecs_set_debug_drawing");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
OgreAssert(lua_gettop(L) == 2, "Bad parameters");
|
||||
luaL_checktype(L, 1, LUA_TNUMBER); // object
|
||||
luaL_checktype(L, 2, LUA_TBOOLEAN);
|
||||
int object = lua_tointeger(L, 1);
|
||||
OgreAssert(lua_gettop(L) >= 1, "Bad parameters");
|
||||
luaL_checktype(L, 1, LUA_TSTRING);
|
||||
Ogre::String command = lua_tostring(L, 1);
|
||||
if (command == "physics-control") {
|
||||
OgreAssert(lua_gettop(L) == 3, "Bad parameters");
|
||||
luaL_checktype(L, 2, LUA_TNUMBER); // object
|
||||
luaL_checktype(L, 3, LUA_TBOOLEAN);
|
||||
int object = lua_tointeger(L, 2);
|
||||
flecs::entity object_e = idmap.get_entity(object);
|
||||
bool enable = lua_toboolean(L, 3);
|
||||
OgreAssert(object_e.has<CharacterBase>(),
|
||||
"Not a character");
|
||||
PhysicsModule::controlPhysics(object_e, enable);
|
||||
object_e.add<CharacterUpdatePhysicsState>();
|
||||
return 0;
|
||||
} else if (command == "is-player") {
|
||||
OgreAssert(lua_gettop(L) == 2, "Bad parameters");
|
||||
luaL_checktype(L, 2, LUA_TNUMBER); // object
|
||||
int object = lua_tointeger(L, 2);
|
||||
flecs::entity object_e = idmap.get_entity(object);
|
||||
lua_pushboolean(L, object_e.has<Player>());
|
||||
return 1;
|
||||
} else if (command == "set-actuator") {
|
||||
OgreAssert(lua_gettop(L) == 3, "Bad parameters");
|
||||
luaL_checktype(L, 2, LUA_TNUMBER); // object
|
||||
luaL_checktype(L, 3, LUA_TSTRING); // animation
|
||||
Ogre::String animation = lua_tostring(L, 3);
|
||||
|
||||
int object = lua_tointeger(L, 2);
|
||||
flecs::entity object_e = idmap.get_entity(object);
|
||||
object_e.set<CharacterVelocity>(
|
||||
{ { 0, 0, 0 }, { 0, 0, 0 } });
|
||||
if (animation.length() > 0)
|
||||
object_e.set<CharacterInActuator>(
|
||||
{ animation, { 0, 0, 0 } });
|
||||
else
|
||||
object_e.remove<CharacterInActuator>();
|
||||
return 0;
|
||||
} else if (command == "animation-state") {
|
||||
OgreAssert(lua_gettop(L) >= 4, "Bad parameters");
|
||||
luaL_checktype(L, 2, LUA_TNUMBER); // object
|
||||
luaL_checktype(L, 3, LUA_TSTRING); // node
|
||||
luaL_checktype(L, 4, LUA_TSTRING); // state
|
||||
if (lua_gettop(L) == 5)
|
||||
luaL_checktype(L, 5, LUA_TBOOLEAN); // reset
|
||||
int object = lua_tointeger(L, 2);
|
||||
flecs::entity object_e = idmap.get_entity(object);
|
||||
Ogre::String nodeName = lua_tostring(L, 3);
|
||||
Ogre::String stateName = lua_tostring(L, 4);
|
||||
bool reset = false;
|
||||
if (lua_gettop(L) == 5)
|
||||
reset = lua_toboolean(L, 5);
|
||||
GameWorld::ValueParameter<flecs::entity> *obj =
|
||||
object_e.world()
|
||||
.get_mut<GameWorld>()
|
||||
.allocate_entity(object_e);
|
||||
GameWorld::ValueParameter<std::string> *obj_node =
|
||||
object_e.world()
|
||||
.get_mut<GameWorld>()
|
||||
.allocate_string(nodeName);
|
||||
GameWorld::ValueParameter<std::string> *obj_state =
|
||||
object_e.world()
|
||||
.get_mut<GameWorld>()
|
||||
.allocate_string(stateName);
|
||||
ECS::get_mut<GameWorld>().command("set_animation_state",
|
||||
{ obj, obj_node,
|
||||
obj_state });
|
||||
ECS::modified<GameWorld>();
|
||||
|
||||
return 0;
|
||||
} else if (command == "params-set") {
|
||||
OgreAssert(lua_gettop(L) == 4, "Invalid parameters");
|
||||
luaL_checktype(L, 2, LUA_TNUMBER);
|
||||
luaL_checktype(L, 3, LUA_TSTRING);
|
||||
luaL_checktype(L, 4, LUA_TBOOLEAN);
|
||||
bool enable = lua_toboolean(L, 4);
|
||||
flecs::entity e = idmap.get_entity(lua_tointeger(L, 2));
|
||||
Ogre::String what = lua_tostring(L, 3);
|
||||
OgreAssert(e.is_valid(), "Invalid character");
|
||||
OgreAssert(e.has<Character>(), "Not a character");
|
||||
if (what == "gravity") {
|
||||
/* clear momentum */
|
||||
if (e.has<CharacterVelocity>()) {
|
||||
e.get_mut<CharacterVelocity>()
|
||||
.gvelocity.y = 0.0f;
|
||||
e.get_mut<CharacterVelocity>()
|
||||
.velocity.y = 0.0f;
|
||||
e.modified<CharacterVelocity>();
|
||||
}
|
||||
if (enable)
|
||||
e.add<CharacterGravity>();
|
||||
else
|
||||
e.remove<CharacterGravity>();
|
||||
} else if (what == "buoyancy") {
|
||||
if (enable)
|
||||
e.add<CharacterBuoyancy>();
|
||||
else
|
||||
e.remove<CharacterBuoyancy>();
|
||||
} else
|
||||
OgreAssert(false, "Bad parameter " + what);
|
||||
return 0;
|
||||
} else {
|
||||
OgreAssert(false, "bad argument " + command);
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
lua_setglobal(L, "ecs_character");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
OgreAssert(lua_gettop(L) == 3, "Bad parameters");
|
||||
luaL_checktype(L, 1, LUA_TNUMBER); // parent
|
||||
luaL_checktype(L, 2, LUA_TNUMBER); // object
|
||||
luaL_checktype(L, 3, LUA_TSTRING); // slot
|
||||
int parent = lua_tointeger(L, 1);
|
||||
int object = lua_tointeger(L, 2);
|
||||
Ogre::String slot = lua_tostring(L, 3);
|
||||
flecs::entity parent_e = idmap.get_entity(parent);
|
||||
flecs::entity object_e = idmap.get_entity(object);
|
||||
bool enable = lua_toboolean(L, 2);
|
||||
if (enable)
|
||||
object_e.remove<CharacterDisablePhysics>();
|
||||
else
|
||||
object_e.add<CharacterDisablePhysics>();
|
||||
object_e.add<CharacterUpdatePhysicsState>();
|
||||
PhysicsModule::controlPhysics(object_e, false);
|
||||
object_e.set<ParentSlot>({ parent_e, slot });
|
||||
return 0;
|
||||
});
|
||||
lua_setglobal(L, "ecs_character_physics_control");
|
||||
lua_setglobal(L, "ecs_set_slot");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
flecs::entity e = ECS::get().lookup("player");
|
||||
int result = idmap.add_entity(e);
|
||||
lua_pushinteger(L, result);
|
||||
return result;
|
||||
});
|
||||
lua_setglobal(L, "ecs_get_player_entity");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
luaL_checktype(L, 1, LUA_TNUMBER); // entity id
|
||||
int id = lua_tointeger(L, 1);
|
||||
flecs::entity e = idmap.get_entity(id);
|
||||
LuaEcsEntity *edata = static_cast<LuaEcsEntity *>(
|
||||
lua_newuserdata(L, sizeof(LuaEcsEntity)));
|
||||
new (edata) LuaEcsEntity();
|
||||
edata->e = e;
|
||||
edata->id = e;
|
||||
luaL_getmetatable(L, "FlecsEntityMetaTable");
|
||||
lua_setmetatable(L, -2);
|
||||
return 1;
|
||||
});
|
||||
lua_setglobal(L, "ecs_get_entity");
|
||||
luaL_newmetatable(L, "FlecsEntityMetaTable");
|
||||
lua_pushstring(L, "__index");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
luaL_checktype(L, 1, LUA_TUSERDATA); //metatable
|
||||
luaL_checktype(L, 2, LUA_TSTRING); //function
|
||||
Ogre::String component = lua_tostring(L, 2);
|
||||
if (component == "components") {
|
||||
lua_pushvalue(L, 1);
|
||||
lua_pushcclosure(
|
||||
L,
|
||||
[](lua_State *L) -> int {
|
||||
luaL_checktype(L, lua_upvalueindex(1),
|
||||
LUA_TUSERDATA);
|
||||
LuaEcsEntity *ent = static_cast<
|
||||
LuaEcsEntity *>(lua_touserdata(
|
||||
L, lua_upvalueindex(1)));
|
||||
std::cout << ent->id << " called!!!\n";
|
||||
ent->e.each([&](flecs::id id) {
|
||||
flecs::entity cmp =
|
||||
ECS::get().entity(id);
|
||||
if (cmp.is_alive()) {
|
||||
const char *name =
|
||||
cmp.name();
|
||||
if (name)
|
||||
std::cout
|
||||
<< "component: "
|
||||
<< name
|
||||
<< std::endl;
|
||||
}
|
||||
});
|
||||
return 0;
|
||||
},
|
||||
1);
|
||||
} else if (component == "is_trigger") {
|
||||
lua_pushvalue(L, 1);
|
||||
lua_pushcclosure(
|
||||
L,
|
||||
[](lua_State *L) -> int {
|
||||
luaL_checktype(L, lua_upvalueindex(1),
|
||||
LUA_TUSERDATA);
|
||||
LuaEcsEntity *ent = static_cast<
|
||||
LuaEcsEntity *>(lua_touserdata(
|
||||
L, lua_upvalueindex(1)));
|
||||
lua_pushboolean(
|
||||
L, ent->e.has<EventTrigger>());
|
||||
return 1;
|
||||
},
|
||||
1);
|
||||
} else if (component == "is_character") {
|
||||
lua_pushvalue(L, 1);
|
||||
lua_pushcclosure(
|
||||
L,
|
||||
[](lua_State *L) -> int {
|
||||
luaL_checktype(L, lua_upvalueindex(1),
|
||||
LUA_TUSERDATA);
|
||||
LuaEcsEntity *ent = static_cast<
|
||||
LuaEcsEntity *>(lua_touserdata(
|
||||
L, lua_upvalueindex(1)));
|
||||
lua_pushboolean(
|
||||
L, ent->e.has<Character>());
|
||||
return 1;
|
||||
},
|
||||
1);
|
||||
} else if (component == "is_boat") {
|
||||
lua_pushvalue(L, 1);
|
||||
lua_pushcclosure(
|
||||
L,
|
||||
[](lua_State *L) -> int {
|
||||
luaL_checktype(L, lua_upvalueindex(1),
|
||||
LUA_TUSERDATA);
|
||||
LuaEcsEntity *ent = static_cast<
|
||||
LuaEcsEntity *>(lua_touserdata(
|
||||
L, lua_upvalueindex(1)));
|
||||
lua_pushboolean(L,
|
||||
ent->e.has<BoatBase>());
|
||||
return 1;
|
||||
},
|
||||
1);
|
||||
} else if (component == "is_player") {
|
||||
lua_pushvalue(L, 1);
|
||||
lua_pushcclosure(
|
||||
L,
|
||||
[](lua_State *L) -> int {
|
||||
luaL_checktype(L, lua_upvalueindex(1),
|
||||
LUA_TUSERDATA);
|
||||
LuaEcsEntity *ent = static_cast<
|
||||
LuaEcsEntity *>(lua_touserdata(
|
||||
L, lua_upvalueindex(1)));
|
||||
lua_pushboolean(L,
|
||||
ent->e.has<Player>());
|
||||
return 1;
|
||||
},
|
||||
1);
|
||||
} else
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
});
|
||||
lua_settable(L, -3);
|
||||
}
|
||||
|
||||
LuaData::~LuaData()
|
||||
@@ -445,6 +843,9 @@ void LuaData::lateSetup()
|
||||
|
||||
LuaModule::LuaModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.module<LuaModule>();
|
||||
ecs.import <SlotsModule>();
|
||||
ecs.import <VehicleManagerModule>();
|
||||
ecs.component<LuaChildEventTrigger>();
|
||||
ecs.component<LuaBase>()
|
||||
.on_add([](LuaBase &lua) {
|
||||
@@ -453,8 +854,7 @@ LuaModule::LuaModule(flecs::world &ecs)
|
||||
lua.startup_called = false;
|
||||
})
|
||||
.add(flecs::Singleton);
|
||||
ecs.add<LuaBase>();
|
||||
|
||||
ecs.component<LuaEvent>().add(flecs::Singleton);
|
||||
ecs.system<const EngineData, LuaBase>("LuaUpdate")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([](const EngineData &eng, LuaBase &lua) {
|
||||
@@ -501,5 +901,14 @@ LuaModule::LuaModule(flecs::world &ecs)
|
||||
trigger.parent = parentNode;
|
||||
e.modified<EventTrigger>();
|
||||
});
|
||||
ecs.system<LuaBase, LuaEvent>("HandleLuaEvents")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([](LuaBase &base, LuaEvent &evt) {
|
||||
while (!evt.events.empty()) {
|
||||
LuaEvent::Event &ev = evt.events.front();
|
||||
base.mLua->call_handler(ev.event, ev.e1, ev.e2);
|
||||
evt.events.pop_front();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,8 @@ struct LuaData {
|
||||
std::vector<int> setup_handlers;
|
||||
int setup_handler();
|
||||
int call_handler(const Ogre::String &event);
|
||||
int call_handler(const Ogre::String &event, flecs::entity e,
|
||||
flecs::entity o);
|
||||
|
||||
LuaData();
|
||||
virtual ~LuaData();
|
||||
@@ -25,5 +27,16 @@ struct LuaBase {
|
||||
struct LuaModule {
|
||||
LuaModule(flecs::world &ecs);
|
||||
};
|
||||
struct LuaEvent {
|
||||
struct Event {
|
||||
Ogre::String event;
|
||||
flecs::entity e1, e2;
|
||||
};
|
||||
std::list<Event> events;
|
||||
void add(const Ogre::String &event, flecs::entity e1, flecs::entity e2)
|
||||
{
|
||||
events.push_back({ event, e1, e2 });
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
751
src/gamedata/PhysicsModule.cpp
Normal file
@@ -0,0 +1,751 @@
|
||||
#include <iostream>
|
||||
#include <Ogre.h>
|
||||
#include <Jolt/Jolt.h>
|
||||
#include <Jolt/Physics/Body/BodyID.h>
|
||||
#include <Jolt/Physics/Character/CharacterBase.h>
|
||||
#include <Jolt/Physics/Character/Character.h>
|
||||
#include <Jolt/Physics/Collision/BroadPhase/BroadPhase.h>
|
||||
#include <Jolt/Physics/Body/BodyLock.h>
|
||||
#include <Jolt/Physics/Collision/ContactListener.h>
|
||||
#include "Components.h"
|
||||
#include "GameData.h"
|
||||
#include "CharacterModule.h"
|
||||
#include "WaterModule.h"
|
||||
#include "BoatModule.h"
|
||||
#include "EventTriggerModule.h"
|
||||
#include "LuaData.h"
|
||||
#include "physics.h"
|
||||
#include "loader.h"
|
||||
#include "EventModule.h"
|
||||
#include "TerrainModule.h"
|
||||
#include "PhysicsModule.h"
|
||||
namespace ECS
|
||||
{
|
||||
struct PhysicsShape {
|
||||
JPH::ShapeRefC shape;
|
||||
};
|
||||
struct ConvexHull {};
|
||||
struct WaterBody {
|
||||
std::set<JPH::BodyID> inWater;
|
||||
bool isInWater(const JPH::BodyID &id) const;
|
||||
};
|
||||
struct TriggerBody {
|
||||
void *foo;
|
||||
};
|
||||
PhysicsModule::PhysicsModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.module<PhysicsModule>();
|
||||
ecs.import <EventModule>();
|
||||
ecs.import <EventTriggerModule>();
|
||||
ecs.import <BoatModule>();
|
||||
flecs::entity PhysicsPreUpdate =
|
||||
ecs.entity().add(flecs::Phase).depends_on(flecs::OnUpdate);
|
||||
flecs::entity PhysicsUpdate =
|
||||
ecs.entity().add(flecs::Phase).depends_on(PhysicsPreUpdate);
|
||||
flecs::entity PhysicsPostUpdate =
|
||||
ecs.entity().add(flecs::Phase).depends_on(PhysicsUpdate);
|
||||
ecs.component<Physics>().add(flecs::Singleton);
|
||||
ecs.component<JPH::BodyID>().member<uint32_t>("mID");
|
||||
/* for terrain */
|
||||
ecs.component<PhysicsBody>();
|
||||
ecs.component<PhysicsShape>();
|
||||
ecs.component<PhysicsNode>();
|
||||
ecs.component<PhysicsMeshName>();
|
||||
ecs.component<PhysicsMeshPtr>();
|
||||
ecs.component<PhysicsHeightfieldData>();
|
||||
|
||||
ecs.component<CharacterBody>();
|
||||
ecs.component<TriggerBody>();
|
||||
ecs.component<CharacterVelocity>();
|
||||
ecs.component<WaterBody>().add(flecs::Singleton);
|
||||
ecs.component<CachedMass>();
|
||||
ecs.import <TerrainModule>();
|
||||
ecs.import <WaterModule>();
|
||||
ecs.system<const EngineData, const Camera>("physics_init")
|
||||
.kind(PhysicsPreUpdate)
|
||||
.without<Physics>()
|
||||
.each([&](const EngineData &e, const Camera &c) {
|
||||
Physics &ph = ECS::get().ensure<Physics>();
|
||||
ph.physics = new JoltPhysicsWrapper(e.mScnMgr,
|
||||
c.mCameraNode);
|
||||
ECS::modified<Physics>();
|
||||
});
|
||||
#if 0
|
||||
ecs.system<PhysicsBody>("create_body")
|
||||
.kind(flecs::OnUpdate)
|
||||
.without<JPH::BodyID>()
|
||||
.each([&](flecs::entity e, PhysicsBody &rb) {
|
||||
JPH::BodyID id =
|
||||
JoltPhysicsWrapper::getSingleton().createBody(
|
||||
rb.shape.get(), rb.node,
|
||||
(JPH::EMotionType)rb.motion,
|
||||
(JPH::ObjectLayer)rb.layer);
|
||||
e.set<JPH::BodyID>(id);
|
||||
});
|
||||
#endif
|
||||
ecs.system<EngineData, Physics>("physics_update")
|
||||
.kind(PhysicsUpdate)
|
||||
.each([&](EngineData &e, Physics &ph) {
|
||||
ph.physics->update(e.delta);
|
||||
});
|
||||
ecs.observer<const EngineData, PhysicsMeshName>(
|
||||
"create_shape_mesh_name")
|
||||
.event(flecs::OnSet)
|
||||
.without<PhysicsShape>()
|
||||
.with<Physics>()
|
||||
.write<PhysicsShape>()
|
||||
.each([&](flecs::entity e, const EngineData &eng,
|
||||
PhysicsMeshName &name) {
|
||||
Ogre::DefaultHardwareBufferManagerBase dmgr;
|
||||
Ogre::MeshPtr mesh =
|
||||
Ogre::MeshManager::getSingleton().getByName(
|
||||
name.meshName);
|
||||
mesh->setHardwareBufferManager(&dmgr);
|
||||
mesh->load();
|
||||
JPH::ShapeRefC shape =
|
||||
JoltPhysicsWrapper::getSingleton()
|
||||
.createMeshShape(mesh);
|
||||
PhysicsShape &s = e.ensure<PhysicsShape>();
|
||||
s.shape = shape;
|
||||
e.modified<PhysicsShape>();
|
||||
});
|
||||
ecs.observer<const EngineData, PhysicsMeshPtr>("create_shape_mesh_ptr")
|
||||
.event(flecs::OnSet)
|
||||
.without<PhysicsShape>()
|
||||
.with<Physics>()
|
||||
.write<PhysicsShape>()
|
||||
.each([&](flecs::entity e, const EngineData &eng,
|
||||
PhysicsMeshPtr &meshPtr) {
|
||||
Ogre::DefaultHardwareBufferManager dmgr;
|
||||
Ogre::MeshPtr mesh = meshPtr.mesh;
|
||||
if (!mesh->isLoaded()) {
|
||||
mesh->setHardwareBufferManager(&dmgr);
|
||||
mesh->load();
|
||||
}
|
||||
JPH::ShapeRefC shape =
|
||||
JoltPhysicsWrapper::getSingleton()
|
||||
.createMeshShape(mesh);
|
||||
PhysicsShape &s = e.ensure<PhysicsShape>();
|
||||
s.shape = shape;
|
||||
e.modified<PhysicsShape>();
|
||||
});
|
||||
ecs.observer<const EngineData, PhysicsHeightfieldData>(
|
||||
"create_shape_heightfield")
|
||||
.event(flecs::OnSet)
|
||||
.without<PhysicsShape>()
|
||||
.with<Physics>()
|
||||
.write<PhysicsShape>()
|
||||
.each([&](flecs::entity e, const EngineData &eng,
|
||||
PhysicsHeightfieldData &hfd) {
|
||||
JPH::ShapeRefC shape =
|
||||
JoltPhysicsWrapper::getSingleton()
|
||||
.createHeightfieldShape(
|
||||
hfd.samples, hfd.offset,
|
||||
hfd.scale, hfd.sampleCount);
|
||||
PhysicsShape &s = e.ensure<PhysicsShape>();
|
||||
s.shape = shape;
|
||||
e.modified<PhysicsShape>();
|
||||
delete hfd.samples;
|
||||
e.remove<PhysicsHeightfieldData>();
|
||||
});
|
||||
#if 1
|
||||
ecs.observer<const EngineData, const PhysicsShape, const PhysicsNode,
|
||||
const PhysicsBody>("create_body_from_shape")
|
||||
.event(flecs::OnSet)
|
||||
.without<JPH::BodyID>()
|
||||
.with<Physics>()
|
||||
.write<PhysicsShape>()
|
||||
.write<JPH::BodyID>()
|
||||
.each([&](flecs::entity e, const EngineData &eng,
|
||||
const PhysicsShape &shape, const PhysicsNode &node,
|
||||
const PhysicsBody &body) {
|
||||
JPH::BodyID id =
|
||||
JoltPhysicsWrapper::getSingleton().createBody(
|
||||
shape.shape.GetPtr(), 0.0f, node.node,
|
||||
(JPH::EMotionType)body.motion,
|
||||
(JPH::ObjectLayer)body.layer);
|
||||
e.set<JPH::BodyID>(id);
|
||||
JoltPhysicsWrapper::getSingleton().addBody(
|
||||
id, JPH::EActivation::Activate);
|
||||
});
|
||||
#endif
|
||||
ecs.observer<const JPH::BodyID>("remove_body")
|
||||
.event(flecs::OnRemove)
|
||||
.each([&](flecs::entity e, const JPH::BodyID &id) {
|
||||
JoltPhysicsWrapper::getSingleton().removeBody(id);
|
||||
JoltPhysicsWrapper::getSingleton().destroyBody(id);
|
||||
std::cout << "body destroyed" << std::endl;
|
||||
});
|
||||
ecs.observer<const EngineData, const CharacterBase>("SetupCharacterPh")
|
||||
.event(flecs::OnSet)
|
||||
.with<Character>()
|
||||
.without<CharacterBody>()
|
||||
.write<CharacterBody>()
|
||||
.each([](flecs::entity e, const EngineData &eng,
|
||||
const CharacterBase &base) {
|
||||
CharacterBody &b = e.ensure<CharacterBody>();
|
||||
b.ch.reset(JoltPhysicsWrapper::getSingleton()
|
||||
.createCharacter(base.mBodyNode,
|
||||
1.75f, 0.23f));
|
||||
if (!e.has<CharacterDisablePhysics>())
|
||||
static_cast<JPH::Character *>(b.ch.get())
|
||||
->AddToPhysicsSystem(
|
||||
JPH::EActivation::Activate);
|
||||
e.set<JPH::BodyID>(
|
||||
static_cast<JPH::Character *>(b.ch.get())
|
||||
->GetBodyID());
|
||||
e.modified<CharacterBody>();
|
||||
});
|
||||
ecs.observer<const EngineData, const EventTrigger>(
|
||||
"CreateTriggerPhysics")
|
||||
.event(flecs::OnSet)
|
||||
.without<JPH::BodyID>()
|
||||
.each([](flecs::entity e, const EngineData &eng,
|
||||
const EventTrigger &trigger) {
|
||||
JPH::ShapeRefC shape =
|
||||
JoltPhysicsWrapper::getSingleton()
|
||||
.createCylinderShape(trigger.halfheight,
|
||||
trigger.radius);
|
||||
JPH::BodyID id =
|
||||
JoltPhysicsWrapper::getSingleton().createSensor(
|
||||
shape.GetPtr(), trigger.node,
|
||||
JPH::EMotionType::Kinematic,
|
||||
Layers::MOVING);
|
||||
e.set<JPH::BodyID>(id);
|
||||
// JoltPhysicsWrapper::getSingleton().setDebugDraw(true);
|
||||
JoltPhysicsWrapper::getSingleton().addBody(
|
||||
id, JPH::EActivation::Activate);
|
||||
JoltPhysicsWrapper::getSingleton().addContactListener(
|
||||
id,
|
||||
[](const JoltPhysics::ContactListener::
|
||||
ContactReport &report) -> void {
|
||||
flecs::entity trigger_e =
|
||||
ECS::get()
|
||||
.query_builder<
|
||||
const JPH::
|
||||
BodyID>()
|
||||
.with<EventTrigger>()
|
||||
.build()
|
||||
.find([&](const JPH::BodyID
|
||||
&id) {
|
||||
return id == report.id1 ||
|
||||
id == report.id2;
|
||||
});
|
||||
flecs::entity parent_e =
|
||||
trigger_e.parent();
|
||||
const JPH::BodyID &trigger_body =
|
||||
trigger_e.get<JPH::BodyID>();
|
||||
const JPH::BodyID &other_body =
|
||||
(trigger_body == report.id1) ?
|
||||
report.id2 :
|
||||
report.id1;
|
||||
flecs::entity other_e =
|
||||
ECS::get()
|
||||
.query_builder<
|
||||
const JPH::
|
||||
BodyID>()
|
||||
.without<EventTrigger>()
|
||||
.build()
|
||||
.find([&](const JPH::BodyID
|
||||
&id) {
|
||||
return id ==
|
||||
other_body;
|
||||
});
|
||||
if (!trigger_e.is_valid() ||
|
||||
!other_e.is_valid())
|
||||
return;
|
||||
if (parent_e.is_valid() &&
|
||||
parent_e == other_e)
|
||||
return;
|
||||
OgreAssert(
|
||||
trigger_e.is_valid(),
|
||||
"trigger entity is not valid");
|
||||
OgreAssert(other_e.is_valid(),
|
||||
"other entity is not valid");
|
||||
const EventTrigger &trg =
|
||||
trigger_e.get<EventTrigger>();
|
||||
if (report.entered) {
|
||||
trigger_e.get_mut<EventData>()
|
||||
.add(trigger_e,
|
||||
trg.event +
|
||||
"_enter",
|
||||
trigger_e,
|
||||
other_e);
|
||||
other_e.add<InTrigger>(
|
||||
trigger_e);
|
||||
trigger_e.add<TriggeredBy>(
|
||||
other_e);
|
||||
} else {
|
||||
trigger_e.get_mut<EventData>()
|
||||
.add(trigger_e,
|
||||
trg.event +
|
||||
"_exit",
|
||||
trigger_e,
|
||||
other_e);
|
||||
#if 0
|
||||
/* do not delete triggers until exit from actuator */
|
||||
other_e.remove<InTrigger>(
|
||||
trigger_e);
|
||||
trigger_e.remove<TriggeredBy>(
|
||||
other_e);
|
||||
#endif
|
||||
}
|
||||
ECS::modified<LuaEvent>();
|
||||
});
|
||||
});
|
||||
#if 0
|
||||
ecs.system<const EngineData, const EventTrigger, const JPH::BodyID>(
|
||||
"UpdateTriggerPhysicsPre")
|
||||
.kind(PhysicsPreUpdate)
|
||||
.with<TerrainReady>()
|
||||
.with<WaterReady>()
|
||||
.with<WaterBody>()
|
||||
.each([](flecs::entity e, const EngineData &eng,
|
||||
const EventTrigger &trigger, const JPH::BodyID &id) {
|
||||
/* FIXME: update positions for triggers, probably need to move somewhere */
|
||||
JoltPhysicsWrapper::getSingleton()
|
||||
.setPositionAndRotation(
|
||||
id, trigger.node->_getDerivedPosition(),
|
||||
trigger.node->_getDerivedOrientation());
|
||||
#if 0
|
||||
std::cout << trigger.node->_getDerivedPosition() << " "
|
||||
<< trigger.node->getPosition() << " "
|
||||
<< trigger.node->getParent()->getName()
|
||||
<< ": " << trigger.node->getName()
|
||||
<< std::endl;
|
||||
// OgreAssert(false, "update triggers");
|
||||
#endif
|
||||
});
|
||||
ecs.system<const EngineData, const EventTrigger, const JPH::BodyID>(
|
||||
"UpdateTriggerPhysicsPost")
|
||||
.kind(PhysicsPostUpdate)
|
||||
.with<TerrainReady>()
|
||||
.with<WaterReady>()
|
||||
.with<WaterBody>()
|
||||
.each([](flecs::entity e, const EngineData &eng,
|
||||
const EventTrigger &trigger, const JPH::BodyID &id) {
|
||||
/* FIXME: update positions for triggers, probably need to move somewhere */
|
||||
Ogre::Vector3 position;
|
||||
Ogre::Quaternion rotation;
|
||||
JoltPhysicsWrapper::getSingleton()
|
||||
.getPositionAndRotation(id, position, rotation);
|
||||
trigger.node->_setDerivedPosition(position);
|
||||
trigger.node->_setDerivedOrientation(rotation);
|
||||
});
|
||||
#endif
|
||||
ecs.system<const EngineData>("init_water")
|
||||
.kind(PhysicsPreUpdate)
|
||||
.with<TerrainReady>()
|
||||
.with<WaterAlmostReady>()
|
||||
.without<WaterBody>()
|
||||
.each([this](const EngineData &eng) {
|
||||
ECS::get().set<WaterBody>({});
|
||||
});
|
||||
ecs.system<const EngineData, WaterBody>("update_water")
|
||||
.kind(PhysicsPostUpdate)
|
||||
.with<TerrainReady>()
|
||||
.with<WaterAlmostReady>()
|
||||
.each([this](const EngineData &eng, WaterBody &body) {
|
||||
const WaterSurface &water = ECS::get<WaterSurface>();
|
||||
body.inWater.clear();
|
||||
JoltPhysicsWrapper::getSingleton().broadphaseQuery(
|
||||
eng.delta,
|
||||
water.mWaterNode->_getDerivedPosition(),
|
||||
body.inWater);
|
||||
#if 0
|
||||
for (JPH::BodyID inBodyID : body.inWater) {
|
||||
if (JoltPhysicsWrapper::getSingleton().isActive(
|
||||
inBodyID)) {
|
||||
float my =
|
||||
JoltPhysicsWrapper::getSingleton()
|
||||
.getPosition(inBodyID)
|
||||
.y;
|
||||
float myv =
|
||||
JoltPhysicsWrapper::getSingleton()
|
||||
.getLinearVelocity(
|
||||
inBodyID)
|
||||
.y;
|
||||
float b = 1.0f;
|
||||
float drag = 0.5f;
|
||||
float adrag = 0.05f;
|
||||
float level = -1.3f;
|
||||
float mdec = 1.0f;
|
||||
float minc = 1.0f;
|
||||
float h = -my + level;
|
||||
if (h < 0)
|
||||
h = 0;
|
||||
if (my < level - 0.1f)
|
||||
b *= 1.1f * (1.0f + 1.2f * h);
|
||||
else if (my > level + 0.1f) {
|
||||
b *= 0.8f;
|
||||
if (myv > 0.1f)
|
||||
b *= 0.9f;
|
||||
if (my > level + 0.4f)
|
||||
b *= 0.5f;
|
||||
}
|
||||
if (myv < 0.0f)
|
||||
drag = 0.7f;
|
||||
JoltPhysicsWrapper::getSingleton().applyBuoyancyImpulse(
|
||||
inBodyID,
|
||||
water.mWaterNode->_getDerivedPosition() -
|
||||
Ogre::Vector3(0, 0.1f,
|
||||
0),
|
||||
Ogre::Vector3::UNIT_Y, b, drag,
|
||||
adrag, Ogre::Vector3::ZERO,
|
||||
eng.delta);
|
||||
// std::cout << b << std::endl;
|
||||
#if 0
|
||||
std::cout << "addHit: "
|
||||
<< JoltPhysics::convert(
|
||||
body.GetPosition())
|
||||
<< std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
ECS::get().add<WaterReady>();
|
||||
});
|
||||
ecs.system<const JPH::BodyID, const WaterBody>("update_water_status1")
|
||||
.kind(PhysicsPostUpdate)
|
||||
.with<TerrainReady>()
|
||||
.with<WaterReady>()
|
||||
.with<InWater>()
|
||||
.each([this](flecs::entity e, const JPH::BodyID &id,
|
||||
const WaterBody &body) {
|
||||
if (!body.isInWater(id))
|
||||
e.remove<InWater>();
|
||||
});
|
||||
ecs.system<const JPH::BodyID, const WaterBody>("update_water_status2")
|
||||
.kind(PhysicsPostUpdate)
|
||||
.with<TerrainReady>()
|
||||
.with<WaterReady>()
|
||||
.without<InWater>()
|
||||
.each([this](flecs::entity e, const JPH::BodyID &id,
|
||||
const WaterBody &body) {
|
||||
if (body.isInWater(id))
|
||||
e.add<InWater>();
|
||||
});
|
||||
ecs.system<const CharacterBody, const WaterBody>(
|
||||
"update_water_character1")
|
||||
.kind(PhysicsPostUpdate)
|
||||
.with<TerrainReady>()
|
||||
.with<WaterReady>()
|
||||
.with<InWater>()
|
||||
.each([this](flecs::entity e, const CharacterBody &ch,
|
||||
const WaterBody &body) {
|
||||
JPH::Character *chptr =
|
||||
static_cast<JPH::Character *>(ch.ch.get());
|
||||
if (!body.isInWater(chptr->GetBodyID()))
|
||||
e.remove<InWater>();
|
||||
});
|
||||
ecs.system<const CharacterBody, const WaterBody>(
|
||||
"update_water_character2")
|
||||
.kind(PhysicsPostUpdate)
|
||||
.with<TerrainReady>()
|
||||
.with<WaterReady>()
|
||||
.without<InWater>()
|
||||
.each([this](flecs::entity e, const CharacterBody &ch,
|
||||
const WaterBody &body) {
|
||||
JPH::Character *chptr =
|
||||
static_cast<JPH::Character *>(ch.ch.get());
|
||||
if (body.isInWater(chptr->GetBodyID()))
|
||||
e.add<InWater>();
|
||||
});
|
||||
ecs.system<const EngineData, const BoatBase, const WaterBody,
|
||||
const JPH::BodyID>("update_water_boat_enable")
|
||||
.kind(PhysicsPreUpdate)
|
||||
.with<TerrainReady>()
|
||||
.with<WaterReady>()
|
||||
.each([this](flecs::entity e, const EngineData &eng,
|
||||
const BoatBase &boat, const WaterBody &body,
|
||||
const JPH::BodyID &id) {
|
||||
if (!JoltPhysicsWrapper::getSingleton().isAdded(id))
|
||||
JoltPhysicsWrapper::getSingleton().addBody(
|
||||
id, JPH::EActivation::Activate);
|
||||
});
|
||||
ecs.system<const EngineData, const BoatBase, const WaterBody,
|
||||
const JPH::BodyID>("update_water_boat_activation")
|
||||
.kind(PhysicsPreUpdate)
|
||||
.with<TerrainReady>()
|
||||
.with<WaterReady>()
|
||||
.with<InWater>()
|
||||
.each([this](flecs::entity e, const EngineData &eng,
|
||||
const BoatBase &boat, const WaterBody &body,
|
||||
const JPH::BodyID &id) {
|
||||
if (!JoltPhysicsWrapper::getSingleton().isActive(id))
|
||||
JoltPhysicsWrapper::getSingleton().activate(id);
|
||||
});
|
||||
ecs.system<const EngineData, const BoatBase, const WaterBody,
|
||||
const JPH::BodyID, const CachedMass>(
|
||||
"update_water_boat_buoyancy")
|
||||
.kind(PhysicsPreUpdate)
|
||||
.with<TerrainReady>()
|
||||
.with<WaterReady>()
|
||||
.with<InWater>()
|
||||
.each([this](flecs::entity e, const EngineData &eng,
|
||||
const BoatBase &boat, const WaterBody &body,
|
||||
const JPH::BodyID &id, const CachedMass &mass) {
|
||||
const WaterSurface &water = ECS::get<WaterSurface>();
|
||||
float b = 1.0f, drag = 0.5f, adrag = 0.5f;
|
||||
float level = 0.25f;
|
||||
float my = JoltPhysicsWrapper::getSingleton()
|
||||
.getPosition(id)
|
||||
.y;
|
||||
float myv = JoltPhysicsWrapper::getSingleton()
|
||||
.getLinearVelocity(id)
|
||||
.y;
|
||||
#if 0
|
||||
if (my < level && myv < 0)
|
||||
b = 10.0f;
|
||||
else if (my > level && myv > 0)
|
||||
b = 0.8f;
|
||||
#endif
|
||||
/* max = 2.7; min = 1.7 */
|
||||
if (my < level)
|
||||
b = 2.2f;
|
||||
else if (my > level)
|
||||
b = 0.9f;
|
||||
// std::cout << my << std::endl;
|
||||
JoltPhysicsWrapper::getSingleton().applyBuoyancyImpulse(
|
||||
id, water.mWaterNode->_getDerivedPosition(),
|
||||
Ogre::Vector3::UNIT_Y, b, drag, adrag,
|
||||
Ogre::Vector3::ZERO, eng.delta);
|
||||
/* stabilisation */
|
||||
Ogre::Vector3 currentAngularVelocity =
|
||||
JoltPhysicsWrapper::getSingleton()
|
||||
.getAngularVelocity(id);
|
||||
Ogre::Quaternion rotation =
|
||||
JoltPhysicsWrapper::getSingleton().getRotation(
|
||||
id);
|
||||
Ogre::Vector3 rotComp =
|
||||
rotation.Inverse() * Ogre::Vector3::UNIT_Y;
|
||||
rotComp.y = 0;
|
||||
if (Ogre::Math::Abs(rotComp.x) > 0.1f ||
|
||||
Ogre::Math::Abs(rotComp.z) > 0.1f) {
|
||||
Ogre::Vector3 localAngularVelocity =
|
||||
rotation.Inverse() *
|
||||
currentAngularVelocity;
|
||||
Ogre::Vector3 localAngularVelocityChange(
|
||||
-localAngularVelocity.x, 0,
|
||||
-localAngularVelocity.z);
|
||||
localAngularVelocityChange -=
|
||||
rotComp * 20.0f * eng.delta;
|
||||
Ogre::Vector3 angularVelocityChange =
|
||||
rotation * localAngularVelocityChange;
|
||||
Ogre::Vector3 angularImpulse =
|
||||
mass.mass * angularVelocityChange;
|
||||
JoltPhysicsWrapper::getSingleton()
|
||||
.addAngularImpulse(id, angularImpulse);
|
||||
}
|
||||
});
|
||||
ecs.system<const EngineData, const CharacterBody, const WaterBody>(
|
||||
"update_water_character_buoyancy")
|
||||
.kind(PhysicsPreUpdate)
|
||||
.with<TerrainReady>()
|
||||
.with<WaterReady>()
|
||||
.with<InWater>()
|
||||
.without<CharacterDisablePhysics>()
|
||||
.with<CharacterBuoyancy>()
|
||||
.each([this](flecs::entity e, const EngineData &eng,
|
||||
const CharacterBody &ch, const WaterBody &body) {
|
||||
JPH::Character *chptr =
|
||||
static_cast<JPH::Character *>(ch.ch.get());
|
||||
JPH::BodyID id = chptr->GetBodyID();
|
||||
if (JoltPhysicsWrapper::getSingleton().isActive(id)) {
|
||||
const WaterSurface &water =
|
||||
ECS::get<WaterSurface>();
|
||||
float my = JoltPhysicsWrapper::getSingleton()
|
||||
.getPosition(id)
|
||||
.y;
|
||||
float myv = JoltPhysicsWrapper::getSingleton()
|
||||
.getLinearVelocity(id)
|
||||
.y;
|
||||
float b = 1.0f;
|
||||
float drag = 0.5f;
|
||||
float adrag = 0.05f;
|
||||
float level = -1.3f;
|
||||
float mdec = 1.0f;
|
||||
float minc = 1.0f;
|
||||
float h = -my + level;
|
||||
if (h < 0)
|
||||
h = 0;
|
||||
if (my < level - 0.1f)
|
||||
b *= 1.1f * (1.0f + 1.2f * h);
|
||||
else if (my > level + 0.1f) {
|
||||
b *= 0.8f;
|
||||
if (myv > 0.1f)
|
||||
b *= 0.9f;
|
||||
if (my > level + 0.4f)
|
||||
b *= 0.5f;
|
||||
}
|
||||
if (myv < 0.0f)
|
||||
drag = 0.7f;
|
||||
JoltPhysicsWrapper::getSingleton()
|
||||
.applyBuoyancyImpulse(
|
||||
id,
|
||||
water.mWaterNode
|
||||
->_getDerivedPosition(),
|
||||
Ogre::Vector3::UNIT_Y, b, drag,
|
||||
adrag, Ogre::Vector3::ZERO,
|
||||
eng.delta);
|
||||
// std::cout << b << std::endl;
|
||||
#if 0
|
||||
std::cout << "addHit: "
|
||||
<< JoltPhysics::convert(
|
||||
body.GetPosition())
|
||||
<< std::endl;
|
||||
#endif
|
||||
}
|
||||
});
|
||||
ecs.system<const EngineData, const CharacterBody>("UpdatePhysics")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<CharacterUpdatePhysicsState>()
|
||||
.write<CharacterUpdatePhysicsState>()
|
||||
.each([](flecs::entity e, const EngineData &eng,
|
||||
const CharacterBody &body) {
|
||||
if (e.has<CharacterDisablePhysics>())
|
||||
PhysicsModule::controlPhysics(e, false);
|
||||
else
|
||||
PhysicsModule::controlPhysics(e, true);
|
||||
e.remove<CharacterUpdatePhysicsState>();
|
||||
});
|
||||
ecs.system<const EngineData, const CharacterBase, const CharacterBody,
|
||||
CharacterVelocity>("HandleVelocity")
|
||||
.kind(PhysicsPostUpdate)
|
||||
.with<TerrainReady>()
|
||||
.with<WaterReady>()
|
||||
.without<CharacterDisablePhysics>()
|
||||
.without<CharacterUpdatePhysicsState>()
|
||||
.each([this](flecs::entity e, const EngineData &eng,
|
||||
const CharacterBase &chbase,
|
||||
const CharacterBody &body, CharacterVelocity &gr) {
|
||||
if (e.has<InWater>() &&
|
||||
chbase.mBodyNode->_getDerivedPosition().y > -0.5f)
|
||||
e.remove<InWater>();
|
||||
Ogre::Vector3 v = gr.velocity;
|
||||
v.y = 0.0f;
|
||||
JPH::Character *ch =
|
||||
static_cast<JPH::Character *>(body.ch.get());
|
||||
if (!e.has<InWater>()) {
|
||||
if (ch->IsSupported()) {
|
||||
v.y = gr.velocity.y;
|
||||
gr.gvelocity.y = 0;
|
||||
} else {
|
||||
v.y = gr.velocity.y;
|
||||
v.y += gr.gvelocity.y;
|
||||
gr.gvelocity.y += -9.8f * eng.delta;
|
||||
}
|
||||
} else {
|
||||
v = JoltPhysics::convert(
|
||||
ch->GetLinearVelocity());
|
||||
v.x = gr.velocity.x;
|
||||
v.z = gr.velocity.z;
|
||||
}
|
||||
// gr.velocity.y = 0.0f;
|
||||
// v.y = 0.0f;
|
||||
ch->SetLinearVelocity(JoltPhysics::convert(v));
|
||||
gr.velocity = Ogre::Vector3::ZERO;
|
||||
});
|
||||
ecs.system<const EngineData, CharacterBase, const CharacterBody,
|
||||
CharacterVelocity>("HandleVelocityNoPhysics")
|
||||
.kind(PhysicsPostUpdate)
|
||||
.with<TerrainReady>()
|
||||
.with<WaterReady>()
|
||||
.with<CharacterDisablePhysics>()
|
||||
.without<CharacterUpdatePhysicsState>()
|
||||
.each([this](flecs::entity e, const EngineData &eng,
|
||||
CharacterBase &ch, const CharacterBody &body,
|
||||
CharacterVelocity &gr) {
|
||||
Ogre::Vector3 v = gr.velocity;
|
||||
// v.y = 0.0f;
|
||||
ch.mBodyNode->_setDerivedPosition(
|
||||
ch.mBodyNode->_getDerivedPosition() +
|
||||
v * eng.delta);
|
||||
gr.velocity = Ogre::Vector3::ZERO;
|
||||
if (e.has<JPH::BodyID>())
|
||||
JoltPhysicsWrapper::getSingleton()
|
||||
.setPositionAndRotation(
|
||||
e.get<JPH::BodyID>(),
|
||||
ch.mBodyNode
|
||||
->_getDerivedPosition(),
|
||||
ch.mBodyNode
|
||||
->_getDerivedOrientation(),
|
||||
false);
|
||||
if (e.has<InWater>() &&
|
||||
ch.mBodyNode->_getDerivedPosition().y > -0.5f) {
|
||||
e.remove<InWater>();
|
||||
ch.is_submerged = false;
|
||||
}
|
||||
if (!e.has<InWater>() && ch.is_submerged)
|
||||
ch.is_submerged = false;
|
||||
});
|
||||
}
|
||||
flecs::entity PhysicsModule::createTerrainChunkBody(Ogre::SceneNode *node,
|
||||
float *samples,
|
||||
const Ogre::Vector3 &offset,
|
||||
const Ogre::Vector3 &scale,
|
||||
int sampleCount)
|
||||
{
|
||||
flecs::entity e = ECS::get().entity();
|
||||
e.set<PhysicsHeightfieldData>({ samples, offset, scale, sampleCount });
|
||||
e.set<PhysicsBody>({ (uint32_t)JPH::EMotionType::Static,
|
||||
(uint32_t)Layers::NON_MOVING });
|
||||
e.set<PhysicsNode>({ node });
|
||||
|
||||
return e;
|
||||
}
|
||||
void PhysicsModule::controlPhysics(flecs::entity e, bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
if (e.has<CharacterBase>()) {
|
||||
e.remove<CharacterDisablePhysics>();
|
||||
OgreAssert(e.has<CharacterBody>(), "No body component");
|
||||
OgreAssert(e.has<JPH::BodyID>(),
|
||||
"No body id in entity");
|
||||
}
|
||||
if (!JoltPhysicsWrapper::getSingleton().isAdded(
|
||||
e.get<JPH::BodyID>())) {
|
||||
Ogre::Vector3 position =
|
||||
e.get<CharacterBase>()
|
||||
.mBodyNode->_getDerivedPosition();
|
||||
Ogre::Quaternion orientation =
|
||||
e.get<CharacterBase>()
|
||||
.mBodyNode->_getDerivedOrientation();
|
||||
if (position.y >= -0.5f)
|
||||
e.remove<InWater>();
|
||||
JoltPhysicsWrapper::getSingleton()
|
||||
.setPositionAndRotation(e.get<JPH::BodyID>(),
|
||||
position, orientation,
|
||||
false);
|
||||
JoltPhysicsWrapper::getSingleton().addBody(
|
||||
e.get<JPH::BodyID>(),
|
||||
JPH::EActivation::Activate);
|
||||
}
|
||||
} else {
|
||||
if (e.has<CharacterBase>()) {
|
||||
e.add<CharacterDisablePhysics>();
|
||||
if (!e.has<CharacterBody>())
|
||||
return;
|
||||
OgreAssert(e.has<CharacterBody>(), "No body component");
|
||||
OgreAssert(e.has<JPH::BodyID>(),
|
||||
"No body id in entity");
|
||||
}
|
||||
if (JoltPhysicsWrapper::getSingleton().isAdded(
|
||||
e.get<JPH::BodyID>()))
|
||||
JoltPhysicsWrapper::getSingleton().removeBody(
|
||||
e.get<JPH::BodyID>());
|
||||
Ogre::Vector3 position =
|
||||
e.get<CharacterBase>().mBodyNode->_getDerivedPosition();
|
||||
e.remove<InWater>();
|
||||
}
|
||||
}
|
||||
bool WaterBody::isInWater(const JPH::BodyID &id) const
|
||||
{
|
||||
flecs::entity e =
|
||||
ECS::get().query_builder<const JPH::BodyID>().build().find(
|
||||
[&](const JPH::BodyID &bodyid) {
|
||||
return bodyid == id;
|
||||
});
|
||||
return inWater.find(id) != inWater.end();
|
||||
}
|
||||
}
|
||||
62
src/gamedata/PhysicsModule.h
Normal file
@@ -0,0 +1,62 @@
|
||||
#ifndef _PHYSICS_MODULE_H_
|
||||
#define _PHYSICS_MODULE_H_
|
||||
#include <flecs.h>
|
||||
class JoltPhysicsWrapper;
|
||||
namespace JPH
|
||||
{
|
||||
class Shape;
|
||||
class CharacterBase;
|
||||
class BodyID;
|
||||
}
|
||||
namespace Ogre
|
||||
{
|
||||
class TerrainGroup;
|
||||
}
|
||||
namespace ECS
|
||||
{
|
||||
struct Physics {
|
||||
JoltPhysicsWrapper *physics;
|
||||
};
|
||||
struct PhysicsNode {
|
||||
Ogre::SceneNode *node;
|
||||
};
|
||||
struct PhysicsMeshName {
|
||||
Ogre::String meshName;
|
||||
};
|
||||
struct PhysicsMeshPtr {
|
||||
Ogre::MeshPtr mesh;
|
||||
};
|
||||
struct PhysicsBody {
|
||||
uint32_t motion;
|
||||
uint32_t layer;
|
||||
};
|
||||
struct CharacterBody {
|
||||
std::shared_ptr<JPH::CharacterBase> ch;
|
||||
};
|
||||
struct BoatBody {
|
||||
void *tmp;
|
||||
};
|
||||
struct PhysicsHeightfieldData {
|
||||
const float *samples;
|
||||
Ogre::Vector3 offset;
|
||||
Ogre::Vector3 scale;
|
||||
int sampleCount;
|
||||
};
|
||||
struct CharacterVelocity {
|
||||
Ogre::Vector3 gvelocity;
|
||||
Ogre::Vector3 velocity;
|
||||
};
|
||||
struct CachedMass {
|
||||
float mass;
|
||||
};
|
||||
struct PhysicsModule {
|
||||
PhysicsModule(flecs::world &ecs);
|
||||
static flecs::entity createTerrainChunkBody(Ogre::SceneNode *node,
|
||||
float *samples,
|
||||
const Ogre::Vector3 &offset,
|
||||
const Ogre::Vector3 &scale,
|
||||
int sampleCount);
|
||||
static void controlPhysics(flecs::entity e, bool enable);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
183
src/gamedata/SlotsModule.cpp
Normal file
@@ -0,0 +1,183 @@
|
||||
#include <iostream>
|
||||
#include "Components.h"
|
||||
#include "GameData.h"
|
||||
#include "CharacterModule.h"
|
||||
#include "BoatModule.h"
|
||||
#include "WaterModule.h"
|
||||
#include "BoatModule.h"
|
||||
#include "SlotsModule.h"
|
||||
namespace ECS
|
||||
{
|
||||
SlotsModule::SlotsModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.module<SlotsModule>();
|
||||
ecs.import <CharacterModule>();
|
||||
ecs.component<ParentSlot>();
|
||||
ecs.component<ParentSlotData>();
|
||||
ecs.component<ObjectSlots>();
|
||||
ecs.import <WaterModule>();
|
||||
ecs.import <BoatModule>();
|
||||
ecs.observer<const EngineData, const BoatBase>("CreateBoatSlots")
|
||||
.event(flecs::OnSet)
|
||||
.each([&](flecs::entity e, const EngineData &eng,
|
||||
const BoatBase &boat) { createBoatSlots(e); });
|
||||
#if 1
|
||||
ecs.system<const EngineData, const CharacterBase, ParentSlot>(
|
||||
"UpdateSlotData")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<CharacterDisablePhysics>()
|
||||
.without<ParentSlotData>()
|
||||
.each([&](flecs::entity e, const EngineData &eng,
|
||||
const CharacterBase &ch, ParentSlot &slot) {
|
||||
if (slot.slot_name == "") {
|
||||
slot.removeSlot(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (slot.parentIsValid()) {
|
||||
if (!slot.check()) {
|
||||
slot.removeSlot(e);
|
||||
return;
|
||||
}
|
||||
slot.addChild(ch.mBodyNode);
|
||||
slot.createSlotData(e);
|
||||
std::cout << "base: "
|
||||
<< slot.getSlotBase()->getName();
|
||||
e.remove<ParentSlot>();
|
||||
e.modified<ParentSlotData>();
|
||||
}
|
||||
});
|
||||
#endif
|
||||
#if 0
|
||||
ecs.system<const EngineData, const CharacterBase, ParentSlot>(
|
||||
"UpdateSlotCharacterTransforms")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<CharacterDisablePhysics>()
|
||||
.without<ParentSlotData>()
|
||||
.with<WaterReady>()
|
||||
.each([&](flecs::entity e, const EngineData &eng,
|
||||
const CharacterBase &ch, ParentSlot &slot) {
|
||||
std::cout << e.name() << std::endl;
|
||||
if (!slot.parentIsValid())
|
||||
return;
|
||||
OgreAssert(slot.parent_e.has<ObjectSlots>(),
|
||||
"parent has no slots");
|
||||
OgreAssert(e.has<Character>(), "not a character");
|
||||
if (slot.parentIsValid()) {
|
||||
if (!slot.check()) {
|
||||
slot.removeSlot(e);
|
||||
OgreAssert(false, "bad slot");
|
||||
return;
|
||||
}
|
||||
slot.createSlot(e);
|
||||
slot.createSlotData(e);
|
||||
}
|
||||
});
|
||||
#endif
|
||||
}
|
||||
void SlotsModule::createBoatSlots(flecs::entity e)
|
||||
{
|
||||
const EngineData &eng = e.world().get<EngineData>();
|
||||
const BoatBase &boat = e.get<BoatBase>();
|
||||
int i;
|
||||
std::vector<Ogre::Node *> slots = boat.mNode->getChildren();
|
||||
for (i = 0; i < slots.size(); i++) {
|
||||
Ogre::Any any = static_cast<Ogre::SceneNode *>(slots[i])
|
||||
->getUserObjectBindings()
|
||||
.getUserAny("type");
|
||||
if (!any.has_value())
|
||||
continue;
|
||||
Ogre::String obj_type = Ogre::any_cast<Ogre::String>(any);
|
||||
std::cout << "child type: " << obj_type << std::endl;
|
||||
}
|
||||
if (slots.size() > 0) {
|
||||
ObjectSlots &vs = e.ensure<ObjectSlots>();
|
||||
for (i = 0; i < slots.size(); i++) {
|
||||
Ogre::Any any = static_cast<Ogre::SceneNode *>(slots[i])
|
||||
->getUserObjectBindings()
|
||||
.getUserAny("type");
|
||||
if (!any.has_value())
|
||||
continue;
|
||||
Ogre::String obj_type =
|
||||
Ogre::any_cast<Ogre::String>(any);
|
||||
any = static_cast<Ogre::SceneNode *>(slots[i])
|
||||
->getUserObjectBindings()
|
||||
.getUserAny("name");
|
||||
if (!any.has_value())
|
||||
continue;
|
||||
Ogre::String obj_name =
|
||||
Ogre::any_cast<Ogre::String>(any);
|
||||
vs.slots[obj_name] = { obj_type,
|
||||
static_cast<Ogre::SceneNode *>(
|
||||
slots[i]) };
|
||||
}
|
||||
}
|
||||
}
|
||||
void ParentSlot::createSlot(flecs::entity e)
|
||||
{
|
||||
if (e.has<CharacterBase>()) {
|
||||
createCharacterSlot(e);
|
||||
}
|
||||
}
|
||||
void ParentSlot::createCharacterSlot(flecs::entity e)
|
||||
{
|
||||
}
|
||||
void ParentSlot::removeSlot(flecs::entity e)
|
||||
{
|
||||
if (e.has<ParentSlot>())
|
||||
e.remove<ParentSlot>();
|
||||
if (e.has<ParentSlot>())
|
||||
e.remove<ParentSlotData>();
|
||||
}
|
||||
bool ParentSlot::check() const
|
||||
{
|
||||
if (!parent_e.has<ObjectSlots>())
|
||||
return false;
|
||||
const ObjectSlots &slots = parent_e.get<ObjectSlots>();
|
||||
if (!slots.exists(slot_name))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool ParentSlot::parentIsValid()
|
||||
{
|
||||
if (!parent_e.has<ObjectSlots>())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
Ogre::SceneNode *ParentSlot::getSlotBase() const
|
||||
{
|
||||
if (!check())
|
||||
return nullptr;
|
||||
const ObjectSlots &slots = parent_e.get<ObjectSlots>();
|
||||
Ogre::SceneNode *slot_base = slots.slots.at(slot_name).second;
|
||||
return slot_base;
|
||||
}
|
||||
void ParentSlot::addChild(Ogre::SceneNode *childNode)
|
||||
{
|
||||
Ogre::SceneNode *parentNode = getSlotBase();
|
||||
if (childNode->getParentSceneNode())
|
||||
childNode->getParentSceneNode()->removeChild(childNode);
|
||||
parentNode->addChild(childNode);
|
||||
childNode->_setDerivedPosition(parentNode->_getDerivedPosition());
|
||||
childNode->_setDerivedOrientation(parentNode->_getDerivedOrientation());
|
||||
}
|
||||
void ParentSlot::createSlotData(flecs::entity e)
|
||||
{
|
||||
const ObjectSlots &slots = parent_e.get<ObjectSlots>();
|
||||
ParentSlotData &psdata = e.ensure<ParentSlotData>();
|
||||
Ogre::SceneNode *slot_base = getSlotBase();
|
||||
// Ogre::Vector3 position = slot_base->_getDerivedPosition();
|
||||
// Ogre::Quaternion orientation = slot_base->_getDerivedOrientation();
|
||||
// ch.mBodyNode->_setDerivedPosition(position);
|
||||
// ch.mBodyNode->_setDerivedOrientation(orientation);
|
||||
psdata.parent_e = parent_e;
|
||||
psdata.parentNode = slot_base;
|
||||
psdata.slot_name = slot_name;
|
||||
e.remove<ParentSlot>();
|
||||
e.modified<ParentSlotData>();
|
||||
}
|
||||
bool ObjectSlots::exists(const Ogre::String &name) const
|
||||
{
|
||||
return slots.find(name) != slots.end();
|
||||
}
|
||||
}
|
||||
35
src/gamedata/SlotsModule.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef _SLOTS_MODULE_H_
|
||||
#define _SLOTS_MODULE_H_
|
||||
#include <flecs.h>
|
||||
#include <Ogre.h>
|
||||
namespace ECS
|
||||
{
|
||||
struct ParentSlot {
|
||||
flecs::entity parent_e;
|
||||
Ogre::String slot_name;
|
||||
void createSlot(flecs::entity e);
|
||||
void createCharacterSlot(flecs::entity e);
|
||||
void removeSlot(flecs::entity e);
|
||||
bool check() const;
|
||||
bool parentIsValid();
|
||||
Ogre::SceneNode *getSlotBase() const;
|
||||
void addChild(Ogre::SceneNode *childNode);
|
||||
void createSlotData(flecs::entity e);
|
||||
};
|
||||
struct ParentSlotData {
|
||||
Ogre::String slot_name;
|
||||
flecs::entity parent_e;
|
||||
Ogre::SceneNode *parentNode;
|
||||
};
|
||||
struct ObjectSlots {
|
||||
std::unordered_map<Ogre::String,
|
||||
std::pair<Ogre::String, Ogre::SceneNode *> >
|
||||
slots;
|
||||
bool exists(const Ogre::String &name) const;
|
||||
};
|
||||
struct SlotsModule {
|
||||
SlotsModule(flecs::world &ecs);
|
||||
void createBoatSlots(flecs::entity e);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
10
src/gamedata/SmartObject.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#include "SmartObject.h"
|
||||
namespace ECS
|
||||
{
|
||||
SmartObjectModule::SmartObjectModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.module<SmartObjectModule>();
|
||||
ecs.component<SmartObjectManager>().add(flecs::Singleton);
|
||||
ecs.component<SmartObject>();
|
||||
}
|
||||
}
|
||||
19
src/gamedata/SmartObject.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef SMART_OBJECT_H_
|
||||
#define SMART_OBJECT_H_
|
||||
#include <Ogre.h>
|
||||
#include <flecs.h>
|
||||
namespace ECS
|
||||
{
|
||||
struct SmartObject {
|
||||
Ogre::String enterNodeName;
|
||||
Ogre::String exitNodeName;
|
||||
Ogre::String scenario;
|
||||
};
|
||||
struct SmartObjectManager {
|
||||
std::vector<flecs::entity> targets;
|
||||
};
|
||||
struct SmartObjectModule {
|
||||
SmartObjectModule(flecs::world &ecs);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
@@ -1,7 +1,6 @@
|
||||
#include <unordered_set>
|
||||
#include <iostream>
|
||||
#include <Ogre.h>
|
||||
#include <OgreBullet.h>
|
||||
#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>
|
||||
#include <OgreTerrain.h>
|
||||
#include <OgreTerrainGroup.h>
|
||||
@@ -14,6 +13,7 @@
|
||||
#include "Components.h"
|
||||
#include "CharacterModule.h"
|
||||
#include "SunModule.h"
|
||||
#include "PhysicsModule.h"
|
||||
#include "TerrainModule.h"
|
||||
|
||||
#define TERRAIN_SIZE 129
|
||||
@@ -173,7 +173,7 @@ class FlatTerrainDefiner
|
||||
: public Ogre::TerrainPagedWorldSection::TerrainDefiner,
|
||||
public Ogre::FrameListener {
|
||||
Ogre::SceneManager *mScnMgr;
|
||||
Ogre::Bullet::DynamicsWorld *mWorld;
|
||||
// Ogre::Bullet::DynamicsWorld *mWorld;
|
||||
struct gen_collider {
|
||||
Ogre::TerrainGroup *group;
|
||||
long x;
|
||||
@@ -182,18 +182,44 @@ class FlatTerrainDefiner
|
||||
std::deque<struct gen_collider> collider_queue;
|
||||
|
||||
public:
|
||||
FlatTerrainDefiner(Ogre::SceneManager *scm,
|
||||
Ogre::Bullet::DynamicsWorld *world)
|
||||
FlatTerrainDefiner(Ogre::SceneManager *
|
||||
scm /*, Ogre::Bullet::DynamicsWorld *world */)
|
||||
: Ogre::TerrainPagedWorldSection::TerrainDefiner()
|
||||
, Ogre::FrameListener()
|
||||
, mScnMgr(scm)
|
||||
, mWorld(world)
|
||||
// , mWorld(world)
|
||||
{
|
||||
Ogre::Root::getSingleton().addFrameListener(this);
|
||||
}
|
||||
|
||||
private:
|
||||
public:
|
||||
void createTerrainChunk(Ogre::TerrainGroup *terrainGroup, long x,
|
||||
long y)
|
||||
{
|
||||
Ogre::Terrain *terrain = terrainGroup->getTerrain(x, y);
|
||||
float minH = terrain->getMinHeight();
|
||||
float maxH = terrain->getMaxHeight();
|
||||
uint16_t terrainSize = terrainGroup->getTerrainSize();
|
||||
OgreAssert(terrain && terrain->getHeightData() &&
|
||||
terrain->isLoaded(),
|
||||
"invalid terrain supplied");
|
||||
uint16_t size = terrain->getSize();
|
||||
float *heightData = terrain->getHeightData();
|
||||
Ogre::SceneNode *node = terrain->_getRootSceneNode();
|
||||
float worldSize = terrain->getWorldSize();
|
||||
float scaled = worldSize / (size - 1);
|
||||
Ogre::Vector3 bodyPosition = terrain->getPosition();
|
||||
bodyPosition.y += (maxH + minH) / 2.0f;
|
||||
bodyPosition.x += worldSize / 2.0f;
|
||||
bodyPosition.z += worldSize / 2.0f;
|
||||
Ogre::Vector3 offset =
|
||||
node->_getDerivedPosition() - bodyPosition;
|
||||
flecs::entity e = PhysicsModule::createTerrainChunkBody(
|
||||
node, heightData, offset,
|
||||
Ogre::Vector3(scaled, 1, scaled), size);
|
||||
node->getUserObjectBindings().setUserAny("_collider", e);
|
||||
}
|
||||
void define(Ogre::TerrainGroup *terrainGroup, long x, long y) override
|
||||
{
|
||||
uint16_t terrainSize = terrainGroup->getTerrainSize();
|
||||
@@ -258,6 +284,10 @@ public:
|
||||
float maxH = terrain->getMaxHeight();
|
||||
int size = terrain->getSize();
|
||||
float worldSize = terrain->getWorldSize();
|
||||
{
|
||||
createTerrainChunk(group, x, y);
|
||||
}
|
||||
#if 0
|
||||
if (true) {
|
||||
btRigidBody *body =
|
||||
mWorld->addTerrainRigidBody(
|
||||
@@ -297,6 +327,7 @@ public:
|
||||
y));
|
||||
created = true;
|
||||
}
|
||||
#endif
|
||||
collider_queue.pop_front();
|
||||
// FIXME: create entities and components instead
|
||||
Ogre::SceneNode *items =
|
||||
@@ -317,11 +348,6 @@ public:
|
||||
what->attachObject(ent);
|
||||
what->setOrientation(item.rotation);
|
||||
what->setPosition(item.position);
|
||||
ECS::get<EngineData>()
|
||||
.mWorld->addRigidBody(
|
||||
0, ent,
|
||||
Ogre::Bullet::CT_TRIMESH,
|
||||
nullptr, 2, 0x7fffffff);
|
||||
}
|
||||
} else {
|
||||
output.push_back(collider_queue.front());
|
||||
@@ -338,13 +364,10 @@ public:
|
||||
};
|
||||
class DummyPageProvider : public Ogre::PageProvider {
|
||||
public:
|
||||
DummyPageProvider(btDynamicsWorld *world)
|
||||
DummyPageProvider(/* btDynamicsWorld *world */)
|
||||
: Ogre::PageProvider()
|
||||
, mBtWorld(world)
|
||||
{
|
||||
}
|
||||
std::unordered_map<Ogre::PageID, btRigidBody *> body;
|
||||
btDynamicsWorld *mBtWorld;
|
||||
bool prepareProceduralPage(Ogre::Page *page,
|
||||
Ogre::PagedWorldSection *section)
|
||||
{
|
||||
@@ -374,9 +397,14 @@ struct TerrainPrivate {
|
||||
TerrainModule::TerrainModule(flecs::world &ecs)
|
||||
{
|
||||
struct CanSetPlayerPosition {};
|
||||
ecs.module<TerrainModule>();
|
||||
ecs.component<CanSetPlayerPosition>().add(flecs::Singleton);
|
||||
ecs.component<Terrain>().add(flecs::Singleton);
|
||||
ecs.component<TerrainPrivate>().add(flecs::Singleton);
|
||||
ecs.component<PlacementObjects>();
|
||||
ecs.component<TerrainReady>().add(flecs::Singleton);
|
||||
ecs.import <CharacterModule>();
|
||||
ecs.import <SunModule>();
|
||||
ecs.set<TerrainPrivate>({ nullptr, {} });
|
||||
ecs.system<const EngineData, const Camera, const Sun, Terrain,
|
||||
TerrainPrivate>("SetupUpdateTerrain")
|
||||
@@ -389,8 +417,8 @@ TerrainModule::TerrainModule(flecs::world &ecs)
|
||||
if (!priv.mDummyPageProvider)
|
||||
priv.mDummyPageProvider =
|
||||
new DummyPageProvider(
|
||||
eng.mWorld
|
||||
->getBtWorld());
|
||||
/* eng.mWorld
|
||||
->getBtWorld() */);
|
||||
terrain.mTerrainGlobals =
|
||||
OGRE_NEW Ogre::TerrainGlobalOptions();
|
||||
|
||||
@@ -477,7 +505,7 @@ TerrainModule::TerrainModule(flecs::world &ecs)
|
||||
|
||||
terrain.mTerrainPagedWorldSection->setDefiner(
|
||||
OGRE_NEW FlatTerrainDefiner(
|
||||
eng.mScnMgr, eng.mWorld));
|
||||
eng.mScnMgr /*, eng.mWorld */));
|
||||
|
||||
terrain.mTerrainGroup->freeTemporaryResources();
|
||||
std::cout << "Terrain setup done\n";
|
||||
|
||||
@@ -36,5 +36,6 @@ struct TerrainModule {
|
||||
static float get_height(Ogre::TerrainGroup *group,
|
||||
const Ogre::Vector3 &position);
|
||||
};
|
||||
struct TerrainReady {};
|
||||
}
|
||||
#endif
|
||||
24
src/gamedata/VehicleManagerModule.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#include "Components.h"
|
||||
#include "GameData.h"
|
||||
#include "BoatModule.h"
|
||||
#include "VehicleManagerModule.h"
|
||||
namespace ECS
|
||||
{
|
||||
VehicleManagerModule::VehicleManagerModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.module<VehicleManagerModule>();
|
||||
ecs.import <BoatModule>();
|
||||
}
|
||||
flecs::entity VehicleManagerModule::createVehicleAtPosition(
|
||||
const Ogre::String &name, const Ogre::Vector3 &position,
|
||||
const Ogre::Quaternion &orientation)
|
||||
{
|
||||
flecs::entity vehicle = ECS::get().entity();
|
||||
if (name == "boat")
|
||||
vehicle.set<BoatType>(
|
||||
{ name + ".scene", position, orientation });
|
||||
else
|
||||
OgreAssert(false, "unsupported type: " + name);
|
||||
return vehicle;
|
||||
}
|
||||
}
|
||||
15
src/gamedata/VehicleManagerModule.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef _VEHICLE_MANAGER_MODULE_H_
|
||||
#define _VEHICLE_MANAGE_MODULE_H_
|
||||
#include <flecs.h>
|
||||
#include <Ogre.h>
|
||||
namespace ECS
|
||||
{
|
||||
struct VehicleManagerModule {
|
||||
VehicleManagerModule(flecs::world &ecs);
|
||||
flecs::entity
|
||||
createVehicleAtPosition(const Ogre::String &name,
|
||||
const Ogre::Vector3 &position,
|
||||
const Ogre::Quaternion &orientation);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "WaterModule.h"
|
||||
namespace ECS
|
||||
{
|
||||
#if 0
|
||||
class WaterPhysicsAction : public btActionInterface {
|
||||
btPairCachingGhostObject *mWaterBody;
|
||||
btManifoldArray mManifoldArray;
|
||||
@@ -53,13 +54,17 @@ public:
|
||||
return (mInWater.find(body) != mInWater.end());
|
||||
}
|
||||
};
|
||||
#endif
|
||||
static const uint32_t WATER_MASK = 0xF00;
|
||||
WaterModule::WaterModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.module<WaterModule>();
|
||||
ecs.component<InWater>();
|
||||
ecs.component<WaterReady>().add(flecs::Singleton);
|
||||
ecs.component<WaterAlmostReady>().add(flecs::Singleton);
|
||||
ecs.component<GroundCheckReady>().add(flecs::Singleton);
|
||||
ecs.component<WaterSurface>()
|
||||
.on_add([](flecs::entity e, WaterSurface &water) {
|
||||
ECS::get().add<WaterBody>();
|
||||
water.mAbove = false;
|
||||
water.mDepthMaterial = nullptr;
|
||||
water.mDepthTech = nullptr;
|
||||
@@ -457,10 +462,14 @@ WaterModule::WaterModule(flecs::world &ecs)
|
||||
eng.mScnMgr->getRenderQueue()->setRenderableListener(
|
||||
&water.mRenderTargetListener);
|
||||
std::cout << "Water setup done\n";
|
||||
ECS::get().add<WaterAlmostReady>();
|
||||
})
|
||||
.add(flecs::Singleton);
|
||||
#if 0
|
||||
ecs.component<WaterBody>().add(flecs::Singleton);
|
||||
ecs.component<WaterBody>()
|
||||
.on_add([this](WaterBody &body) {
|
||||
#if 0
|
||||
body.mShapeAabbMax = btVector3(0, 0, 0);
|
||||
body.mShapeAabbMin = btVector3(0, 0, 0);
|
||||
body.mSurface.clear();
|
||||
@@ -489,9 +498,11 @@ WaterModule::WaterModule(flecs::world &ecs)
|
||||
.get<EngineData>()
|
||||
.mWorld->getBtWorld()
|
||||
->addAction(body.action);
|
||||
#endif
|
||||
ECS::get().add<WaterReady>();
|
||||
})
|
||||
.add(flecs::Singleton);
|
||||
#endif
|
||||
ecs.system<const EngineData, const Camera, WaterSurface>("UpdateWater")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<WaterReady>()
|
||||
@@ -520,6 +531,7 @@ WaterModule::WaterModule(flecs::world &ecs)
|
||||
// water.mRenderTargetListener.mInDepth = false;
|
||||
// water.mWaterEnt->setVisible(true);
|
||||
});
|
||||
#if 0
|
||||
ecs.system<const EngineData, const WaterSurface, WaterBody>(
|
||||
"UpdateWaterBody")
|
||||
.kind(flecs::OnUpdate)
|
||||
@@ -527,6 +539,7 @@ WaterModule::WaterModule(flecs::world &ecs)
|
||||
.each([this](const EngineData &eng, const WaterSurface &water,
|
||||
WaterBody &body) {
|
||||
int i;
|
||||
#if 0
|
||||
OgreAssert(body.mWaterBody, "Water not ready");
|
||||
std::set<btCollisionObject *> currentOverlaps;
|
||||
Ogre::Vector3 waterPos =
|
||||
@@ -542,6 +555,7 @@ WaterModule::WaterModule(flecs::world &ecs)
|
||||
body.mWaterBody->getWorldTransform().setOrigin(
|
||||
Ogre::Bullet::convert(waterBodyPos +
|
||||
d));
|
||||
#endif
|
||||
#if 0
|
||||
btCompoundShape *mshape =
|
||||
static_cast<btCompoundShape *>(
|
||||
@@ -646,6 +660,7 @@ WaterModule::WaterModule(flecs::world &ecs)
|
||||
}
|
||||
#endif
|
||||
});
|
||||
#endif
|
||||
}
|
||||
struct shapeParams {
|
||||
float offsetX, offsetY, offsetZ;
|
||||
@@ -654,6 +669,7 @@ struct shapeParams {
|
||||
static struct shapeParams childShapes[] = {
|
||||
{ 0.0f, 0.0f, 0.0f, 100.0f, 100.0f, 100.0f },
|
||||
};
|
||||
#if 0
|
||||
void WaterModule::createWaterShape(WaterBody *water)
|
||||
{
|
||||
int i = 0;
|
||||
@@ -684,6 +700,7 @@ void WaterModule::createWaterShape(WaterBody *water)
|
||||
shape->recalculateLocalAabb();
|
||||
water->mWaterShape = shape;
|
||||
}
|
||||
#endif
|
||||
void WaterSurface::RenderTextureListener::preRenderTargetUpdate(
|
||||
const Ogre::RenderTargetEvent &evt)
|
||||
{
|
||||
@@ -712,6 +729,7 @@ bool WaterSurface::RenderTextureListener::renderableQueued(
|
||||
*ppTech = mSurface->mDepthTech;
|
||||
return true;
|
||||
}
|
||||
#if 0
|
||||
struct DeepPenetrationContactResultCallback : public btManifoldResult {
|
||||
DeepPenetrationContactResultCallback(
|
||||
const btCollisionObjectWrapper *body0Wrap,
|
||||
@@ -954,8 +972,11 @@ void WaterPhysicsAction::debugDraw(btIDebugDraw *debugDrawer)
|
||||
void WaterPhysicsAction::setupBody()
|
||||
{
|
||||
}
|
||||
#endif
|
||||
#if 0
|
||||
bool WaterBody::isInWater(const btCollisionObject *body) const
|
||||
{
|
||||
return static_cast<WaterPhysicsAction *>(action)->isInWater(body);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -37,20 +37,13 @@ struct WaterSurface {
|
||||
mRefractionClipPlaneBelow;
|
||||
Ogre::Viewport *mViewports[4];
|
||||
};
|
||||
struct WaterBody {
|
||||
std::vector<btCollisionShape *> mChildShapes;
|
||||
btCollisionShape *mWaterShape;
|
||||
btPairCachingGhostObject *mWaterBody;
|
||||
std::unordered_map<btCollisionObject *, float> mSurface;
|
||||
btVector3 mShapeAabbMin, mShapeAabbMax;
|
||||
int count;
|
||||
btActionInterface *action;
|
||||
bool isInWater(const btCollisionObject *body) const;
|
||||
};
|
||||
struct InWater {};
|
||||
struct WaterAlmostReady {};
|
||||
struct WaterReady {};
|
||||
struct WaterModule {
|
||||
btPairCachingGhostObject *mGhostObject;
|
||||
// btPairCachingGhostObject *mGhostObject;
|
||||
WaterModule(flecs::world &ecs);
|
||||
void createWaterShape(WaterBody *water);
|
||||
// void createWaterShape(WaterBody *water);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
38
src/gamedata/goap.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#include <set>
|
||||
#include "goap.h"
|
||||
bool BaseAction::can_run(const ECS::Blackboard &state, bool debug)
|
||||
{
|
||||
return m_exec->_can_run(state, debug);
|
||||
}
|
||||
void BaseAction::_plan_effects(ECS::Blackboard &state)
|
||||
{
|
||||
state.clear_flag(m_exec->m_clear_bits);
|
||||
state.set_flag(m_exec->m_set_bits);
|
||||
}
|
||||
bool BaseAction::is_active(const ECS::Blackboard &state)
|
||||
{
|
||||
return m_exec->is_active(state);
|
||||
}
|
||||
bool BaseAction::stop(ECS::Blackboard &state)
|
||||
{
|
||||
if (!is_active(state))
|
||||
return false;
|
||||
m_exec->_exit(state);
|
||||
state._active.erase(m_exec.get());
|
||||
return true;
|
||||
}
|
||||
int BaseAction::execute(ECS::Blackboard &state)
|
||||
{
|
||||
if (!is_active(state)) {
|
||||
state._active.insert(m_exec.get());
|
||||
m_exec->_enter(state);
|
||||
}
|
||||
int ret = m_exec->_execute(state);
|
||||
if (ret == OK)
|
||||
stop(state);
|
||||
return ret;
|
||||
}
|
||||
int BaseAction::get_cost(const ECS::Blackboard &state) const
|
||||
{
|
||||
return m_exec->_get_cost(state);
|
||||
}
|
||||
350
src/gamedata/goap.h
Normal file
@@ -0,0 +1,350 @@
|
||||
#ifndef H_GOAP_H_
|
||||
#define H_GOAP_H_
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <flecs.h>
|
||||
namespace ECS
|
||||
{
|
||||
struct Blackboard;
|
||||
}
|
||||
struct BaseAction;
|
||||
struct BaseActionExec;
|
||||
namespace ECS
|
||||
{
|
||||
struct Blackboard {
|
||||
enum {
|
||||
HIGH_LUST = (1 << 0),
|
||||
LOW_LUST = (1 << 1),
|
||||
FULL_LUST = (1 << 2),
|
||||
LOW_HEALTH = (1 << 3),
|
||||
FULL_HEALTH = (1 << 4),
|
||||
HAS_TARGET = (1 << 5),
|
||||
LOW_STAMINA = (1 << 6),
|
||||
FULL_STAMINA = (1 << 7),
|
||||
REACHED_TARGET = (1 << 8),
|
||||
};
|
||||
flecs::entity me;
|
||||
int health;
|
||||
int stamina;
|
||||
int lust;
|
||||
uint32_t flags;
|
||||
std::set<BaseActionExec *> _active;
|
||||
bool operator==(const Blackboard &other)
|
||||
{
|
||||
return flags == other.flags;
|
||||
}
|
||||
void set_flag(int flag)
|
||||
{
|
||||
flags |= flag;
|
||||
}
|
||||
void clear_flag(int flag)
|
||||
{
|
||||
flags &= ~flag;
|
||||
}
|
||||
bool get_flag(int flag) const
|
||||
{
|
||||
return flags & flag;
|
||||
}
|
||||
bool check_flag(int flag) const
|
||||
{
|
||||
return (flags & flag) == flag;
|
||||
}
|
||||
};
|
||||
}
|
||||
struct BaseAction {
|
||||
std::string m_name;
|
||||
|
||||
public:
|
||||
enum { OK = 0, BUSY, ABORT };
|
||||
|
||||
private:
|
||||
std::unique_ptr<BaseActionExec> m_exec;
|
||||
|
||||
public:
|
||||
BaseAction(const std::string &name, BaseActionExec *exec)
|
||||
: m_name(name)
|
||||
, m_exec(exec)
|
||||
{
|
||||
}
|
||||
const std::string &get_name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
void plan_effects(ECS::Blackboard &state)
|
||||
{
|
||||
// std::cout << m_name << " pre: " << &state << " " << state.flags
|
||||
// << std::endl;
|
||||
_plan_effects(state);
|
||||
// std::cout << m_name << " post: " << &state << " " << state.flags
|
||||
// << std::endl;
|
||||
}
|
||||
virtual bool can_run(const ECS::Blackboard &state, bool debug = false);
|
||||
virtual void _plan_effects(ECS::Blackboard &state);
|
||||
bool is_active(const ECS::Blackboard &state);
|
||||
bool stop(ECS::Blackboard &state);
|
||||
int execute(ECS::Blackboard &state);
|
||||
virtual int get_cost(const ECS::Blackboard &state) const;
|
||||
};
|
||||
struct BaseActionExec {
|
||||
enum {
|
||||
OK = BaseAction::OK,
|
||||
BUSY = BaseAction::BUSY,
|
||||
ABORT = BaseAction::ABORT
|
||||
};
|
||||
BaseActionExec(int set_bits, int clear_bits)
|
||||
: m_set_bits(set_bits)
|
||||
, m_clear_bits(clear_bits)
|
||||
{
|
||||
}
|
||||
bool is_active(const ECS::Blackboard &state)
|
||||
{
|
||||
return state._active.find(this) != state._active.end();
|
||||
}
|
||||
virtual int _execute(ECS::Blackboard &state) = 0;
|
||||
virtual void _enter(ECS::Blackboard &state) = 0;
|
||||
virtual void _exit(ECS::Blackboard &state) = 0;
|
||||
virtual int _get_cost(const ECS::Blackboard &state) const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
virtual bool _can_run(const ECS::Blackboard &state, bool debug = false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
int m_set_bits, m_clear_bits;
|
||||
};
|
||||
|
||||
template <typename State, typename Act, int N = 100> class BasePlanner {
|
||||
struct VisitedState {
|
||||
int priority;
|
||||
int cost;
|
||||
State state;
|
||||
|
||||
// Used to reconstruct the path
|
||||
VisitedState *parent;
|
||||
Act *action;
|
||||
|
||||
// Only used for linked list management
|
||||
VisitedState *next;
|
||||
};
|
||||
VisitedState nodes[N];
|
||||
void visited_states_array_to_list(VisitedState *nodes, int len)
|
||||
{
|
||||
for (auto i = 0; i < len - 1; i++) {
|
||||
nodes[i].next = &nodes[i + 1];
|
||||
}
|
||||
nodes[len - 1].next = nullptr;
|
||||
}
|
||||
VisitedState *priority_list_pop(VisitedState *&list)
|
||||
{
|
||||
VisitedState *min_elem = nullptr;
|
||||
auto min_prio = std::numeric_limits<int>::max();
|
||||
|
||||
// Find the element with the lowest priority
|
||||
for (auto p = list; p != nullptr; p = p->next) {
|
||||
if (p->priority < min_prio) {
|
||||
min_elem = p;
|
||||
min_prio = p->priority;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove it from the list
|
||||
if (min_elem == list) {
|
||||
list = list->next;
|
||||
} else {
|
||||
for (auto p = list; p != nullptr; p = p->next) {
|
||||
if (p->next == min_elem) {
|
||||
p->next = p->next->next;
|
||||
min_elem->next = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return min_elem;
|
||||
}
|
||||
|
||||
VisitedState *list_pop_head(VisitedState *&head)
|
||||
{
|
||||
auto ret = head;
|
||||
head = head->next;
|
||||
ret->next = nullptr;
|
||||
return ret;
|
||||
}
|
||||
void list_push_head(VisitedState *&head, VisitedState *elem)
|
||||
{
|
||||
elem->next = head;
|
||||
head = elem;
|
||||
}
|
||||
void update_queued_state(VisitedState *previous,
|
||||
const VisitedState *current)
|
||||
{
|
||||
if (previous->cost > current->cost) {
|
||||
previous->priority = current->cost;
|
||||
previous->priority = current->priority;
|
||||
previous->parent = current->parent;
|
||||
previous->action = current->action;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
struct BaseGoal {
|
||||
std::string m_name;
|
||||
|
||||
public:
|
||||
BaseGoal(const std::string &name)
|
||||
: m_name(name)
|
||||
{
|
||||
}
|
||||
/** Checks if the goal is reached for the given state. */
|
||||
virtual bool is_reached(const State &state) const
|
||||
{
|
||||
return distance_to(state) == 0;
|
||||
}
|
||||
|
||||
/** Computes the distance from state to goal. */
|
||||
virtual int distance_to(const State &state) const = 0;
|
||||
|
||||
virtual ~BaseGoal() = default;
|
||||
const std::string &get_name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
};
|
||||
|
||||
/** Finds a plan from state to goal and returns its length.
|
||||
*
|
||||
* If path is given, then the found path is stored there.
|
||||
*/
|
||||
int plan(const State &state, BaseGoal &goal, Act *actions[],
|
||||
unsigned action_count, Act **path = nullptr, int path_len = 10)
|
||||
{
|
||||
visited_states_array_to_list(nodes, N);
|
||||
|
||||
auto free_nodes = &nodes[0];
|
||||
|
||||
VisitedState *open = list_pop_head(free_nodes);
|
||||
VisitedState *close = nullptr;
|
||||
|
||||
open->state = state;
|
||||
open->cost = 0;
|
||||
open->priority = 0;
|
||||
open->parent = nullptr;
|
||||
open->action = nullptr;
|
||||
|
||||
while (open) {
|
||||
VisitedState *current = priority_list_pop(open);
|
||||
list_push_head(close, current);
|
||||
|
||||
if (goal.is_reached(current->state)) {
|
||||
auto len = 0;
|
||||
for (auto p = current->parent; p;
|
||||
p = p->parent) {
|
||||
len++;
|
||||
}
|
||||
|
||||
if (len > path_len) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (path) {
|
||||
auto i = len - 1;
|
||||
for (auto p = current; p->parent;
|
||||
p = p->parent, i--) {
|
||||
path[i] = p->action;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
for (auto i = 0u; i < action_count; i++) {
|
||||
auto action = actions[i];
|
||||
|
||||
if (action->can_run(current->state)) {
|
||||
// Cannot allocate a new node, abort
|
||||
if (free_nodes == nullptr) {
|
||||
// Garbage collect the node that is most unlikely to be
|
||||
// visited (i.e. lowest priority)
|
||||
VisitedState *gc,
|
||||
*gc_prev = nullptr;
|
||||
for (gc = open; gc && gc->next;
|
||||
gc = gc->next) {
|
||||
gc_prev = gc;
|
||||
}
|
||||
|
||||
if (!gc) {
|
||||
return -2 /* OOM */;
|
||||
}
|
||||
|
||||
if (gc_prev) {
|
||||
gc_prev->next = nullptr;
|
||||
}
|
||||
|
||||
free_nodes = gc;
|
||||
}
|
||||
|
||||
auto neighbor =
|
||||
list_pop_head(free_nodes);
|
||||
neighbor->state = current->state;
|
||||
action->plan_effects(neighbor->state);
|
||||
neighbor->cost =
|
||||
current->cost + 1 +
|
||||
action->get_cost(
|
||||
neighbor->state);
|
||||
neighbor->priority =
|
||||
current->priority + 1 +
|
||||
goal.distance_to(
|
||||
neighbor->state);
|
||||
neighbor->parent = current;
|
||||
neighbor->action = action;
|
||||
|
||||
bool should_insert = true;
|
||||
|
||||
// Check if the node is already in the list of nodes
|
||||
// scheduled to be visited
|
||||
for (auto p = open; p; p = p->next) {
|
||||
if (p->state ==
|
||||
neighbor->state) {
|
||||
should_insert = false;
|
||||
update_queued_state(
|
||||
p, neighbor);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the state is in the list of already visited
|
||||
// state
|
||||
for (auto p = close; p; p = p->next) {
|
||||
if (p->state ==
|
||||
neighbor->state) {
|
||||
should_insert = false;
|
||||
update_queued_state(
|
||||
p, neighbor);
|
||||
}
|
||||
}
|
||||
|
||||
if (should_insert) {
|
||||
list_push_head(open, neighbor);
|
||||
} else {
|
||||
list_push_head(free_nodes,
|
||||
neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reaching here means we did not find a path
|
||||
return -1 /* No path */;
|
||||
}
|
||||
};
|
||||
template <class RunnerType> struct DeclareAction : public BaseAction {
|
||||
RunnerType runner;
|
||||
template <class... Args>
|
||||
DeclareAction(const std::string &name, Args... args)
|
||||
: runner(args...)
|
||||
, BaseAction(name, &runner)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
18
src/physics/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
cmake_minimum_required(VERSION 3.13.0)
|
||||
project(jolt-physics)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
# The COMPONENTS part checks that OGRE was built the way we need it
|
||||
# The CONFIG flag makes sure we get OGRE instead of OGRE-next
|
||||
find_package(Jolt REQUIRED)
|
||||
find_package(OGRE REQUIRED COMPONENTS Bites Paging Terrain CONFIG)
|
||||
find_package(ZLIB)
|
||||
find_package(SDL2)
|
||||
find_package(assimp REQUIRED CONFIG)
|
||||
find_package(OgreProcedural REQUIRED CONFIG)
|
||||
find_package(pugixml REQUIRED CONFIG)
|
||||
find_package(flecs REQUIRED CONFIG)
|
||||
|
||||
add_library(physics STATIC physics.cpp)
|
||||
target_link_libraries(physics PUBLIC OgreMain Jolt::Jolt)
|
||||
target_include_directories(physics PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
1746
src/physics/physics.cpp
Normal file
208
src/physics/physics.h
Normal file
@@ -0,0 +1,208 @@
|
||||
#ifndef __PHYSICS_H_
|
||||
#define __PHYSICS_H_
|
||||
#include <Ogre.h>
|
||||
#include <OgreSingleton.h>
|
||||
#include <Jolt/Jolt.h>
|
||||
#include <Jolt/Physics/Collision/Shape/Shape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>
|
||||
#include <Jolt/Physics/Collision/ObjectLayer.h>
|
||||
#include <Jolt/Physics/Collision/ContactListener.h>
|
||||
#include <Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Jolt/Physics/EActivation.h>
|
||||
void physics();
|
||||
namespace JPH
|
||||
{
|
||||
class CharacterBase;
|
||||
class ContactManifold;
|
||||
class ContactSettings;
|
||||
class SubShapeIDPair;
|
||||
}
|
||||
// Layer that objects can be in, determines which other objects it can collide with
|
||||
// Typically you at least want to have 1 layer for moving bodies and 1 layer for static bodies, but you can have more
|
||||
// layers if you want. E.g. you could have a layer for high detail collision (which is not used by the physics simulation
|
||||
// but only if you do collision testing).
|
||||
namespace Layers
|
||||
{
|
||||
static constexpr JPH::ObjectLayer NON_MOVING = 0;
|
||||
static constexpr JPH::ObjectLayer MOVING = 1;
|
||||
static constexpr JPH::ObjectLayer NUM_LAYERS = 2;
|
||||
};
|
||||
|
||||
// Each broadphase layer results in a separate bounding volume tree in the broad phase. You at least want to have
|
||||
// a layer for non-moving and moving objects to avoid having to update a tree full of static objects every frame.
|
||||
// You can have a 1-on-1 mapping between object layers and broadphase layers (like in this case) but if you have
|
||||
// many object layers you'll be creating many broad phase trees, which is not efficient. If you want to fine tune
|
||||
// your broadphase layers define JPH_TRACK_BROADPHASE_STATS and look at the stats reported on the TTY.
|
||||
namespace BroadPhaseLayers
|
||||
{
|
||||
static constexpr JPH::BroadPhaseLayer NON_MOVING(0);
|
||||
static constexpr JPH::BroadPhaseLayer MOVING(1);
|
||||
static constexpr uint NUM_LAYERS(2);
|
||||
};
|
||||
|
||||
namespace JoltPhysics
|
||||
{
|
||||
Ogre::Vector3 convert(const JPH::Vec3Arg &vec);
|
||||
JPH::Vec3 convert(const Ogre::Vector3 &vec);
|
||||
Ogre::Quaternion convert(const JPH::QuatArg &rot);
|
||||
JPH::Quat convert(const Ogre::Quaternion &rot);
|
||||
struct ShapeData;
|
||||
struct CompoundShapeBuilder {
|
||||
JPH::StaticCompoundShapeSettings shapeSettings;
|
||||
void addShape(JPH::ShapeRefC shape, const Ogre::Vector3 &position,
|
||||
const Ogre::Quaternion &rotation);
|
||||
JPH::ShapeRefC build();
|
||||
};
|
||||
class ContactListener : public JPH::ContactListener {
|
||||
public:
|
||||
struct ContactReport {
|
||||
bool entered;
|
||||
JPH::BodyID id1, id2;
|
||||
JPH::ContactManifold manifold;
|
||||
JPH::ContactSettings settings;
|
||||
};
|
||||
|
||||
private:
|
||||
std::list<ContactReport> reports;
|
||||
std::function<void(const ContactReport &report)> dispatch;
|
||||
std::map<JPH::BodyID, std::function<void(const ContactReport &report)> >
|
||||
listeners;
|
||||
|
||||
public:
|
||||
ContactListener();
|
||||
JPH::ValidateResult OnContactValidate(
|
||||
const JPH::Body &inBody1, const JPH::Body &inBody2,
|
||||
JPH::RVec3Arg inBaseOffset,
|
||||
const JPH::CollideShapeResult &inCollisionResult) override;
|
||||
void OnContactAdded(const JPH::Body &inBody1, const JPH::Body &inBody2,
|
||||
const JPH::ContactManifold &inManifold,
|
||||
JPH::ContactSettings &ioSettings) override;
|
||||
void OnContactPersisted(const JPH::Body &inBody1,
|
||||
const JPH::Body &inBody2,
|
||||
const JPH::ContactManifold &inManifold,
|
||||
JPH::ContactSettings &ioSettings) override;
|
||||
void
|
||||
OnContactRemoved(const JPH::SubShapeIDPair &inSubShapePair) override;
|
||||
void setDispatch(const std::function<void(const ContactReport &report)>
|
||||
dispatcher)
|
||||
{
|
||||
dispatch = dispatcher;
|
||||
}
|
||||
void addListener(
|
||||
const JPH::BodyID &id,
|
||||
const std::function<void(const ContactReport &report)> listener)
|
||||
{
|
||||
listeners[id] = listener;
|
||||
}
|
||||
void removeListener(const JPH::BodyID &id)
|
||||
{
|
||||
listeners.erase(id);
|
||||
}
|
||||
void update();
|
||||
};
|
||||
}
|
||||
|
||||
class JoltPhysicsWrapper : public Ogre::Singleton<JoltPhysicsWrapper> {
|
||||
public:
|
||||
JoltPhysics::ContactListener contacts;
|
||||
JoltPhysicsWrapper(Ogre::SceneManager *scnMgr,
|
||||
Ogre::SceneNode *cameraNode);
|
||||
~JoltPhysicsWrapper();
|
||||
void update(float dt);
|
||||
void addBody(const JPH::BodyID &body, JPH::EActivation activation);
|
||||
bool isAdded(const JPH::BodyID &body);
|
||||
JPH::ShapeRefC createBoxShape(const Ogre::Vector3 &extents);
|
||||
JPH::ShapeRefC createSphereShape(float radius);
|
||||
JPH::ShapeRefC createCylinderShape(float halfHeight, float radius);
|
||||
JPH::ShapeRefC createMeshShape(Ogre::MeshPtr mesh);
|
||||
JPH::ShapeRefC createMeshShape(Ogre::String meshName);
|
||||
JPH::ShapeRefC createConvexHullShape(Ogre::MeshPtr mesh);
|
||||
JPH::ShapeRefC createConvexHullShape(Ogre::String meshName);
|
||||
JPH::ShapeRefC createHeightfieldShape(const float *samples,
|
||||
Ogre::Vector3 offset,
|
||||
Ogre::Vector3 scale,
|
||||
int sampleCount);
|
||||
JPH::ShapeRefC createMutableCompoundShape(
|
||||
const std::vector<JPH::ShapeRefC> &shapes,
|
||||
const std::vector<Ogre::Vector3> &positions,
|
||||
const std::vector<Ogre::Quaternion> &rotations);
|
||||
JPH::ShapeRefC createStaticCompoundShape(
|
||||
const std::vector<JPH::ShapeRefC> &shapes,
|
||||
const std::vector<Ogre::Vector3> &positions,
|
||||
const std::vector<Ogre::Quaternion> &rotations);
|
||||
JPH::ShapeRefC
|
||||
createOffsetCenterOfMassShape(const Ogre::Vector3 &offset,
|
||||
JPH::ShapeRefC shape);
|
||||
JPH::BodyID createBody(const JPH::BodyCreationSettings &settings);
|
||||
JPH::BodyID createBody(const JPH::Shape *shape, float mass,
|
||||
const Ogre::Vector3 &position,
|
||||
const Ogre::Quaternion &rotation,
|
||||
JPH::EMotionType motion, JPH::ObjectLayer layer);
|
||||
JPH::BodyID createBody(const JPH::Shape *shape, float mass,
|
||||
Ogre::SceneNode *node, JPH::EMotionType motion,
|
||||
JPH::ObjectLayer layer);
|
||||
JPH::BodyID createSensor(const JPH::Shape *shape,
|
||||
const Ogre::Vector3 &position,
|
||||
const Ogre::Quaternion &rotation,
|
||||
JPH::EMotionType motion,
|
||||
JPH::ObjectLayer layer);
|
||||
JPH::BodyID createSensor(const JPH::Shape *shape, Ogre::SceneNode *node,
|
||||
JPH::EMotionType motion,
|
||||
JPH::ObjectLayer layer);
|
||||
JPH::CharacterBase *createCharacter(Ogre::SceneNode *node,
|
||||
float characterHeight,
|
||||
float characterRadius);
|
||||
void addShapeToCompound(JPH::Ref<JPH::Shape> compoundShape,
|
||||
JPH::ShapeRefC childShape,
|
||||
const Ogre::Vector3 &position,
|
||||
const Ogre::Quaternion &rotation);
|
||||
void removeBody(const JPH::BodyID &id);
|
||||
void destroyBody(const JPH::BodyID &id);
|
||||
void setDebugDraw(bool enable);
|
||||
void broadphaseQuery(float dt, const Ogre::Vector3 &position,
|
||||
std::set<JPH::BodyID> &inWater);
|
||||
void applyBuoyancyImpulse(JPH::BodyID id,
|
||||
const Ogre::Vector3 &surfacePosition,
|
||||
const Ogre::Vector3 &surfaceNormal,
|
||||
float buoyancy, float linearDrag,
|
||||
float angularDrag,
|
||||
const Ogre::Vector3 &fluidVelocity,
|
||||
const Ogre::Vector3 &gravity, float dt);
|
||||
void applyBuoyancyImpulse(JPH::BodyID id,
|
||||
const Ogre::Vector3 &surfacePosition,
|
||||
const Ogre::Vector3 &surfaceNormal,
|
||||
float buoyancy, float linearDrag,
|
||||
float angularDrag,
|
||||
const Ogre::Vector3 &fluidVelocity, float dt);
|
||||
bool isActive(JPH::BodyID id);
|
||||
void activate(JPH::BodyID id);
|
||||
Ogre::Vector3 getPosition(JPH::BodyID id);
|
||||
void setPosition(JPH::BodyID id, const Ogre::Vector3 &position,
|
||||
bool activate = true);
|
||||
Ogre::Quaternion getRotation(JPH::BodyID id);
|
||||
void setRotation(JPH::BodyID id, const Ogre::Quaternion &rotation,
|
||||
bool activate = true);
|
||||
void getPositionAndRotation(JPH::BodyID id, Ogre::Vector3 &position,
|
||||
Ogre::Quaternion &rotation);
|
||||
void setPositionAndRotation(JPH::BodyID id,
|
||||
const Ogre::Vector3 &position,
|
||||
const Ogre::Quaternion &rotation,
|
||||
bool activate = true);
|
||||
Ogre::Vector3 getLinearVelocity(JPH::BodyID id);
|
||||
Ogre::Vector3 getAngularVelocity(JPH::BodyID id);
|
||||
float getFriction(JPH::BodyID id);
|
||||
void setFriction(JPH::BodyID id, float friction);
|
||||
void addAngularImpulse(const JPH::BodyID &id,
|
||||
const Ogre::Vector3 &impulse);
|
||||
void setDispatch(std::function<void(const JoltPhysics::ContactListener::
|
||||
ContactReport &report)>
|
||||
dispatcher);
|
||||
void addContactListener(
|
||||
const JPH::BodyID &id,
|
||||
const std::function<void(const JoltPhysics::ContactListener::
|
||||
ContactReport &report)>
|
||||
listener);
|
||||
void removeContactListener(const JPH::BodyID &id);
|
||||
};
|
||||
#endif
|
||||
7
src/sceneloader/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
project(sceneloader)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
add_library(sceneloader STATIC loader.cpp)
|
||||
target_include_directories(sceneloader PUBLIC .)
|
||||
target_link_libraries(sceneloader PUBLIC OgreMain PRIVATE pugixml GameData physics)
|
||||
target_link_libraries(sceneloader PUBLIC OgreTerrain)
|
||||
|
||||
2306
src/sceneloader/loader.cpp
Normal file
84
src/sceneloader/loader.h
Normal file
@@ -0,0 +1,84 @@
|
||||
#ifndef LOADER_H_
|
||||
#define LOADER_H_
|
||||
#include <pugixml.hpp>
|
||||
#include <flecs.h>
|
||||
#include <OgreColourValue.h>
|
||||
#include <OgreQuaternion.h>
|
||||
#include <OgreResourceGroupManager.h>
|
||||
#include <OgreString.h>
|
||||
#include <OgrePlugin.h>
|
||||
#include <OgreCodec.h>
|
||||
|
||||
class SceneLoader {
|
||||
public:
|
||||
SceneLoader();
|
||||
virtual ~SceneLoader();
|
||||
|
||||
void load(Ogre::DataStreamPtr &stream, const Ogre::String &groupName,
|
||||
Ogre::SceneNode *rootNode, flecs::entity e);
|
||||
|
||||
void exportScene(Ogre::SceneNode *rootNode,
|
||||
const Ogre::String &outFileName);
|
||||
|
||||
const Ogre::ColourValue &getBackgroundColour()
|
||||
{
|
||||
return mBackgroundColour;
|
||||
}
|
||||
static void setupPhysicsBody(Ogre::SceneNode *node,
|
||||
Ogre::MovableObject *entity,
|
||||
flecs::entity base_e);
|
||||
|
||||
protected:
|
||||
void writeNode(pugi::xml_node &parentXML, const Ogre::SceneNode *node);
|
||||
void processScene(pugi::xml_node &XMLRoot);
|
||||
|
||||
void processNodes(pugi::xml_node &XMLNode);
|
||||
void processExternals(pugi::xml_node &XMLNode);
|
||||
void processEnvironment(pugi::xml_node &XMLNode);
|
||||
void processTerrainGroup(pugi::xml_node &XMLNode);
|
||||
void processBlendmaps(pugi::xml_node &XMLNode);
|
||||
void processUserData(pugi::xml_node &XMLNode,
|
||||
Ogre::UserObjectBindings &userData);
|
||||
void processLight(pugi::xml_node &XMLNode,
|
||||
Ogre::SceneNode *pParent = 0);
|
||||
void processCamera(pugi::xml_node &XMLNode,
|
||||
Ogre::SceneNode *pParent = 0);
|
||||
|
||||
void processNode(pugi::xml_node &XMLNode, Ogre::SceneNode *pParent = 0);
|
||||
void processLookTarget(pugi::xml_node &XMLNode,
|
||||
Ogre::SceneNode *pParent);
|
||||
void processTrackTarget(pugi::xml_node &XMLNode,
|
||||
Ogre::SceneNode *pParent);
|
||||
void processEntity(pugi::xml_node &XMLNode, Ogre::SceneNode *pParent);
|
||||
void processParticleSystem(pugi::xml_node &XMLNode,
|
||||
Ogre::SceneNode *pParent);
|
||||
void processBillboardSet(pugi::xml_node &XMLNode,
|
||||
Ogre::SceneNode *pParent);
|
||||
void processPlane(pugi::xml_node &XMLNode, Ogre::SceneNode *pParent);
|
||||
void processNodeAnimations(pugi::xml_node &XMLNode,
|
||||
Ogre::SceneNode *pParent);
|
||||
void processNodeAnimation(pugi::xml_node &XMLNode,
|
||||
Ogre::SceneNode *pParent);
|
||||
void processKeyframe(pugi::xml_node &XMLNode,
|
||||
Ogre::NodeAnimationTrack *pTrack);
|
||||
|
||||
void processFog(pugi::xml_node &XMLNode);
|
||||
void processSkyBox(pugi::xml_node &XMLNode);
|
||||
void processSkyDome(pugi::xml_node &XMLNode);
|
||||
void processSkyPlane(pugi::xml_node &XMLNode);
|
||||
|
||||
void processLightRange(pugi::xml_node &XMLNode, Ogre::Light *pLight);
|
||||
void processLightAttenuation(pugi::xml_node &XMLNode,
|
||||
Ogre::Light *pLight);
|
||||
void processLightSourceSize(pugi::xml_node &XMLNode,
|
||||
Ogre::Light *pLight);
|
||||
Ogre::SceneManager *mSceneMgr;
|
||||
Ogre::SceneNode *mAttachNode;
|
||||
Ogre::String m_sGroupName;
|
||||
Ogre::ColourValue mBackgroundColour;
|
||||
Ogre::String mNamePrefix;
|
||||
int counter;
|
||||
flecs::entity base_e;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -13,7 +13,6 @@
|
||||
#include <OgreMaterialManager.h>
|
||||
#include <OgreTerrainAutoUpdateLod.h>
|
||||
#include <OgreTerrainPagedWorldSection.h>
|
||||
#include <OgreBullet.h>
|
||||
#include <OgreMath.h>
|
||||
#include <OgreLogManager.h>
|
||||
#include "terrain.h"
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
#include <unordered_set>
|
||||
#include <iostream>
|
||||
#include <Ogre.h>
|
||||
#include <OgreBullet.h>
|
||||
#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>
|
||||
#include <OgreTerrain.h>
|
||||
#include <OgreTerrainGroup.h>
|
||||
#include <OgrePageManager.h>
|
||||
@@ -404,9 +402,11 @@ public:
|
||||
return body;
|
||||
}
|
||||
#endif
|
||||
bool isLoadedAt(const Ogre::Vector3 &position) const {
|
||||
bool isLoadedAt(const Ogre::Vector3 &position) const
|
||||
{
|
||||
long x, y;
|
||||
mTerrainGroup->convertWorldPositionToTerrainSlot(position, &x, &y);
|
||||
mTerrainGroup->convertWorldPositionToTerrainSlot(position, &x,
|
||||
&y);
|
||||
if (mTerrainGroup->getTerrain(x, y))
|
||||
return mTerrainGroup->getTerrain(x, y)->isLoaded();
|
||||
return false;
|
||||
|
||||
12
src/tests/CMakeLists.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
project(tests)
|
||||
find_package(pugixml CONFIG)
|
||||
find_package(assimp REQUIRED CONFIG)
|
||||
find_package(OGRE REQUIRED COMPONENTS Bites Bullet Paging Terrain CONFIG)
|
||||
|
||||
add_executable(check_uv check_uv.cpp)
|
||||
target_link_libraries(check_uv ${ASSIMP_LIBRARIES})
|
||||
add_executable(ogre_check_uv ogre_check_uv.cpp)
|
||||
target_link_libraries(ogre_check_uv OgreBites OgreMain)
|
||||
add_executable(check_scene_loader check_scene_loader.cpp)
|
||||
target_link_libraries(check_scene_loader OgreBites OgreMain)
|
||||
|
||||
64
src/tests/check_scene_loader.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include <iostream>
|
||||
#include <Ogre.h>
|
||||
#include <OgreCodec.h>
|
||||
#include <OgreFileSystem.h>
|
||||
#include <OgreFileSystemLayer.h>
|
||||
#include <OgreMaterialManager.h>
|
||||
#include <OgreShaderGenerator.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
Ogre::LogManager logMgr;
|
||||
logMgr.createLog("messages.log", true, true, true);
|
||||
Ogre::DefaultHardwareBufferManager
|
||||
bufferManager; // needed because we don't have a rendersystem
|
||||
Ogre::Root *ogre = new Ogre::Root("", "", "");
|
||||
Ogre::ConfigFile pluginsCfg;
|
||||
Ogre::FileSystemLayer fsLayer("Ogre3D");
|
||||
pluginsCfg.load(fsLayer.getConfigFilePath("plugins.cfg"));
|
||||
|
||||
auto pluginDir = Ogre::FileSystemLayer::resolveBundlePath(
|
||||
pluginsCfg.getSetting("PluginFolder") + "/");
|
||||
ogre->loadPlugin(pluginDir + "/Codec_Assimp");
|
||||
ogre->loadPlugin(pluginDir + "/Plugin_DotScene");
|
||||
Ogre::MaterialManager::getSingleton().initialise();
|
||||
Ogre::RTShader::ShaderGenerator::initialize();
|
||||
Ogre::DefaultTextureManager texMgr;
|
||||
|
||||
auto &shadergen = Ogre::RTShader::ShaderGenerator::getSingleton();
|
||||
shadergen.setTargetLanguage(
|
||||
"glsl"); // must be valid, but otherwise arbitrary
|
||||
shadergen.getRenderState(Ogre::MSN_SHADERGEN)
|
||||
->setLightCountAutoUpdate(false);
|
||||
shadergen.validateScheme(Ogre::MSN_SHADERGEN);
|
||||
Ogre::SceneManager *sceneManager =
|
||||
ogre->createSceneManager("DefaultSceneManager");
|
||||
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
|
||||
"resources/vehicles", "FileSystem", "Generic", true, true);
|
||||
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
|
||||
"../resources/vehicles", "FileSystem", "Generic", true, true);
|
||||
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
|
||||
"../../resources/vehicles", "FileSystem", "Generic", true,
|
||||
true);
|
||||
std::map<Ogre::String, Ogre::Any> options;
|
||||
options["prefix"] = Ogre::String("0/");
|
||||
Ogre::SceneNode *p1 =
|
||||
sceneManager->getRootSceneNode()->createChildSceneNode();
|
||||
Ogre::Any optionsAny1 = options;
|
||||
p1->getUserObjectBindings().setUserAny("_DotSceneLoaderOptions",
|
||||
optionsAny1);
|
||||
p1->loadChildren("boat.scene");
|
||||
options["prefix"] = Ogre::String("1/");
|
||||
Ogre::SceneNode *p2 =
|
||||
sceneManager->getRootSceneNode()->createChildSceneNode();
|
||||
Ogre::Any optionsAny2 = options;
|
||||
p2->getUserObjectBindings().setUserAny("_DotSceneLoaderOptions",
|
||||
optionsAny2);
|
||||
p2->loadChildren("boat.scene");
|
||||
// Ogre::DataStreamPtr sceneData =
|
||||
// Ogre::ResourceGroupManager::getSingleton().openResource(
|
||||
// "boat.scene", "General");
|
||||
// auto codec = Ogre::Codec::getCodec("scene");
|
||||
// codec->decode(sceneData, sceneManager->getRootSceneNode());
|
||||
return 0;
|
||||
}
|
||||
54
src/tests/check_uv.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <assimp/version.h>
|
||||
#include <assimp/scene.h>
|
||||
#include <assimp/postprocess.h>
|
||||
#include <assimp/Importer.hpp>
|
||||
#include <assimp/IOStream.hpp>
|
||||
#include <assimp/IOSystem.hpp>
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
Assimp::Importer importer;
|
||||
uint32_t flags = aiProcessPreset_TargetRealtime_Fast | aiProcess_TransformUVCoords | aiProcess_FlipUVs;
|
||||
flags &= ~(aiProcess_JoinIdenticalVertices | aiProcess_CalcTangentSpace); // optimize for fast loading
|
||||
if((flags & (aiProcess_GenSmoothNormals | aiProcess_GenNormals)) != aiProcess_GenNormals)
|
||||
flags &= ~aiProcess_GenNormals; // prefer smooth normals
|
||||
|
||||
float maxEdgeAngle = 0.75f;
|
||||
|
||||
importer.SetPropertyFloat("PP_GSN_MAX_SMOOTHING_ANGLE", maxEdgeAngle);
|
||||
importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, true);
|
||||
|
||||
if (argc < 2) {
|
||||
std::cerr << "path name needed" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
const char *pname = argv[1];
|
||||
const aiScene* scene = importer.ReadFile(pname,
|
||||
flags);
|
||||
std::list<aiNode *> queue;
|
||||
if (!scene) {
|
||||
std::cerr << "could not process file" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
queue.push_back(scene->mRootNode);
|
||||
while(!queue.empty()) {
|
||||
aiNode *node = queue.front();
|
||||
queue.pop_front();
|
||||
for (i = 0; i < node->mNumMeshes; i++) {
|
||||
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
|
||||
int uv_count = 0;
|
||||
for (int uvindex = 0; uvindex < AI_MAX_NUMBER_OF_TEXTURECOORDS; uvindex++) {
|
||||
aiVector3D* uv = mesh->mTextureCoords[uvindex];
|
||||
if (!uv)
|
||||
break;
|
||||
uv_count++;
|
||||
}
|
||||
std::cout << "node: " << node->mName.C_Str() << " mesh: " << i << " mesh uv count: " << uv_count << std::endl;
|
||||
}
|
||||
for (i = 0; i < node->mNumChildren; i++)
|
||||
queue.push_back(node->mChildren[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
83
src/tests/ogre_check_uv.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include <iostream>
|
||||
#include <Ogre.h>
|
||||
#include <OgreCodec.h>
|
||||
#include <OgreFileSystem.h>
|
||||
#include <OgreFileSystemLayer.h>
|
||||
#include <OgreMaterialManager.h>
|
||||
#include <OgreShaderGenerator.h>
|
||||
|
||||
static void getSubmeshUVs(const Ogre::Mesh *mesh, const Ogre::SubMesh *submesh,
|
||||
std::vector<Ogre::Vector2> &uvs, int index)
|
||||
{
|
||||
int j;
|
||||
float *pReal;
|
||||
Ogre::HardwareVertexBufferSharedPtr vbuf;
|
||||
Ogre::VertexData *vertex_data = submesh->useSharedVertices ?
|
||||
mesh->sharedVertexData :
|
||||
submesh->vertexData;
|
||||
const Ogre::VertexElement *uvElem =
|
||||
vertex_data->vertexDeclaration->findElementBySemantic(
|
||||
Ogre::VES_TEXTURE_COORDINATES, index);
|
||||
int vertex_count = 0;
|
||||
if (submesh->useSharedVertices)
|
||||
vertex_count += mesh->sharedVertexData->vertexCount;
|
||||
else
|
||||
vertex_count += submesh->vertexData->vertexCount;
|
||||
if (!uvElem)
|
||||
return;
|
||||
OgreAssert(uvs.size() == 0 || uvs.size() == vertex_count,
|
||||
"bad vertex count");
|
||||
uvs.resize(vertex_count);
|
||||
vbuf = vertex_data->vertexBufferBinding->getBuffer(uvElem->getSource());
|
||||
unsigned char *uv = static_cast<unsigned char *>(
|
||||
vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
|
||||
for (j = 0; j < vertex_data->vertexCount; ++j) {
|
||||
uvElem->baseVertexPointerToElement(uv, &pReal);
|
||||
uvs[j] = Ogre::Vector2(pReal[0], pReal[1]);
|
||||
uv += vbuf->getVertexSize();
|
||||
}
|
||||
vbuf->unlock();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
Ogre::LogManager logMgr;
|
||||
logMgr.createLog("messages.log", true, true, true);
|
||||
Ogre::DefaultHardwareBufferManager bufferManager; // needed because we don't have a rendersystem
|
||||
Ogre::Root *ogre = new Ogre::Root("", "", "");
|
||||
Ogre::ConfigFile pluginsCfg;
|
||||
Ogre::FileSystemLayer fsLayer("Ogre3D");
|
||||
pluginsCfg.load(fsLayer.getConfigFilePath("plugins.cfg"));
|
||||
|
||||
auto pluginDir = Ogre::FileSystemLayer::resolveBundlePath(pluginsCfg.getSetting("PluginFolder")+"/");
|
||||
ogre->loadPlugin(pluginDir + "/Codec_Assimp");
|
||||
Ogre::MaterialManager::getSingleton().initialise();
|
||||
Ogre::RTShader::ShaderGenerator::initialize();
|
||||
Ogre::DefaultTextureManager texMgr;
|
||||
|
||||
auto& shadergen = Ogre::RTShader::ShaderGenerator::getSingleton();
|
||||
shadergen.setTargetLanguage("glsl"); // must be valid, but otherwise arbitrary
|
||||
shadergen.getRenderState(Ogre::MSN_SHADERGEN)->setLightCountAutoUpdate(false);
|
||||
shadergen.validateScheme(Ogre::MSN_SHADERGEN);
|
||||
|
||||
auto codec = Ogre::Codec::getCodec("glb");
|
||||
|
||||
Ogre::ResourceGroupManager::getSingleton().createResourceGroup(
|
||||
"Characters", true);
|
||||
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
|
||||
"characters", "FileSystem", "Characters", true, true);
|
||||
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
|
||||
"../characters", "FileSystem", "Characters", true, true);
|
||||
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
|
||||
"../../characters", "FileSystem", "Characters", true, true);
|
||||
Ogre::DataStreamPtr meshData = Ogre::ResourceGroupManager::getSingleton().openResource("shapes/male/edited-normal-male-base.glb", "Characters");
|
||||
Ogre::MeshPtr mesh = Ogre::MeshManager::getSingleton().createManual("shapes/male/edited-normal-male-base.glb", "Characters");
|
||||
codec->decode(meshData, mesh.get());
|
||||
std::vector<Ogre::Vector2> uvs;
|
||||
getSubmeshUVs(mesh.get(), mesh->getSubMesh(0), uvs, 0);
|
||||
std::cout << "UV0: " << uvs.size() << std::endl;
|
||||
uvs.clear();
|
||||
getSubmeshUVs(mesh.get(), mesh->getSubMesh(0), uvs, 1);
|
||||
std::cout << "UV1: " << uvs.size() << std::endl;
|
||||
return 0;
|
||||
}
|
||||
21
src/world/CMakeLists.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
project(world)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
find_package(Bullet)
|
||||
find_package(OGRE REQUIRED COMPONENTS Bites Bullet Paging Terrain Overlay CONFIG)
|
||||
add_library(action STATIC action.cpp)
|
||||
target_link_libraries(action PRIVATE GameData)
|
||||
target_include_directories(action PUBLIC .)
|
||||
add_library(world-build STATIC world-build.cpp)
|
||||
target_link_libraries(world-build PRIVATE GameData)
|
||||
target_include_directories(world-build PUBLIC .)
|
||||
|
||||
add_executable(test test.cpp)
|
||||
target_link_libraries(test PRIVATE action world-build lua GameData OgreMain)
|
||||
|
||||
add_executable(test2 test2.cpp)
|
||||
target_link_libraries(test2 PRIVATE action world-build lua GameData OgreMain)
|
||||
|
||||
add_executable(mark_harbors mark_harbors.cpp)
|
||||
target_link_libraries(mark_harbors PRIVATE lua OgreMain OgreRTShaderSystem)
|
||||
|
||||
add_custom_target(world ALL DEPENDS test test2)
|
||||
2
src/world/action.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
#include <goap/goap.hpp>
|
||||
#include "action.h"
|
||||
2
src/world/action.h
Normal file
@@ -0,0 +1,2 @@
|
||||
class CharacterAction {};
|
||||
class WorldAction {};
|
||||
320
src/world/aitoolkit/behtree.hpp
Normal file
@@ -0,0 +1,320 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
@defgroup behtree Behavior Tree
|
||||
|
||||
## Introduction
|
||||
|
||||
A behavior tree is a tree structure where each node represents a behavior. The
|
||||
tree is evaluated from the root node to the leaf nodes. The leaf nodes are
|
||||
either tasks or checks. A task is a node that performs an action and returns
|
||||
either success or failure. A check is a node that returns either success or
|
||||
failure based on some condition.
|
||||
|
||||
<center><pre class="mermaid">
|
||||
flowchart TD
|
||||
|
||||
root{{Selector}} --> seq1
|
||||
root --> seq2
|
||||
root --> seq3
|
||||
|
||||
seq1{{Sequence}} --> seq1a1
|
||||
seq1 --> seq1a2
|
||||
|
||||
seq2{{Sequence}} --> seq2a1
|
||||
seq2 --> seq2a2
|
||||
|
||||
seq3{{Sequence}} --> seq3a1
|
||||
seq3 --> seq3a2
|
||||
|
||||
seq1a1[Check enemy in attack range]
|
||||
seq1a2[[Destroy enemy]]
|
||||
|
||||
seq2a1[Check enemy in sight]
|
||||
seq2a2[[Move towards enemy]]
|
||||
|
||||
seq3a1[[Move towards waypoint]]
|
||||
seq3a2[[Select next waypoint]]
|
||||
|
||||
style root fill:darkred
|
||||
style seq1 fill:darkgreen
|
||||
style seq2 fill:darkgreen
|
||||
style seq3 fill:darkgreen
|
||||
</pre></center>
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
First, include the header file:
|
||||
|
||||
```cpp
|
||||
#include <aitoolkit/behtree.hpp>
|
||||
```
|
||||
|
||||
Then, create a blackboard class that will hold the state of the tree:
|
||||
|
||||
```cpp
|
||||
struct blackboard_type {
|
||||
glm::vec2 agent_position;
|
||||
glm::vec2 enemy_position;
|
||||
|
||||
float attack_range;
|
||||
float sight_range;
|
||||
|
||||
size_t current_waypoint;
|
||||
std::vector<glm::vec2> waypoints;
|
||||
};
|
||||
```
|
||||
|
||||
Next, create the tree:
|
||||
|
||||
```cpp
|
||||
using namespace aitoolkit::bt;
|
||||
|
||||
auto tree = sel<blackboard_type>(
|
||||
node_list<blackboard_type>(
|
||||
seq<blackboard_type>(
|
||||
node_list<blackboard_type>(
|
||||
check<blackboard_type>([](const blackboard_type& bb) {
|
||||
auto distance = glm::distance(bb.agent_position, bb.enemy_position);
|
||||
return distance <= bb.attack_range;
|
||||
}),
|
||||
task<blackboard_type>([](blackboard_type& bb) {
|
||||
// Destroy enemy
|
||||
return execution_state::success;
|
||||
})
|
||||
)
|
||||
),
|
||||
seq<blackboard_type>(
|
||||
node_list<blackboard_type>(
|
||||
check<blackboard_type>([](const blackboard_type& bb) {
|
||||
auto distance = glm::distance(bb.agent_position, bb.enemy_position);
|
||||
return distance <= bb.sight_range;
|
||||
}),
|
||||
task<blackboard_type>([](blackboard_type& bb) {
|
||||
// Move towards enemy
|
||||
return execution_state::success;
|
||||
})
|
||||
)
|
||||
),
|
||||
seq<blackboard_type>::make({
|
||||
task<blackboard_type>([](blackboard_type& bb) {
|
||||
// Move towards waypoint
|
||||
return execution_state::success;
|
||||
}),
|
||||
task<blackboard_type>([](blackboard_type& bb) {
|
||||
// Select next waypoint
|
||||
return execution_state::success;
|
||||
})
|
||||
})
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
Finally, evaluate the tree:
|
||||
|
||||
```cpp
|
||||
auto bb = blackboard_type{
|
||||
.agent_position = { 0.0f, 0.0f },
|
||||
.enemy_position = { 1.0f, 1.0f },
|
||||
.attack_range = 0.5f,
|
||||
.sight_range = 1.0f
|
||||
};
|
||||
|
||||
while (true) {
|
||||
auto state = tree.evaluate(bb);
|
||||
if (state == execution_state::success) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
*/
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <type_traits>
|
||||
#include <concepts>
|
||||
|
||||
namespace aitoolkit::bt {
|
||||
/**
|
||||
* @ingroup behtree
|
||||
* @enum execution_state
|
||||
* @brief Represent the state of a node
|
||||
*/
|
||||
enum class execution_state {
|
||||
success, /**< The node was successful */
|
||||
failure, /**< The node failed */
|
||||
running /**< The node is still running */
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup behtree
|
||||
* @class node
|
||||
* @brief Base abstract class for all nodes
|
||||
*/
|
||||
template <class T>
|
||||
class node {
|
||||
public:
|
||||
node() = default;
|
||||
node(const node&) = delete;
|
||||
node(node&& other) {
|
||||
m_children = std::move(other.m_children);
|
||||
}
|
||||
|
||||
virtual ~node() = default;
|
||||
|
||||
virtual execution_state evaluate(T& blackboard) const = 0;
|
||||
|
||||
protected:
|
||||
std::vector<std::unique_ptr<node<T>>> m_children;
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup behtree
|
||||
* @brief Heap-allocated pointer to node
|
||||
*/
|
||||
template <class T>
|
||||
using node_ptr = std::unique_ptr<node<T>>;
|
||||
|
||||
template <typename N, class T>
|
||||
concept node_trait = std::derived_from<N, node<T>>;
|
||||
|
||||
/**
|
||||
* @ingroup behtree
|
||||
* @brief Helper function to create a list of nodes
|
||||
*/
|
||||
template <typename T, node_trait<T> ...Children>
|
||||
std::vector<node_ptr<T>> node_list(Children&&... children) {
|
||||
auto nodes = std::vector<node_ptr<T>>{};
|
||||
nodes.reserve(sizeof...(children));
|
||||
(nodes.push_back(std::make_unique<Children>(std::move(children))), ...);
|
||||
return nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup behtree
|
||||
* @class seq
|
||||
* @brief Sequence node, will execute all children in order until one fails
|
||||
*/
|
||||
template <class T>
|
||||
class seq final : public node<T> {
|
||||
public:
|
||||
seq(std::vector<node_ptr<T>> children) {
|
||||
this->m_children = std::move(children);
|
||||
}
|
||||
|
||||
virtual execution_state evaluate(T& blackboard) const override {
|
||||
for (auto& child : this->m_children) {
|
||||
auto state = child->evaluate(blackboard);
|
||||
if (state != execution_state::success) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
return execution_state::success;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup behtree
|
||||
* @class sel
|
||||
* @brief Selector node, will execute all children in order until one succeeds
|
||||
*/
|
||||
template <class T>
|
||||
class sel final : public node<T> {
|
||||
public:
|
||||
sel(std::vector<node_ptr<T>> children) {
|
||||
this->m_children = std::move(children);
|
||||
}
|
||||
|
||||
virtual execution_state evaluate(T& blackboard) const override {
|
||||
for (auto& child : this->m_children) {
|
||||
auto state = child->evaluate(blackboard);
|
||||
if (state != execution_state::failure) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
return execution_state::failure;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup behtree
|
||||
* @class neg
|
||||
* @brief Negate node, will return the opposite of the child node
|
||||
*/
|
||||
template <class T>
|
||||
class neg final : public node<T> {
|
||||
public:
|
||||
template <node_trait<T> N>
|
||||
neg(N&& child) {
|
||||
this->m_children.reserve(1);
|
||||
this->m_children.push_back(std::make_unique<N>(std::move(child)));
|
||||
}
|
||||
|
||||
virtual execution_state evaluate(T& blackboard) const override {
|
||||
if (this->m_children.size() != 1) {
|
||||
return execution_state::failure;
|
||||
}
|
||||
|
||||
auto& child = this->m_children.front();
|
||||
auto state = child->evaluate(blackboard);
|
||||
if (state == execution_state::success) {
|
||||
return execution_state::failure;
|
||||
} else if (state == execution_state::failure) {
|
||||
return execution_state::success;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup behtree
|
||||
* @class check
|
||||
* @brief Check node, will return success if the callback returns true
|
||||
*/
|
||||
template <class T>
|
||||
class check final : public node<T> {
|
||||
public:
|
||||
using callback_type = std::function<bool(const T&)>;
|
||||
|
||||
public:
|
||||
check(callback_type fn) : m_fn(fn) {}
|
||||
|
||||
virtual execution_state evaluate(T& blackboard) const override {
|
||||
if (m_fn(blackboard)) {
|
||||
return execution_state::success;
|
||||
}
|
||||
|
||||
return execution_state::failure;
|
||||
}
|
||||
|
||||
private:
|
||||
callback_type m_fn;
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup behtree
|
||||
* @class task
|
||||
* @brief Task node, will execute the callback and return the result
|
||||
*/
|
||||
template <class T>
|
||||
class task final : public node<T> {
|
||||
public:
|
||||
using callback_type = std::function<execution_state(T&)>;
|
||||
|
||||
public:
|
||||
task(callback_type fn) : m_fn(fn) {}
|
||||
|
||||
virtual execution_state evaluate(T& blackboard) const override {
|
||||
return m_fn(blackboard);
|
||||
}
|
||||
|
||||
private:
|
||||
callback_type m_fn;
|
||||
};
|
||||
}
|
||||
318
src/world/aitoolkit/fsm.hpp
Normal file
@@ -0,0 +1,318 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
@defgroup fsm Finite State Machine
|
||||
|
||||
## Introduction
|
||||
|
||||
A finite state machine (FSM) is a mathematical model of computation. It is an
|
||||
abstract machine that can be in exactly one of a finite number of states at any
|
||||
given time. The FSM can change from one state to another in response to some
|
||||
external inputs; the change from one state to another is called a transition.
|
||||
|
||||
This library provides 2 types of FSMs: a simple machiine and a stack machine.
|
||||
The simple machine is the simplest form of FSM, it can only be in one state at a
|
||||
time. The stack machine is a more complex form of FSM, it can be in multiple
|
||||
states at a time.
|
||||
|
||||
## Usage
|
||||
|
||||
First, include the header:
|
||||
|
||||
```cpp
|
||||
#include <aitoolkit/fsm.hpp>
|
||||
```
|
||||
|
||||
Then, create a blackboard type:
|
||||
|
||||
```cpp
|
||||
struct blackboard_type {
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
Then, create a state type for each of your states:
|
||||
|
||||
```cpp
|
||||
class state_dummy final : public state<blackboard_type> {
|
||||
public:
|
||||
virtual void enter(blackboard_type& blackboard) override {
|
||||
// ...
|
||||
}
|
||||
|
||||
virtual void exit(blackboard_type& blackboard) override {
|
||||
// ...
|
||||
}
|
||||
|
||||
virtual void pause(blackboard_type& blackboard) override {
|
||||
// ...
|
||||
}
|
||||
|
||||
virtual void resume(blackboard_type& blackboard) override {
|
||||
// ...
|
||||
}
|
||||
|
||||
virtual void update(blackboard_type& blackboard) override {
|
||||
// ...
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Simple state machine
|
||||
|
||||
Create an instance of the FSM:
|
||||
|
||||
```cpp
|
||||
using namespace aitoolkit::fsm;
|
||||
|
||||
auto machine = simple_machine<blackboard_type>{};
|
||||
```
|
||||
|
||||
To transition to a new state, call `set_state()`:
|
||||
|
||||
```cpp
|
||||
machine.set_state(state_dummy{}, blackboard);
|
||||
```
|
||||
|
||||
> **NB:**
|
||||
>
|
||||
> - this will call the `exit` method of the current state (if any) and the
|
||||
> `enter` method of the new state
|
||||
> - if the machine is paused while transitioning to a new state, the new state
|
||||
> will be paused as well
|
||||
|
||||
To pause the machine, call `pause()`:
|
||||
|
||||
```cpp
|
||||
machine.pause(blackboard);
|
||||
```
|
||||
|
||||
> **NB:** This will call the `pause` method of the current state (if any).
|
||||
|
||||
To resume the machine, call `resume()`:
|
||||
|
||||
```cpp
|
||||
machine.resume(blackboard);
|
||||
```
|
||||
|
||||
> **NB:** This will call the `resume` method of the current state (if any).
|
||||
|
||||
To update the machine, call `update()`:
|
||||
|
||||
```cpp
|
||||
machine.update(blackboard);
|
||||
```
|
||||
|
||||
> **NB:**
|
||||
>
|
||||
> - this will call the `update` method of the current state (if any)
|
||||
> - if the machine is paused, calling `update()` will do nothing
|
||||
|
||||
To clear any state, call `clear_state()`:
|
||||
|
||||
```cpp
|
||||
machine.clear_state(blackboard);
|
||||
```
|
||||
|
||||
> **NB:** This will call the `exit` method of the current state (if any).
|
||||
|
||||
### Stack state machine
|
||||
|
||||
Create an instance of the FSM:
|
||||
|
||||
```cpp
|
||||
using namespace aitoolkit::fsm;
|
||||
|
||||
auto machine = stack_machine<blackboard_type>{};
|
||||
```
|
||||
|
||||
To push a new state onto the stack, call `push_state()`:
|
||||
|
||||
```cpp
|
||||
machine.push_state(state_dummy{}, blackboard);
|
||||
```
|
||||
|
||||
> **NB:** This will call the `pause` method of the current state (if any) and
|
||||
> the `enter` method of the new state.
|
||||
|
||||
To pop the top state off the stack, call `pop_state()`:
|
||||
|
||||
```cpp
|
||||
machine.pop_state(blackboard);
|
||||
```
|
||||
|
||||
> **NB:** This will call the `exit` method of the current state (if any) and
|
||||
> the `resume` method of the new top state (if any).
|
||||
|
||||
To update the machine, call `update()`:
|
||||
|
||||
```cpp
|
||||
machine.update(blackboard);
|
||||
```
|
||||
|
||||
> **NB:** This will call the `update` method of the top state (if any).
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <type_traits>
|
||||
#include <concepts>
|
||||
|
||||
namespace aitoolkit::fsm {
|
||||
/**
|
||||
* @ingroup fsm
|
||||
* @class state
|
||||
* @brief A state of the FSM.
|
||||
*/
|
||||
template <typename T>
|
||||
class state {
|
||||
public:
|
||||
virtual ~state() = default;
|
||||
|
||||
virtual void enter(T& blackboard) {};
|
||||
virtual void exit(T& blackboard) {};
|
||||
|
||||
virtual void pause(T& blackboard) {};
|
||||
virtual void resume(T& blackboard) {};
|
||||
|
||||
virtual void update(T& blackboard) {};
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup fsm
|
||||
* @brief Heap-allocated pointer to a state.
|
||||
*/
|
||||
template <typename T>
|
||||
using state_ptr = std::unique_ptr<state<T>>;
|
||||
|
||||
template <typename S, typename T>
|
||||
concept state_trait = std::derived_from<S, state<T>>;
|
||||
|
||||
/**
|
||||
* @ingroup fsm
|
||||
* @class simple_machine
|
||||
* @brief A simple FSM.
|
||||
*/
|
||||
template <typename T>
|
||||
class simple_machine {
|
||||
public:
|
||||
/**
|
||||
* @brief Enters in a new state, exiting the previous one (if any).
|
||||
*/
|
||||
template <state_trait<T> S>
|
||||
void set_state(S state, T& blackboard) {
|
||||
if (m_current_state) {
|
||||
m_current_state->exit(blackboard);
|
||||
}
|
||||
|
||||
m_current_state = std::make_unique<S>(state);
|
||||
m_current_state->enter(blackboard);
|
||||
|
||||
if (m_paused) {
|
||||
m_current_state->pause(blackboard);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear the current state.
|
||||
*/
|
||||
void clear_state(T& blackboard) {
|
||||
if (m_current_state) {
|
||||
m_current_state->exit(blackboard);
|
||||
m_current_state = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Pause the machine.
|
||||
*/
|
||||
void pause(T& blackboard) {
|
||||
m_paused = true;
|
||||
|
||||
if (m_current_state) {
|
||||
m_current_state->pause(blackboard);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resume the machine.
|
||||
*/
|
||||
void resume(T& blackboard) {
|
||||
m_paused = false;
|
||||
|
||||
if (m_current_state) {
|
||||
m_current_state->resume(blackboard);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the machine.
|
||||
*/
|
||||
void update(T& blackboard) {
|
||||
if (m_paused) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_current_state) {
|
||||
m_current_state->update(blackboard);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
state_ptr<T> m_current_state{nullptr};
|
||||
bool m_paused{false};
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup fsm
|
||||
* @class stack_machine
|
||||
* @brief A stack FSM.
|
||||
*/
|
||||
template <typename T>
|
||||
class stack_machine {
|
||||
public:
|
||||
/**
|
||||
* @brief Enters in a new state, pausing the previous one (if any).
|
||||
*/
|
||||
template <state_trait<T> S>
|
||||
void push_state(S state, T& blackboard) {
|
||||
if (!m_state_stack.empty()) {
|
||||
auto& current_state = m_state_stack.back();
|
||||
current_state->pause(blackboard);
|
||||
}
|
||||
|
||||
state.enter(blackboard);
|
||||
m_state_stack.push_back(std::make_unique<S>(state));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exits the current state, resuming the previous one (if any).
|
||||
*/
|
||||
void pop_state(T& blackboard) {
|
||||
if (!m_state_stack.empty()) {
|
||||
auto& current_state = m_state_stack.back();
|
||||
current_state->exit(blackboard);
|
||||
m_state_stack.pop_back();
|
||||
}
|
||||
|
||||
if (!m_state_stack.empty()) {
|
||||
auto& current_state = m_state_stack.back();
|
||||
current_state->resume(blackboard);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the machine.
|
||||
*/
|
||||
void update(T& blackboard) {
|
||||
if (!m_state_stack.empty()) {
|
||||
auto& current_state = m_state_stack.back();
|
||||
current_state->update(blackboard);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<state_ptr<T>> m_state_stack;
|
||||
};
|
||||
}
|
||||
383
src/world/aitoolkit/goap.hpp
Normal file
@@ -0,0 +1,383 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
@defgroup goap Goal Oriented Action Planning
|
||||
|
||||
## Introduction
|
||||
|
||||
Goal Oriented Action Planning (GOAP) is a planning algorithm that can be used
|
||||
to find a sequence of actions that will lead to a goal state. The algorithm
|
||||
works by searching through a graph of possible actions and their effects. The
|
||||
algorithm is guaranteed to find a solution if one exists, but it is not
|
||||
guaranteed to find the optimal solution.
|
||||
|
||||
<center><pre class="mermaid">
|
||||
graph LR
|
||||
|
||||
start[Start] --> action1
|
||||
action1[Get axe] --> action2
|
||||
action2[Chop tree] --> goal
|
||||
goal[Goal]
|
||||
|
||||
action3[Get pickaxe] --> action4
|
||||
action3 --> action5
|
||||
|
||||
action4[Mine gold]
|
||||
action5[Mine stone]
|
||||
|
||||
style start fill:darkred
|
||||
style goal fill:darkgreen
|
||||
</pre></center>
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
First, include the header file:
|
||||
|
||||
```cpp
|
||||
#include <aitoolkit/goap.hpp>
|
||||
```
|
||||
|
||||
Then, create a blackboard class that will hold the state of the planner:
|
||||
|
||||
```cpp
|
||||
struct blackboard_type {
|
||||
bool has_axe{false};
|
||||
bool has_pickaxe{false};
|
||||
int wood{0};
|
||||
int gold{0};
|
||||
int stone{0};
|
||||
};
|
||||
```
|
||||
|
||||
> **NB:** The blackboard needs to be comparable (`a == b`) and hashable.
|
||||
|
||||
Next, create a class for each action that you want to be able to perform:
|
||||
|
||||
```cpp
|
||||
using namespace aitoolkit::goap;
|
||||
|
||||
class get_axe final : public action<blackboard_type> {
|
||||
public:
|
||||
virtual float cost(const blackboard_type& blackboard) const override {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
virtual bool check_preconditions(const blackboard_type& blackboard) const override {
|
||||
return !blackboard.has_axe;
|
||||
}
|
||||
|
||||
virtual void apply_effects(blackboard_type& blackboard, bool dry_run) const override {
|
||||
blackboard.has_axe = true;
|
||||
}
|
||||
};
|
||||
|
||||
class get_pickaxe final : public action<blackboard_type> {
|
||||
public:
|
||||
virtual float cost(const blackboard_type& blackboard) const override {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
virtual bool check_preconditions(const blackboard_type& blackboard) const override {
|
||||
return !blackboard.has_pickaxe;
|
||||
}
|
||||
|
||||
virtual void apply_effects(blackboard_type& blackboard, bool dry_run) const override {
|
||||
blackboard.has_pickaxe = true;
|
||||
}
|
||||
};
|
||||
|
||||
class chop_tree final : public action<blackboard_type> {
|
||||
public:
|
||||
virtual float cost(const blackboard_type& blackboard) const override {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
virtual bool check_preconditions(const blackboard_type& blackboard) const override {
|
||||
return blackboard.has_axe;
|
||||
}
|
||||
|
||||
virtual void apply_effects(blackboard_type& blackboard, bool dry_run) const override {
|
||||
blackboard.wood += 1;
|
||||
}
|
||||
};
|
||||
|
||||
class mine_gold final : public action<blackboard_type> {
|
||||
public:
|
||||
virtual float cost(const blackboard_type& blackboard) const override {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
virtual bool check_preconditions(const blackboard_type& blackboard) const override {
|
||||
return blackboard.has_pickaxe;
|
||||
}
|
||||
|
||||
virtual void apply_effects(blackboard_type& blackboard, bool dry_run) const override {
|
||||
blackboard.gold += 1;
|
||||
}
|
||||
};
|
||||
|
||||
class mine_stone final : public action<blackboard_type> {
|
||||
public:
|
||||
virtual float cost(const blackboard_type& blackboard) const override {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
virtual bool check_preconditions(const blackboard_type& blackboard) const override {
|
||||
return blackboard.has_pickaxe;
|
||||
}
|
||||
|
||||
virtual void apply_effects(blackboard_type& blackboard, bool dry_run) const override {
|
||||
blackboard.stone += 1;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Finally, create a plan and run it:
|
||||
|
||||
```cpp
|
||||
auto initial = blackboard_type{};
|
||||
auto goal = blackboard_type{
|
||||
.has_axe = true,
|
||||
.has_pickaxe = true,
|
||||
.wood = 1,
|
||||
.gold = 1,
|
||||
.stone = 1
|
||||
};
|
||||
|
||||
auto p = planner<blackboard_type>(
|
||||
action_list<blackboard_type>(
|
||||
get_axe{},
|
||||
get_pickaxe{},
|
||||
chop_tree{},
|
||||
mine_gold{},
|
||||
mine_stone{}
|
||||
)
|
||||
initial,
|
||||
goal
|
||||
);
|
||||
|
||||
auto blackboard = initial;
|
||||
while (p) {
|
||||
p.run_next(blackboard); // will mutate the blackboard
|
||||
}
|
||||
```
|
||||
*/
|
||||
|
||||
#include <unordered_set>
|
||||
#include <functional>
|
||||
#include <coroutine>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <stack>
|
||||
|
||||
#include <type_traits>
|
||||
#include <concepts>
|
||||
|
||||
namespace aitoolkit::goap {
|
||||
template <typename T>
|
||||
concept blackboard_trait = requires(const T &a, const T &b) {
|
||||
{ a == b } -> std::convertible_to<bool>;
|
||||
{ std::hash<T>{}(a) } -> std::convertible_to<size_t>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup goap
|
||||
* @class action
|
||||
* @brief An action that can be performed on a blackboard.
|
||||
*
|
||||
* An action hae a cost, which is used during planning, if 2 actions have a
|
||||
* similar effect, the one with the lowest cost will be chosen.
|
||||
*
|
||||
* An action also has preconditions, which are used to determine if an action
|
||||
* can be performed on a blackboard. If the preconditions are not met, the
|
||||
* action will not be considered during planning.
|
||||
*
|
||||
* Finally, an action has effects, which are applied to the blackboard when
|
||||
* the action is performed.
|
||||
*/
|
||||
template <blackboard_trait T>
|
||||
class action {
|
||||
public:
|
||||
virtual ~action() = default;
|
||||
|
||||
/**
|
||||
* @brief The cost of performing this action.
|
||||
*/
|
||||
virtual float cost(const T& blackboard) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Check if the preconditions for this action are met.
|
||||
*/
|
||||
virtual bool check_preconditions(const T& blackboard) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Apply the effects of this action to the blackboard.
|
||||
*/
|
||||
virtual void apply_effects(T& blackboard, bool dry_run) const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Heeap allocated pointer to an action.
|
||||
*/
|
||||
template <blackboard_trait T>
|
||||
using action_ptr = std::unique_ptr<action<T>>;
|
||||
|
||||
template <typename A, typename T>
|
||||
concept action_trait = std::derived_from<A, action<T>>;
|
||||
|
||||
template <blackboard_trait T, action_trait<T>... Actions>
|
||||
std::vector<action_ptr<T>> action_list(Actions... actions) {
|
||||
auto action_list = std::vector<action_ptr<T>>{};
|
||||
action_list.reserve(sizeof...(Actions));
|
||||
(action_list.push_back(std::make_unique<Actions>(std::move(actions))), ...);
|
||||
return action_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup goap
|
||||
* @class plan
|
||||
* @brief A plan is a sequence of actions that will lead to a goal state.
|
||||
*/
|
||||
template <blackboard_trait T>
|
||||
class plan {
|
||||
public:
|
||||
plan() = default;
|
||||
|
||||
/**
|
||||
* @brief Get the number of actions in the plan.
|
||||
*/
|
||||
size_t size() const {
|
||||
return m_plan.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the plan is empty.
|
||||
*/
|
||||
operator bool() const {
|
||||
return !m_plan.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Execute the next planned action.
|
||||
*/
|
||||
void run_next(T& blackboard) {
|
||||
if (!m_plan.empty()) {
|
||||
auto action_idx = m_plan.top();
|
||||
m_plan.pop();
|
||||
|
||||
auto& action = m_actions[action_idx];
|
||||
action->apply_effects(blackboard, false);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::stack<size_t> m_plan;
|
||||
std::vector<action_ptr<T>> m_actions;
|
||||
|
||||
friend plan<T> planner<T>(
|
||||
std::vector<action_ptr<T>> actions,
|
||||
T initital_blackboard,
|
||||
T goal_blackboard,
|
||||
size_t max_iterations
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup goap
|
||||
* @brief Create a plan.
|
||||
*
|
||||
* The plan is created by providing a list of actions, an initial blackboard
|
||||
* state, and a goal blackboard state. The plan will then find a sequence of
|
||||
* actions that will lead to the goal state.
|
||||
*
|
||||
* @param actions The list of actions that can be performed.
|
||||
* @param initital_blackboard The initial state of the blackboard.
|
||||
* @param goal_blackboard The goal state of the blackboard.
|
||||
* @param max_iterations The maximum number of iterations to perform
|
||||
* before giving up. If 0, the plan will run until it finds a solution.
|
||||
* @return A plan that can be executed.
|
||||
*/
|
||||
template <blackboard_trait T>
|
||||
plan<T> planner(
|
||||
std::vector<action_ptr<T>> actions,
|
||||
T initital_blackboard,
|
||||
T goal_blackboard,
|
||||
size_t max_iterations = 0
|
||||
) {
|
||||
struct node_type {
|
||||
T blackboard;
|
||||
float cost;
|
||||
|
||||
std::optional<size_t> action_taken_idx;
|
||||
std::shared_ptr<node_type> parent;
|
||||
};
|
||||
|
||||
using node_ptr = std::shared_ptr<node_type>;
|
||||
|
||||
struct node_compare {
|
||||
bool operator()(const node_ptr& a, const node_ptr& b) const {
|
||||
return a->cost > b->cost;
|
||||
}
|
||||
};
|
||||
|
||||
std::priority_queue<node_ptr, std::vector<node_ptr>, node_compare> open_set;
|
||||
std::unordered_set<T> closed_set;
|
||||
|
||||
open_set.push(std::make_shared<node_type>(node_type{
|
||||
.blackboard = initital_blackboard,
|
||||
.cost = 0.0f,
|
||||
.action_taken_idx = std::nullopt,
|
||||
.parent = nullptr
|
||||
}));
|
||||
|
||||
for (
|
||||
size_t iteration = 0;
|
||||
!open_set.empty() && (max_iterations == 0 || iteration < max_iterations);
|
||||
++iteration
|
||||
) {
|
||||
auto current_node = open_set.top();
|
||||
open_set.pop();
|
||||
|
||||
if (current_node->blackboard == goal_blackboard) {
|
||||
auto p = plan<T>();
|
||||
p.m_actions = std::move(actions);
|
||||
|
||||
while (current_node->parent != nullptr) {
|
||||
auto action_idx = current_node->action_taken_idx.value();
|
||||
p.m_plan.push(action_idx);
|
||||
current_node = current_node->parent;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
if (!closed_set.contains(current_node->blackboard)) {
|
||||
closed_set.insert(current_node->blackboard);
|
||||
|
||||
for (size_t action_idx = 0; action_idx < actions.size(); action_idx++) {
|
||||
auto& action = actions[action_idx];
|
||||
|
||||
if (action->check_preconditions(current_node->blackboard)) {
|
||||
auto next_blackboard = current_node->blackboard;
|
||||
action->apply_effects(next_blackboard, true);
|
||||
auto next_cost = current_node->cost + action->cost(current_node->blackboard);
|
||||
|
||||
if (!closed_set.contains(next_blackboard)) {
|
||||
open_set.push(std::make_shared<node_type>(node_type{
|
||||
.blackboard = next_blackboard,
|
||||
.cost = next_cost,
|
||||
.action_taken_idx = action_idx,
|
||||
.parent = current_node
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return plan<T>();
|
||||
}
|
||||
}
|
||||
203
src/world/aitoolkit/utility.hpp
Normal file
@@ -0,0 +1,203 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
@defgroup utility Utility AI
|
||||
|
||||
## Introduction
|
||||
|
||||
Utility AI is a planning algorithm that can be used to find the best action to
|
||||
perform in a given situation. The algorithm works by assigning a score to each
|
||||
action based on how well it will achieve the goal. The algorithm is guaranteed
|
||||
to find a solution.
|
||||
|
||||
<center><pre class="mermaid">
|
||||
flowchart TD
|
||||
|
||||
a1[Collect food\nscore: +50]
|
||||
a2[Collect wood\nscore: +150]
|
||||
a3[Collect stone\nscore: -10]
|
||||
a4[Collect gold\nscore: +75]
|
||||
|
||||
style a1 color:darkred
|
||||
style a2 color:darkgreen
|
||||
style a3 color:darkred
|
||||
style a4 color:darkred
|
||||
</pre></center>
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
First, include the header file:
|
||||
|
||||
```cpp
|
||||
#include <aitoolkit/utility.hpp>
|
||||
```
|
||||
|
||||
Then, create a blackboard type:
|
||||
|
||||
```cpp
|
||||
struct blackboard_type {
|
||||
int food{0};
|
||||
int wood{0};
|
||||
int stone{0};
|
||||
int gold{0};
|
||||
};
|
||||
```
|
||||
|
||||
Next, create a class for each action that you want to be able to perform:
|
||||
|
||||
```cpp
|
||||
using namespace aitoolkit::utility;
|
||||
|
||||
class collect_food final : public action<blackboard_type> {
|
||||
public:
|
||||
virtual float score(const blackboard_type& blackboard) const override {
|
||||
return 50.0f;
|
||||
}
|
||||
|
||||
virtual void apply(blackboard_type& blackboard) const override {
|
||||
blackboard.food += 1;
|
||||
}
|
||||
};
|
||||
|
||||
class collect_wood final : public action<blackboard_type> {
|
||||
public:
|
||||
virtual float score(const blackboard_type& blackboard) const override {
|
||||
return 150.0f;
|
||||
}
|
||||
|
||||
virtual void apply(blackboard_type& blackboard) const override {
|
||||
blackboard.wood += 1;
|
||||
}
|
||||
};
|
||||
|
||||
class collect_stone final : public action<blackboard_type> {
|
||||
public:
|
||||
virtual float score(const blackboard_type& blackboard) const override {
|
||||
return -10.0f;
|
||||
}
|
||||
|
||||
virtual void apply(blackboard_type& blackboard) const override {
|
||||
blackboard.stone += 1;
|
||||
}
|
||||
};
|
||||
|
||||
class collect_gold final : public action<blackboard_type> {
|
||||
public:
|
||||
virtual float score(const blackboard_type& blackboard) const override {
|
||||
return 75.0f;
|
||||
}
|
||||
|
||||
virtual void apply(blackboard_type& blackboard) const override {
|
||||
blackboard.gold += 1;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Finally, create an evaluator and run it:
|
||||
|
||||
```cpp
|
||||
auto evaluator = evaluator<blackboard_type>(
|
||||
action_list<blackboard_type>(
|
||||
collect_food{},
|
||||
collect_wood{},
|
||||
collect_stone{},
|
||||
collect_gol{}
|
||||
)
|
||||
};
|
||||
|
||||
auto blackboard = blackboard_type{};
|
||||
evaluator.run(blackboard);
|
||||
```
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <limits>
|
||||
|
||||
#include <type_traits>
|
||||
#include <concepts>
|
||||
|
||||
namespace aitoolkit::utility {
|
||||
/**
|
||||
* @ingroup utility
|
||||
* @class action
|
||||
* @brief Base abstract class for all actions
|
||||
*/
|
||||
template <typename T>
|
||||
class action {
|
||||
public:
|
||||
virtual ~action() = default;
|
||||
|
||||
/**
|
||||
* @brief Return the score of the action
|
||||
*/
|
||||
virtual float score(const T& blackboard) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Apply the action to the blackboard
|
||||
*/
|
||||
virtual void apply(T& blackboard) const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup utility
|
||||
* @brief Heap allocated pointer to an action.
|
||||
*/
|
||||
template <typename T>
|
||||
using action_ptr = std::unique_ptr<action<T>>;
|
||||
|
||||
template <typename A, typename T>
|
||||
concept action_trait = std::derived_from<A, action<T>>;
|
||||
|
||||
/**
|
||||
* @ingroup utility
|
||||
* @brief Helper function to create a list of actions
|
||||
*/
|
||||
template <typename T, action_trait<T> ...Actions>
|
||||
std::vector<action_ptr<T>> action_list(Actions&&... actions) {
|
||||
auto actions_list = std::vector<action_ptr<T>>{};
|
||||
actions_list.reserve(sizeof...(Actions));
|
||||
(actions_list.push_back(std::make_unique<Actions>(std::move(actions))), ...);
|
||||
return actions_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup utility
|
||||
* @class evaluator
|
||||
* @brief Evaluate a set of actions and apply the best one.
|
||||
*/
|
||||
template <typename T>
|
||||
class evaluator {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct an evaluator from a list of actions
|
||||
*/
|
||||
evaluator(std::vector<action_ptr<T>> actions) : m_actions(std::move(actions)) {}
|
||||
|
||||
/**
|
||||
* @brief Find the best action and apply it to the blackboard
|
||||
*/
|
||||
void run(T& blackboard) const {
|
||||
if (m_actions.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto best_score = std::numeric_limits<float>::min();
|
||||
auto best_action = m_actions.front().get();
|
||||
|
||||
for (auto& action : m_actions) {
|
||||
auto score = action->score(blackboard);
|
||||
if (score > best_score) {
|
||||
best_score = score;
|
||||
best_action = action.get();
|
||||
}
|
||||
}
|
||||
|
||||
best_action->apply(blackboard);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<action_ptr<T>> m_actions;
|
||||
};
|
||||
}
|
||||
21
src/world/goap/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Club Vaudois de Robotique Autonome
|
||||
|
||||
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.
|
||||
193
src/world/goap/goap.hpp
Normal file
@@ -0,0 +1,193 @@
|
||||
/** Action planner for basic AI purposes
|
||||
*
|
||||
* This file implements the technique presented in "Three States and a Plan:
|
||||
* The A.I. of F.E.A.R." by Jeff Orkin.
|
||||
*/
|
||||
#ifndef GOAP_HPP
|
||||
#define GOAP_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <goap/goap_internals.hpp>
|
||||
#include <cstring>
|
||||
|
||||
namespace goap {
|
||||
|
||||
const int kErrorNoPathFound = -1;
|
||||
const int kErrorNotEnoughMemory = -2;
|
||||
|
||||
template <typename State>
|
||||
class Action {
|
||||
public:
|
||||
/** Checks if the given state allows this action to proceed. */
|
||||
virtual bool can_run(const State& state) = 0;
|
||||
|
||||
/** Plans the effects of this action on the state */
|
||||
virtual void plan_effects(State& state) = 0;
|
||||
|
||||
/** Tries to execute the task and returns true if it suceeded. */
|
||||
virtual bool execute(State& state) = 0;
|
||||
|
||||
virtual ~Action() = default;
|
||||
};
|
||||
|
||||
template <typename State>
|
||||
class Goal {
|
||||
public:
|
||||
/** Checks if the goal is reached for the given state. */
|
||||
virtual bool is_reached(const State& state) const
|
||||
{
|
||||
return distance_to(state) == 0;
|
||||
}
|
||||
|
||||
/** Computes the distance from state to goal. */
|
||||
virtual int distance_to(const State& state) const = 0;
|
||||
|
||||
virtual ~Goal() = default;
|
||||
};
|
||||
|
||||
template <typename State, int N = 100>
|
||||
class Planner {
|
||||
VisitedState<State> nodes[N];
|
||||
|
||||
public:
|
||||
/** Finds a plan from state to goal and returns its length.
|
||||
*
|
||||
* If path is given, then the found path is stored there.
|
||||
*/
|
||||
int plan(const State& state, Goal<State>& goal, Action<State>* actions[], unsigned action_count, Action<State>** path = nullptr, int path_len = 10)
|
||||
{
|
||||
visited_states_array_to_list(nodes, N);
|
||||
|
||||
auto free_nodes = &nodes[0];
|
||||
|
||||
auto open = list_pop_head(free_nodes);
|
||||
VisitedState<State>* close = nullptr;
|
||||
|
||||
open->state = state;
|
||||
open->cost = 0;
|
||||
open->priority = 0;
|
||||
open->parent = nullptr;
|
||||
open->action = nullptr;
|
||||
|
||||
while (open) {
|
||||
auto current = priority_list_pop(open);
|
||||
list_push_head(close, current);
|
||||
|
||||
if (goal.is_reached(current->state)) {
|
||||
auto len = 0;
|
||||
for (auto p = current->parent; p; p = p->parent) {
|
||||
len++;
|
||||
}
|
||||
|
||||
if (len > path_len) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (path) {
|
||||
auto i = len - 1;
|
||||
for (auto p = current; p->parent; p = p->parent, i--) {
|
||||
path[i] = p->action;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
for (auto i = 0u; i < action_count; i++) {
|
||||
auto action = actions[i];
|
||||
|
||||
if (action->can_run(current->state)) {
|
||||
// Cannot allocate a new node, abort
|
||||
if (free_nodes == nullptr) {
|
||||
// Garbage collect the node that is most unlikely to be
|
||||
// visited (i.e. lowest priority)
|
||||
VisitedState<State>*gc, *gc_prev = nullptr;
|
||||
for (gc = open; gc && gc->next; gc = gc->next) {
|
||||
gc_prev = gc;
|
||||
}
|
||||
|
||||
if (!gc) {
|
||||
return kErrorNotEnoughMemory;
|
||||
}
|
||||
|
||||
if (gc_prev) {
|
||||
gc_prev->next = nullptr;
|
||||
}
|
||||
|
||||
free_nodes = gc;
|
||||
}
|
||||
|
||||
auto neighbor = list_pop_head(free_nodes);
|
||||
neighbor->state = current->state;
|
||||
action->plan_effects(neighbor->state);
|
||||
neighbor->cost = current->cost + 1;
|
||||
neighbor->priority = current->priority + 1 + goal.distance_to(neighbor->state);
|
||||
neighbor->parent = current;
|
||||
neighbor->action = action;
|
||||
|
||||
bool should_insert = true;
|
||||
|
||||
// Check if the node is already in the list of nodes
|
||||
// scheduled to be visited
|
||||
for (auto p = open; p; p = p->next) {
|
||||
if (p->state == neighbor->state) {
|
||||
should_insert = false;
|
||||
update_queued_state(p, neighbor);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the state is in the list of already visited
|
||||
// state
|
||||
for (auto p = close; p; p = p->next) {
|
||||
if (p->state == neighbor->state) {
|
||||
should_insert = false;
|
||||
update_queued_state(p, neighbor);
|
||||
}
|
||||
}
|
||||
|
||||
if (should_insert) {
|
||||
list_push_head(open, neighbor);
|
||||
} else {
|
||||
list_push_head(free_nodes, neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reaching here means we did not find a path
|
||||
return kErrorNoPathFound;
|
||||
}
|
||||
};
|
||||
|
||||
// Distance class, used to build distance metrics that read easily
|
||||
class Distance {
|
||||
int distance;
|
||||
|
||||
public:
|
||||
Distance shouldBeTrue(bool var)
|
||||
{
|
||||
distance += var ? 0 : 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Distance shouldBeFalse(bool var)
|
||||
{
|
||||
distance += var ? 1 : 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Distance shouldBeEqual(int target, int var)
|
||||
{
|
||||
distance += abs(var - target);
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator int()
|
||||
{
|
||||
return distance;
|
||||
}
|
||||
};
|
||||
}; // namespace goap
|
||||
|
||||
#endif
|
||||