Compare commits
15 Commits
9bb9e2c09b
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| c860152f9b | |||
| da4c1fee0e | |||
| 4cf0ea5321 | |||
| 8320b14358 | |||
| 1d358d206e | |||
| cd91174f5d | |||
| 4b24d85123 | |||
| f86e7fd96c | |||
| 272e202774 | |||
| 80fba23cd2 | |||
| c031056e17 | |||
| 49fc547295 | |||
| d6d61229f8 | |||
| 5bb529bc31 | |||
| d3c93c5c18 |
@@ -77,6 +77,7 @@ add_subdirectory(src/tests)
|
||||
add_subdirectory(src/physics)
|
||||
add_subdirectory(src/editor)
|
||||
add_subdirectory(assets/blender/buildings/parts)
|
||||
add_subdirectory(resources)
|
||||
|
||||
add_executable(Game Game.cpp ${WATER_SRC})
|
||||
target_include_directories(Game PRIVATE src/gamedata)
|
||||
@@ -203,34 +204,8 @@ add_custom_target(stage_stories ALL DEPENDS stage_lua_scripts ${CMAKE_BINARY_DIR
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_BINARY_DIR}/resources.cfg
|
||||
COMMAND cp ${CMAKE_SOURCE_DIR}/resources.cfg ${CMAKE_BINARY_DIR}/resources.cfg
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/resources/main
|
||||
${CMAKE_BINARY_DIR}/resources/main
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/resources/shaderlib
|
||||
${CMAKE_BINARY_DIR}/resources/shaderlib
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/resources/terrain
|
||||
${CMAKE_BINARY_DIR}/resources/terrain
|
||||
# COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/lua-scripts
|
||||
# ${CMAKE_BINARY_DIR}/lua-scripts
|
||||
# COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/skybox
|
||||
# ${CMAKE_BINARY_DIR}/skybox
|
||||
# COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/resources/debug
|
||||
# ${CMAKE_BINARY_DIR}/resources/debug
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/resources.cfg ${CMAKE_BINARY_DIR}/resources.cfg
|
||||
DEPENDS ${CMAKE_SOURCE_DIR}/resources.cfg)
|
||||
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/resources/terrain/world_map.png
|
||||
COMMAND unzip -o ${CMAKE_SOURCE_DIR}/world_map.kra mergedimage.png -d ${CMAKE_BINARY_DIR}/world_map
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_BINARY_DIR}/world_map/mergedimage.png
|
||||
${CMAKE_BINARY_DIR}/resources/terrain/world_map.png
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/world_map
|
||||
DEPENDS ${CMAKE_SOURCE_DIR}/world_map.kra)
|
||||
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/resources/terrain/brushes.png
|
||||
COMMAND unzip -o ${CMAKE_SOURCE_DIR}/brushes.kra mergedimage.png -d ${CMAKE_BINARY_DIR}/brushes
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_BINARY_DIR}/brushes/mergedimage.png
|
||||
${CMAKE_BINARY_DIR}/resources/terrain/brushes.png
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/brushes
|
||||
DEPENDS ${CMAKE_SOURCE_DIR}/brushes.kra)
|
||||
|
||||
set(SKYBOX_SRC
|
||||
early_morning_bk.jpg
|
||||
@@ -356,29 +331,11 @@ add_custom_command(
|
||||
COMMAND ${CMAKE_COMMAND} -E touch ${CHARACTER_GLBS}
|
||||
DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_models.py ${VRM_IMPORTED_BLENDS} ${EDITED_BLEND_TARGETS}
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
#add_custom_command(
|
||||
# OUTPUT ${CMAKE_SOURCE_DIR}/characters/female/vroid-normal-female.scene
|
||||
# ${CMAKE_SOURCE_DIR}/characters/male/vroid-normal-male.scene
|
||||
# ${CMAKE_SOURCE_DIR}/assets/blender/vrm-vroid-normal-female.blend
|
||||
# ${CMAKE_SOURCE_DIR}/assets/blender/vrm-vroid-normal-male.blend
|
||||
# ${CMAKE_SOURCE_DIR}/assets/blender/shapes/male/vrm-vroid-normal-male-chibi.blend
|
||||
# COMMAND mkdir -p ${CREATE_DIRECTORIES}
|
||||
# COMMAND ${BLENDER} -b -Y -P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/import_vrm.py
|
||||
# COMMAND ${BLENDER} -b -Y -P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_models.py
|
||||
## COMMAND ${BLENDER} -b -Y -P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_ogre_scene.py
|
||||
# COMMAND echo rm -Rf ${CMAKE_SOURCE_DIR}/characters/male/*.material ${CMAKE_SOURCE_DIR}/characters/female/*.material
|
||||
# COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/characters
|
||||
# ${CMAKE_BINARY_DIR}/characters
|
||||
# DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_models.py ${CMAKE_SOURCE_DIR}/assets/blender/scripts/import_vrm.py
|
||||
# ${CMAKE_SOURCE_DIR}/assets/vroid/buch1.vrm
|
||||
# ${CMAKE_SOURCE_DIR}/assets/vroid/buch1-chibi.vrm
|
||||
# ${CMAKE_SOURCE_DIR}/assets/vroid/jane2-dress.vrm
|
||||
# WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
|
||||
add_custom_target(stage_files ALL DEPENDS ${CMAKE_BINARY_DIR}/resources.cfg ${MATERIALS_OUTPUT}
|
||||
${CMAKE_BINARY_DIR}/resources/terrain/world_map.png
|
||||
${CMAKE_BINARY_DIR}/resources/terrain/brushes.png
|
||||
edited-blends)
|
||||
edited-blends stage_resources)
|
||||
|
||||
add_custom_target(remove_scenes COMMAND rm -f ${VRM_SOURCE} ${VRM_IMPORTED_BLENDS} ${CHARACTER_GLBS})
|
||||
|
||||
|
||||
118
Game.cpp
118
Game.cpp
@@ -15,7 +15,7 @@
|
||||
#include "Components.h"
|
||||
#include "CharacterModule.h"
|
||||
#include "TerrainModule.h"
|
||||
#include "GUIModule.h"
|
||||
#include "GUIModuleCommon.h"
|
||||
#include "AppModule.h"
|
||||
#include "sound.h"
|
||||
class App;
|
||||
@@ -236,15 +236,14 @@ public:
|
||||
else if (key == 'f')
|
||||
control |= 64;
|
||||
if (key == 'w' || key == 'a' || key == 's' || key == 'd' ||
|
||||
key == 'e' || key == OgreBites::SDLK_LSHIFT)
|
||||
key == 'e' || key == OgreBites::SDLK_LSHIFT || key == 'e' ||
|
||||
key == 'f')
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
bool keyReleased(const OgreBites::KeyboardEvent &evt) override
|
||||
{
|
||||
OgreBites::Keycode key = evt.keysym.sym;
|
||||
if (isGuiEnabled())
|
||||
return false;
|
||||
if (key == 'w')
|
||||
control &= ~1;
|
||||
else if (key == 'a')
|
||||
@@ -255,8 +254,14 @@ public:
|
||||
control &= ~8;
|
||||
else if (key == OgreBites::SDLK_LSHIFT)
|
||||
control &= ~16;
|
||||
if (key == 'w' || key == 'a' || key == 's' || key == 'd' ||
|
||||
key == OgreBites::SDLK_LSHIFT)
|
||||
else if (key == 'e')
|
||||
control &= ~32;
|
||||
else if (key == 'f')
|
||||
control &= ~64;
|
||||
if (isGuiEnabled())
|
||||
return false;
|
||||
if (key == 'w' || key == 'a' || key == 's' || key == 'd' ||
|
||||
key == OgreBites::SDLK_LSHIFT || key == 'e' || key == 'f')
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@@ -295,9 +300,9 @@ public:
|
||||
};
|
||||
class App : public OgreBites::ApplicationContext {
|
||||
Ogre::SceneNode *mCameraNode, *mCameraPivot, *mCameraGoal;
|
||||
Ogre::Camera *mCamera;
|
||||
Ogre::Camera *mCamera, *mCameraInterior, *mCameraInventory;
|
||||
Ogre::Real mPivotPitch;
|
||||
Ogre::SceneManager *mScnMgr;
|
||||
Ogre::SceneManager *mScnMgr, *mScnMgrInterior, *mScnMgrInventory;
|
||||
Ogre::Viewport *mViewport;
|
||||
SkyBoxRenderer *sky;
|
||||
bool mGrab;
|
||||
@@ -313,15 +318,19 @@ public:
|
||||
virtual ~App()
|
||||
{
|
||||
}
|
||||
void setup()
|
||||
void setup() override
|
||||
{
|
||||
OgreBites::ApplicationContext::setup();
|
||||
Ogre::Root *root = getRoot();
|
||||
Ogre::SceneManager *scnMgr = root->createSceneManager();
|
||||
mScnMgr = scnMgr;
|
||||
Ogre::OverlaySystem *pOverlaySystem = getOverlaySystem();
|
||||
mScnMgrInterior = root->createSceneManager();
|
||||
mScnMgrInventory = root->createSceneManager();
|
||||
Ogre::OverlaySystem *pOverlaySystem = getOverlaySystem();
|
||||
mScnMgr->addRenderQueueListener(pOverlaySystem);
|
||||
// mTrayMgr = new OgreBites::TrayManager("AppTrays",
|
||||
mScnMgrInterior->addRenderQueueListener(pOverlaySystem);
|
||||
mScnMgrInventory->addRenderQueueListener(pOverlaySystem);
|
||||
// mTrayMgr = new OgreBites::TrayManager("AppTrays",
|
||||
// getRenderWindow());
|
||||
}
|
||||
bool isWindowGrab()
|
||||
@@ -347,7 +356,32 @@ public:
|
||||
mGrab = grab;
|
||||
ApplicationContextBase::setWindowGrab(grab);
|
||||
}
|
||||
|
||||
void initInteriorCamera()
|
||||
{
|
||||
Ogre::SceneNode *cameraNode =
|
||||
mScnMgrInterior->getRootSceneNode()
|
||||
->createChildSceneNode("InteriorCameraNode");
|
||||
cameraNode->setPosition(0, 2, 3);
|
||||
cameraNode->lookAt(Ogre::Vector3(0, 1, -1),
|
||||
Ogre::Node::TS_PARENT);
|
||||
mCameraInterior = mScnMgr->createCamera("interior_camera");
|
||||
mCameraInterior->setNearClipDistance(0.05f);
|
||||
mCameraInterior->setAutoAspectRatio(true);
|
||||
cameraNode->attachObject(mCameraInterior);
|
||||
}
|
||||
void initInventoryCamera()
|
||||
{
|
||||
Ogre::SceneNode *cameraNode =
|
||||
mScnMgrInventory->getRootSceneNode()
|
||||
->createChildSceneNode("InteriorCameraNode");
|
||||
cameraNode->setPosition(0, 2, 3);
|
||||
cameraNode->lookAt(Ogre::Vector3(0, 1, -1),
|
||||
Ogre::Node::TS_PARENT);
|
||||
mCameraInventory = mScnMgr->createCamera("inventory_camera");
|
||||
mCameraInventory->setNearClipDistance(0.05f);
|
||||
mCameraInventory->setAutoAspectRatio(true);
|
||||
cameraNode->attachObject(mCameraInventory);
|
||||
}
|
||||
void initCamera()
|
||||
{
|
||||
mCameraNode = mScnMgr->getRootSceneNode()->createChildSceneNode(
|
||||
@@ -378,12 +412,16 @@ public:
|
||||
mCamera->setNearClipDistance(0.1f);
|
||||
mCamera->setFarClipDistance(800);
|
||||
mPivotPitch = 0;
|
||||
initInteriorCamera();
|
||||
initInventoryCamera();
|
||||
}
|
||||
void configure()
|
||||
{
|
||||
std::cout << "Startup" << "\n";
|
||||
std::cout << "Startup"
|
||||
<< "\n";
|
||||
initApp();
|
||||
std::cout << "Set up RTSS" << "\n";
|
||||
std::cout << "Set up RTSS"
|
||||
<< "\n";
|
||||
Ogre::Root *root = getRoot();
|
||||
Ogre::SceneManager *scnMgr = getSceneManager();
|
||||
|
||||
@@ -392,24 +430,32 @@ public:
|
||||
Ogre::RTShader::ShaderGenerator::getSingletonPtr();
|
||||
shadergen->addSceneManager(scnMgr);
|
||||
setWindowGrab(true);
|
||||
std::cout << "Init camera" << "\n";
|
||||
std::cout << "Init camera"
|
||||
<< "\n";
|
||||
initCamera();
|
||||
std::cout << "Set up water" << "\n";
|
||||
std::cout << "Set up cursor" << "\n";
|
||||
std::cout << "Set up water"
|
||||
<< "\n";
|
||||
std::cout << "Set up cursor"
|
||||
<< "\n";
|
||||
Ogre::ResourceGroupManager::getSingleton()
|
||||
.initialiseAllResourceGroups();
|
||||
// OgreBites::ApplicationContext::loadResources();
|
||||
// setupCursor();
|
||||
std::cout << "Setup input" << "\n";
|
||||
std::cout << "Setup input"
|
||||
<< "\n";
|
||||
setupInput();
|
||||
std::cout << "Create content" << "\n";
|
||||
std::cout << "Create content"
|
||||
<< "\n";
|
||||
createContent();
|
||||
std::cout << "Setup done" << "\n";
|
||||
std::cout << "Setup done"
|
||||
<< "\n";
|
||||
#if 0
|
||||
mDbgDraw->setDebugMode(mDbgDraw->getDebugMode() |
|
||||
btIDebugDraw::DBG_DrawContactPoints);
|
||||
#endif
|
||||
}
|
||||
Ogre::LogManager::getSingleton().setMinLogLevel(
|
||||
Ogre::LML_CRITICAL);
|
||||
}
|
||||
Ogre::SceneManager *getSceneManager()
|
||||
{
|
||||
return mScnMgr;
|
||||
@@ -435,14 +481,19 @@ public:
|
||||
mDynWorld->getBtWorld()->stepSimulation(delta, 3);
|
||||
#endif
|
||||
if (!ECS::get().has<ECS::GUI>())
|
||||
return;
|
||||
/* Update window grab */
|
||||
ECS::GUI &gui = ECS::get().get_mut<ECS::GUI>();
|
||||
if (gui.grabChanged) {
|
||||
setWindowGrab(gui.grab);
|
||||
gui.grabChanged = false;
|
||||
ECS::get().modified<ECS::GUI>();
|
||||
}
|
||||
goto end;
|
||||
{
|
||||
/* Update window grab */
|
||||
ECS::GUI &gui = ECS::get().get_mut<ECS::GUI>();
|
||||
if (gui.grabChanged) {
|
||||
setWindowGrab(gui.grab);
|
||||
gui.grabChanged = false;
|
||||
ECS::get().modified<ECS::GUI>();
|
||||
std::cout << "updateWorld " << gui.grabChanged
|
||||
<< " " << gui.grab << std::endl;
|
||||
}
|
||||
}
|
||||
end:
|
||||
ECS::update(delta);
|
||||
|
||||
#if 0
|
||||
@@ -601,8 +652,9 @@ public:
|
||||
"Skybox/Dynamic", "General");
|
||||
OgreAssert(m, "Sky box material not found.");
|
||||
m->load();
|
||||
ECS::setup(mScnMgr, /*mDynWorld.get(), */ mCameraNode, mCamera,
|
||||
getRenderWindow());
|
||||
ECS::setupExteriorScene(mScnMgr,
|
||||
/*mDynWorld.get(), */ mCameraNode,
|
||||
mCamera, getRenderWindow());
|
||||
ECS::get().set<ECS::RenderWindow>(
|
||||
{ getRenderWindow(), getDisplayDPI() });
|
||||
ECS::get()
|
||||
@@ -736,9 +788,7 @@ public:
|
||||
}
|
||||
flecs::entity getPlayer() const
|
||||
{
|
||||
flecs::entity player =
|
||||
ECS::get().lookup("ECS::CharacterModule::player");
|
||||
return player;
|
||||
return ECS::player;
|
||||
}
|
||||
void enableDbgDraw(bool enable)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
project(building-parts)
|
||||
set(PARTS_FILES pier.blend)
|
||||
set(FURNITURE_FILES furniture.blend furniture-sofa.blend)
|
||||
set(PARTS_OUTPUT_DIRS)
|
||||
foreach(PARTS_FILE ${PARTS_FILES})
|
||||
get_filename_component(FILE_NAME ${PARTS_FILE} NAME_WE)
|
||||
@@ -14,4 +15,17 @@ foreach(PARTS_FILE ${PARTS_FILES})
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${PARTS_FILE} ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_building_parts.py)
|
||||
list(APPEND PARTS_OUTPUT_DIRS ${PARTS_OUTPUT_DIR})
|
||||
endforeach()
|
||||
foreach(FURNITURE_FILE ${FURNITURE_FILES})
|
||||
get_filename_component(FILE_NAME ${FURNITURE_FILE} NAME_WE)
|
||||
set(PARTS_OUTPUT_DIR ${CMAKE_BINARY_DIR}/resources/buildings/parts/${FILE_NAME})
|
||||
add_custom_command(
|
||||
OUTPUT ${PARTS_OUTPUT_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${PARTS_OUTPUT_DIR}
|
||||
COMMAND ${BLENDER} ${CMAKE_CURRENT_SOURCE_DIR}/${FURNITURE_FILE}
|
||||
-b -Y -P
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/export_furniture_parts.py
|
||||
-- ${PARTS_OUTPUT_DIR}
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${PARTS_FILE} ${CMAKE_CURRENT_SOURCE_DIR}/export_furniture_parts.py)
|
||||
list(APPEND PARTS_OUTPUT_DIRS ${PARTS_OUTPUT_DIR})
|
||||
endforeach()
|
||||
add_custom_target(import_building_parts ALL DEPENDS ${PARTS_OUTPUT_DIRS})
|
||||
|
||||
182
assets/blender/buildings/parts/export_furniture_parts.py
Normal file
182
assets/blender/buildings/parts/export_furniture_parts.py
Normal file
@@ -0,0 +1,182 @@
|
||||
import bpy
|
||||
import os, sys, time
|
||||
import json
|
||||
from mathutils import Matrix, Quaternion
|
||||
argv = sys.argv
|
||||
argv = argv[argv.index("--") + 1:]
|
||||
|
||||
incpath = os.path.dirname(__file__)
|
||||
|
||||
sys.path.insert(0, incpath)
|
||||
outdir = argv[0]
|
||||
|
||||
basis_change = Matrix((
|
||||
(1, 0, 0, 0),
|
||||
(0, 0, 1, 0),
|
||||
(0, -1, 0, 0),
|
||||
(0, 0, 0, 1)
|
||||
))
|
||||
|
||||
def export_root_objects_to_gltf(output_dir):
|
||||
# Ensure the output directory exists
|
||||
if not os.path.exists(output_dir):
|
||||
os.makedirs(output_dir)
|
||||
|
||||
# Deselect all objects
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
|
||||
# Iterate through all objects in the scene
|
||||
for obj in bpy.context.scene.objects:
|
||||
# Check if the object has no parent and has children
|
||||
# The original request specifies "objects having no parent with children".
|
||||
# This condition captures those that are explicitly root objects of a hierarchy.
|
||||
if obj.parent is None:
|
||||
# Select the root object and all its children
|
||||
|
||||
print(obj.name)
|
||||
if not obj.name.startswith("furniture-"):
|
||||
continue
|
||||
desc = {}
|
||||
desc["name"] = obj.name.replace("furniture-","")
|
||||
desc["mesh"] = obj.name + ".glb"
|
||||
if "furniture_tags" in obj:
|
||||
mtags = obj["furniture_tags"]
|
||||
if "," in mtags:
|
||||
atags = [m.strip() for m in mtags.split(",")]
|
||||
desc["tags"] = atags
|
||||
else:
|
||||
desc["tags"] = [mtags]
|
||||
else:
|
||||
desc["tags"] = []
|
||||
desc["sensors"] = []
|
||||
desc["actions"] = []
|
||||
desc["positions"] = []
|
||||
for child in obj.children:
|
||||
if child.name.startswith("action-"):
|
||||
if not "action" in child:
|
||||
continue
|
||||
if not "height" in child:
|
||||
continue
|
||||
if not "radius" in child:
|
||||
continue
|
||||
local_matrix = child.matrix_local
|
||||
ogre_local_matrix = basis_change @ local_matrix @ basis_change.inverted()
|
||||
local_pos_d = ogre_local_matrix.to_translation()
|
||||
local_rot_d = ogre_local_matrix.to_quaternion()
|
||||
local_pos = [round(x, 4) for x in local_pos_d]
|
||||
local_rot = [round(x, 4) for x in local_rot_d]
|
||||
action = {}
|
||||
action["position_x"] = local_pos[0]
|
||||
action["position_y"] = local_pos[1]
|
||||
action["position_z"] = local_pos[2]
|
||||
action["rotation_w"] = local_rot[0]
|
||||
action["rotation_x"] = local_rot[1]
|
||||
action["rotation_y"] = local_rot[2]
|
||||
action["rotation_z"] = local_rot[3]
|
||||
action["height"] = child["height"]
|
||||
action["radius"] = child["radius"]
|
||||
action["action"] = child["action"]
|
||||
if "action_text" in child:
|
||||
action["action_text"] = child["action_text"]
|
||||
else:
|
||||
action["action_text"] = child["action"].capitalize()
|
||||
if "name" in child:
|
||||
action["name"] = child["name"]
|
||||
else:
|
||||
action["name"] = desc["name"] + "_" + str(len(desc["actions"]))
|
||||
action["furniture"] = {}
|
||||
action["furniture"]["name"] = desc["name"]
|
||||
action["furniture"]["tags"] = desc["tags"]
|
||||
action["positions"] = []
|
||||
for schild in child.children:
|
||||
if schild.name.startswith("position-"):
|
||||
local_matrix = schild.matrix_local
|
||||
ogre_local_matrix = basis_change @ local_matrix @ basis_change.inverted()
|
||||
local_pos_d = ogre_local_matrix.to_translation()
|
||||
local_rot_d = ogre_local_matrix.to_quaternion()
|
||||
local_pos = [round(x, 4) for x in local_pos_d]
|
||||
local_rot = [round(x, 4) for x in local_rot_d]
|
||||
position = {}
|
||||
if "name" in schild:
|
||||
position["name"] = schild["name"]
|
||||
if "type" in schild:
|
||||
position["type"] = schild["type"]
|
||||
position["position_x"] = local_pos[0]
|
||||
position["position_y"] = local_pos[2]
|
||||
position["position_z"] = local_pos[1]
|
||||
position["rotation_w"] = local_rot[0]
|
||||
position["rotation_x"] = local_rot[1]
|
||||
position["rotation_y"] = local_rot[2]
|
||||
position["rotation_z"] = local_rot[3]
|
||||
action["positions"].append(position)
|
||||
desc["actions"].append(action)
|
||||
if child.name.startswith("position-"):
|
||||
local_matrix = child.matrix_local
|
||||
ogre_local_matrix = basis_change @ local_matrix @ basis_change.inverted()
|
||||
local_pos_d = ogre_local_matrix.to_translation()
|
||||
local_rot_d = ogre_local_matrix.to_quaternion()
|
||||
local_pos = [round(x, 4) for x in local_pos_d]
|
||||
local_rot = [round(x, 4) for x in local_rot_d]
|
||||
position = {}
|
||||
if "name" in child:
|
||||
position["name"] = child["name"]
|
||||
if "type" in child:
|
||||
position["type"] = child["type"]
|
||||
position["position_x"] = local_pos[0]
|
||||
position["position_y"] = local_pos[2]
|
||||
position["position_z"] = local_pos[1]
|
||||
position["rotation_w"] = local_rot[0]
|
||||
position["rotation_x"] = local_rot[1]
|
||||
position["rotation_y"] = local_rot[2]
|
||||
position["rotation_z"] = local_rot[3]
|
||||
desc["positions"].append(position)
|
||||
|
||||
obj.select_set(True)
|
||||
for child in obj.children_recursive:
|
||||
child.select_set(True)
|
||||
|
||||
obj.location = (0.0, 0.0, 0.0)
|
||||
|
||||
# Set the root object as the active object for the export operator
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
|
||||
# Define the output file path
|
||||
file_path = os.path.join(output_dir, f"{obj.name}.glb")
|
||||
|
||||
# Export the selected objects to a glTF file
|
||||
bpy.ops.export_scene.gltf(filepath=file_path,
|
||||
use_selection=True,
|
||||
check_existing=False,
|
||||
export_format='GLB',
|
||||
export_texture_dir='textures', export_texcoords=True,
|
||||
export_normals=True,
|
||||
export_tangents=True,
|
||||
export_materials='EXPORT',
|
||||
export_colors=True,
|
||||
use_mesh_edges=False,
|
||||
use_mesh_vertices=False,
|
||||
export_cameras=False,
|
||||
use_visible=False,
|
||||
use_renderable=False,
|
||||
export_yup=True,
|
||||
export_apply=True,
|
||||
export_animations=True,
|
||||
export_force_sampling=True,
|
||||
export_def_bones=False,
|
||||
export_current_frame=False,
|
||||
export_morph=True,
|
||||
export_morph_animation=False,
|
||||
export_morph_normal=True,
|
||||
export_morph_tangent=True,
|
||||
export_lights=False,
|
||||
export_skins=True)
|
||||
|
||||
# Deselect all objects for the next iteration
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
|
||||
print(f"Exported {obj.name} and its children to {file_path}")
|
||||
with open(file_path + ".json", "w") as json_data:
|
||||
json.dump(desc, json_data)
|
||||
|
||||
export_root_objects_to_gltf(outdir)
|
||||
|
||||
BIN
assets/blender/buildings/parts/furniture-full.blend
LFS
Normal file
BIN
assets/blender/buildings/parts/furniture-full.blend
LFS
Normal file
Binary file not shown.
BIN
assets/blender/buildings/parts/furniture-sofa.blend
LFS
Normal file
BIN
assets/blender/buildings/parts/furniture-sofa.blend
LFS
Normal file
Binary file not shown.
BIN
assets/blender/buildings/parts/furniture.blend
LFS
Normal file
BIN
assets/blender/buildings/parts/furniture.blend
LFS
Normal file
Binary file not shown.
71
assets/blender/scripts/export_building_parts.py
Normal file
71
assets/blender/scripts/export_building_parts.py
Normal file
@@ -0,0 +1,71 @@
|
||||
import bpy
|
||||
import os, sys, time
|
||||
argv = sys.argv
|
||||
argv = argv[argv.index("--") + 1:]
|
||||
|
||||
incpath = os.path.dirname(__file__)
|
||||
|
||||
sys.path.insert(0, incpath)
|
||||
outdir = argv[0]
|
||||
|
||||
def export_root_objects_to_gltf(output_dir):
|
||||
# Ensure the output directory exists
|
||||
if not os.path.exists(output_dir):
|
||||
os.makedirs(output_dir)
|
||||
|
||||
# Deselect all objects
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
|
||||
# Iterate through all objects in the scene
|
||||
for obj in bpy.context.scene.objects:
|
||||
# Check if the object has no parent and has children
|
||||
# The original request specifies "objects having no parent with children".
|
||||
# This condition captures those that are explicitly root objects of a hierarchy.
|
||||
if obj.parent is None:
|
||||
# Select the root object and all its children
|
||||
print(obj.name)
|
||||
obj.select_set(True)
|
||||
for child in obj.children_recursive:
|
||||
child.select_set(True)
|
||||
|
||||
# Set the root object as the active object for the export operator
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
|
||||
# Define the output file path
|
||||
file_path = os.path.join(output_dir, f"{obj.name}.glb")
|
||||
|
||||
# Export the selected objects to a glTF file
|
||||
bpy.ops.export_scene.gltf(filepath=file_path,
|
||||
use_selection=True,
|
||||
check_existing=False,
|
||||
export_format='GLB',
|
||||
export_texture_dir='textures', export_texcoords=True,
|
||||
export_normals=True,
|
||||
export_tangents=True,
|
||||
export_materials='EXPORT',
|
||||
export_colors=True,
|
||||
use_mesh_edges=False,
|
||||
use_mesh_vertices=False,
|
||||
export_cameras=False,
|
||||
use_visible=False,
|
||||
use_renderable=False,
|
||||
export_yup=True,
|
||||
export_apply=True,
|
||||
export_animations=True,
|
||||
export_force_sampling=True,
|
||||
export_def_bones=False,
|
||||
export_current_frame=False,
|
||||
export_morph=True,
|
||||
export_morph_animation=False,
|
||||
export_morph_normal=True,
|
||||
export_morph_tangent=True,
|
||||
export_lights=False,
|
||||
export_skins=True)
|
||||
|
||||
# Deselect all objects for the next iteration
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
|
||||
print(f"Exported {obj.name} and its children to {file_path}")
|
||||
|
||||
export_root_objects_to_gltf(outdir)
|
||||
|
||||
@@ -3,7 +3,7 @@ set(LUA_SCRIPTS_SRC
|
||||
data.lua
|
||||
)
|
||||
set(LUA_SCRIPTS_OUTPUT)
|
||||
set(LUA_PACKAGES narrator stories)
|
||||
set(LUA_PACKAGES narrator stories json)
|
||||
foreach(LUA_SCRIPT_FILE ${LUA_SCRIPTS_SRC})
|
||||
get_filename_component(FILE_NAME ${LUA_SCRIPT_FILE} NAME_WE)
|
||||
set(LUA_SCRIPT_OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/${FILE_NAME}.lua)
|
||||
@@ -21,4 +21,4 @@ foreach(LUA_PACKAGE ${LUA_PACKAGES})
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${LUA_PACKAGE})
|
||||
list(APPEND LUA_PACKAGES_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${LUA_PACKAGE})
|
||||
endforeach()
|
||||
add_custom_target(stage_lua_scripts ALL DEPENDS ${LUA_SCRIPTS_OUTPUT} ${LUA_PACKAGES_OUTPUT})
|
||||
add_custom_target(stage_lua_scripts ALL DEPENDS ${LUA_SCRIPTS_OUTPUT} ${LUA_PACKAGES_OUTPUT})
|
||||
|
||||
@@ -62,6 +62,7 @@ dropped you into the sea. Last thing you heard before you hit the water was happ
|
||||
}
|
||||
]]--
|
||||
local narrator = require('narrator.narrator')
|
||||
local json = require('json.json')
|
||||
|
||||
function dump(o)
|
||||
if type(o) == 'table' then
|
||||
@@ -655,3 +656,154 @@ setup_handler(function(event, trigger_entity, what_entity)
|
||||
--]]
|
||||
end
|
||||
end)
|
||||
|
||||
setup_action_handler("talk", {
|
||||
activate = function(this)
|
||||
local book = narrator.parse_file('stories.talk')
|
||||
this.story = narrator.init_story(book)
|
||||
local get_crash_bind = function(this)
|
||||
local crash_bind = function()
|
||||
print("variables")
|
||||
print(dump(this.story.variables))
|
||||
crash()
|
||||
end
|
||||
return crash_bind
|
||||
end
|
||||
this.story:bind('crash', get_crash_bind(this))
|
||||
local props = this._get_properties()
|
||||
local goals = this._get_goals()
|
||||
print("node activated")
|
||||
-- print(dump(goals))
|
||||
local json_data = json.decode(props)
|
||||
print(dump(json_data))
|
||||
for i, v in ipairs(goals) do
|
||||
print("Goal: ", i)
|
||||
local goal_data = json.decode(v)
|
||||
print(dump(goal_data))
|
||||
end
|
||||
this.story:begin()
|
||||
this:narration_update()
|
||||
get_crash_bind(this)()
|
||||
crash()
|
||||
end,
|
||||
event = function(this, event)
|
||||
if event == "narration_progress" then
|
||||
this:narration_update()
|
||||
end
|
||||
if event == "narration_answered" then
|
||||
local answer = this._get_narration_answer()
|
||||
this.story:choose(answer)
|
||||
this:narration_update()
|
||||
end
|
||||
end,
|
||||
finish = function(this)
|
||||
end,
|
||||
narration_update = function(this)
|
||||
local ret = ""
|
||||
local choices = {}
|
||||
local have_choice = false
|
||||
local have_paragraph = false
|
||||
print("narration_update")
|
||||
if this.story:can_continue() then
|
||||
print("CAN continue")
|
||||
have_paragraph = true
|
||||
local paragraph = this.story:continue(1)
|
||||
print(dump(paragraph))
|
||||
local text = paragraph.text
|
||||
if paragraph.tags then
|
||||
-- text = text .. ' #' .. table.concat(paragraph.tags, ' #')
|
||||
for i, tag in ipairs(paragraph.tags) do
|
||||
if tag == 'discard' then
|
||||
text = ''
|
||||
elseif tag == 'crash' then
|
||||
print(text)
|
||||
crash()
|
||||
end
|
||||
this:handle_tag(tag)
|
||||
end
|
||||
end
|
||||
ret = text
|
||||
if this.story:can_choose() then
|
||||
have_choice = true
|
||||
local ch = this.story:get_choices()
|
||||
for i, choice in ipairs(ch) do
|
||||
table.insert(choices, choice.text)
|
||||
print(i, dump(choice))
|
||||
end
|
||||
if #choices == 1 and choices[1] == "Ascend" then
|
||||
this.story:choose(1)
|
||||
choices = {}
|
||||
end
|
||||
if #choices == 1 and choices[1] == "Continue" then
|
||||
this.story:choose(1)
|
||||
choices = {}
|
||||
end
|
||||
end
|
||||
else
|
||||
print("can NOT continue")
|
||||
end
|
||||
print(ret)
|
||||
if (#choices > 0) then
|
||||
print("choices!!!")
|
||||
this._narration(ret, choices)
|
||||
else
|
||||
this._narration(ret, {})
|
||||
end
|
||||
if not have_choice and not have_paragraph then
|
||||
this._finish()
|
||||
end
|
||||
end,
|
||||
handle_tag = function(this, tag)
|
||||
print("tag: " .. tag)
|
||||
end,
|
||||
})
|
||||
--[[
|
||||
active_dialogues = {}
|
||||
setup_action_handler("talk", function(town, index, word)
|
||||
local ret = ""
|
||||
local choices = {}
|
||||
local have_choice = false
|
||||
local have_paragraph = false
|
||||
local book = narrator.parse_file('stories.talk')
|
||||
local story = narrator.init_story(book)
|
||||
if story == nil then
|
||||
crash()
|
||||
end
|
||||
story:begin()
|
||||
narrate("Boo!!!!")
|
||||
if story.can_continue() then
|
||||
local paragraph = story:continue(1)
|
||||
print(dump(paragraph))
|
||||
if story:can_choose() then
|
||||
have_choice = true
|
||||
local ch = story:get_choices()
|
||||
for i, choice in ipairs(ch) do
|
||||
table.insert(choices, choice.text)
|
||||
print(i, dump(choice))
|
||||
end
|
||||
if #choices == 1 and choices[1] == "Ascend" then
|
||||
story:choose(1)
|
||||
choices = {}
|
||||
end
|
||||
if #choices == 1 and choices[1] == "Continue" then
|
||||
this.story:choose(1)
|
||||
choices = {}
|
||||
end
|
||||
end
|
||||
end
|
||||
if (#choices > 0) then
|
||||
print("choices!!!")
|
||||
narrate(ret, choices)
|
||||
else
|
||||
narrate(ret)
|
||||
end
|
||||
if not have_choice and not have_paragraph then
|
||||
-- complete
|
||||
crash()
|
||||
else
|
||||
print("can continue")
|
||||
end
|
||||
|
||||
end)
|
||||
]]--
|
||||
|
||||
|
||||
388
lua-scripts/json/json.lua
Normal file
388
lua-scripts/json/json.lua
Normal file
@@ -0,0 +1,388 @@
|
||||
--
|
||||
-- json.lua
|
||||
--
|
||||
-- Copyright (c) 2020 rxi
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
|
||||
local json = { _version = "0.1.2" }
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Encode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local encode
|
||||
|
||||
local escape_char_map = {
|
||||
[ "\\" ] = "\\",
|
||||
[ "\"" ] = "\"",
|
||||
[ "\b" ] = "b",
|
||||
[ "\f" ] = "f",
|
||||
[ "\n" ] = "n",
|
||||
[ "\r" ] = "r",
|
||||
[ "\t" ] = "t",
|
||||
}
|
||||
|
||||
local escape_char_map_inv = { [ "/" ] = "/" }
|
||||
for k, v in pairs(escape_char_map) do
|
||||
escape_char_map_inv[v] = k
|
||||
end
|
||||
|
||||
|
||||
local function escape_char(c)
|
||||
return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte()))
|
||||
end
|
||||
|
||||
|
||||
local function encode_nil(val)
|
||||
return "null"
|
||||
end
|
||||
|
||||
|
||||
local function encode_table(val, stack)
|
||||
local res = {}
|
||||
stack = stack or {}
|
||||
|
||||
-- Circular reference?
|
||||
if stack[val] then error("circular reference") end
|
||||
|
||||
stack[val] = true
|
||||
|
||||
if rawget(val, 1) ~= nil or next(val) == nil then
|
||||
-- Treat as array -- check keys are valid and it is not sparse
|
||||
local n = 0
|
||||
for k in pairs(val) do
|
||||
if type(k) ~= "number" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
n = n + 1
|
||||
end
|
||||
if n ~= #val then
|
||||
error("invalid table: sparse array")
|
||||
end
|
||||
-- Encode
|
||||
for i, v in ipairs(val) do
|
||||
table.insert(res, encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "[" .. table.concat(res, ",") .. "]"
|
||||
|
||||
else
|
||||
-- Treat as an object
|
||||
for k, v in pairs(val) do
|
||||
if type(k) ~= "string" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "{" .. table.concat(res, ",") .. "}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function encode_string(val)
|
||||
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
|
||||
end
|
||||
|
||||
|
||||
local function encode_number(val)
|
||||
-- Check for NaN, -inf and inf
|
||||
if val ~= val or val <= -math.huge or val >= math.huge then
|
||||
error("unexpected number value '" .. tostring(val) .. "'")
|
||||
end
|
||||
return string.format("%.14g", val)
|
||||
end
|
||||
|
||||
|
||||
local type_func_map = {
|
||||
[ "nil" ] = encode_nil,
|
||||
[ "table" ] = encode_table,
|
||||
[ "string" ] = encode_string,
|
||||
[ "number" ] = encode_number,
|
||||
[ "boolean" ] = tostring,
|
||||
}
|
||||
|
||||
|
||||
encode = function(val, stack)
|
||||
local t = type(val)
|
||||
local f = type_func_map[t]
|
||||
if f then
|
||||
return f(val, stack)
|
||||
end
|
||||
error("unexpected type '" .. t .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.encode(val)
|
||||
return ( encode(val) )
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Decode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local parse
|
||||
|
||||
local function create_set(...)
|
||||
local res = {}
|
||||
for i = 1, select("#", ...) do
|
||||
res[ select(i, ...) ] = true
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local space_chars = create_set(" ", "\t", "\r", "\n")
|
||||
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
|
||||
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
|
||||
local literals = create_set("true", "false", "null")
|
||||
|
||||
local literal_map = {
|
||||
[ "true" ] = true,
|
||||
[ "false" ] = false,
|
||||
[ "null" ] = nil,
|
||||
}
|
||||
|
||||
|
||||
local function next_char(str, idx, set, negate)
|
||||
for i = idx, #str do
|
||||
if set[str:sub(i, i)] ~= negate then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return #str + 1
|
||||
end
|
||||
|
||||
|
||||
local function decode_error(str, idx, msg)
|
||||
local line_count = 1
|
||||
local col_count = 1
|
||||
for i = 1, idx - 1 do
|
||||
col_count = col_count + 1
|
||||
if str:sub(i, i) == "\n" then
|
||||
line_count = line_count + 1
|
||||
col_count = 1
|
||||
end
|
||||
end
|
||||
error( string.format("%s at line %d col %d", msg, line_count, col_count) )
|
||||
end
|
||||
|
||||
|
||||
local function codepoint_to_utf8(n)
|
||||
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
|
||||
local f = math.floor
|
||||
if n <= 0x7f then
|
||||
return string.char(n)
|
||||
elseif n <= 0x7ff then
|
||||
return string.char(f(n / 64) + 192, n % 64 + 128)
|
||||
elseif n <= 0xffff then
|
||||
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
elseif n <= 0x10ffff then
|
||||
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
|
||||
f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
end
|
||||
error( string.format("invalid unicode codepoint '%x'", n) )
|
||||
end
|
||||
|
||||
|
||||
local function parse_unicode_escape(s)
|
||||
local n1 = tonumber( s:sub(1, 4), 16 )
|
||||
local n2 = tonumber( s:sub(7, 10), 16 )
|
||||
-- Surrogate pair?
|
||||
if n2 then
|
||||
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
|
||||
else
|
||||
return codepoint_to_utf8(n1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function parse_string(str, i)
|
||||
local res = ""
|
||||
local j = i + 1
|
||||
local k = j
|
||||
|
||||
while j <= #str do
|
||||
local x = str:byte(j)
|
||||
|
||||
if x < 32 then
|
||||
decode_error(str, j, "control character in string")
|
||||
|
||||
elseif x == 92 then -- `\`: Escape
|
||||
res = res .. str:sub(k, j - 1)
|
||||
j = j + 1
|
||||
local c = str:sub(j, j)
|
||||
if c == "u" then
|
||||
local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1)
|
||||
or str:match("^%x%x%x%x", j + 1)
|
||||
or decode_error(str, j - 1, "invalid unicode escape in string")
|
||||
res = res .. parse_unicode_escape(hex)
|
||||
j = j + #hex
|
||||
else
|
||||
if not escape_chars[c] then
|
||||
decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string")
|
||||
end
|
||||
res = res .. escape_char_map_inv[c]
|
||||
end
|
||||
k = j + 1
|
||||
|
||||
elseif x == 34 then -- `"`: End of string
|
||||
res = res .. str:sub(k, j - 1)
|
||||
return res, j + 1
|
||||
end
|
||||
|
||||
j = j + 1
|
||||
end
|
||||
|
||||
decode_error(str, i, "expected closing quote for string")
|
||||
end
|
||||
|
||||
|
||||
local function parse_number(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local s = str:sub(i, x - 1)
|
||||
local n = tonumber(s)
|
||||
if not n then
|
||||
decode_error(str, i, "invalid number '" .. s .. "'")
|
||||
end
|
||||
return n, x
|
||||
end
|
||||
|
||||
|
||||
local function parse_literal(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local word = str:sub(i, x - 1)
|
||||
if not literals[word] then
|
||||
decode_error(str, i, "invalid literal '" .. word .. "'")
|
||||
end
|
||||
return literal_map[word], x
|
||||
end
|
||||
|
||||
|
||||
local function parse_array(str, i)
|
||||
local res = {}
|
||||
local n = 1
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local x
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of array?
|
||||
if str:sub(i, i) == "]" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read token
|
||||
x, i = parse(str, i)
|
||||
res[n] = x
|
||||
n = n + 1
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "]" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local function parse_object(str, i)
|
||||
local res = {}
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local key, val
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of object?
|
||||
if str:sub(i, i) == "}" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read key
|
||||
if str:sub(i, i) ~= '"' then
|
||||
decode_error(str, i, "expected string for key")
|
||||
end
|
||||
key, i = parse(str, i)
|
||||
-- Read ':' delimiter
|
||||
i = next_char(str, i, space_chars, true)
|
||||
if str:sub(i, i) ~= ":" then
|
||||
decode_error(str, i, "expected ':' after key")
|
||||
end
|
||||
i = next_char(str, i + 1, space_chars, true)
|
||||
-- Read value
|
||||
val, i = parse(str, i)
|
||||
-- Set
|
||||
res[key] = val
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "}" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local char_func_map = {
|
||||
[ '"' ] = parse_string,
|
||||
[ "0" ] = parse_number,
|
||||
[ "1" ] = parse_number,
|
||||
[ "2" ] = parse_number,
|
||||
[ "3" ] = parse_number,
|
||||
[ "4" ] = parse_number,
|
||||
[ "5" ] = parse_number,
|
||||
[ "6" ] = parse_number,
|
||||
[ "7" ] = parse_number,
|
||||
[ "8" ] = parse_number,
|
||||
[ "9" ] = parse_number,
|
||||
[ "-" ] = parse_number,
|
||||
[ "t" ] = parse_literal,
|
||||
[ "f" ] = parse_literal,
|
||||
[ "n" ] = parse_literal,
|
||||
[ "[" ] = parse_array,
|
||||
[ "{" ] = parse_object,
|
||||
}
|
||||
|
||||
|
||||
parse = function(str, idx)
|
||||
local chr = str:sub(idx, idx)
|
||||
local f = char_func_map[chr]
|
||||
if f then
|
||||
return f(str, idx)
|
||||
end
|
||||
decode_error(str, idx, "unexpected character '" .. chr .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.decode(str)
|
||||
if type(str) ~= "string" then
|
||||
error("expected argument of type string, got " .. type(str))
|
||||
end
|
||||
local res, idx = parse(str, next_char(str, 1, space_chars, true))
|
||||
idx = next_char(str, idx, space_chars, true)
|
||||
if idx <= #str then
|
||||
decode_error(str, idx, "trailing garbage")
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
|
||||
return json
|
||||
20
lua-scripts/stories/talk.ink
Normal file
20
lua-scripts/stories/talk.ink
Normal file
@@ -0,0 +1,20 @@
|
||||
Yes?
|
||||
-> start
|
||||
=== start ===
|
||||
+ [Common] -> common
|
||||
+ [Debug] -> debug
|
||||
+ [Leave] -> END
|
||||
=== common ===
|
||||
# discard
|
||||
+ [What do you think about me?]
|
||||
I don't know.
|
||||
-> END
|
||||
+ [How are you?]
|
||||
I think everything is allright.
|
||||
-> END
|
||||
=== debug ===
|
||||
Debug #discard
|
||||
+ Crash
|
||||
#crash
|
||||
-> END
|
||||
|
||||
@@ -21,6 +21,7 @@ FileSystem=resources/terrain
|
||||
FileSystem=skybox
|
||||
FileSystem=resources/buildings
|
||||
FileSystem=resources/buildings/parts/pier
|
||||
FileSystem=resources/buildings/parts/furniture
|
||||
FileSystem=resources/vehicles
|
||||
FileSystem=resources/debug
|
||||
FileSystem=resources/fonts
|
||||
|
||||
34
resources/CMakeLists.txt
Normal file
34
resources/CMakeLists.txt
Normal file
@@ -0,0 +1,34 @@
|
||||
project(resources)
|
||||
set(DIRECTORY_LIST
|
||||
main
|
||||
shaderlib
|
||||
terrain
|
||||
)
|
||||
set(TARGET_PATHS)
|
||||
foreach(DIR_NAME ${DIRECTORY_LIST})
|
||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${DIR_NAME}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/${DIR_NAME}
|
||||
${CMAKE_CURRENT_BINARY_DIR}/${DIR_NAME}
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${DIR_NAME})
|
||||
list(APPEND TARGET_PATHS ${CMAKE_CURRENT_BINARY_DIR}/${DIR_NAME})
|
||||
endforeach()
|
||||
|
||||
#add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/terrain/world_map.png
|
||||
# COMMAND unzip -o ${CMAKE_CURRENT_SOURCE_DIR}/world_map.kra mergedimage.png -d ${CMAKE_CURRENT_BINARY_DIR}/world_map
|
||||
# COMMAND ${CMAKE_COMMAND} -E copy
|
||||
# ${CMAKE_BINARY_DIR}/world_map/mergedimage.png
|
||||
# ${CMAKE_BINARY_DIR}/resources/terrain/world_map.png
|
||||
# COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/world_map
|
||||
# DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/world_map.kra)
|
||||
#list(APPEND TARGET_PATHS ${CMAKE_CURRENT_BINARY_DIR}/terrain/world_map.png)
|
||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/terrain/brushes.png
|
||||
COMMAND unzip -o ${CMAKE_CURRENT_SOURCE_DIR}/brushes.kra mergedimage.png -d ${CMAKE_CURRENT_BINARY_DIR}/brushes
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_CURRENT_BINARY_DIR}/brushes/mergedimage.png
|
||||
${CMAKE_CURRENT_BINARY_DIR}/terrain/brushes.png
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_CURRENT_BINARY_DIR}/brushes
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/brushes.kra)
|
||||
list(APPEND TARGET_PATHS ${CMAKE_CURRENT_BINARY_DIR}/terrain/brushes.png)
|
||||
|
||||
add_custom_target(stage_resources ALL DEPENDS ${TARGET_PATHS})
|
||||
16
resources/shaderlib/RTSLib_Colour.glsl
Normal file
16
resources/shaderlib/RTSLib_Colour.glsl
Normal file
@@ -0,0 +1,16 @@
|
||||
// This file is part of the OGRE project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at https://www.ogre3d.org/licensing.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#ifdef USE_LINEAR_COLOURS
|
||||
#define ENABLE_LINEAR_COLOUR(colour) colour.rgb = pow(colour.rgb, vec3_splat(2.2))
|
||||
#else
|
||||
#define ENABLE_LINEAR_COLOUR(colour)
|
||||
#endif
|
||||
|
||||
#if defined(USE_LINEAR_COLOURS) && !defined(TARGET_CONSUMES_LINEAR)
|
||||
#define COLOUR_TRANSFER(colour) colour.rgb = pow(colour.rgb, vec3_splat(1.0/2.2))
|
||||
#else
|
||||
#define COLOUR_TRANSFER(colour)
|
||||
#endif
|
||||
BIN
resources/terrain/world_map.png
Normal file
BIN
resources/terrain/world_map.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
2586
src/FastNoiseLite/FastNoiseLite.h
Normal file
2586
src/FastNoiseLite/FastNoiseLite.h
Normal file
File diff suppressed because it is too large
Load Diff
22
src/FastNoiseLite/LICENSE
Normal file
22
src/FastNoiseLite/LICENSE
Normal file
@@ -0,0 +1,22 @@
|
||||
MIT License
|
||||
|
||||
Copyright(c) 2020 Jordan Peck (jordan.me2@gmail.com)
|
||||
Copyright(c) 2020 Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
23
src/FastNoiseLite/README.md
Normal file
23
src/FastNoiseLite/README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
## Getting Started
|
||||
|
||||
Here's an example for creating a 128x128 array of OpenSimplex2 noise
|
||||
|
||||
```cpp
|
||||
// Create and configure FastNoise object
|
||||
FastNoiseLite noise;
|
||||
noise.SetNoiseType(FastNoiseLite::NoiseType_OpenSimplex2);
|
||||
|
||||
// Gather noise data
|
||||
std::vector<float> noiseData(128 * 128);
|
||||
int index = 0;
|
||||
|
||||
for (int y = 0; y < 128; y++)
|
||||
{
|
||||
for (int x = 0; x < 128; x++)
|
||||
{
|
||||
noiseData[index++] = noise.GetNoise((float)x, (float)y);
|
||||
}
|
||||
}
|
||||
|
||||
// Do something with this data...
|
||||
```
|
||||
@@ -9,7 +9,7 @@ find_package(OgreProcedural REQUIRED CONFIG)
|
||||
find_package(pugixml REQUIRED CONFIG)
|
||||
find_package(flecs REQUIRED CONFIG)
|
||||
|
||||
set(COPY_DIRECTORIES resources skybox water resources/buildings/parts)
|
||||
set(COPY_DIRECTORIES characters resources skybox water resources/buildings/parts)
|
||||
set(INSTALL_DEPS ${CMAKE_CURRENT_BINARY_DIR}/resources.cfg)
|
||||
foreach(DIR_NAME ${COPY_DIRECTORIES})
|
||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${DIR_NAME}
|
||||
@@ -29,6 +29,7 @@ add_library(editor STATIC EditorGizmoModule.cpp EditorInputModule.cpp)
|
||||
target_link_libraries(editor PRIVATE
|
||||
OgreMain
|
||||
GameData
|
||||
physics
|
||||
)
|
||||
target_include_directories(editor PUBLIC .)
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include <iostream>
|
||||
#include <Ogre.h>
|
||||
#include <Jolt/Jolt.h>
|
||||
#include <Jolt/Physics/Body/BodyID.h>
|
||||
#include "Components.h"
|
||||
#include "GameData.h"
|
||||
#include "PhysicsModule.h"
|
||||
@@ -35,8 +37,6 @@ EditorInputModule::EditorInputModule(flecs::world &ecs)
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([this](const EngineData &eng, Input &input,
|
||||
Camera &camera) {
|
||||
if (ECS::player.is_valid())
|
||||
return;
|
||||
if (!camera.configured) {
|
||||
// create a pivot at roughly the character's shoulder
|
||||
camera.mCameraPivot =
|
||||
@@ -147,10 +147,11 @@ EditorInputModule::EditorInputModule(flecs::world &ecs)
|
||||
position =
|
||||
ray.getPoint(ogreResult.second);
|
||||
Ogre::Vector3 position2;
|
||||
JPH::BodyID id;
|
||||
bool hit = PhysicsModule::raycastQuery(
|
||||
ray.getOrigin(),
|
||||
ray.getPoint(2000.0f),
|
||||
position2);
|
||||
position2, id);
|
||||
if (hit) {
|
||||
float d1 =
|
||||
ray.getOrigin()
|
||||
@@ -169,10 +170,11 @@ EditorInputModule::EditorInputModule(flecs::world &ecs)
|
||||
.sceneNode->_setDerivedPosition(
|
||||
position);
|
||||
} else {
|
||||
JPH::BodyID id;
|
||||
bool hit = PhysicsModule::raycastQuery(
|
||||
ray.getOrigin(),
|
||||
ray.getPoint(2000.0f),
|
||||
position);
|
||||
ray.getPoint(2000.0f), position,
|
||||
id);
|
||||
if (hit) {
|
||||
std::cout
|
||||
<< "HIT!: " << position
|
||||
@@ -238,4 +240,4 @@ EditorInputModule::EditorInputModule(flecs::world &ecs)
|
||||
ECS::get().modified<ECS::Input>();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#include "Components.h"
|
||||
#include "CharacterModule.h"
|
||||
#include "TerrainModule.h"
|
||||
#include "GUIModule.h"
|
||||
#include "GUIModuleCommon.h"
|
||||
#include "AppModule.h"
|
||||
#include "EditorGizmoModule.h"
|
||||
#include "EditorInputModule.h"
|
||||
@@ -296,15 +296,71 @@ public:
|
||||
void frameRendered(const Ogre::FrameEvent &evt) override;
|
||||
};
|
||||
|
||||
class App : public OgreBites::ApplicationContext {
|
||||
Ogre::SceneNode *mCameraNode, *mCameraPivot, *mCameraGoal;
|
||||
Ogre::Camera *mCamera;
|
||||
Ogre::Real mPivotPitch;
|
||||
struct SceneData {
|
||||
Ogre::SceneManager *mScnMgr;
|
||||
Ogre::Camera *mCamera;
|
||||
Ogre::SceneNode *mCameraNode, *mCameraPivot, *mCameraGoal;
|
||||
Ogre::Real mPivotPitch;
|
||||
SceneData()
|
||||
: mScnMgr(nullptr)
|
||||
, mCamera(nullptr)
|
||||
, mCameraNode(nullptr)
|
||||
, mCameraPivot(nullptr)
|
||||
, mCameraGoal(nullptr)
|
||||
{
|
||||
}
|
||||
void setup(Ogre::Root *root, Ogre::OverlaySystem *overlaySystem)
|
||||
{
|
||||
Ogre::SceneManager *scnMgr = root->createSceneManager();
|
||||
mScnMgr = scnMgr;
|
||||
mScnMgr->addRenderQueueListener(overlaySystem);
|
||||
}
|
||||
void initCamera(const Ogre::String &cameraName)
|
||||
{
|
||||
mCameraNode = mScnMgr->getRootSceneNode()->createChildSceneNode(
|
||||
cameraName + "CameraNode");
|
||||
mCameraNode->setPosition(0, 2, 3);
|
||||
mCameraNode->lookAt(Ogre::Vector3(0, 1, -1),
|
||||
Ogre::Node::TS_PARENT);
|
||||
|
||||
// create the camera
|
||||
mCamera = mScnMgr->createCamera(cameraName);
|
||||
mCamera->setNearClipDistance(0.05f);
|
||||
mCamera->setAutoAspectRatio(true);
|
||||
mCameraNode->attachObject(mCamera);
|
||||
mCameraPivot =
|
||||
mScnMgr->getRootSceneNode()->createChildSceneNode(
|
||||
cameraName + "FPSCameraPivot");
|
||||
mCameraGoal = mCameraPivot->createChildSceneNode(
|
||||
cameraName + "FPSCameraGoal", Ogre::Vector3(0, 2, 3));
|
||||
mCameraNode->setPosition(mCameraPivot->getPosition() +
|
||||
mCameraGoal->getPosition());
|
||||
mCameraPivot->setFixedYawAxis(true);
|
||||
mCameraGoal->setFixedYawAxis(true);
|
||||
mCameraNode->setFixedYawAxis(true);
|
||||
// our model is quite small, so reduce the clipping planes
|
||||
mCamera->setNearClipDistance(0.1f);
|
||||
mCamera->setFarClipDistance(800);
|
||||
mPivotPitch = 0;
|
||||
}
|
||||
void setupRTSS()
|
||||
{
|
||||
Ogre::RTShader::ShaderGenerator *shadergen =
|
||||
Ogre::RTShader::ShaderGenerator::getSingletonPtr();
|
||||
shadergen->addSceneManager(mScnMgr);
|
||||
}
|
||||
Ogre::SceneManager *getSceneManager()
|
||||
{
|
||||
return mScnMgr;
|
||||
}
|
||||
};
|
||||
|
||||
class App : public OgreBites::ApplicationContext {
|
||||
Ogre::Viewport *mViewport;
|
||||
SkyBoxRenderer *sky;
|
||||
bool mGrab;
|
||||
KeyboardListener mKbd;
|
||||
SceneData mEditorNormalScene;
|
||||
|
||||
public:
|
||||
App()
|
||||
@@ -316,16 +372,11 @@ public:
|
||||
virtual ~App()
|
||||
{
|
||||
}
|
||||
void setup()
|
||||
void setup() override
|
||||
{
|
||||
OgreBites::ApplicationContext::setup();
|
||||
Ogre::Root *root = getRoot();
|
||||
Ogre::SceneManager *scnMgr = root->createSceneManager();
|
||||
mScnMgr = scnMgr;
|
||||
Ogre::OverlaySystem *pOverlaySystem = getOverlaySystem();
|
||||
mScnMgr->addRenderQueueListener(pOverlaySystem);
|
||||
// mTrayMgr = new OgreBites::TrayManager("AppTrays",
|
||||
// getRenderWindow());
|
||||
mEditorNormalScene.setup(root, getOverlaySystem());
|
||||
}
|
||||
bool isWindowGrab()
|
||||
{
|
||||
@@ -353,77 +404,41 @@ public:
|
||||
|
||||
void initCamera()
|
||||
{
|
||||
mCameraNode = mScnMgr->getRootSceneNode()->createChildSceneNode(
|
||||
"CameraNode");
|
||||
mCameraNode->setPosition(0, 2, 3);
|
||||
mCameraNode->lookAt(Ogre::Vector3(0, 1, -1),
|
||||
Ogre::Node::TS_PARENT);
|
||||
|
||||
// create the camera
|
||||
mCamera = mScnMgr->createCamera("fps_camera");
|
||||
mCamera->setNearClipDistance(0.05f);
|
||||
mCamera->setAutoAspectRatio(true);
|
||||
mCameraNode->attachObject(mCamera);
|
||||
|
||||
// and tell it to render into the main window
|
||||
mViewport = getRenderWindow()->addViewport(mCamera);
|
||||
mCameraPivot =
|
||||
mScnMgr->getRootSceneNode()->createChildSceneNode(
|
||||
"FPSCameraPivot");
|
||||
mCameraGoal = mCameraPivot->createChildSceneNode(
|
||||
"FPSCameraGoal", Ogre::Vector3(0, 2, 3));
|
||||
mCameraNode->setPosition(mCameraPivot->getPosition() +
|
||||
mCameraGoal->getPosition());
|
||||
mCameraPivot->setFixedYawAxis(true);
|
||||
mCameraGoal->setFixedYawAxis(true);
|
||||
mCameraNode->setFixedYawAxis(true);
|
||||
// our model is quite small, so reduce the clipping planes
|
||||
mCamera->setNearClipDistance(0.1f);
|
||||
mCamera->setFarClipDistance(800);
|
||||
mPivotPitch = 0;
|
||||
mEditorNormalScene.initCamera("fps_camera");
|
||||
mViewport = getRenderWindow()->addViewport(
|
||||
mEditorNormalScene.mCamera);
|
||||
}
|
||||
void configure()
|
||||
{
|
||||
std::cout << "Startup" << "\n";
|
||||
std::cout << "Startup"
|
||||
<< "\n";
|
||||
initApp();
|
||||
std::cout << "Set up RTSS" << "\n";
|
||||
std::cout << "Set up RTSS"
|
||||
<< "\n";
|
||||
Ogre::Root *root = getRoot();
|
||||
Ogre::SceneManager *scnMgr = getSceneManager();
|
||||
|
||||
// register our scene with the RTSS
|
||||
Ogre::RTShader::ShaderGenerator *shadergen =
|
||||
Ogre::RTShader::ShaderGenerator::getSingletonPtr();
|
||||
shadergen->addSceneManager(scnMgr);
|
||||
mEditorNormalScene.setupRTSS();
|
||||
setWindowGrab(false);
|
||||
std::cout << "Init camera" << "\n";
|
||||
std::cout << "Init camera"
|
||||
<< "\n";
|
||||
initCamera();
|
||||
std::cout << "Set up water" << "\n";
|
||||
std::cout << "Set up cursor" << "\n";
|
||||
Ogre::ResourceGroupManager::getSingleton()
|
||||
.initialiseAllResourceGroups();
|
||||
// OgreBites::ApplicationContext::loadResources();
|
||||
// setupCursor();
|
||||
std::cout << "Setup input" << "\n";
|
||||
std::cout << "Setup input"
|
||||
<< "\n";
|
||||
setupInput();
|
||||
std::cout << "Create content" << "\n";
|
||||
std::cout << "Create content"
|
||||
<< "\n";
|
||||
createContent();
|
||||
std::cout << "Setup done" << "\n";
|
||||
#if 0
|
||||
mDbgDraw->setDebugMode(mDbgDraw->getDebugMode() |
|
||||
btIDebugDraw::DBG_DrawContactPoints);
|
||||
#endif
|
||||
}
|
||||
Ogre::SceneManager *getSceneManager()
|
||||
{
|
||||
return mScnMgr;
|
||||
}
|
||||
std::cout << "Setup done"
|
||||
<< "\n";
|
||||
Ogre::LogManager::getSingleton().setMinLogLevel(
|
||||
Ogre::LML_TRIVIAL);
|
||||
}
|
||||
Ogre::Timer mTerrainUpd;
|
||||
// TODO: implement rough water level calculation
|
||||
float getWaterLevel(const Ogre::Vector3 &position)
|
||||
{
|
||||
Ogre::Vector3::UNIT_Y;
|
||||
float etime =
|
||||
Ogre::ControllerManager::getSingleton().getElapsedTime();
|
||||
return 0.0f;
|
||||
}
|
||||
void updateWorld(float delta)
|
||||
@@ -572,10 +587,11 @@ public:
|
||||
void setupInput()
|
||||
{
|
||||
}
|
||||
bool switchWindow = false;
|
||||
void createContent()
|
||||
{
|
||||
int i;
|
||||
sky = new SkyBoxRenderer(getSceneManager());
|
||||
sky = new SkyBoxRenderer(mEditorNormalScene.getSceneManager());
|
||||
bool drawFirst = true;
|
||||
uint8_t renderQueue = drawFirst ?
|
||||
Ogre::RENDER_QUEUE_SKIES_EARLY :
|
||||
@@ -591,8 +607,10 @@ public:
|
||||
"Skybox/Dynamic", "General");
|
||||
OgreAssert(m, "Sky box material not found.");
|
||||
m->load();
|
||||
ECS::setupEditor(mScnMgr, /*mDynWorld.get(), */ mCameraNode,
|
||||
mCamera, getRenderWindow());
|
||||
ECS::setupEditor(
|
||||
mEditorNormalScene.mScnMgr,
|
||||
/*mDynWorld.get(), */ mEditorNormalScene.mCameraNode,
|
||||
mEditorNormalScene.mCamera, getRenderWindow());
|
||||
ECS::get().import <ECS::EditorGizmoModule>();
|
||||
ECS::get().import <ECS::EditorInputModule>();
|
||||
ECS::get().set<ECS::RenderWindow>(
|
||||
@@ -625,86 +643,14 @@ public:
|
||||
{ getImGuiInputListener(), &mKbd } });
|
||||
ECS::get().add<ECS::EditorDebugMaterial>();
|
||||
std::shared_ptr<Ogre::ManualObject> manualObj(
|
||||
mScnMgr->createManualObject("EditorGizmo"));
|
||||
mEditorNormalScene.mScnMgr->createManualObject(
|
||||
"EditorGizmo"));
|
||||
Ogre::SceneNode *gizmoNode =
|
||||
mScnMgr->getRootSceneNode()->createChildSceneNode(
|
||||
"EditorGizmoNode");
|
||||
mEditorNormalScene.mScnMgr->getRootSceneNode()
|
||||
->createChildSceneNode("EditorGizmoNode");
|
||||
gizmoNode->attachObject(manualObj.get());
|
||||
manualObj->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY);
|
||||
ECS::get().set<ECS::EditorGizmo>({ manualObj, gizmoNode });
|
||||
#if 0
|
||||
ECS::get()
|
||||
.system<const ECS::EngineData, ECS::Camera,
|
||||
const ECS::Input>("UpdateEditorCamera")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([&](const ECS::EngineData &eng,
|
||||
ECS::Camera &camera,
|
||||
const ECS::Input &input) {
|
||||
if (!camera.configured) {
|
||||
// create a pivot at roughly the character's shoulder
|
||||
camera.mCameraPivot =
|
||||
eng.mScnMgr->getRootSceneNode()
|
||||
->createChildSceneNode();
|
||||
camera.mCameraGoal =
|
||||
camera.mCameraPivot
|
||||
->createChildSceneNode(
|
||||
Ogre::Vector3(
|
||||
0, 2,
|
||||
0));
|
||||
camera.mCameraNode->setPosition(
|
||||
camera.mCameraPivot
|
||||
->getPosition() +
|
||||
camera.mCameraGoal
|
||||
->getPosition());
|
||||
camera.mCameraGoal->lookAt(
|
||||
Ogre::Vector3(0, 0, -10),
|
||||
Ogre::Node::TS_PARENT);
|
||||
camera.mCameraPivot->setFixedYawAxis(
|
||||
true);
|
||||
camera.mCameraGoal->setFixedYawAxis(
|
||||
true);
|
||||
camera.mCameraNode->setFixedYawAxis(
|
||||
true);
|
||||
// our model is quite small, so reduce the clipping planes
|
||||
camera.mCamera->setNearClipDistance(
|
||||
0.1f);
|
||||
camera.mCamera->setFarClipDistance(700);
|
||||
|
||||
camera.mPivotPitch = 0;
|
||||
camera.configured = true;
|
||||
} else {
|
||||
// place the camera pivot roughly at the character's shoulder
|
||||
#if 0
|
||||
camera.mCameraPivot->setPosition(
|
||||
ch.mBodyNode->getPosition() +
|
||||
Ogre::Vector3::UNIT_Y * CAM_HEIGHT);
|
||||
#endif
|
||||
// move the camera smoothly to the goal
|
||||
Ogre::Vector3 goalOffset =
|
||||
camera.mCameraGoal
|
||||
->_getDerivedPosition() -
|
||||
camera.mCameraNode
|
||||
->getPosition();
|
||||
camera.mCameraNode->translate(
|
||||
goalOffset * eng.delta * 9.0f);
|
||||
// always look at the pivot
|
||||
#if 0
|
||||
camera.mCameraNode->lookAt(
|
||||
camera.mCameraPivot
|
||||
->_getDerivedPosition(),
|
||||
Ogre::Node::TS_PARENT);
|
||||
#endif
|
||||
camera.mCameraNode->_setDerivedOrientation(
|
||||
camera.mCameraGoal
|
||||
->_getDerivedOrientation());
|
||||
}
|
||||
if (input.control & 512 && input.mouse_moved) {
|
||||
mCameraPivot->yaw(
|
||||
Ogre::Radian(-input.mouse.x *
|
||||
3.0f * eng.delta));
|
||||
}
|
||||
});
|
||||
#endif
|
||||
}
|
||||
bool get_gui_active()
|
||||
{
|
||||
@@ -715,15 +661,9 @@ public:
|
||||
ECS::get().get_mut<ECS::GUI>().enabled = active;
|
||||
ECS::get().modified<ECS::GUI>();
|
||||
}
|
||||
Ogre::Camera *getCamera()
|
||||
{
|
||||
return mCamera;
|
||||
}
|
||||
flecs::entity getPlayer() const
|
||||
{
|
||||
flecs::entity player =
|
||||
ECS::get().lookup("ECS::CharacterModule::player");
|
||||
return player;
|
||||
return ECS::player;
|
||||
}
|
||||
void enableDbgDraw(bool enable)
|
||||
{
|
||||
@@ -789,7 +729,7 @@ int main()
|
||||
{
|
||||
App ctx;
|
||||
ctx.configure();
|
||||
ctx.enableDbgDraw(false);
|
||||
// ctx.enableDbgDraw(false);
|
||||
ctx.getRoot()->startRendering();
|
||||
ctx.setWindowGrab(false);
|
||||
ctx.closeApp();
|
||||
|
||||
@@ -4,17 +4,19 @@ find_package(OGRE REQUIRED COMPONENTS Bites Bullet Paging Terrain Overlay CONFIG
|
||||
find_package(Bullet REQUIRED)
|
||||
find_package(nlohmann_json REQUIRED)
|
||||
find_package(OgreProcedural REQUIRED CONFIG)
|
||||
add_subdirectory(items)
|
||||
add_library(GameData STATIC GameData.cpp CharacterModule.cpp WaterModule.cpp SunModule.cpp TerrainModule.cpp
|
||||
GUIModule.cpp LuaData.cpp WorldMapModule.cpp BoatModule.cpp EventTriggerModule.cpp
|
||||
GUIModule.cpp EditorGUIModule.cpp LuaData.cpp WorldMapModule.cpp BoatModule.cpp EventTriggerModule.cpp
|
||||
CharacterAnimationModule.cpp PhysicsModule.cpp EventModule.cpp CharacterManagerModule.cpp
|
||||
VehicleManagerModule.cpp AppModule.cpp StaticGeometryModule.cpp SmartObject.cpp SlotsModule.cpp goap.cpp)
|
||||
VehicleManagerModule.cpp AppModule.cpp StaticGeometryModule.cpp SmartObject.cpp SlotsModule.cpp
|
||||
PlayerActionModule.cpp CharacterAIModule.cpp goap.cpp)
|
||||
target_link_libraries(GameData PUBLIC
|
||||
lua
|
||||
flecs::flecs_static
|
||||
nlohmann_json::nlohmann_json
|
||||
OgreMain
|
||||
OgreBites
|
||||
OgrePaging OgreTerrain OgreOverlay OgreProcedural::OgreProcedural
|
||||
OgrePaging OgreTerrain OgreOverlay OgreProcedural::OgreProcedural items
|
||||
PRIVATE sceneloader world-build physics editor)
|
||||
target_include_directories(GameData PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${BULLET_INCLUDE_DIR} ../luaaa)
|
||||
target_include_directories(GameData PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${BULLET_INCLUDE_DIR} ../luaaa ../aitoolkit/include)
|
||||
target_compile_definitions(GameData PRIVATE FLECS_CPP_NO_AUTO_REGISTRATION)
|
||||
|
||||
887
src/gamedata/CharacterAIModule.cpp
Normal file
887
src/gamedata/CharacterAIModule.cpp
Normal file
@@ -0,0 +1,887 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <Ogre.h>
|
||||
#include "goap.h"
|
||||
#include "CharacterManagerModule.h"
|
||||
#include "PlayerActionModule.h"
|
||||
#include "CharacterModule.h"
|
||||
#include "items.h"
|
||||
#include "CharacterAIModule.h"
|
||||
namespace ECS
|
||||
{
|
||||
class ActionNodeActions {
|
||||
struct WalkToAction : public goap::BaseAction<Blackboard> {
|
||||
int node;
|
||||
WalkToAction(int node, int cost)
|
||||
: goap::BaseAction<Blackboard>(
|
||||
"WalkTo(" +
|
||||
Ogre::StringConverter::toString(
|
||||
node) +
|
||||
")",
|
||||
{ { { "at_object", 0 } } },
|
||||
{ { { "at_object", 1 } } }, cost)
|
||||
, node(node)
|
||||
{
|
||||
}
|
||||
bool can_run(const Blackboard &state,
|
||||
bool debug = false) override
|
||||
{
|
||||
return (state.distance_to(prereq) == 0);
|
||||
}
|
||||
void _plan_effects(Blackboard &state) override
|
||||
{
|
||||
const ActionNodeList &alist =
|
||||
ECS::get<ActionNodeList>();
|
||||
state.apply(effects);
|
||||
// node position can change in case of character
|
||||
const Ogre::Vector3 &nodePosition =
|
||||
alist.dynamicNodes[node].position;
|
||||
state.setPosition(nodePosition);
|
||||
}
|
||||
int get_cost(const Blackboard &bb) const override
|
||||
{
|
||||
int ret = m_cost;
|
||||
if (!bb.town.is_valid()) {
|
||||
ret += 1000000;
|
||||
goto out;
|
||||
}
|
||||
|
||||
{
|
||||
const ActionNodeList &alist =
|
||||
ECS::get<ActionNodeList>();
|
||||
// const TownNPCs &npcs = bb.town.get<TownNPCs>();
|
||||
// const TownAI &ai = bb.town.get<TownAI>();
|
||||
const Ogre::Vector3 &nodePosition =
|
||||
alist.dynamicNodes[node].position;
|
||||
// flecs::entity e = npcs.npcs.at(bb.index).e;
|
||||
// bool validActive = e.is_valid() &&
|
||||
// e.has<CharacterBase>();
|
||||
const Ogre::Vector3 &npcPosition =
|
||||
bb.getPosition();
|
||||
float dist = npcPosition.squaredDistance(
|
||||
nodePosition);
|
||||
ret += (int)Ogre::Math::Ceil(dist);
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
struct RunActionNode : public goap::BaseAction<Blackboard> {
|
||||
int node;
|
||||
float radius;
|
||||
Ogre::String action;
|
||||
RunActionNode(int node, const Ogre::String &action,
|
||||
const Blackboard &prereq,
|
||||
const Blackboard &effects, int cost)
|
||||
: goap::BaseAction<Blackboard>(
|
||||
"Use(" + action + "," +
|
||||
Ogre::StringConverter::toString(
|
||||
node) +
|
||||
")",
|
||||
prereq, effects, cost)
|
||||
, node(node)
|
||||
, action(action)
|
||||
{
|
||||
const ActionNodeList &alist =
|
||||
ECS::get<ActionNodeList>();
|
||||
radius = alist.dynamicNodes[node].radius;
|
||||
}
|
||||
bool can_run(const Blackboard &state,
|
||||
bool debug = false) override
|
||||
{
|
||||
bool pre = (state.distance_to(prereq) == 0);
|
||||
if (!pre)
|
||||
return pre;
|
||||
const ActionNodeList &alist =
|
||||
ECS::get<ActionNodeList>();
|
||||
const Ogre::Vector3 &nodePosition =
|
||||
alist.dynamicNodes[node].position;
|
||||
const Ogre::Vector3 &npcPosition = state.getPosition();
|
||||
return (npcPosition.squaredDistance(nodePosition) <
|
||||
radius * radius);
|
||||
}
|
||||
};
|
||||
std::vector<goap::BaseAction<Blackboard> *> m_actions;
|
||||
|
||||
public:
|
||||
ActionNodeActions(int node, const Blackboard &prereq, int cost)
|
||||
{
|
||||
OgreAssert(
|
||||
node < ECS::get<ActionNodeList>().dynamicNodes.size(),
|
||||
"bad node " + Ogre::StringConverter::toString(node));
|
||||
m_actions.push_back(OGRE_NEW WalkToAction(node, 10000));
|
||||
nlohmann::json jactionPrereq = nlohmann::json::object();
|
||||
nlohmann::json jactionEffect = nlohmann::json::object();
|
||||
jactionPrereq["at_object"] = 1;
|
||||
const nlohmann::json props =
|
||||
ECS::get<ActionNodeList>().dynamicNodes[node].props;
|
||||
OgreAssert(!props.is_null(),
|
||||
"bad node " + Ogre::StringConverter::toString(node));
|
||||
Ogre::String prefix = "goap_prereq_";
|
||||
for (auto it = props.begin(); it != props.end(); it++) {
|
||||
if (it.key().substr(0, prefix.length()) == prefix) {
|
||||
Ogre::String key =
|
||||
it.key().substr(prefix.length());
|
||||
jactionPrereq[key] = it.value();
|
||||
}
|
||||
}
|
||||
Ogre::String prefix2 = "goap_effect_";
|
||||
for (auto it = props.begin(); it != props.end(); it++) {
|
||||
if (it.key().substr(0, prefix2.length()) == prefix2) {
|
||||
Ogre::String key =
|
||||
it.key().substr(prefix2.length());
|
||||
jactionPrereq[key] = it.value();
|
||||
}
|
||||
}
|
||||
OgreAssert(props.find("action") != props.end(),
|
||||
"bad action" + props.dump(4));
|
||||
const Ogre::String &action =
|
||||
props["action"].get<Ogre::String>();
|
||||
Ogre::String effectName = "";
|
||||
#if 0
|
||||
if (action == "sit") {
|
||||
const Ogre::String &nodeName =
|
||||
props["name"].get<Ogre::String>();
|
||||
effectName = "is_" + nodeName + "_seated";
|
||||
} else if (action == "use") {
|
||||
const Ogre::String &nodeName =
|
||||
props["name"].get<Ogre::String>();
|
||||
effectName = "is_" + nodeName + "_used";
|
||||
}
|
||||
#endif
|
||||
// const Ogre::String &nodeName =
|
||||
// props["name"].get<Ogre::String>();
|
||||
Ogre::String nodeID = Ogre::StringConverter::toString(node);
|
||||
effectName = "is_used";
|
||||
if (effectName.length() > 0) {
|
||||
jactionPrereq[effectName] = 0;
|
||||
jactionEffect[effectName] = 1;
|
||||
}
|
||||
// FIXME: add this to Blender goap_prereq_ and goap_effect_ variables
|
||||
if (action == "sit") {
|
||||
jactionPrereq["is_seated"] = 0;
|
||||
jactionEffect["is_seated"] = 1;
|
||||
}
|
||||
Blackboard actionPrereq({ jactionPrereq });
|
||||
Blackboard actionEffect({ jactionEffect });
|
||||
if (!prereq.stats.is_null())
|
||||
actionPrereq.apply(prereq);
|
||||
m_actions.push_back(OGRE_NEW RunActionNode(
|
||||
node, action, actionPrereq, actionEffect, cost));
|
||||
if (effectName == "") {
|
||||
std::cout << props.dump(4) << std::endl;
|
||||
std::cout << "Prereq" << std::endl;
|
||||
std::cout << actionPrereq.stats.dump(4) << std::endl;
|
||||
std::cout << "Effect" << std::endl;
|
||||
std::cout << actionEffect.stats.dump(4) << std::endl;
|
||||
OgreAssert(false, "action");
|
||||
}
|
||||
}
|
||||
std::vector<goap::BaseAction<Blackboard> *> getActions() const
|
||||
{
|
||||
return m_actions;
|
||||
}
|
||||
};
|
||||
CharacterAIModule::CharacterAIModule(flecs::world &ecs)
|
||||
{
|
||||
static std::mutex ecs_mutex;
|
||||
ecs.module<CharacterAIModule>();
|
||||
ecs.import <CharacterManagerModule>();
|
||||
ecs.component<Blackboard>();
|
||||
ecs.component<TownAI>().on_add([](flecs::entity e, TownAI &ai) {
|
||||
std::lock_guard<std::mutex> lock(ecs_mutex);
|
||||
ai.mutex = std::make_shared<std::mutex>();
|
||||
std::lock_guard<std::mutex> lock2(*ai.mutex);
|
||||
ai.goals.push_back(
|
||||
{ "HealthGoal",
|
||||
{ nlohmann::json::object({ { "healthy", 1 } }) } });
|
||||
ai.goals.push_back(
|
||||
{ "NotHungryGoal", { { { "hungry", 0 } } } });
|
||||
ai.goals.push_back(
|
||||
{ "NotThirstyGoal", { { { "thirsty", 0 } } } });
|
||||
ai.goals.push_back(
|
||||
{ "SatisfyToiletNeedGoal", { { { "toilet", 0 } } } });
|
||||
struct ActionData {
|
||||
Ogre::String name;
|
||||
Blackboard prereq;
|
||||
Blackboard effects;
|
||||
int cost;
|
||||
};
|
||||
struct ActionData actionData[] = {
|
||||
#if 0
|
||||
{ "WalkTo",
|
||||
{ { { "at_object", 0 } } },
|
||||
{ { { "at_object", 1 } } },
|
||||
1000 },
|
||||
{ "Sit#",
|
||||
{ { { "at_object", 1 }, { "is_seated", 0 } } },
|
||||
{ { { "is_seated", 1 } } },
|
||||
10 },
|
||||
#endif
|
||||
{ "EatFoodSeated",
|
||||
{ { { "have_food", 1 },
|
||||
{ "is_seated", 1 },
|
||||
{ "hungry", 1 } } },
|
||||
{ { { "healthy", 1 }, { "hungry", 0 } } },
|
||||
10 },
|
||||
{ "EatFood",
|
||||
{ { { "have_food", 1 }, { "hungry", 1 } } },
|
||||
{ { { "healthy", 1 }, { "hungry", 0 } } },
|
||||
2000 },
|
||||
{ "DrinkWaterSeated",
|
||||
{ { { "have_water", 1 },
|
||||
{ "is_seated", 1 },
|
||||
{ "thirsty", 1 } } },
|
||||
{ { { "thirsty", 0 } } },
|
||||
10 },
|
||||
#if 0
|
||||
{ "DrinkWater",
|
||||
{ { { "have_water", 1 }, { "thirsty", 1 } } },
|
||||
{ { { "thirsty", 0 } } },
|
||||
2000 },
|
||||
#endif
|
||||
{ "EatMedicine",
|
||||
{ { { "have_medicine", 1 }, { "healty", 0 } } },
|
||||
{ { { "healthy", 1 } } },
|
||||
100 },
|
||||
{ "UseToilet",
|
||||
{ { { "toilet", 1 } } },
|
||||
{ { { "toilet", 0 } } },
|
||||
100 },
|
||||
{ "GetFood",
|
||||
{ { { "have_food", 0 } } },
|
||||
{ { { "have_food", 1 } } },
|
||||
1000 },
|
||||
{ "GetWater",
|
||||
{ { { "have_water", 0 } } },
|
||||
{ { { "have_water", 1 } } },
|
||||
1000 },
|
||||
{ "GetMedicine",
|
||||
{ { { "have_medicine", 0 } } },
|
||||
{ { { "have_medicine", 1 } } },
|
||||
1000 },
|
||||
};
|
||||
for (const auto &adata : actionData)
|
||||
ai.actions.push_back(
|
||||
OGRE_NEW goap::BaseAction<Blackboard>(
|
||||
adata.name, adata.prereq, adata.effects,
|
||||
adata.cost));
|
||||
ai.planner = std::make_shared<goap::BasePlanner<
|
||||
Blackboard, goap::BaseAction<Blackboard> > >();
|
||||
});
|
||||
ecs.system<TownAI, TownNPCs>("CreateBlackboards")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([this](flecs::entity town, TownAI &ai,
|
||||
const TownNPCs &npcs) {
|
||||
Ogre::Root::getSingleton().getWorkQueue()->addTask(
|
||||
[this, town, npcs, &ai]() {
|
||||
Ogre::Root::getSingleton()
|
||||
.getWorkQueue()
|
||||
->addMainThreadTask([this, town,
|
||||
npcs,
|
||||
&ai]() {
|
||||
std::lock_guard<
|
||||
std::mutex>
|
||||
lock(ecs_mutex);
|
||||
createBlackboards(
|
||||
town, npcs, ai);
|
||||
});
|
||||
});
|
||||
});
|
||||
ecs.system<ActionNodeList, TownAI, TownNPCs>("UpdateDynamicActions")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([](flecs::entity e, ActionNodeList &alist, TownAI &ai,
|
||||
TownNPCs &npcs) {
|
||||
std::lock_guard<std::mutex> lock(ecs_mutex);
|
||||
if (ai.nodeActions.size() > 0)
|
||||
return;
|
||||
if (alist.dynamicNodes.size() == 0)
|
||||
ECS::get_mut<ActionNodeList>()
|
||||
.updateDynamicNodes();
|
||||
OgreAssert(alist.dynamicNodes.size() > 0,
|
||||
"bad dynamic nodes");
|
||||
int nodeIndex;
|
||||
for (nodeIndex = 0;
|
||||
nodeIndex < alist.dynamicNodes.size();
|
||||
nodeIndex++) {
|
||||
ActionNodeActions aactions(
|
||||
nodeIndex,
|
||||
Blackboard(
|
||||
{ nlohmann::json::object() }),
|
||||
10);
|
||||
ai.nodeActions[nodeIndex] =
|
||||
aactions.getActions();
|
||||
OgreAssert(ai.nodeActions[nodeIndex].size() > 0,
|
||||
"bad action count");
|
||||
}
|
||||
OgreAssert(ai.nodeActions.size() > 0,
|
||||
"no dynamic actions?");
|
||||
});
|
||||
ecs.system<ActionNodeList, TownAI, TownNPCs>("UpdateDynamicNodes")
|
||||
.kind(flecs::OnUpdate)
|
||||
.interval(0.1f)
|
||||
.each([this](flecs::entity town, ActionNodeList &alist,
|
||||
TownAI &ai, TownNPCs &npcs) {
|
||||
std::lock_guard<std::mutex> lock(ecs_mutex);
|
||||
ECS::get_mut<ActionNodeList>().updateDynamicNodes();
|
||||
});
|
||||
ecs.system<TownNPCs>("UpdateNPCPositions")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([](flecs::entity e, TownNPCs &npcs) {
|
||||
for (auto it = npcs.npcs.begin(); it != npcs.npcs.end();
|
||||
it++) {
|
||||
auto &npc = npcs.npcs.at(it->first);
|
||||
if (npc.e.is_valid() &&
|
||||
npc.e.has<CharacterBase>())
|
||||
npc.position =
|
||||
npc.e.get<CharacterBase>()
|
||||
.mBodyNode
|
||||
->_getDerivedPosition();
|
||||
}
|
||||
});
|
||||
ecs.system<ActionNodeList, TownAI, TownNPCs>("UpdateBlackboards")
|
||||
.kind(flecs::OnUpdate)
|
||||
.interval(0.1f)
|
||||
.each([this](flecs::entity town, ActionNodeList &alist,
|
||||
TownAI &ai, const TownNPCs &npcs) {
|
||||
for (auto it = npcs.npcs.begin(); it != npcs.npcs.end();
|
||||
it++) {
|
||||
const auto &npc = npcs.npcs.at(it->first);
|
||||
if (ai.blackboards.find(it->first) ==
|
||||
ai.blackboards.end())
|
||||
continue;
|
||||
ai.blackboards.at(it->first).setPosition(
|
||||
npc.position);
|
||||
}
|
||||
Ogre::Root::getSingleton().getWorkQueue()->addTask(
|
||||
[this, town, &alist, npcs, &ai]() {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(
|
||||
ecs_mutex);
|
||||
|
||||
alist.build();
|
||||
updateBlackboardsBits(
|
||||
town, alist, npcs, ai);
|
||||
updateBlackboards(town, alist,
|
||||
npcs, ai);
|
||||
}
|
||||
Ogre::Root::getSingleton()
|
||||
.getWorkQueue()
|
||||
->addMainThreadTask([this, town,
|
||||
&alist]() {
|
||||
town.modified<TownAI>();
|
||||
ECS::modified<
|
||||
ActionNodeList>();
|
||||
});
|
||||
});
|
||||
});
|
||||
ecs.system<TownAI, TownNPCs>("PlanAI")
|
||||
.kind(flecs::OnUpdate)
|
||||
.interval(1.0f)
|
||||
.each([&](flecs::entity town, TownAI &ai,
|
||||
const TownNPCs &npcs) {
|
||||
Ogre::Root::getSingleton().getWorkQueue()->addTask(
|
||||
[this, town, npcs, &ai]() {
|
||||
std::lock_guard<std::mutex> lock(
|
||||
ecs_mutex);
|
||||
buildPlans(town, npcs, ai);
|
||||
Ogre::Root::getSingleton()
|
||||
.getWorkQueue()
|
||||
->addMainThreadTask([this,
|
||||
town]() {
|
||||
town.modified<TownAI>();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void CharacterAIModule::createAI(flecs::entity town)
|
||||
{
|
||||
town.add<TownAI>();
|
||||
}
|
||||
|
||||
void CharacterAIModule::buildPlans(flecs::entity town, const TownNPCs &npcs,
|
||||
TownAI &ai)
|
||||
{
|
||||
OgreAssert(town.is_valid(), "Bad town entity");
|
||||
std::lock_guard<std::mutex> lock(*ai.mutex);
|
||||
for (auto it = npcs.npcs.begin(); it != npcs.npcs.end(); it++) {
|
||||
if (ai.blackboards.find(it->first) == ai.blackboards.end())
|
||||
continue;
|
||||
auto &bb = ai.blackboards.at(it->first);
|
||||
/* if there are plans, skip until these get discarded */
|
||||
if (ai.plans.find(it->first) != ai.plans.end() &&
|
||||
ai.plans.at(it->first).size() > 0)
|
||||
continue;
|
||||
ai.plans[it->first] = {};
|
||||
for (const auto &goal : ai.goals) {
|
||||
if (goal.is_reached(bb))
|
||||
continue;
|
||||
#if 0
|
||||
std::cout << "blackboard: "
|
||||
<< bb.stats.dump(4)
|
||||
<< std::endl;
|
||||
std::cout << "goal: "
|
||||
<< goal.goal.stats.dump(4)
|
||||
<< std::endl;
|
||||
#endif
|
||||
std::vector<goap::BaseAction<Blackboard> *> path;
|
||||
int actionCount =
|
||||
ai.blackboards.at(it->first).getActionsCount();
|
||||
auto actionsData =
|
||||
ai.blackboards.at(it->first).getActionsData();
|
||||
path.resize(actionCount * actionCount);
|
||||
int path_length = ai.planner->plan(
|
||||
bb, goal, actionsData, actionCount, path.data(),
|
||||
path.size());
|
||||
#if 0
|
||||
std::cout << "Actions: " << std::endl;
|
||||
for (auto &action : actions) {
|
||||
std::cout << "name: "
|
||||
<< action->get_name()
|
||||
<< std::endl;
|
||||
std::cout
|
||||
<< "\tprereq:\n"
|
||||
<< action->prereq.stats
|
||||
.dump(4)
|
||||
<< std::endl;
|
||||
std::cout
|
||||
<< "\teffects:\n"
|
||||
<< action->effects.stats
|
||||
.dump(4)
|
||||
<< std::endl;
|
||||
}
|
||||
#endif
|
||||
#if 1
|
||||
std::cout << bb.index << " ";
|
||||
std::cout << "Goal: " << goal.get_name();
|
||||
std::cout << std::endl;
|
||||
std::cout << "Path: ";
|
||||
int count = 0;
|
||||
if (path_length < 0) {
|
||||
std::cout << "Bad plan " << path_length
|
||||
<< std::endl;
|
||||
}
|
||||
for (auto &action : path) {
|
||||
if (count >= path_length)
|
||||
break;
|
||||
OgreAssert(action, "No action");
|
||||
std::cout << action->get_name();
|
||||
if (count < path_length - 1)
|
||||
std::cout << ", ";
|
||||
count++;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
std::cout << path_length << std::endl;
|
||||
// OgreAssert(path_length == 0,
|
||||
// "planning");
|
||||
#endif
|
||||
if (path_length > 0) {
|
||||
struct TownAI::Plan plan;
|
||||
plan.goal = &goal;
|
||||
plan.plan.insert(plan.plan.end(), path.begin(),
|
||||
path.begin() + path_length);
|
||||
ai.plans[it->first].push_back(plan);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterAIModule::createBlackboards(flecs::entity town,
|
||||
const TownNPCs &npcs, TownAI &ai)
|
||||
{
|
||||
OgreAssert(town.is_valid(), "Bad town entity");
|
||||
std::lock_guard<std::mutex> lock(*ai.mutex);
|
||||
for (auto it = npcs.npcs.begin(); it != npcs.npcs.end(); it++) {
|
||||
if (ai.blackboards.find(it->first) == ai.blackboards.end()) {
|
||||
int strength = 10;
|
||||
int dexterity = 10;
|
||||
int health = 100;
|
||||
int stamina = 100;
|
||||
int sex = it->second.props["sex"].get<int>();
|
||||
if (sex == 0) { // male
|
||||
strength += 10;
|
||||
dexterity += 10;
|
||||
}
|
||||
nlohmann::json bb;
|
||||
bb["strength"] = strength;
|
||||
bb["dexterity"] = dexterity;
|
||||
bb["health"] = health;
|
||||
bb["stamina"] = stamina;
|
||||
|
||||
bb["needs_hunger"] = 0;
|
||||
bb["needs_thirst"] = 0;
|
||||
bb["needs_toilet"] = 0;
|
||||
|
||||
bb["have_water"] = 0;
|
||||
bb["have_food"] = 0;
|
||||
bb["have_medicine"] = 0;
|
||||
bb["at_object"] = 0;
|
||||
ai.blackboards[it->first] = Blackboard(bb);
|
||||
ai.blackboards[it->first].index = it->first;
|
||||
ai.blackboards.at(it->first).town = town;
|
||||
// FIXME: do this once
|
||||
ai.blackboards.at(it->first).actionRefResize(0);
|
||||
ai.blackboards.at(it->first).actionRefAddActions(
|
||||
ai.actions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterAIModule::updateBlackboardsBits(flecs::entity town,
|
||||
ActionNodeList &alist,
|
||||
const TownNPCs &npcs, TownAI &ai)
|
||||
{
|
||||
OgreAssert(town.is_valid(), "Bad town entity");
|
||||
std::lock_guard<std::mutex> lock(*ai.mutex);
|
||||
struct UpdateBit {
|
||||
Ogre::String checkValue;
|
||||
int recover;
|
||||
int minValue;
|
||||
int maxValue;
|
||||
int threshold;
|
||||
Ogre::String writeValue;
|
||||
};
|
||||
struct UpdateBit updateBits[] = {
|
||||
{ "health", 0, 0, 100, 20, "healthy" },
|
||||
{ "needs_hunger", 1, 0, 10000, 2000, "hungry" },
|
||||
{ "needs_thirst", 1, 0, 10000, 1000, "thirsty" },
|
||||
{ "needs_toilet", 1, 0, 10000, 1500, "toilet" }
|
||||
};
|
||||
for (auto it = npcs.npcs.begin(); it != npcs.npcs.end(); it++) {
|
||||
if (ai.blackboards.find(it->first) == ai.blackboards.end())
|
||||
continue;
|
||||
auto &stats = ai.blackboards.at(it->first).stats;
|
||||
auto &object = ai.blackboards.at(it->first).object;
|
||||
ai.blackboards.at(it->first).index = it->first;
|
||||
ai.blackboards.at(it->first).town = town;
|
||||
for (const auto &bits : updateBits) {
|
||||
int value = stats[bits.checkValue].get<int>();
|
||||
int maxValue = bits.maxValue;
|
||||
int minValue = bits.minValue;
|
||||
int threshold = bits.threshold;
|
||||
if (it->second.props.find(bits.checkValue + "_max") !=
|
||||
it->second.props.end())
|
||||
maxValue =
|
||||
it->second
|
||||
.props[bits.checkValue + "_max"]
|
||||
.get<int>();
|
||||
if (it->second.props.find(bits.checkValue +
|
||||
"_threshold") !=
|
||||
it->second.props.end())
|
||||
threshold = it->second
|
||||
.props[bits.checkValue +
|
||||
"_threshold"]
|
||||
.get<int>();
|
||||
value += bits.recover;
|
||||
if (value > maxValue)
|
||||
value = maxValue;
|
||||
if (value >= threshold)
|
||||
stats[bits.writeValue] = 1;
|
||||
else
|
||||
stats[bits.writeValue] = 0;
|
||||
if (value < bits.minValue)
|
||||
value = bits.minValue;
|
||||
stats[bits.checkValue] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterAIModule::updateBlackboards(flecs::entity town,
|
||||
const ActionNodeList &alist,
|
||||
const TownNPCs &npcs, TownAI &ai)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(*ai.mutex);
|
||||
OgreAssert(town.is_valid(), "Bad town entity");
|
||||
for (auto it = npcs.npcs.begin(); it != npcs.npcs.end(); it++) {
|
||||
if (ai.blackboards.find(it->first) == ai.blackboards.end())
|
||||
continue;
|
||||
auto &stats = ai.blackboards.at(it->first).stats;
|
||||
auto &object = ai.blackboards.at(it->first).object;
|
||||
ai.blackboards.at(it->first).index = it->first;
|
||||
ai.blackboards.at(it->first).town = town;
|
||||
auto &bb = ai.blackboards.at(it->first);
|
||||
bb.query_ai();
|
||||
|
||||
#if 0
|
||||
OgreAssert(nodeActionCount > 0 ||
|
||||
points.size() == 0,
|
||||
"no node actions and no points");
|
||||
if (nodeActionCount == 0) {
|
||||
std::cout << "nodes:"
|
||||
<< alist.nodes.size() << " "
|
||||
<< alist.dynamicNodes.size()
|
||||
<< std::endl;
|
||||
std::cout << "points: " << points.size()
|
||||
<< std::endl;
|
||||
std::cout << "position: " << position
|
||||
<< std::endl;
|
||||
}
|
||||
OgreAssert(nodeActionCount > 0,
|
||||
"no node actions");
|
||||
#endif
|
||||
bb.fixupBooleanKeys();
|
||||
}
|
||||
}
|
||||
|
||||
void Blackboard::_actionRefResize(int count)
|
||||
{
|
||||
if (count >= actionRef.size()) {
|
||||
int allocate = count;
|
||||
if (allocate < 1000)
|
||||
allocate = 1000;
|
||||
actionRef.resize(allocate);
|
||||
}
|
||||
OgreAssert(count < actionRef.size(), "out of memory");
|
||||
actionRefCount = count;
|
||||
actionRefPtr = count;
|
||||
}
|
||||
|
||||
Blackboard::Blackboard()
|
||||
: stats(nlohmann::json::object())
|
||||
, object(-1)
|
||||
, index(-1)
|
||||
, actionRefCount(0)
|
||||
, mutex(std::make_shared<std::mutex>())
|
||||
{
|
||||
}
|
||||
|
||||
Blackboard::Blackboard(const nlohmann::json &stats)
|
||||
: Blackboard()
|
||||
{
|
||||
this->stats = stats;
|
||||
}
|
||||
|
||||
bool Blackboard::operator==(const Blackboard &other) const
|
||||
{
|
||||
return is_satisfied_by(this->stats, other.stats);
|
||||
}
|
||||
|
||||
bool Blackboard::operator!=(const Blackboard &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
void Blackboard::apply(const Blackboard &other)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(*mutex);
|
||||
stats.update(other.stats);
|
||||
}
|
||||
|
||||
Ogre::String Blackboard::dumpActions()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(*mutex);
|
||||
Ogre::String ret;
|
||||
ret += "Actions:\n";
|
||||
int count = 0;
|
||||
for (count = 0; count < actionRefCount; count++) {
|
||||
auto &action = actionRef[count];
|
||||
ret += "name: " + action->get_name() + "\n";
|
||||
ret += "\tprereq:\n" + action->prereq.stats.dump(4) + "\n";
|
||||
ret += "\teffects:\n" + action->effects.stats.dump(4) + "\n";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Blackboard::printActions()
|
||||
{
|
||||
std::cout << dumpActions() << std::endl;
|
||||
}
|
||||
|
||||
void Blackboard::fixupBooleanKeys()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(*mutex);
|
||||
int count;
|
||||
for (count = 0; count < actionRefCount; count++) {
|
||||
auto &action = actionRef[count];
|
||||
const nlohmann::json &prereq = action->prereq.stats;
|
||||
const nlohmann::json &effects = action->effects.stats;
|
||||
for (auto it = prereq.begin(); it != prereq.end(); it++)
|
||||
if (stats.find(it.key()) == stats.end())
|
||||
stats[it.key()] = 0;
|
||||
for (auto it = effects.begin(); it != effects.end(); it++)
|
||||
if (stats.find(it.key()) == stats.end())
|
||||
stats[it.key()] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Blackboard::actionRefResize(int count)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(*mutex);
|
||||
_actionRefResize(count);
|
||||
}
|
||||
|
||||
void Blackboard::actionRefAddAction(goap::BaseAction<Blackboard> *action)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(*mutex);
|
||||
if (actionRef.size() <= actionRefCount + 16)
|
||||
actionRef.resize(actionRefCount + 32);
|
||||
OgreAssert(actionRefPtr < actionRef.size(), "out of memory");
|
||||
OgreAssert(action, "bad action");
|
||||
actionRef[actionRefPtr++] = action;
|
||||
actionRefCount++;
|
||||
}
|
||||
|
||||
void Blackboard::actionRefAddActions(
|
||||
const std::vector<goap::BaseAction<Blackboard> *> &actions)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(*mutex);
|
||||
_actionRefAddActions(actions);
|
||||
}
|
||||
|
||||
void Blackboard::_actionRefAddActions(
|
||||
const std::vector<goap::BaseAction<Blackboard> *> &actions)
|
||||
{
|
||||
if (actionRef.size() <= actionRefCount + actions.size())
|
||||
actionRef.resize(actionRefCount + actions.size() * 2);
|
||||
for (const auto &action : actions) {
|
||||
OgreAssert(actionRefPtr < actionRef.size(), "out of memory");
|
||||
OgreAssert(action, "bad action");
|
||||
actionRef[actionRefPtr++] = action;
|
||||
actionRefCount++;
|
||||
}
|
||||
}
|
||||
|
||||
void Blackboard::actionRefAddActions(goap::BaseAction<Blackboard> **actions,
|
||||
int count)
|
||||
{
|
||||
int i;
|
||||
std::lock_guard<std::mutex> lock(*mutex);
|
||||
if (actionRef.size() <= actionRefCount + count)
|
||||
actionRef.resize(actionRefCount + count * 2);
|
||||
for (i = 0; i < count; i++) {
|
||||
OgreAssert(actionRefPtr < actionRef.size(), "out of memory");
|
||||
OgreAssert(actions[i], "bad action");
|
||||
actionRef[actionRefPtr++] = actions[i];
|
||||
actionRefCount++;
|
||||
}
|
||||
}
|
||||
|
||||
struct ComparePair {
|
||||
const nlohmann::json ¤t;
|
||||
const nlohmann::json ⌖
|
||||
};
|
||||
|
||||
bool Blackboard::is_satisfied_by(const nlohmann::json ¤t,
|
||||
const nlohmann::json &target, float epsilon)
|
||||
{
|
||||
std::deque<ComparePair> queue;
|
||||
queue.push_back({ current, target });
|
||||
while (!queue.empty()) {
|
||||
ComparePair pair = queue.front();
|
||||
queue.pop_front();
|
||||
const nlohmann::json &curr = pair.current;
|
||||
const nlohmann::json &tgt = pair.target;
|
||||
if (curr.type() != tgt.type() &&
|
||||
!(curr.is_number() && tgt.is_number()))
|
||||
return false;
|
||||
if (tgt.is_object())
|
||||
for (auto it = tgt.begin(); it != tgt.end(); ++it) {
|
||||
auto found = curr.find(it.key());
|
||||
if (found == curr.end())
|
||||
return false;
|
||||
queue.push_back({ *found, it.value() });
|
||||
}
|
||||
else if (tgt.is_array()) {
|
||||
if (curr.size() != tgt.size())
|
||||
return false;
|
||||
for (int i = 0; i < tgt.size(); ++i)
|
||||
queue.push_back({ curr[i], tgt[i] });
|
||||
} else if (tgt.is_number_float() || curr.is_number_float()) {
|
||||
if (std::abs(curr.get<float>() - tgt.get<float>()) >=
|
||||
epsilon)
|
||||
return false;
|
||||
} else if (curr != tgt)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int Blackboard::distance_to(const Blackboard &goal) const
|
||||
{
|
||||
int distance = 0;
|
||||
|
||||
OgreAssert(goal.stats.is_object(),
|
||||
"Not an object:\n" + goal.stats.dump(4));
|
||||
for (auto it = goal.stats.begin(); it != goal.stats.end(); ++it) {
|
||||
const std::string &key = it.key();
|
||||
const auto &goalVal = it.value();
|
||||
|
||||
// If current state doesn't have the key, treat it as a maximum difference
|
||||
if (stats.find(key) == stats.end()) {
|
||||
distance += 100; // Example: High cost for missing state
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto ¤tVal = stats[key];
|
||||
|
||||
if (goalVal.is_number() && currentVal.is_number()) {
|
||||
// Add numerical difference
|
||||
distance += std::abs(goalVal.get<float>() -
|
||||
currentVal.get<float>());
|
||||
} else {
|
||||
// Check non-numeric equality
|
||||
if (goalVal != currentVal) {
|
||||
distance += 1; // Penalty for mismatch
|
||||
}
|
||||
}
|
||||
}
|
||||
return distance;
|
||||
}
|
||||
|
||||
void Blackboard::setPosition(const Ogre::Vector3 &position)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(*mutex);
|
||||
this->position = position;
|
||||
}
|
||||
|
||||
void Blackboard::query_ai()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(*mutex);
|
||||
TownAI &ai = town.get_mut<TownAI>();
|
||||
const TownNPCs &npcs = town.get<TownNPCs>();
|
||||
const float distance = 10000.0f;
|
||||
Ogre::Vector3 position(0, 0, 0);
|
||||
if (npcs.npcs.at(index).e.is_valid())
|
||||
position = npcs.npcs.at(index)
|
||||
.e.get<CharacterBase>()
|
||||
.mBodyNode->_getDerivedPosition();
|
||||
else
|
||||
from_json(npcs.npcs.at(index).props["position"], position);
|
||||
this->position = position;
|
||||
ActionNodeList &alist = ECS::get_mut<ActionNodeList>();
|
||||
alist.query_ai(position, distance, points, distances);
|
||||
_actionRefResize(ai.actions.size());
|
||||
int actionRefIndex = ai.actions.size();
|
||||
int nodeActionCount = 0;
|
||||
for (size_t point : points) {
|
||||
Ogre::Vector3 &p = alist.dynamicNodes[point].position;
|
||||
float radius = alist.dynamicNodes[point].radius;
|
||||
float distance = p.squaredDistance(position);
|
||||
if (object >= 0 && (size_t)object == point &&
|
||||
distance > radius * radius) {
|
||||
object = -1;
|
||||
stats["at_object"] = 0;
|
||||
} else if (object >= 0 && (size_t)object == point &&
|
||||
distance <= radius * radius)
|
||||
stats["at_object"] = 1;
|
||||
/* some nodes do not have usable actions */
|
||||
if (ai.nodeActions[point].size() > 0) {
|
||||
OgreAssert(ai.nodeActions[point].size() > 0,
|
||||
|
||||
"bad node actions count " +
|
||||
alist.dynamicNodes[point].props.dump(
|
||||
4));
|
||||
_actionRefAddActions(ai.nodeActions[point]);
|
||||
nodeActionCount += ai.nodeActions[point].size();
|
||||
}
|
||||
nlohmann::json nodes = nlohmann::json::array();
|
||||
nlohmann::json j = alist.dynamicNodes[point].props;
|
||||
j["global_position_x"] = p.x;
|
||||
j["global_position_y"] = p.y;
|
||||
j["global_position_z"] = p.z;
|
||||
nodes.push_back(j);
|
||||
stats["nodes"] = nodes;
|
||||
}
|
||||
}
|
||||
}
|
||||
101
src/gamedata/CharacterAIModule.h
Normal file
101
src/gamedata/CharacterAIModule.h
Normal file
@@ -0,0 +1,101 @@
|
||||
#ifndef CHARACTERAIMODULE_H
|
||||
#define CHARACTERAIMODULE_H
|
||||
#include <vector>
|
||||
#include <flecs.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "goap.h"
|
||||
|
||||
namespace ECS
|
||||
{
|
||||
|
||||
struct Blackboard {
|
||||
nlohmann::json stats;
|
||||
int object;
|
||||
int index;
|
||||
flecs::entity town;
|
||||
std::shared_ptr<std::mutex> mutex;
|
||||
|
||||
private:
|
||||
std::vector<goap::BaseAction<Blackboard> *> actionRef;
|
||||
int actionRefCount;
|
||||
int actionRefPtr;
|
||||
Ogre::Vector3 position;
|
||||
std::vector<size_t> points;
|
||||
std::vector<float> distances;
|
||||
void _actionRefResize(int count);
|
||||
void _actionRefAddActions(
|
||||
const std::vector<goap::BaseAction<Blackboard> *> &actions);
|
||||
|
||||
public:
|
||||
Blackboard();
|
||||
Blackboard(const nlohmann::json &stats);
|
||||
bool operator==(const Blackboard &other) const;
|
||||
bool operator!=(const Blackboard &other) const;
|
||||
void apply(const Blackboard &other);
|
||||
Ogre::String dumpActions();
|
||||
void printActions();
|
||||
void fixupBooleanKeys();
|
||||
void actionRefResize(int count);
|
||||
void actionRefAddAction(goap::BaseAction<Blackboard> *action);
|
||||
void actionRefAddActions(
|
||||
const std::vector<goap::BaseAction<Blackboard> *> &actions);
|
||||
void actionRefAddActions(goap::BaseAction<Blackboard> **actions,
|
||||
int count);
|
||||
goap::BaseAction<Blackboard> **getActionsData()
|
||||
{
|
||||
return actionRef.data();
|
||||
}
|
||||
int getActionsCount()
|
||||
{
|
||||
return actionRefCount;
|
||||
}
|
||||
|
||||
private:
|
||||
static bool is_satisfied_by(const nlohmann::json ¤t,
|
||||
const nlohmann::json &target,
|
||||
float epsilon = 1e-4);
|
||||
|
||||
public:
|
||||
int distance_to(const Blackboard &goal) const;
|
||||
void setPosition(const Ogre::Vector3 &position);
|
||||
const Ogre::Vector3 &getPosition() const
|
||||
{
|
||||
return position;
|
||||
}
|
||||
void query_ai();
|
||||
};
|
||||
|
||||
struct TownNPCs;
|
||||
struct ActionNodeList;
|
||||
|
||||
struct TownAI {
|
||||
std::shared_ptr<std::mutex> mutex;
|
||||
std::vector<goap::BasePlanner<Blackboard,
|
||||
goap::BaseAction<Blackboard> >::BaseGoal>
|
||||
goals;
|
||||
std::vector<goap::BaseAction<Blackboard> *> actions;
|
||||
std::shared_ptr<
|
||||
goap::BasePlanner<Blackboard, goap::BaseAction<Blackboard> > >
|
||||
planner;
|
||||
std::unordered_map<int, Blackboard> blackboards;
|
||||
struct Plan {
|
||||
const goap::BasePlanner<Blackboard,
|
||||
goap::BaseAction<Blackboard> >::BaseGoal *goal;
|
||||
std::vector<goap::BaseAction<Blackboard> *> plan;
|
||||
};
|
||||
std::unordered_map<int, std::vector<struct Plan> > plans;
|
||||
std::unordered_map<int, std::vector<goap::BaseAction<Blackboard> *> > nodeActions;
|
||||
};
|
||||
|
||||
struct CharacterAIModule {
|
||||
CharacterAIModule(flecs::world &ecs);
|
||||
void createAI(flecs::entity town);
|
||||
void buildPlans(flecs::entity town, const TownNPCs &npcs, TownAI &ai);
|
||||
void createBlackboards(flecs::entity town, const TownNPCs &npcs, TownAI &ai);
|
||||
void updateBlackboardsBits(flecs::entity town, ActionNodeList &alist, const TownNPCs &npcs, TownAI &ai);
|
||||
void updateBlackboards(flecs::entity town, const ActionNodeList &alist, const TownNPCs &npcs, TownAI &ai);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // CHARACTERAIMODULE_H
|
||||
@@ -22,38 +22,28 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([this](flecs::entity e, const CharacterBase &ch,
|
||||
AnimationControl &anim) {
|
||||
if (!anim.configured && ch.mSkeleton) {
|
||||
if (!anim.configured) {
|
||||
int i, j;
|
||||
e.set<EventData>({});
|
||||
ch.mSkeleton->setBlendMode(
|
||||
ch.mBodyEnt->getSkeleton()->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++) {
|
||||
Ogre::AnimationStateSet *animStateSet =
|
||||
ch.mBodyEnt->getAllAnimationStates();
|
||||
const Ogre::AnimationStateMap &animMap =
|
||||
animStateSet->getAnimationStates();
|
||||
anim.mAnimationSystem =
|
||||
new AnimationSystem(false);
|
||||
ch.mBodyEnt->getSkeleton()
|
||||
->getBone("Root")
|
||||
->removeAllChildren();
|
||||
for (auto it = animMap.begin();
|
||||
it != animMap.end(); it++) {
|
||||
Animation *animation = new Animation(
|
||||
ch.mSkeleton,
|
||||
ch.mBodyEnt->getAnimationState(
|
||||
animNames[i]),
|
||||
ch.mSkeleton->getAnimation(
|
||||
animNames[i]),
|
||||
ch.mBodyEnt->getSkeleton(),
|
||||
it->second,
|
||||
ch.mBodyEnt->getSkeleton()
|
||||
->getAnimation(
|
||||
it->first),
|
||||
e);
|
||||
#ifdef VDEBUG
|
||||
std::cout
|
||||
@@ -62,7 +52,7 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
|
||||
#endif
|
||||
animation->setLoop(true);
|
||||
anim.mAnimationSystem->add_animation(
|
||||
animNames[i], animation);
|
||||
it->first, animation);
|
||||
}
|
||||
anim.mAnimationSystem
|
||||
->builder()
|
||||
@@ -157,10 +147,39 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
|
||||
CharacterBase &ch, AnimationControl &anim) {
|
||||
float delta = eng.delta;
|
||||
// ch.mBoneMotion = Ogre::Vector3::ZERO;
|
||||
bool result = anim.mAnimationSystem->addTime(delta);
|
||||
if (!ch.mRootBone)
|
||||
if (!anim.mAnimationSystem)
|
||||
return;
|
||||
// The value we get is interpolated value. When result is true it is new step
|
||||
#if 0
|
||||
ch.mBodyEnt->getSkeleton()->getBone("Root")->reset();
|
||||
ch.mBodyEnt->getSkeleton()->getBone("Root")->setPosition(
|
||||
Ogre::Vector3::ZERO);
|
||||
#endif
|
||||
bool result = anim.mAnimationSystem->addTime(delta);
|
||||
Ogre::Vector3 rootMotion =
|
||||
anim.mAnimationSystem->getRootMotionDelta();
|
||||
ch.mBonePrevMotion = ch.mBoneMotion;
|
||||
ch.mBoneMotion = rootMotion;
|
||||
ch.mBodyEnt->_updateAnimation();
|
||||
ch.mBodyEnt->getSkeleton()->getBone("Root")->setPosition(
|
||||
Ogre::Vector3::ZERO);
|
||||
#if 0
|
||||
Ogre::Vector3 deltaMotion;
|
||||
ch.mBodyEnt->_updateAnimation();
|
||||
std::cout << "motion: " << ch.mBoneMotion << " "
|
||||
<< rootMotion << " " << ch.mBonePrevMotion
|
||||
<< std::endl;
|
||||
rootMotion = ch.mBodyEnt->getSkeleton()
|
||||
->getBone("Root")
|
||||
->getPosition();
|
||||
if (rootMotion.squaredLength() <
|
||||
ch.mBoneMotion.squaredLength())
|
||||
deltaMotion = rootMotion;
|
||||
else
|
||||
deltaMotion = rootMotion - ch.mBoneMotion;
|
||||
ch.mBonePrevMotion = ch.mBoneMotion;
|
||||
ch.mBoneMotion = deltaMotion;
|
||||
#endif
|
||||
// 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 *>(
|
||||
@@ -302,54 +321,7 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
|
||||
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")
|
||||
@@ -360,6 +332,8 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
|
||||
const CharacterBase &ch, AnimationControl &anim) {
|
||||
if (!anim.configured)
|
||||
return;
|
||||
if (!anim.mAnimationSystem)
|
||||
return;
|
||||
AnimationNodeStateMachine *state_machine =
|
||||
anim.mAnimationSystem
|
||||
->get<AnimationNodeStateMachine>(
|
||||
@@ -405,6 +379,8 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
|
||||
AnimationControl &anim) {
|
||||
if (!anim.configured)
|
||||
return;
|
||||
if (!anim.mAnimationSystem)
|
||||
return;
|
||||
AnimationNodeStateMachine *main_sm =
|
||||
anim.mAnimationSystem
|
||||
->get<AnimationNodeStateMachine>(
|
||||
@@ -568,15 +544,6 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
|
||||
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")
|
||||
@@ -616,87 +583,6 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
|
||||
<< "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)
|
||||
|
||||
@@ -9,22 +9,16 @@
|
||||
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
|
||||
{
|
||||
#if 0
|
||||
Ogre::TransformKeyFrame *vkf =
|
||||
static_cast<Ogre::TransformKeyFrame *>(kf);
|
||||
Ogre::KeyFrame *kf1, *kf2;
|
||||
@@ -36,14 +30,12 @@ public:
|
||||
k2 = static_cast<Ogre::TransformKeyFrame *>(kf2);
|
||||
Ogre::Vector3 translation;
|
||||
Ogre::Quaternion rotation;
|
||||
if (tm == 0.0f) {
|
||||
Ogre::Vector3 deltaMotion;
|
||||
Ogre::Vector3 prevMotion;
|
||||
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);
|
||||
@@ -55,14 +47,7 @@ public:
|
||||
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;
|
||||
@@ -70,7 +55,9 @@ public:
|
||||
e.get_mut<CharacterBase>().mBonePrevMotion = prevTranslation;
|
||||
e.modified<CharacterBase>();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
};
|
||||
struct AnimationTrigger;
|
||||
struct AnimationTriggerSubscriber {
|
||||
@@ -126,46 +113,66 @@ struct AnimationTrigger {
|
||||
{
|
||||
}
|
||||
};
|
||||
struct SetupTracks {
|
||||
Ogre::NodeAnimationTrack *mHipsTrack;
|
||||
Ogre::NodeAnimationTrack *mRootTrack;
|
||||
RootMotionListener *mListener;
|
||||
Ogre::Vector3 mRootTranslation;
|
||||
SetupTracks(flecs::entity e, Ogre::Skeleton *skeleton,
|
||||
Ogre::Animation *animation)
|
||||
: mListener(OGRE_NEW RootMotionListener(e))
|
||||
{
|
||||
mHipsTrack = nullptr;
|
||||
mRootTrack = nullptr;
|
||||
for (const auto &it : animation->_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 = animation->createNodeTrack(
|
||||
bone->getHandle(), bone);
|
||||
Ogre::TransformKeyFrame *kf =
|
||||
mRootTrack->createNodeKeyFrame(0.0f);
|
||||
kf->setTranslate(Ogre::Vector3::ZERO);
|
||||
kf->setRotation(Ogre::Quaternion::IDENTITY);
|
||||
}
|
||||
// if (e.has<Player>()) // FIXME
|
||||
// mRootTrack->setListener(mListener);
|
||||
Ogre::TransformKeyFrame *tkfBeg =
|
||||
(Ogre::TransformKeyFrame *)mRootTrack->getKeyFrame(0);
|
||||
Ogre::TransformKeyFrame *tkfEnd =
|
||||
(Ogre::TransformKeyFrame *)mRootTrack->getKeyFrame(
|
||||
mRootTrack->getNumKeyFrames() - 1);
|
||||
mRootTranslation =
|
||||
tkfEnd->getTranslate() - tkfBeg->getTranslate();
|
||||
}
|
||||
};
|
||||
struct Animation {
|
||||
Ogre::AnimationState *mAnimationState;
|
||||
Ogre::Animation *mSkelAnimation;
|
||||
Ogre::NodeAnimationTrack *mHipsTrack;
|
||||
Ogre::NodeAnimationTrack *mRootTrack;
|
||||
RootMotionListener *mListener;
|
||||
SetupTracks *mTracks;
|
||||
float m_weight;
|
||||
float m_accWeight;
|
||||
flecs::entity e;
|
||||
Ogre::Skeleton *mSkeleton;
|
||||
Animation(Ogre::Skeleton *skeleton, Ogre::AnimationState *animState,
|
||||
Ogre::Animation *skelAnimation, flecs::entity e)
|
||||
: mAnimationState(animState)
|
||||
: mTracks(OGRE_NEW SetupTracks(e, skeleton, skelAnimation))
|
||||
, mAnimationState(animState)
|
||||
, mSkelAnimation(skelAnimation)
|
||||
, mListener(OGRE_NEW RootMotionListener(e))
|
||||
, m_weight(0)
|
||||
, m_accWeight(0)
|
||||
, e(e)
|
||||
, mSkeleton(skeleton)
|
||||
{
|
||||
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()
|
||||
{
|
||||
@@ -198,22 +205,134 @@ struct Animation {
|
||||
{
|
||||
return m_weight;
|
||||
}
|
||||
bool addTime(float time)
|
||||
Ogre::Vector3 rootMotion;
|
||||
Ogre::Vector3 getRootMotionDelta()
|
||||
{
|
||||
#if 0
|
||||
Ogre::KeyFrame *kf1, *kf2;
|
||||
Ogre::TransformKeyFrame *k1, *k2;
|
||||
unsigned short firstKeyIndex;
|
||||
Ogre::Real timePos = mAnimationState->getTimePosition();
|
||||
Ogre::TimeIndex index = mSkelAnimation->_getTimeIndex(timePos);
|
||||
float tm = mTracks->mRootTrack->getKeyFramesAtTime(
|
||||
index, &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();
|
||||
} else {
|
||||
rotation = Ogre::Quaternion::nlerp(
|
||||
tm, k1->getRotation(),
|
||||
k2->getRotation(), true);
|
||||
translation = k1->getTranslate() +
|
||||
(k2->getTranslate() -
|
||||
k1->getTranslate()) *
|
||||
tm;
|
||||
}
|
||||
return translation * mAnimationState->getWeight();
|
||||
#endif
|
||||
if (mAnimationState->getEnabled())
|
||||
return rootMotion * mAnimationState->getWeight();
|
||||
else
|
||||
return Ogre::Vector3(0, 0, 0);
|
||||
}
|
||||
#if 0
|
||||
void updateRootMotion(Ogre::Real timePos)
|
||||
{
|
||||
Ogre::KeyFrame *kf1, *kf2;
|
||||
Ogre::TransformKeyFrame *k1, *k2;
|
||||
unsigned short firstKeyIndex;
|
||||
mSkeleton->getBone("Root")->setManuallyControlled(true);
|
||||
Ogre::TimeIndex index = mSkelAnimation->_getTimeIndex(timePos);
|
||||
float tm = mTracks->mRootTrack->getKeyFramesAtTime(
|
||||
index, &kf1, &kf2, &firstKeyIndex);
|
||||
k1 = static_cast<Ogre::TransformKeyFrame *>(kf1);
|
||||
k2 = static_cast<Ogre::TransformKeyFrame *>(kf2);
|
||||
Ogre::Vector3 translation;
|
||||
Ogre::Quaternion rotation;
|
||||
Ogre::Vector3 deltaMotion =
|
||||
e.get_mut<CharacterBase>().mBoneMotion;
|
||||
Ogre::Vector3 prevTranslation =
|
||||
e.get_mut<CharacterBase>().mBonePrevMotion;
|
||||
if (tm == 0.0f) {
|
||||
rotation = k1->getRotation() * m_weight;
|
||||
translation = k1->getTranslate() * m_weight;
|
||||
deltaMotion = translation;
|
||||
} else {
|
||||
rotation = Ogre::Quaternion::nlerp(
|
||||
tm, k1->getRotation() * m_weight,
|
||||
k2->getRotation() * m_weight, true);
|
||||
translation = k1->getTranslate() * m_weight +
|
||||
(k2->getTranslate() * m_weight -
|
||||
k1->getTranslate() * m_weight) *
|
||||
tm;
|
||||
deltaMotion = translation - prevTranslation;
|
||||
if (deltaMotion.squaredLength() >
|
||||
translation.squaredLength())
|
||||
deltaMotion = translation;
|
||||
}
|
||||
e.get_mut<CharacterBase>().mBoneMotion = deltaMotion;
|
||||
e.get_mut<CharacterBase>().mBonePrevMotion = prevTranslation;
|
||||
e.modified<CharacterBase>();
|
||||
#if 1
|
||||
if (timePos > 0.5f && m_weight > 0.2 && translation.squaredLength() > 0.0f) {
|
||||
std::cout << timePos << " " << m_weight << " "
|
||||
<< e.get<CharacterBase>()
|
||||
.mBodyEnt->getMesh()
|
||||
->getName()
|
||||
<< " " << deltaMotion << " "
|
||||
<< prevTranslation << std::endl;
|
||||
std::cout << translation << " " << rotation << std::endl;
|
||||
// OgreAssert(false, "updateRootMotion");
|
||||
}
|
||||
#endif
|
||||
mTracks->mRootTrack->getAssociatedNode()->setPosition(
|
||||
Ogre::Vector3());
|
||||
mSkeleton->getBone("Root")->reset();
|
||||
}
|
||||
#endif
|
||||
void getKeyframeIndices(Ogre::Real timePos, unsigned short *pindex)
|
||||
{
|
||||
Ogre::TimeIndex index = mSkelAnimation->_getTimeIndex(timePos);
|
||||
Ogre::KeyFrame *kf1, *kf2;
|
||||
mTracks->mRootTrack->getKeyFramesAtTime(index, &kf1, &kf2, pindex);
|
||||
|
||||
}
|
||||
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 short prev_index, next_index;
|
||||
Ogre::TimeIndex index = mSkelAnimation->_getTimeIndex(mAnimationState->getTimePosition());
|
||||
getKeyframeIndices(mAnimationState->getTimePosition(), &prev_index);
|
||||
unsigned int previous_frame = index.getKeyIndex();
|
||||
float lastTime = mAnimationState->getTimePosition();
|
||||
mAnimationState->addTime(time);
|
||||
index = mSkelAnimation->_getTimeIndex(
|
||||
mAnimationState->getTimePosition());
|
||||
mRootTrack->getKeyFramesAtTime(index, &kf1, &kf2, &next_index);
|
||||
return prev_index != next_index;
|
||||
float thisTime = mAnimationState->getTimePosition();
|
||||
float length = mAnimationState->getLength();
|
||||
bool loop = mAnimationState->getLoop();
|
||||
int loops = loop ? (int)std::round((lastTime + time - thisTime) / length) : 0;
|
||||
Ogre::TransformKeyFrame tkf(0, 0);
|
||||
mTracks->mRootTrack->getInterpolatedKeyFrame(lastTime, &tkf);
|
||||
Ogre::Vector3 lastRootPos = tkf.getTranslate();
|
||||
mTracks->mRootTrack->getInterpolatedKeyFrame(thisTime, &tkf);
|
||||
Ogre::Vector3 thisRootPos = tkf.getTranslate();
|
||||
#if 0
|
||||
if (thisTime > lastTime)
|
||||
rootMotion = thisRootPos - lastRootPos;
|
||||
else
|
||||
rootMotion = mTracks->mRootTranslation + thisRootPos - lastRootPos;
|
||||
#else
|
||||
rootMotion = (thisRootPos - lastRootPos) + (loops * mTracks->mRootTranslation);
|
||||
|
||||
#endif
|
||||
getKeyframeIndices(mAnimationState->getTimePosition(), &next_index);
|
||||
// updateRootMotion(mAnimationState->getTimePosition());
|
||||
return prev_index != next_index;
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
@@ -827,6 +946,14 @@ struct AnimationSystem : AnimationNode {
|
||||
}
|
||||
};
|
||||
AnimationSystemBuilder m_builder;
|
||||
Ogre::Vector3 getRootMotionDelta()
|
||||
{
|
||||
Ogre::Vector3 motionDelta(0, 0, 0);
|
||||
int i;
|
||||
for (i = 0; i < vanimation_list.size(); i++)
|
||||
motionDelta += vanimation_list[i]->getRootMotionDelta();
|
||||
return motionDelta;
|
||||
}
|
||||
bool addTime(float time)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -4,26 +4,191 @@
|
||||
#include "Components.h"
|
||||
#include "CharacterModule.h"
|
||||
#include "CharacterAnimationModule.h"
|
||||
#include "StaticGeometryModule.h"
|
||||
#include "PhysicsModule.h"
|
||||
#include "PlayerActionModule.h"
|
||||
#include "items.h"
|
||||
#include "CharacterManagerModule.h"
|
||||
|
||||
namespace ECS
|
||||
{
|
||||
void createNPCActionNodes(flecs::entity town, int index)
|
||||
{
|
||||
TownNPCs &npcs = town.get_mut<TownNPCs>();
|
||||
TownNPCs::NPCData &npc = npcs.npcs.at(index);
|
||||
flecs::entity e = npc.e;
|
||||
nlohmann::json npcprops = npc.props;
|
||||
const CharacterBase &ch = e.get<CharacterBase>();
|
||||
Ogre::Vector3 characterPos = ch.mBodyNode->_getDerivedPosition();
|
||||
Ogre::Quaternion characterRot = ch.mBodyNode->_getDerivedOrientation();
|
||||
if (npc.actionNodes.size() > 0) {
|
||||
int i;
|
||||
for (i = 0; i < npc.actionNodes.size(); i++) {
|
||||
auto &anode = npc.actionNodes[i];
|
||||
Ogre::Vector3 offset = Ogre::Vector3::UNIT_Z * 0.3f +
|
||||
Ogre::Vector3::UNIT_Y;
|
||||
if (i == 1)
|
||||
offset = Ogre::Vector3::NEGATIVE_UNIT_Z * 0.3f +
|
||||
Ogre::Vector3::UNIT_Y;
|
||||
anode.position = characterPos + characterRot * offset;
|
||||
anode.rotation = characterRot;
|
||||
to_json(anode.props["position"], anode.position);
|
||||
to_json(anode.props["rotation"], anode.rotation);
|
||||
}
|
||||
return;
|
||||
}
|
||||
{
|
||||
ActionNodeList::ActionNode anode;
|
||||
anode.props["action"] = "talk";
|
||||
anode.props["action_text"] = "Talk";
|
||||
anode.action = "talk";
|
||||
anode.action_text = "Talk";
|
||||
Ogre::Vector3 offset = Ogre::Vector3::UNIT_Z * 0.25f +
|
||||
Ogre::Vector3::UNIT_Y * 1.5f;
|
||||
anode.position = characterPos + characterRot * offset;
|
||||
anode.rotation = characterRot;
|
||||
anode.radius = 0.6f;
|
||||
anode.height = 1.0f;
|
||||
to_json(anode.props["position"], anode.position);
|
||||
to_json(anode.props["rotation"], anode.rotation);
|
||||
|
||||
anode.props["radius"] = anode.radius;
|
||||
anode.props["height"] = anode.height;
|
||||
anode.props["town"] = town.id();
|
||||
anode.props["index"] = index;
|
||||
anode.props["npc"] = npcprops;
|
||||
npc.actionNodes.push_back(anode);
|
||||
}
|
||||
{
|
||||
ActionNodeList::ActionNode anode;
|
||||
anode.props["action"] = "action";
|
||||
anode.props["action_text"] = "Action";
|
||||
anode.action = "action";
|
||||
anode.action_text = "Action";
|
||||
Ogre::Vector3 offset = Ogre::Vector3::NEGATIVE_UNIT_Z * 0.2f +
|
||||
Ogre::Vector3::UNIT_Y;
|
||||
anode.position = characterPos + characterRot * offset;
|
||||
anode.rotation = characterRot;
|
||||
anode.radius = 0.3f;
|
||||
anode.height = 1.0f;
|
||||
to_json(anode.props["position"], anode.position);
|
||||
to_json(anode.props["rotation"], anode.rotation);
|
||||
anode.props["radius"] = anode.radius;
|
||||
anode.props["height"] = anode.height;
|
||||
anode.props["town"] = town.id();
|
||||
anode.props["index"] = index;
|
||||
anode.props["npc"] = npcprops;
|
||||
npc.actionNodes.push_back(anode);
|
||||
}
|
||||
}
|
||||
CharacterManagerModule::CharacterManagerModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.module<CharacterManagerModule>();
|
||||
ecs.import <CharacterModule>();
|
||||
ecs.import <CharacterAnimationModule>();
|
||||
ecs.import <PhysicsModule>();
|
||||
ecs.import <PlayerActionModule>();
|
||||
ecs.component<TownCharacterHolder>();
|
||||
ecs.component<TownNPCs>();
|
||||
ecs.component<LivesIn>();
|
||||
ecs.system<TerrainItem, TownNPCs>("UpdateCharacters")
|
||||
.kind(flecs::OnUpdate)
|
||||
.interval(1.0f)
|
||||
.write<CharacterBase>()
|
||||
.write<CharacterLocation>()
|
||||
.write<CharacterConf>()
|
||||
.write<Character>()
|
||||
.write<LivesIn>()
|
||||
.each([this](flecs::entity town, TerrainItem &item,
|
||||
TownNPCs &npcs) {
|
||||
if (!player.is_valid())
|
||||
return;
|
||||
if (!player.has<CharacterBase>())
|
||||
return;
|
||||
|
||||
Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask([this,
|
||||
town]() {
|
||||
flecs::entity player =
|
||||
ECS::get<CharacterManagerModule>()
|
||||
.getPlayer();
|
||||
if (!player.is_valid())
|
||||
return;
|
||||
if (!player.has<CharacterBase>())
|
||||
return;
|
||||
TownNPCs &npcs = town.get_mut<TownNPCs>();
|
||||
Ogre::Vector3 cameraPos =
|
||||
player.get<CharacterBase>()
|
||||
.mBodyNode
|
||||
->_getDerivedPosition();
|
||||
for (auto &npc : npcs.npcs) {
|
||||
int index = npc.first;
|
||||
TownNPCs::NPCData &data = npc.second;
|
||||
Ogre::Vector3 npcPosition =
|
||||
data.position;
|
||||
Ogre::Quaternion npcOrientation =
|
||||
data.orientation;
|
||||
if (cameraPos.squaredDistance(
|
||||
npcPosition) < 10000.0f) {
|
||||
if (!data.e.is_valid()) {
|
||||
data.e = createCharacterData(
|
||||
data.model,
|
||||
data.position,
|
||||
data.orientation);
|
||||
data.e.add<LivesIn>(
|
||||
town);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cameraPos.squaredDistance(
|
||||
npcPosition) > 22500.0f) {
|
||||
if (data.e.is_valid()) {
|
||||
data.e.destruct();
|
||||
data.e =
|
||||
flecs::entity();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto &npc : npcs.npcs) {
|
||||
int index = npc.first;
|
||||
TownNPCs::NPCData &data = npc.second;
|
||||
Ogre::Vector3 npcPosition =
|
||||
data.position;
|
||||
Ogre::Quaternion npcOrientation =
|
||||
data.orientation;
|
||||
if (cameraPos.squaredDistance(
|
||||
npcPosition) < 10000.0f) {
|
||||
if (data.e.is_valid()) {
|
||||
if (data.e.has<
|
||||
CharacterBase>() &&
|
||||
data.e.has<LivesIn>(
|
||||
town))
|
||||
createNPCActionNodes(
|
||||
town,
|
||||
index);
|
||||
}
|
||||
}
|
||||
}
|
||||
town.modified<TownNPCs>();
|
||||
});
|
||||
});
|
||||
}
|
||||
flecs::entity
|
||||
CharacterManagerModule::createPlayer(const Ogre::Vector3 &position,
|
||||
const Ogre::Quaternion &rotation)
|
||||
{
|
||||
static int count = 0;
|
||||
OgreAssert(count == 0, "overspawn");
|
||||
OgreAssert(!player.is_valid(), "Player already created");
|
||||
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>();
|
||||
OgreAssert(player.is_valid(), "Can't create player");
|
||||
std::cout << "Begin player create" << std::endl;
|
||||
player.add<Player>();
|
||||
ECS::get_mut<CharacterModule>().createCharacter(
|
||||
player, position, rotation, "normal-male.glb");
|
||||
ECS::modified<CharacterModule>();
|
||||
std::cout << "End player create" << std::endl;
|
||||
count++;
|
||||
return player;
|
||||
}
|
||||
flecs::entity
|
||||
@@ -31,12 +196,46 @@ 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;
|
||||
flecs::entity e = ECS::get().entity();
|
||||
ECS::get_mut<CharacterModule>().createCharacter(e, position, rotation,
|
||||
model);
|
||||
ECS::modified<CharacterModule>();
|
||||
return e;
|
||||
}
|
||||
|
||||
void CharacterManagerModule::registerTownCharacters(flecs::entity town)
|
||||
{
|
||||
Ogre::MeshManager::getSingleton().load("normal-male.glb", "General");
|
||||
Ogre::MeshManager::getSingleton().load("normal-female.glb", "General");
|
||||
Ogre::String props = StaticGeometryModule::getItemProperties(town);
|
||||
nlohmann::json j = nlohmann::json::parse(props);
|
||||
nlohmann::json npcs = nlohmann::json::array();
|
||||
if (town.has<TownNPCs>())
|
||||
return;
|
||||
if (j.find("npcs") != j.end())
|
||||
npcs = j["npcs"];
|
||||
std::cout << npcs.dump(4) << std::endl;
|
||||
int index = 0;
|
||||
std::map<int, TownNPCs::NPCData> npcMap;
|
||||
for (auto &npc : npcs) {
|
||||
const char *models[] = { "normal-male.glb",
|
||||
"normal-female.glb" };
|
||||
int sex = npc["sex"].get<int>();
|
||||
Ogre::Vector3 npcPosition;
|
||||
Ogre::Quaternion npcOrientation;
|
||||
from_json(npc["position"], npcPosition);
|
||||
from_json(npc["orientation"], npcOrientation);
|
||||
Ogre::String model = models[sex];
|
||||
TownNPCs::NPCData npcData;
|
||||
npcData.e = flecs::entity();
|
||||
npcData.model = model;
|
||||
npcData.orientation = npcOrientation;
|
||||
npcData.position = npcPosition;
|
||||
npcData.props = npc;
|
||||
npcMap[index] = npcData;
|
||||
index++;
|
||||
}
|
||||
town.set<TownNPCs>({ npcMap });
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,27 @@
|
||||
#ifndef _CHARACTER_MANAGER_MODULE_
|
||||
#define _CHARACTER_MANAGER_MODULE_
|
||||
#include <flecs.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "PlayerActionModule.h"
|
||||
namespace ECS
|
||||
{
|
||||
struct TownCharacterHolder{int index;};
|
||||
struct TownNPCs {
|
||||
struct NPCData {
|
||||
flecs::entity e;
|
||||
nlohmann::json props;
|
||||
Ogre::Vector3 position;
|
||||
Ogre::Quaternion orientation;
|
||||
Ogre::String model;
|
||||
std::vector<ActionNodeList::ActionNode> actionNodes;
|
||||
};
|
||||
|
||||
std::map<int, NPCData> npcs;
|
||||
};
|
||||
struct LivesIn {};
|
||||
struct CharacterManagerModule {
|
||||
std::set<flecs::entity> characters;
|
||||
flecs::entity player;
|
||||
CharacterManagerModule(flecs::world &ecs);
|
||||
flecs::entity createPlayer(const Ogre::Vector3 &position,
|
||||
const Ogre::Quaternion &rotation);
|
||||
@@ -12,6 +29,14 @@ struct CharacterManagerModule {
|
||||
const Ogre::Vector3 &position,
|
||||
const Ogre::Quaternion &rotation);
|
||||
void removeCharacterData(int id);
|
||||
flecs::entity getPlayer() const
|
||||
{
|
||||
return player;
|
||||
}
|
||||
void registerTownCharacters(flecs::entity town);
|
||||
void setTownCharacter(flecs::entity town, int index, bool enable);
|
||||
CharacterManagerModule(CharacterManagerModule &&) = delete;
|
||||
CharacterManagerModule &operator=(CharacterManagerModule&&) = delete;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
#include "TerrainModule.h"
|
||||
#include "Components.h"
|
||||
#include "PhysicsModule.h"
|
||||
#include "physics.h"
|
||||
#include "CharacterAnimationModule.h"
|
||||
#include "CharacterManagerModule.h"
|
||||
#include "CharacterModule.h"
|
||||
#include "goap.h"
|
||||
namespace ECS
|
||||
{
|
||||
CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
@@ -24,12 +25,9 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
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 <CharacterAnimationModule>();
|
||||
ecs.import <TerrainModule>();
|
||||
ecs.import <WaterModule>();
|
||||
|
||||
@@ -41,8 +39,10 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
ecs.system<Input, Camera>("HandleInput")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([this](Input &input, Camera &camera) {
|
||||
if (!ECS::player.is_valid())
|
||||
return;
|
||||
flecs::entity player =
|
||||
ECS::get<CharacterManagerModule>().getPlayer();
|
||||
if (!player.is_valid())
|
||||
return;
|
||||
/* handle input */
|
||||
// if (input.control == input.control_prev)
|
||||
// return;
|
||||
@@ -263,38 +263,31 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
}
|
||||
});
|
||||
#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;
|
||||
});
|
||||
static int characterCount = 0;
|
||||
ecs.observer<const EngineData, const CharacterLocation,
|
||||
const CharacterConf, CharacterBase>(
|
||||
"SetupCharacterBaseObs")
|
||||
.event(flecs::OnAdd)
|
||||
.each([&](flecs::entity e, const EngineData &eng,
|
||||
const CharacterLocation &loc,
|
||||
const CharacterConf &conf, CharacterBase &ch) {
|
||||
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);
|
||||
OgreAssert(ch.mBodyEnt->getSkeleton()->hasBone("Root"),
|
||||
"No root bone");
|
||||
ch.mBoneMotion = Ogre::Vector3::ZERO;
|
||||
ch.mBonePrevMotion = Ogre::Vector3::ZERO;
|
||||
});
|
||||
ecs.observer<AnimationControl>("SetupAnimationControlObs")
|
||||
.event(flecs::OnAdd)
|
||||
.each([](flecs::entity e, AnimationControl &anim) {
|
||||
anim.configured = false;
|
||||
});
|
||||
|
||||
#if 0
|
||||
ecs.system<const EngineData, const CharacterLocation,
|
||||
const CharacterConf, Body2Entity>("SetupCharacter")
|
||||
@@ -557,6 +550,7 @@ void CharacterModule::updateCameraGoal(Camera &camera, Ogre::Real deltaYaw,
|
||||
Ogre::Real deltaPitch,
|
||||
Ogre::Real deltaZoom)
|
||||
{
|
||||
static float canonDist = 0;
|
||||
camera.mCameraPivot->yaw(Ogre::Degree(deltaYaw), Ogre::Node::TS_PARENT);
|
||||
if (!(camera.mPivotPitch + deltaPitch > 25 && deltaPitch > 0) &&
|
||||
!(camera.mPivotPitch + deltaPitch < -60 && deltaPitch < 0)) {
|
||||
@@ -569,187 +563,62 @@ void CharacterModule::updateCameraGoal(Camera &camera, Ogre::Real deltaYaw,
|
||||
Ogre::Real distChange = deltaZoom * dist;
|
||||
|
||||
// bound the zoom
|
||||
if (!(dist + distChange < 8 && distChange < 0) &&
|
||||
!(dist + distChange > 25 && distChange > 0))
|
||||
if (!(dist + distChange < 1.5f && distChange < 0) &&
|
||||
!(dist + distChange > 10 && distChange > 0)) {
|
||||
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;
|
||||
canonDist += distChange;
|
||||
}
|
||||
JPH::BodyID id;
|
||||
Ogre::Vector3 position;
|
||||
Ogre::Vector3 d = (camera.mCameraPivot->_getDerivedPosition() -
|
||||
camera.mCameraGoal->_getDerivedPosition())
|
||||
.normalisedCopy();
|
||||
|
||||
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>();
|
||||
}
|
||||
}
|
||||
});
|
||||
if (JoltPhysicsWrapper::getSingleton().raycastQuery(
|
||||
camera.mCameraPivot->_getDerivedPosition(),
|
||||
camera.mCameraGoal->_getDerivedPosition() - d * 0.6,
|
||||
position, id)) {
|
||||
float l = camera.mCameraPivot->_getDerivedPosition()
|
||||
.squaredDistance(
|
||||
camera.mCameraGoal
|
||||
->_getDerivedPosition());
|
||||
float m = camera.mCameraPivot->_getDerivedPosition()
|
||||
.squaredDistance(position);
|
||||
if (m < l)
|
||||
camera.mCameraGoal->_setDerivedPosition(position +
|
||||
d * 0.6f);
|
||||
} else {
|
||||
Ogre::Real dist2 =
|
||||
camera.mCameraGoal->_getDerivedPosition().distance(
|
||||
camera.mCameraPivot->_getDerivedPosition());
|
||||
if (deltaZoom < 0.0f || deltaZoom > 0.0f)
|
||||
canonDist = dist2;
|
||||
else {
|
||||
if (canonDist < dist2)
|
||||
canonDist = dist2;
|
||||
if (dist2 < canonDist)
|
||||
camera.mCameraGoal->translate(
|
||||
0, 0, 0.08f, Ogre::Node::TS_LOCAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterModule::createCharacter(flecs::entity e,
|
||||
const Ogre::Vector3 &position,
|
||||
const Ogre::Quaternion &rotation,
|
||||
const Ogre::String model)
|
||||
{
|
||||
if (e.has<CharacterBase>() || e.has<AnimationControl>())
|
||||
return;
|
||||
e.set<CharacterLocation>({ rotation, position });
|
||||
e.set<CharacterConf>({ model });
|
||||
e.add<CharacterBase>();
|
||||
e.add<AnimationControl>();
|
||||
e.set<CharacterVelocity>(
|
||||
{ { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } });
|
||||
e.add<CharacterGravity>();
|
||||
e.add<CharacterBuoyancy>();
|
||||
e.add<Character>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#define CHARACTER_MODULE_H_
|
||||
#include <flecs.h>
|
||||
#include <Ogre.h>
|
||||
#include "goap.h"
|
||||
#include "Components.h"
|
||||
namespace ECS
|
||||
{
|
||||
struct Camera;
|
||||
@@ -21,8 +21,6 @@ struct CharacterBase {
|
||||
float mTimer;
|
||||
Ogre::SceneNode *mBodyNode;
|
||||
Ogre::Entity *mBodyEnt;
|
||||
Ogre::Skeleton *mSkeleton;
|
||||
Ogre::Node *mRootBone;
|
||||
Ogre::Vector3 mBoneMotion;
|
||||
Ogre::Vector3 mBonePrevMotion;
|
||||
Ogre::Vector3 mGoalDirection; // actual intended direction in world-space
|
||||
@@ -37,30 +35,15 @@ struct CharacterConf {
|
||||
};
|
||||
struct CharacterInActuator {
|
||||
Ogre::String animationState;
|
||||
Vector3 prevMotion;
|
||||
Vector3 prevMotion;
|
||||
};
|
||||
struct ActionTarget {
|
||||
std::vector<BaseAction *> actions;
|
||||
};
|
||||
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 updateCameraGoal(Camera &camera, Ogre::Real deltaYaw,
|
||||
Ogre::Real deltaPitch, Ogre::Real deltaZoom);
|
||||
};
|
||||
struct CharacterAIModule {
|
||||
CharacterAIModule(flecs::world &ecs);
|
||||
void createCharacter(flecs::entity e, const Ogre::Vector3 &position,
|
||||
const Ogre::Quaternion &rotation,
|
||||
const Ogre::String model);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -75,5 +75,8 @@ struct GroundCheckReady {};
|
||||
struct Body2Entity {
|
||||
/* std::unordered_map<btCollisionObject *, flecs::entity> entities; */
|
||||
};
|
||||
struct EditorSceneSwitch {
|
||||
int scene;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
1421
src/gamedata/EditorGUIModule.cpp
Normal file
1421
src/gamedata/EditorGUIModule.cpp
Normal file
File diff suppressed because it is too large
Load Diff
9
src/gamedata/EditorGUIModule.h
Normal file
9
src/gamedata/EditorGUIModule.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef __EDITORGUIMODULE_H__
|
||||
#define __EDITORGUIMODULE_H__
|
||||
|
||||
namespace ECS {
|
||||
struct EditorGUIModule {
|
||||
EditorGUIModule(flecs::world &ecs);
|
||||
};
|
||||
}
|
||||
#endif // EDITORGUIMODULE_H
|
||||
@@ -26,4 +26,4 @@ struct EventModule {
|
||||
flecs::entity to);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,40 +6,9 @@ namespace OgreBites
|
||||
}
|
||||
namespace ECS
|
||||
{
|
||||
struct GUI {
|
||||
bool enabled;
|
||||
bool grab;
|
||||
bool grabChanged;
|
||||
bool narrationBox;
|
||||
bool mainMenu;
|
||||
Ogre::String narrationText;
|
||||
std::vector<Ogre::String> choices;
|
||||
int narration_answer;
|
||||
static void setWindowGrab(bool g = true)
|
||||
{
|
||||
ECS::GUI &gui = ECS::get().get_mut<GUI>();
|
||||
if (gui.grab != g) {
|
||||
gui.grab = g;
|
||||
gui.grabChanged = true;
|
||||
ECS::get().modified<GUI>();
|
||||
}
|
||||
}
|
||||
static void finish()
|
||||
{
|
||||
ECS::GUI &gui = ECS::get().get_mut<ECS::GUI>();
|
||||
gui.enabled = false;
|
||||
gui.mainMenu = false;
|
||||
gui.narrationBox = false;
|
||||
ECS::get().modified<ECS::GUI>();
|
||||
setWindowGrab(true);
|
||||
}
|
||||
};
|
||||
struct GUIModule {
|
||||
flecs::entity ui_wait;
|
||||
GUIModule(flecs::world &ecs);
|
||||
};
|
||||
struct EditorGUIModule {
|
||||
EditorGUIModule(flecs::world &ecs);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
141
src/gamedata/GUIModuleCommon.h
Normal file
141
src/gamedata/GUIModuleCommon.h
Normal file
@@ -0,0 +1,141 @@
|
||||
#ifndef __GUIMODULECOMMON_H__
|
||||
#define __GUIMODULECOMMON_H__
|
||||
#include <iostream>
|
||||
#include <Ogre.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "Components.h"
|
||||
#include "GameData.h"
|
||||
namespace ECS
|
||||
{
|
||||
|
||||
struct GUI {
|
||||
bool enabled;
|
||||
bool grab;
|
||||
bool grabChanged;
|
||||
bool narrationBox;
|
||||
bool mainMenu;
|
||||
Ogre::String narrationText;
|
||||
std::vector<Ogre::String> choices;
|
||||
int narration_answer;
|
||||
struct NarrationHandler {
|
||||
private:
|
||||
Ogre::String mnarrationText;
|
||||
std::vector<Ogre::String> mchoices;
|
||||
int narration_answer;
|
||||
nlohmann::json props;
|
||||
|
||||
private:
|
||||
bool complete;
|
||||
bool active;
|
||||
public:
|
||||
bool is_complete()
|
||||
{
|
||||
return complete;
|
||||
}
|
||||
bool is_active()
|
||||
{
|
||||
return active;
|
||||
}
|
||||
const Ogre::String &getNarrationText() const
|
||||
{
|
||||
return mnarrationText;
|
||||
}
|
||||
const std::vector<Ogre::String> &getChoices() const
|
||||
{
|
||||
return mchoices;
|
||||
}
|
||||
void progress()
|
||||
{
|
||||
_event("narration_progress");
|
||||
}
|
||||
void setNarrationAnswer(int answer)
|
||||
{
|
||||
narration_answer = answer;
|
||||
_event("narration_answered");
|
||||
}
|
||||
int getNarrationAnswer() const
|
||||
{
|
||||
return narration_answer;
|
||||
}
|
||||
|
||||
NarrationHandler(): complete(false), active(false) {}
|
||||
private:
|
||||
virtual void finish() = 0;
|
||||
virtual void activate() = 0;
|
||||
virtual void event(const Ogre::String &event) = 0;
|
||||
protected:
|
||||
void _activate()
|
||||
{
|
||||
activate();
|
||||
active = true;
|
||||
}
|
||||
void _finish()
|
||||
{
|
||||
finish();
|
||||
complete = true;
|
||||
}
|
||||
void _narration(const Ogre::String &text, const std::vector<Ogre::String> &choices)
|
||||
{
|
||||
mnarrationText = text;
|
||||
mchoices = choices;
|
||||
|
||||
}
|
||||
void _clear_narration()
|
||||
{
|
||||
mnarrationText = "";
|
||||
mchoices.clear();
|
||||
}
|
||||
public:
|
||||
void _event(const Ogre::String &ev)
|
||||
{
|
||||
if (!active && !complete)
|
||||
_activate();
|
||||
event(ev);
|
||||
}
|
||||
virtual ~NarrationHandler() {}
|
||||
void setProperties(const Ogre::String &properties)
|
||||
{
|
||||
props = nlohmann::json::parse(properties);
|
||||
}
|
||||
Ogre::String getProperties() const
|
||||
{
|
||||
return props.dump();
|
||||
}
|
||||
const nlohmann::json &getPropsJSON() const
|
||||
{
|
||||
return props;
|
||||
}
|
||||
};
|
||||
|
||||
static void setWindowGrab(bool g = true)
|
||||
{
|
||||
ECS::GUI &gui = ECS::get().get_mut<GUI>();
|
||||
if (gui.grab != g) {
|
||||
gui.grab = g;
|
||||
gui.grabChanged = true;
|
||||
ECS::get().modified<GUI>();
|
||||
}
|
||||
}
|
||||
static void finish()
|
||||
{
|
||||
ECS::GUI &gui = ECS::get().get_mut<ECS::GUI>();
|
||||
gui.enabled = false;
|
||||
gui.mainMenu = false;
|
||||
gui.narrationBox = false;
|
||||
ECS::get().modified<ECS::GUI>();
|
||||
setWindowGrab(true);
|
||||
}
|
||||
std::vector<NarrationHandler *> narrationHandlers;
|
||||
void addNarrationHandler(struct NarrationHandler *handler)
|
||||
{
|
||||
narrationHandlers.push_back(handler);
|
||||
}
|
||||
void removeNarrationHandler(struct NarrationHandler *handler)
|
||||
{
|
||||
auto it = std::find(narrationHandlers.begin(), narrationHandlers.end(), handler);
|
||||
narrationHandlers.erase(it);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif // GUIMODULECOMMON_H
|
||||
@@ -1,12 +1,18 @@
|
||||
#include <iostream>
|
||||
#include <Ogre.h>
|
||||
#include <OgreTerrain.h>
|
||||
#include <OgreTerrainGroup.h>
|
||||
#include <Jolt/Jolt.h>
|
||||
#include <Jolt/Physics/Body/BodyID.h>
|
||||
#include "GameData.h"
|
||||
#include "Components.h"
|
||||
#include "CharacterModule.h"
|
||||
#include "WaterModule.h"
|
||||
#include "TerrainModule.h"
|
||||
#include "SunModule.h"
|
||||
#include "GUIModuleCommon.h"
|
||||
#include "GUIModule.h"
|
||||
#include "EditorGUIModule.h"
|
||||
#include "LuaData.h"
|
||||
#include "WorldMapModule.h"
|
||||
#include "BoatModule.h"
|
||||
@@ -16,13 +22,14 @@
|
||||
#include "EventModule.h"
|
||||
#include "CharacterManagerModule.h"
|
||||
#include "VehicleManagerModule.h"
|
||||
#include "PlayerActionModule.h"
|
||||
#include "AppModule.h"
|
||||
#include "CharacterAIModule.h"
|
||||
#include "world-build.h"
|
||||
|
||||
namespace ECS
|
||||
{
|
||||
static flecs::world ecs;
|
||||
flecs::entity player;
|
||||
void setup_minimal()
|
||||
{
|
||||
ecs.component<EngineData>().add(flecs::Singleton);
|
||||
@@ -38,8 +45,8 @@ void setup_minimal()
|
||||
/* 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)
|
||||
void setupExteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
|
||||
Ogre::Camera *camera, Ogre::RenderWindow *window)
|
||||
{
|
||||
std::cout << "Setup GameData\n";
|
||||
setup_minimal();
|
||||
@@ -50,11 +57,14 @@ void setup(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
|
||||
ecs.import <WaterModule>();
|
||||
ecs.import <SunModule>();
|
||||
ecs.import <TerrainModule>();
|
||||
ecs.import <GUIModule>();
|
||||
ecs.import <GUIModule>();
|
||||
ecs.import <EventTriggerModule>();
|
||||
ecs.import <LuaModule>();
|
||||
ecs.import <WorldMapModule>();
|
||||
ecs.import <CharacterAnimationModule>();
|
||||
ecs.import <PlayerActionModule>();
|
||||
ecs.import <CharacterAIModule>();
|
||||
ecs.add<ActionNodeList>();
|
||||
|
||||
ecs.system<EngineData>("UpdateDelta")
|
||||
.kind(flecs::OnUpdate)
|
||||
@@ -87,7 +97,7 @@ void setup(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
|
||||
#endif
|
||||
});
|
||||
ecs.set<EngineData>({ scnMgr, 0.0f, 5.0f, (int)window->getWidth(),
|
||||
(int)window->getHeight(), false });
|
||||
(int)window->getHeight(), false });
|
||||
ecs.set<Camera>({ cameraNode, camera, false });
|
||||
ecs.add<GameData>();
|
||||
ecs.add<Input>();
|
||||
@@ -109,18 +119,68 @@ void setup(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
|
||||
|
||||
// ecs.set<Body2Entity>({});
|
||||
std::cout << "Setup GameData done\n";
|
||||
|
||||
/* Create player */
|
||||
player = ecs.get_mut<CharacterManagerModule>().createPlayer(
|
||||
{ 0, 0, 4 }, Ogre::Quaternion(Ogre::Radian(Ogre::Math::PI),
|
||||
Ogre::Vector3::UNIT_Y));
|
||||
ecs.system("SpawnPlayer").kind(flecs::OnUpdate).interval(0.5f).run([&](flecs::iter &it) {
|
||||
flecs::entity player =
|
||||
ECS::get<CharacterManagerModule>().getPlayer();
|
||||
if (!player.is_valid()) {
|
||||
/* Create player */
|
||||
Ogre::Vector3 position;
|
||||
JPH::BodyID id;
|
||||
long x, y;
|
||||
Ogre::TerrainGroup *tg =
|
||||
ECS::get<ECS::Terrain>().mTerrainGroup;
|
||||
if (tg->isDerivedDataUpdateInProgress())
|
||||
return;
|
||||
tg->convertWorldPositionToTerrainSlot(
|
||||
Ogre::Vector3(0, 0, 4), &x, &y);
|
||||
Ogre::Terrain *terrain = tg->getTerrain(x, y);
|
||||
if (terrain && terrain->isLoaded()) {
|
||||
if (PhysicsModule::raycastQuery(
|
||||
Ogre::Vector3(0, 500, 4),
|
||||
Ogre::Vector3(0, -500, 4), position,
|
||||
id)) {
|
||||
if (position.y < -10.0f &&
|
||||
position.y > -50.0f) {
|
||||
player =
|
||||
ecs.get_mut<
|
||||
CharacterManagerModule>()
|
||||
.createPlayer(
|
||||
{ position.x,
|
||||
position.y,
|
||||
position.z },
|
||||
Ogre::Quaternion(
|
||||
Ogre::Radian(
|
||||
Ogre::Math::
|
||||
PI),
|
||||
Ogre::Vector3::
|
||||
UNIT_Y));
|
||||
std::cout << position
|
||||
<< std::endl;
|
||||
// OgreAssert(false, "spawn");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
void setupInteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
|
||||
Ogre::Camera *camera, Ogre::RenderWindow *window)
|
||||
{
|
||||
}
|
||||
|
||||
void setupInventoryScene(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)
|
||||
Ogre::Camera *camera, Ogre::RenderWindow *window)
|
||||
{
|
||||
std::cout << "Setup Editor\n";
|
||||
setup_minimal();
|
||||
ecs.component<RenderWindow>().add(flecs::Singleton);
|
||||
ecs.component<EditorSceneSwitch>().add(flecs::Singleton);
|
||||
ecs.import <CharacterModule>();
|
||||
ecs.import <BoatModule>();
|
||||
ecs.import <PhysicsModule>();
|
||||
@@ -132,6 +192,8 @@ void setupEditor(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
|
||||
ecs.import <LuaModule>();
|
||||
// ecs.import <WorldMapModule>();
|
||||
ecs.import <CharacterAnimationModule>();
|
||||
ecs.import <PlayerActionModule>();
|
||||
ecs.add<ActionNodeList>();
|
||||
|
||||
ecs.system<EngineData>("UpdateDelta")
|
||||
.kind(flecs::OnUpdate)
|
||||
@@ -164,8 +226,11 @@ void setupEditor(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
|
||||
#endif
|
||||
});
|
||||
ecs.set<EngineData>({ scnMgr, 0.0f, 5.0f, (int)window->getWidth(),
|
||||
(int)window->getHeight(), false });
|
||||
(int)window->getHeight(), false });
|
||||
ecs.set<Camera>({ cameraNode, camera, false });
|
||||
#if 0
|
||||
ecs.set<EditorSceneSwitch>({ 0 });
|
||||
#endif
|
||||
ecs.add<GameData>();
|
||||
ecs.add<Input>();
|
||||
ecs.add<WaterSurface>();
|
||||
@@ -202,4 +267,5 @@ bool Vector3::zeroLength() const
|
||||
float l = x * x + y * y + z * z;
|
||||
return (l < 1e-06 * 1e-06);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,10 +5,16 @@ namespace ECS
|
||||
{
|
||||
extern flecs::entity player;
|
||||
void setup_minimal();
|
||||
void setup(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
|
||||
Ogre::Camera *camera, Ogre::RenderWindow *window);
|
||||
void setupExteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
|
||||
Ogre::Camera *camera, Ogre::RenderWindow *window);
|
||||
void setupInteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
|
||||
Ogre::Camera *camera, Ogre::RenderWindow *window);
|
||||
void setupInventoryScene(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);
|
||||
Ogre::Camera *camera, Ogre::RenderWindow *window);
|
||||
void setupEditorAlt(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()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include <OgreFileSystemLayer.h>
|
||||
#include "GameData.h"
|
||||
#include "Components.h"
|
||||
#include "GUIModule.h"
|
||||
#include "GUIModuleCommon.h"
|
||||
#include "PhysicsModule.h"
|
||||
#include "CharacterModule.h"
|
||||
#include "CharacterAnimationModule.h"
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "EventTriggerModule.h"
|
||||
#include "SlotsModule.h"
|
||||
#include "world-build.h"
|
||||
#include "PlayerActionModule.h"
|
||||
#include "LuaData.h"
|
||||
#include "luaaa.hpp"
|
||||
extern "C" {
|
||||
@@ -299,7 +300,23 @@ LuaData::LuaData()
|
||||
return 0;
|
||||
});
|
||||
lua_setglobal(L, "setup_handler");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
luaL_checktype(L, 1, LUA_TSTRING);
|
||||
if (lua_type(L, 2) == LUA_TFUNCTION)
|
||||
luaL_checktype(L, 2, LUA_TFUNCTION);
|
||||
else
|
||||
luaL_checktype(L, 2, LUA_TTABLE);
|
||||
ECS::get_mut<PlayerActionModule>().setupLuaActionHandler(L);
|
||||
ECS::modified<PlayerActionModule>();
|
||||
return 0;
|
||||
});
|
||||
lua_setglobal(L, "setup_action_handler");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
// FIXME
|
||||
return 0;
|
||||
});
|
||||
lua_setglobal(L, "setup_narration_handler");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
int args = lua_gettop(L);
|
||||
if (args < 1)
|
||||
return 0;
|
||||
@@ -846,6 +863,7 @@ LuaModule::LuaModule(flecs::world &ecs)
|
||||
ecs.module<LuaModule>();
|
||||
ecs.import <SlotsModule>();
|
||||
ecs.import <VehicleManagerModule>();
|
||||
ecs.import <PlayerActionModule>();
|
||||
ecs.component<LuaChildEventTrigger>();
|
||||
ecs.component<LuaBase>()
|
||||
.on_add([](LuaBase &lua) {
|
||||
@@ -911,4 +929,4 @@ LuaModule::LuaModule(flecs::world &ecs)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ struct LuaData {
|
||||
lua_State *L;
|
||||
std::vector<int> setup_handlers;
|
||||
int setup_handler();
|
||||
int call_handler(const Ogre::String &event);
|
||||
int call_handler(const Ogre::String &event);
|
||||
int call_handler(const Ogre::String &event, flecs::entity e,
|
||||
flecs::entity o);
|
||||
|
||||
|
||||
@@ -172,14 +172,18 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
|
||||
.event(flecs::OnRemove)
|
||||
.each([&](flecs::entity e, const JPH::BodyID &id) {
|
||||
JoltPhysicsWrapper::getSingleton().removeBody(id);
|
||||
JoltPhysicsWrapper::getSingleton().destroyBody(id);
|
||||
if (e.has<CharacterBase>() || e.has<Character>())
|
||||
return;
|
||||
JoltPhysicsWrapper::getSingleton().destroyBody(id);
|
||||
std::cout << "body destroyed" << std::endl;
|
||||
});
|
||||
ecs.observer<const EngineData, const CharacterBase>("SetupCharacterPh")
|
||||
.event(flecs::OnSet)
|
||||
ecs.system<const EngineData, const CharacterBase>("SetupCharacterPh")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<Character>()
|
||||
.without<CharacterBody>()
|
||||
.write<CharacterBody>()
|
||||
.without<CharacterBody>()
|
||||
.without<JPH::BodyID>()
|
||||
.write<CharacterBody>()
|
||||
.write<JPH::BodyID>()
|
||||
.each([](flecs::entity e, const EngineData &eng,
|
||||
const CharacterBase &base) {
|
||||
CharacterBody &b = e.ensure<CharacterBody>();
|
||||
@@ -652,7 +656,10 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
|
||||
}
|
||||
// gr.velocity.y = 0.0f;
|
||||
// v.y = 0.0f;
|
||||
ch->SetLinearVelocity(JoltPhysics::convert(v));
|
||||
OgreAssert(v.squaredLength() < 1000.0f,
|
||||
"shitty velocity setting");
|
||||
ch->SetLinearVelocity(
|
||||
JoltPhysics::convert<JPH::Vec3>(v));
|
||||
gr.velocity = Ogre::Vector3::ZERO;
|
||||
});
|
||||
ecs.system<const EngineData, CharacterBase, const CharacterBody,
|
||||
@@ -750,10 +757,15 @@ void PhysicsModule::controlPhysics(flecs::entity e, bool enable)
|
||||
}
|
||||
bool PhysicsModule::raycastQuery(const Ogre::Vector3 &startPos,
|
||||
const Ogre::Vector3 &endPos,
|
||||
Ogre::Vector3 &position)
|
||||
Ogre::Vector3 &position, JPH::BodyID &id)
|
||||
{
|
||||
return JoltPhysicsWrapper::getSingleton().raycastQuery(startPos, endPos,
|
||||
position);
|
||||
position, id);
|
||||
}
|
||||
|
||||
void PhysicsModule::setDebugDraw(bool enable)
|
||||
{
|
||||
JoltPhysicsWrapper::getSingleton().setDebugDraw(enable);
|
||||
}
|
||||
bool WaterBody::isInWater(const JPH::BodyID &id) const
|
||||
{
|
||||
|
||||
@@ -59,7 +59,8 @@ struct PhysicsModule {
|
||||
static void controlPhysics(flecs::entity e, bool enable);
|
||||
static bool raycastQuery(const Ogre::Vector3 &startPos,
|
||||
const Ogre::Vector3 &endPos,
|
||||
Ogre::Vector3 &position);
|
||||
Ogre::Vector3 &position, JPH::BodyID &id);
|
||||
static void setDebugDraw(bool enable);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
626
src/gamedata/PlayerActionModule.cpp
Normal file
626
src/gamedata/PlayerActionModule.cpp
Normal file
@@ -0,0 +1,626 @@
|
||||
#include <nanoflann.hpp>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <flecs.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <lua.hpp>
|
||||
#include "Components.h"
|
||||
#include "GameData.h"
|
||||
#include "CharacterManagerModule.h"
|
||||
#include "CharacterModule.h"
|
||||
#include "items.h"
|
||||
#include "GUIModule.h"
|
||||
#include "GUIModuleCommon.h"
|
||||
#include "LuaData.h"
|
||||
#include "PlayerActionModule.h"
|
||||
|
||||
namespace ECS
|
||||
{
|
||||
struct OgreVector3Adaptor {
|
||||
const std::vector<ActionNodeList::ActionNode> &nodes;
|
||||
|
||||
OgreVector3Adaptor(const std::vector<ActionNodeList::ActionNode> &nodes)
|
||||
: nodes(nodes)
|
||||
{
|
||||
}
|
||||
|
||||
// Required by nanoflann: Number of data points
|
||||
inline size_t kdtree_get_point_count() const
|
||||
{
|
||||
return nodes.size();
|
||||
}
|
||||
|
||||
// Required by nanoflann: Returns the distance between the vector and a point
|
||||
// Using squared distance is standard for performance
|
||||
inline float kdtree_get_pt(const size_t idx, const size_t dim) const
|
||||
{
|
||||
return nodes[idx].position[dim];
|
||||
}
|
||||
|
||||
// Optional: bounding box optimization (return false if not implemented)
|
||||
template <class BBOX> bool kdtree_get_bbox(BBOX & /*bb*/) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
typedef nanoflann::KDTreeSingleIndexAdaptor<
|
||||
nanoflann::L2_Simple_Adaptor<float, OgreVector3Adaptor>,
|
||||
OgreVector3Adaptor, 3 /* dimensionality */
|
||||
>
|
||||
OgreKDTree;
|
||||
struct ActionNodeList::indexObject {
|
||||
OgreVector3Adaptor adaptor;
|
||||
OgreKDTree index;
|
||||
indexObject(const std::vector<ActionNodeList::ActionNode> &nodes)
|
||||
: adaptor(nodes)
|
||||
, index(3, adaptor,
|
||||
nanoflann::KDTreeSingleIndexAdaptorParams(10))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct TestNarrativeHandler : GUI::NarrationHandler {
|
||||
int count;
|
||||
TestNarrativeHandler()
|
||||
: GUI::NarrationHandler()
|
||||
, count(0)
|
||||
{
|
||||
}
|
||||
void finish() override
|
||||
{
|
||||
_clear_narration();
|
||||
}
|
||||
void activate() override
|
||||
{
|
||||
_narration("Greetings...", {});
|
||||
std::cout << getPropsJSON().dump(4) << std::endl;
|
||||
count = 0;
|
||||
}
|
||||
void event(const Ogre::String &evt) override
|
||||
{
|
||||
if (evt == "narration_progress" ||
|
||||
evt == "narration_answered") {
|
||||
count++;
|
||||
if (count == 1) {
|
||||
_narration(
|
||||
"Question..." +
|
||||
Ogre::StringConverter::toString(
|
||||
count),
|
||||
{ "Answer1", "Answer2" });
|
||||
} else {
|
||||
_narration(
|
||||
"Whatever..." +
|
||||
Ogre::StringConverter::toString(
|
||||
count),
|
||||
{});
|
||||
}
|
||||
if (count > 5)
|
||||
_finish();
|
||||
}
|
||||
if (evt == "narration_answered")
|
||||
std::cout << "answer: " << getNarrationAnswer()
|
||||
<< std::endl;
|
||||
}
|
||||
};
|
||||
struct LuaNarrationHandler : GUI::NarrationHandler {
|
||||
int ref;
|
||||
lua_State *L;
|
||||
LuaNarrationHandler(lua_State *L, int ref)
|
||||
: ref(ref)
|
||||
, L(L)
|
||||
{
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
|
||||
lua_pushlightuserdata(L, this);
|
||||
lua_pushcclosure(
|
||||
L,
|
||||
[](lua_State *L) {
|
||||
luaL_checktype(L, 1, LUA_TSTRING);
|
||||
luaL_checktype(L, 2, LUA_TTABLE);
|
||||
LuaNarrationHandler *handler =
|
||||
static_cast<LuaNarrationHandler *>(
|
||||
lua_touserdata(
|
||||
L,
|
||||
lua_upvalueindex(1)));
|
||||
Ogre::String event = lua_tostring(L, 1);
|
||||
std::vector<Ogre::String> choices;
|
||||
int choicesLen = (int)lua_rawlen(L, 2);
|
||||
choices.reserve(choicesLen);
|
||||
for (int i = 1; i <= choicesLen; ++i) {
|
||||
lua_rawgeti(L, 2, i);
|
||||
if (lua_isstring(L, -1))
|
||||
choices.push_back(
|
||||
lua_tostring(L, -1));
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
handler->_narration(event, choices);
|
||||
return 0;
|
||||
},
|
||||
1);
|
||||
lua_setfield(L, -2, "_narration");
|
||||
lua_pushlightuserdata(L, this);
|
||||
lua_pushcclosure(
|
||||
L,
|
||||
[](lua_State *L) {
|
||||
LuaNarrationHandler *handler =
|
||||
static_cast<LuaNarrationHandler *>(
|
||||
lua_touserdata(
|
||||
L,
|
||||
lua_upvalueindex(1)));
|
||||
handler->_finish();
|
||||
return 0;
|
||||
},
|
||||
1);
|
||||
lua_setfield(L, -2, "_finish");
|
||||
lua_pushlightuserdata(L, this);
|
||||
lua_pushcclosure(
|
||||
L,
|
||||
[](lua_State *L) {
|
||||
LuaNarrationHandler *handler =
|
||||
static_cast<LuaNarrationHandler *>(
|
||||
lua_touserdata(
|
||||
L,
|
||||
lua_upvalueindex(1)));
|
||||
int answer = handler->getNarrationAnswer();
|
||||
lua_pushinteger(L, answer);
|
||||
return 1;
|
||||
},
|
||||
1);
|
||||
lua_setfield(L, -2, "_get_narration_answer");
|
||||
lua_pushlightuserdata(L, this);
|
||||
lua_pushcclosure(
|
||||
L,
|
||||
[](lua_State *L) {
|
||||
LuaNarrationHandler *handler =
|
||||
static_cast<LuaNarrationHandler *>(
|
||||
lua_touserdata(
|
||||
L,
|
||||
lua_upvalueindex(1)));
|
||||
lua_pushstring(
|
||||
L, handler->getProperties().c_str());
|
||||
return 1;
|
||||
},
|
||||
1);
|
||||
lua_setfield(L, -2, "_get_properties");
|
||||
lua_pushlightuserdata(L, this);
|
||||
lua_pushcclosure(
|
||||
L,
|
||||
[](lua_State *L) {
|
||||
LuaNarrationHandler *handler =
|
||||
static_cast<LuaNarrationHandler *>(
|
||||
lua_touserdata(
|
||||
L,
|
||||
lua_upvalueindex(1)));
|
||||
lua_pushstring(
|
||||
L, handler->getProperties().c_str());
|
||||
const std::vector<ActionNodeList::ActionNode>
|
||||
&nodes = ECS::get<ActionNodeList>()
|
||||
.dynamicNodes;
|
||||
lua_newtable(L);
|
||||
int i;
|
||||
for (i = 0; i < nodes.size(); i++) {
|
||||
lua_pushinteger(L, i + 1);
|
||||
lua_pushstring(
|
||||
L,
|
||||
nodes[i].props.dump().c_str());
|
||||
lua_settable(L, -3);
|
||||
}
|
||||
return 1;
|
||||
},
|
||||
1);
|
||||
lua_setfield(L, -2, "_get_goals");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
void finish() override
|
||||
{
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
|
||||
int type = lua_getfield(L, -1, "finish");
|
||||
OgreAssert(type == LUA_TFUNCTION, "bad finish()");
|
||||
lua_insert(L, -2);
|
||||
if (lua_pcall(L, 1, 0, 0) != 0) {
|
||||
std::cerr << lua_tostring(L, -1) << std::endl;
|
||||
OgreAssert(false, "lua error");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
_clear_narration();
|
||||
}
|
||||
void activate() override
|
||||
{
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
|
||||
int type = lua_getfield(L, -1, "activate");
|
||||
OgreAssert(type == LUA_TFUNCTION, "bad activate()");
|
||||
lua_insert(L, -2);
|
||||
if (lua_pcall(L, 1, 0, 0) != 0) {
|
||||
std::cerr << lua_tostring(L, -1) << std::endl;
|
||||
OgreAssert(false, "lua error");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
void event(const Ogre::String &evt) override
|
||||
{
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
|
||||
int type = lua_getfield(L, -1, "event");
|
||||
OgreAssert(type == LUA_TFUNCTION, "bad event()");
|
||||
lua_insert(L, -2);
|
||||
lua_pushstring(L, evt.c_str());
|
||||
if (lua_pcall(L, 2, 0, 0) != 0) {
|
||||
std::cerr << lua_tostring(L, -1) << std::endl;
|
||||
OgreAssert(false, "lua error");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct SimpleWordHandler : PlayerActionModule::ActionWordHandler {
|
||||
void operator()(flecs::entity town, int index,
|
||||
const Ogre::String &word) override
|
||||
{
|
||||
TestNarrativeHandler *handle = OGRE_NEW TestNarrativeHandler();
|
||||
const TownNPCs::NPCData &npc =
|
||||
town.get<TownNPCs>().npcs.at(index);
|
||||
flecs::entity e = npc.e;
|
||||
for (const auto &anode : npc.actionNodes) {
|
||||
if (anode.action == word) {
|
||||
nlohmann::json props = anode.props;
|
||||
props["initiator"] =
|
||||
ECS::get<CharacterManagerModule>()
|
||||
.getPlayer()
|
||||
.id();
|
||||
props["recipient"] = e.id();
|
||||
handle->setProperties(props.dump());
|
||||
break;
|
||||
}
|
||||
}
|
||||
ECS::get_mut<GUI>().addNarrationHandler(handle);
|
||||
ECS::modified<GUI>();
|
||||
}
|
||||
};
|
||||
|
||||
PlayerActionModule::PlayerActionModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.module<PlayerActionModule>();
|
||||
ecs.import <CharacterManagerModule>();
|
||||
ecs.component<ActionNodeList>()
|
||||
.on_add([](flecs::entity e, ActionNodeList &alist) {
|
||||
alist.nodeMutex = std::make_shared<std::mutex>();
|
||||
alist.setDirty();
|
||||
alist.nodes.reserve(1000);
|
||||
alist.dynamicNodes.reserve(1000);
|
||||
alist.setUISelected(-1);
|
||||
alist.setReady();
|
||||
})
|
||||
.add(flecs::Singleton);
|
||||
ecs.system<ActionNodeList>("updateNodeList")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([](ActionNodeList &list) {
|
||||
if (list.isBusy())
|
||||
return;
|
||||
if (list.nodes.size() > 0) {
|
||||
Ogre::SceneNode *cameraNode =
|
||||
ECS::get<Camera>().mCameraNode;
|
||||
Ogre::Vector3 cameraPos =
|
||||
cameraNode->_getDerivedPosition();
|
||||
flecs::entity player =
|
||||
ECS::get<CharacterManagerModule>()
|
||||
.getPlayer();
|
||||
if (player.is_valid()) {
|
||||
Ogre::Vector3 playerPos =
|
||||
player.get<CharacterBase>()
|
||||
.mBodyNode
|
||||
->_getDerivedPosition();
|
||||
list.UIquery(playerPos);
|
||||
} else {
|
||||
list.UIquery(cameraPos);
|
||||
}
|
||||
}
|
||||
});
|
||||
ecs.system<ActionNodeList, const Input>("ActivateActionNode")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([this](ActionNodeList &list, const Input &input) {
|
||||
std::lock_guard<std::mutex> lock(*list.nodeMutex);
|
||||
if (input.control & 32)
|
||||
std::cout << "act pressed" << std::endl;
|
||||
if (list.isBusy())
|
||||
return;
|
||||
if (input.act_pressed &&
|
||||
list.getUIData().selected >= 0) {
|
||||
std::cout
|
||||
<< list.dynamicNodes[list.getUIData()
|
||||
.selected]
|
||||
.props.dump(4)
|
||||
<< std::endl;
|
||||
flecs::entity_t townid =
|
||||
list.dynamicNodes[list.getUIData()
|
||||
.selected]
|
||||
.props["town"]
|
||||
.get<flecs::entity_t>();
|
||||
flecs::entity town = ECS::get().entity(townid);
|
||||
int index = list.dynamicNodes[list.getUIData()
|
||||
.selected]
|
||||
.props["index"]
|
||||
.get<int>();
|
||||
for (auto it = actionWords.begin();
|
||||
it != actionWords.end(); it++) {
|
||||
if (it->first ==
|
||||
list.dynamicNodes[list.getUIData()
|
||||
.selected]
|
||||
.action) {
|
||||
(*it->second)(
|
||||
town, index,
|
||||
list.dynamicNodes
|
||||
[list.getUIData()
|
||||
.selected]
|
||||
.action);
|
||||
list.setBusy();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ECS::get<GUI>().enabled)
|
||||
list.setReady();
|
||||
});
|
||||
}
|
||||
|
||||
void PlayerActionModule::addWordHandler(const Ogre::String &word,
|
||||
ActionWordHandler *handler)
|
||||
{
|
||||
actionWords.insert({ word, handler });
|
||||
}
|
||||
|
||||
void PlayerActionModule::removeWordHandler(const Ogre::String &word,
|
||||
ActionWordHandler *handler)
|
||||
{
|
||||
for (auto it = actionWords.begin(); it != actionWords.end();) {
|
||||
if (it->first == word && it->second == handler)
|
||||
it = actionWords.erase(it);
|
||||
else
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
struct LuaWordHandler : PlayerActionModule::ActionWordHandler {
|
||||
lua_State *L;
|
||||
int ref;
|
||||
void operator()(flecs::entity town, int index,
|
||||
const Ogre::String &word) override
|
||||
{
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
|
||||
if (lua_type(L, -1) == LUA_TFUNCTION) {
|
||||
luaL_checktype(L, -1, LUA_TFUNCTION);
|
||||
lua_pushinteger(L, town.id());
|
||||
lua_pushinteger(L, index);
|
||||
lua_pushstring(L, word.c_str());
|
||||
if (lua_pcall(L, 3, 0, 0)) {
|
||||
Ogre::LogManager::getSingleton().stream()
|
||||
<< lua_tostring(L, -1);
|
||||
OgreAssert(false, "Lua error");
|
||||
}
|
||||
} else if (lua_type(L, -1) == LUA_TTABLE) {
|
||||
luaL_checktype(L, -1, LUA_TTABLE);
|
||||
lua_pop(L, 1);
|
||||
LuaNarrationHandler *handle =
|
||||
OGRE_NEW LuaNarrationHandler(L, ref);
|
||||
const TownNPCs::NPCData &npc =
|
||||
town.get<TownNPCs>().npcs.at(index);
|
||||
flecs::entity e = npc.e;
|
||||
for (const auto &anode : npc.actionNodes) {
|
||||
if (anode.action == word) {
|
||||
nlohmann::json props = anode.props;
|
||||
props["initiator"] =
|
||||
ECS::get<CharacterManagerModule>()
|
||||
.getPlayer()
|
||||
.id();
|
||||
props["recipient"] = e.id();
|
||||
handle->setProperties(props.dump());
|
||||
break;
|
||||
}
|
||||
}
|
||||
ECS::get_mut<GUI>().addNarrationHandler(handle);
|
||||
ECS::modified<GUI>();
|
||||
}
|
||||
}
|
||||
};
|
||||
void PlayerActionModule::addLuaWordHandler(const Ogre::String &word,
|
||||
lua_State *L, int ref)
|
||||
{
|
||||
struct LuaWordHandler *handler = OGRE_NEW LuaWordHandler;
|
||||
handler->L = L;
|
||||
handler->ref = ref;
|
||||
addWordHandler(word, handler);
|
||||
}
|
||||
|
||||
void PlayerActionModule::removeLuaWordHandler(const Ogre::String &word,
|
||||
lua_State *L, int ref)
|
||||
{
|
||||
for (auto it = actionWords.begin(); it != actionWords.end();) {
|
||||
LuaWordHandler *handler =
|
||||
static_cast<LuaWordHandler *>(it->second);
|
||||
if (it->first == word && handler->L == L && handler->ref == ref)
|
||||
it = actionWords.erase(it);
|
||||
else
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
int PlayerActionModule::setupLuaActionHandler(lua_State *L)
|
||||
{
|
||||
luaL_checktype(L, 1, LUA_TSTRING);
|
||||
if (lua_type(L, 2) == LUA_TFUNCTION) {
|
||||
luaL_checktype(L, 2, LUA_TFUNCTION);
|
||||
Ogre::String word = lua_tostring(L, 1);
|
||||
lua_pushvalue(L, 2);
|
||||
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
addLuaWordHandler(word, L, ref);
|
||||
} else if (lua_type(L, 2) == LUA_TTABLE) {
|
||||
luaL_checktype(L, 2, LUA_TTABLE);
|
||||
Ogre::String word = lua_tostring(L, 1);
|
||||
lua_pushvalue(L, 2);
|
||||
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
addLuaWordHandler(word, L, ref);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ActionNodeList::updateDynamicNodes()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(*nodeMutex);
|
||||
if (dynamicNodes.size() > nodes.size())
|
||||
dynamicNodes.resize(nodes.size());
|
||||
else {
|
||||
dynamicNodes.clear();
|
||||
dynamicNodes.insert(dynamicNodes.end(), nodes.begin(),
|
||||
nodes.end());
|
||||
}
|
||||
ECS::get().query_builder<const TownNPCs>().each(
|
||||
[this](flecs::entity town, const TownNPCs &npcs) {
|
||||
for (auto it = npcs.npcs.begin(); it != npcs.npcs.end();
|
||||
it++) {
|
||||
dynamicNodes.insert(
|
||||
dynamicNodes.end(),
|
||||
it->second.actionNodes.begin(),
|
||||
it->second.actionNodes.end());
|
||||
}
|
||||
});
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
void ActionNodeList::build()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(*nodeMutex);
|
||||
indexObj = std::make_shared<ActionNodeList::indexObject>(dynamicNodes);
|
||||
indexObj->index.buildIndex();
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
bool ActionNodeList::_query(const Ogre::Vector3 &position,
|
||||
std::vector<size_t> &points,
|
||||
std::vector<float> &distances)
|
||||
{
|
||||
std::vector<size_t> tmppoints;
|
||||
std::vector<float> tmpdistances;
|
||||
points.clear();
|
||||
points.reserve(4);
|
||||
distances.clear();
|
||||
distances.reserve(4);
|
||||
tmppoints.resize(4);
|
||||
tmpdistances.resize(4);
|
||||
if (!indexObj) {
|
||||
dirty = true;
|
||||
return false;
|
||||
}
|
||||
nanoflann::KNNResultSet<float> resultSet(4);
|
||||
resultSet.init(tmppoints.data(), tmpdistances.data());
|
||||
bool ret = indexObj->index.findNeighbors(resultSet, &position.x,
|
||||
nanoflann::SearchParameters());
|
||||
int i;
|
||||
for (i = 0; i < resultSet.size(); i++)
|
||||
if (tmpdistances[i] < 25.0f) {
|
||||
points.push_back(tmppoints[i]);
|
||||
distances.push_back(tmpdistances[i]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ActionNodeList::query_ai(const Ogre::Vector3 &position, float distance,
|
||||
std::vector<size_t> &points,
|
||||
std::vector<float> &distances)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(*nodeMutex);
|
||||
std::vector<size_t> tmppoints;
|
||||
std::vector<float> tmpdistances;
|
||||
points.clear();
|
||||
points.reserve(100);
|
||||
distances.clear();
|
||||
distances.reserve(100);
|
||||
tmppoints.resize(100);
|
||||
tmpdistances.resize(100);
|
||||
if (!indexObj) {
|
||||
dirty = true;
|
||||
return false;
|
||||
}
|
||||
nanoflann::KNNResultSet<float> resultSet(100);
|
||||
resultSet.init(tmppoints.data(), tmpdistances.data());
|
||||
bool ret = indexObj->index.findNeighbors(resultSet, &position.x,
|
||||
nanoflann::SearchParameters());
|
||||
int i;
|
||||
for (i = 0; i < resultSet.size(); i++)
|
||||
if (tmpdistances[i] < distance) {
|
||||
points.push_back(tmppoints[i]);
|
||||
distances.push_back(tmpdistances[i]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ActionNodeList::addNode(ActionNode &node)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(*nodeMutex);
|
||||
int index = nodes.size();
|
||||
nodes.push_back(node);
|
||||
dirty = true;
|
||||
return index;
|
||||
}
|
||||
|
||||
void ActionNodeList::removeNode(int index)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(*nodeMutex);
|
||||
nodes.erase(nodes.begin() + index);
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
const ActionNodeList::UIData &ActionNodeList::getUIData()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(*uidata.mutex);
|
||||
return uidata;
|
||||
}
|
||||
|
||||
void ActionNodeList::setUISelected(int selected)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(*uidata.mutex);
|
||||
|
||||
uidata.selected = selected;
|
||||
}
|
||||
|
||||
void ActionNodeList::setUIPoints(const std::vector<size_t> &points,
|
||||
const std::vector<float> &distances)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(*uidata.mutex);
|
||||
uidata.points = points;
|
||||
uidata.distances = distances;
|
||||
}
|
||||
|
||||
void ActionNodeList::UIquery(const Ogre::Vector3 &position)
|
||||
{
|
||||
bool needBuild = false;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(*nodeMutex);
|
||||
if (dirty || !indexObj)
|
||||
needBuild = true;
|
||||
}
|
||||
if (needBuild)
|
||||
build();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(*uidata.mutex);
|
||||
_query(position, uidata.points, uidata.distances);
|
||||
}
|
||||
}
|
||||
|
||||
void ActionNodeList::setDirty()
|
||||
{
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
void ActionNodeList::setReady()
|
||||
{
|
||||
busy = false;
|
||||
}
|
||||
|
||||
void ActionNodeList::setBusy()
|
||||
{
|
||||
busy = true;
|
||||
}
|
||||
|
||||
bool ActionNodeList::isBusy()
|
||||
{
|
||||
return busy;
|
||||
}
|
||||
}
|
||||
79
src/gamedata/PlayerActionModule.h
Normal file
79
src/gamedata/PlayerActionModule.h
Normal file
@@ -0,0 +1,79 @@
|
||||
#ifndef PLAYERACTIONMODULE_H
|
||||
#define PLAYERACTIONMODULE_H
|
||||
#include <flecs.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <lua.h>
|
||||
#include <Ogre.h>
|
||||
|
||||
namespace ECS {
|
||||
|
||||
struct ActionNodeList {
|
||||
struct indexObject;
|
||||
struct ActionNode {
|
||||
Ogre::String action;
|
||||
Ogre::String action_text;
|
||||
Ogre::Vector3 position;
|
||||
Ogre::Quaternion rotation;
|
||||
float height;
|
||||
float radius;
|
||||
nlohmann::json props;
|
||||
};
|
||||
struct UIData {
|
||||
std::shared_ptr<std::mutex> mutex;
|
||||
int selected;
|
||||
std::vector<size_t> points;
|
||||
std::vector<float> distances;
|
||||
UIData()
|
||||
: mutex(std::make_shared<std::mutex>())
|
||||
, selected(-1)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
bool dirty;
|
||||
bool busy;
|
||||
std::shared_ptr<indexObject> indexObj;
|
||||
struct UIData uidata;
|
||||
bool _query(const Ogre::Vector3 &position, std::vector<size_t> &points,
|
||||
std::vector<float> &distances);
|
||||
|
||||
public:
|
||||
std::shared_ptr<std::mutex> nodeMutex;
|
||||
std::vector<ActionNode> nodes, dynamicNodes;
|
||||
void updateDynamicNodes();
|
||||
void build();
|
||||
bool query_ai(const Ogre::Vector3 &position, float distance,
|
||||
std::vector<size_t> &points,
|
||||
std::vector<float> &distances);
|
||||
int addNode(struct ActionNodeList::ActionNode &node);
|
||||
void removeNode(int index);
|
||||
const UIData &getUIData();
|
||||
void setUISelected(int selected);
|
||||
void setUIPoints(const std::vector<size_t> &points,
|
||||
const std::vector<float> &distances);
|
||||
void UIquery(const Ogre::Vector3 &position);
|
||||
void setDirty(); // node was added or removed
|
||||
void setReady();
|
||||
void setBusy();
|
||||
bool isBusy();
|
||||
};
|
||||
struct PlayerActionModule {
|
||||
struct ActionWordHandler {
|
||||
virtual void operator()(flecs::entity town, int index,
|
||||
const Ogre::String &word) = 0;
|
||||
};
|
||||
|
||||
std::multimap<Ogre::String, ActionWordHandler *>
|
||||
actionWords;
|
||||
|
||||
PlayerActionModule(flecs::world &ecs);
|
||||
void addWordHandler(const Ogre::String &word, ActionWordHandler *handler);
|
||||
void removeWordHandler(const Ogre::String &word, ActionWordHandler *handler);
|
||||
void addLuaWordHandler(const Ogre::String &word, lua_State *L, int ref);
|
||||
void removeLuaWordHandler(const Ogre::String &word, lua_State *L, int ref);
|
||||
int setupLuaActionHandler(lua_State *L);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // PLAYERACTIONMODULE_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,16 @@
|
||||
#ifndef _STATIC_GEOMETRY_MODULE_H_
|
||||
#define _STATIC_GEOMETRY_MODULE_H_
|
||||
#include <flecs.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <Ogre.h>
|
||||
namespace Procedural
|
||||
{
|
||||
class TriangleBuffer;
|
||||
}
|
||||
namespace Ogre
|
||||
{
|
||||
struct LodConfig;
|
||||
}
|
||||
namespace ECS
|
||||
{
|
||||
struct TerrainSlotParent {
|
||||
@@ -14,7 +23,22 @@ struct TerrainItem {
|
||||
};
|
||||
struct TerrainItemNode {
|
||||
Ogre::SceneNode *itemNode;
|
||||
Ogre::StaticGeometry *geo;
|
||||
};
|
||||
struct TerrainItemMeshNode {
|
||||
Ogre::SceneNode *itemNode;
|
||||
Ogre::StaticGeometry *geo;
|
||||
};
|
||||
struct FurnitureItem {
|
||||
Ogre::String properties;
|
||||
std::vector<Ogre::String> tags;
|
||||
};
|
||||
struct FurnitureInstance {
|
||||
Ogre::SceneNode *furniture;
|
||||
};
|
||||
struct GeometryUpdateItem {};
|
||||
|
||||
struct TownCollider {};
|
||||
|
||||
struct StaticGeometryModule {
|
||||
StaticGeometryModule(flecs::world &ecs);
|
||||
@@ -26,8 +50,12 @@ struct StaticGeometryModule {
|
||||
static void setItemProperties(flecs::entity id,
|
||||
Ogre::String properties);
|
||||
static const Ogre::String &getItemProperties(flecs::entity id);
|
||||
static void saveItems();
|
||||
static void loadTemplates();
|
||||
static void saveTemplates();
|
||||
static void saveItems();
|
||||
static void loadItems();
|
||||
static void saveFurniture();
|
||||
static void loadFurniture();
|
||||
static void getItemPositionPerSlot(long x, long y,
|
||||
std::list<Ogre::Vector3> *positions);
|
||||
static void getItemPositions(std::list<Ogre::Vector3> *positions);
|
||||
@@ -37,11 +65,14 @@ struct StaticGeometryModule {
|
||||
static void getItemsProperties(
|
||||
std::list<std::pair<flecs::entity, Ogre::String> > *items);
|
||||
static void createItemGeometry(flecs::entity e);
|
||||
static void createBridge(flecs::entity e, Ogre::SceneNode *sceneNode,
|
||||
Ogre::StaticGeometry *geo);
|
||||
static void createPier(flecs::entity e, Ogre::SceneNode *sceneNode,
|
||||
Ogre::StaticGeometry *geo);
|
||||
static void createHarbour(flecs::entity e, Ogre::SceneNode *sceneNode);
|
||||
static void destroyItemGeometry(flecs::entity e);
|
||||
static nlohmann::json &getTemplates();
|
||||
static void updateItemGeometry(flecs::entity e);
|
||||
static void addTriangleBufferWork(const Ogre::String &meshName,
|
||||
Ogre::StaticGeometry *geo,
|
||||
const Ogre::Vector3 &position,
|
||||
const Ogre::Quaternion &rotation,
|
||||
const Procedural::TriangleBuffer &tb);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -198,10 +198,12 @@ out:
|
||||
long world_grid_y = world_y + grid_center_y;
|
||||
float amplitude = 150.0f;
|
||||
if (world_grid_x < 0 || world_grid_y < 0) {
|
||||
#if 0
|
||||
std::cout << "world: " << world_x << " " << world_y
|
||||
<< " ";
|
||||
std::cout << "grid: " << world_grid_x << " ";
|
||||
std::cout << world_grid_y << std::endl;
|
||||
#endif
|
||||
return -amplitude;
|
||||
}
|
||||
OgreAssert(world_grid_x >= 0, "bad world x");
|
||||
@@ -263,7 +265,6 @@ public:
|
||||
void createTerrainChunk(Ogre::TerrainGroup *terrainGroup, long x,
|
||||
long y)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(mtx);
|
||||
Ogre::Terrain *terrain = terrainGroup->getTerrain(x, y);
|
||||
float minH = terrain->getMinHeight();
|
||||
float maxH = terrain->getMaxHeight();
|
||||
@@ -277,7 +278,7 @@ public:
|
||||
float worldSize = terrain->getWorldSize();
|
||||
float scaled = worldSize / (size - 1);
|
||||
Ogre::Vector3 bodyPosition = terrain->getPosition();
|
||||
bodyPosition.y += (maxH + minH) / 2.0f;
|
||||
// bodyPosition.y += (maxH + minH) / 2.0f;
|
||||
bodyPosition.x += worldSize / 2.0f;
|
||||
bodyPosition.z += worldSize / 2.0f;
|
||||
Ogre::Vector3 offset =
|
||||
@@ -337,17 +338,30 @@ public:
|
||||
}
|
||||
void update()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(mtx);
|
||||
static bool created = false;
|
||||
std::deque<struct gen_collider> output;
|
||||
while (!collider_queue.empty()) {
|
||||
Ogre::TerrainGroup *group =
|
||||
collider_queue.front().group;
|
||||
if (group->isDerivedDataUpdateInProgress())
|
||||
break;
|
||||
long x = collider_queue.front().x;
|
||||
long y = collider_queue.front().y;
|
||||
// std::cout << x << " " << y << " "
|
||||
// << collider_queue.size() << std::endl;
|
||||
Ogre::Terrain *terrain = group->getTerrain(x, y);
|
||||
Ogre::Vector3 worldPos;
|
||||
group->convertTerrainSlotToWorldPosition(x, y,
|
||||
&worldPos);
|
||||
#if 0
|
||||
std::cout << "terrain: " << terrain;
|
||||
if (terrain)
|
||||
std::cout
|
||||
<< terrain->getHeightData() << " "
|
||||
<< terrain->isLoaded() << " "
|
||||
<< terrain->isDerivedDataUpdateInProgress()
|
||||
<< std::endl;
|
||||
#endif
|
||||
if (terrain && terrain->getHeightData() &&
|
||||
terrain->isLoaded() &&
|
||||
!terrain->isDerivedDataUpdateInProgress()) {
|
||||
@@ -356,8 +370,8 @@ public:
|
||||
Ogre::StringConverter::toString(x) +
|
||||
" " +
|
||||
Ogre::StringConverter::toString(y));
|
||||
float minH = terrain->getMinHeight();
|
||||
float maxH = terrain->getMaxHeight();
|
||||
// float minH = terrain->getMinHeight();
|
||||
// float maxH = terrain->getMaxHeight();
|
||||
int size = terrain->getSize();
|
||||
float worldSize = terrain->getWorldSize();
|
||||
{
|
||||
@@ -404,8 +418,8 @@ public:
|
||||
created = true;
|
||||
}
|
||||
#endif
|
||||
collider_queue.pop_front();
|
||||
// FIXME: create entities and components instead
|
||||
#if 0
|
||||
Ogre::SceneNode *items =
|
||||
terrain->_getRootSceneNode()
|
||||
->createChildSceneNode();
|
||||
@@ -425,20 +439,24 @@ public:
|
||||
what->setOrientation(item.rotation);
|
||||
what->setPosition(item.position);
|
||||
}
|
||||
#endif
|
||||
/* Spawn items */
|
||||
StaticGeometryModule::addGeometryForSlot(x, y);
|
||||
collider_queue.pop_front();
|
||||
|
||||
} else {
|
||||
output.push_back(collider_queue.front());
|
||||
/* Terrain data not ready maybe move to next terrain */
|
||||
gen_collider m = collider_queue.front();
|
||||
collider_queue.pop_front();
|
||||
}
|
||||
if (collider_queue.empty() &&
|
||||
!ECS::get<Terrain>().mTerrainReady) {
|
||||
ECS::get_mut<Terrain>().mTerrainReady = true;
|
||||
ECS::modified<Terrain>();
|
||||
collider_queue.push_back(m);
|
||||
break; // allow system to move on
|
||||
}
|
||||
}
|
||||
collider_queue = output;
|
||||
if (collider_queue.empty() &&
|
||||
!ECS::get<Terrain>().mTerrainReady) {
|
||||
ECS::get_mut<Terrain>().mTerrainReady = true;
|
||||
ECS::modified<Terrain>();
|
||||
}
|
||||
}
|
||||
};
|
||||
class DummyPageProvider : public Ogre::PageProvider {
|
||||
@@ -504,9 +522,6 @@ TerrainModule::TerrainModule(flecs::world &ecs)
|
||||
OgreAssert(terrain.mTerrainGlobals,
|
||||
"Failed to allocate global options");
|
||||
|
||||
Ogre::LogManager::getSingleton().setMinLogLevel(
|
||||
Ogre::LML_TRIVIAL);
|
||||
|
||||
terrain.mTerrainGroup =
|
||||
OGRE_NEW Ogre::TerrainGroup(
|
||||
eng.mScnMgr,
|
||||
@@ -717,11 +732,12 @@ TerrainModule::TerrainModule(flecs::world &ecs)
|
||||
ECS::get().add<CanSetPlayerPosition>();
|
||||
}
|
||||
});
|
||||
#if 0
|
||||
ecs.system<const Terrain>("SetPlayerPosition")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<CanSetPlayerPosition>()
|
||||
.each([this](const Terrain &terrain) {
|
||||
flecs::entity player = ECS::player;
|
||||
flecs::entity player = ECS::get<CharacterM;
|
||||
if (!player.is_valid())
|
||||
return;
|
||||
if (!player.has<CharacterLocation>())
|
||||
@@ -745,6 +761,7 @@ TerrainModule::TerrainModule(flecs::world &ecs)
|
||||
player.modified<CharacterLocation>();
|
||||
ECS::get().remove<CanSetPlayerPosition>();
|
||||
});
|
||||
#endif
|
||||
ecs.system<const Terrain>("UpdateTerrainGroup")
|
||||
.kind(flecs::OnUpdate)
|
||||
.interval(2.0f)
|
||||
@@ -797,4 +814,4 @@ void TerrainModule::defineTerrain(long x, long y)
|
||||
ECS::get<Terrain>().definer->define(ECS::get<Terrain>().mTerrainGroup,
|
||||
x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,6 +468,31 @@ WaterModule::WaterModule(flecs::world &ecs)
|
||||
std::cout << "Water setup done\n";
|
||||
ECS::get().add<WaterAlmostReady>();
|
||||
})
|
||||
.on_remove([](flecs::entity e, WaterSurface &water) {
|
||||
const Ogre::String renderTargetName =
|
||||
"ReflectionRefractionTexture";
|
||||
ECS::get<EngineData>()
|
||||
.mScnMgr->getRenderQueue()
|
||||
->setRenderableListener(nullptr);
|
||||
water.mReflectionTexture->removeAllViewports();
|
||||
ECS::get<EngineData>().mScnMgr->destroyCamera(
|
||||
water.mRefractionCamera);
|
||||
ECS::get<EngineData>().mScnMgr->destroyCamera(
|
||||
water.mRefractionDepthCamera);
|
||||
ECS::get<EngineData>().mScnMgr->destroyCamera(
|
||||
water.mReflectionCamera);
|
||||
ECS::get<EngineData>().mScnMgr->destroyCamera(
|
||||
water.mReflectionDepthCamera);
|
||||
Ogre::TextureManager::getSingleton().remove(
|
||||
renderTargetName);
|
||||
water.mWaterNode->destroyAllChildrenAndObjects();
|
||||
ECS::get<EngineData>().mScnMgr->destroySceneNode(
|
||||
water.mWaterNode);
|
||||
Ogre::MaterialManager::getSingleton().remove(
|
||||
"Water/Depth");
|
||||
Ogre::MaterialManager::getSingleton().remove(
|
||||
"Water/Above");
|
||||
})
|
||||
.add(flecs::Singleton);
|
||||
#if 0
|
||||
ecs.component<WaterBody>().add(flecs::Singleton);
|
||||
|
||||
@@ -1,38 +1,5 @@
|
||||
#include <set>
|
||||
#include "goap.h"
|
||||
bool BaseAction::can_run(const ECS::Blackboard &state, bool debug)
|
||||
namespace goap
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1,77 +1,35 @@
|
||||
#ifndef H_GOAP_H_
|
||||
#define H_GOAP_H_
|
||||
#undef NDEBUG
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <flecs.h>
|
||||
namespace ECS
|
||||
#include <Ogre.h>
|
||||
namespace goap
|
||||
{
|
||||
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 {
|
||||
template <typename State> struct BaseAction {
|
||||
std::string m_name;
|
||||
State prereq;
|
||||
State effects;
|
||||
int m_cost;
|
||||
|
||||
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)
|
||||
BaseAction(const std::string &name, const State &prereq, const State &effects, int cost)
|
||||
: m_name(name)
|
||||
, prereq(prereq)
|
||||
, effects(effects)
|
||||
, m_cost(cost)
|
||||
{
|
||||
}
|
||||
const std::string &get_name() const
|
||||
virtual const std::string &get_name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
void plan_effects(ECS::Blackboard &state)
|
||||
void plan_effects(State &state)
|
||||
{
|
||||
// std::cout << m_name << " pre: " << &state << " " << state.flags
|
||||
// << std::endl;
|
||||
@@ -79,42 +37,20 @@ public:
|
||||
// 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;
|
||||
virtual bool can_run(const State &state, bool debug = false)
|
||||
{
|
||||
return state.distance_to(prereq) == 0;
|
||||
}
|
||||
virtual void _plan_effects(State &state)
|
||||
{
|
||||
state.apply(effects);
|
||||
}
|
||||
// constant cost
|
||||
virtual int get_cost(const State &state) const
|
||||
{
|
||||
return m_cost;
|
||||
}
|
||||
};
|
||||
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;
|
||||
@@ -190,20 +126,24 @@ template <typename State, typename Act, int N = 100> class BasePlanner {
|
||||
public:
|
||||
struct BaseGoal {
|
||||
std::string m_name;
|
||||
State goal;
|
||||
|
||||
public:
|
||||
BaseGoal(const std::string &name)
|
||||
: m_name(name)
|
||||
BaseGoal(const std::string &name, const State &goal)
|
||||
: m_name(name), goal(goal)
|
||||
{
|
||||
}
|
||||
/** Checks if the goal is reached for the given state. */
|
||||
virtual bool is_reached(const State &state) const
|
||||
{
|
||||
return distance_to(state) == 0;
|
||||
return distance_to(state) == 0;
|
||||
}
|
||||
|
||||
/** Computes the distance from state to goal. */
|
||||
virtual int distance_to(const State &state) const = 0;
|
||||
virtual int distance_to(const State &state) const
|
||||
{
|
||||
return state.distance_to(goal);
|
||||
}
|
||||
|
||||
virtual ~BaseGoal() = default;
|
||||
const std::string &get_name() const
|
||||
@@ -216,7 +156,7 @@ public:
|
||||
*
|
||||
* If path is given, then the found path is stored there.
|
||||
*/
|
||||
int plan(const State &state, BaseGoal &goal, Act *actions[],
|
||||
int plan(const State &state, const BaseGoal &goal, Act *actions[],
|
||||
unsigned action_count, Act **path = nullptr, int path_len = 10)
|
||||
{
|
||||
visited_states_array_to_list(nodes, N);
|
||||
@@ -244,7 +184,8 @@ public:
|
||||
}
|
||||
|
||||
if (len > path_len) {
|
||||
return -1;
|
||||
OgreAssert(len <= path_len, "Out of plan length");
|
||||
return -3;
|
||||
}
|
||||
|
||||
if (path) {
|
||||
@@ -255,6 +196,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
OgreAssert(len >= 0, "Bad plan length");
|
||||
return len;
|
||||
}
|
||||
|
||||
@@ -274,6 +216,7 @@ public:
|
||||
}
|
||||
|
||||
if (!gc) {
|
||||
OgreAssert(gc, "Out of memory");
|
||||
return -2 /* OOM */;
|
||||
}
|
||||
|
||||
@@ -337,14 +280,16 @@ public:
|
||||
return -1 /* No path */;
|
||||
}
|
||||
};
|
||||
template <class RunnerType> struct DeclareAction : public BaseAction {
|
||||
template <class RunnerType, class State>
|
||||
struct DeclareAction : public BaseAction<State> {
|
||||
RunnerType runner;
|
||||
template <class... Args>
|
||||
DeclareAction(const std::string &name, Args... args)
|
||||
: runner(args...)
|
||||
, BaseAction(name, &runner)
|
||||
, BaseAction<State>(name, &runner)
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
16
src/gamedata/items/CMakeLists.txt
Normal file
16
src/gamedata/items/CMakeLists.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
project(items)
|
||||
find_package(OGRE REQUIRED COMPONENTS Bites Bullet Paging Terrain Overlay CONFIG)
|
||||
find_package(Bullet REQUIRED)
|
||||
find_package(nlohmann_json REQUIRED)
|
||||
find_package(OgreProcedural REQUIRED CONFIG)
|
||||
add_library(items STATIC items.cpp harbour.cpp temple.cpp town.cpp)
|
||||
target_include_directories(items PUBLIC . ${CMAKE_SOURCE_DIR}/src/FastNoiseLite)
|
||||
target_link_libraries(items PRIVATE
|
||||
flecs::flecs_static
|
||||
nlohmann_json::nlohmann_json
|
||||
GameData
|
||||
OgreMain
|
||||
OgreBites
|
||||
editor
|
||||
physics
|
||||
)
|
||||
983
src/gamedata/items/harbour.cpp
Normal file
983
src/gamedata/items/harbour.cpp
Normal file
@@ -0,0 +1,983 @@
|
||||
#include <iostream>
|
||||
#include <Ogre.h>
|
||||
#include <OgreTerrainGroup.h>
|
||||
#include <OgreImGuiOverlay.h>
|
||||
#include <OgreRTShaderSystem.h>
|
||||
#include <Procedural.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "Components.h"
|
||||
#include "GameData.h"
|
||||
#include "EditorGizmoModule.h"
|
||||
#include "TerrainModule.h"
|
||||
#include "StaticGeometryModule.h"
|
||||
#include "items.h"
|
||||
#include "harbour.h"
|
||||
namespace ECS
|
||||
{
|
||||
namespace Items
|
||||
{
|
||||
/* This is editor function */
|
||||
static bool findPierOffset(float &offset)
|
||||
{
|
||||
Ogre::Vector3 basePos =
|
||||
ECS::get<EditorGizmo>().sceneNode->_getDerivedPosition();
|
||||
Ogre::Quaternion baseRot =
|
||||
ECS::get<EditorGizmo>().sceneNode->_getDerivedOrientation();
|
||||
Ogre::Vector3 direction = baseRot * Ogre::Vector3(0, 0, 1);
|
||||
float length = 0.0f;
|
||||
while (length < 250.0f) {
|
||||
Ogre::Vector3 currentPosition = basePos + direction * length;
|
||||
float height = ECS::get<Terrain>()
|
||||
.mTerrainGroup->getHeightAtWorldPosition(
|
||||
currentPosition);
|
||||
if (height < -4.0f) {
|
||||
offset = length;
|
||||
break;
|
||||
}
|
||||
length += 2.0f;
|
||||
}
|
||||
return length < 250.0f;
|
||||
}
|
||||
static bool findPierOffsetAndLengthAndDepth(float &offset, float &length,
|
||||
float &depth)
|
||||
{
|
||||
if (!findPierOffset(offset))
|
||||
return false;
|
||||
Ogre::Vector3 basePos =
|
||||
ECS::get<EditorGizmo>().sceneNode->_getDerivedPosition();
|
||||
Ogre::Quaternion baseRot =
|
||||
ECS::get<EditorGizmo>().sceneNode->_getDerivedOrientation();
|
||||
Ogre::Vector3 direction = baseRot * Ogre::Vector3(0, 0, 1);
|
||||
length = 0.0f;
|
||||
depth = 4.0f;
|
||||
while (length < 60.0f) {
|
||||
Ogre::Vector3 currentPosition =
|
||||
basePos + direction * (offset + length);
|
||||
float height = ECS::get<Terrain>()
|
||||
.mTerrainGroup->getHeightAtWorldPosition(
|
||||
currentPosition);
|
||||
if (depth < -height)
|
||||
depth = -height;
|
||||
if (height > -4.0f)
|
||||
break;
|
||||
length += 6.0f;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static void findPierHeight(float maxLength, float &height)
|
||||
{
|
||||
Ogre::Vector3 basePos =
|
||||
ECS::get<EditorGizmo>().sceneNode->_getDerivedPosition();
|
||||
Ogre::Quaternion baseRot =
|
||||
ECS::get<EditorGizmo>().sceneNode->_getDerivedOrientation();
|
||||
Ogre::Vector3 direction = baseRot * Ogre::Vector3(0, 0, 1);
|
||||
float length = 0.0f;
|
||||
height = 0.0f;
|
||||
while (length < 60.0f) {
|
||||
Ogre::Vector3 currentPosition = basePos + direction * (length);
|
||||
float dheight =
|
||||
ECS::get<Terrain>()
|
||||
.mTerrainGroup->getHeightAtWorldPosition(
|
||||
currentPosition);
|
||||
if (height < dheight)
|
||||
height = dheight;
|
||||
length += 1.0f;
|
||||
}
|
||||
}
|
||||
static void findPierPath(float pathLength, std::vector<Ogre::Vector3> &path)
|
||||
{
|
||||
float minHeight = 0.2f;
|
||||
int i;
|
||||
Ogre::Vector3 basePos =
|
||||
ECS::get<EditorGizmo>().sceneNode->_getDerivedPosition();
|
||||
Ogre::Quaternion baseRot =
|
||||
ECS::get<EditorGizmo>().sceneNode->_getDerivedOrientation();
|
||||
Ogre::Vector3 direction = baseRot * Ogre::Vector3(0, 0, 1);
|
||||
float length = 0.0f;
|
||||
while (length < pathLength) {
|
||||
Ogre::Vector3 currentPosition = basePos + direction * (length);
|
||||
float dheight =
|
||||
ECS::get<Terrain>()
|
||||
.mTerrainGroup->getHeightAtWorldPosition(
|
||||
currentPosition);
|
||||
if (dheight < minHeight)
|
||||
dheight = minHeight;
|
||||
Ogre::Vector3 localOffset = Ogre::Vector3(0, 0, 1) * length;
|
||||
Ogre::Vector3 localFromWorld =
|
||||
ECS::get<EditorGizmo>()
|
||||
.sceneNode->convertWorldToLocalPosition(
|
||||
{ currentPosition.x, dheight,
|
||||
currentPosition.z });
|
||||
path.push_back(
|
||||
{ localOffset.x, localFromWorld.y, localOffset.z });
|
||||
length += 2.0f;
|
||||
}
|
||||
if (path.size() == 0) {
|
||||
path.push_back(Ogre::Vector3(0, 0, 0));
|
||||
path.push_back(Ogre::Vector3(0, 0, pathLength));
|
||||
}
|
||||
std::vector<Ogre::Vector3> tmppath = path;
|
||||
path.clear();
|
||||
for (auto &pt : tmppath) {
|
||||
if (path.size() == 0)
|
||||
path.push_back(pt);
|
||||
else {
|
||||
if (path.back().z + 0.5f >= pt.z)
|
||||
continue;
|
||||
else
|
||||
path.push_back(pt);
|
||||
}
|
||||
}
|
||||
Ogre::Vector3 lastp = path.back();
|
||||
for (i = 1; i < path.size(); i++) {
|
||||
Ogre::Vector3 &prev = path[i - 1];
|
||||
if (path[i].y > prev.y)
|
||||
continue;
|
||||
else if (path[i].y < prev.y) {
|
||||
float d = prev.y - path[i].y;
|
||||
if (d > 0.15f)
|
||||
path[i].y = prev.y - 0.15f;
|
||||
}
|
||||
}
|
||||
float dy = path.back().y - lastp.y;
|
||||
if (dy > 0)
|
||||
path.push_back({ lastp.x, lastp.y, path.back().z + dy * 3.0f });
|
||||
path.push_back({ path.back().x, path.back().y, path.back().z + 2.0f });
|
||||
}
|
||||
bool editHarbourDistrict(nlohmann::json &jitem)
|
||||
{
|
||||
float plazzaRadius = 5.0f;
|
||||
float plazzaHeight = 0.2f;
|
||||
float plazzaElevation = 0.0f;
|
||||
float centerOffset = 0.0f;
|
||||
bool plazza = false;
|
||||
bool changed = false;
|
||||
struct LotData {
|
||||
float distance;
|
||||
float angle;
|
||||
float width;
|
||||
float depth;
|
||||
float elevation;
|
||||
bool valid;
|
||||
Ogre::String type;
|
||||
Ogre::String properties;
|
||||
};
|
||||
std::vector<LotData> centerBuildings;
|
||||
nlohmann::json &j = jitem;
|
||||
if (j.find("centerOffset") != j.end())
|
||||
centerOffset = j["centerOffset"].get<float>();
|
||||
if (j.find("plazza") != j.end())
|
||||
plazza = j["plazza"].get<bool>();
|
||||
if (j.find("plazzaRadius") != j.end())
|
||||
plazzaRadius = j["plazzaRadius"].get<float>();
|
||||
if (j.find("plazzaHeight") != j.end())
|
||||
plazzaHeight = j["plazzaHeight"].get<float>();
|
||||
if (j.find("plazzaElevation") != j.end())
|
||||
plazzaElevation = j["plazzaElevation"].get<float>();
|
||||
if (j.find("centerBuildings") != j.end()) {
|
||||
for (auto &jb : j["centerBuildings"]) {
|
||||
LotData data;
|
||||
data.distance = 100.0f;
|
||||
data.angle = 0;
|
||||
data.width = 50.0f;
|
||||
data.depth = 50.0f;
|
||||
data.elevation = 0.0f;
|
||||
data.type = "base";
|
||||
data.properties = "{}";
|
||||
data.valid = true;
|
||||
if (jb.find("distance") != jb.end())
|
||||
data.distance = jb["distance"].get<float>();
|
||||
if (jb.find("angle") != jb.end())
|
||||
data.angle = jb["angle"].get<float>();
|
||||
if (jb.find("width") != jb.end())
|
||||
data.width = jb["width"].get<float>();
|
||||
if (jb.find("depth") != jb.end())
|
||||
data.depth = jb["depth"].get<float>();
|
||||
if (jb.find("elevation") != jb.end())
|
||||
data.elevation = jb["elevation"].get<float>();
|
||||
if (jb.find("type") != jb.end())
|
||||
data.type = jb["type"].get<Ogre::String>();
|
||||
if (jb.find("properties") != jb.end())
|
||||
data.properties =
|
||||
jb["properties"].get<Ogre::String>();
|
||||
centerBuildings.push_back(data);
|
||||
}
|
||||
}
|
||||
if (ImGui::SliderFloat("Center Offset", ¢erOffset, 0.0f, 100.0f))
|
||||
changed = true;
|
||||
if (ImGui::Checkbox("Plazza", &plazza))
|
||||
changed = true;
|
||||
if (plazza) {
|
||||
if (ImGui::SliderFloat("Plazza Radius", &plazzaRadius, 5.0f,
|
||||
100.0f))
|
||||
changed = true;
|
||||
if (ImGui::SliderFloat("Plazza Height", &plazzaHeight, 0.2f,
|
||||
1.0f))
|
||||
changed = true;
|
||||
if (ImGui::SliderFloat("Plazza Elevation", &plazzaElevation,
|
||||
-5.0f, 5.0f))
|
||||
changed = true;
|
||||
}
|
||||
int count = 0;
|
||||
for (auto &b : centerBuildings) {
|
||||
ImGui::Text("Lot %d", count);
|
||||
Ogre::String ms = "##" + Ogre::StringConverter::toString(count);
|
||||
if (ImGui::SliderFloat(("Lot distance" + ms).c_str(),
|
||||
&b.distance, 0.0f, 100.0f))
|
||||
changed = true;
|
||||
if (ImGui::SliderFloat(("Lot angle" + ms).c_str(), &b.angle,
|
||||
0.0f, 360.0f))
|
||||
changed = true;
|
||||
if (ImGui::SliderFloat(("Width" + ms).c_str(), &b.width, 10.0f,
|
||||
200.0f))
|
||||
changed = true;
|
||||
if (ImGui::SliderFloat(("Depth" + ms).c_str(), &b.depth, 10.0f,
|
||||
200.0f))
|
||||
changed = true;
|
||||
if (ImGui::SliderFloat(("Elevation" + ms).c_str(), &b.elevation,
|
||||
-10.0f, 10.0f))
|
||||
changed = true;
|
||||
if (ImGui::SmallButton(("Delete" + ms).c_str()))
|
||||
b.valid = false;
|
||||
count++;
|
||||
}
|
||||
if (ImGui::SmallButton("Add building")) {
|
||||
changed = true;
|
||||
LotData data;
|
||||
data.distance = 100.0f;
|
||||
data.angle = 0;
|
||||
data.width = 50.0f;
|
||||
data.depth = 50.0f;
|
||||
data.elevation = 0.0f;
|
||||
data.type = "base";
|
||||
data.properties = "{}";
|
||||
data.valid = true;
|
||||
centerBuildings.push_back(data);
|
||||
}
|
||||
if (changed) {
|
||||
j["centerOffset"] = centerOffset;
|
||||
j["plazza"] = plazza;
|
||||
j["plazzaRadius"] = plazzaRadius;
|
||||
j["plazzaHeight"] = plazzaHeight;
|
||||
j["plazzaElevation"] = plazzaElevation;
|
||||
nlohmann::json lots = nlohmann::json::array();
|
||||
for (const auto &d : centerBuildings) {
|
||||
nlohmann::json jdata;
|
||||
if (!d.valid)
|
||||
continue;
|
||||
jdata["distance"] = d.distance;
|
||||
jdata["angle"] = d.angle;
|
||||
jdata["width"] = d.width;
|
||||
jdata["depth"] = d.depth;
|
||||
jdata["elevation"] = d.elevation;
|
||||
jdata["type"] = d.type;
|
||||
jdata["properties"] = d.properties;
|
||||
lots.push_back(jdata);
|
||||
}
|
||||
j["centerBuildings"] = lots;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
void createHarbourPopup(const std::pair<flecs::entity, Ogre::String> item)
|
||||
{
|
||||
bool lighthouse = false;
|
||||
float lighthouseDistance = 0.0f;
|
||||
float lighthouseAngle = 0.0f;
|
||||
float centerOffset = 0.0f;
|
||||
bool changed = false;
|
||||
|
||||
Ogre::String prop = StaticGeometryModule::getItemProperties(item.first);
|
||||
nlohmann::json j = nlohmann::json::parse(prop);
|
||||
nlohmann::json jcenter = nlohmann::json::object();
|
||||
if (j.find("lighthouse") != j.end())
|
||||
lighthouse = j["lighthouse"].get<bool>();
|
||||
if (j.find("lighthouseDistance") != j.end())
|
||||
lighthouseDistance = j["lighthouseDistance"].get<float>();
|
||||
if (j.find("lighthouseAngle") != j.end())
|
||||
lighthouseAngle = j["lighthouseAngle"].get<float>();
|
||||
if (j.find("centerOffset") != j.end())
|
||||
centerOffset = j["centerOffset"].get<float>();
|
||||
if (j.find("center") != j.end())
|
||||
jcenter = j["center"];
|
||||
if (ImGui::Checkbox("Lighthouse", &lighthouse))
|
||||
changed = true;
|
||||
if (lighthouse) {
|
||||
if (ImGui::SliderFloat("Lighthouse Distance",
|
||||
&lighthouseDistance, 0.0f, 100.0f))
|
||||
changed = true;
|
||||
if (ImGui::SliderFloat("Lighthouse Angle", &lighthouseAngle,
|
||||
-90.0f, 90.0f))
|
||||
changed = true;
|
||||
}
|
||||
if (ImGui::SliderFloat("Center Offset Global", ¢erOffset, 0.0f,
|
||||
100.0f))
|
||||
changed = true;
|
||||
changed = changed || editHarbourDistrict(jcenter);
|
||||
if (changed) {
|
||||
j["lighthouse"] = lighthouse;
|
||||
j["lighthouseDistance"] = lighthouseDistance;
|
||||
j["lighthouseAngle"] = lighthouseAngle;
|
||||
j["center"] = jcenter;
|
||||
j["centerOffset"] = centerOffset;
|
||||
StaticGeometryModule::setItemProperties(item.first, j.dump());
|
||||
StaticGeometryModule::saveItems();
|
||||
StaticGeometryModule::destroyItemGeometry(item.first);
|
||||
StaticGeometryModule::createItemGeometry(item.first);
|
||||
}
|
||||
ImGui::Text("%s", j.dump(4).c_str());
|
||||
}
|
||||
void createHarbourItem()
|
||||
{
|
||||
Ogre::Vector3 itemPosition =
|
||||
ECS::get<EditorGizmo>().sceneNode->_getDerivedPosition();
|
||||
Ogre::Quaternion itemOrientation =
|
||||
ECS::get<EditorGizmo>().sceneNode->_getDerivedOrientation();
|
||||
float pierLength, pierDepth;
|
||||
float pierOffset;
|
||||
float pierHeight;
|
||||
if (!findPierOffsetAndLengthAndDepth(pierOffset, pierLength, pierDepth))
|
||||
return;
|
||||
findPierHeight(pierOffset + pierLength, pierHeight);
|
||||
std::vector<Ogre::Vector3> pierPath;
|
||||
findPierPath(pierOffset + 2.0f, pierPath);
|
||||
flecs::entity e = StaticGeometryModule::createItem(
|
||||
itemPosition, itemOrientation, "harbour");
|
||||
Ogre::String prop = StaticGeometryModule::getItemProperties(e);
|
||||
nlohmann::json j = nlohmann::json::parse(prop);
|
||||
j["pierOffset"] = pierOffset;
|
||||
j["pierLength"] = pierLength;
|
||||
j["pierDepth"] = pierDepth;
|
||||
j["pierHeight"] = pierHeight;
|
||||
nlohmann::json p = nlohmann::json::array();
|
||||
for (const auto &pt : pierPath) {
|
||||
nlohmann::json pj;
|
||||
to_json(pj, pt);
|
||||
p.push_back(pj);
|
||||
}
|
||||
j["pierPath"] = p;
|
||||
StaticGeometryModule::setItemProperties(e, j.dump());
|
||||
// setHarbourSurface();
|
||||
StaticGeometryModule::saveItems();
|
||||
// updateWorldTexture();
|
||||
// updateHeightmap();
|
||||
// TerrainModule::save_heightmap();
|
||||
}
|
||||
void createHarbourMenu()
|
||||
{
|
||||
if (ImGui::MenuItem("Create"))
|
||||
createHarbourItem();
|
||||
}
|
||||
}
|
||||
namespace Geometry
|
||||
{
|
||||
void createBridge(flecs::entity e, Ogre::SceneNode *sceneNode,
|
||||
Ogre::StaticGeometry *geo)
|
||||
{
|
||||
int i;
|
||||
Procedural::TriangleBuffer tb;
|
||||
Ogre::MaterialPtr harbourMaterial;
|
||||
harbourMaterial = Ogre::MaterialManager::getSingleton().getByName(
|
||||
"proceduralMaterialHarbour" +
|
||||
Ogre::StringConverter::toString(e.id()));
|
||||
float stepWidth = 12.0f;
|
||||
float stepHeight = 0.15f;
|
||||
float stepDepth = 0.25f;
|
||||
float stairsHeight = 0.0f;
|
||||
float stairsOffset = 0.0f;
|
||||
std::vector<Ogre::Vector3> pierPath;
|
||||
Ogre::String props = e.get<TerrainItem>().properties;
|
||||
nlohmann::json jp = nlohmann::json::parse(props);
|
||||
if (jp.find("stairsHeight") != jp.end())
|
||||
stairsHeight = jp["stairsHeight"].get<float>();
|
||||
if (jp.find("stairsOffset") != jp.end())
|
||||
stairsOffset = jp["stairsOffset"].get<float>();
|
||||
if (jp.find("stepHeight") != jp.end())
|
||||
stepHeight = jp["stepHeight"].get<float>();
|
||||
if (jp.find("stepDepth") != jp.end())
|
||||
stepDepth = jp["stepDepth"].get<float>();
|
||||
if (jp.find("pierPath") != jp.end()) {
|
||||
pierPath.reserve(jp["pierPath"].size());
|
||||
for (auto &jpt : jp["pierPath"]) {
|
||||
Ogre::Vector3 pt;
|
||||
from_json(jpt, pt);
|
||||
pierPath.push_back(pt);
|
||||
}
|
||||
}
|
||||
#if 1
|
||||
Procedural::Shape *pierShape = new Procedural::Shape();
|
||||
pierShape->addPoint(8.f, -0.7f);
|
||||
pierShape->addPoint(6.f, 0.7f);
|
||||
pierShape->addPoint(6.05f, 0.65f);
|
||||
pierShape->addPoint(0.f, 0.6f);
|
||||
pierShape->addPoint(-6.05f, 0.65f);
|
||||
pierShape->addPoint(-6.0f, 0.7f);
|
||||
pierShape->addPoint(-8.f, -0.7f);
|
||||
pierShape->close();
|
||||
|
||||
Procedural::CubicHermiteSpline3 *pierCurve;
|
||||
Procedural::Path pierCurvePath;
|
||||
if (jp.find("pierPath") != jp.end()) {
|
||||
pierPath.reserve(jp["pierPath"].size());
|
||||
pierCurve = new Procedural::CubicHermiteSpline3();
|
||||
// pierCurve = new Procedural::RoundedCornerSpline3();
|
||||
#if 0
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 5; i++) {
|
||||
Ogre::Vector3 pt;
|
||||
from_json(jp["pierPath"][i], pt);
|
||||
pierPath.push_back(pt);
|
||||
pierCurve->addPoint(
|
||||
pt, pt - Ogre::Vector3(0, 0, -1),
|
||||
pt + Ogre::Vector3(0, 0, 1));
|
||||
}
|
||||
Ogre::Vector3 pt2;
|
||||
from_json(jp["pierPath"].back(), pt2);
|
||||
pierPath.push_back(pt2);
|
||||
pierCurve->addPoint(pt2);
|
||||
for (const auto &m : pierPath) {
|
||||
std::cout << m << std::endl;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if 1
|
||||
for (auto &jpt : jp["pierPath"]) {
|
||||
Ogre::Vector3 pt;
|
||||
from_json(jpt, pt);
|
||||
pierPath.push_back(pt);
|
||||
pierCurve->addPoint(pt, Ogre::Vector3(0, 0, 1));
|
||||
// pierCurve->addPoint(pt);
|
||||
}
|
||||
#endif
|
||||
OgreAssert(pierPath.size() > 0, "empty path");
|
||||
// pierCurve->setNumSeg(8);
|
||||
pierCurvePath = pierCurve->realizePath();
|
||||
// V
|
||||
Procedural::Track shapeTextureTrack =
|
||||
Procedural::Track(Procedural::Track::AM_POINT)
|
||||
.addKeyFrame(0, 0.0f)
|
||||
.addKeyFrame(6, 0.1f);
|
||||
// U
|
||||
Procedural::Track pathTextureTrack =
|
||||
Procedural::Track(Procedural::Track::AM_POINT)
|
||||
.addKeyFrame(0, 0.45f)
|
||||
.addKeyFrame(6, 0.9f);
|
||||
Procedural::Extruder extruder;
|
||||
extruder.setShapeTextureTrack(&shapeTextureTrack)
|
||||
.setPathTextureTrack(&pathTextureTrack)
|
||||
.setShapeToExtrude(pierShape)
|
||||
.setExtrusionPath(&pierCurvePath)
|
||||
.setEnableNormals(true)
|
||||
.setUTile(1.0f)
|
||||
.setVTile(1.0f)
|
||||
.addToTriangleBuffer(tb);
|
||||
for (auto &v : tb.getVertices()) {
|
||||
v.mUV.x = Ogre::Math::Clamp(v.mUV.x, 0.41f, 0.9f);
|
||||
v.mUV.y = Ogre::Math::Clamp(v.mUV.y, 0.0f, 0.1f);
|
||||
}
|
||||
#if 0
|
||||
for (const auto &v : tb.getVertices()) {
|
||||
std::cout << "UV: " << v.mUV << std::endl;
|
||||
}
|
||||
OgreAssert(false, "uvs");
|
||||
#endif
|
||||
#if 0
|
||||
Ogre::LodConfig config(extrudedMesh);
|
||||
setupLods(config);
|
||||
Ogre::Entity *pathEnt =
|
||||
ECS::get<EngineData>().mScnMgr->createEntity(
|
||||
extrudedMesh);
|
||||
pathEnt->setMaterial(harbourMaterial);
|
||||
float xofft = 0.0f;
|
||||
Ogre::Vector3 worldPosition =
|
||||
sceneNode->_getDerivedPosition() +
|
||||
sceneNode->_getDerivedOrientation() *
|
||||
Ogre::Vector3::UNIT_Z * 0.0f;
|
||||
Ogre::Quaternion worldOrientation =
|
||||
sceneNode->_getDerivedOrientation();
|
||||
Ogre::Vector3 xoffset =
|
||||
worldOrientation * (Ogre::Vector3::UNIT_X * xofft);
|
||||
geo->addEntity(pathEnt, worldPosition + xoffset,
|
||||
worldOrientation, Ogre::Vector3(1, 1, 1));
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
for (i = 0; i < pierPath.size(); i++) {
|
||||
if (i == 0)
|
||||
continue;
|
||||
Ogre::Vector3 lvec = pierPath[i] - pierPath[i - 1];
|
||||
Ogre::Vector3 xvec = Ogre::Vector3::UNIT_Y.crossProduct(lvec);
|
||||
Ogre::Vector3 n = lvec.crossProduct(xvec).normalisedCopy();
|
||||
Ogre::Vector3 pos = (pierPath[i] + pierPath[i - 1]) * 0.5f;
|
||||
Procedural::PlaneGenerator()
|
||||
.setNormal(n)
|
||||
.setNumSegX(2)
|
||||
.setNumSegY(2)
|
||||
.setSizeX(1)
|
||||
.setSizeY(1)
|
||||
.setPosition(pos)
|
||||
.addToTriangleBuffer(tb);
|
||||
}
|
||||
#endif
|
||||
Ogre::String meshName =
|
||||
"pierPathMesh" + Ogre::StringConverter::toString(e.id());
|
||||
Ogre::MeshPtr mesh =
|
||||
Ogre::MeshManager::getSingleton().getByName(meshName);
|
||||
if (mesh)
|
||||
Ogre::MeshManager::getSingleton().remove(mesh);
|
||||
mesh = tb.transformToMesh(meshName);
|
||||
Ogre::LodConfig config(mesh);
|
||||
setupLods(config);
|
||||
Ogre::Entity *pathEnt =
|
||||
ECS::get<EngineData>().mScnMgr->createEntity(mesh);
|
||||
pathEnt->setMaterial(harbourMaterial);
|
||||
float xofft = 0.0f;
|
||||
Ogre::Vector3 worldPosition = sceneNode->_getDerivedPosition() +
|
||||
sceneNode->_getDerivedOrientation() *
|
||||
Ogre::Vector3::UNIT_Z * 0.0f;
|
||||
Ogre::Quaternion worldOrientation = sceneNode->_getDerivedOrientation();
|
||||
Ogre::Vector3 xoffset =
|
||||
worldOrientation * (Ogre::Vector3::UNIT_X * xofft);
|
||||
geo->addEntity(pathEnt, worldPosition + xoffset, worldOrientation,
|
||||
Ogre::Vector3(1, 1, 1));
|
||||
ECS::get<EngineData>().mScnMgr->destroyEntity(pathEnt);
|
||||
}
|
||||
void createPier(flecs::entity e, Ogre::SceneNode *sceneNode,
|
||||
Ogre::StaticGeometry *geo)
|
||||
{
|
||||
Ogre::MaterialPtr harbourMaterial;
|
||||
harbourMaterial = Ogre::MaterialManager::getSingleton().getByName(
|
||||
"proceduralMaterialHarbour" +
|
||||
Ogre::StringConverter::toString(e.id()));
|
||||
harbourMaker hm(e);
|
||||
Ogre::Vector3 position = sceneNode->_getDerivedPosition();
|
||||
Ogre::String props = e.get<TerrainItem>().properties;
|
||||
nlohmann::json jp = nlohmann::json::parse(props);
|
||||
float pierHeight = 0.0f, pierLength = 6.0f, pierDepth = 6.0f;
|
||||
if (jp.find("pierLength") != jp.end())
|
||||
pierLength = jp["pierLength"].get<float>();
|
||||
if (jp.find("pierDepth") != jp.end())
|
||||
pierDepth = jp["pierDepth"].get<float>();
|
||||
if (jp.find("pierHeight") != jp.end())
|
||||
pierHeight = jp["pierHeight"].get<float>();
|
||||
Procedural::TriangleBuffer tb;
|
||||
float plankLength = 2.0f;
|
||||
float plankWidth = 4.01f;
|
||||
float plankHeight = 0.3f;
|
||||
const float beamLength = 6.0f;
|
||||
const float beamWidth = 5.5f;
|
||||
float beamHeight = 0.3f;
|
||||
if (pierLength < 12.0f)
|
||||
pierLength = 12.0f;
|
||||
auto processGrid = [&sceneNode, &geo](float stepLength, float length,
|
||||
float zoffset, float yofft,
|
||||
float xofft, Ogre::Entity *ent) {
|
||||
float step = 0.0f;
|
||||
while (step < length) {
|
||||
Ogre::Vector3 worldPosition =
|
||||
sceneNode->_getDerivedPosition() +
|
||||
sceneNode->_getDerivedOrientation() *
|
||||
Ogre::Vector3::UNIT_Z *
|
||||
(step + zoffset);
|
||||
Ogre::Quaternion worldOrientation =
|
||||
sceneNode->_getDerivedOrientation();
|
||||
Ogre::Vector3 xoffset = worldOrientation *
|
||||
(Ogre::Vector3::UNIT_X * xofft);
|
||||
Ogre::Vector3 yoffset = worldOrientation *
|
||||
(Ogre::Vector3::UNIT_Y * yofft);
|
||||
geo->addEntity(ent, worldPosition + xoffset + yoffset,
|
||||
worldOrientation,
|
||||
Ogre::Vector3(1, 1, 1));
|
||||
step += stepLength;
|
||||
}
|
||||
};
|
||||
float xofftPlanks;
|
||||
for (xofftPlanks = -plankWidth; xofftPlanks <= plankWidth;
|
||||
xofftPlanks += plankWidth)
|
||||
processGrid(plankLength, pierLength, plankLength / 2.0f,
|
||||
plankHeight / 2.0f + beamHeight / 2.0f, xofftPlanks,
|
||||
hm.planks);
|
||||
{
|
||||
Procedural::TriangleBuffer tbPillars, tbBollards;
|
||||
float step = 0.0f;
|
||||
while (step < pierLength) {
|
||||
// pillars
|
||||
Procedural::BoxGenerator()
|
||||
.setSizeX(0.5f)
|
||||
.setSizeY(pierDepth)
|
||||
.setSizeZ(0.5f)
|
||||
.setEnableNormals(true)
|
||||
.setPosition(Ogre::Vector3(
|
||||
-5.0f, -pierDepth / 2.0f + 0.5f, step))
|
||||
.addToTriangleBuffer(tbPillars);
|
||||
Procedural::BoxGenerator()
|
||||
.setSizeX(0.5f)
|
||||
.setSizeY(pierDepth)
|
||||
.setSizeZ(0.5f)
|
||||
.setEnableNormals(true)
|
||||
.setPosition(Ogre::Vector3(
|
||||
5.0f, -pierDepth / 2.0f + 0.5f, step))
|
||||
.addToTriangleBuffer(tbPillars);
|
||||
step += 6.0f;
|
||||
}
|
||||
step = pierLength - 0.5f;
|
||||
while (step >= 0.0f) {
|
||||
// bollards
|
||||
Procedural::CylinderGenerator()
|
||||
.setHeight(0.8f)
|
||||
.setRadius(0.3f)
|
||||
.setPosition(Ogre::Vector3(
|
||||
-5.3f, 0.4f + 0.5f + 0.1f, step))
|
||||
.addToTriangleBuffer(tbBollards);
|
||||
Procedural::CylinderGenerator()
|
||||
.setHeight(0.8f)
|
||||
.setRadius(0.3f)
|
||||
.setPosition(Ogre::Vector3(
|
||||
5.3f, 0.4f + 0.5f + 0.1f, step))
|
||||
.addToTriangleBuffer(tbBollards);
|
||||
step -= 12.0f;
|
||||
}
|
||||
// beams
|
||||
Procedural::BoxGenerator()
|
||||
.setSizeX(0.5f)
|
||||
.setSizeY(beamHeight)
|
||||
.setSizeZ(pierLength)
|
||||
.setEnableNormals(true)
|
||||
.setPosition(Ogre::Vector3(-5.0f,
|
||||
0.2 + beamHeight / 2.0f,
|
||||
pierLength / 2.0f))
|
||||
.addToTriangleBuffer(tbPillars);
|
||||
Procedural::BoxGenerator()
|
||||
.setSizeX(0.5f)
|
||||
.setSizeY(beamHeight)
|
||||
.setSizeZ(pierLength)
|
||||
.setEnableNormals(true)
|
||||
.setPosition(Ogre::Vector3(5.0f,
|
||||
0.2 + beamHeight / 2.0f,
|
||||
pierLength / 2.0f))
|
||||
.addToTriangleBuffer(tbPillars);
|
||||
for (auto &v : tbPillars.getVertices()) {
|
||||
v.mUV *= 0.08f;
|
||||
v.mUV += Ogre::Vector2(0.01f, 0.01f);
|
||||
v.mUV.x = Ogre::Math::Clamp(v.mUV.x, 0.0f, 0.1f);
|
||||
v.mUV.y = Ogre::Math::Clamp(v.mUV.y, 0.0f, 0.1f);
|
||||
}
|
||||
for (auto &v : tbBollards.getVertices()) {
|
||||
v.mUV *= 0.08f;
|
||||
v.mUV.x += 0.21f;
|
||||
v.mUV.y += 0.01f;
|
||||
v.mUV.x = Ogre::Math::Clamp(v.mUV.x, 0.2f, 0.3f);
|
||||
v.mUV.y = Ogre::Math::Clamp(v.mUV.y, 0.0f, 0.1f);
|
||||
}
|
||||
tb.append(tbPillars);
|
||||
tb.append(tbBollards);
|
||||
}
|
||||
Ogre::String meshName =
|
||||
"pier" + Ogre::StringConverter::toString(e.id());
|
||||
{
|
||||
Ogre::MeshPtr mesh =
|
||||
Ogre::MeshManager::getSingleton().getByName(meshName);
|
||||
if (mesh)
|
||||
Ogre::MeshManager::getSingleton().remove(mesh);
|
||||
|
||||
mesh = tb.transformToMesh(meshName);
|
||||
Ogre::LodConfig config(mesh);
|
||||
// config.advanced.useCompression = false;
|
||||
// config.advanced.useVertexNormals = true;
|
||||
setupLods(config);
|
||||
Ogre::Entity *ent =
|
||||
ECS::get<EngineData>().mScnMgr->createEntity(mesh);
|
||||
ent->setMaterial(harbourMaterial);
|
||||
float xofft = 0.0f;
|
||||
Ogre::Vector3 worldPosition =
|
||||
sceneNode->_getDerivedPosition() +
|
||||
sceneNode->_getDerivedOrientation() *
|
||||
Ogre::Vector3::UNIT_Z * 0.0f;
|
||||
Ogre::Quaternion worldOrientation =
|
||||
sceneNode->_getDerivedOrientation();
|
||||
Ogre::Vector3 xoffset =
|
||||
worldOrientation * (Ogre::Vector3::UNIT_X * xofft);
|
||||
geo->addEntity(ent, worldPosition + xoffset, worldOrientation,
|
||||
Ogre::Vector3(1, 1, 1));
|
||||
}
|
||||
std::cout << meshName << std::endl;
|
||||
}
|
||||
void createPlazza(flecs::entity e, const nlohmann::json &district,
|
||||
Ogre::SceneNode *sceneNode, Ogre::StaticGeometry *geo)
|
||||
{
|
||||
Ogre::MaterialPtr harbourMaterial;
|
||||
harbourMaterial = Ogre::MaterialManager::getSingleton().getByName(
|
||||
"proceduralMaterialHarbour" +
|
||||
Ogre::StringConverter::toString(e.id()));
|
||||
Ogre::Vector3 worldPosition = sceneNode->_getDerivedPosition();
|
||||
Ogre::Quaternion worldOrientation = sceneNode->_getDerivedOrientation();
|
||||
const nlohmann::json &jp = district;
|
||||
Procedural::TriangleBuffer tb;
|
||||
float centerOffset = 0.0f;
|
||||
float plazzaRadius = 5.0f;
|
||||
float plazzaHeight = 0.2f;
|
||||
float plazzaElevation = 0.0f;
|
||||
if (jp.find("centerOffset") != jp.end())
|
||||
centerOffset = jp["centerOffset"].get<float>();
|
||||
if (jp.find("plazzaRadius") != jp.end())
|
||||
plazzaRadius = jp["plazzaRadius"].get<float>();
|
||||
if (jp.find("plazzaHeight") != jp.end())
|
||||
plazzaHeight = jp["plazzaHeight"].get<float>();
|
||||
if (jp.find("plazzaElevation") != jp.end())
|
||||
plazzaElevation = jp["plazzaElevation"].get<float>();
|
||||
if (plazzaHeight < 0.1f)
|
||||
plazzaHeight = 0.1f;
|
||||
if (plazzaRadius < 5.0f)
|
||||
plazzaRadius = 5.0f;
|
||||
Ogre::Vector3 worldPlazzaCenter = worldPosition;
|
||||
#if 0
|
||||
Procedural::CylinderGenerator()
|
||||
.setHeight(plazzaHeight)
|
||||
.setRadius(plazzaRadius)
|
||||
.setEnableNormals(true)
|
||||
.setPosition(Ogre::Vector3(
|
||||
0.0f, plazzaHeight / 2.0f + plazzaElevation, 0.0f))
|
||||
.addToTriangleBuffer(tb);
|
||||
#endif
|
||||
float mh = 4.0f;
|
||||
Procedural::Shape *plazzaShape = new Procedural::Shape();
|
||||
plazzaShape->addPoint(0, -plazzaHeight - mh - mh);
|
||||
plazzaShape->addPoint(plazzaRadius * 0.5f + mh,
|
||||
-plazzaHeight - mh - mh);
|
||||
plazzaShape->addPoint(plazzaRadius + mh, -plazzaHeight - mh);
|
||||
plazzaShape->addPoint(plazzaRadius, -plazzaHeight);
|
||||
plazzaShape->addPoint(plazzaRadius, 0.0f);
|
||||
plazzaShape->addPoint(plazzaRadius - 0.1f, 0.1f);
|
||||
plazzaShape->addPoint(plazzaRadius - mh + 0.1f, plazzaHeight);
|
||||
plazzaShape->addPoint(plazzaRadius - mh, plazzaHeight + 0.1f);
|
||||
plazzaShape->addPoint(plazzaRadius * 0.5f + mh, plazzaHeight);
|
||||
plazzaShape->addPoint(0, plazzaHeight);
|
||||
Procedural::Lathe()
|
||||
.setShapeToExtrude(plazzaShape)
|
||||
.setEnableNormals(true)
|
||||
.setPosition(Ogre::Vector3(
|
||||
0.0f, plazzaHeight / 2.0f + plazzaElevation, 0.0f))
|
||||
.setNumSeg(24)
|
||||
.addToTriangleBuffer(tb);
|
||||
for (auto &v : tb.getVertices()) {
|
||||
v.mUV *= 0.08f;
|
||||
v.mUV.x += 0.41f;
|
||||
v.mUV.x = Ogre::Math::Clamp(v.mUV.x, 0.4f, 0.5f);
|
||||
v.mUV.y = Ogre::Math::Clamp(v.mUV.y, 0.0f, 0.1f);
|
||||
}
|
||||
Ogre::String meshName =
|
||||
"plazzaMesh" + Ogre::StringConverter::toString(e.id());
|
||||
Ogre::MeshPtr mesh =
|
||||
Ogre::MeshManager::getSingleton().getByName(meshName);
|
||||
if (mesh)
|
||||
Ogre::MeshManager::getSingleton().remove(mesh);
|
||||
mesh = tb.transformToMesh(meshName);
|
||||
Ogre::LodConfig config(mesh);
|
||||
setupLods(config);
|
||||
Ogre::Entity *pathEnt =
|
||||
ECS::get<EngineData>().mScnMgr->createEntity(mesh);
|
||||
pathEnt->setMaterial(harbourMaterial);
|
||||
geo->addEntity(pathEnt, worldPlazzaCenter, worldOrientation,
|
||||
Ogre::Vector3(1, 1, 1));
|
||||
}
|
||||
|
||||
void createBuildings(flecs::entity e, const nlohmann::json &district,
|
||||
Ogre::SceneNode *sceneNode, Ogre::StaticGeometry *geo)
|
||||
{
|
||||
Ogre::MaterialPtr harbourMaterial;
|
||||
harbourMaterial = Ogre::MaterialManager::getSingleton().getByName(
|
||||
"proceduralMaterialHarbour" +
|
||||
Ogre::StringConverter::toString(e.id()));
|
||||
nlohmann::json jbuildings = nlohmann::json::array();
|
||||
if (district.find("centerBuildings") != district.end())
|
||||
jbuildings = district["centerBuildings"];
|
||||
Ogre::Vector3 centerPosition = sceneNode->_getDerivedPosition();
|
||||
Ogre::Quaternion centerOrientation =
|
||||
sceneNode->_getDerivedOrientation();
|
||||
int count = 0;
|
||||
float baseHeight = 4.0f;
|
||||
for (const auto &jb : jbuildings) {
|
||||
Procedural::TriangleBuffer tb;
|
||||
float angle = 0.0f;
|
||||
float depth = 50.0f;
|
||||
float width = 100.0f;
|
||||
float distance = 100.0f;
|
||||
float elevation = 0.0f;
|
||||
std::cout << jb.dump() << std::endl;
|
||||
if (jb.find("angle") != jb.end())
|
||||
angle = jb["angle"].get<float>();
|
||||
if (jb.find("depth") != jb.end())
|
||||
depth = jb["depth"].get<float>();
|
||||
if (jb.find("width") != jb.end())
|
||||
width = jb["width"].get<float>();
|
||||
if (jb.find("distance") != jb.end())
|
||||
distance = jb["distance"].get<float>();
|
||||
if (jb.find("elevation") != jb.end())
|
||||
elevation = jb["elevation"].get<float>();
|
||||
|
||||
OgreAssert(width > 1 && depth > 1 && baseHeight > 1,
|
||||
"Bad stuff happen");
|
||||
|
||||
Ogre::Quaternion rotation = Ogre::Quaternion(
|
||||
Ogre::Degree(angle), Ogre::Vector3::UNIT_Y);
|
||||
Ogre::Vector3 offset = centerOrientation * rotation *
|
||||
(Ogre::Vector3::UNIT_Z * distance);
|
||||
Procedural::BoxGenerator()
|
||||
.setSizeX(width)
|
||||
.setSizeY(baseHeight)
|
||||
.setSizeZ(depth)
|
||||
.setEnableNormals(true)
|
||||
.setPosition(Ogre::Vector3(
|
||||
0.0f, -baseHeight / 2.0f + elevation, 0.0f))
|
||||
.addToTriangleBuffer(tb);
|
||||
OgreAssert(tb.getVertices().size() > 8, "bad box");
|
||||
for (auto &v : tb.getVertices()) {
|
||||
float c = 1.0 + 1.0 / (v.mPosition.y + baseHeight +
|
||||
elevation);
|
||||
v.mPosition.x *= c;
|
||||
v.mPosition.z *= c;
|
||||
v.mUV *= 0.08f;
|
||||
v.mUV.x += 0.41f;
|
||||
v.mUV.x = Ogre::Math::Clamp(v.mUV.x, 0.4f, 0.5f);
|
||||
v.mUV.y = Ogre::Math::Clamp(v.mUV.y, 0.0f, 0.1f);
|
||||
}
|
||||
Ogre::String meshName =
|
||||
"lotbase" + Ogre::StringConverter::toString(count) +
|
||||
"_" + Ogre::StringConverter::toString(e.id());
|
||||
Ogre::MeshPtr mesh =
|
||||
Ogre::MeshManager::getSingleton().getByName(meshName);
|
||||
if (mesh)
|
||||
Ogre::MeshManager::getSingleton().remove(mesh);
|
||||
mesh = tb.transformToMesh(meshName);
|
||||
Ogre::LodConfig config(mesh);
|
||||
setupLods(config);
|
||||
Ogre::Entity *ent =
|
||||
ECS::get<EngineData>().mScnMgr->createEntity(
|
||||
"Ent" + meshName, mesh);
|
||||
ent->setMaterial(harbourMaterial);
|
||||
geo->addEntity(ent, centerPosition + offset, rotation,
|
||||
Ogre::Vector3::UNIT_SCALE);
|
||||
ECS::get<EngineData>().mScnMgr->destroyEntity(ent);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
void createHarbour(flecs::entity e, Ogre::SceneNode *sceneNode,
|
||||
Ogre::StaticGeometry *geo)
|
||||
{
|
||||
std::cout << "createHarbour " << e.id() << std::endl;
|
||||
Ogre::MaterialPtr harbourMaterial;
|
||||
harbourMaterial = Ogre::MaterialManager::getSingleton().getByName(
|
||||
"proceduralMaterialHarbour" +
|
||||
Ogre::StringConverter::toString(e.id()));
|
||||
if (!harbourMaterial) {
|
||||
Procedural::TextureBuffer colorAtlas(1024);
|
||||
Procedural::RectangleTexture drawAtlas(&colorAtlas);
|
||||
Ogre::ColourValue normalYellow(0.8f, 0.6f, 0, 1);
|
||||
Ogre::ColourValue roadBrown(0.4f, 0.3f, 0.3f, 1);
|
||||
Ogre::ColourValue bollardGrey(0.2f, 0.2f, 0.2f, 1);
|
||||
drawAtlas.setRectangle(Ogre::RealRect(0.0f, 0.0f, 0.4f, 1.0f))
|
||||
.setColour(normalYellow)
|
||||
.process();
|
||||
drawAtlas.setRectangle(Ogre::RealRect(0.2f, 0.0f, 0.3f, 1.0f))
|
||||
.setColour(bollardGrey)
|
||||
.process();
|
||||
drawAtlas.setRectangle(Ogre::RealRect(0.4f, 0.0f, 1.0f, 1.0f))
|
||||
.setColour(roadBrown)
|
||||
.process();
|
||||
Ogre::TexturePtr pierTexture = colorAtlas.createTexture(
|
||||
"proceduralTextureHarbour" +
|
||||
Ogre::StringConverter::toString(e.id()));
|
||||
colorAtlas.saveImage("tmp.png");
|
||||
harbourMaterial =
|
||||
Ogre::MaterialManager::getSingletonPtr()->create(
|
||||
"proceduralMaterialHarbour" +
|
||||
Ogre::StringConverter::toString(e.id()),
|
||||
Ogre::ResourceGroupManager::
|
||||
DEFAULT_RESOURCE_GROUP_NAME);
|
||||
harbourMaterial->getTechnique(0)->getPass(0)->setShininess(0);
|
||||
harbourMaterial->getTechnique(0)->getPass(0)->setDiffuse(
|
||||
Ogre::ColourValue::White);
|
||||
harbourMaterial->getTechnique(0)->getPass(0)->setSpecular(
|
||||
Ogre::ColourValue(1.0f, 1.0f, 0.9f));
|
||||
harbourMaterial->getTechnique(0)
|
||||
->getPass(0)
|
||||
->createTextureUnitState(
|
||||
"proceduralTextureHarbour" +
|
||||
Ogre::StringConverter::toString(e.id()));
|
||||
if (Ogre::RTShader::ShaderGenerator::initialize()) {
|
||||
harbourMaterial->prepare();
|
||||
Ogre::RTShader::ShaderGenerator *mShaderGenerator =
|
||||
Ogre::RTShader::ShaderGenerator::
|
||||
getSingletonPtr();
|
||||
mShaderGenerator->createShaderBasedTechnique(
|
||||
*harbourMaterial,
|
||||
Ogre::MaterialManager::DEFAULT_SCHEME_NAME,
|
||||
Ogre::RTShader::ShaderGenerator::
|
||||
DEFAULT_SCHEME_NAME);
|
||||
Ogre::RTShader::RenderState *pMainRenderState =
|
||||
mShaderGenerator->getRenderState(
|
||||
Ogre::RTShader::ShaderGenerator::
|
||||
DEFAULT_SCHEME_NAME,
|
||||
*harbourMaterial);
|
||||
}
|
||||
}
|
||||
{
|
||||
Ogre::String props = e.get<TerrainItem>().properties;
|
||||
nlohmann::json jp = nlohmann::json::parse(props);
|
||||
float pierOffset = 0.0f;
|
||||
float centerOffset = 0.0f;
|
||||
float plazzaRadius = 5.0f;
|
||||
bool plazza = false;
|
||||
nlohmann::json jcenter = nlohmann::json::object();
|
||||
if (jp.find("center") != jp.end())
|
||||
jcenter = jp["center"];
|
||||
if (jcenter.find("plazzaRadius") != jcenter.end())
|
||||
plazzaRadius = jcenter["plazzaRadius"];
|
||||
if (jp.find("pierOffset") != jp.end())
|
||||
pierOffset = jp["pierOffset"].get<float>();
|
||||
if (jp.find("plazza") != jp.end())
|
||||
plazza = jp["plazza"].get<bool>();
|
||||
if (jp.find("centerOffset") != jp.end())
|
||||
centerOffset = jp["centerOffset"].get<float>();
|
||||
Ogre::Vector3 worldPosition =
|
||||
sceneNode->_getDerivedPosition() +
|
||||
sceneNode->_getDerivedOrientation() *
|
||||
Ogre::Vector3::UNIT_Z * (pierOffset);
|
||||
float xofft = 0.0f;
|
||||
float stairLengthZ = worldPosition.y;
|
||||
float stairsOffset = 0.0f;
|
||||
stairsOffset = stairLengthZ;
|
||||
jp["stairsHeight"] = worldPosition.y;
|
||||
jp["stairsOffset"] = stairsOffset;
|
||||
e.get_mut<TerrainItem>().properties = jp.dump();
|
||||
e.modified<TerrainItem>();
|
||||
worldPosition.y = 0.0f;
|
||||
Ogre::Quaternion worldOrientation =
|
||||
sceneNode->_getDerivedOrientation();
|
||||
Ogre::Vector3 xoffset =
|
||||
worldOrientation * (Ogre::Vector3::UNIT_X * xofft);
|
||||
Ogre::Vector3 zoffset =
|
||||
worldOrientation *
|
||||
(Ogre::Vector3::UNIT_Z * (stairsOffset - 1));
|
||||
Ogre::SceneNode *elevatorNode =
|
||||
sceneNode->createChildSceneNode();
|
||||
Ogre::SceneNode *pierNode = sceneNode->createChildSceneNode();
|
||||
pierNode->_setDerivedPosition(worldPosition + zoffset +
|
||||
xoffset);
|
||||
Ogre::Vector3 worldPlazzaCenter =
|
||||
sceneNode->_getDerivedPosition() +
|
||||
sceneNode->_getDerivedOrientation() *
|
||||
Ogre::Vector3::UNIT_Z *
|
||||
(-centerOffset - plazzaRadius + 2.0f);
|
||||
Ogre::SceneNode *centerNode = sceneNode->createChildSceneNode();
|
||||
centerNode->_setDerivedPosition(worldPlazzaCenter);
|
||||
createBridge(e, elevatorNode, geo);
|
||||
createPier(e, pierNode, geo);
|
||||
createPlazza(e, jcenter, centerNode, geo);
|
||||
createBuildings(e, jcenter, centerNode, geo);
|
||||
}
|
||||
geo->build();
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/gamedata/items/harbour.h
Normal file
25
src/gamedata/items/harbour.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef __HARBOUR_H__
|
||||
#define __HARBOUR_H__
|
||||
#include <flecs.h>
|
||||
namespace ECS
|
||||
{
|
||||
namespace Items
|
||||
{
|
||||
void createHarbourPopup(const std::pair<flecs::entity, Ogre::String> item);
|
||||
void createHarbourMenu();
|
||||
}
|
||||
namespace Geometry
|
||||
{
|
||||
void createHarbour(flecs::entity e, Ogre::SceneNode *sceneNode,
|
||||
Ogre::StaticGeometry *geo);
|
||||
void createBridge(flecs::entity e, Ogre::SceneNode *sceneNode,
|
||||
Ogre::StaticGeometry *geo);
|
||||
void createPier(flecs::entity e, Ogre::SceneNode *sceneNode,
|
||||
Ogre::StaticGeometry *geo);
|
||||
void createPlazza(flecs::entity e, const nlohmann::json &district,
|
||||
Ogre::SceneNode *sceneNode, Ogre::StaticGeometry *geo);
|
||||
void createBuildings(flecs::entity e, const nlohmann::json &district,
|
||||
Ogre::SceneNode *sceneNode, Ogre::StaticGeometry *geo);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
374
src/gamedata/items/items.cpp
Normal file
374
src/gamedata/items/items.cpp
Normal file
@@ -0,0 +1,374 @@
|
||||
#include <iostream>
|
||||
#include <Ogre.h>
|
||||
#include <OgreImGuiOverlay.h>
|
||||
#include <OgreTerrainGroup.h>
|
||||
#include <OgreRTShaderSystem.h>
|
||||
#include <OgreMeshLodGenerator.h>
|
||||
#include <Procedural.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "Components.h"
|
||||
#include "GameData.h"
|
||||
#include "StaticGeometryModule.h"
|
||||
#include "EditorGizmoModule.h"
|
||||
#include "TerrainModule.h"
|
||||
#include "physics.h"
|
||||
#include "PhysicsModule.h"
|
||||
#include "LuaData.h"
|
||||
#include "harbour.h"
|
||||
#include "temple.h"
|
||||
#include "town.h"
|
||||
#include "items.h"
|
||||
namespace ECS
|
||||
{
|
||||
namespace Items
|
||||
{
|
||||
void runScriptsForAllTowns()
|
||||
{
|
||||
std::pair<flecs::entity, Ogre::String> selected_item;
|
||||
std::list<std::pair<flecs::entity, Ogre::String> > items;
|
||||
StaticGeometryModule::getItemsProperties(&items);
|
||||
for (const auto &item : items) {
|
||||
nlohmann::json j = nlohmann::json::parse(item.second);
|
||||
Ogre::String itemType = j["type"].get<Ogre::String>();
|
||||
if (itemType == "town") {
|
||||
Items::runAllScriptsForTown(item.first);
|
||||
if (item.first.has<TerrainItemNode>())
|
||||
Geometry::updateItemGeometry(item.first);
|
||||
}
|
||||
}
|
||||
StaticGeometryModule::saveItems();
|
||||
}
|
||||
void showItemPopup(const std::pair<flecs::entity, Ogre::String> &item)
|
||||
{
|
||||
Ogre::String popupLabel =
|
||||
"EditPopup" + Ogre::StringConverter::toString(item.first.id());
|
||||
if (ImGui::BeginPopup(popupLabel.c_str())) {
|
||||
Ogre::String prop =
|
||||
StaticGeometryModule::getItemProperties(item.first);
|
||||
nlohmann::json j = nlohmann::json::parse(prop);
|
||||
Ogre::String itemType = j["type"].get<Ogre::String>();
|
||||
bool changed = false;
|
||||
bool set = false;
|
||||
bool offset = false;
|
||||
Ogre::Vector3 offsetVal(0, 0, 0);
|
||||
int i;
|
||||
struct OffsetButton {
|
||||
Ogre::String label;
|
||||
Ogre::Vector3 offset;
|
||||
ImGuiDir direction;
|
||||
};
|
||||
struct OffsetButton buttons_large[] = {
|
||||
{ "ElevateUp", { 0, 1, 0 }, ImGuiDir::ImGuiDir_Up },
|
||||
{ "ElevateDown", { 0, -1, 0 }, ImGuiDir::ImGuiDir_Down },
|
||||
{ "X+", { 1, 0, 0 }, ImGuiDir::ImGuiDir_None },
|
||||
{ "X-", { -1, 0, 0 }, ImGuiDir::ImGuiDir_None },
|
||||
{ "Z+", { 0, 0, 1 }, ImGuiDir::ImGuiDir_None },
|
||||
{ "Z-", { 0, 0, -1 }, ImGuiDir::ImGuiDir_None },
|
||||
};
|
||||
struct OffsetButton buttons_small[] = {
|
||||
{ "SmallElevateUp",
|
||||
{ 0, 0.1f, 0 },
|
||||
ImGuiDir::ImGuiDir_Up },
|
||||
{ "SmallElevateDown",
|
||||
{ 0, -0.1f, 0 },
|
||||
ImGuiDir::ImGuiDir_Down },
|
||||
{ "0.1 X+", { 0.1f, 0, 0 }, ImGuiDir::ImGuiDir_None },
|
||||
{ "0.1 X-", { -0.1f, 0, 0 }, ImGuiDir::ImGuiDir_None },
|
||||
{ "0.1 Z+", { 0, 0, 0.1f }, ImGuiDir::ImGuiDir_None },
|
||||
{ "0.1 Z-", { 0, 0, -0.1f }, ImGuiDir::ImGuiDir_None },
|
||||
};
|
||||
|
||||
if (ImGui::SmallButton("Set position from cursor")) {
|
||||
changed = true;
|
||||
set = true;
|
||||
}
|
||||
auto buttonGroup = [&](struct OffsetButton *button, int count) {
|
||||
int i;
|
||||
for (i = 0; i < count; i++) {
|
||||
if (i > 0)
|
||||
ImGui::SameLine();
|
||||
if (button[i].direction !=
|
||||
ImGuiDir::ImGuiDir_None) {
|
||||
if (ImGui::ArrowButton(
|
||||
button[i].label.c_str(),
|
||||
button[i].direction)) {
|
||||
offsetVal = button[i].offset;
|
||||
offset = true;
|
||||
changed = true;
|
||||
}
|
||||
} else {
|
||||
if (ImGui::SmallButton(
|
||||
button[i].label.c_str())) {
|
||||
offsetVal = button[i].offset;
|
||||
offset = true;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
static float windowWidth = 1.6f;
|
||||
static float windowHeight = 2.0f;
|
||||
static float windowDepth = 0.12f;
|
||||
static float windowFrameWidth = 0.2f;
|
||||
ImGui::Text("Windows tools...");
|
||||
char windowStyleName[32] = { 0 };
|
||||
ImGui::InputFloat("windowWidth", &windowWidth);
|
||||
ImGui::InputFloat("windowHeight", &windowHeight);
|
||||
ImGui::InputFloat("windowDepth", &windowDepth);
|
||||
ImGui::InputFloat("windowFrameWidth", &windowFrameWidth);
|
||||
if (ImGui::InputText("windowStyleAdd", windowStyleName,
|
||||
sizeof(windowStyleName))) {
|
||||
}
|
||||
if (ImGui::SmallButton("Add")) {
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
buttonGroup(buttons_large,
|
||||
sizeof(buttons_large) / sizeof(buttons_large[0]));
|
||||
buttonGroup(buttons_small,
|
||||
sizeof(buttons_small) / sizeof(buttons_small[0]));
|
||||
if (changed) {
|
||||
Ogre::Vector3 position =
|
||||
item.first.get<TerrainItem>().position;
|
||||
Ogre::Quaternion orientation =
|
||||
item.first.get<TerrainItem>().orientation;
|
||||
if (set) {
|
||||
position = ECS::get<EditorGizmo>()
|
||||
.sceneNode
|
||||
->_getDerivedPosition();
|
||||
orientation =
|
||||
ECS::get<EditorGizmo>()
|
||||
.sceneNode
|
||||
->_getDerivedOrientation();
|
||||
} else if (offset)
|
||||
position += offsetVal;
|
||||
item.first.get_mut<TerrainItem>().position = position;
|
||||
item.first.get_mut<TerrainItem>().orientation =
|
||||
orientation;
|
||||
item.first.modified<TerrainItem>();
|
||||
StaticGeometryModule::saveItems();
|
||||
StaticGeometryModule::updateItemGeometry(item.first);
|
||||
}
|
||||
if (itemType == "harbour")
|
||||
createHarbourPopup(item);
|
||||
else if (itemType == "temple")
|
||||
createTemplePopup(item);
|
||||
else if (itemType == "town")
|
||||
createTownPopup(item);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
void showItemButtons(const std::pair<flecs::entity, Ogre::String> &item)
|
||||
{
|
||||
ImGui::SameLine();
|
||||
Ogre::String upd_label =
|
||||
"Update Height##" +
|
||||
Ogre::StringConverter::toString(item.first.id());
|
||||
if (ImGui::SmallButton(upd_label.c_str())) {
|
||||
TerrainItem &uitem = item.first.get_mut<TerrainItem>();
|
||||
uitem.position.y =
|
||||
ECS::get<Terrain>()
|
||||
.mTerrainGroup->getHeightAtWorldPosition(
|
||||
uitem.position);
|
||||
StaticGeometryModule::saveItems();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
Ogre::String edit_label =
|
||||
"Edit##" + Ogre::StringConverter::toString(item.first.id());
|
||||
Ogre::String popupLabel =
|
||||
"EditPopup" + Ogre::StringConverter::toString(item.first.id());
|
||||
if (ImGui::SmallButton(edit_label.c_str()))
|
||||
ImGui::OpenPopup(popupLabel.c_str());
|
||||
ImGui::SameLine();
|
||||
Ogre::String del_label =
|
||||
"delete##" + Ogre::StringConverter::toString(item.first.id());
|
||||
if (ImGui::SmallButton(del_label.c_str())) {
|
||||
item.first.destruct();
|
||||
StaticGeometryModule::saveItems();
|
||||
}
|
||||
ImGui::Spacing();
|
||||
}
|
||||
void createItemsMenu()
|
||||
{
|
||||
if (ImGui::BeginMenu("Harbour")) {
|
||||
Items::createHarbourMenu();
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("Temple")) {
|
||||
Items::createTempleMenu();
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("Town")) {
|
||||
Items::createTownMenu();
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
namespace Geometry
|
||||
{
|
||||
void setupLods(Ogre::LodConfig &config)
|
||||
{
|
||||
int count = 0;
|
||||
// config.advanced.useCompression = false;
|
||||
config.advanced.useVertexNormals = true;
|
||||
config.advanced.preventPunchingHoles = true;
|
||||
config.advanced.preventBreakingLines = true;
|
||||
config.createGeneratedLodLevel(10, 0.15f);
|
||||
// config.createGeneratedLodLevel(50, 0.25f);
|
||||
// config.createGeneratedLodLevel(100, 0.36f);
|
||||
// config.createGeneratedLodLevel(200, 0.50f);
|
||||
config.createGeneratedLodLevel(500, 0.85f);
|
||||
config.advanced.useBackgroundQueue = false;
|
||||
std::cout << "mesh name: " << config.mesh->getName() << std::endl;
|
||||
bool crash = false;
|
||||
for (count = 0; count < config.mesh->getSubMeshes().size(); count++) {
|
||||
Ogre::SubMesh *submesh = config.mesh->getSubMeshes()[count];
|
||||
std::cout << "unprocessed submesh: " << count << " " << submesh
|
||||
<< std::endl;
|
||||
if (submesh)
|
||||
submesh->parent = config.mesh.get();
|
||||
else
|
||||
crash = true;
|
||||
}
|
||||
Ogre::MeshLodGenerator::getSingleton().generateLodLevels(config);
|
||||
for (count = 0; count < config.mesh->getSubMeshes().size(); count++) {
|
||||
Ogre::SubMesh *submesh = config.mesh->getSubMeshes()[count];
|
||||
std::cout << "submesh: " << count << " " << submesh
|
||||
<< std::endl;
|
||||
if (submesh)
|
||||
submesh->parent = config.mesh.get();
|
||||
else
|
||||
crash = true;
|
||||
}
|
||||
OgreAssert(!crash, "no submesh");
|
||||
}
|
||||
|
||||
Ogre::StaticGeometry *createStaticGeometry(flecs::entity e)
|
||||
{
|
||||
Ogre::String props = e.get<TerrainItem>().properties;
|
||||
nlohmann::json jp = nlohmann::json::parse(props);
|
||||
if (jp.find("type") != jp.end()) {
|
||||
Ogre::String itemType = jp["type"].get<Ogre::String>();
|
||||
Ogre::String geoName = itemType + "_" +
|
||||
Ogre::StringConverter::toString(e.id());
|
||||
OgreAssert((ECS::get<EngineData>().mScnMgr->hasStaticGeometry(
|
||||
geoName)) == false,
|
||||
"" + geoName + " already exists for some reason");
|
||||
Ogre::StaticGeometry *geo =
|
||||
ECS::get<EngineData>().mScnMgr->createStaticGeometry(
|
||||
geoName);
|
||||
return geo;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
void createItemGeometry(flecs::entity e)
|
||||
{
|
||||
OgreAssert(!e.has<TerrainItemNode>(), "Geometry already created");
|
||||
std::cout << "creating geometry for item: " << e.id() << std::endl;
|
||||
Ogre::String props = e.get<TerrainItem>().properties;
|
||||
nlohmann::json jp = nlohmann::json::parse(props);
|
||||
Ogre::SceneNode *itemNode = ECS::get<EngineData>()
|
||||
.mScnMgr->getRootSceneNode()
|
||||
->createChildSceneNode();
|
||||
itemNode->_setDerivedPosition(e.get<TerrainItem>().position);
|
||||
itemNode->_setDerivedOrientation(e.get<TerrainItem>().orientation);
|
||||
if (jp.find("staticMesh") != jp.end()) {
|
||||
Ogre::String meshName = jp["staticMesh"].get<Ogre::String>();
|
||||
Ogre::MeshPtr mesh =
|
||||
Ogre::MeshManager::getSingleton().getByName(meshName);
|
||||
if (mesh) {
|
||||
Ogre::Entity *ent =
|
||||
ECS::get<EngineData>().mScnMgr->createEntity(
|
||||
mesh);
|
||||
itemNode->attachObject(ent);
|
||||
}
|
||||
} else if (jp.find("type") != jp.end()) {
|
||||
Ogre::String itemType = jp["type"].get<Ogre::String>();
|
||||
std::cout << "type: " << itemType << std::endl;
|
||||
std::cout << "props: " << props << std::endl;
|
||||
Ogre::StaticGeometry *geo = createStaticGeometry(e);
|
||||
if (geo) {
|
||||
geo->setRegionDimensions(Ogre::Vector3(140, 140, 140));
|
||||
Ogre::Vector3 geoposition =
|
||||
itemNode->_getDerivedPosition();
|
||||
geoposition.y = 0.0f;
|
||||
geo->setOrigin(geoposition);
|
||||
}
|
||||
if (itemType == "harbour") {
|
||||
OgreAssert(geo, "Can't create static geometry");
|
||||
Geometry::createHarbour(e, itemNode, geo);
|
||||
e.set<TerrainItemNode>({ itemNode, geo });
|
||||
} else if (itemType == "temple") {
|
||||
OgreAssert(geo, "Can't create static geometry");
|
||||
createTemple(e, itemNode, geo);
|
||||
e.set<TerrainItemNode>({ itemNode, geo });
|
||||
} else if (itemType == "town") {
|
||||
OgreAssert(geo, "Can't create static geometry");
|
||||
createTown(e, itemNode, geo);
|
||||
e.set<TerrainItemNode>({ itemNode, geo });
|
||||
std::cout << " town created: " << e.id() << std::endl;
|
||||
} else {
|
||||
OgreAssert(geo, "Can't create static geometry");
|
||||
e.set<TerrainItemNode>({ itemNode, geo });
|
||||
OgreAssert(false, "Unknown item type: " + itemType);
|
||||
}
|
||||
} else {
|
||||
std::cout << "can't build item" << std::endl;
|
||||
std::cout << "props: " << props << std::endl;
|
||||
OgreAssert(false, "can't create item");
|
||||
}
|
||||
}
|
||||
|
||||
void destroyItemGeometry(flecs::entity e)
|
||||
{
|
||||
OgreAssert(e.has<TerrainItemNode>(), "No geometry created");
|
||||
#if 0
|
||||
ECS::get<EngineData>().mScnMgr->destroyStaticGeometry()
|
||||
e.get<TerrainItemNode>().geo->destroy();
|
||||
e.get_mut<TerrainItemNode>().geo = nullptr;
|
||||
e.modified<TerrainItemNode>();
|
||||
#endif
|
||||
e.remove<TerrainItemNode>();
|
||||
}
|
||||
void updateItemGeometry(flecs::entity e)
|
||||
{
|
||||
OgreAssert(e.has<TerrainItem>(), "not terrain item");
|
||||
if (e.has<TerrainItemNode>())
|
||||
destroyItemGeometry(e);
|
||||
createItemGeometry(e);
|
||||
}
|
||||
|
||||
flecs::entity createMeshGeometry(const Ogre::String &meshName,
|
||||
flecs::entity parente,
|
||||
Ogre::SceneNode *sceneNode,
|
||||
Ogre::StaticGeometry *geo)
|
||||
{
|
||||
Ogre::MeshPtr mesh = Ogre::MeshManager::getSingleton().load(
|
||||
meshName,
|
||||
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
|
||||
OgreAssert(mesh, "mesh " + meshName + " not found");
|
||||
Ogre::LodConfig config(mesh);
|
||||
setupLods(config);
|
||||
Ogre::Entity *ent = ECS::get<EngineData>().mScnMgr->createEntity(
|
||||
"Ent" + meshName, mesh);
|
||||
geo->addEntity(ent, sceneNode->_getDerivedPosition(),
|
||||
sceneNode->_getDerivedOrientation());
|
||||
ECS::get<EngineData>().mScnMgr->destroyEntity(ent);
|
||||
JPH::ShapeRefC shape =
|
||||
JoltPhysicsWrapper::getSingleton().createMeshShape(mesh);
|
||||
JPH::BodyID id = JoltPhysicsWrapper::getSingleton().createBody(
|
||||
shape.GetPtr(), 0, sceneNode, JPH::EMotionType::Static,
|
||||
Layers::NON_MOVING);
|
||||
flecs::entity e = ECS::get()
|
||||
.entity()
|
||||
.child_of(parente)
|
||||
.set<JPH::BodyID>(id)
|
||||
.set<TerrainItemMeshNode>({ sceneNode, geo });
|
||||
JoltPhysicsWrapper::getSingleton().addBody(id,
|
||||
JPH::EActivation::Activate);
|
||||
return e;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
107
src/gamedata/items/items.h
Normal file
107
src/gamedata/items/items.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#ifndef __ITEMS_H__
|
||||
#define __ITEMS_H__
|
||||
#include <OgreMeshLodGenerator.h>
|
||||
#include <flecs.h>
|
||||
#include "Components.h"
|
||||
#include "GameData.h"
|
||||
namespace ECS
|
||||
{
|
||||
namespace Items
|
||||
{
|
||||
void showItemPopup(const std::pair<flecs::entity, Ogre::String> &item);
|
||||
void showItemButtons(const std::pair<flecs::entity, Ogre::String> &item);
|
||||
void createItemsMenu();
|
||||
void runScriptsForAllTowns();
|
||||
}
|
||||
namespace Geometry
|
||||
{
|
||||
void setupLods(Ogre::LodConfig &config);
|
||||
struct harbourMaker {
|
||||
Ogre::Entity *planks, *pillar, *beam;
|
||||
Ogre::String makeName(const Ogre::String &base, flecs::entity e)
|
||||
{
|
||||
return base + Ogre::StringConverter::toString(e.id());
|
||||
}
|
||||
harbourMaker(flecs::entity e)
|
||||
{
|
||||
Ogre::String planksName = makeName("Plank", e);
|
||||
Ogre::String beamName = makeName("Beam", e);
|
||||
Ogre::String pillarName = makeName("Pillar", e);
|
||||
std::pair<Ogre::String, Ogre::Entity **> sets[] = {
|
||||
{ planksName, &planks },
|
||||
{ beamName, &beam },
|
||||
{ pillarName, &pillar }
|
||||
};
|
||||
std::map<Ogre::String, Ogre::String> meshes = {
|
||||
{ planksName, "pier-plank.glb" },
|
||||
{ beamName, "pier-beam.glb" },
|
||||
{ pillarName, "pier-pillar.glb" },
|
||||
};
|
||||
for (auto &p : sets) {
|
||||
if (ECS::get<EngineData>().mScnMgr->hasEntity(p.first))
|
||||
*p.second =
|
||||
ECS::get<EngineData>()
|
||||
.mScnMgr->getEntity(p.first);
|
||||
else {
|
||||
*p.second = ECS::get<EngineData>()
|
||||
.mScnMgr->createEntity(
|
||||
p.first,
|
||||
meshes.at(p.first));
|
||||
Ogre::MeshPtr mesh = (*p.second)->getMesh();
|
||||
Ogre::LodConfig config(mesh);
|
||||
setupLods(config);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
void createItemGeometry(flecs::entity e);
|
||||
void destroyItemGeometry(flecs::entity e);
|
||||
void updateItemGeometry(flecs::entity e);
|
||||
flecs::entity createMeshGeometry(const Ogre::String &meshName,
|
||||
flecs::entity parente,
|
||||
Ogre::SceneNode *sceneNode,
|
||||
Ogre::StaticGeometry *geo);
|
||||
|
||||
}
|
||||
static void to_json(nlohmann::json &j, const Ogre::Vector3 &position)
|
||||
{
|
||||
j["x"] = position.x;
|
||||
j["y"] = position.y;
|
||||
j["z"] = position.z;
|
||||
}
|
||||
static void to_json(nlohmann::json &j, const Ogre::Quaternion &orientation)
|
||||
{
|
||||
j["w"] = orientation.w;
|
||||
j["x"] = orientation.x;
|
||||
j["y"] = orientation.y;
|
||||
j["z"] = orientation.z;
|
||||
}
|
||||
static void to_json(nlohmann::json &j, const Ogre::ColourValue &colour)
|
||||
{
|
||||
j["r"] = colour.r;
|
||||
j["g"] = colour.g;
|
||||
j["b"] = colour.b;
|
||||
j["a"] = colour.a;
|
||||
}
|
||||
static void from_json(const nlohmann::json &j, Ogre::Vector3 &position)
|
||||
{
|
||||
position.x = j["x"].get<float>();
|
||||
position.y = j["y"].get<float>();
|
||||
position.z = j["z"].get<float>();
|
||||
}
|
||||
static void from_json(const nlohmann::json &j, Ogre::Quaternion &orientation)
|
||||
{
|
||||
orientation.w = j["w"].get<float>();
|
||||
orientation.x = j["x"].get<float>();
|
||||
orientation.y = j["y"].get<float>();
|
||||
orientation.z = j["z"].get<float>();
|
||||
}
|
||||
static void from_json(const nlohmann::json &j, Ogre::ColourValue &colour)
|
||||
{
|
||||
colour.r = j["r"].get<float>();
|
||||
colour.g = j["g"].get<float>();
|
||||
colour.b = j["b"].get<float>();
|
||||
colour.a = j["a"].get<float>();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
240
src/gamedata/items/temple.cpp
Normal file
240
src/gamedata/items/temple.cpp
Normal file
@@ -0,0 +1,240 @@
|
||||
#include <iostream>
|
||||
#include <Ogre.h>
|
||||
#include <OgreTerrainGroup.h>
|
||||
#include <OgreImGuiOverlay.h>
|
||||
#include <OgreRTShaderSystem.h>
|
||||
#include <Procedural.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "Components.h"
|
||||
#include "GameData.h"
|
||||
#include "EditorGizmoModule.h"
|
||||
#include "TerrainModule.h"
|
||||
#include "StaticGeometryModule.h"
|
||||
#include "physics.h"
|
||||
#include "PhysicsModule.h"
|
||||
#include "items.h"
|
||||
#include "temple.h"
|
||||
namespace ECS
|
||||
{
|
||||
namespace Items
|
||||
{
|
||||
void createTempleItem()
|
||||
{
|
||||
Ogre::Vector3 itemPosition =
|
||||
ECS::get<EditorGizmo>().sceneNode->_getDerivedPosition();
|
||||
Ogre::Quaternion itemOrientation =
|
||||
ECS::get<EditorGizmo>().sceneNode->_getDerivedOrientation();
|
||||
flecs::entity e = StaticGeometryModule::createItem(
|
||||
itemPosition, itemOrientation, "temple");
|
||||
Ogre::String prop = StaticGeometryModule::getItemProperties(e);
|
||||
nlohmann::json j = nlohmann::json::parse(prop);
|
||||
j["size"] = 40.0f;
|
||||
j["form"] = "rect";
|
||||
j["pillarHeight"] = 16.0f;
|
||||
j["pillarRadius"] = 0.5f;
|
||||
j["pillarCount"] = 20;
|
||||
StaticGeometryModule::setItemProperties(e, j.dump());
|
||||
StaticGeometryModule::saveItems();
|
||||
}
|
||||
void createTempleMenu()
|
||||
{
|
||||
if (ImGui::MenuItem("Create"))
|
||||
createTempleItem();
|
||||
}
|
||||
void createTemplePopup(const std::pair<flecs::entity, Ogre::String> item)
|
||||
{
|
||||
float size = 40.0f;
|
||||
float pillarRadius = 0.5f;
|
||||
float pillarHeight = 16.0f;
|
||||
int pillarCount = 20;
|
||||
Ogre::String form;
|
||||
|
||||
Ogre::String prop = StaticGeometryModule::getItemProperties(item.first);
|
||||
nlohmann::json j = nlohmann::json::parse(prop);
|
||||
bool changed = false;
|
||||
if (j.find("size") != j.end())
|
||||
size = j["size"].get<float>();
|
||||
if (j.find("pillarRadius") != j.end())
|
||||
pillarRadius = j["pillarRadius"].get<float>();
|
||||
if (j.find("pillarHeight") != j.end())
|
||||
pillarHeight = j["pillarHeight"].get<float>();
|
||||
if (j.find("pillarCount") != j.end())
|
||||
pillarCount = j["pillarCount"].get<int>();
|
||||
if (ImGui::SliderFloat("Temple Size", &size, 40.0f, 100.0f))
|
||||
changed = true;
|
||||
if (ImGui::SliderFloat("Temple Pillar Radius", &pillarRadius, 0.5f,
|
||||
2.0f))
|
||||
changed = true;
|
||||
if (ImGui::SliderFloat("Temple Pillar Height", &pillarHeight, 16.0f,
|
||||
60.0f))
|
||||
changed = true;
|
||||
if (ImGui::SliderInt("Pillar Count", &pillarCount, 1, 200))
|
||||
changed = true;
|
||||
if (changed) {
|
||||
j["size"] = size;
|
||||
j["pillarRadius"] = pillarRadius;
|
||||
j["pillarHeight"] = pillarHeight;
|
||||
j["pillarCount"] = pillarCount;
|
||||
StaticGeometryModule::setItemProperties(item.first, j.dump());
|
||||
StaticGeometryModule::saveItems();
|
||||
StaticGeometryModule::destroyItemGeometry(item.first);
|
||||
StaticGeometryModule::createItemGeometry(item.first);
|
||||
}
|
||||
ImGui::Text("%s", j.dump(4).c_str());
|
||||
}
|
||||
}
|
||||
namespace Geometry
|
||||
{
|
||||
void createTemple(flecs::entity e, Ogre::SceneNode *sceneNode,
|
||||
Ogre::StaticGeometry *geo)
|
||||
{
|
||||
Ogre::String props = e.get<TerrainItem>().properties;
|
||||
nlohmann::json jp = nlohmann::json::parse(props);
|
||||
float size = 20.0f;
|
||||
int pillarCount = 4;
|
||||
float pillarRadius = 0.5f, pillarHeight = 10.0f;
|
||||
if (jp.find("size") != jp.end())
|
||||
size = jp["size"].get<float>();
|
||||
if (jp.find("pillarCount") != jp.end())
|
||||
pillarCount = jp["pillarCount"].get<int>();
|
||||
if (jp.find("pillarHeight") != jp.end())
|
||||
pillarHeight = jp["pillarHeight"].get<float>();
|
||||
if (jp.find("pillarCount") != jp.end())
|
||||
size = jp["size"].get<float>();
|
||||
Procedural::TriangleBuffer tb, colliderTb;
|
||||
float elevation = 0.0f;
|
||||
Ogre::MaterialPtr templeMaterial;
|
||||
templeMaterial = Ogre::MaterialManager::getSingleton().getByName(
|
||||
"proceduralMaterialTemple" +
|
||||
Ogre::StringConverter::toString(e.id()));
|
||||
if (!templeMaterial) {
|
||||
Procedural::TextureBuffer colorAtlas(1024);
|
||||
Procedural::RectangleTexture drawAtlas(&colorAtlas);
|
||||
Ogre::ColourValue normalGreen(0.6f, 0.8f, 0.5f, 1);
|
||||
drawAtlas.setRectangle(Ogre::RealRect(0.0f, 0.0f, 0.4f, 1.0f))
|
||||
.setColour(normalGreen)
|
||||
.process();
|
||||
Ogre::TexturePtr pierTexture = colorAtlas.createTexture(
|
||||
"proceduralTextureTemple" +
|
||||
Ogre::StringConverter::toString(e.id()));
|
||||
colorAtlas.saveImage("tmp2.png");
|
||||
templeMaterial = Ogre::MaterialManager::getSingletonPtr()->create(
|
||||
"proceduralMaterialTemple" +
|
||||
Ogre::StringConverter::toString(e.id()),
|
||||
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
|
||||
templeMaterial->getTechnique(0)->getPass(0)->setShininess(0);
|
||||
templeMaterial->getTechnique(0)->getPass(0)->setDiffuse(
|
||||
Ogre::ColourValue::White);
|
||||
templeMaterial->getTechnique(0)->getPass(0)->setSpecular(
|
||||
Ogre::ColourValue(1.0f, 1.0f, 0.9f));
|
||||
templeMaterial->getTechnique(0)
|
||||
->getPass(0)
|
||||
->createTextureUnitState(
|
||||
"proceduralTextureTemple" +
|
||||
Ogre::StringConverter::toString(e.id()));
|
||||
if (Ogre::RTShader::ShaderGenerator::initialize()) {
|
||||
templeMaterial->prepare();
|
||||
Ogre::RTShader::ShaderGenerator *mShaderGenerator =
|
||||
Ogre::RTShader::ShaderGenerator::
|
||||
getSingletonPtr();
|
||||
mShaderGenerator->createShaderBasedTechnique(
|
||||
*templeMaterial,
|
||||
Ogre::MaterialManager::DEFAULT_SCHEME_NAME,
|
||||
Ogre::RTShader::ShaderGenerator::
|
||||
DEFAULT_SCHEME_NAME);
|
||||
Ogre::RTShader::RenderState *pMainRenderState =
|
||||
mShaderGenerator->getRenderState(
|
||||
Ogre::RTShader::ShaderGenerator::
|
||||
DEFAULT_SCHEME_NAME,
|
||||
*templeMaterial);
|
||||
}
|
||||
}
|
||||
Ogre::Vector3 worldPosition = sceneNode->_getDerivedPosition();
|
||||
Ogre::Quaternion worldOrientation = sceneNode->_getDerivedOrientation();
|
||||
Procedural::BoxGenerator()
|
||||
.setSizeX(size)
|
||||
.setSizeY(20)
|
||||
.setSizeZ(size)
|
||||
.setEnableNormals(true)
|
||||
.setPosition(
|
||||
Ogre::Vector3(0.0f, -20.0f / 2.0f + elevation, 0.0f))
|
||||
.addToTriangleBuffer(tb);
|
||||
float pillarStep = 0.0f;
|
||||
float pillarStepSize = size * 4.0f / (float)pillarCount;
|
||||
int pcount = pillarCount;
|
||||
while (pillarStep < size - pillarRadius * 2.0f && pcount > 0) {
|
||||
float s = pillarStep - size / 2.0f + pillarStepSize / 2.0f;
|
||||
Procedural::CylinderGenerator()
|
||||
.setHeight(pillarHeight)
|
||||
.setRadius(pillarRadius)
|
||||
.setNumSegBase(8)
|
||||
.setPosition(s, 0.0f, -size / 2.0f + pillarRadius)
|
||||
.addToTriangleBuffer(tb);
|
||||
Procedural::CylinderGenerator()
|
||||
.setHeight(pillarHeight)
|
||||
.setRadius(pillarRadius)
|
||||
.setNumSegBase(8)
|
||||
.setPosition(s, 0.0f, size / 2.0f - pillarRadius)
|
||||
.addToTriangleBuffer(tb);
|
||||
Procedural::CylinderGenerator()
|
||||
.setHeight(pillarHeight)
|
||||
.setRadius(pillarRadius)
|
||||
.setNumSegBase(8)
|
||||
.setPosition(-size / 2.0f + pillarRadius, 0.0f, s)
|
||||
.addToTriangleBuffer(tb);
|
||||
Procedural::CylinderGenerator()
|
||||
.setHeight(pillarHeight)
|
||||
.setRadius(pillarRadius)
|
||||
.setNumSegBase(8)
|
||||
.setPosition(size / 2.0f - pillarRadius, 0.0f, s)
|
||||
.addToTriangleBuffer(tb);
|
||||
pillarStep += pillarStepSize;
|
||||
pcount--;
|
||||
pcount--;
|
||||
pcount--;
|
||||
pcount--;
|
||||
}
|
||||
OgreAssert(tb.getVertices().size() > 8, "bad box");
|
||||
for (auto &v : tb.getVertices()) {
|
||||
#if 0
|
||||
float c = 1.0 + 1.0 / (v.mPosition.y + baseHeight +
|
||||
elevation);
|
||||
v.mPosition.x *= c;
|
||||
v.mPosition.z *= c;
|
||||
#endif
|
||||
v.mUV *= 0.08f;
|
||||
v.mUV.x += 0.01f;
|
||||
v.mUV.x = Ogre::Math::Clamp(v.mUV.x, 0.0f, 0.4f);
|
||||
v.mUV.y = Ogre::Math::Clamp(v.mUV.y, 0.0f, 0.1f);
|
||||
}
|
||||
Ogre::String meshName =
|
||||
"Temple" + Ogre::StringConverter::toString(e.id());
|
||||
Ogre::MeshPtr mesh =
|
||||
Ogre::MeshManager::getSingleton().getByName(meshName);
|
||||
if (mesh)
|
||||
Ogre::MeshManager::getSingleton().remove(mesh);
|
||||
mesh = tb.transformToMesh(meshName);
|
||||
Ogre::LodConfig config(mesh);
|
||||
Geometry::setupLods(config);
|
||||
Ogre::Entity *ent = ECS::get<EngineData>().mScnMgr->createEntity(
|
||||
"Ent" + meshName, mesh);
|
||||
ent->setMaterial(templeMaterial);
|
||||
geo->addEntity(ent, worldPosition, worldOrientation,
|
||||
Ogre::Vector3::UNIT_SCALE);
|
||||
ECS::get<EngineData>().mScnMgr->destroyEntity(ent);
|
||||
// TODO set altar position
|
||||
createMeshGeometry("altar.glb", e, sceneNode->createChildSceneNode(),
|
||||
geo);
|
||||
geo->build();
|
||||
JPH::ShapeRefC shape =
|
||||
JoltPhysicsWrapper::getSingleton().createMeshShape(mesh);
|
||||
JPH::BodyID id = JoltPhysicsWrapper::getSingleton().createBody(
|
||||
shape.GetPtr(), 0, sceneNode, JPH::EMotionType::Static,
|
||||
Layers::NON_MOVING);
|
||||
e.set<JPH::BodyID>(id);
|
||||
JoltPhysicsWrapper::getSingleton().addBody(id,
|
||||
JPH::EActivation::Activate);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
20
src/gamedata/items/temple.h
Normal file
20
src/gamedata/items/temple.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef __TEMPLE_H__
|
||||
#define __TEMPLE_H__
|
||||
#include <OgreMeshLodGenerator.h>
|
||||
#include <flecs.h>
|
||||
namespace ECS
|
||||
{
|
||||
namespace Items
|
||||
{
|
||||
void createTempleItem();
|
||||
void createTempleMenu();
|
||||
void createTemplePopup(const std::pair<flecs::entity, Ogre::String> item);
|
||||
}
|
||||
namespace Geometry
|
||||
{
|
||||
void createTemple(flecs::entity e, Ogre::SceneNode *sceneNode,
|
||||
Ogre::StaticGeometry *geo);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
5935
src/gamedata/items/town.cpp
Normal file
5935
src/gamedata/items/town.cpp
Normal file
File diff suppressed because it is too large
Load Diff
26
src/gamedata/items/town.h
Normal file
26
src/gamedata/items/town.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef __TOWN_H__
|
||||
#define __TOWN_H__
|
||||
#include <OgreMeshLodGenerator.h>
|
||||
#include <flecs.h>
|
||||
namespace Procedural {
|
||||
class TriangleBuffer;
|
||||
}
|
||||
namespace ECS
|
||||
{
|
||||
namespace Items
|
||||
{
|
||||
void createTownItem();
|
||||
void createTownMenu();
|
||||
void createTownPopup(const std::pair<flecs::entity, Ogre::String> item);
|
||||
void runAllScriptsForTown(flecs::entity e);
|
||||
}
|
||||
namespace Geometry
|
||||
{
|
||||
void clampUV(flecs::entity e, Procedural::TriangleBuffer &tb,
|
||||
const Ogre::String &rectKey);
|
||||
Ogre::MaterialPtr createTownMaterial(flecs::entity e, bool force = false);
|
||||
void createTown(flecs::entity e, Ogre::SceneNode *sceneNode,
|
||||
Ogre::StaticGeometry *geo);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -90,7 +90,10 @@ public:
|
||||
Layers::MOVING; // Non moving only collides with moving
|
||||
case Layers::MOVING:
|
||||
return true; // Moving collides with everything
|
||||
default:
|
||||
case Layers::SENSORS:
|
||||
return inObject2 ==
|
||||
Layers::MOVING; // Non moving only collides with moving
|
||||
default:
|
||||
JPH_ASSERT(false);
|
||||
return false;
|
||||
}
|
||||
@@ -107,7 +110,8 @@ public:
|
||||
mObjectToBroadPhase[Layers::NON_MOVING] =
|
||||
BroadPhaseLayers::NON_MOVING;
|
||||
mObjectToBroadPhase[Layers::MOVING] = BroadPhaseLayers::MOVING;
|
||||
}
|
||||
mObjectToBroadPhase[Layers::SENSORS] = BroadPhaseLayers::MOVING;
|
||||
}
|
||||
|
||||
virtual uint GetNumBroadPhaseLayers() const override
|
||||
{
|
||||
@@ -265,11 +269,13 @@ public:
|
||||
void DrawLine(JPH::RVec3Arg inFrom, JPH::RVec3Arg inTo,
|
||||
JPH::ColorArg inColor) override
|
||||
{
|
||||
JPH::Vec4 color = inColor.ToVec4();
|
||||
mLines.push_back({ { inFrom[0], inFrom[1], inFrom[2] },
|
||||
{ inTo[0], inTo[1], inTo[2] },
|
||||
Ogre::ColourValue(color[0], color[1],
|
||||
color[2], color[3]) });
|
||||
JPH::Vec4 color = inColor.ToVec4();
|
||||
mLines.push_back(
|
||||
{ { (float)inFrom[0], (float)inFrom[1],
|
||||
(float)inFrom[2] },
|
||||
{ (float)inTo[0], (float)inTo[1], (float)inTo[2] },
|
||||
Ogre::ColourValue(color[0], color[1], color[2],
|
||||
color[3]) });
|
||||
}
|
||||
void DrawTriangle(JPH::RVec3Arg inV1, JPH::RVec3Arg inV2,
|
||||
JPH::RVec3Arg inV3, JPH::ColorArg inColor,
|
||||
@@ -283,6 +289,7 @@ public:
|
||||
Ogre::Vector3 p3 = JoltPhysics::convert(inV3);
|
||||
Ogre::ColourValue cv(color[0], color[1], color[2], color[3]);
|
||||
|
||||
#if 0
|
||||
float dproj1 = p1.dotProduct(d);
|
||||
float dproj2 = p2.dotProduct(d);
|
||||
float dproj3 = p3.dotProduct(d);
|
||||
@@ -290,6 +297,7 @@ public:
|
||||
return;
|
||||
if (dproj1 > 50 && dproj2 > 50 && dproj3 > 50)
|
||||
return;
|
||||
#endif
|
||||
mLines.push_back({ p1, p2, cv });
|
||||
#if 0
|
||||
mTriangles.push_back({ { { inV1[0], inV1[1], inV1[2] },
|
||||
@@ -414,11 +422,14 @@ DebugRenderer::DebugRenderer(Ogre::SceneManager *scnMgr,
|
||||
pass->setCullingMode(Ogre::CullingMode::CULL_NONE);
|
||||
pass->setVertexColourTracking(Ogre::TVC_AMBIENT);
|
||||
pass->setLightingEnabled(false);
|
||||
pass->setDepthWriteEnabled(false);
|
||||
pass->setDepthCheckEnabled(false);
|
||||
DebugRenderer::Initialize();
|
||||
scnMgr->getRootSceneNode()->attachObject(mObject);
|
||||
mLines.reserve(6000);
|
||||
mObject->estimateVertexCount(64000);
|
||||
mObject->estimateIndexCount(8000);
|
||||
mObject->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY);
|
||||
}
|
||||
DebugRenderer::~DebugRenderer()
|
||||
{
|
||||
@@ -435,7 +446,7 @@ Ogre::Vector3 convert(const JPH::Vec3Arg &vec)
|
||||
{
|
||||
return { vec[0], vec[1], vec[2] };
|
||||
}
|
||||
JPH::Vec3 convert(const Ogre::Vector3 &vec)
|
||||
JPH::RVec3 convert(const Ogre::Vector3 &vec)
|
||||
{
|
||||
return { vec.x, vec.y, vec.z };
|
||||
}
|
||||
@@ -451,7 +462,7 @@ void CompoundShapeBuilder::addShape(JPH::ShapeRefC shape,
|
||||
const Ogre::Vector3 &position,
|
||||
const Ogre::Quaternion &rotation)
|
||||
{
|
||||
shapeSettings.AddShape(JoltPhysics::convert(position),
|
||||
shapeSettings.AddShape(JoltPhysics::convert<JPH::Vec3>(position),
|
||||
JoltPhysics::convert(rotation), shape.GetPtr());
|
||||
}
|
||||
JPH::ShapeRefC CompoundShapeBuilder::build()
|
||||
@@ -760,7 +771,7 @@ public:
|
||||
timeAccumulator -= fixedDeltaTime;
|
||||
}
|
||||
for (JPH::BodyID bID : bodies) {
|
||||
JPH::Vec3 p;
|
||||
JPH::RVec3 p;
|
||||
JPH::Quat q;
|
||||
if (id2node.find(bID) == id2node.end())
|
||||
continue;
|
||||
@@ -801,7 +812,7 @@ public:
|
||||
}
|
||||
static JPH::ShapeRefC createBoxShape(float x, float y, float z)
|
||||
{
|
||||
return new JPH::BoxShape(JPH::RVec3(x, y, z));
|
||||
return new JPH::BoxShape(JPH::Vec3(x, y, z));
|
||||
}
|
||||
static JPH::ShapeRefC createCylinderShape(float halfHeight,
|
||||
float radius)
|
||||
@@ -809,7 +820,7 @@ public:
|
||||
return new JPH::CylinderShape(halfHeight, radius);
|
||||
}
|
||||
JPH::BodyCreationSettings createBodyCreationSettings(
|
||||
JPH::Shape *shape, JPH::Vec3 &position, JPH::Quat &rotation,
|
||||
JPH::Shape *shape, JPH::RVec3 &position, JPH::Quat &rotation,
|
||||
JPH::EMotionType motionType, JPH::ObjectLayer layer)
|
||||
{
|
||||
JPH::BodyCreationSettings body_settings(
|
||||
@@ -818,7 +829,7 @@ public:
|
||||
}
|
||||
JPH::BodyCreationSettings
|
||||
createBodyCreationSettings(JPH::ShapeSettings *shapeSettings,
|
||||
JPH::Vec3 &position, JPH::Quat &rotation,
|
||||
JPH::RVec3 &position, JPH::Quat &rotation,
|
||||
JPH::EMotionType motionType,
|
||||
JPH::ObjectLayer layer)
|
||||
{
|
||||
@@ -843,8 +854,8 @@ public:
|
||||
{
|
||||
JPH::BodyInterface &body_interface =
|
||||
physics_system.GetBodyInterface();
|
||||
body_interface.AddAngularImpulse(id,
|
||||
JoltPhysics::convert(impulse));
|
||||
body_interface.AddAngularImpulse(
|
||||
id, JoltPhysics::convert<JPH::Vec3>(impulse));
|
||||
}
|
||||
JPH::BodyID createBody(const JPH::BodyCreationSettings &settings,
|
||||
ActivationListener *listener = nullptr)
|
||||
@@ -890,8 +901,14 @@ public:
|
||||
msp.ScaleToMass(mass);
|
||||
bodySettings.mMassPropertiesOverride = msp;
|
||||
}
|
||||
return createBody(bodySettings, listener);
|
||||
}
|
||||
JPH::BodyID id = createBody(bodySettings, listener);
|
||||
if (shape->GetType() == JPH::EShapeType::HeightField) {
|
||||
JPH::BodyInterface &body_interface =
|
||||
physics_system.GetBodyInterface();
|
||||
body_interface.SetFriction(id, 1.0f);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
JPH::BodyID createBody(const JPH::Shape *shape, float mass,
|
||||
Ogre::SceneNode *node, JPH::EMotionType motion,
|
||||
JPH::ObjectLayer layer,
|
||||
@@ -901,20 +918,8 @@ public:
|
||||
const Ogre::Quaternion &rotation =
|
||||
node->_getDerivedOrientation();
|
||||
std::cout << "body position: " << position << std::endl;
|
||||
JPH::BodyCreationSettings bodySettings(
|
||||
shape, JoltPhysics::convert(position),
|
||||
JoltPhysics::convert(rotation), motion, layer);
|
||||
if (mass > 0.001f) {
|
||||
JPH::MassProperties msp;
|
||||
msp.ScaleToMass(mass);
|
||||
bodySettings.mMassPropertiesOverride = msp;
|
||||
}
|
||||
JPH::BodyID id = createBody(bodySettings, listener);
|
||||
if (shape->GetType() == JPH::EShapeType::HeightField) {
|
||||
JPH::BodyInterface &body_interface =
|
||||
physics_system.GetBodyInterface();
|
||||
body_interface.SetFriction(id, 1.0f);
|
||||
}
|
||||
JPH::BodyID id = createBody(shape, mass, position, rotation,
|
||||
motion, layer, listener);
|
||||
id2node[id] = node;
|
||||
node2id[node] = id;
|
||||
return id;
|
||||
@@ -961,7 +966,7 @@ public:
|
||||
JPH::MutableCompoundShape *master =
|
||||
static_cast<JPH::MutableCompoundShape *>(
|
||||
compoundShape.GetPtr());
|
||||
master->AddShape(JoltPhysics::convert(position),
|
||||
master->AddShape(JoltPhysics::convert<JPH::Vec3>(position),
|
||||
JoltPhysics::convert(rotation),
|
||||
childShape.GetPtr());
|
||||
}
|
||||
@@ -1272,8 +1277,9 @@ public:
|
||||
{
|
||||
int i;
|
||||
JPH::HeightFieldShapeSettings heightfieldSettings(
|
||||
samples, JoltPhysics::convert(offset),
|
||||
JoltPhysics::convert(scale), (uint32_t)sampleCount);
|
||||
samples, JoltPhysics::convert<JPH::Vec3>(offset),
|
||||
JoltPhysics::convert<JPH::Vec3>(scale),
|
||||
(uint32_t)sampleCount);
|
||||
for (i = 0; i < sampleCount; i++) {
|
||||
memcpy(heightfieldSettings.mHeightSamples.data() +
|
||||
sampleCount * i,
|
||||
@@ -1296,9 +1302,10 @@ public:
|
||||
"bad parameters");
|
||||
JPH::MutableCompoundShapeSettings settings;
|
||||
for (i = 0; i < shapes.size(); i++)
|
||||
settings.AddShape(JoltPhysics::convert(positions[i]),
|
||||
JoltPhysics::convert(rotations[i]),
|
||||
shapes[i].GetPtr());
|
||||
settings.AddShape(
|
||||
JoltPhysics::convert<JPH::Vec3>(positions[i]),
|
||||
JoltPhysics::convert(rotations[i]),
|
||||
shapes[i].GetPtr());
|
||||
JPH::ShapeSettings::ShapeResult result = settings.Create();
|
||||
OgreAssert(result.Get(), "Can not create compound shape");
|
||||
return result.Get();
|
||||
@@ -1314,9 +1321,10 @@ public:
|
||||
"bad parameters");
|
||||
JPH::StaticCompoundShapeSettings settings;
|
||||
for (i = 0; i < shapes.size(); i++)
|
||||
settings.AddShape(JoltPhysics::convert(positions[i]),
|
||||
JoltPhysics::convert(rotations[i]),
|
||||
shapes[i].GetPtr());
|
||||
settings.AddShape(
|
||||
JoltPhysics::convert<JPH::Vec3>(positions[i]),
|
||||
JoltPhysics::convert(rotations[i]),
|
||||
shapes[i].GetPtr());
|
||||
JPH::ShapeSettings::ShapeResult result = settings.Create();
|
||||
OgreAssert(result.Get(), "Can not create compound shape");
|
||||
return result.Get();
|
||||
@@ -1326,12 +1334,24 @@ public:
|
||||
JPH::ShapeRefC shape)
|
||||
{
|
||||
JPH::OffsetCenterOfMassShapeSettings settings(
|
||||
JoltPhysics::convert(offset), shape.GetPtr());
|
||||
JoltPhysics::convert<JPH::Vec3>(offset),
|
||||
shape.GetPtr());
|
||||
JPH::ShapeSettings::ShapeResult result = settings.Create();
|
||||
OgreAssert(result.Get(), "Can not create com offset shape");
|
||||
return result.Get();
|
||||
}
|
||||
void applyBuoyancyImpulse(JPH::BodyID id,
|
||||
JPH::ShapeRefC
|
||||
createRotatedTranslatedShape(const Ogre::Vector3 &offset,
|
||||
const Ogre::Quaternion rotation,
|
||||
JPH::ShapeRefC shape)
|
||||
{
|
||||
return JPH::RotatedTranslatedShapeSettings(
|
||||
JoltPhysics::convert<JPH::Vec3>(offset),
|
||||
JoltPhysics::convert(rotation), shape)
|
||||
.Create()
|
||||
.Get();
|
||||
}
|
||||
void applyBuoyancyImpulse(JPH::BodyID id,
|
||||
const Ogre::Vector3 &surfacePosition,
|
||||
const Ogre::Vector3 &surfaceNormal,
|
||||
float buoyancy, float linearDrag,
|
||||
@@ -1342,11 +1362,12 @@ public:
|
||||
JPH::BodyLockWrite lock(physics_system.GetBodyLockInterface(),
|
||||
id);
|
||||
JPH::Body &body = lock.GetBody();
|
||||
body.ApplyBuoyancyImpulse(JoltPhysics::convert(surfacePosition),
|
||||
JoltPhysics::convert(surfaceNormal),
|
||||
buoyancy, linearDrag, angularDrag,
|
||||
JoltPhysics::convert(fluidVelocity),
|
||||
JoltPhysics::convert(gravity), dt);
|
||||
body.ApplyBuoyancyImpulse(
|
||||
JoltPhysics::convert(surfacePosition),
|
||||
JoltPhysics::convert<JPH::Vec3>(surfaceNormal),
|
||||
buoyancy, linearDrag, angularDrag,
|
||||
JoltPhysics::convert<JPH::Vec3>(fluidVelocity),
|
||||
JoltPhysics::convert<JPH::Vec3>(gravity), dt);
|
||||
}
|
||||
void applyBuoyancyImpulse(JPH::BodyID id,
|
||||
const Ogre::Vector3 &surfacePosition,
|
||||
@@ -1358,11 +1379,12 @@ public:
|
||||
JPH::BodyLockWrite lock(physics_system.GetBodyLockInterface(),
|
||||
id);
|
||||
JPH::Body &body = lock.GetBody();
|
||||
body.ApplyBuoyancyImpulse(JoltPhysics::convert(surfacePosition),
|
||||
JoltPhysics::convert(surfaceNormal),
|
||||
buoyancy, linearDrag, angularDrag,
|
||||
JoltPhysics::convert(fluidVelocity),
|
||||
physics_system.GetGravity(), dt);
|
||||
body.ApplyBuoyancyImpulse(
|
||||
JoltPhysics::convert(surfacePosition),
|
||||
JoltPhysics::convert<JPH::Vec3>(surfaceNormal),
|
||||
buoyancy, linearDrag, angularDrag,
|
||||
JoltPhysics::convert<JPH::Vec3>(fluidVelocity),
|
||||
physics_system.GetGravity(), dt);
|
||||
}
|
||||
bool isActive(JPH::BodyID id)
|
||||
{
|
||||
@@ -1385,7 +1407,7 @@ public:
|
||||
void getPositionAndRotation(JPH::BodyID id, Ogre::Vector3 &position,
|
||||
Ogre::Quaternion &rotation)
|
||||
{
|
||||
JPH::Vec3 _position;
|
||||
JPH::RVec3 _position;
|
||||
JPH::Quat _rotation;
|
||||
physics_system.GetBodyInterface().GetPositionAndRotation(
|
||||
id, _position, _rotation);
|
||||
@@ -1443,11 +1465,11 @@ public:
|
||||
void broadphaseQuery(float dt, const Ogre::Vector3 &position,
|
||||
std::set<JPH::BodyID> &inWater)
|
||||
{
|
||||
JPH::Vec3 surface_point = JoltPhysics::convert(
|
||||
JPH::RVec3 surface_point = JoltPhysics::convert(
|
||||
position + Ogre::Vector3(0, -0.1f, 0));
|
||||
|
||||
MyCollector collector(&physics_system, surface_point,
|
||||
JPH::Vec3::sAxisY(), dt);
|
||||
JPH::Vec3::sAxisY(), dt);
|
||||
// Apply buoyancy to all bodies that intersect with the water
|
||||
JPH::AABox water_box(-JPH::Vec3(1000, 1000, 1000),
|
||||
JPH::Vec3(1000, 0.1f, 1000));
|
||||
@@ -1469,19 +1491,21 @@ public:
|
||||
}
|
||||
}
|
||||
bool raycastQuery(Ogre::Vector3 startPoint, Ogre::Vector3 endPoint,
|
||||
Ogre::Vector3 &position)
|
||||
Ogre::Vector3 &position, JPH::BodyID &id)
|
||||
{
|
||||
int i;
|
||||
Ogre::Vector3 direction = endPoint - startPoint;
|
||||
JPH::RRayCast ray{ JoltPhysics::convert(startPoint),
|
||||
JoltPhysics::convert(direction) };
|
||||
JoltPhysics::convert<JPH::Vec3>(direction) };
|
||||
JPH::RayCastResult hit;
|
||||
bool hadHit = physics_system.GetNarrowPhaseQuery().CastRay(
|
||||
ray, hit, {},
|
||||
JPH::SpecifiedObjectLayerFilter(Layers::NON_MOVING));
|
||||
if (hadHit)
|
||||
if (hadHit) {
|
||||
position = JoltPhysics::convert(
|
||||
ray.GetPointOnRay(hit.mFraction));
|
||||
id = hit.mBodyID;
|
||||
}
|
||||
return hadHit;
|
||||
}
|
||||
};
|
||||
@@ -1596,7 +1620,14 @@ JPH::ShapeRefC
|
||||
JoltPhysicsWrapper::createOffsetCenterOfMassShape(const Ogre::Vector3 &offset,
|
||||
JPH::ShapeRefC shape)
|
||||
{
|
||||
return phys->createOffsetCenterOfMassShape(offset, shape);
|
||||
return phys->createOffsetCenterOfMassShape(offset, shape);
|
||||
}
|
||||
|
||||
JPH::ShapeRefC JoltPhysicsWrapper::createRotatedTranslatedShape(
|
||||
const Ogre::Vector3 &offset, const Ogre::Quaternion rotation,
|
||||
JPH::ShapeRefC shape)
|
||||
{
|
||||
return phys->createRotatedTranslatedShape(offset, rotation, shape);
|
||||
}
|
||||
|
||||
JPH::BodyID
|
||||
@@ -1765,9 +1796,9 @@ void JoltPhysicsWrapper::removeContactListener(const JPH::BodyID &id)
|
||||
}
|
||||
bool JoltPhysicsWrapper::raycastQuery(Ogre::Vector3 startPoint,
|
||||
Ogre::Vector3 endPoint,
|
||||
Ogre::Vector3 &position)
|
||||
Ogre::Vector3 &position, JPH::BodyID &id)
|
||||
{
|
||||
return phys->raycastQuery(startPoint, endPoint, position);
|
||||
return phys->raycastQuery(startPoint, endPoint, position, id);
|
||||
}
|
||||
template <>
|
||||
JoltPhysicsWrapper *Ogre::Singleton<JoltPhysicsWrapper>::msSingleton = 0;
|
||||
|
||||
@@ -26,7 +26,8 @@ namespace Layers
|
||||
{
|
||||
static constexpr JPH::ObjectLayer NON_MOVING = 0;
|
||||
static constexpr JPH::ObjectLayer MOVING = 1;
|
||||
static constexpr JPH::ObjectLayer NUM_LAYERS = 2;
|
||||
static constexpr JPH::ObjectLayer SENSORS = 2;
|
||||
static constexpr JPH::ObjectLayer NUM_LAYERS = 3;
|
||||
};
|
||||
|
||||
// Each broadphase layer results in a separate bounding volume tree in the broad phase. You at least want to have
|
||||
@@ -43,8 +44,15 @@ static constexpr uint NUM_LAYERS(2);
|
||||
|
||||
namespace JoltPhysics
|
||||
{
|
||||
Ogre::Vector3 convert(const JPH::Vec3Arg &vec);
|
||||
JPH::Vec3 convert(const Ogre::Vector3 &vec);
|
||||
template<class T>
|
||||
Ogre::Vector3 convert(const T &vec)
|
||||
{
|
||||
return {vec[0], vec[1], vec[2]};
|
||||
}
|
||||
template<class T> T convert(const Ogre::Vector3 &vec)
|
||||
{
|
||||
return { vec.x, vec.y, vec.z };
|
||||
}
|
||||
Ogre::Quaternion convert(const JPH::QuatArg &rot);
|
||||
JPH::Quat convert(const Ogre::Quaternion &rot);
|
||||
struct ShapeData;
|
||||
@@ -134,7 +142,10 @@ public:
|
||||
JPH::ShapeRefC
|
||||
createOffsetCenterOfMassShape(const Ogre::Vector3 &offset,
|
||||
JPH::ShapeRefC shape);
|
||||
JPH::BodyID createBody(const JPH::BodyCreationSettings &settings);
|
||||
JPH::ShapeRefC
|
||||
createRotatedTranslatedShape(const Ogre::Vector3 &offset, const Ogre::Quaternion rotation,
|
||||
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,
|
||||
@@ -205,6 +216,6 @@ public:
|
||||
listener);
|
||||
void removeContactListener(const JPH::BodyID &id);
|
||||
bool raycastQuery(Ogre::Vector3 startPoint, Ogre::Vector3 endPoint,
|
||||
Ogre::Vector3 &position);
|
||||
Ogre::Vector3 &position, JPH::BodyID &id);
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -9,13 +9,14 @@ 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(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(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_executable(mark_harbors mark_harbors.cpp)
|
||||
#target_link_libraries(mark_harbors PRIVATE lua OgreMain OgreRTShaderSystem)
|
||||
|
||||
#add_custom_target(world ALL DEPENDS test test2)
|
||||
|
||||
add_custom_target(world ALL DEPENDS test test2)
|
||||
|
||||
Reference in New Issue
Block a user