Compare commits

...

15 Commits

63 changed files with 15563 additions and 2036 deletions

View File

@@ -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
View File

@@ -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)
{

View 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})

View 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)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View 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)

View File

@@ -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})

View File

@@ -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
View 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

View 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

View File

@@ -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
View 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})

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

File diff suppressed because it is too large Load Diff

22
src/FastNoiseLite/LICENSE Normal file
View 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.

View 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...
```

View File

@@ -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 .)

View File

@@ -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>();
});
}
}
}

View File

@@ -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();

View File

@@ -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)

View File

@@ -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)

View File

@@ -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;

View File

@@ -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 });
}
}
}

View File

@@ -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

View File

@@ -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)
{

View File

@@ -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

View File

@@ -75,5 +75,8 @@ struct GroundCheckReady {};
struct Body2Entity {
/* std::unordered_map<btCollisionObject *, flecs::entity> entities; */
};
struct EditorSceneSwitch {
int scene;
};
}
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
#ifndef __EDITORGUIMODULE_H__
#define __EDITORGUIMODULE_H__
namespace ECS {
struct EditorGUIModule {
EditorGUIModule(flecs::world &ecs);
};
}
#endif // EDITORGUIMODULE_H

View File

@@ -26,4 +26,4 @@ struct EventModule {
flecs::entity to);
};
}
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -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

View 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

View File

@@ -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);
}
}

View File

@@ -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()

View File

@@ -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)
}
});
}
}
}

View File

@@ -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);

View File

@@ -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
{

View File

@@ -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

View 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;
}
}

View 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

View 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()
{
}
};
}

View 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

View File

@@ -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;
});
}
}

View File

@@ -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 {};
}

View File

@@ -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);

View 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
)

View 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", &centerOffset, 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", &centerOffset, 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();
}
}
}

View 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

View 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
View 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

View 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);
}
}
}

View 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

File diff suppressed because it is too large Load Diff

26
src/gamedata/items/town.h Normal file
View 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

View File

@@ -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;

View File

@@ -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