Compare commits
15 Commits
81a78990ce
...
4cf0ea5321
| Author | SHA1 | Date | |
|---|---|---|---|
| 4cf0ea5321 | |||
| 8320b14358 | |||
| 1d358d206e | |||
| cd91174f5d | |||
| 4b24d85123 | |||
| f86e7fd96c | |||
| 272e202774 | |||
| 80fba23cd2 | |||
| c031056e17 | |||
| 49fc547295 | |||
| d6d61229f8 | |||
| 5bb529bc31 | |||
| d3c93c5c18 | |||
| 9bb9e2c09b | |||
| 3f99099919 |
@@ -76,6 +76,8 @@ add_subdirectory(src/world)
|
||||
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)
|
||||
@@ -202,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
|
||||
@@ -355,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})
|
||||
|
||||
|
||||
114
Game.cpp
114
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,19 +430,25 @@ 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);
|
||||
@@ -435,14 +479,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 +650,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 +786,7 @@ public:
|
||||
}
|
||||
flecs::entity getPlayer() const
|
||||
{
|
||||
flecs::entity player =
|
||||
ECS::get().lookup("ECS::CharacterModule::player");
|
||||
return player;
|
||||
return ECS::player;
|
||||
}
|
||||
void enableDbgDraw(bool enable)
|
||||
{
|
||||
|
||||
31
assets/blender/buildings/parts/CMakeLists.txt
Normal file
31
assets/blender/buildings/parts/CMakeLists.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
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)
|
||||
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}/${PARTS_FILE}
|
||||
-b -Y -P
|
||||
${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_building_parts.py
|
||||
-- ${PARTS_OUTPUT_DIR}
|
||||
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.
BIN
assets/blender/buildings/parts/pier.blend
LFS
Normal file
BIN
assets/blender/buildings/parts/pier.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,141 @@ 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 crash_bind = function()
|
||||
crash()
|
||||
end
|
||||
this.story:bind('crash', crash_bind)
|
||||
this.story:begin()
|
||||
this:narration_update()
|
||||
local props = this._get_properties()
|
||||
print(props)
|
||||
local json_data = json.decode(props)
|
||||
print(dump(json_data))
|
||||
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
|
||||
|
||||
@@ -20,6 +20,8 @@ FileSystem=resources/terrain
|
||||
[General]
|
||||
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)
|
||||
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}
|
||||
@@ -19,7 +19,7 @@ foreach(DIR_NAME ${COPY_DIRECTORIES})
|
||||
list(APPEND INSTALL_DEPS ${CMAKE_CURRENT_BINARY_DIR}/${DIR_NAME})
|
||||
endforeach()
|
||||
|
||||
add_custom_target(install_resources DEPENDS ${INSTALL_DEPS})
|
||||
add_custom_target(install_resources DEPENDS import_buildings import_building_parts ${INSTALL_DEPS})
|
||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/resources.cfg
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/../../resources.cfg ${CMAKE_CURRENT_BINARY_DIR}/resources.cfg
|
||||
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/../../resources.cfg
|
||||
@@ -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,39 @@ 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::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 +585,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 +605,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 +641,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 +659,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 +727,7 @@ int main()
|
||||
{
|
||||
App ctx;
|
||||
ctx.configure();
|
||||
ctx.enableDbgDraw(false);
|
||||
// ctx.enableDbgDraw(false);
|
||||
ctx.getRoot()->startRendering();
|
||||
ctx.setWindowGrab(false);
|
||||
ctx.closeApp();
|
||||
|
||||
@@ -3,17 +3,20 @@ set(CMAKE_CXX_STANDARD 17)
|
||||
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 SmartObject.cpp SlotsModule.cpp goap.cpp)
|
||||
VehicleManagerModule.cpp AppModule.cpp StaticGeometryModule.cpp SmartObject.cpp SlotsModule.cpp
|
||||
PlayerActionModule.cpp goap.cpp)
|
||||
target_link_libraries(GameData PUBLIC
|
||||
lua
|
||||
flecs::flecs_static
|
||||
nlohmann_json::nlohmann_json
|
||||
OgreMain
|
||||
OgreBites
|
||||
OgrePaging OgreTerrain OgreOverlay
|
||||
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_compile_definitions(GameData PRIVATE FLECS_CPP_NO_AUTO_REGISTRATION)
|
||||
|
||||
@@ -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,200 @@
|
||||
#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, flecs::entity e, int index)
|
||||
{
|
||||
NPCActionNodes &anodes = e.get_mut<NPCActionNodes>();
|
||||
const TownNPCs &npcs = town.get<TownNPCs>();
|
||||
nlohmann::json npcprops = npcs.npcs.at(index).props;
|
||||
const CharacterBase &ch = e.get<CharacterBase>();
|
||||
Ogre::Vector3 characterPos = ch.mBodyNode->_getDerivedPosition();
|
||||
Ogre::Quaternion characterRot = ch.mBodyNode->_getDerivedOrientation();
|
||||
if (anodes.anodes.size() > 0) {
|
||||
int i;
|
||||
for (i = 0; i < anodes.anodes.size(); i++) {
|
||||
auto &anode = anodes.anodes[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);
|
||||
}
|
||||
e.modified<NPCActionNodes>();
|
||||
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;
|
||||
anodes.anodes.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;
|
||||
anodes.anodes.push_back(anode);
|
||||
}
|
||||
e.modified<NPCActionNodes>();
|
||||
}
|
||||
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.component<NPCActionNodes>().on_add(
|
||||
[](flecs::entity e, NPCActionNodes &anodes) {
|
||||
anodes.anodes.clear();
|
||||
});
|
||||
ecs.system<TerrainItem, TownNPCs>("UpdateCharacters")
|
||||
.immediate()
|
||||
.kind(flecs::OnUpdate)
|
||||
.interval(1.0f)
|
||||
.write<CharacterBase>()
|
||||
.write<NPCActionNodes>()
|
||||
.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;
|
||||
ECS::get().defer_suspend();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
ECS::get().defer_resume();
|
||||
});
|
||||
ecs.system<TerrainItem, TownNPCs>("UpdateCharacters2")
|
||||
.immediate()
|
||||
.kind(flecs::OnUpdate)
|
||||
.write<CharacterBase>()
|
||||
.write<NPCActionNodes>()
|
||||
.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::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()) {
|
||||
if (data.e.has<CharacterBase>() &&
|
||||
data.e.has<LivesIn>(town))
|
||||
createNPCActionNodes(
|
||||
town, data.e,
|
||||
index);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
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.set<CharacterLocation>({ rotation, position })
|
||||
.set<CharacterConf>({ "normal-male.glb" })
|
||||
.add<Character>()
|
||||
// .add<CharacterDisablePhysics>()
|
||||
.add<Player>();
|
||||
std::cout << "End player create" << std::endl;
|
||||
count++;
|
||||
return player;
|
||||
}
|
||||
flecs::entity
|
||||
@@ -36,7 +210,44 @@ CharacterManagerModule::createCharacterData(const Ogre::String model,
|
||||
.entity()
|
||||
.set<CharacterLocation>({ rotation, position })
|
||||
.set<CharacterConf>({ model })
|
||||
.add<Character>();
|
||||
return e;
|
||||
.add<Character>()
|
||||
.add<NPCActionNodes>();
|
||||
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,25 @@
|
||||
#ifndef _CHARACTER_MANAGER_MODULE_
|
||||
#define _CHARACTER_MANAGER_MODULE_
|
||||
#include <flecs.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
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::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 +27,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,7 +5,9 @@
|
||||
#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
|
||||
@@ -30,6 +32,7 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
ecs.component<Male>();
|
||||
ecs.component<Female>();
|
||||
ecs.component<Planner>().add(flecs::Singleton);
|
||||
ecs.import <CharacterAnimationModule>();
|
||||
ecs.import <TerrainModule>();
|
||||
ecs.import <WaterModule>();
|
||||
|
||||
@@ -41,8 +44,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 +268,54 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
}
|
||||
});
|
||||
#endif
|
||||
ecs.observer<const EngineData, const CharacterLocation,
|
||||
const CharacterConf>("SetupCharacterM")
|
||||
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;
|
||||
});
|
||||
|
||||
ecs.observer<const CharacterLocation, const CharacterConf>(
|
||||
"SetupCharacterObs")
|
||||
.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;
|
||||
.without<CharacterBase>()
|
||||
.without<AnimationControl>()
|
||||
.write<CharacterBase>()
|
||||
.write<AnimationControl>()
|
||||
.each([&](flecs::entity e, const CharacterLocation &loc,
|
||||
const CharacterConf &conf) {
|
||||
std::cout << "OBSERVER!!!"
|
||||
<< " " << e.id() << std::endl;
|
||||
if (e.has<CharacterBase>() || e.has<AnimationControl>())
|
||||
return;
|
||||
e.world().defer_begin();
|
||||
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>();
|
||||
anim.configured = false;
|
||||
});
|
||||
e.world().defer_end();
|
||||
});
|
||||
#if 0
|
||||
ecs.system<const EngineData, const CharacterLocation,
|
||||
const CharacterConf, Body2Entity>("SetupCharacter")
|
||||
@@ -557,6 +578,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,10 +591,45 @@ 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);
|
||||
canonDist += distChange;
|
||||
}
|
||||
JPH::BodyID id;
|
||||
Ogre::Vector3 position;
|
||||
Ogre::Vector3 d = (camera.mCameraPivot->_getDerivedPosition() -
|
||||
camera.mCameraGoal->_getDerivedPosition())
|
||||
.normalisedCopy();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
CharacterAIModule::CharacterAIModule(flecs::world &ecs)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,13 @@
|
||||
#include "EventModule.h"
|
||||
#include "CharacterManagerModule.h"
|
||||
#include "VehicleManagerModule.h"
|
||||
#include "PlayerActionModule.h"
|
||||
#include "AppModule.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 +44,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 +56,13 @@ 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.add<ActionNodeList>();
|
||||
|
||||
ecs.system<EngineData>("UpdateDelta")
|
||||
.kind(flecs::OnUpdate)
|
||||
@@ -87,7 +95,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>();
|
||||
@@ -99,6 +107,7 @@ void setup(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
false,
|
||||
{ 0, 0, 0 } });
|
||||
if (!ecs.has<LuaBase>())
|
||||
@@ -108,18 +117,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>();
|
||||
@@ -131,6 +190,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)
|
||||
@@ -163,8 +224,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>();
|
||||
@@ -175,6 +239,7 @@ void setupEditor(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
false,
|
||||
{ 0, 0, 0 } });
|
||||
ecs.set<GUI>({ true, true, true, false, false, "", {}, -1 });
|
||||
@@ -200,4 +265,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,8 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
|
||||
}
|
||||
// gr.velocity.y = 0.0f;
|
||||
// v.y = 0.0f;
|
||||
ch->SetLinearVelocity(JoltPhysics::convert(v));
|
||||
ch->SetLinearVelocity(
|
||||
JoltPhysics::convert<JPH::Vec3>(v));
|
||||
gr.velocity = Ogre::Vector3::ZERO;
|
||||
});
|
||||
ecs.system<const EngineData, CharacterBase, const CharacterBody,
|
||||
@@ -750,10 +755,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
|
||||
|
||||
470
src/gamedata/PlayerActionModule.cpp
Normal file
470
src/gamedata/PlayerActionModule.cpp
Normal file
@@ -0,0 +1,470 @@
|
||||
#include <nanoflann.hpp>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <flecs.h>
|
||||
#include <nlohmann/json.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_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 : e.get<NPCActionNodes>().anodes) {
|
||||
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.dirty = true;
|
||||
alist.nodes.reserve(1000);
|
||||
alist.dynamicNodes.reserve(1000);
|
||||
alist.selected = -1;
|
||||
alist.busy = false;
|
||||
})
|
||||
.add(flecs::Singleton);
|
||||
ecs.system<ActionNodeList>("updateNodeList")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([](ActionNodeList &list) {
|
||||
if (list.busy)
|
||||
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.query(playerPos, list.points,
|
||||
list.distances);
|
||||
} else {
|
||||
list.query(cameraPos, list.points,
|
||||
list.distances);
|
||||
}
|
||||
}
|
||||
});
|
||||
ecs.system<ActionNodeList, const Input>("ActivateActionNode")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([this](ActionNodeList &list, const Input &input) {
|
||||
if (input.control & 32)
|
||||
std::cout << "act pressed" << std::endl;
|
||||
if (list.busy)
|
||||
return;
|
||||
if (input.act_pressed && list.selected >= 0) {
|
||||
std::cout << list.dynamicNodes[list.selected]
|
||||
.props.dump(4)
|
||||
<< std::endl;
|
||||
flecs::entity_t townid =
|
||||
list.dynamicNodes[list.selected]
|
||||
.props["town"]
|
||||
.get<flecs::entity_t>();
|
||||
flecs::entity town = ECS::get().entity(townid);
|
||||
int index = list.dynamicNodes[list.selected]
|
||||
.props["index"]
|
||||
.get<int>();
|
||||
for (auto it = actionWords.begin();
|
||||
it != actionWords.end(); it++) {
|
||||
if (it->first ==
|
||||
list.dynamicNodes[list.selected]
|
||||
.action) {
|
||||
(*it->second)(
|
||||
town, index,
|
||||
list.dynamicNodes
|
||||
[list.selected]
|
||||
.action);
|
||||
list.busy = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ECS::get<GUI>().enabled)
|
||||
list.busy = false;
|
||||
});
|
||||
}
|
||||
|
||||
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 :
|
||||
e.get<NPCActionNodes>().anodes) {
|
||||
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::build()
|
||||
{
|
||||
dynamicNodes.clear();
|
||||
dynamicNodes.insert(dynamicNodes.end(), nodes.begin(), nodes.end());
|
||||
ECS::get().query_builder<const NPCActionNodes>().each(
|
||||
[&](flecs::entity e, const NPCActionNodes &anodes) {
|
||||
dynamicNodes.insert(dynamicNodes.end(),
|
||||
anodes.anodes.begin(),
|
||||
anodes.anodes.end());
|
||||
});
|
||||
|
||||
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)
|
||||
{
|
||||
build();
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
63
src/gamedata/PlayerActionModule.h
Normal file
63
src/gamedata/PlayerActionModule.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#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;
|
||||
};
|
||||
std::vector<ActionNode> nodes, dynamicNodes;
|
||||
std::shared_ptr<indexObject> indexObj;
|
||||
std::vector<size_t> points;
|
||||
std::vector<float> distances;
|
||||
int selected;
|
||||
bool dirty;
|
||||
bool busy;
|
||||
void build();
|
||||
bool query(const Ogre::Vector3 &position, std::vector<size_t> &points, std::vector<float> &distances);
|
||||
int addNode(struct ActionNodeList::ActionNode &node)
|
||||
{
|
||||
int index = nodes.size();
|
||||
nodes.push_back(node);
|
||||
dirty = true;
|
||||
return index;
|
||||
}
|
||||
void removeNode(int index)
|
||||
{
|
||||
nodes.erase(nodes.begin() + index);
|
||||
}
|
||||
};
|
||||
struct NPCActionNodes {
|
||||
std::vector<ActionNodeList::ActionNode> anodes;
|
||||
};
|
||||
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
|
||||
806
src/gamedata/StaticGeometryModule.cpp
Normal file
806
src/gamedata/StaticGeometryModule.cpp
Normal file
@@ -0,0 +1,806 @@
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <OgreTerrainGroup.h>
|
||||
#include <OgreFileSystemLayer.h>
|
||||
#include <OgreRTShaderSystem.h>
|
||||
#include <OgreStaticGeometry.h>
|
||||
#include <OgreMeshLodGenerator.h>
|
||||
#include <OgreWorkQueue.h>
|
||||
#include <Procedural.h>
|
||||
#include "Components.h"
|
||||
#include "GameData.h"
|
||||
#include "TerrainModule.h"
|
||||
#include "physics.h"
|
||||
#include "PhysicsModule.h"
|
||||
#include "items.h"
|
||||
#include "StaticGeometryModule.h"
|
||||
|
||||
namespace ECS
|
||||
{
|
||||
|
||||
static bool itemsLoaded = false;
|
||||
static bool furnitureLoaded = false;
|
||||
static bool templatesLoaded = false;
|
||||
static std::list<std::pair<long, long> > addQueue;
|
||||
StaticGeometryModule::StaticGeometryModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.module<StaticGeometryModule>();
|
||||
ecs.component<TerrainSlotParent>();
|
||||
ecs.component<TerrainItem>();
|
||||
ecs.component<FurnitureItem>();
|
||||
ecs.component<FurnitureInstance>()
|
||||
.on_remove([](flecs::entity e, FurnitureInstance &instance) {
|
||||
if (instance.furniture) {
|
||||
instance.furniture
|
||||
->destroyAllChildrenAndObjects();
|
||||
instance.furniture->getCreator()
|
||||
->destroySceneNode(instance.furniture);
|
||||
instance.furniture = nullptr;
|
||||
}
|
||||
})
|
||||
.on_set([](flecs::entity e, FurnitureInstance &instance) {
|
||||
if (instance.furniture !=
|
||||
e.get<FurnitureInstance>().furniture) {
|
||||
FurnitureInstance &f =
|
||||
e.get_mut<FurnitureInstance>();
|
||||
if (f.furniture) {
|
||||
f.furniture
|
||||
->destroyAllChildrenAndObjects();
|
||||
f.furniture->getCreator()
|
||||
->destroySceneNode(f.furniture);
|
||||
}
|
||||
}
|
||||
})
|
||||
.on_add([](flecs::entity e, FurnitureInstance &instance) {
|
||||
instance.furniture = nullptr;
|
||||
});
|
||||
ecs.component<TerrainItemNode>().on_remove([](flecs::entity e,
|
||||
TerrainItemNode &item) {
|
||||
if (item.itemNode) {
|
||||
item.itemNode->destroyAllChildrenAndObjects();
|
||||
item.itemNode->getCreator()->destroySceneNode(
|
||||
item.itemNode);
|
||||
item.itemNode = nullptr;
|
||||
}
|
||||
if (item.geo) {
|
||||
ECS::get<EngineData>().mScnMgr->destroyStaticGeometry(
|
||||
item.geo);
|
||||
item.geo = nullptr;
|
||||
}
|
||||
});
|
||||
ecs.component<TerrainItemMeshNode>();
|
||||
ecs.component<GeometryUpdateItem>();
|
||||
ecs.import <TerrainModule>();
|
||||
ecs.observer<const Terrain>("LoadTerrainItems")
|
||||
.event(flecs::OnSet)
|
||||
.each([&](const Terrain &terrain) {
|
||||
if (terrain.mTerrainGroup && !itemsLoaded) {
|
||||
loadItems();
|
||||
itemsLoaded = true;
|
||||
}
|
||||
});
|
||||
if (!Ogre::MeshLodGenerator::getSingletonPtr())
|
||||
new Ogre::MeshLodGenerator();
|
||||
ecs.system("AddGeometryQueue").kind(flecs::OnUpdate).run([&](flecs::iter &it) {
|
||||
std::list<flecs::entity> items;
|
||||
if (!ECS::get<Terrain>().mTerrainGroup)
|
||||
return;
|
||||
if (!itemsLoaded) {
|
||||
loadItems();
|
||||
itemsLoaded = true;
|
||||
return;
|
||||
}
|
||||
if (!furnitureLoaded) {
|
||||
loadFurniture();
|
||||
furnitureLoaded = true;
|
||||
}
|
||||
if (!templatesLoaded) {
|
||||
loadTemplates();
|
||||
templatesLoaded = true;
|
||||
}
|
||||
std::list<std::pair<long, long> > output;
|
||||
while (!addQueue.empty()) {
|
||||
std::pair<long, long> item = addQueue.front();
|
||||
std::pair<long, long> slot = { item.first,
|
||||
item.second };
|
||||
flecs::entity parent =
|
||||
ECS::get()
|
||||
.query_builder<const TerrainSlotParent>()
|
||||
.build()
|
||||
.find([&slot](const TerrainSlotParent
|
||||
&parent) {
|
||||
return parent.slot == slot;
|
||||
});
|
||||
if (parent.is_valid()) {
|
||||
items.clear();
|
||||
ECS::get()
|
||||
.query_builder<const TerrainItem>()
|
||||
.with(flecs::ChildOf, parent)
|
||||
.without<TerrainItemNode>()
|
||||
.write<TerrainItemNode>()
|
||||
.build()
|
||||
.each([&](flecs::entity e,
|
||||
const TerrainItem &item) {
|
||||
items.push_back(e);
|
||||
});
|
||||
for (auto e : items) {
|
||||
createItemGeometry(e);
|
||||
}
|
||||
addQueue.pop_front();
|
||||
} else {
|
||||
output.push_back(item);
|
||||
addQueue.pop_front();
|
||||
}
|
||||
}
|
||||
OgreAssert(addQueue.empty(), "queue is not empty");
|
||||
if (!output.empty())
|
||||
addQueue = output;
|
||||
});
|
||||
}
|
||||
void StaticGeometryModule::addGeometryForSlot(long x, long y)
|
||||
{
|
||||
addQueue.push_back({ x, y });
|
||||
}
|
||||
void StaticGeometryModule::removeGeometryForSlot(long x, long y)
|
||||
{
|
||||
std::pair<long, long> slot = { x, y };
|
||||
flecs::entity parent =
|
||||
ECS::get().query_builder<const TerrainSlotParent>().build().find(
|
||||
[&slot](const TerrainSlotParent &parent) {
|
||||
return parent.slot == slot;
|
||||
});
|
||||
if (parent.is_valid()) {
|
||||
ECS::get()
|
||||
.query_builder<const TerrainItem>()
|
||||
.with(flecs::ChildOf, parent)
|
||||
.without<TerrainItemNode>()
|
||||
.build()
|
||||
.each([](flecs::entity e, const TerrainItem &item) {
|
||||
OgreAssert(false, "Implement item geo destroy" +
|
||||
item.properties);
|
||||
});
|
||||
}
|
||||
}
|
||||
flecs::entity
|
||||
StaticGeometryModule::createItem(const Ogre::Vector3 &position,
|
||||
const Ogre::Quaternion &orientation,
|
||||
const Ogre::String &type)
|
||||
{
|
||||
long x, y;
|
||||
ECS::get<Terrain>().mTerrainGroup->convertWorldPositionToTerrainSlot(
|
||||
position, &x, &y);
|
||||
std::pair<long, long> pos{ x, y };
|
||||
flecs::entity slot =
|
||||
ECS::get().query_builder<const TerrainSlotParent>().build().find(
|
||||
[&](const TerrainSlotParent &slot) -> bool {
|
||||
return slot.slot == pos;
|
||||
});
|
||||
if (!slot.is_valid())
|
||||
slot = ECS::get().entity().set<TerrainSlotParent>({ pos });
|
||||
flecs::entity item = ECS::get().entity().child_of(slot);
|
||||
nlohmann::json jproperties;
|
||||
jproperties["type"] = type;
|
||||
item.set<TerrainItem>({ position, orientation, jproperties.dump() });
|
||||
std::cout << "createItem: " << x << " " << y << " " << item.id()
|
||||
<< std::endl;
|
||||
return item;
|
||||
}
|
||||
void StaticGeometryModule::setItemProperties(flecs::entity id,
|
||||
Ogre::String properties)
|
||||
{
|
||||
OgreAssert(id.is_valid(), "bad id");
|
||||
id.get_mut<TerrainItem>().properties = properties;
|
||||
id.modified<TerrainItem>();
|
||||
}
|
||||
const Ogre::String &StaticGeometryModule::getItemProperties(flecs::entity id)
|
||||
{
|
||||
OgreAssert(id.is_valid(), "bad id");
|
||||
return id.get<TerrainItem>().properties;
|
||||
}
|
||||
|
||||
nlohmann::json templates;
|
||||
void StaticGeometryModule::loadTemplates()
|
||||
{
|
||||
if (!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(
|
||||
"templates.list"))
|
||||
return;
|
||||
Ogre::String group =
|
||||
Ogre::ResourceGroupManager::getSingleton()
|
||||
.findGroupContainingResource("templates.list");
|
||||
Ogre::DataStreamPtr stream =
|
||||
Ogre::ResourceGroupManager::getSingleton().openResource(
|
||||
"templates.list", group);
|
||||
Ogre::String json = stream->getAsString();
|
||||
nlohmann::json jtemplates = nlohmann::json::parse(json);
|
||||
templates = jtemplates;
|
||||
}
|
||||
|
||||
void StaticGeometryModule::saveTemplates()
|
||||
{
|
||||
Ogre::String path = "resources/buildings/templates.list";
|
||||
if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(
|
||||
"templates.list")) {
|
||||
Ogre::String group =
|
||||
Ogre::ResourceGroupManager::getSingleton()
|
||||
.findGroupContainingResource("templates.list");
|
||||
Ogre::FileInfoListPtr fileInfoList(
|
||||
Ogre::ResourceGroupManager::getSingleton()
|
||||
.findResourceFileInfo(group, "templates.list"));
|
||||
OgreAssert(fileInfoList->size() == 1,
|
||||
"templates.list should be there and only once");
|
||||
path = fileInfoList->at(0).archive->getName() + "/" +
|
||||
"templates.list";
|
||||
Ogre::FileSystemLayer::removeFile(path);
|
||||
}
|
||||
std::fstream fout(path.c_str(), std::ios::out);
|
||||
fout << templates.dump();
|
||||
fout.close();
|
||||
}
|
||||
|
||||
void StaticGeometryModule::saveItems()
|
||||
{
|
||||
Ogre::String path = "resources/buildings/items.list";
|
||||
if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(
|
||||
"items.list")) {
|
||||
Ogre::String group =
|
||||
Ogre::ResourceGroupManager::getSingleton()
|
||||
.findGroupContainingResource("items.list");
|
||||
Ogre::FileInfoListPtr fileInfoList(
|
||||
Ogre::ResourceGroupManager::getSingleton()
|
||||
.findResourceFileInfo(group, "items.list"));
|
||||
OgreAssert(fileInfoList->size() == 1,
|
||||
"worpd_map.png should be there and only once");
|
||||
path = fileInfoList->at(0).archive->getName() + "/" +
|
||||
"items.list";
|
||||
Ogre::FileSystemLayer::removeFile(path);
|
||||
}
|
||||
std::fstream fout(path.c_str(), std::ios::out);
|
||||
nlohmann::json jitemlist;
|
||||
ECS::get().query_builder<const TerrainItem>().build().each(
|
||||
[&](flecs::entity e, const TerrainItem &item) {
|
||||
nlohmann::json jitem;
|
||||
to_json(jitem["position"], item.position);
|
||||
to_json(jitem["orientation"], item.orientation);
|
||||
to_json(jitem["properties"], item.properties);
|
||||
jitem["objects"] = nlohmann::json::array();
|
||||
ECS::get()
|
||||
.query_builder<const TerrainItem>()
|
||||
.with(flecs::ChildOf, e)
|
||||
.build()
|
||||
.each([&](flecs::entity obje,
|
||||
const TerrainItem &objItem) {
|
||||
nlohmann::json jobjitem;
|
||||
to_json(jobjitem["position"],
|
||||
objItem.position);
|
||||
to_json(jobjitem["orientation"],
|
||||
objItem.orientation);
|
||||
to_json(jobjitem["properties"],
|
||||
objItem.properties);
|
||||
jitem["objects"].push_back(jobjitem);
|
||||
});
|
||||
jitemlist.push_back(jitem);
|
||||
});
|
||||
fout << jitemlist.dump();
|
||||
fout.close();
|
||||
}
|
||||
void StaticGeometryModule::loadItems()
|
||||
{
|
||||
if (!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(
|
||||
"items.list"))
|
||||
return;
|
||||
Ogre::String group = Ogre::ResourceGroupManager::getSingleton()
|
||||
.findGroupContainingResource("items.list");
|
||||
Ogre::DataStreamPtr stream =
|
||||
Ogre::ResourceGroupManager::getSingleton().openResource(
|
||||
"items.list", group);
|
||||
Ogre::String json = stream->getAsString();
|
||||
nlohmann::json jlist = nlohmann::json::parse(json);
|
||||
ECS::get().delete_with<TerrainItem>();
|
||||
ECS::get().delete_with<TerrainSlotParent>();
|
||||
|
||||
for (const auto &v : jlist) {
|
||||
Ogre::Vector3 position;
|
||||
Ogre::Quaternion orientation;
|
||||
Ogre::String properties;
|
||||
from_json(v["position"], position);
|
||||
from_json(v["orientation"], orientation);
|
||||
properties = v["properties"].get<Ogre::String>();
|
||||
std::cout << v.dump(4) << std::endl;
|
||||
long x, y;
|
||||
ECS::get<Terrain>()
|
||||
.mTerrainGroup->convertWorldPositionToTerrainSlot(
|
||||
position, &x, &y);
|
||||
std::pair<long, long> pos{ x, y };
|
||||
flecs::entity slot =
|
||||
ECS::get()
|
||||
.query_builder<const TerrainSlotParent>()
|
||||
.build()
|
||||
.find([&](const TerrainSlotParent &slot)
|
||||
-> bool {
|
||||
return slot.slot == pos;
|
||||
});
|
||||
if (!slot.is_valid())
|
||||
slot = ECS::get().entity().set<TerrainSlotParent>(
|
||||
{ pos });
|
||||
OgreAssert(slot.is_valid(), "Failed to create slot");
|
||||
flecs::entity item =
|
||||
ECS::get().entity().child_of(slot).set<TerrainItem>(
|
||||
{ position, orientation, properties });
|
||||
if (v.find("objects") != v.end()) {
|
||||
for (auto &obj : v["objects"]) {
|
||||
Ogre::Vector3 objposition;
|
||||
Ogre::Quaternion objorientation;
|
||||
Ogre::String objproperties;
|
||||
from_json(obj["position"], objposition);
|
||||
from_json(obj["orientation"], objorientation);
|
||||
objproperties =
|
||||
obj["properties"].get<Ogre::String>();
|
||||
flecs::entity itemObj =
|
||||
ECS::get()
|
||||
.entity()
|
||||
.child_of(item)
|
||||
.set<TerrainItem>(
|
||||
{ objposition,
|
||||
objorientation,
|
||||
objproperties });
|
||||
}
|
||||
}
|
||||
std::cout << "createItem: " << x << " " << y << " " << item.id()
|
||||
<< std::endl;
|
||||
std::cout << "position: " << item.id() << " " << position
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void StaticGeometryModule::saveFurniture()
|
||||
{
|
||||
/* No saving - furniture is generated by blender */
|
||||
}
|
||||
|
||||
void StaticGeometryModule::loadFurniture()
|
||||
{
|
||||
ECS::get().delete_with<FurnitureItem>();
|
||||
static std::vector<Ogre::String> glb_names;
|
||||
const std::vector<Ogre::String> &groups =
|
||||
Ogre::ResourceGroupManager::getSingleton().getResourceGroups();
|
||||
if (glb_names.size() == 0) {
|
||||
int i;
|
||||
for (i = 0; i < groups.size(); i++) {
|
||||
std::vector<Ogre::String> names =
|
||||
*Ogre::ResourceGroupManager::getSingleton()
|
||||
.findResourceNames(
|
||||
groups[i],
|
||||
"furniture-*.glb.json");
|
||||
glb_names.insert(glb_names.end(), names.begin(),
|
||||
names.end());
|
||||
}
|
||||
}
|
||||
for (auto &g : glb_names) {
|
||||
Ogre::String group = Ogre::ResourceGroupManager::getSingleton()
|
||||
.findGroupContainingResource(g);
|
||||
Ogre::DataStreamPtr stream =
|
||||
Ogre::ResourceGroupManager::getSingleton().openResource(
|
||||
g, group);
|
||||
Ogre::String json = stream->getAsString();
|
||||
nlohmann::json jdata = nlohmann::json::parse(json);
|
||||
std::vector<Ogre::String> tags;
|
||||
for (auto &tag : jdata["tags"]) {
|
||||
Ogre::String stag = tag.get<Ogre::String>();
|
||||
tags.push_back(stag);
|
||||
}
|
||||
Ogre::String meshName = jdata["mesh"].get<Ogre::String>();
|
||||
Ogre::MeshPtr mesh =
|
||||
Ogre::MeshManager::getSingleton().getByName(meshName);
|
||||
if (mesh) {
|
||||
Ogre::LodConfig meshconf(mesh);
|
||||
Geometry::setupLods(meshconf);
|
||||
}
|
||||
ECS::get().entity().set<FurnitureItem>({ json, tags });
|
||||
std::cout << "path: " << g << std::endl;
|
||||
}
|
||||
}
|
||||
void StaticGeometryModule::getItemPositionPerSlot(
|
||||
long x, long y, std::list<Ogre::Vector3> *positions)
|
||||
{
|
||||
std::pair<long, long> pos{ x, y };
|
||||
if (!positions)
|
||||
return;
|
||||
flecs::entity slot =
|
||||
ECS::get().query_builder<const TerrainSlotParent>().build().find(
|
||||
[&](const TerrainSlotParent &slot) -> bool {
|
||||
return slot.slot == pos;
|
||||
});
|
||||
if (!slot.is_valid())
|
||||
return;
|
||||
ECS::get()
|
||||
.query_builder<const TerrainItem>()
|
||||
.with(flecs::ChildOf, slot)
|
||||
.build()
|
||||
.each([&](flecs::entity e, const TerrainItem &item) {
|
||||
positions->push_back(item.position);
|
||||
});
|
||||
}
|
||||
void StaticGeometryModule::getItemPositions(std::list<Ogre::Vector3> *positions)
|
||||
{
|
||||
ECS::get().query_builder<const TerrainItem>().build().each(
|
||||
[&](flecs::entity e, const TerrainItem &item) {
|
||||
positions->push_back(item.position);
|
||||
});
|
||||
}
|
||||
void StaticGeometryModule::getItemPositionAndRotation(
|
||||
flecs::entity e, Ogre::Vector3 &position, Ogre::Quaternion &orientation)
|
||||
{
|
||||
position = e.get<TerrainItem>().position;
|
||||
orientation = e.get<TerrainItem>().orientation;
|
||||
}
|
||||
void StaticGeometryModule::getItemsProperties(
|
||||
std::list<std::pair<flecs::entity, Ogre::String> > *items)
|
||||
{
|
||||
ECS::get().query_builder<const TerrainItem>().build().each(
|
||||
[&](flecs::entity e, const TerrainItem &item) {
|
||||
items->push_back({ e, item.properties });
|
||||
});
|
||||
}
|
||||
void StaticGeometryModule::createItemGeometry(flecs::entity e)
|
||||
{
|
||||
Geometry::createItemGeometry(e);
|
||||
}
|
||||
|
||||
void StaticGeometryModule::destroyItemGeometry(flecs::entity e)
|
||||
{
|
||||
Geometry::destroyItemGeometry(e);
|
||||
}
|
||||
|
||||
nlohmann::json &StaticGeometryModule::getTemplates()
|
||||
{
|
||||
return templates;
|
||||
}
|
||||
|
||||
void StaticGeometryModule::updateItemGeometry(flecs::entity e)
|
||||
{
|
||||
if (e.has<GeometryUpdateItem>())
|
||||
return;
|
||||
e.add<GeometryUpdateItem>();
|
||||
Ogre::Root::getSingleton().getWorkQueue()->addTask([e]() {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask(
|
||||
[e]() {
|
||||
Geometry::updateItemGeometry(e);
|
||||
e.remove<GeometryUpdateItem>();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void StaticGeometryModule::addTriangleBufferWork(
|
||||
const Ogre::String &meshName, Ogre::StaticGeometry *geo,
|
||||
const Ogre::Vector3 &position, const Ogre::Quaternion &rotation,
|
||||
const Procedural::TriangleBuffer &tb)
|
||||
{
|
||||
struct WorkData {
|
||||
Ogre::String meshName;
|
||||
Ogre::StaticGeometry *geo;
|
||||
Ogre::Vector3 position;
|
||||
Ogre::Quaternion rotation;
|
||||
Procedural::TriangleBuffer tb;
|
||||
};
|
||||
WorkData data = { meshName, geo, position, rotation, tb };
|
||||
|
||||
Ogre::Root::getSingleton().getWorkQueue()->addTask([captData = std::move(
|
||||
data)]() {
|
||||
Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask(
|
||||
[captData]() {
|
||||
Ogre::MeshPtr mesh =
|
||||
captData.tb.transformToMesh(
|
||||
captData.meshName);
|
||||
Ogre::Entity *ent =
|
||||
ECS::get<EngineData>()
|
||||
.mScnMgr->createEntity(mesh);
|
||||
captData.geo->addEntity(ent, captData.position,
|
||||
captData.rotation);
|
||||
ECS::get<EngineData>().mScnMgr->destroyEntity(
|
||||
ent);
|
||||
});
|
||||
});
|
||||
}
|
||||
struct TiledMeshes {
|
||||
struct Tile {
|
||||
Ogre::String materialName;
|
||||
std::shared_ptr<Ogre::VertexData> vertexData;
|
||||
std::shared_ptr<Ogre::IndexData> indexData;
|
||||
std::set<uint32_t> positions;
|
||||
};
|
||||
std::map<Ogre::String, Tile> tiles;
|
||||
uint32_t packKey(const Ogre::Vector3i &position)
|
||||
{
|
||||
uint32_t key = 0;
|
||||
key |= (uint32_t)(position[2] + 512) << 20;
|
||||
key |= (uint32_t)(position[1] + 512) << 10;
|
||||
key |= (uint32_t)(position[0] + 512) << 0;
|
||||
return key;
|
||||
}
|
||||
void unpackKey(uint32_t key, Ogre::Vector3i &position)
|
||||
{
|
||||
uint32_t mask = 0x3ff;
|
||||
position[0] = (int)(key & mask) - 512;
|
||||
position[1] = (int)((key >> 10) & mask) - 512;
|
||||
position[2] = (int)((key >> 20) & mask) - 512;
|
||||
}
|
||||
void setTile(const Ogre::String &name, const Ogre::Vector3i &position)
|
||||
{
|
||||
if (tiles.find(name) == tiles.end())
|
||||
return;
|
||||
tiles[name].positions.insert(packKey(position));
|
||||
}
|
||||
void clearTile(const Ogre::String &name, const Ogre::Vector3i &position)
|
||||
{
|
||||
if (tiles.find(name) == tiles.end())
|
||||
return;
|
||||
tiles[name].positions.erase(packKey(position));
|
||||
}
|
||||
void addTile(const Ogre::String &name, Ogre::MeshPtr mesh)
|
||||
{
|
||||
if (mesh->getSubMeshes().size() != 1)
|
||||
return;
|
||||
Ogre::SubMesh *submesh = mesh->getSubMesh(0);
|
||||
Ogre::VertexData *vertexData;
|
||||
if (submesh->useSharedVertices)
|
||||
vertexData = mesh->sharedVertexData->clone();
|
||||
else
|
||||
vertexData = submesh->vertexData->clone();
|
||||
tiles[name] = { submesh->getMaterialName(),
|
||||
std::shared_ptr<Ogre::VertexData>(vertexData),
|
||||
std::shared_ptr<Ogre::IndexData>(
|
||||
submesh->indexData->clone()),
|
||||
{} };
|
||||
}
|
||||
struct buildSettings {
|
||||
Ogre::String meshName;
|
||||
Ogre::String materialName;
|
||||
Ogre::MeshPtr mesh;
|
||||
Ogre::SubMesh *sm;
|
||||
int vertexCount, vertexOffset;
|
||||
int indexCount, indexOffset;
|
||||
Ogre::VertexDeclaration *vdecl;
|
||||
Ogre::HardwareVertexBufferSharedPtr vbuf;
|
||||
Ogre::AxisAlignedBox bounds;
|
||||
bool setBounds;
|
||||
};
|
||||
void configureSettings(struct buildSettings &settings)
|
||||
{
|
||||
settings.mesh = Ogre::MeshManager::getSingleton().createManual(
|
||||
settings.meshName,
|
||||
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
|
||||
settings.mesh->createVertexData();
|
||||
settings.sm = settings.mesh->createSubMesh();
|
||||
settings.vertexCount = 0;
|
||||
settings.indexCount = 0;
|
||||
settings.vdecl = nullptr;
|
||||
for (const auto &tile : tiles) {
|
||||
settings.vertexCount +=
|
||||
tile.second.vertexData->vertexCount *
|
||||
tile.second.positions.size();
|
||||
settings.indexCount +=
|
||||
tile.second.indexData->indexCount *
|
||||
tile.second.positions.size();
|
||||
if (!settings.vdecl) {
|
||||
settings.vdecl =
|
||||
tile.second.vertexData
|
||||
->vertexDeclaration->clone();
|
||||
settings.materialName =
|
||||
tile.second.materialName;
|
||||
}
|
||||
}
|
||||
settings.mesh->sharedVertexData->vertexStart = 0;
|
||||
settings.mesh->sharedVertexData->vertexCount =
|
||||
settings.vertexCount;
|
||||
settings.mesh->sharedVertexData->vertexDeclaration =
|
||||
settings.vdecl;
|
||||
settings.vbuf =
|
||||
Ogre::HardwareBufferManager::getSingleton()
|
||||
.createVertexBuffer(
|
||||
settings.vdecl->getVertexSize(0),
|
||||
settings.vertexCount,
|
||||
Ogre::HBU_GPU_ONLY);
|
||||
settings.mesh->sharedVertexData->vertexBufferBinding->setBinding(
|
||||
0, settings.vbuf);
|
||||
settings.sm->indexData->indexStart = 0;
|
||||
settings.sm->indexData->indexCount = settings.indexCount;
|
||||
settings.sm->indexData->indexBuffer =
|
||||
Ogre::HardwareBufferManager::getSingleton()
|
||||
.createIndexBuffer(
|
||||
Ogre::HardwareIndexBuffer::IT_32BIT,
|
||||
settings.sm->indexData->indexCount * 8,
|
||||
Ogre::HBU_GPU_ONLY);
|
||||
settings.sm->setMaterialName(settings.materialName);
|
||||
settings.setBounds = true;
|
||||
}
|
||||
void processIndex(struct buildSettings &settings,
|
||||
const struct Tile &tile, unsigned int *dstIndices)
|
||||
{
|
||||
int j;
|
||||
std::shared_ptr<Ogre::IndexData> srcIndexData = tile.indexData;
|
||||
int srcIndexCount = srcIndexData->indexCount;
|
||||
std::shared_ptr<Ogre::VertexData> srcVertexData =
|
||||
tile.vertexData;
|
||||
int srcVertexCount = srcVertexData->vertexCount;
|
||||
Ogre::HardwareIndexBufferSharedPtr srcIbuf =
|
||||
srcIndexData->indexBuffer;
|
||||
Ogre::HardwareBufferLockGuard srcIndexLock(
|
||||
srcIbuf, Ogre::HardwareBuffer::HBL_READ_ONLY);
|
||||
if (srcIndexData->indexBuffer->getType() ==
|
||||
Ogre::HardwareIndexBuffer::IT_32BIT) {
|
||||
unsigned int *indices =
|
||||
static_cast<unsigned int *>(srcIndexLock.pData);
|
||||
for (j = 0; j < srcIndexCount; j++)
|
||||
dstIndices[settings.indexOffset + j] =
|
||||
indices[j] + settings.vertexOffset;
|
||||
} else if (srcIndexData->indexBuffer->getType() ==
|
||||
Ogre::HardwareIndexBuffer::IT_16BIT) {
|
||||
unsigned short *indices = static_cast<unsigned short *>(
|
||||
srcIndexLock.pData);
|
||||
for (j = 0; j < srcIndexCount; j++)
|
||||
dstIndices[settings.indexOffset + j] =
|
||||
indices[j] + settings.vertexOffset;
|
||||
}
|
||||
}
|
||||
void processSingleVertex(
|
||||
struct buildSettings &settings,
|
||||
const Ogre::VertexDeclaration::VertexElementList &srcElements,
|
||||
uint32_t offset, unsigned char *srcData, unsigned char *dstData)
|
||||
{
|
||||
for (const auto &srcElement : srcElements) {
|
||||
unsigned char *srcPtr, *dstPtr;
|
||||
const Ogre::VertexElement *destElement =
|
||||
settings.vdecl->findElementBySemantic(
|
||||
srcElement.getSemantic(),
|
||||
srcElement.getIndex());
|
||||
if (!destElement)
|
||||
goto out;
|
||||
if (srcElement.getType() != destElement->getType() ||
|
||||
srcElement.getSize() != destElement->getSize())
|
||||
goto out;
|
||||
srcPtr = srcData + srcElement.getOffset();
|
||||
dstPtr = dstData + destElement->getOffset();
|
||||
if (destElement->getSemantic() == Ogre::VES_POSITION) {
|
||||
float *srcPositionData =
|
||||
reinterpret_cast<float *>(srcPtr);
|
||||
float *dstPositionData =
|
||||
reinterpret_cast<float *>(dstPtr);
|
||||
Ogre::Vector3 position(srcPositionData[0],
|
||||
srcPositionData[1],
|
||||
srcPositionData[2]);
|
||||
Ogre::Vector3i offsetv;
|
||||
unpackKey(offset, offsetv);
|
||||
position.x += (float)offsetv[0];
|
||||
position.y += (float)offsetv[1];
|
||||
position.z += (float)offsetv[2];
|
||||
dstPositionData[0] = position.x;
|
||||
dstPositionData[1] = position.y;
|
||||
dstPositionData[2] = position.z;
|
||||
if (settings.setBounds) {
|
||||
settings.bounds.setMinimum(position);
|
||||
settings.bounds.setMaximum(position);
|
||||
settings.setBounds = false;
|
||||
} else
|
||||
settings.bounds.merge(position);
|
||||
} else if (destElement->getSemantic() ==
|
||||
Ogre::VES_NORMAL) {
|
||||
float *srcNormalData =
|
||||
reinterpret_cast<float *>(srcPtr);
|
||||
float *dstNormalData =
|
||||
reinterpret_cast<float *>(dstPtr);
|
||||
Ogre::Vector3 normal(srcNormalData[0],
|
||||
srcNormalData[1],
|
||||
srcNormalData[2]);
|
||||
dstNormalData[0] = normal.x;
|
||||
dstNormalData[1] = normal.y;
|
||||
dstNormalData[2] = normal.z;
|
||||
} else
|
||||
memcpy(dstPtr, srcPtr, srcElement.getSize());
|
||||
out:;
|
||||
}
|
||||
}
|
||||
void processTile(struct buildSettings &settings,
|
||||
const struct Tile &tile, uint32_t position,
|
||||
unsigned char *dstpData)
|
||||
{
|
||||
std::shared_ptr<Ogre::VertexData> srcVertexData =
|
||||
tile.vertexData;
|
||||
const Ogre::VertexDeclaration *srcDecl =
|
||||
srcVertexData->vertexDeclaration;
|
||||
const Ogre::VertexBufferBinding *srcBind =
|
||||
srcVertexData->vertexBufferBinding;
|
||||
int srcVertexCount = srcVertexData->vertexCount;
|
||||
Ogre::HardwareVertexBufferSharedPtr srcVbuf =
|
||||
srcBind->getBuffer(0);
|
||||
std::shared_ptr<Ogre::IndexData> srcIndexData = tile.indexData;
|
||||
int srcIndexCount = srcIndexData->indexCount;
|
||||
|
||||
Ogre::HardwareBufferLockGuard srcVertexLock(
|
||||
srcVbuf, 0, srcVertexCount * srcDecl->getVertexSize(0),
|
||||
Ogre::HardwareBuffer::HBL_READ_ONLY);
|
||||
const Ogre::VertexDeclaration::VertexElementList &srcElements =
|
||||
srcDecl->getElements();
|
||||
int j;
|
||||
unsigned char *srcpData =
|
||||
static_cast<unsigned char *>(srcVertexLock.pData);
|
||||
|
||||
for (j = 0; j < srcVertexCount; j++) {
|
||||
unsigned char *srcData =
|
||||
srcpData + j * srcVbuf->getVertexSize();
|
||||
unsigned char *dstData =
|
||||
dstpData + (settings.vertexOffset +
|
||||
j) * settings.vbuf->getVertexSize();
|
||||
processSingleVertex(settings, srcElements, position,
|
||||
srcData, dstData);
|
||||
}
|
||||
}
|
||||
Ogre::MeshPtr build(const Ogre::String &meshName)
|
||||
{
|
||||
buildSettings settings;
|
||||
settings.meshName = meshName;
|
||||
configureSettings(settings);
|
||||
{
|
||||
Ogre::HardwareBufferLockGuard vertexLock(
|
||||
settings.vbuf, 0,
|
||||
settings.vertexCount *
|
||||
settings.vdecl->getVertexSize(0),
|
||||
Ogre::HardwareBuffer::HBL_NO_OVERWRITE);
|
||||
Ogre::HardwareBufferLockGuard indexLock(
|
||||
settings.sm->indexData->indexBuffer,
|
||||
Ogre::HardwareBuffer::HBL_NO_OVERWRITE);
|
||||
settings.vertexOffset = 0;
|
||||
settings.indexOffset = 0;
|
||||
unsigned char *dstpData =
|
||||
static_cast<unsigned char *>(vertexLock.pData);
|
||||
unsigned int *dstIndices =
|
||||
static_cast<unsigned int *>(indexLock.pData);
|
||||
|
||||
for (const auto &tile : tiles) {
|
||||
std::shared_ptr<Ogre::IndexData> srcIndexData =
|
||||
tile.second.indexData;
|
||||
int srcIndexCount = srcIndexData->indexCount;
|
||||
std::shared_ptr<Ogre::VertexData> srcVertexData =
|
||||
tile.second.vertexData;
|
||||
int srcVertexCount = srcVertexData->vertexCount;
|
||||
for (const auto &position :
|
||||
tile.second.positions) {
|
||||
processTile(settings, tile.second,
|
||||
position, dstpData);
|
||||
processIndex(settings, tile.second,
|
||||
dstIndices);
|
||||
settings.vertexOffset += srcVertexCount;
|
||||
settings.indexOffset += srcIndexCount;
|
||||
Ogre::Vector3i vposition;
|
||||
unpackKey(position, vposition);
|
||||
std::cout << "position: " << position
|
||||
<< " " << vposition
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
settings.mesh->_setBounds(settings.bounds);
|
||||
}
|
||||
Ogre::LodConfig config(settings.mesh);
|
||||
// config.advanced.useCompression = false;
|
||||
// config.advanced.useVertexNormals = true;
|
||||
config.advanced.preventPunchingHoles = true;
|
||||
config.advanced.preventBreakingLines = true;
|
||||
config.createGeneratedLodLevel(2, 0.15f);
|
||||
config.createGeneratedLodLevel(20, 0.49f);
|
||||
config.createGeneratedLodLevel(15, 0.49f);
|
||||
config.createGeneratedLodLevel(150, 0.75f);
|
||||
config.advanced.useBackgroundQueue = false;
|
||||
Ogre::MeshLodGenerator::getSingleton().generateLodLevels(
|
||||
config);
|
||||
return settings.mesh;
|
||||
}
|
||||
void removeTile(const Ogre::String &name)
|
||||
{
|
||||
tiles.erase(name);
|
||||
}
|
||||
TiledMeshes()
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
78
src/gamedata/StaticGeometryModule.h
Normal file
78
src/gamedata/StaticGeometryModule.h
Normal file
@@ -0,0 +1,78 @@
|
||||
#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 {
|
||||
std::pair<long, long> slot;
|
||||
};
|
||||
struct TerrainItem {
|
||||
Ogre::Vector3 position;
|
||||
Ogre::Quaternion orientation;
|
||||
Ogre::String properties;
|
||||
};
|
||||
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);
|
||||
static void addGeometryForSlot(long x, long y);
|
||||
static void removeGeometryForSlot(long x, long y);
|
||||
static flecs::entity createItem(const Ogre::Vector3 &position,
|
||||
const Ogre::Quaternion &orientation,
|
||||
const Ogre::String &type);
|
||||
static void setItemProperties(flecs::entity id,
|
||||
Ogre::String properties);
|
||||
static const Ogre::String &getItemProperties(flecs::entity id);
|
||||
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);
|
||||
static void getItemPositionAndRotation(flecs::entity e,
|
||||
Ogre::Vector3 &position,
|
||||
Ogre::Quaternion &orientation);
|
||||
static void getItemsProperties(
|
||||
std::list<std::pair<flecs::entity, Ogre::String> > *items);
|
||||
static void createItemGeometry(flecs::entity e);
|
||||
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
|
||||
@@ -16,10 +16,11 @@
|
||||
#include "CharacterModule.h"
|
||||
#include "SunModule.h"
|
||||
#include "PhysicsModule.h"
|
||||
#include "StaticGeometryModule.h"
|
||||
#include "TerrainModule.h"
|
||||
|
||||
#define TERRAIN_SIZE 129
|
||||
#define TERRAIN_WORLD_SIZE 1000.0f
|
||||
#define TERRAIN_SIZE 65
|
||||
#define TERRAIN_WORLD_SIZE 500.0f
|
||||
#define ENDLESS_TERRAIN_FILE_PREFIX Ogre::String("EndlessWorldTerrain")
|
||||
#define ENDLESS_TERRAIN_FILE_SUFFIX Ogre::String("dat")
|
||||
|
||||
@@ -32,6 +33,12 @@
|
||||
#define ENDLESS_PAGE_MAX_Y 0x7FFF
|
||||
namespace ECS
|
||||
{
|
||||
class DummyPageProvider;
|
||||
/* Components */
|
||||
struct TerrainPrivate {
|
||||
DummyPageProvider *mDummyPageProvider;
|
||||
Ogre::Timer mSunUpdate;
|
||||
};
|
||||
|
||||
#define BRUSH_SIZE 64
|
||||
struct HeightData {
|
||||
@@ -83,15 +90,17 @@ struct HeightData {
|
||||
}
|
||||
int get_img_x(float world_x)
|
||||
{
|
||||
float world_img_x = world_x + img.getWidth() * BRUSH_SIZE / 2;
|
||||
int ret = world_img_x / BRUSH_SIZE;
|
||||
float world_img_x = world_x + (float)img.getWidth() *
|
||||
(float)BRUSH_SIZE / 2.0f;
|
||||
int ret = (world_img_x + BRUSH_SIZE - 1) / BRUSH_SIZE;
|
||||
// ret = Ogre::Math::Clamp(ret, 0, (int)img.getWidth() - 1);
|
||||
return ret;
|
||||
}
|
||||
int get_img_y(float world_z)
|
||||
{
|
||||
float world_img_y = world_z + img.getHeight() * BRUSH_SIZE / 2;
|
||||
int ret = world_img_y / BRUSH_SIZE;
|
||||
float world_img_y = world_z + (float)img.getHeight() *
|
||||
(float)BRUSH_SIZE / 2.0f;
|
||||
int ret = (world_img_y + BRUSH_SIZE - 1) / BRUSH_SIZE;
|
||||
// ret = Ogre::Math::Clamp(ret, 0, (int)img.getHeight() - 1);
|
||||
return ret;
|
||||
}
|
||||
@@ -189,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");
|
||||
@@ -234,6 +245,7 @@ class FlatTerrainDefiner
|
||||
long y;
|
||||
};
|
||||
std::deque<struct gen_collider> collider_queue;
|
||||
std::deque<struct gen_collider> colliderRemove_queue;
|
||||
|
||||
public:
|
||||
FlatTerrainDefiner(Ogre::SceneManager *
|
||||
@@ -247,6 +259,8 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex mtx;
|
||||
|
||||
public:
|
||||
void createTerrainChunk(Ogre::TerrainGroup *terrainGroup, long x,
|
||||
long y)
|
||||
@@ -264,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 =
|
||||
@@ -276,6 +290,7 @@ public:
|
||||
}
|
||||
void define(Ogre::TerrainGroup *terrainGroup, long x, long y) override
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(mtx);
|
||||
uint16_t terrainSize = terrainGroup->getTerrainSize();
|
||||
float *heightMap = OGRE_ALLOC_T(float, terrainSize *terrainSize,
|
||||
MEMCATEGORY_GEOMETRY);
|
||||
@@ -295,19 +310,9 @@ public:
|
||||
long world_y = (long)(worldPos.z + i -
|
||||
(terrainSize - 1) / 2);
|
||||
float height = 0.0f;
|
||||
int k, l;
|
||||
for (l = -1; l < 2; l++)
|
||||
for (k = -1; k < 2; k++) {
|
||||
height +=
|
||||
HeightData::get_singleton()
|
||||
->get_height(
|
||||
terrainGroup,
|
||||
world_x +
|
||||
4 * k,
|
||||
world_y +
|
||||
4 * l);
|
||||
}
|
||||
height /= 9.0f;
|
||||
height +=
|
||||
HeightData::get_singleton()->get_height(
|
||||
terrainGroup, world_x, world_y);
|
||||
|
||||
// height = -2.0f;
|
||||
heightMap[i * terrainSize + j] = height;
|
||||
@@ -333,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()) {
|
||||
@@ -352,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();
|
||||
{
|
||||
@@ -400,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();
|
||||
@@ -421,17 +439,24 @@ public:
|
||||
what->setOrientation(item.rotation);
|
||||
what->setPosition(item.position);
|
||||
}
|
||||
} else {
|
||||
output.push_back(collider_queue.front());
|
||||
#endif
|
||||
/* Spawn items */
|
||||
StaticGeometryModule::addGeometryForSlot(x, y);
|
||||
collider_queue.pop_front();
|
||||
}
|
||||
if (collider_queue.empty() &&
|
||||
!ECS::get<Terrain>().mTerrainReady) {
|
||||
ECS::get_mut<Terrain>().mTerrainReady = true;
|
||||
ECS::modified<Terrain>();
|
||||
|
||||
} else {
|
||||
/* Terrain data not ready maybe move to next terrain */
|
||||
gen_collider m = collider_queue.front();
|
||||
collider_queue.pop_front();
|
||||
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 {
|
||||
@@ -453,6 +478,10 @@ public:
|
||||
bool unloadProceduralPage(Ogre::Page *page,
|
||||
Ogre::PagedWorldSection *section)
|
||||
{
|
||||
long x, y;
|
||||
ECS::get<Terrain>().mTerrainGroup->unpackIndex(page->CHUNK_ID,
|
||||
&x, &y);
|
||||
StaticGeometryModule::removeGeometryForSlot(x, y);
|
||||
return true;
|
||||
}
|
||||
bool unprepareProceduralPage(Ogre::Page *page,
|
||||
@@ -461,20 +490,6 @@ public:
|
||||
return true;
|
||||
}
|
||||
};
|
||||
struct TerrainPrivate {
|
||||
DummyPageProvider *mDummyPageProvider;
|
||||
Ogre::Timer mSunUpdate;
|
||||
};
|
||||
|
||||
struct TerrainSlotParent {
|
||||
std::pair<long, long> slot;
|
||||
};
|
||||
struct TerrainItem {
|
||||
Ogre::Vector3 position;
|
||||
Ogre::Quaternion orientation;
|
||||
Ogre::String properties;
|
||||
};
|
||||
|
||||
TerrainModule::TerrainModule(flecs::world &ecs)
|
||||
{
|
||||
struct CanSetPlayerPosition {};
|
||||
@@ -482,12 +497,11 @@ TerrainModule::TerrainModule(flecs::world &ecs)
|
||||
ecs.component<CanSetPlayerPosition>().add(flecs::Singleton);
|
||||
ecs.component<Terrain>().add(flecs::Singleton);
|
||||
ecs.component<TerrainPrivate>().add(flecs::Singleton);
|
||||
ecs.component<TerrainSlotParent>();
|
||||
ecs.component<TerrainItem>();
|
||||
ecs.component<PlacementObjects>();
|
||||
ecs.component<TerrainReady>().add(flecs::Singleton);
|
||||
ecs.import <CharacterModule>();
|
||||
ecs.import <SunModule>();
|
||||
ecs.import <StaticGeometryModule>();
|
||||
ecs.set<TerrainPrivate>({ nullptr, {} });
|
||||
ecs.system<const EngineData, const Camera, const Sun, Terrain,
|
||||
TerrainPrivate>("SetupUpdateTerrain")
|
||||
@@ -523,11 +537,11 @@ TerrainModule::TerrainModule(flecs::world &ecs)
|
||||
terrain.mTerrainGroup->setOrigin(
|
||||
terrain.mTerrainPos);
|
||||
// Configure global
|
||||
terrain.mTerrainGlobals->setMaxPixelError(0);
|
||||
terrain.mTerrainGlobals->setMaxPixelError(1);
|
||||
// testing composite map
|
||||
// mTerrainGlobals->setCompositeMapDistance(30);
|
||||
terrain.mTerrainGlobals->setCompositeMapDistance(
|
||||
500);
|
||||
300);
|
||||
//mTerrainGlobals->setUseRayBoxDistanceCalculation(true);
|
||||
terrain.mTerrainGlobals
|
||||
->getDefaultMaterialGenerator()
|
||||
@@ -547,7 +561,8 @@ TerrainModule::TerrainModule(flecs::world &ecs)
|
||||
defaultimp.terrainSize = TERRAIN_SIZE;
|
||||
defaultimp.worldSize = TERRAIN_WORLD_SIZE;
|
||||
defaultimp.inputScale = 1.0f;
|
||||
defaultimp.minBatchSize = 33;
|
||||
// defaultimp.minBatchSize = 33;
|
||||
defaultimp.minBatchSize = 5;
|
||||
defaultimp.maxBatchSize = 65;
|
||||
Ogre::Image combined;
|
||||
combined.loadTwoImagesAsRGBA(
|
||||
@@ -575,24 +590,33 @@ TerrainModule::TerrainModule(flecs::world &ecs)
|
||||
terrain.mPageManager);
|
||||
terrain.mPagedWorld =
|
||||
terrain.mPageManager->createWorld();
|
||||
#if 0
|
||||
terrain.mTerrainGroup->setAutoUpdateLod(
|
||||
Ogre::TerrainAutoUpdateLodFactory::
|
||||
getAutoUpdateLod(
|
||||
Ogre::BY_DISTANCE));
|
||||
#endif
|
||||
|
||||
terrain.mTerrainPagedWorldSection =
|
||||
terrain.mTerrainPaging
|
||||
->createWorldSection(
|
||||
terrain.mPagedWorld,
|
||||
terrain.mTerrainGroup,
|
||||
300, 800,
|
||||
300, 500,
|
||||
ENDLESS_PAGE_MIN_X,
|
||||
ENDLESS_PAGE_MIN_Y,
|
||||
ENDLESS_PAGE_MAX_X,
|
||||
ENDLESS_PAGE_MAX_Y);
|
||||
terrain.definer = OGRE_NEW FlatTerrainDefiner(
|
||||
eng.mScnMgr /*, eng.mWorld */);
|
||||
|
||||
terrain.mTerrainPagedWorldSection->setDefiner(
|
||||
OGRE_NEW FlatTerrainDefiner(
|
||||
eng.mScnMgr /*, eng.mWorld */));
|
||||
terrain.definer);
|
||||
|
||||
terrain.mTerrainGroup->freeTemporaryResources();
|
||||
std::cout << "Terrain setup done\n";
|
||||
ECS::get().set<PlacementObjects>({});
|
||||
terrain.mTerrainGroup->loadAllTerrains(true);
|
||||
}
|
||||
if (sun.mSun &&
|
||||
priv.mSunUpdate.getMilliseconds() > 1000) {
|
||||
@@ -607,6 +631,7 @@ TerrainModule::TerrainModule(flecs::world &ecs)
|
||||
.getPitch()
|
||||
<< "\n";
|
||||
priv.mSunUpdate.reset();
|
||||
//terrain.mTerrainGroup->autoUpdateLodAll()
|
||||
}
|
||||
});
|
||||
ecs.system<const ECS::Camera, const Terrain>("UpdateTerrainStatus")
|
||||
@@ -710,11 +735,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>())
|
||||
@@ -738,11 +764,14 @@ TerrainModule::TerrainModule(flecs::world &ecs)
|
||||
player.modified<CharacterLocation>();
|
||||
ECS::get().remove<CanSetPlayerPosition>();
|
||||
});
|
||||
ecs.observer<const Terrain>("LoadTerrainItems")
|
||||
.event(flecs::OnSet)
|
||||
#endif
|
||||
ecs.system<const Terrain>("UpdateTerrainGroup")
|
||||
.kind(flecs::OnUpdate)
|
||||
.interval(2.0f)
|
||||
.each([](const Terrain &terrain) {
|
||||
if (terrain.mTerrainGroup)
|
||||
loadItems();
|
||||
if (!terrain.mTerrainGroup
|
||||
->isDerivedDataUpdateInProgress())
|
||||
terrain.mTerrainGroup->update(false);
|
||||
});
|
||||
}
|
||||
float TerrainModule::get_height(Ogre::TerrainGroup *group,
|
||||
@@ -783,167 +812,9 @@ void TerrainModule::save_heightmap()
|
||||
{
|
||||
HeightData::get_singleton()->save_heightmap();
|
||||
}
|
||||
flecs::entity TerrainModule::createItem(const Ogre::Vector3 &position,
|
||||
const Ogre::Quaternion &orientation,
|
||||
const Ogre::String &type)
|
||||
void TerrainModule::defineTerrain(long x, long y)
|
||||
{
|
||||
long x, y;
|
||||
ECS::get<Terrain>().mTerrainGroup->convertWorldPositionToTerrainSlot(
|
||||
position, &x, &y);
|
||||
std::pair<long, long> pos{ x, y };
|
||||
flecs::entity slot =
|
||||
ECS::get().query_builder<const TerrainSlotParent>().build().find(
|
||||
[&](const TerrainSlotParent &slot) -> bool {
|
||||
return slot.slot == pos;
|
||||
});
|
||||
if (!slot.is_valid())
|
||||
slot = ECS::get().entity().add<TerrainSlotParent>();
|
||||
flecs::entity item = ECS::get().entity().child_of(slot);
|
||||
nlohmann::json jproperties;
|
||||
jproperties["type"] = type;
|
||||
item.set<TerrainItem>({ position, orientation, jproperties.dump() });
|
||||
return item;
|
||||
ECS::get<Terrain>().definer->define(ECS::get<Terrain>().mTerrainGroup,
|
||||
x, y);
|
||||
}
|
||||
void TerrainModule::setItemProperties(flecs::entity id, Ogre::String properties)
|
||||
{
|
||||
OgreAssert(id.is_valid(), "bad id");
|
||||
id.get_mut<TerrainItem>().properties = properties;
|
||||
id.modified<TerrainItem>();
|
||||
}
|
||||
const Ogre::String &TerrainModule::getItemProperties(flecs::entity id)
|
||||
{
|
||||
OgreAssert(id.is_valid(), "bad id");
|
||||
return id.get<TerrainItem>().properties;
|
||||
}
|
||||
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 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>();
|
||||
}
|
||||
void TerrainModule::saveItems()
|
||||
{
|
||||
Ogre::String path = "resources/buildings/items.list";
|
||||
if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(
|
||||
"items.list")) {
|
||||
Ogre::String group =
|
||||
Ogre::ResourceGroupManager::getSingleton()
|
||||
.findGroupContainingResource("items.list");
|
||||
Ogre::FileInfoListPtr fileInfoList(
|
||||
Ogre::ResourceGroupManager::getSingleton()
|
||||
.findResourceFileInfo(group, "items.list"));
|
||||
OgreAssert(fileInfoList->size() == 1,
|
||||
"worpd_map.png should be there and only once");
|
||||
path = fileInfoList->at(0).archive->getName() + "/" +
|
||||
"items.list";
|
||||
Ogre::FileSystemLayer::removeFile(path);
|
||||
}
|
||||
std::fstream fout(path.c_str(), std::ios::out);
|
||||
nlohmann::json jitemlist;
|
||||
ECS::get().query_builder<const TerrainItem>().build().each(
|
||||
[&](flecs::entity e, const TerrainItem &item) {
|
||||
nlohmann::json jitem;
|
||||
to_json(jitem["position"], item.position);
|
||||
to_json(jitem["orientation"], item.orientation);
|
||||
to_json(jitem["properties"], item.properties);
|
||||
jitemlist.push_back(jitem);
|
||||
});
|
||||
fout << jitemlist.dump();
|
||||
fout.close();
|
||||
}
|
||||
void TerrainModule::loadItems()
|
||||
{
|
||||
if (!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(
|
||||
"items.list"))
|
||||
return;
|
||||
Ogre::String group = Ogre::ResourceGroupManager::getSingleton()
|
||||
.findGroupContainingResource("items.list");
|
||||
Ogre::DataStreamPtr stream =
|
||||
Ogre::ResourceGroupManager::getSingleton().openResource(
|
||||
"items.list", group);
|
||||
Ogre::String json = stream->getAsString();
|
||||
nlohmann::json jlist = nlohmann::json::parse(json);
|
||||
ECS::get().delete_with<TerrainItem>();
|
||||
ECS::get().delete_with<TerrainSlotParent>();
|
||||
|
||||
for (const auto &v : jlist) {
|
||||
Ogre::Vector3 position;
|
||||
Ogre::Quaternion orientation;
|
||||
Ogre::String properties;
|
||||
from_json(v["position"], position);
|
||||
from_json(v["orientation"], orientation);
|
||||
properties = v["properties"].get<Ogre::String>();
|
||||
long x, y;
|
||||
ECS::get<Terrain>()
|
||||
.mTerrainGroup->convertWorldPositionToTerrainSlot(
|
||||
position, &x, &y);
|
||||
std::pair<long, long> pos{ x, y };
|
||||
flecs::entity slot =
|
||||
ECS::get()
|
||||
.query_builder<const TerrainSlotParent>()
|
||||
.build()
|
||||
.find([&](const TerrainSlotParent &slot)
|
||||
-> bool {
|
||||
return slot.slot == pos;
|
||||
});
|
||||
if (!slot.is_valid())
|
||||
slot = ECS::get().entity().add<TerrainSlotParent>();
|
||||
flecs::entity item = ECS::get().entity().child_of(slot);
|
||||
item.set<TerrainItem>({ position, orientation, properties });
|
||||
std::cout << "position: " << item.id() << " " << position
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
void TerrainModule::getItemPositionPerSlot(long x, long y,
|
||||
std::list<Ogre::Vector3> *positions)
|
||||
{
|
||||
std::pair<long, long> pos{ x, y };
|
||||
if (!positions)
|
||||
return;
|
||||
flecs::entity slot =
|
||||
ECS::get().query_builder<const TerrainSlotParent>().build().find(
|
||||
[&](const TerrainSlotParent &slot) -> bool {
|
||||
return slot.slot == pos;
|
||||
});
|
||||
if (!slot.is_valid())
|
||||
return;
|
||||
ECS::get()
|
||||
.query_builder<const TerrainItem>()
|
||||
.with(flecs::ChildOf, slot)
|
||||
.build()
|
||||
.each([&](flecs::entity e, const TerrainItem &item) {
|
||||
positions->push_back(item.position);
|
||||
std::cout << e.id() << " " << item.position
|
||||
<< std::endl;
|
||||
});
|
||||
}
|
||||
void TerrainModule::getItemPositions(std::list<Ogre::Vector3> *positions)
|
||||
{
|
||||
ECS::get().query_builder<const TerrainItem>().build().each(
|
||||
[&](flecs::entity e, const TerrainItem &item) {
|
||||
positions->push_back(item.position);
|
||||
std::cout << e.id() << " " << item.position
|
||||
<< std::endl;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -12,12 +12,14 @@ class TerrainPagedWorldSection;
|
||||
}
|
||||
namespace ECS
|
||||
{
|
||||
class FlatTerrainDefiner;
|
||||
struct Terrain {
|
||||
Ogre::TerrainGlobalOptions *mTerrainGlobals;
|
||||
Ogre::TerrainGroup *mTerrainGroup;
|
||||
Ogre::TerrainPaging *mTerrainPaging;
|
||||
Ogre::PageManager *mPageManager;
|
||||
Ogre::PagedWorld *mPagedWorld;
|
||||
FlatTerrainDefiner *definer;
|
||||
Ogre::TerrainPagedWorldSection *mTerrainPagedWorldSection;
|
||||
bool mTerrainReady;
|
||||
|
||||
@@ -41,17 +43,7 @@ struct TerrainModule {
|
||||
static int get_img_y(float world_z);
|
||||
static void update_heightmap(const Ogre::Image &heightmap);
|
||||
static void save_heightmap();
|
||||
static flecs::entity createItem(const Ogre::Vector3 &position,
|
||||
const Ogre::Quaternion &orientation,
|
||||
const Ogre::String &type);
|
||||
static void setItemProperties(flecs::entity id,
|
||||
Ogre::String properties);
|
||||
static const Ogre::String &getItemProperties(flecs::entity id);
|
||||
static void saveItems();
|
||||
static void loadItems();
|
||||
static void getItemPositionPerSlot(long x, long y,
|
||||
std::list<Ogre::Vector3> *positions);
|
||||
static void getItemPositions(std::list<Ogre::Vector3> *positions);
|
||||
static void defineTerrain(long x, long y);
|
||||
};
|
||||
struct TerrainReady {};
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
105
src/gamedata/items/items.h
Normal file
105
src/gamedata/items/items.h
Normal file
@@ -0,0 +1,105 @@
|
||||
#ifndef __ITEMS_H__
|
||||
#define __ITEMS_H__
|
||||
#include <OgreMeshLodGenerator.h>
|
||||
#include <flecs.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
|
||||
5862
src/gamedata/items/town.cpp
Normal file
5862
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)
|
||||
@@ -852,6 +863,8 @@ public:
|
||||
JPH::BodyInterface &body_interface =
|
||||
physics_system.GetBodyInterface();
|
||||
JPH::Body *body = body_interface.CreateBody(settings);
|
||||
if (!body)
|
||||
return JPH::BodyID();
|
||||
return body->GetID();
|
||||
}
|
||||
void removeBody(const JPH::BodyID &id)
|
||||
@@ -888,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,
|
||||
@@ -899,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;
|
||||
@@ -959,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());
|
||||
}
|
||||
@@ -1270,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,
|
||||
@@ -1294,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();
|
||||
@@ -1312,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();
|
||||
@@ -1324,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,
|
||||
@@ -1340,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,
|
||||
@@ -1356,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)
|
||||
{
|
||||
@@ -1383,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);
|
||||
@@ -1441,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));
|
||||
@@ -1467,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;
|
||||
}
|
||||
};
|
||||
@@ -1594,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
|
||||
@@ -1763,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
|
||||
|
||||
Reference in New Issue
Block a user