Compare commits

..

15 Commits

66 changed files with 16542 additions and 3451 deletions

View File

@@ -77,6 +77,7 @@ add_subdirectory(src/tests)
add_subdirectory(src/physics)
add_subdirectory(src/editor)
add_subdirectory(assets/blender/buildings/parts)
add_subdirectory(resources)
add_executable(Game Game.cpp ${WATER_SRC})
target_include_directories(Game PRIVATE src/gamedata)
@@ -203,34 +204,8 @@ add_custom_target(stage_stories ALL DEPENDS stage_lua_scripts ${CMAKE_BINARY_DIR
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/resources.cfg
COMMAND cp ${CMAKE_SOURCE_DIR}/resources.cfg ${CMAKE_BINARY_DIR}/resources.cfg
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/resources/main
${CMAKE_BINARY_DIR}/resources/main
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/resources/shaderlib
${CMAKE_BINARY_DIR}/resources/shaderlib
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/resources/terrain
${CMAKE_BINARY_DIR}/resources/terrain
# COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/lua-scripts
# ${CMAKE_BINARY_DIR}/lua-scripts
# COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/skybox
# ${CMAKE_BINARY_DIR}/skybox
# COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/resources/debug
# ${CMAKE_BINARY_DIR}/resources/debug
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/resources.cfg ${CMAKE_BINARY_DIR}/resources.cfg
DEPENDS ${CMAKE_SOURCE_DIR}/resources.cfg)
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/resources/terrain/world_map.png
COMMAND unzip -o ${CMAKE_SOURCE_DIR}/world_map.kra mergedimage.png -d ${CMAKE_BINARY_DIR}/world_map
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_BINARY_DIR}/world_map/mergedimage.png
${CMAKE_BINARY_DIR}/resources/terrain/world_map.png
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/world_map
DEPENDS ${CMAKE_SOURCE_DIR}/world_map.kra)
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/resources/terrain/brushes.png
COMMAND unzip -o ${CMAKE_SOURCE_DIR}/brushes.kra mergedimage.png -d ${CMAKE_BINARY_DIR}/brushes
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_BINARY_DIR}/brushes/mergedimage.png
${CMAKE_BINARY_DIR}/resources/terrain/brushes.png
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/brushes
DEPENDS ${CMAKE_SOURCE_DIR}/brushes.kra)
set(SKYBOX_SRC
early_morning_bk.jpg
@@ -356,29 +331,11 @@ add_custom_command(
COMMAND ${CMAKE_COMMAND} -E touch ${CHARACTER_GLBS}
DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_models.py ${VRM_IMPORTED_BLENDS} ${EDITED_BLEND_TARGETS}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
#add_custom_command(
# OUTPUT ${CMAKE_SOURCE_DIR}/characters/female/vroid-normal-female.scene
# ${CMAKE_SOURCE_DIR}/characters/male/vroid-normal-male.scene
# ${CMAKE_SOURCE_DIR}/assets/blender/vrm-vroid-normal-female.blend
# ${CMAKE_SOURCE_DIR}/assets/blender/vrm-vroid-normal-male.blend
# ${CMAKE_SOURCE_DIR}/assets/blender/shapes/male/vrm-vroid-normal-male-chibi.blend
# COMMAND mkdir -p ${CREATE_DIRECTORIES}
# COMMAND ${BLENDER} -b -Y -P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/import_vrm.py
# COMMAND ${BLENDER} -b -Y -P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_models.py
## COMMAND ${BLENDER} -b -Y -P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_ogre_scene.py
# COMMAND echo rm -Rf ${CMAKE_SOURCE_DIR}/characters/male/*.material ${CMAKE_SOURCE_DIR}/characters/female/*.material
# COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/characters
# ${CMAKE_BINARY_DIR}/characters
# DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_models.py ${CMAKE_SOURCE_DIR}/assets/blender/scripts/import_vrm.py
# ${CMAKE_SOURCE_DIR}/assets/vroid/buch1.vrm
# ${CMAKE_SOURCE_DIR}/assets/vroid/buch1-chibi.vrm
# ${CMAKE_SOURCE_DIR}/assets/vroid/jane2-dress.vrm
# WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
add_custom_target(stage_files ALL DEPENDS ${CMAKE_BINARY_DIR}/resources.cfg ${MATERIALS_OUTPUT}
${CMAKE_BINARY_DIR}/resources/terrain/world_map.png
${CMAKE_BINARY_DIR}/resources/terrain/brushes.png
edited-blends)
edited-blends stage_resources)
add_custom_target(remove_scenes COMMAND rm -f ${VRM_SOURCE} ${VRM_IMPORTED_BLENDS} ${CHARACTER_GLBS})

118
Game.cpp
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,24 +430,32 @@ public:
Ogre::RTShader::ShaderGenerator::getSingletonPtr();
shadergen->addSceneManager(scnMgr);
setWindowGrab(true);
std::cout << "Init camera" << "\n";
std::cout << "Init camera"
<< "\n";
initCamera();
std::cout << "Set up water" << "\n";
std::cout << "Set up cursor" << "\n";
std::cout << "Set up water"
<< "\n";
std::cout << "Set up cursor"
<< "\n";
Ogre::ResourceGroupManager::getSingleton()
.initialiseAllResourceGroups();
// OgreBites::ApplicationContext::loadResources();
// setupCursor();
std::cout << "Setup input" << "\n";
std::cout << "Setup input"
<< "\n";
setupInput();
std::cout << "Create content" << "\n";
std::cout << "Create content"
<< "\n";
createContent();
std::cout << "Setup done" << "\n";
std::cout << "Setup done"
<< "\n";
#if 0
mDbgDraw->setDebugMode(mDbgDraw->getDebugMode() |
btIDebugDraw::DBG_DrawContactPoints);
#endif
}
Ogre::LogManager::getSingleton().setMinLogLevel(
Ogre::LML_CRITICAL);
}
Ogre::SceneManager *getSceneManager()
{
return mScnMgr;
@@ -435,14 +481,19 @@ public:
mDynWorld->getBtWorld()->stepSimulation(delta, 3);
#endif
if (!ECS::get().has<ECS::GUI>())
return;
/* Update window grab */
ECS::GUI &gui = ECS::get().get_mut<ECS::GUI>();
if (gui.grabChanged) {
setWindowGrab(gui.grab);
gui.grabChanged = false;
ECS::get().modified<ECS::GUI>();
}
goto end;
{
/* Update window grab */
ECS::GUI &gui = ECS::get().get_mut<ECS::GUI>();
if (gui.grabChanged) {
setWindowGrab(gui.grab);
gui.grabChanged = false;
ECS::get().modified<ECS::GUI>();
std::cout << "updateWorld " << gui.grabChanged
<< " " << gui.grab << std::endl;
}
}
end:
ECS::update(delta);
#if 0
@@ -601,8 +652,9 @@ public:
"Skybox/Dynamic", "General");
OgreAssert(m, "Sky box material not found.");
m->load();
ECS::setup(mScnMgr, /*mDynWorld.get(), */ mCameraNode, mCamera,
getRenderWindow());
ECS::setupExteriorScene(mScnMgr,
/*mDynWorld.get(), */ mCameraNode,
mCamera, getRenderWindow());
ECS::get().set<ECS::RenderWindow>(
{ getRenderWindow(), getDisplayDPI() });
ECS::get()
@@ -736,9 +788,7 @@ public:
}
flecs::entity getPlayer() const
{
flecs::entity player =
ECS::get().lookup("ECS::CharacterModule::player");
return player;
return ECS::player;
}
void enableDbgDraw(bool enable)
{

View File

@@ -1,5 +1,6 @@
project(building-parts)
set(PARTS_FILES pier.blend)
set(FURNITURE_FILES furniture.blend furniture-sofa.blend)
set(PARTS_OUTPUT_DIRS)
foreach(PARTS_FILE ${PARTS_FILES})
get_filename_component(FILE_NAME ${PARTS_FILE} NAME_WE)
@@ -14,4 +15,17 @@ foreach(PARTS_FILE ${PARTS_FILES})
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${PARTS_FILE} ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_building_parts.py)
list(APPEND PARTS_OUTPUT_DIRS ${PARTS_OUTPUT_DIR})
endforeach()
foreach(FURNITURE_FILE ${FURNITURE_FILES})
get_filename_component(FILE_NAME ${FURNITURE_FILE} NAME_WE)
set(PARTS_OUTPUT_DIR ${CMAKE_BINARY_DIR}/resources/buildings/parts/${FILE_NAME})
add_custom_command(
OUTPUT ${PARTS_OUTPUT_DIR}
COMMAND ${CMAKE_COMMAND} -E make_directory ${PARTS_OUTPUT_DIR}
COMMAND ${BLENDER} ${CMAKE_CURRENT_SOURCE_DIR}/${FURNITURE_FILE}
-b -Y -P
${CMAKE_CURRENT_SOURCE_DIR}/export_furniture_parts.py
-- ${PARTS_OUTPUT_DIR}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${PARTS_FILE} ${CMAKE_CURRENT_SOURCE_DIR}/export_furniture_parts.py)
list(APPEND PARTS_OUTPUT_DIRS ${PARTS_OUTPUT_DIR})
endforeach()
add_custom_target(import_building_parts ALL DEPENDS ${PARTS_OUTPUT_DIRS})

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.

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,154 @@ setup_handler(function(event, trigger_entity, what_entity)
--]]
end
end)
setup_action_handler("talk", {
activate = function(this)
local book = narrator.parse_file('stories.talk')
this.story = narrator.init_story(book)
local get_crash_bind = function(this)
local crash_bind = function()
print("variables")
print(dump(this.story.variables))
crash()
end
return crash_bind
end
this.story:bind('crash', get_crash_bind(this))
local props = this._get_properties()
local goals = this._get_goals()
print("node activated")
-- print(dump(goals))
local json_data = json.decode(props)
print(dump(json_data))
for i, v in ipairs(goals) do
print("Goal: ", i)
local goal_data = json.decode(v)
print(dump(goal_data))
end
this.story:begin()
this:narration_update()
get_crash_bind(this)()
crash()
end,
event = function(this, event)
if event == "narration_progress" then
this:narration_update()
end
if event == "narration_answered" then
local answer = this._get_narration_answer()
this.story:choose(answer)
this:narration_update()
end
end,
finish = function(this)
end,
narration_update = function(this)
local ret = ""
local choices = {}
local have_choice = false
local have_paragraph = false
print("narration_update")
if this.story:can_continue() then
print("CAN continue")
have_paragraph = true
local paragraph = this.story:continue(1)
print(dump(paragraph))
local text = paragraph.text
if paragraph.tags then
-- text = text .. ' #' .. table.concat(paragraph.tags, ' #')
for i, tag in ipairs(paragraph.tags) do
if tag == 'discard' then
text = ''
elseif tag == 'crash' then
print(text)
crash()
end
this:handle_tag(tag)
end
end
ret = text
if this.story:can_choose() then
have_choice = true
local ch = this.story:get_choices()
for i, choice in ipairs(ch) do
table.insert(choices, choice.text)
print(i, dump(choice))
end
if #choices == 1 and choices[1] == "Ascend" then
this.story:choose(1)
choices = {}
end
if #choices == 1 and choices[1] == "Continue" then
this.story:choose(1)
choices = {}
end
end
else
print("can NOT continue")
end
print(ret)
if (#choices > 0) then
print("choices!!!")
this._narration(ret, choices)
else
this._narration(ret, {})
end
if not have_choice and not have_paragraph then
this._finish()
end
end,
handle_tag = function(this, tag)
print("tag: " .. tag)
end,
})
--[[
active_dialogues = {}
setup_action_handler("talk", function(town, index, word)
local ret = ""
local choices = {}
local have_choice = false
local have_paragraph = false
local book = narrator.parse_file('stories.talk')
local story = narrator.init_story(book)
if story == nil then
crash()
end
story:begin()
narrate("Boo!!!!")
if story.can_continue() then
local paragraph = story:continue(1)
print(dump(paragraph))
if story:can_choose() then
have_choice = true
local ch = story:get_choices()
for i, choice in ipairs(ch) do
table.insert(choices, choice.text)
print(i, dump(choice))
end
if #choices == 1 and choices[1] == "Ascend" then
story:choose(1)
choices = {}
end
if #choices == 1 and choices[1] == "Continue" then
this.story:choose(1)
choices = {}
end
end
end
if (#choices > 0) then
print("choices!!!")
narrate(ret, choices)
else
narrate(ret)
end
if not have_choice and not have_paragraph then
-- complete
crash()
else
print("can continue")
end
end)
]]--

388
lua-scripts/json/json.lua Normal file
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

@@ -21,6 +21,7 @@ FileSystem=resources/terrain
FileSystem=skybox
FileSystem=resources/buildings
FileSystem=resources/buildings/parts/pier
FileSystem=resources/buildings/parts/furniture
FileSystem=resources/vehicles
FileSystem=resources/debug
FileSystem=resources/fonts

34
resources/CMakeLists.txt Normal file
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 resources/buildings/parts)
set(COPY_DIRECTORIES characters resources skybox water resources/buildings/parts)
set(INSTALL_DEPS ${CMAKE_CURRENT_BINARY_DIR}/resources.cfg)
foreach(DIR_NAME ${COPY_DIRECTORIES})
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${DIR_NAME}
@@ -29,6 +29,7 @@ add_library(editor STATIC EditorGizmoModule.cpp EditorInputModule.cpp)
target_link_libraries(editor PRIVATE
OgreMain
GameData
physics
)
target_include_directories(editor PUBLIC .)

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,41 @@ public:
void initCamera()
{
mCameraNode = mScnMgr->getRootSceneNode()->createChildSceneNode(
"CameraNode");
mCameraNode->setPosition(0, 2, 3);
mCameraNode->lookAt(Ogre::Vector3(0, 1, -1),
Ogre::Node::TS_PARENT);
// create the camera
mCamera = mScnMgr->createCamera("fps_camera");
mCamera->setNearClipDistance(0.05f);
mCamera->setAutoAspectRatio(true);
mCameraNode->attachObject(mCamera);
// and tell it to render into the main window
mViewport = getRenderWindow()->addViewport(mCamera);
mCameraPivot =
mScnMgr->getRootSceneNode()->createChildSceneNode(
"FPSCameraPivot");
mCameraGoal = mCameraPivot->createChildSceneNode(
"FPSCameraGoal", Ogre::Vector3(0, 2, 3));
mCameraNode->setPosition(mCameraPivot->getPosition() +
mCameraGoal->getPosition());
mCameraPivot->setFixedYawAxis(true);
mCameraGoal->setFixedYawAxis(true);
mCameraNode->setFixedYawAxis(true);
// our model is quite small, so reduce the clipping planes
mCamera->setNearClipDistance(0.1f);
mCamera->setFarClipDistance(800);
mPivotPitch = 0;
mEditorNormalScene.initCamera("fps_camera");
mViewport = getRenderWindow()->addViewport(
mEditorNormalScene.mCamera);
}
void configure()
{
std::cout << "Startup" << "\n";
std::cout << "Startup"
<< "\n";
initApp();
std::cout << "Set up RTSS" << "\n";
std::cout << "Set up RTSS"
<< "\n";
Ogre::Root *root = getRoot();
Ogre::SceneManager *scnMgr = getSceneManager();
// register our scene with the RTSS
Ogre::RTShader::ShaderGenerator *shadergen =
Ogre::RTShader::ShaderGenerator::getSingletonPtr();
shadergen->addSceneManager(scnMgr);
mEditorNormalScene.setupRTSS();
setWindowGrab(false);
std::cout << "Init camera" << "\n";
std::cout << "Init camera"
<< "\n";
initCamera();
std::cout << "Set up water" << "\n";
std::cout << "Set up cursor" << "\n";
Ogre::ResourceGroupManager::getSingleton()
.initialiseAllResourceGroups();
// OgreBites::ApplicationContext::loadResources();
// setupCursor();
std::cout << "Setup input" << "\n";
std::cout << "Setup input"
<< "\n";
setupInput();
std::cout << "Create content" << "\n";
std::cout << "Create content"
<< "\n";
createContent();
std::cout << "Setup done" << "\n";
#if 0
mDbgDraw->setDebugMode(mDbgDraw->getDebugMode() |
btIDebugDraw::DBG_DrawContactPoints);
#endif
}
Ogre::SceneManager *getSceneManager()
{
return mScnMgr;
}
std::cout << "Setup done"
<< "\n";
Ogre::LogManager::getSingleton().setMinLogLevel(
Ogre::LML_TRIVIAL);
}
Ogre::Timer mTerrainUpd;
// TODO: implement rough water level calculation
float getWaterLevel(const Ogre::Vector3 &position)
{
Ogre::Vector3::UNIT_Y;
float etime =
Ogre::ControllerManager::getSingleton().getElapsedTime();
return 0.0f;
}
void updateWorld(float delta)
@@ -572,10 +587,11 @@ public:
void setupInput()
{
}
bool switchWindow = false;
void createContent()
{
int i;
sky = new SkyBoxRenderer(getSceneManager());
sky = new SkyBoxRenderer(mEditorNormalScene.getSceneManager());
bool drawFirst = true;
uint8_t renderQueue = drawFirst ?
Ogre::RENDER_QUEUE_SKIES_EARLY :
@@ -591,8 +607,10 @@ public:
"Skybox/Dynamic", "General");
OgreAssert(m, "Sky box material not found.");
m->load();
ECS::setupEditor(mScnMgr, /*mDynWorld.get(), */ mCameraNode,
mCamera, getRenderWindow());
ECS::setupEditor(
mEditorNormalScene.mScnMgr,
/*mDynWorld.get(), */ mEditorNormalScene.mCameraNode,
mEditorNormalScene.mCamera, getRenderWindow());
ECS::get().import <ECS::EditorGizmoModule>();
ECS::get().import <ECS::EditorInputModule>();
ECS::get().set<ECS::RenderWindow>(
@@ -625,86 +643,14 @@ public:
{ getImGuiInputListener(), &mKbd } });
ECS::get().add<ECS::EditorDebugMaterial>();
std::shared_ptr<Ogre::ManualObject> manualObj(
mScnMgr->createManualObject("EditorGizmo"));
mEditorNormalScene.mScnMgr->createManualObject(
"EditorGizmo"));
Ogre::SceneNode *gizmoNode =
mScnMgr->getRootSceneNode()->createChildSceneNode(
"EditorGizmoNode");
mEditorNormalScene.mScnMgr->getRootSceneNode()
->createChildSceneNode("EditorGizmoNode");
gizmoNode->attachObject(manualObj.get());
manualObj->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY);
ECS::get().set<ECS::EditorGizmo>({ manualObj, gizmoNode });
#if 0
ECS::get()
.system<const ECS::EngineData, ECS::Camera,
const ECS::Input>("UpdateEditorCamera")
.kind(flecs::OnUpdate)
.each([&](const ECS::EngineData &eng,
ECS::Camera &camera,
const ECS::Input &input) {
if (!camera.configured) {
// create a pivot at roughly the character's shoulder
camera.mCameraPivot =
eng.mScnMgr->getRootSceneNode()
->createChildSceneNode();
camera.mCameraGoal =
camera.mCameraPivot
->createChildSceneNode(
Ogre::Vector3(
0, 2,
0));
camera.mCameraNode->setPosition(
camera.mCameraPivot
->getPosition() +
camera.mCameraGoal
->getPosition());
camera.mCameraGoal->lookAt(
Ogre::Vector3(0, 0, -10),
Ogre::Node::TS_PARENT);
camera.mCameraPivot->setFixedYawAxis(
true);
camera.mCameraGoal->setFixedYawAxis(
true);
camera.mCameraNode->setFixedYawAxis(
true);
// our model is quite small, so reduce the clipping planes
camera.mCamera->setNearClipDistance(
0.1f);
camera.mCamera->setFarClipDistance(700);
camera.mPivotPitch = 0;
camera.configured = true;
} else {
// place the camera pivot roughly at the character's shoulder
#if 0
camera.mCameraPivot->setPosition(
ch.mBodyNode->getPosition() +
Ogre::Vector3::UNIT_Y * CAM_HEIGHT);
#endif
// move the camera smoothly to the goal
Ogre::Vector3 goalOffset =
camera.mCameraGoal
->_getDerivedPosition() -
camera.mCameraNode
->getPosition();
camera.mCameraNode->translate(
goalOffset * eng.delta * 9.0f);
// always look at the pivot
#if 0
camera.mCameraNode->lookAt(
camera.mCameraPivot
->_getDerivedPosition(),
Ogre::Node::TS_PARENT);
#endif
camera.mCameraNode->_setDerivedOrientation(
camera.mCameraGoal
->_getDerivedOrientation());
}
if (input.control & 512 && input.mouse_moved) {
mCameraPivot->yaw(
Ogre::Radian(-input.mouse.x *
3.0f * eng.delta));
}
});
#endif
}
bool get_gui_active()
{
@@ -715,15 +661,9 @@ public:
ECS::get().get_mut<ECS::GUI>().enabled = active;
ECS::get().modified<ECS::GUI>();
}
Ogre::Camera *getCamera()
{
return mCamera;
}
flecs::entity getPlayer() const
{
flecs::entity player =
ECS::get().lookup("ECS::CharacterModule::player");
return player;
return ECS::player;
}
void enableDbgDraw(bool enable)
{
@@ -789,7 +729,7 @@ int main()
{
App ctx;
ctx.configure();
ctx.enableDbgDraw(false);
// ctx.enableDbgDraw(false);
ctx.getRoot()->startRendering();
ctx.setWindowGrab(false);
ctx.closeApp();

View File

@@ -4,17 +4,19 @@ find_package(OGRE REQUIRED COMPONENTS Bites Bullet Paging Terrain Overlay CONFIG
find_package(Bullet REQUIRED)
find_package(nlohmann_json REQUIRED)
find_package(OgreProcedural REQUIRED CONFIG)
add_subdirectory(items)
add_library(GameData STATIC GameData.cpp CharacterModule.cpp WaterModule.cpp SunModule.cpp TerrainModule.cpp
GUIModule.cpp LuaData.cpp WorldMapModule.cpp BoatModule.cpp EventTriggerModule.cpp
GUIModule.cpp EditorGUIModule.cpp LuaData.cpp WorldMapModule.cpp BoatModule.cpp EventTriggerModule.cpp
CharacterAnimationModule.cpp PhysicsModule.cpp EventModule.cpp CharacterManagerModule.cpp
VehicleManagerModule.cpp AppModule.cpp StaticGeometryModule.cpp SmartObject.cpp SlotsModule.cpp goap.cpp)
VehicleManagerModule.cpp AppModule.cpp StaticGeometryModule.cpp SmartObject.cpp SlotsModule.cpp
PlayerActionModule.cpp CharacterAIModule.cpp goap.cpp)
target_link_libraries(GameData PUBLIC
lua
flecs::flecs_static
nlohmann_json::nlohmann_json
OgreMain
OgreBites
OgrePaging OgreTerrain OgreOverlay OgreProcedural::OgreProcedural
OgrePaging OgreTerrain OgreOverlay OgreProcedural::OgreProcedural items
PRIVATE sceneloader world-build physics editor)
target_include_directories(GameData PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${BULLET_INCLUDE_DIR} ../luaaa)
target_include_directories(GameData PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${BULLET_INCLUDE_DIR} ../luaaa ../aitoolkit/include)
target_compile_definitions(GameData PRIVATE FLECS_CPP_NO_AUTO_REGISTRATION)

View File

@@ -0,0 +1,887 @@
#include <iostream>
#include <nlohmann/json.hpp>
#include <Ogre.h>
#include "goap.h"
#include "CharacterManagerModule.h"
#include "PlayerActionModule.h"
#include "CharacterModule.h"
#include "items.h"
#include "CharacterAIModule.h"
namespace ECS
{
class ActionNodeActions {
struct WalkToAction : public goap::BaseAction<Blackboard> {
int node;
WalkToAction(int node, int cost)
: goap::BaseAction<Blackboard>(
"WalkTo(" +
Ogre::StringConverter::toString(
node) +
")",
{ { { "at_object", 0 } } },
{ { { "at_object", 1 } } }, cost)
, node(node)
{
}
bool can_run(const Blackboard &state,
bool debug = false) override
{
return (state.distance_to(prereq) == 0);
}
void _plan_effects(Blackboard &state) override
{
const ActionNodeList &alist =
ECS::get<ActionNodeList>();
state.apply(effects);
// node position can change in case of character
const Ogre::Vector3 &nodePosition =
alist.dynamicNodes[node].position;
state.setPosition(nodePosition);
}
int get_cost(const Blackboard &bb) const override
{
int ret = m_cost;
if (!bb.town.is_valid()) {
ret += 1000000;
goto out;
}
{
const ActionNodeList &alist =
ECS::get<ActionNodeList>();
// const TownNPCs &npcs = bb.town.get<TownNPCs>();
// const TownAI &ai = bb.town.get<TownAI>();
const Ogre::Vector3 &nodePosition =
alist.dynamicNodes[node].position;
// flecs::entity e = npcs.npcs.at(bb.index).e;
// bool validActive = e.is_valid() &&
// e.has<CharacterBase>();
const Ogre::Vector3 &npcPosition =
bb.getPosition();
float dist = npcPosition.squaredDistance(
nodePosition);
ret += (int)Ogre::Math::Ceil(dist);
}
out:
return ret;
}
};
struct RunActionNode : public goap::BaseAction<Blackboard> {
int node;
float radius;
Ogre::String action;
RunActionNode(int node, const Ogre::String &action,
const Blackboard &prereq,
const Blackboard &effects, int cost)
: goap::BaseAction<Blackboard>(
"Use(" + action + "," +
Ogre::StringConverter::toString(
node) +
")",
prereq, effects, cost)
, node(node)
, action(action)
{
const ActionNodeList &alist =
ECS::get<ActionNodeList>();
radius = alist.dynamicNodes[node].radius;
}
bool can_run(const Blackboard &state,
bool debug = false) override
{
bool pre = (state.distance_to(prereq) == 0);
if (!pre)
return pre;
const ActionNodeList &alist =
ECS::get<ActionNodeList>();
const Ogre::Vector3 &nodePosition =
alist.dynamicNodes[node].position;
const Ogre::Vector3 &npcPosition = state.getPosition();
return (npcPosition.squaredDistance(nodePosition) <
radius * radius);
}
};
std::vector<goap::BaseAction<Blackboard> *> m_actions;
public:
ActionNodeActions(int node, const Blackboard &prereq, int cost)
{
OgreAssert(
node < ECS::get<ActionNodeList>().dynamicNodes.size(),
"bad node " + Ogre::StringConverter::toString(node));
m_actions.push_back(OGRE_NEW WalkToAction(node, 10000));
nlohmann::json jactionPrereq = nlohmann::json::object();
nlohmann::json jactionEffect = nlohmann::json::object();
jactionPrereq["at_object"] = 1;
const nlohmann::json props =
ECS::get<ActionNodeList>().dynamicNodes[node].props;
OgreAssert(!props.is_null(),
"bad node " + Ogre::StringConverter::toString(node));
Ogre::String prefix = "goap_prereq_";
for (auto it = props.begin(); it != props.end(); it++) {
if (it.key().substr(0, prefix.length()) == prefix) {
Ogre::String key =
it.key().substr(prefix.length());
jactionPrereq[key] = it.value();
}
}
Ogre::String prefix2 = "goap_effect_";
for (auto it = props.begin(); it != props.end(); it++) {
if (it.key().substr(0, prefix2.length()) == prefix2) {
Ogre::String key =
it.key().substr(prefix2.length());
jactionPrereq[key] = it.value();
}
}
OgreAssert(props.find("action") != props.end(),
"bad action" + props.dump(4));
const Ogre::String &action =
props["action"].get<Ogre::String>();
Ogre::String effectName = "";
#if 0
if (action == "sit") {
const Ogre::String &nodeName =
props["name"].get<Ogre::String>();
effectName = "is_" + nodeName + "_seated";
} else if (action == "use") {
const Ogre::String &nodeName =
props["name"].get<Ogre::String>();
effectName = "is_" + nodeName + "_used";
}
#endif
// const Ogre::String &nodeName =
// props["name"].get<Ogre::String>();
Ogre::String nodeID = Ogre::StringConverter::toString(node);
effectName = "is_used";
if (effectName.length() > 0) {
jactionPrereq[effectName] = 0;
jactionEffect[effectName] = 1;
}
// FIXME: add this to Blender goap_prereq_ and goap_effect_ variables
if (action == "sit") {
jactionPrereq["is_seated"] = 0;
jactionEffect["is_seated"] = 1;
}
Blackboard actionPrereq({ jactionPrereq });
Blackboard actionEffect({ jactionEffect });
if (!prereq.stats.is_null())
actionPrereq.apply(prereq);
m_actions.push_back(OGRE_NEW RunActionNode(
node, action, actionPrereq, actionEffect, cost));
if (effectName == "") {
std::cout << props.dump(4) << std::endl;
std::cout << "Prereq" << std::endl;
std::cout << actionPrereq.stats.dump(4) << std::endl;
std::cout << "Effect" << std::endl;
std::cout << actionEffect.stats.dump(4) << std::endl;
OgreAssert(false, "action");
}
}
std::vector<goap::BaseAction<Blackboard> *> getActions() const
{
return m_actions;
}
};
CharacterAIModule::CharacterAIModule(flecs::world &ecs)
{
static std::mutex ecs_mutex;
ecs.module<CharacterAIModule>();
ecs.import <CharacterManagerModule>();
ecs.component<Blackboard>();
ecs.component<TownAI>().on_add([](flecs::entity e, TownAI &ai) {
std::lock_guard<std::mutex> lock(ecs_mutex);
ai.mutex = std::make_shared<std::mutex>();
std::lock_guard<std::mutex> lock2(*ai.mutex);
ai.goals.push_back(
{ "HealthGoal",
{ nlohmann::json::object({ { "healthy", 1 } }) } });
ai.goals.push_back(
{ "NotHungryGoal", { { { "hungry", 0 } } } });
ai.goals.push_back(
{ "NotThirstyGoal", { { { "thirsty", 0 } } } });
ai.goals.push_back(
{ "SatisfyToiletNeedGoal", { { { "toilet", 0 } } } });
struct ActionData {
Ogre::String name;
Blackboard prereq;
Blackboard effects;
int cost;
};
struct ActionData actionData[] = {
#if 0
{ "WalkTo",
{ { { "at_object", 0 } } },
{ { { "at_object", 1 } } },
1000 },
{ "Sit#",
{ { { "at_object", 1 }, { "is_seated", 0 } } },
{ { { "is_seated", 1 } } },
10 },
#endif
{ "EatFoodSeated",
{ { { "have_food", 1 },
{ "is_seated", 1 },
{ "hungry", 1 } } },
{ { { "healthy", 1 }, { "hungry", 0 } } },
10 },
{ "EatFood",
{ { { "have_food", 1 }, { "hungry", 1 } } },
{ { { "healthy", 1 }, { "hungry", 0 } } },
2000 },
{ "DrinkWaterSeated",
{ { { "have_water", 1 },
{ "is_seated", 1 },
{ "thirsty", 1 } } },
{ { { "thirsty", 0 } } },
10 },
#if 0
{ "DrinkWater",
{ { { "have_water", 1 }, { "thirsty", 1 } } },
{ { { "thirsty", 0 } } },
2000 },
#endif
{ "EatMedicine",
{ { { "have_medicine", 1 }, { "healty", 0 } } },
{ { { "healthy", 1 } } },
100 },
{ "UseToilet",
{ { { "toilet", 1 } } },
{ { { "toilet", 0 } } },
100 },
{ "GetFood",
{ { { "have_food", 0 } } },
{ { { "have_food", 1 } } },
1000 },
{ "GetWater",
{ { { "have_water", 0 } } },
{ { { "have_water", 1 } } },
1000 },
{ "GetMedicine",
{ { { "have_medicine", 0 } } },
{ { { "have_medicine", 1 } } },
1000 },
};
for (const auto &adata : actionData)
ai.actions.push_back(
OGRE_NEW goap::BaseAction<Blackboard>(
adata.name, adata.prereq, adata.effects,
adata.cost));
ai.planner = std::make_shared<goap::BasePlanner<
Blackboard, goap::BaseAction<Blackboard> > >();
});
ecs.system<TownAI, TownNPCs>("CreateBlackboards")
.kind(flecs::OnUpdate)
.each([this](flecs::entity town, TownAI &ai,
const TownNPCs &npcs) {
Ogre::Root::getSingleton().getWorkQueue()->addTask(
[this, town, npcs, &ai]() {
Ogre::Root::getSingleton()
.getWorkQueue()
->addMainThreadTask([this, town,
npcs,
&ai]() {
std::lock_guard<
std::mutex>
lock(ecs_mutex);
createBlackboards(
town, npcs, ai);
});
});
});
ecs.system<ActionNodeList, TownAI, TownNPCs>("UpdateDynamicActions")
.kind(flecs::OnUpdate)
.each([](flecs::entity e, ActionNodeList &alist, TownAI &ai,
TownNPCs &npcs) {
std::lock_guard<std::mutex> lock(ecs_mutex);
if (ai.nodeActions.size() > 0)
return;
if (alist.dynamicNodes.size() == 0)
ECS::get_mut<ActionNodeList>()
.updateDynamicNodes();
OgreAssert(alist.dynamicNodes.size() > 0,
"bad dynamic nodes");
int nodeIndex;
for (nodeIndex = 0;
nodeIndex < alist.dynamicNodes.size();
nodeIndex++) {
ActionNodeActions aactions(
nodeIndex,
Blackboard(
{ nlohmann::json::object() }),
10);
ai.nodeActions[nodeIndex] =
aactions.getActions();
OgreAssert(ai.nodeActions[nodeIndex].size() > 0,
"bad action count");
}
OgreAssert(ai.nodeActions.size() > 0,
"no dynamic actions?");
});
ecs.system<ActionNodeList, TownAI, TownNPCs>("UpdateDynamicNodes")
.kind(flecs::OnUpdate)
.interval(0.1f)
.each([this](flecs::entity town, ActionNodeList &alist,
TownAI &ai, TownNPCs &npcs) {
std::lock_guard<std::mutex> lock(ecs_mutex);
ECS::get_mut<ActionNodeList>().updateDynamicNodes();
});
ecs.system<TownNPCs>("UpdateNPCPositions")
.kind(flecs::OnUpdate)
.each([](flecs::entity e, TownNPCs &npcs) {
for (auto it = npcs.npcs.begin(); it != npcs.npcs.end();
it++) {
auto &npc = npcs.npcs.at(it->first);
if (npc.e.is_valid() &&
npc.e.has<CharacterBase>())
npc.position =
npc.e.get<CharacterBase>()
.mBodyNode
->_getDerivedPosition();
}
});
ecs.system<ActionNodeList, TownAI, TownNPCs>("UpdateBlackboards")
.kind(flecs::OnUpdate)
.interval(0.1f)
.each([this](flecs::entity town, ActionNodeList &alist,
TownAI &ai, const TownNPCs &npcs) {
for (auto it = npcs.npcs.begin(); it != npcs.npcs.end();
it++) {
const auto &npc = npcs.npcs.at(it->first);
if (ai.blackboards.find(it->first) ==
ai.blackboards.end())
continue;
ai.blackboards.at(it->first).setPosition(
npc.position);
}
Ogre::Root::getSingleton().getWorkQueue()->addTask(
[this, town, &alist, npcs, &ai]() {
{
std::lock_guard<std::mutex> lock(
ecs_mutex);
alist.build();
updateBlackboardsBits(
town, alist, npcs, ai);
updateBlackboards(town, alist,
npcs, ai);
}
Ogre::Root::getSingleton()
.getWorkQueue()
->addMainThreadTask([this, town,
&alist]() {
town.modified<TownAI>();
ECS::modified<
ActionNodeList>();
});
});
});
ecs.system<TownAI, TownNPCs>("PlanAI")
.kind(flecs::OnUpdate)
.interval(1.0f)
.each([&](flecs::entity town, TownAI &ai,
const TownNPCs &npcs) {
Ogre::Root::getSingleton().getWorkQueue()->addTask(
[this, town, npcs, &ai]() {
std::lock_guard<std::mutex> lock(
ecs_mutex);
buildPlans(town, npcs, ai);
Ogre::Root::getSingleton()
.getWorkQueue()
->addMainThreadTask([this,
town]() {
town.modified<TownAI>();
});
});
});
}
void CharacterAIModule::createAI(flecs::entity town)
{
town.add<TownAI>();
}
void CharacterAIModule::buildPlans(flecs::entity town, const TownNPCs &npcs,
TownAI &ai)
{
OgreAssert(town.is_valid(), "Bad town entity");
std::lock_guard<std::mutex> lock(*ai.mutex);
for (auto it = npcs.npcs.begin(); it != npcs.npcs.end(); it++) {
if (ai.blackboards.find(it->first) == ai.blackboards.end())
continue;
auto &bb = ai.blackboards.at(it->first);
/* if there are plans, skip until these get discarded */
if (ai.plans.find(it->first) != ai.plans.end() &&
ai.plans.at(it->first).size() > 0)
continue;
ai.plans[it->first] = {};
for (const auto &goal : ai.goals) {
if (goal.is_reached(bb))
continue;
#if 0
std::cout << "blackboard: "
<< bb.stats.dump(4)
<< std::endl;
std::cout << "goal: "
<< goal.goal.stats.dump(4)
<< std::endl;
#endif
std::vector<goap::BaseAction<Blackboard> *> path;
int actionCount =
ai.blackboards.at(it->first).getActionsCount();
auto actionsData =
ai.blackboards.at(it->first).getActionsData();
path.resize(actionCount * actionCount);
int path_length = ai.planner->plan(
bb, goal, actionsData, actionCount, path.data(),
path.size());
#if 0
std::cout << "Actions: " << std::endl;
for (auto &action : actions) {
std::cout << "name: "
<< action->get_name()
<< std::endl;
std::cout
<< "\tprereq:\n"
<< action->prereq.stats
.dump(4)
<< std::endl;
std::cout
<< "\teffects:\n"
<< action->effects.stats
.dump(4)
<< std::endl;
}
#endif
#if 1
std::cout << bb.index << " ";
std::cout << "Goal: " << goal.get_name();
std::cout << std::endl;
std::cout << "Path: ";
int count = 0;
if (path_length < 0) {
std::cout << "Bad plan " << path_length
<< std::endl;
}
for (auto &action : path) {
if (count >= path_length)
break;
OgreAssert(action, "No action");
std::cout << action->get_name();
if (count < path_length - 1)
std::cout << ", ";
count++;
}
std::cout << std::endl;
std::cout << path_length << std::endl;
// OgreAssert(path_length == 0,
// "planning");
#endif
if (path_length > 0) {
struct TownAI::Plan plan;
plan.goal = &goal;
plan.plan.insert(plan.plan.end(), path.begin(),
path.begin() + path_length);
ai.plans[it->first].push_back(plan);
}
}
}
}
void CharacterAIModule::createBlackboards(flecs::entity town,
const TownNPCs &npcs, TownAI &ai)
{
OgreAssert(town.is_valid(), "Bad town entity");
std::lock_guard<std::mutex> lock(*ai.mutex);
for (auto it = npcs.npcs.begin(); it != npcs.npcs.end(); it++) {
if (ai.blackboards.find(it->first) == ai.blackboards.end()) {
int strength = 10;
int dexterity = 10;
int health = 100;
int stamina = 100;
int sex = it->second.props["sex"].get<int>();
if (sex == 0) { // male
strength += 10;
dexterity += 10;
}
nlohmann::json bb;
bb["strength"] = strength;
bb["dexterity"] = dexterity;
bb["health"] = health;
bb["stamina"] = stamina;
bb["needs_hunger"] = 0;
bb["needs_thirst"] = 0;
bb["needs_toilet"] = 0;
bb["have_water"] = 0;
bb["have_food"] = 0;
bb["have_medicine"] = 0;
bb["at_object"] = 0;
ai.blackboards[it->first] = Blackboard(bb);
ai.blackboards[it->first].index = it->first;
ai.blackboards.at(it->first).town = town;
// FIXME: do this once
ai.blackboards.at(it->first).actionRefResize(0);
ai.blackboards.at(it->first).actionRefAddActions(
ai.actions);
}
}
}
void CharacterAIModule::updateBlackboardsBits(flecs::entity town,
ActionNodeList &alist,
const TownNPCs &npcs, TownAI &ai)
{
OgreAssert(town.is_valid(), "Bad town entity");
std::lock_guard<std::mutex> lock(*ai.mutex);
struct UpdateBit {
Ogre::String checkValue;
int recover;
int minValue;
int maxValue;
int threshold;
Ogre::String writeValue;
};
struct UpdateBit updateBits[] = {
{ "health", 0, 0, 100, 20, "healthy" },
{ "needs_hunger", 1, 0, 10000, 2000, "hungry" },
{ "needs_thirst", 1, 0, 10000, 1000, "thirsty" },
{ "needs_toilet", 1, 0, 10000, 1500, "toilet" }
};
for (auto it = npcs.npcs.begin(); it != npcs.npcs.end(); it++) {
if (ai.blackboards.find(it->first) == ai.blackboards.end())
continue;
auto &stats = ai.blackboards.at(it->first).stats;
auto &object = ai.blackboards.at(it->first).object;
ai.blackboards.at(it->first).index = it->first;
ai.blackboards.at(it->first).town = town;
for (const auto &bits : updateBits) {
int value = stats[bits.checkValue].get<int>();
int maxValue = bits.maxValue;
int minValue = bits.minValue;
int threshold = bits.threshold;
if (it->second.props.find(bits.checkValue + "_max") !=
it->second.props.end())
maxValue =
it->second
.props[bits.checkValue + "_max"]
.get<int>();
if (it->second.props.find(bits.checkValue +
"_threshold") !=
it->second.props.end())
threshold = it->second
.props[bits.checkValue +
"_threshold"]
.get<int>();
value += bits.recover;
if (value > maxValue)
value = maxValue;
if (value >= threshold)
stats[bits.writeValue] = 1;
else
stats[bits.writeValue] = 0;
if (value < bits.minValue)
value = bits.minValue;
stats[bits.checkValue] = value;
}
}
}
void CharacterAIModule::updateBlackboards(flecs::entity town,
const ActionNodeList &alist,
const TownNPCs &npcs, TownAI &ai)
{
std::lock_guard<std::mutex> lock(*ai.mutex);
OgreAssert(town.is_valid(), "Bad town entity");
for (auto it = npcs.npcs.begin(); it != npcs.npcs.end(); it++) {
if (ai.blackboards.find(it->first) == ai.blackboards.end())
continue;
auto &stats = ai.blackboards.at(it->first).stats;
auto &object = ai.blackboards.at(it->first).object;
ai.blackboards.at(it->first).index = it->first;
ai.blackboards.at(it->first).town = town;
auto &bb = ai.blackboards.at(it->first);
bb.query_ai();
#if 0
OgreAssert(nodeActionCount > 0 ||
points.size() == 0,
"no node actions and no points");
if (nodeActionCount == 0) {
std::cout << "nodes:"
<< alist.nodes.size() << " "
<< alist.dynamicNodes.size()
<< std::endl;
std::cout << "points: " << points.size()
<< std::endl;
std::cout << "position: " << position
<< std::endl;
}
OgreAssert(nodeActionCount > 0,
"no node actions");
#endif
bb.fixupBooleanKeys();
}
}
void Blackboard::_actionRefResize(int count)
{
if (count >= actionRef.size()) {
int allocate = count;
if (allocate < 1000)
allocate = 1000;
actionRef.resize(allocate);
}
OgreAssert(count < actionRef.size(), "out of memory");
actionRefCount = count;
actionRefPtr = count;
}
Blackboard::Blackboard()
: stats(nlohmann::json::object())
, object(-1)
, index(-1)
, actionRefCount(0)
, mutex(std::make_shared<std::mutex>())
{
}
Blackboard::Blackboard(const nlohmann::json &stats)
: Blackboard()
{
this->stats = stats;
}
bool Blackboard::operator==(const Blackboard &other) const
{
return is_satisfied_by(this->stats, other.stats);
}
bool Blackboard::operator!=(const Blackboard &other) const
{
return !(*this == other);
}
void Blackboard::apply(const Blackboard &other)
{
std::lock_guard<std::mutex> lock(*mutex);
stats.update(other.stats);
}
Ogre::String Blackboard::dumpActions()
{
std::lock_guard<std::mutex> lock(*mutex);
Ogre::String ret;
ret += "Actions:\n";
int count = 0;
for (count = 0; count < actionRefCount; count++) {
auto &action = actionRef[count];
ret += "name: " + action->get_name() + "\n";
ret += "\tprereq:\n" + action->prereq.stats.dump(4) + "\n";
ret += "\teffects:\n" + action->effects.stats.dump(4) + "\n";
}
return ret;
}
void Blackboard::printActions()
{
std::cout << dumpActions() << std::endl;
}
void Blackboard::fixupBooleanKeys()
{
std::lock_guard<std::mutex> lock(*mutex);
int count;
for (count = 0; count < actionRefCount; count++) {
auto &action = actionRef[count];
const nlohmann::json &prereq = action->prereq.stats;
const nlohmann::json &effects = action->effects.stats;
for (auto it = prereq.begin(); it != prereq.end(); it++)
if (stats.find(it.key()) == stats.end())
stats[it.key()] = 0;
for (auto it = effects.begin(); it != effects.end(); it++)
if (stats.find(it.key()) == stats.end())
stats[it.key()] = 0;
}
}
void Blackboard::actionRefResize(int count)
{
std::lock_guard<std::mutex> lock(*mutex);
_actionRefResize(count);
}
void Blackboard::actionRefAddAction(goap::BaseAction<Blackboard> *action)
{
std::lock_guard<std::mutex> lock(*mutex);
if (actionRef.size() <= actionRefCount + 16)
actionRef.resize(actionRefCount + 32);
OgreAssert(actionRefPtr < actionRef.size(), "out of memory");
OgreAssert(action, "bad action");
actionRef[actionRefPtr++] = action;
actionRefCount++;
}
void Blackboard::actionRefAddActions(
const std::vector<goap::BaseAction<Blackboard> *> &actions)
{
std::lock_guard<std::mutex> lock(*mutex);
_actionRefAddActions(actions);
}
void Blackboard::_actionRefAddActions(
const std::vector<goap::BaseAction<Blackboard> *> &actions)
{
if (actionRef.size() <= actionRefCount + actions.size())
actionRef.resize(actionRefCount + actions.size() * 2);
for (const auto &action : actions) {
OgreAssert(actionRefPtr < actionRef.size(), "out of memory");
OgreAssert(action, "bad action");
actionRef[actionRefPtr++] = action;
actionRefCount++;
}
}
void Blackboard::actionRefAddActions(goap::BaseAction<Blackboard> **actions,
int count)
{
int i;
std::lock_guard<std::mutex> lock(*mutex);
if (actionRef.size() <= actionRefCount + count)
actionRef.resize(actionRefCount + count * 2);
for (i = 0; i < count; i++) {
OgreAssert(actionRefPtr < actionRef.size(), "out of memory");
OgreAssert(actions[i], "bad action");
actionRef[actionRefPtr++] = actions[i];
actionRefCount++;
}
}
struct ComparePair {
const nlohmann::json &current;
const nlohmann::json &target;
};
bool Blackboard::is_satisfied_by(const nlohmann::json &current,
const nlohmann::json &target, float epsilon)
{
std::deque<ComparePair> queue;
queue.push_back({ current, target });
while (!queue.empty()) {
ComparePair pair = queue.front();
queue.pop_front();
const nlohmann::json &curr = pair.current;
const nlohmann::json &tgt = pair.target;
if (curr.type() != tgt.type() &&
!(curr.is_number() && tgt.is_number()))
return false;
if (tgt.is_object())
for (auto it = tgt.begin(); it != tgt.end(); ++it) {
auto found = curr.find(it.key());
if (found == curr.end())
return false;
queue.push_back({ *found, it.value() });
}
else if (tgt.is_array()) {
if (curr.size() != tgt.size())
return false;
for (int i = 0; i < tgt.size(); ++i)
queue.push_back({ curr[i], tgt[i] });
} else if (tgt.is_number_float() || curr.is_number_float()) {
if (std::abs(curr.get<float>() - tgt.get<float>()) >=
epsilon)
return false;
} else if (curr != tgt)
return false;
}
return true;
}
int Blackboard::distance_to(const Blackboard &goal) const
{
int distance = 0;
OgreAssert(goal.stats.is_object(),
"Not an object:\n" + goal.stats.dump(4));
for (auto it = goal.stats.begin(); it != goal.stats.end(); ++it) {
const std::string &key = it.key();
const auto &goalVal = it.value();
// If current state doesn't have the key, treat it as a maximum difference
if (stats.find(key) == stats.end()) {
distance += 100; // Example: High cost for missing state
continue;
}
const auto &currentVal = stats[key];
if (goalVal.is_number() && currentVal.is_number()) {
// Add numerical difference
distance += std::abs(goalVal.get<float>() -
currentVal.get<float>());
} else {
// Check non-numeric equality
if (goalVal != currentVal) {
distance += 1; // Penalty for mismatch
}
}
}
return distance;
}
void Blackboard::setPosition(const Ogre::Vector3 &position)
{
std::lock_guard<std::mutex> lock(*mutex);
this->position = position;
}
void Blackboard::query_ai()
{
std::lock_guard<std::mutex> lock(*mutex);
TownAI &ai = town.get_mut<TownAI>();
const TownNPCs &npcs = town.get<TownNPCs>();
const float distance = 10000.0f;
Ogre::Vector3 position(0, 0, 0);
if (npcs.npcs.at(index).e.is_valid())
position = npcs.npcs.at(index)
.e.get<CharacterBase>()
.mBodyNode->_getDerivedPosition();
else
from_json(npcs.npcs.at(index).props["position"], position);
this->position = position;
ActionNodeList &alist = ECS::get_mut<ActionNodeList>();
alist.query_ai(position, distance, points, distances);
_actionRefResize(ai.actions.size());
int actionRefIndex = ai.actions.size();
int nodeActionCount = 0;
for (size_t point : points) {
Ogre::Vector3 &p = alist.dynamicNodes[point].position;
float radius = alist.dynamicNodes[point].radius;
float distance = p.squaredDistance(position);
if (object >= 0 && (size_t)object == point &&
distance > radius * radius) {
object = -1;
stats["at_object"] = 0;
} else if (object >= 0 && (size_t)object == point &&
distance <= radius * radius)
stats["at_object"] = 1;
/* some nodes do not have usable actions */
if (ai.nodeActions[point].size() > 0) {
OgreAssert(ai.nodeActions[point].size() > 0,
"bad node actions count " +
alist.dynamicNodes[point].props.dump(
4));
_actionRefAddActions(ai.nodeActions[point]);
nodeActionCount += ai.nodeActions[point].size();
}
nlohmann::json nodes = nlohmann::json::array();
nlohmann::json j = alist.dynamicNodes[point].props;
j["global_position_x"] = p.x;
j["global_position_y"] = p.y;
j["global_position_z"] = p.z;
nodes.push_back(j);
stats["nodes"] = nodes;
}
}
}

View File

@@ -0,0 +1,101 @@
#ifndef CHARACTERAIMODULE_H
#define CHARACTERAIMODULE_H
#include <vector>
#include <flecs.h>
#include <nlohmann/json.hpp>
#include "goap.h"
namespace ECS
{
struct Blackboard {
nlohmann::json stats;
int object;
int index;
flecs::entity town;
std::shared_ptr<std::mutex> mutex;
private:
std::vector<goap::BaseAction<Blackboard> *> actionRef;
int actionRefCount;
int actionRefPtr;
Ogre::Vector3 position;
std::vector<size_t> points;
std::vector<float> distances;
void _actionRefResize(int count);
void _actionRefAddActions(
const std::vector<goap::BaseAction<Blackboard> *> &actions);
public:
Blackboard();
Blackboard(const nlohmann::json &stats);
bool operator==(const Blackboard &other) const;
bool operator!=(const Blackboard &other) const;
void apply(const Blackboard &other);
Ogre::String dumpActions();
void printActions();
void fixupBooleanKeys();
void actionRefResize(int count);
void actionRefAddAction(goap::BaseAction<Blackboard> *action);
void actionRefAddActions(
const std::vector<goap::BaseAction<Blackboard> *> &actions);
void actionRefAddActions(goap::BaseAction<Blackboard> **actions,
int count);
goap::BaseAction<Blackboard> **getActionsData()
{
return actionRef.data();
}
int getActionsCount()
{
return actionRefCount;
}
private:
static bool is_satisfied_by(const nlohmann::json &current,
const nlohmann::json &target,
float epsilon = 1e-4);
public:
int distance_to(const Blackboard &goal) const;
void setPosition(const Ogre::Vector3 &position);
const Ogre::Vector3 &getPosition() const
{
return position;
}
void query_ai();
};
struct TownNPCs;
struct ActionNodeList;
struct TownAI {
std::shared_ptr<std::mutex> mutex;
std::vector<goap::BasePlanner<Blackboard,
goap::BaseAction<Blackboard> >::BaseGoal>
goals;
std::vector<goap::BaseAction<Blackboard> *> actions;
std::shared_ptr<
goap::BasePlanner<Blackboard, goap::BaseAction<Blackboard> > >
planner;
std::unordered_map<int, Blackboard> blackboards;
struct Plan {
const goap::BasePlanner<Blackboard,
goap::BaseAction<Blackboard> >::BaseGoal *goal;
std::vector<goap::BaseAction<Blackboard> *> plan;
};
std::unordered_map<int, std::vector<struct Plan> > plans;
std::unordered_map<int, std::vector<goap::BaseAction<Blackboard> *> > nodeActions;
};
struct CharacterAIModule {
CharacterAIModule(flecs::world &ecs);
void createAI(flecs::entity town);
void buildPlans(flecs::entity town, const TownNPCs &npcs, TownAI &ai);
void createBlackboards(flecs::entity town, const TownNPCs &npcs, TownAI &ai);
void updateBlackboardsBits(flecs::entity town, ActionNodeList &alist, const TownNPCs &npcs, TownAI &ai);
void updateBlackboards(flecs::entity town, const ActionNodeList &alist, const TownNPCs &npcs, TownAI &ai);
};
}
#endif // CHARACTERAIMODULE_H

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,191 @@
#include "Components.h"
#include "CharacterModule.h"
#include "CharacterAnimationModule.h"
#include "StaticGeometryModule.h"
#include "PhysicsModule.h"
#include "PlayerActionModule.h"
#include "items.h"
#include "CharacterManagerModule.h"
namespace ECS
{
void createNPCActionNodes(flecs::entity town, int index)
{
TownNPCs &npcs = town.get_mut<TownNPCs>();
TownNPCs::NPCData &npc = npcs.npcs.at(index);
flecs::entity e = npc.e;
nlohmann::json npcprops = npc.props;
const CharacterBase &ch = e.get<CharacterBase>();
Ogre::Vector3 characterPos = ch.mBodyNode->_getDerivedPosition();
Ogre::Quaternion characterRot = ch.mBodyNode->_getDerivedOrientation();
if (npc.actionNodes.size() > 0) {
int i;
for (i = 0; i < npc.actionNodes.size(); i++) {
auto &anode = npc.actionNodes[i];
Ogre::Vector3 offset = Ogre::Vector3::UNIT_Z * 0.3f +
Ogre::Vector3::UNIT_Y;
if (i == 1)
offset = Ogre::Vector3::NEGATIVE_UNIT_Z * 0.3f +
Ogre::Vector3::UNIT_Y;
anode.position = characterPos + characterRot * offset;
anode.rotation = characterRot;
to_json(anode.props["position"], anode.position);
to_json(anode.props["rotation"], anode.rotation);
}
return;
}
{
ActionNodeList::ActionNode anode;
anode.props["action"] = "talk";
anode.props["action_text"] = "Talk";
anode.action = "talk";
anode.action_text = "Talk";
Ogre::Vector3 offset = Ogre::Vector3::UNIT_Z * 0.25f +
Ogre::Vector3::UNIT_Y * 1.5f;
anode.position = characterPos + characterRot * offset;
anode.rotation = characterRot;
anode.radius = 0.6f;
anode.height = 1.0f;
to_json(anode.props["position"], anode.position);
to_json(anode.props["rotation"], anode.rotation);
anode.props["radius"] = anode.radius;
anode.props["height"] = anode.height;
anode.props["town"] = town.id();
anode.props["index"] = index;
anode.props["npc"] = npcprops;
npc.actionNodes.push_back(anode);
}
{
ActionNodeList::ActionNode anode;
anode.props["action"] = "action";
anode.props["action_text"] = "Action";
anode.action = "action";
anode.action_text = "Action";
Ogre::Vector3 offset = Ogre::Vector3::NEGATIVE_UNIT_Z * 0.2f +
Ogre::Vector3::UNIT_Y;
anode.position = characterPos + characterRot * offset;
anode.rotation = characterRot;
anode.radius = 0.3f;
anode.height = 1.0f;
to_json(anode.props["position"], anode.position);
to_json(anode.props["rotation"], anode.rotation);
anode.props["radius"] = anode.radius;
anode.props["height"] = anode.height;
anode.props["town"] = town.id();
anode.props["index"] = index;
anode.props["npc"] = npcprops;
npc.actionNodes.push_back(anode);
}
}
CharacterManagerModule::CharacterManagerModule(flecs::world &ecs)
{
ecs.module<CharacterManagerModule>();
ecs.import <CharacterModule>();
ecs.import <CharacterAnimationModule>();
ecs.import <PhysicsModule>();
ecs.import <PlayerActionModule>();
ecs.component<TownCharacterHolder>();
ecs.component<TownNPCs>();
ecs.component<LivesIn>();
ecs.system<TerrainItem, TownNPCs>("UpdateCharacters")
.kind(flecs::OnUpdate)
.interval(1.0f)
.write<CharacterBase>()
.write<CharacterLocation>()
.write<CharacterConf>()
.write<Character>()
.write<LivesIn>()
.each([this](flecs::entity town, TerrainItem &item,
TownNPCs &npcs) {
if (!player.is_valid())
return;
if (!player.has<CharacterBase>())
return;
Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask([this,
town]() {
flecs::entity player =
ECS::get<CharacterManagerModule>()
.getPlayer();
if (!player.is_valid())
return;
if (!player.has<CharacterBase>())
return;
TownNPCs &npcs = town.get_mut<TownNPCs>();
Ogre::Vector3 cameraPos =
player.get<CharacterBase>()
.mBodyNode
->_getDerivedPosition();
for (auto &npc : npcs.npcs) {
int index = npc.first;
TownNPCs::NPCData &data = npc.second;
Ogre::Vector3 npcPosition =
data.position;
Ogre::Quaternion npcOrientation =
data.orientation;
if (cameraPos.squaredDistance(
npcPosition) < 10000.0f) {
if (!data.e.is_valid()) {
data.e = createCharacterData(
data.model,
data.position,
data.orientation);
data.e.add<LivesIn>(
town);
break;
}
}
if (cameraPos.squaredDistance(
npcPosition) > 22500.0f) {
if (data.e.is_valid()) {
data.e.destruct();
data.e =
flecs::entity();
break;
}
}
}
for (auto &npc : npcs.npcs) {
int index = npc.first;
TownNPCs::NPCData &data = npc.second;
Ogre::Vector3 npcPosition =
data.position;
Ogre::Quaternion npcOrientation =
data.orientation;
if (cameraPos.squaredDistance(
npcPosition) < 10000.0f) {
if (data.e.is_valid()) {
if (data.e.has<
CharacterBase>() &&
data.e.has<LivesIn>(
town))
createNPCActionNodes(
town,
index);
}
}
}
town.modified<TownNPCs>();
});
});
}
flecs::entity
CharacterManagerModule::createPlayer(const Ogre::Vector3 &position,
const Ogre::Quaternion &rotation)
{
static int count = 0;
OgreAssert(count == 0, "overspawn");
OgreAssert(!player.is_valid(), "Player already created");
player = ECS::get().entity("player");
Ogre::Vector3 playerPos(0, 0, 4);
player.set<CharacterLocation>({ rotation, position });
player.set<CharacterConf>({ "normal-male.glb" });
player.add<Character>();
player.add<Player>();
OgreAssert(player.is_valid(), "Can't create player");
std::cout << "Begin player create" << std::endl;
player.add<Player>();
ECS::get_mut<CharacterModule>().createCharacter(
player, position, rotation, "normal-male.glb");
ECS::modified<CharacterModule>();
std::cout << "End player create" << std::endl;
count++;
return player;
}
flecs::entity
@@ -31,12 +196,46 @@ CharacterManagerModule::createCharacterData(const Ogre::String model,
const Ogre::Vector3 &position,
const Ogre::Quaternion &rotation)
{
flecs::entity e =
ECS::get()
.entity()
.set<CharacterLocation>({ rotation, position })
.set<CharacterConf>({ model })
.add<Character>();
return e;
flecs::entity e = ECS::get().entity();
ECS::get_mut<CharacterModule>().createCharacter(e, position, rotation,
model);
ECS::modified<CharacterModule>();
return e;
}
void CharacterManagerModule::registerTownCharacters(flecs::entity town)
{
Ogre::MeshManager::getSingleton().load("normal-male.glb", "General");
Ogre::MeshManager::getSingleton().load("normal-female.glb", "General");
Ogre::String props = StaticGeometryModule::getItemProperties(town);
nlohmann::json j = nlohmann::json::parse(props);
nlohmann::json npcs = nlohmann::json::array();
if (town.has<TownNPCs>())
return;
if (j.find("npcs") != j.end())
npcs = j["npcs"];
std::cout << npcs.dump(4) << std::endl;
int index = 0;
std::map<int, TownNPCs::NPCData> npcMap;
for (auto &npc : npcs) {
const char *models[] = { "normal-male.glb",
"normal-female.glb" };
int sex = npc["sex"].get<int>();
Ogre::Vector3 npcPosition;
Ogre::Quaternion npcOrientation;
from_json(npc["position"], npcPosition);
from_json(npc["orientation"], npcOrientation);
Ogre::String model = models[sex];
TownNPCs::NPCData npcData;
npcData.e = flecs::entity();
npcData.model = model;
npcData.orientation = npcOrientation;
npcData.position = npcPosition;
npcData.props = npc;
npcMap[index] = npcData;
index++;
}
town.set<TownNPCs>({ npcMap });
}
}
}

View File

@@ -1,10 +1,27 @@
#ifndef _CHARACTER_MANAGER_MODULE_
#define _CHARACTER_MANAGER_MODULE_
#include <flecs.h>
#include <nlohmann/json.hpp>
#include "PlayerActionModule.h"
namespace ECS
{
struct TownCharacterHolder{int index;};
struct TownNPCs {
struct NPCData {
flecs::entity e;
nlohmann::json props;
Ogre::Vector3 position;
Ogre::Quaternion orientation;
Ogre::String model;
std::vector<ActionNodeList::ActionNode> actionNodes;
};
std::map<int, NPCData> npcs;
};
struct LivesIn {};
struct CharacterManagerModule {
std::set<flecs::entity> characters;
flecs::entity player;
CharacterManagerModule(flecs::world &ecs);
flecs::entity createPlayer(const Ogre::Vector3 &position,
const Ogre::Quaternion &rotation);
@@ -12,6 +29,14 @@ struct CharacterManagerModule {
const Ogre::Vector3 &position,
const Ogre::Quaternion &rotation);
void removeCharacterData(int id);
flecs::entity getPlayer() const
{
return player;
}
void registerTownCharacters(flecs::entity town);
void setTownCharacter(flecs::entity town, int index, bool enable);
CharacterManagerModule(CharacterManagerModule &&) = delete;
CharacterManagerModule &operator=(CharacterManagerModule&&) = delete;
};
}
#endif
#endif

View File

@@ -5,9 +5,10 @@
#include "TerrainModule.h"
#include "Components.h"
#include "PhysicsModule.h"
#include "physics.h"
#include "CharacterAnimationModule.h"
#include "CharacterManagerModule.h"
#include "CharacterModule.h"
#include "goap.h"
namespace ECS
{
CharacterModule::CharacterModule(flecs::world &ecs)
@@ -24,12 +25,9 @@ CharacterModule::CharacterModule(flecs::world &ecs)
ecs.component<CharacterDisablePhysics>();
ecs.component<CharacterUpdatePhysicsState>();
ecs.component<CharacterInActuator>();
ecs.component<Blackboard>();
ecs.component<ActionTarget>();
ecs.component<Plan>();
ecs.component<Male>();
ecs.component<Female>();
ecs.component<Planner>().add(flecs::Singleton);
ecs.import <CharacterAnimationModule>();
ecs.import <TerrainModule>();
ecs.import <WaterModule>();
@@ -41,8 +39,10 @@ CharacterModule::CharacterModule(flecs::world &ecs)
ecs.system<Input, Camera>("HandleInput")
.kind(flecs::OnUpdate)
.each([this](Input &input, Camera &camera) {
if (!ECS::player.is_valid())
return;
flecs::entity player =
ECS::get<CharacterManagerModule>().getPlayer();
if (!player.is_valid())
return;
/* handle input */
// if (input.control == input.control_prev)
// return;
@@ -263,38 +263,31 @@ CharacterModule::CharacterModule(flecs::world &ecs)
}
});
#endif
ecs.observer<const EngineData, const CharacterLocation,
const CharacterConf>("SetupCharacterM")
.event(flecs::OnSet)
.with<Character>()
.without<CharacterBase>()
.each([](flecs::entity e, const EngineData &eng,
const CharacterLocation &loc,
const CharacterConf &conf) {
CharacterBase &ch = e.ensure<CharacterBase>();
AnimationControl &anim = e.ensure<AnimationControl>();
ch.mBodyEnt = eng.mScnMgr->createEntity(conf.type);
ch.mBodyNode = eng.mScnMgr->getRootSceneNode()
->createChildSceneNode();
ch.mBodyNode->setOrientation(loc.orientation);
ch.mBodyNode->setPosition(loc.position);
ch.mBodyNode->attachObject(ch.mBodyEnt);
ch.mSkeleton = ch.mBodyEnt->getSkeleton();
OgreAssert(ch.mBodyEnt->getSkeleton()->hasBone("Root"),
"No root bone");
OgreAssert(ch.mSkeleton->hasBone("Root"),
"No root bone");
ch.mRootBone = ch.mSkeleton->getBone("Root");
OgreAssert(ch.mRootBone, "No root bone");
// body.mController = nullptr;
ch.mBoneMotion = Ogre::Vector3::ZERO;
ch.mBonePrevMotion = Ogre::Vector3::ZERO;
e.set<CharacterVelocity>(
{ { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } });
e.add<CharacterGravity>();
e.add<CharacterBuoyancy>();
anim.configured = false;
});
static int characterCount = 0;
ecs.observer<const EngineData, const CharacterLocation,
const CharacterConf, CharacterBase>(
"SetupCharacterBaseObs")
.event(flecs::OnAdd)
.each([&](flecs::entity e, const EngineData &eng,
const CharacterLocation &loc,
const CharacterConf &conf, CharacterBase &ch) {
ch.mBodyEnt = eng.mScnMgr->createEntity(conf.type);
ch.mBodyNode = eng.mScnMgr->getRootSceneNode()
->createChildSceneNode();
ch.mBodyNode->setOrientation(loc.orientation);
ch.mBodyNode->setPosition(loc.position);
ch.mBodyNode->attachObject(ch.mBodyEnt);
OgreAssert(ch.mBodyEnt->getSkeleton()->hasBone("Root"),
"No root bone");
ch.mBoneMotion = Ogre::Vector3::ZERO;
ch.mBonePrevMotion = Ogre::Vector3::ZERO;
});
ecs.observer<AnimationControl>("SetupAnimationControlObs")
.event(flecs::OnAdd)
.each([](flecs::entity e, AnimationControl &anim) {
anim.configured = false;
});
#if 0
ecs.system<const EngineData, const CharacterLocation,
const CharacterConf, Body2Entity>("SetupCharacter")
@@ -557,6 +550,7 @@ void CharacterModule::updateCameraGoal(Camera &camera, Ogre::Real deltaYaw,
Ogre::Real deltaPitch,
Ogre::Real deltaZoom)
{
static float canonDist = 0;
camera.mCameraPivot->yaw(Ogre::Degree(deltaYaw), Ogre::Node::TS_PARENT);
if (!(camera.mPivotPitch + deltaPitch > 25 && deltaPitch > 0) &&
!(camera.mPivotPitch + deltaPitch < -60 && deltaPitch < 0)) {
@@ -569,187 +563,62 @@ void CharacterModule::updateCameraGoal(Camera &camera, Ogre::Real deltaYaw,
Ogre::Real distChange = deltaZoom * dist;
// bound the zoom
if (!(dist + distChange < 8 && distChange < 0) &&
!(dist + distChange > 25 && distChange > 0))
if (!(dist + distChange < 1.5f && distChange < 0) &&
!(dist + distChange > 10 && distChange > 0)) {
camera.mCameraGoal->translate(0, 0, distChange,
Ogre::Node::TS_LOCAL);
}
CharacterAIModule::CharacterAIModule(flecs::world &ecs)
{
ecs.module<CharacterAIModule>();
ecs.system<Blackboard>("UpdateCharacters")
.kind(flecs::OnUpdate)
.each([&](flecs::entity e, Blackboard &bb) {
bb.flags &=
~(Blackboard::LOW_HEALTH |
Blackboard::FULL_HEALTH |
Blackboard::LOW_STAMINA |
Blackboard::FULL_STAMINA |
Blackboard::LOW_LUST | Blackboard::HIGH_LUST |
Blackboard::FULL_LUST);
if (bb.health < 5)
bb.flags |= Blackboard::LOW_HEALTH;
else if (bb.health >= 100)
bb.flags |= Blackboard::FULL_HEALTH;
if (bb.stamina < 5)
bb.flags |= Blackboard::LOW_STAMINA;
if (bb.stamina >= 100)
bb.flags |= Blackboard::FULL_STAMINA;
if (bb.lust >= 100)
bb.flags |= Blackboard::FULL_LUST;
if (bb.lust > 10)
bb.flags |= Blackboard::HIGH_LUST;
if (bb.lust < 5)
bb.flags |= Blackboard::LOW_LUST;
if (bb.stamina < 0)
bb.stamina = 0;
else if (bb.stamina > 100)
bb.stamina = 100;
if (bb.lust < 0)
bb.lust = 0;
else if (bb.lust > 100)
bb.lust = 100;
});
ecs.system<Blackboard, Plan>("UpdateCharactersPlan2")
.kind(flecs::OnUpdate)
.each([&](flecs::entity e, Blackboard &bb, Plan &p) {
int i;
bool ok_plan = true;
canonDist += distChange;
}
JPH::BodyID id;
Ogre::Vector3 position;
Ogre::Vector3 d = (camera.mCameraPivot->_getDerivedPosition() -
camera.mCameraGoal->_getDerivedPosition())
.normalisedCopy();
for (i = p.position; i < p.length; i++) {
if (!p.actions[i]->can_run(bb, true)) {
ok_plan = false;
break;
}
int ret = p.actions[i]->execute(bb);
p.position = i;
if (ret == BaseAction::BUSY)
break;
else if (ret == BaseAction::ABORT) {
ok_plan = false;
break;
}
if (ret == BaseAction::OK && i == p.length - 1)
ok_plan = false;
// stop_this = true;
}
if (!ok_plan) {
std::cout << e.name() << ": invalidated plan"
<< " step: " << i << std::endl;
for (i = 0; i < p.length; i++)
p.actions[i]->stop(bb);
e.remove<Plan>();
}
});
ecs.system<Blackboard, Planner>("UpdateCharacters2")
.kind(flecs::OnUpdate)
.without<Plan>()
.each([&](flecs::entity e, Blackboard &bb, Planner &planner) {
int i;
std::vector<BaseAction *> actions;
actions.insert(actions.end(), planner.actions.begin(),
planner.actions.end());
e.world()
.query_builder<const ActionTarget>()
.build()
.each([&](flecs::entity me,
const ActionTarget &t) {
actions.insert(actions.end(),
t.actions.begin(),
t.actions.end());
#if 0
auto it = t.actions.begin();
while (it != t.actions.end()) {
if (me != bb.me)
actions.push_back(*it);
// std::cout << (*it)->get_name()
// << std::endl;
it++;
}
#endif
});
#if 0
for (i = 0; i < actions.size(); i++)
std::cout << "action: " << i << " "
<< actions[i]->get_name()
<< std::endl;
#endif
if (actions.size() == 0)
return;
int len = -1;
OgreAssert(
bb.stamina < 100 ||
bb.stamina >= 100 &&
!bb.get_flag(
Blackboard::LOW_STAMINA),
"bad thing");
for (i = 0; i < planner.goals.size(); i++) {
if (planner.goals[i]->distance_to(bb) > 1000000)
continue;
len = planner.planner.plan(
bb, *planner.goals[i], actions.data(),
actions.size(), planner.path.data(),
100);
std::cout << bb.me.name() << ": goal: " << len
<< " " << planner.goals[i]->get_name()
<< std::endl;
if (len > 0)
break;
}
// std::cout << "plan length: " << len << std::endl;
#if 0
if (len > 0)
stop_this = true;
if (len < 0)
stop_this = true;
#endif
if (len > 0) {
Plan &p = e.ensure<Plan>();
p.actions = planner.path;
p.position = 0;
p.length = len;
for (i = 0; i < len; i++)
std::cout
<< i << " "
<< planner.path[i]->get_name()
<< " "
<< planner.path[i]->get_cost(bb)
<< std::endl;
bool ok_plan = true;
for (i = 0; i < len; i++) {
if (!planner.path[i]->can_run(bb,
true)) {
ok_plan = false;
break;
}
int ret = planner.path[i]->execute(bb);
p.position = i;
std::cout << "exec: " << i << " "
<< planner.path[i]->get_name()
<< std::endl;
if (ret == BaseAction::BUSY)
break;
else if (ret == BaseAction::ABORT) {
ok_plan = false;
} else if (ret == BaseAction::OK)
std::cout
<< "exec: complete "
<< i << " "
<< planner.path[i]
->get_name()
<< std::endl;
}
e.modified<Plan>();
if (!ok_plan) {
std::cout << e.name()
<< ": invalidate plan"
<< " step: " << i
<< std::endl;
for (i = 0; i < len; i++)
planner.path[i]->stop(bb);
e.remove<Plan>();
}
}
});
if (JoltPhysicsWrapper::getSingleton().raycastQuery(
camera.mCameraPivot->_getDerivedPosition(),
camera.mCameraGoal->_getDerivedPosition() - d * 0.6,
position, id)) {
float l = camera.mCameraPivot->_getDerivedPosition()
.squaredDistance(
camera.mCameraGoal
->_getDerivedPosition());
float m = camera.mCameraPivot->_getDerivedPosition()
.squaredDistance(position);
if (m < l)
camera.mCameraGoal->_setDerivedPosition(position +
d * 0.6f);
} else {
Ogre::Real dist2 =
camera.mCameraGoal->_getDerivedPosition().distance(
camera.mCameraPivot->_getDerivedPosition());
if (deltaZoom < 0.0f || deltaZoom > 0.0f)
canonDist = dist2;
else {
if (canonDist < dist2)
canonDist = dist2;
if (dist2 < canonDist)
camera.mCameraGoal->translate(
0, 0, 0.08f, Ogre::Node::TS_LOCAL);
}
}
}
void CharacterModule::createCharacter(flecs::entity e,
const Ogre::Vector3 &position,
const Ogre::Quaternion &rotation,
const Ogre::String model)
{
if (e.has<CharacterBase>() || e.has<AnimationControl>())
return;
e.set<CharacterLocation>({ rotation, position });
e.set<CharacterConf>({ model });
e.add<CharacterBase>();
e.add<AnimationControl>();
e.set<CharacterVelocity>(
{ { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } });
e.add<CharacterGravity>();
e.add<CharacterBuoyancy>();
e.add<Character>();
}
}

View File

@@ -2,7 +2,7 @@
#define CHARACTER_MODULE_H_
#include <flecs.h>
#include <Ogre.h>
#include "goap.h"
#include "Components.h"
namespace ECS
{
struct Camera;
@@ -21,8 +21,6 @@ struct CharacterBase {
float mTimer;
Ogre::SceneNode *mBodyNode;
Ogre::Entity *mBodyEnt;
Ogre::Skeleton *mSkeleton;
Ogre::Node *mRootBone;
Ogre::Vector3 mBoneMotion;
Ogre::Vector3 mBonePrevMotion;
Ogre::Vector3 mGoalDirection; // actual intended direction in world-space
@@ -37,30 +35,15 @@ struct CharacterConf {
};
struct CharacterInActuator {
Ogre::String animationState;
Vector3 prevMotion;
Vector3 prevMotion;
};
struct ActionTarget {
std::vector<BaseAction *> actions;
};
struct Plan {
std::vector<BaseAction *> actions;
int position;
int length;
};
struct Planner {
BasePlanner<ECS::Blackboard, BaseAction> planner;
std::vector<BaseAction *> path;
std::vector<BasePlanner<ECS::Blackboard, BaseAction>::BaseGoal *> goals;
std::vector<BaseAction *> actions;
};
struct CharacterModule {
CharacterModule(flecs::world &ecs);
void updateCameraGoal(Camera &camera, Ogre::Real deltaYaw,
Ogre::Real deltaPitch, Ogre::Real deltaZoom);
};
struct CharacterAIModule {
CharacterAIModule(flecs::world &ecs);
void createCharacter(flecs::entity e, const Ogre::Vector3 &position,
const Ogre::Quaternion &rotation,
const Ogre::String model);
};
}
#endif

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,14 @@
#include "EventModule.h"
#include "CharacterManagerModule.h"
#include "VehicleManagerModule.h"
#include "PlayerActionModule.h"
#include "AppModule.h"
#include "CharacterAIModule.h"
#include "world-build.h"
namespace ECS
{
static flecs::world ecs;
flecs::entity player;
void setup_minimal()
{
ecs.component<EngineData>().add(flecs::Singleton);
@@ -38,8 +45,8 @@ void setup_minimal()
/* lots of things depend on it */
ecs.component<Body2Entity>().add(flecs::Singleton);
}
void setup(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
Ogre::Camera *camera, Ogre::RenderWindow *window)
void setupExteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
Ogre::Camera *camera, Ogre::RenderWindow *window)
{
std::cout << "Setup GameData\n";
setup_minimal();
@@ -50,11 +57,14 @@ void setup(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
ecs.import <WaterModule>();
ecs.import <SunModule>();
ecs.import <TerrainModule>();
ecs.import <GUIModule>();
ecs.import <GUIModule>();
ecs.import <EventTriggerModule>();
ecs.import <LuaModule>();
ecs.import <WorldMapModule>();
ecs.import <CharacterAnimationModule>();
ecs.import <PlayerActionModule>();
ecs.import <CharacterAIModule>();
ecs.add<ActionNodeList>();
ecs.system<EngineData>("UpdateDelta")
.kind(flecs::OnUpdate)
@@ -87,7 +97,7 @@ void setup(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
#endif
});
ecs.set<EngineData>({ scnMgr, 0.0f, 5.0f, (int)window->getWidth(),
(int)window->getHeight(), false });
(int)window->getHeight(), false });
ecs.set<Camera>({ cameraNode, camera, false });
ecs.add<GameData>();
ecs.add<Input>();
@@ -109,18 +119,68 @@ void setup(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
// ecs.set<Body2Entity>({});
std::cout << "Setup GameData done\n";
/* Create player */
player = ecs.get_mut<CharacterManagerModule>().createPlayer(
{ 0, 0, 4 }, Ogre::Quaternion(Ogre::Radian(Ogre::Math::PI),
Ogre::Vector3::UNIT_Y));
ecs.system("SpawnPlayer").kind(flecs::OnUpdate).interval(0.5f).run([&](flecs::iter &it) {
flecs::entity player =
ECS::get<CharacterManagerModule>().getPlayer();
if (!player.is_valid()) {
/* Create player */
Ogre::Vector3 position;
JPH::BodyID id;
long x, y;
Ogre::TerrainGroup *tg =
ECS::get<ECS::Terrain>().mTerrainGroup;
if (tg->isDerivedDataUpdateInProgress())
return;
tg->convertWorldPositionToTerrainSlot(
Ogre::Vector3(0, 0, 4), &x, &y);
Ogre::Terrain *terrain = tg->getTerrain(x, y);
if (terrain && terrain->isLoaded()) {
if (PhysicsModule::raycastQuery(
Ogre::Vector3(0, 500, 4),
Ogre::Vector3(0, -500, 4), position,
id)) {
if (position.y < -10.0f &&
position.y > -50.0f) {
player =
ecs.get_mut<
CharacterManagerModule>()
.createPlayer(
{ position.x,
position.y,
position.z },
Ogre::Quaternion(
Ogre::Radian(
Ogre::Math::
PI),
Ogre::Vector3::
UNIT_Y));
std::cout << position
<< std::endl;
// OgreAssert(false, "spawn");
}
}
}
}
});
}
void setupInteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
Ogre::Camera *camera, Ogre::RenderWindow *window)
{
}
void setupInventoryScene(Ogre::SceneManager *scnMgr,
Ogre::SceneNode *cameraNode, Ogre::Camera *camera,
Ogre::RenderWindow *window)
{
}
void setupEditor(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
Ogre::Camera *camera, Ogre::RenderWindow *window)
Ogre::Camera *camera, Ogre::RenderWindow *window)
{
std::cout << "Setup Editor\n";
setup_minimal();
ecs.component<RenderWindow>().add(flecs::Singleton);
ecs.component<EditorSceneSwitch>().add(flecs::Singleton);
ecs.import <CharacterModule>();
ecs.import <BoatModule>();
ecs.import <PhysicsModule>();
@@ -132,6 +192,8 @@ void setupEditor(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
ecs.import <LuaModule>();
// ecs.import <WorldMapModule>();
ecs.import <CharacterAnimationModule>();
ecs.import <PlayerActionModule>();
ecs.add<ActionNodeList>();
ecs.system<EngineData>("UpdateDelta")
.kind(flecs::OnUpdate)
@@ -164,8 +226,11 @@ void setupEditor(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
#endif
});
ecs.set<EngineData>({ scnMgr, 0.0f, 5.0f, (int)window->getWidth(),
(int)window->getHeight(), false });
(int)window->getHeight(), false });
ecs.set<Camera>({ cameraNode, camera, false });
#if 0
ecs.set<EditorSceneSwitch>({ 0 });
#endif
ecs.add<GameData>();
ecs.add<Input>();
ecs.add<WaterSurface>();
@@ -202,4 +267,5 @@ bool Vector3::zeroLength() const
float l = x * x + y * y + z * z;
return (l < 1e-06 * 1e-06);
}
}

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,10 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
}
// gr.velocity.y = 0.0f;
// v.y = 0.0f;
ch->SetLinearVelocity(JoltPhysics::convert(v));
OgreAssert(v.squaredLength() < 1000.0f,
"shitty velocity setting");
ch->SetLinearVelocity(
JoltPhysics::convert<JPH::Vec3>(v));
gr.velocity = Ogre::Vector3::ZERO;
});
ecs.system<const EngineData, CharacterBase, const CharacterBody,
@@ -750,10 +757,15 @@ void PhysicsModule::controlPhysics(flecs::entity e, bool enable)
}
bool PhysicsModule::raycastQuery(const Ogre::Vector3 &startPos,
const Ogre::Vector3 &endPos,
Ogre::Vector3 &position)
Ogre::Vector3 &position, JPH::BodyID &id)
{
return JoltPhysicsWrapper::getSingleton().raycastQuery(startPos, endPos,
position);
position, id);
}
void PhysicsModule::setDebugDraw(bool enable)
{
JoltPhysicsWrapper::getSingleton().setDebugDraw(enable);
}
bool WaterBody::isInWater(const JPH::BodyID &id) const
{

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,626 @@
#include <nanoflann.hpp>
#include <vector>
#include <iostream>
#include <flecs.h>
#include <nlohmann/json.hpp>
#include <lua.hpp>
#include "Components.h"
#include "GameData.h"
#include "CharacterManagerModule.h"
#include "CharacterModule.h"
#include "items.h"
#include "GUIModule.h"
#include "GUIModuleCommon.h"
#include "LuaData.h"
#include "PlayerActionModule.h"
namespace ECS
{
struct OgreVector3Adaptor {
const std::vector<ActionNodeList::ActionNode> &nodes;
OgreVector3Adaptor(const std::vector<ActionNodeList::ActionNode> &nodes)
: nodes(nodes)
{
}
// Required by nanoflann: Number of data points
inline size_t kdtree_get_point_count() const
{
return nodes.size();
}
// Required by nanoflann: Returns the distance between the vector and a point
// Using squared distance is standard for performance
inline float kdtree_get_pt(const size_t idx, const size_t dim) const
{
return nodes[idx].position[dim];
}
// Optional: bounding box optimization (return false if not implemented)
template <class BBOX> bool kdtree_get_bbox(BBOX & /*bb*/) const
{
return false;
}
};
typedef nanoflann::KDTreeSingleIndexAdaptor<
nanoflann::L2_Simple_Adaptor<float, OgreVector3Adaptor>,
OgreVector3Adaptor, 3 /* dimensionality */
>
OgreKDTree;
struct ActionNodeList::indexObject {
OgreVector3Adaptor adaptor;
OgreKDTree index;
indexObject(const std::vector<ActionNodeList::ActionNode> &nodes)
: adaptor(nodes)
, index(3, adaptor,
nanoflann::KDTreeSingleIndexAdaptorParams(10))
{
}
};
struct TestNarrativeHandler : GUI::NarrationHandler {
int count;
TestNarrativeHandler()
: GUI::NarrationHandler()
, count(0)
{
}
void finish() override
{
_clear_narration();
}
void activate() override
{
_narration("Greetings...", {});
std::cout << getPropsJSON().dump(4) << std::endl;
count = 0;
}
void event(const Ogre::String &evt) override
{
if (evt == "narration_progress" ||
evt == "narration_answered") {
count++;
if (count == 1) {
_narration(
"Question..." +
Ogre::StringConverter::toString(
count),
{ "Answer1", "Answer2" });
} else {
_narration(
"Whatever..." +
Ogre::StringConverter::toString(
count),
{});
}
if (count > 5)
_finish();
}
if (evt == "narration_answered")
std::cout << "answer: " << getNarrationAnswer()
<< std::endl;
}
};
struct LuaNarrationHandler : GUI::NarrationHandler {
int ref;
lua_State *L;
LuaNarrationHandler(lua_State *L, int ref)
: ref(ref)
, L(L)
{
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
lua_pushlightuserdata(L, this);
lua_pushcclosure(
L,
[](lua_State *L) {
luaL_checktype(L, 1, LUA_TSTRING);
luaL_checktype(L, 2, LUA_TTABLE);
LuaNarrationHandler *handler =
static_cast<LuaNarrationHandler *>(
lua_touserdata(
L,
lua_upvalueindex(1)));
Ogre::String event = lua_tostring(L, 1);
std::vector<Ogre::String> choices;
int choicesLen = (int)lua_rawlen(L, 2);
choices.reserve(choicesLen);
for (int i = 1; i <= choicesLen; ++i) {
lua_rawgeti(L, 2, i);
if (lua_isstring(L, -1))
choices.push_back(
lua_tostring(L, -1));
lua_pop(L, 1);
}
handler->_narration(event, choices);
return 0;
},
1);
lua_setfield(L, -2, "_narration");
lua_pushlightuserdata(L, this);
lua_pushcclosure(
L,
[](lua_State *L) {
LuaNarrationHandler *handler =
static_cast<LuaNarrationHandler *>(
lua_touserdata(
L,
lua_upvalueindex(1)));
handler->_finish();
return 0;
},
1);
lua_setfield(L, -2, "_finish");
lua_pushlightuserdata(L, this);
lua_pushcclosure(
L,
[](lua_State *L) {
LuaNarrationHandler *handler =
static_cast<LuaNarrationHandler *>(
lua_touserdata(
L,
lua_upvalueindex(1)));
int answer = handler->getNarrationAnswer();
lua_pushinteger(L, answer);
return 1;
},
1);
lua_setfield(L, -2, "_get_narration_answer");
lua_pushlightuserdata(L, this);
lua_pushcclosure(
L,
[](lua_State *L) {
LuaNarrationHandler *handler =
static_cast<LuaNarrationHandler *>(
lua_touserdata(
L,
lua_upvalueindex(1)));
lua_pushstring(
L, handler->getProperties().c_str());
return 1;
},
1);
lua_setfield(L, -2, "_get_properties");
lua_pushlightuserdata(L, this);
lua_pushcclosure(
L,
[](lua_State *L) {
LuaNarrationHandler *handler =
static_cast<LuaNarrationHandler *>(
lua_touserdata(
L,
lua_upvalueindex(1)));
lua_pushstring(
L, handler->getProperties().c_str());
const std::vector<ActionNodeList::ActionNode>
&nodes = ECS::get<ActionNodeList>()
.dynamicNodes;
lua_newtable(L);
int i;
for (i = 0; i < nodes.size(); i++) {
lua_pushinteger(L, i + 1);
lua_pushstring(
L,
nodes[i].props.dump().c_str());
lua_settable(L, -3);
}
return 1;
},
1);
lua_setfield(L, -2, "_get_goals");
lua_pop(L, 1);
}
void finish() override
{
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
int type = lua_getfield(L, -1, "finish");
OgreAssert(type == LUA_TFUNCTION, "bad finish()");
lua_insert(L, -2);
if (lua_pcall(L, 1, 0, 0) != 0) {
std::cerr << lua_tostring(L, -1) << std::endl;
OgreAssert(false, "lua error");
lua_pop(L, 1);
}
_clear_narration();
}
void activate() override
{
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
int type = lua_getfield(L, -1, "activate");
OgreAssert(type == LUA_TFUNCTION, "bad activate()");
lua_insert(L, -2);
if (lua_pcall(L, 1, 0, 0) != 0) {
std::cerr << lua_tostring(L, -1) << std::endl;
OgreAssert(false, "lua error");
lua_pop(L, 1);
}
}
void event(const Ogre::String &evt) override
{
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
int type = lua_getfield(L, -1, "event");
OgreAssert(type == LUA_TFUNCTION, "bad event()");
lua_insert(L, -2);
lua_pushstring(L, evt.c_str());
if (lua_pcall(L, 2, 0, 0) != 0) {
std::cerr << lua_tostring(L, -1) << std::endl;
OgreAssert(false, "lua error");
lua_pop(L, 1);
}
}
};
struct SimpleWordHandler : PlayerActionModule::ActionWordHandler {
void operator()(flecs::entity town, int index,
const Ogre::String &word) override
{
TestNarrativeHandler *handle = OGRE_NEW TestNarrativeHandler();
const TownNPCs::NPCData &npc =
town.get<TownNPCs>().npcs.at(index);
flecs::entity e = npc.e;
for (const auto &anode : npc.actionNodes) {
if (anode.action == word) {
nlohmann::json props = anode.props;
props["initiator"] =
ECS::get<CharacterManagerModule>()
.getPlayer()
.id();
props["recipient"] = e.id();
handle->setProperties(props.dump());
break;
}
}
ECS::get_mut<GUI>().addNarrationHandler(handle);
ECS::modified<GUI>();
}
};
PlayerActionModule::PlayerActionModule(flecs::world &ecs)
{
ecs.module<PlayerActionModule>();
ecs.import <CharacterManagerModule>();
ecs.component<ActionNodeList>()
.on_add([](flecs::entity e, ActionNodeList &alist) {
alist.nodeMutex = std::make_shared<std::mutex>();
alist.setDirty();
alist.nodes.reserve(1000);
alist.dynamicNodes.reserve(1000);
alist.setUISelected(-1);
alist.setReady();
})
.add(flecs::Singleton);
ecs.system<ActionNodeList>("updateNodeList")
.kind(flecs::OnUpdate)
.each([](ActionNodeList &list) {
if (list.isBusy())
return;
if (list.nodes.size() > 0) {
Ogre::SceneNode *cameraNode =
ECS::get<Camera>().mCameraNode;
Ogre::Vector3 cameraPos =
cameraNode->_getDerivedPosition();
flecs::entity player =
ECS::get<CharacterManagerModule>()
.getPlayer();
if (player.is_valid()) {
Ogre::Vector3 playerPos =
player.get<CharacterBase>()
.mBodyNode
->_getDerivedPosition();
list.UIquery(playerPos);
} else {
list.UIquery(cameraPos);
}
}
});
ecs.system<ActionNodeList, const Input>("ActivateActionNode")
.kind(flecs::OnUpdate)
.each([this](ActionNodeList &list, const Input &input) {
std::lock_guard<std::mutex> lock(*list.nodeMutex);
if (input.control & 32)
std::cout << "act pressed" << std::endl;
if (list.isBusy())
return;
if (input.act_pressed &&
list.getUIData().selected >= 0) {
std::cout
<< list.dynamicNodes[list.getUIData()
.selected]
.props.dump(4)
<< std::endl;
flecs::entity_t townid =
list.dynamicNodes[list.getUIData()
.selected]
.props["town"]
.get<flecs::entity_t>();
flecs::entity town = ECS::get().entity(townid);
int index = list.dynamicNodes[list.getUIData()
.selected]
.props["index"]
.get<int>();
for (auto it = actionWords.begin();
it != actionWords.end(); it++) {
if (it->first ==
list.dynamicNodes[list.getUIData()
.selected]
.action) {
(*it->second)(
town, index,
list.dynamicNodes
[list.getUIData()
.selected]
.action);
list.setBusy();
}
}
}
if (!ECS::get<GUI>().enabled)
list.setReady();
});
}
void PlayerActionModule::addWordHandler(const Ogre::String &word,
ActionWordHandler *handler)
{
actionWords.insert({ word, handler });
}
void PlayerActionModule::removeWordHandler(const Ogre::String &word,
ActionWordHandler *handler)
{
for (auto it = actionWords.begin(); it != actionWords.end();) {
if (it->first == word && it->second == handler)
it = actionWords.erase(it);
else
it++;
}
}
struct LuaWordHandler : PlayerActionModule::ActionWordHandler {
lua_State *L;
int ref;
void operator()(flecs::entity town, int index,
const Ogre::String &word) override
{
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
if (lua_type(L, -1) == LUA_TFUNCTION) {
luaL_checktype(L, -1, LUA_TFUNCTION);
lua_pushinteger(L, town.id());
lua_pushinteger(L, index);
lua_pushstring(L, word.c_str());
if (lua_pcall(L, 3, 0, 0)) {
Ogre::LogManager::getSingleton().stream()
<< lua_tostring(L, -1);
OgreAssert(false, "Lua error");
}
} else if (lua_type(L, -1) == LUA_TTABLE) {
luaL_checktype(L, -1, LUA_TTABLE);
lua_pop(L, 1);
LuaNarrationHandler *handle =
OGRE_NEW LuaNarrationHandler(L, ref);
const TownNPCs::NPCData &npc =
town.get<TownNPCs>().npcs.at(index);
flecs::entity e = npc.e;
for (const auto &anode : npc.actionNodes) {
if (anode.action == word) {
nlohmann::json props = anode.props;
props["initiator"] =
ECS::get<CharacterManagerModule>()
.getPlayer()
.id();
props["recipient"] = e.id();
handle->setProperties(props.dump());
break;
}
}
ECS::get_mut<GUI>().addNarrationHandler(handle);
ECS::modified<GUI>();
}
}
};
void PlayerActionModule::addLuaWordHandler(const Ogre::String &word,
lua_State *L, int ref)
{
struct LuaWordHandler *handler = OGRE_NEW LuaWordHandler;
handler->L = L;
handler->ref = ref;
addWordHandler(word, handler);
}
void PlayerActionModule::removeLuaWordHandler(const Ogre::String &word,
lua_State *L, int ref)
{
for (auto it = actionWords.begin(); it != actionWords.end();) {
LuaWordHandler *handler =
static_cast<LuaWordHandler *>(it->second);
if (it->first == word && handler->L == L && handler->ref == ref)
it = actionWords.erase(it);
else
it++;
}
}
int PlayerActionModule::setupLuaActionHandler(lua_State *L)
{
luaL_checktype(L, 1, LUA_TSTRING);
if (lua_type(L, 2) == LUA_TFUNCTION) {
luaL_checktype(L, 2, LUA_TFUNCTION);
Ogre::String word = lua_tostring(L, 1);
lua_pushvalue(L, 2);
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
addLuaWordHandler(word, L, ref);
} else if (lua_type(L, 2) == LUA_TTABLE) {
luaL_checktype(L, 2, LUA_TTABLE);
Ogre::String word = lua_tostring(L, 1);
lua_pushvalue(L, 2);
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
addLuaWordHandler(word, L, ref);
}
return 0;
}
void ActionNodeList::updateDynamicNodes()
{
std::lock_guard<std::mutex> lock(*nodeMutex);
if (dynamicNodes.size() > nodes.size())
dynamicNodes.resize(nodes.size());
else {
dynamicNodes.clear();
dynamicNodes.insert(dynamicNodes.end(), nodes.begin(),
nodes.end());
}
ECS::get().query_builder<const TownNPCs>().each(
[this](flecs::entity town, const TownNPCs &npcs) {
for (auto it = npcs.npcs.begin(); it != npcs.npcs.end();
it++) {
dynamicNodes.insert(
dynamicNodes.end(),
it->second.actionNodes.begin(),
it->second.actionNodes.end());
}
});
dirty = true;
}
void ActionNodeList::build()
{
std::lock_guard<std::mutex> lock(*nodeMutex);
indexObj = std::make_shared<ActionNodeList::indexObject>(dynamicNodes);
indexObj->index.buildIndex();
dirty = false;
}
bool ActionNodeList::_query(const Ogre::Vector3 &position,
std::vector<size_t> &points,
std::vector<float> &distances)
{
std::vector<size_t> tmppoints;
std::vector<float> tmpdistances;
points.clear();
points.reserve(4);
distances.clear();
distances.reserve(4);
tmppoints.resize(4);
tmpdistances.resize(4);
if (!indexObj) {
dirty = true;
return false;
}
nanoflann::KNNResultSet<float> resultSet(4);
resultSet.init(tmppoints.data(), tmpdistances.data());
bool ret = indexObj->index.findNeighbors(resultSet, &position.x,
nanoflann::SearchParameters());
int i;
for (i = 0; i < resultSet.size(); i++)
if (tmpdistances[i] < 25.0f) {
points.push_back(tmppoints[i]);
distances.push_back(tmpdistances[i]);
}
return ret;
}
bool ActionNodeList::query_ai(const Ogre::Vector3 &position, float distance,
std::vector<size_t> &points,
std::vector<float> &distances)
{
std::lock_guard<std::mutex> lock(*nodeMutex);
std::vector<size_t> tmppoints;
std::vector<float> tmpdistances;
points.clear();
points.reserve(100);
distances.clear();
distances.reserve(100);
tmppoints.resize(100);
tmpdistances.resize(100);
if (!indexObj) {
dirty = true;
return false;
}
nanoflann::KNNResultSet<float> resultSet(100);
resultSet.init(tmppoints.data(), tmpdistances.data());
bool ret = indexObj->index.findNeighbors(resultSet, &position.x,
nanoflann::SearchParameters());
int i;
for (i = 0; i < resultSet.size(); i++)
if (tmpdistances[i] < distance) {
points.push_back(tmppoints[i]);
distances.push_back(tmpdistances[i]);
}
return ret;
}
int ActionNodeList::addNode(ActionNode &node)
{
std::lock_guard<std::mutex> lock(*nodeMutex);
int index = nodes.size();
nodes.push_back(node);
dirty = true;
return index;
}
void ActionNodeList::removeNode(int index)
{
std::lock_guard<std::mutex> lock(*nodeMutex);
nodes.erase(nodes.begin() + index);
dirty = true;
}
const ActionNodeList::UIData &ActionNodeList::getUIData()
{
std::lock_guard<std::mutex> lock(*uidata.mutex);
return uidata;
}
void ActionNodeList::setUISelected(int selected)
{
std::lock_guard<std::mutex> lock(*uidata.mutex);
uidata.selected = selected;
}
void ActionNodeList::setUIPoints(const std::vector<size_t> &points,
const std::vector<float> &distances)
{
std::lock_guard<std::mutex> lock(*uidata.mutex);
uidata.points = points;
uidata.distances = distances;
}
void ActionNodeList::UIquery(const Ogre::Vector3 &position)
{
bool needBuild = false;
{
std::lock_guard<std::mutex> lock(*nodeMutex);
if (dirty || !indexObj)
needBuild = true;
}
if (needBuild)
build();
{
std::lock_guard<std::mutex> lock(*uidata.mutex);
_query(position, uidata.points, uidata.distances);
}
}
void ActionNodeList::setDirty()
{
dirty = true;
}
void ActionNodeList::setReady()
{
busy = false;
}
void ActionNodeList::setBusy()
{
busy = true;
}
bool ActionNodeList::isBusy()
{
return busy;
}
}

View File

@@ -0,0 +1,79 @@
#ifndef PLAYERACTIONMODULE_H
#define PLAYERACTIONMODULE_H
#include <flecs.h>
#include <nlohmann/json.hpp>
#include <lua.h>
#include <Ogre.h>
namespace ECS {
struct ActionNodeList {
struct indexObject;
struct ActionNode {
Ogre::String action;
Ogre::String action_text;
Ogre::Vector3 position;
Ogre::Quaternion rotation;
float height;
float radius;
nlohmann::json props;
};
struct UIData {
std::shared_ptr<std::mutex> mutex;
int selected;
std::vector<size_t> points;
std::vector<float> distances;
UIData()
: mutex(std::make_shared<std::mutex>())
, selected(-1)
{
}
};
private:
bool dirty;
bool busy;
std::shared_ptr<indexObject> indexObj;
struct UIData uidata;
bool _query(const Ogre::Vector3 &position, std::vector<size_t> &points,
std::vector<float> &distances);
public:
std::shared_ptr<std::mutex> nodeMutex;
std::vector<ActionNode> nodes, dynamicNodes;
void updateDynamicNodes();
void build();
bool query_ai(const Ogre::Vector3 &position, float distance,
std::vector<size_t> &points,
std::vector<float> &distances);
int addNode(struct ActionNodeList::ActionNode &node);
void removeNode(int index);
const UIData &getUIData();
void setUISelected(int selected);
void setUIPoints(const std::vector<size_t> &points,
const std::vector<float> &distances);
void UIquery(const Ogre::Vector3 &position);
void setDirty(); // node was added or removed
void setReady();
void setBusy();
bool isBusy();
};
struct PlayerActionModule {
struct ActionWordHandler {
virtual void operator()(flecs::entity town, int index,
const Ogre::String &word) = 0;
};
std::multimap<Ogre::String, ActionWordHandler *>
actionWords;
PlayerActionModule(flecs::world &ecs);
void addWordHandler(const Ogre::String &word, ActionWordHandler *handler);
void removeWordHandler(const Ogre::String &word, ActionWordHandler *handler);
void addLuaWordHandler(const Ogre::String &word, lua_State *L, int ref);
void removeLuaWordHandler(const Ogre::String &word, lua_State *L, int ref);
int setupLuaActionHandler(lua_State *L);
};
}
#endif // PLAYERACTIONMODULE_H

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,16 @@
#ifndef _STATIC_GEOMETRY_MODULE_H_
#define _STATIC_GEOMETRY_MODULE_H_
#include <flecs.h>
#include <nlohmann/json.hpp>
#include <Ogre.h>
namespace Procedural
{
class TriangleBuffer;
}
namespace Ogre
{
struct LodConfig;
}
namespace ECS
{
struct TerrainSlotParent {
@@ -14,7 +23,22 @@ struct TerrainItem {
};
struct TerrainItemNode {
Ogre::SceneNode *itemNode;
Ogre::StaticGeometry *geo;
};
struct TerrainItemMeshNode {
Ogre::SceneNode *itemNode;
Ogre::StaticGeometry *geo;
};
struct FurnitureItem {
Ogre::String properties;
std::vector<Ogre::String> tags;
};
struct FurnitureInstance {
Ogre::SceneNode *furniture;
};
struct GeometryUpdateItem {};
struct TownCollider {};
struct StaticGeometryModule {
StaticGeometryModule(flecs::world &ecs);
@@ -26,8 +50,12 @@ struct StaticGeometryModule {
static void setItemProperties(flecs::entity id,
Ogre::String properties);
static const Ogre::String &getItemProperties(flecs::entity id);
static void saveItems();
static void loadTemplates();
static void saveTemplates();
static void saveItems();
static void loadItems();
static void saveFurniture();
static void loadFurniture();
static void getItemPositionPerSlot(long x, long y,
std::list<Ogre::Vector3> *positions);
static void getItemPositions(std::list<Ogre::Vector3> *positions);
@@ -37,11 +65,14 @@ struct StaticGeometryModule {
static void getItemsProperties(
std::list<std::pair<flecs::entity, Ogre::String> > *items);
static void createItemGeometry(flecs::entity e);
static void createBridge(flecs::entity e, Ogre::SceneNode *sceneNode,
Ogre::StaticGeometry *geo);
static void createPier(flecs::entity e, Ogre::SceneNode *sceneNode,
Ogre::StaticGeometry *geo);
static void createHarbour(flecs::entity e, Ogre::SceneNode *sceneNode);
static void destroyItemGeometry(flecs::entity e);
static nlohmann::json &getTemplates();
static void updateItemGeometry(flecs::entity e);
static void addTriangleBufferWork(const Ogre::String &meshName,
Ogre::StaticGeometry *geo,
const Ogre::Vector3 &position,
const Ogre::Quaternion &rotation,
const Procedural::TriangleBuffer &tb);
};
}
#endif
#endif

View File

@@ -198,10 +198,12 @@ out:
long world_grid_y = world_y + grid_center_y;
float amplitude = 150.0f;
if (world_grid_x < 0 || world_grid_y < 0) {
#if 0
std::cout << "world: " << world_x << " " << world_y
<< " ";
std::cout << "grid: " << world_grid_x << " ";
std::cout << world_grid_y << std::endl;
#endif
return -amplitude;
}
OgreAssert(world_grid_x >= 0, "bad world x");
@@ -263,7 +265,6 @@ public:
void createTerrainChunk(Ogre::TerrainGroup *terrainGroup, long x,
long y)
{
std::lock_guard<std::mutex> guard(mtx);
Ogre::Terrain *terrain = terrainGroup->getTerrain(x, y);
float minH = terrain->getMinHeight();
float maxH = terrain->getMaxHeight();
@@ -277,7 +278,7 @@ public:
float worldSize = terrain->getWorldSize();
float scaled = worldSize / (size - 1);
Ogre::Vector3 bodyPosition = terrain->getPosition();
bodyPosition.y += (maxH + minH) / 2.0f;
// bodyPosition.y += (maxH + minH) / 2.0f;
bodyPosition.x += worldSize / 2.0f;
bodyPosition.z += worldSize / 2.0f;
Ogre::Vector3 offset =
@@ -337,17 +338,30 @@ public:
}
void update()
{
std::lock_guard<std::mutex> guard(mtx);
static bool created = false;
std::deque<struct gen_collider> output;
while (!collider_queue.empty()) {
Ogre::TerrainGroup *group =
collider_queue.front().group;
if (group->isDerivedDataUpdateInProgress())
break;
long x = collider_queue.front().x;
long y = collider_queue.front().y;
// std::cout << x << " " << y << " "
// << collider_queue.size() << std::endl;
Ogre::Terrain *terrain = group->getTerrain(x, y);
Ogre::Vector3 worldPos;
group->convertTerrainSlotToWorldPosition(x, y,
&worldPos);
#if 0
std::cout << "terrain: " << terrain;
if (terrain)
std::cout
<< terrain->getHeightData() << " "
<< terrain->isLoaded() << " "
<< terrain->isDerivedDataUpdateInProgress()
<< std::endl;
#endif
if (terrain && terrain->getHeightData() &&
terrain->isLoaded() &&
!terrain->isDerivedDataUpdateInProgress()) {
@@ -356,8 +370,8 @@ public:
Ogre::StringConverter::toString(x) +
" " +
Ogre::StringConverter::toString(y));
float minH = terrain->getMinHeight();
float maxH = terrain->getMaxHeight();
// float minH = terrain->getMinHeight();
// float maxH = terrain->getMaxHeight();
int size = terrain->getSize();
float worldSize = terrain->getWorldSize();
{
@@ -404,8 +418,8 @@ public:
created = true;
}
#endif
collider_queue.pop_front();
// FIXME: create entities and components instead
#if 0
Ogre::SceneNode *items =
terrain->_getRootSceneNode()
->createChildSceneNode();
@@ -425,20 +439,24 @@ public:
what->setOrientation(item.rotation);
what->setPosition(item.position);
}
#endif
/* Spawn items */
StaticGeometryModule::addGeometryForSlot(x, y);
collider_queue.pop_front();
} else {
output.push_back(collider_queue.front());
/* Terrain data not ready maybe move to next terrain */
gen_collider m = collider_queue.front();
collider_queue.pop_front();
}
if (collider_queue.empty() &&
!ECS::get<Terrain>().mTerrainReady) {
ECS::get_mut<Terrain>().mTerrainReady = true;
ECS::modified<Terrain>();
collider_queue.push_back(m);
break; // allow system to move on
}
}
collider_queue = output;
if (collider_queue.empty() &&
!ECS::get<Terrain>().mTerrainReady) {
ECS::get_mut<Terrain>().mTerrainReady = true;
ECS::modified<Terrain>();
}
}
};
class DummyPageProvider : public Ogre::PageProvider {
@@ -504,9 +522,6 @@ TerrainModule::TerrainModule(flecs::world &ecs)
OgreAssert(terrain.mTerrainGlobals,
"Failed to allocate global options");
Ogre::LogManager::getSingleton().setMinLogLevel(
Ogre::LML_TRIVIAL);
terrain.mTerrainGroup =
OGRE_NEW Ogre::TerrainGroup(
eng.mScnMgr,
@@ -717,11 +732,12 @@ TerrainModule::TerrainModule(flecs::world &ecs)
ECS::get().add<CanSetPlayerPosition>();
}
});
#if 0
ecs.system<const Terrain>("SetPlayerPosition")
.kind(flecs::OnUpdate)
.with<CanSetPlayerPosition>()
.each([this](const Terrain &terrain) {
flecs::entity player = ECS::player;
flecs::entity player = ECS::get<CharacterM;
if (!player.is_valid())
return;
if (!player.has<CharacterLocation>())
@@ -745,6 +761,7 @@ TerrainModule::TerrainModule(flecs::world &ecs)
player.modified<CharacterLocation>();
ECS::get().remove<CanSetPlayerPosition>();
});
#endif
ecs.system<const Terrain>("UpdateTerrainGroup")
.kind(flecs::OnUpdate)
.interval(2.0f)
@@ -797,4 +814,4 @@ void TerrainModule::defineTerrain(long x, long y)
ECS::get<Terrain>().definer->define(ECS::get<Terrain>().mTerrainGroup,
x, y);
}
}
}

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

@@ -1,38 +1,5 @@
#include <set>
#include "goap.h"
bool BaseAction::can_run(const ECS::Blackboard &state, bool debug)
namespace goap
{
return m_exec->_can_run(state, debug);
}
void BaseAction::_plan_effects(ECS::Blackboard &state)
{
state.clear_flag(m_exec->m_clear_bits);
state.set_flag(m_exec->m_set_bits);
}
bool BaseAction::is_active(const ECS::Blackboard &state)
{
return m_exec->is_active(state);
}
bool BaseAction::stop(ECS::Blackboard &state)
{
if (!is_active(state))
return false;
m_exec->_exit(state);
state._active.erase(m_exec.get());
return true;
}
int BaseAction::execute(ECS::Blackboard &state)
{
if (!is_active(state)) {
state._active.insert(m_exec.get());
m_exec->_enter(state);
}
int ret = m_exec->_execute(state);
if (ret == OK)
stop(state);
return ret;
}
int BaseAction::get_cost(const ECS::Blackboard &state) const
{
return m_exec->_get_cost(state);
}

View File

@@ -1,77 +1,35 @@
#ifndef H_GOAP_H_
#define H_GOAP_H_
#undef NDEBUG
#include <string>
#include <memory>
#include <set>
#include <flecs.h>
namespace ECS
#include <Ogre.h>
namespace goap
{
struct Blackboard;
}
struct BaseAction;
struct BaseActionExec;
namespace ECS
{
struct Blackboard {
enum {
HIGH_LUST = (1 << 0),
LOW_LUST = (1 << 1),
FULL_LUST = (1 << 2),
LOW_HEALTH = (1 << 3),
FULL_HEALTH = (1 << 4),
HAS_TARGET = (1 << 5),
LOW_STAMINA = (1 << 6),
FULL_STAMINA = (1 << 7),
REACHED_TARGET = (1 << 8),
};
flecs::entity me;
int health;
int stamina;
int lust;
uint32_t flags;
std::set<BaseActionExec *> _active;
bool operator==(const Blackboard &other)
{
return flags == other.flags;
}
void set_flag(int flag)
{
flags |= flag;
}
void clear_flag(int flag)
{
flags &= ~flag;
}
bool get_flag(int flag) const
{
return flags & flag;
}
bool check_flag(int flag) const
{
return (flags & flag) == flag;
}
};
}
struct BaseAction {
template <typename State> struct BaseAction {
std::string m_name;
State prereq;
State effects;
int m_cost;
public:
enum { OK = 0, BUSY, ABORT };
private:
std::unique_ptr<BaseActionExec> m_exec;
public:
BaseAction(const std::string &name, BaseActionExec *exec)
: m_name(name)
, m_exec(exec)
BaseAction(const std::string &name, const State &prereq, const State &effects, int cost)
: m_name(name)
, prereq(prereq)
, effects(effects)
, m_cost(cost)
{
}
const std::string &get_name() const
virtual const std::string &get_name() const
{
return m_name;
}
void plan_effects(ECS::Blackboard &state)
void plan_effects(State &state)
{
// std::cout << m_name << " pre: " << &state << " " << state.flags
// << std::endl;
@@ -79,42 +37,20 @@ public:
// std::cout << m_name << " post: " << &state << " " << state.flags
// << std::endl;
}
virtual bool can_run(const ECS::Blackboard &state, bool debug = false);
virtual void _plan_effects(ECS::Blackboard &state);
bool is_active(const ECS::Blackboard &state);
bool stop(ECS::Blackboard &state);
int execute(ECS::Blackboard &state);
virtual int get_cost(const ECS::Blackboard &state) const;
virtual bool can_run(const State &state, bool debug = false)
{
return state.distance_to(prereq) == 0;
}
virtual void _plan_effects(State &state)
{
state.apply(effects);
}
// constant cost
virtual int get_cost(const State &state) const
{
return m_cost;
}
};
struct BaseActionExec {
enum {
OK = BaseAction::OK,
BUSY = BaseAction::BUSY,
ABORT = BaseAction::ABORT
};
BaseActionExec(int set_bits, int clear_bits)
: m_set_bits(set_bits)
, m_clear_bits(clear_bits)
{
}
bool is_active(const ECS::Blackboard &state)
{
return state._active.find(this) != state._active.end();
}
virtual int _execute(ECS::Blackboard &state) = 0;
virtual void _enter(ECS::Blackboard &state) = 0;
virtual void _exit(ECS::Blackboard &state) = 0;
virtual int _get_cost(const ECS::Blackboard &state) const
{
return 1;
}
virtual bool _can_run(const ECS::Blackboard &state, bool debug = false)
{
return true;
}
int m_set_bits, m_clear_bits;
};
template <typename State, typename Act, int N = 100> class BasePlanner {
struct VisitedState {
int priority;
@@ -190,20 +126,24 @@ template <typename State, typename Act, int N = 100> class BasePlanner {
public:
struct BaseGoal {
std::string m_name;
State goal;
public:
BaseGoal(const std::string &name)
: m_name(name)
BaseGoal(const std::string &name, const State &goal)
: m_name(name), goal(goal)
{
}
/** Checks if the goal is reached for the given state. */
virtual bool is_reached(const State &state) const
{
return distance_to(state) == 0;
return distance_to(state) == 0;
}
/** Computes the distance from state to goal. */
virtual int distance_to(const State &state) const = 0;
virtual int distance_to(const State &state) const
{
return state.distance_to(goal);
}
virtual ~BaseGoal() = default;
const std::string &get_name() const
@@ -216,7 +156,7 @@ public:
*
* If path is given, then the found path is stored there.
*/
int plan(const State &state, BaseGoal &goal, Act *actions[],
int plan(const State &state, const BaseGoal &goal, Act *actions[],
unsigned action_count, Act **path = nullptr, int path_len = 10)
{
visited_states_array_to_list(nodes, N);
@@ -244,7 +184,8 @@ public:
}
if (len > path_len) {
return -1;
OgreAssert(len <= path_len, "Out of plan length");
return -3;
}
if (path) {
@@ -255,6 +196,7 @@ public:
}
}
OgreAssert(len >= 0, "Bad plan length");
return len;
}
@@ -274,6 +216,7 @@ public:
}
if (!gc) {
OgreAssert(gc, "Out of memory");
return -2 /* OOM */;
}
@@ -337,14 +280,16 @@ public:
return -1 /* No path */;
}
};
template <class RunnerType> struct DeclareAction : public BaseAction {
template <class RunnerType, class State>
struct DeclareAction : public BaseAction<State> {
RunnerType runner;
template <class... Args>
DeclareAction(const std::string &name, Args... args)
: runner(args...)
, BaseAction(name, &runner)
, BaseAction<State>(name, &runner)
{
}
};
}
#endif

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

107
src/gamedata/items/items.h Normal file
View File

@@ -0,0 +1,107 @@
#ifndef __ITEMS_H__
#define __ITEMS_H__
#include <OgreMeshLodGenerator.h>
#include <flecs.h>
#include "Components.h"
#include "GameData.h"
namespace ECS
{
namespace Items
{
void showItemPopup(const std::pair<flecs::entity, Ogre::String> &item);
void showItemButtons(const std::pair<flecs::entity, Ogre::String> &item);
void createItemsMenu();
void runScriptsForAllTowns();
}
namespace Geometry
{
void setupLods(Ogre::LodConfig &config);
struct harbourMaker {
Ogre::Entity *planks, *pillar, *beam;
Ogre::String makeName(const Ogre::String &base, flecs::entity e)
{
return base + Ogre::StringConverter::toString(e.id());
}
harbourMaker(flecs::entity e)
{
Ogre::String planksName = makeName("Plank", e);
Ogre::String beamName = makeName("Beam", e);
Ogre::String pillarName = makeName("Pillar", e);
std::pair<Ogre::String, Ogre::Entity **> sets[] = {
{ planksName, &planks },
{ beamName, &beam },
{ pillarName, &pillar }
};
std::map<Ogre::String, Ogre::String> meshes = {
{ planksName, "pier-plank.glb" },
{ beamName, "pier-beam.glb" },
{ pillarName, "pier-pillar.glb" },
};
for (auto &p : sets) {
if (ECS::get<EngineData>().mScnMgr->hasEntity(p.first))
*p.second =
ECS::get<EngineData>()
.mScnMgr->getEntity(p.first);
else {
*p.second = ECS::get<EngineData>()
.mScnMgr->createEntity(
p.first,
meshes.at(p.first));
Ogre::MeshPtr mesh = (*p.second)->getMesh();
Ogre::LodConfig config(mesh);
setupLods(config);
}
}
}
};
void createItemGeometry(flecs::entity e);
void destroyItemGeometry(flecs::entity e);
void updateItemGeometry(flecs::entity e);
flecs::entity createMeshGeometry(const Ogre::String &meshName,
flecs::entity parente,
Ogre::SceneNode *sceneNode,
Ogre::StaticGeometry *geo);
}
static void to_json(nlohmann::json &j, const Ogre::Vector3 &position)
{
j["x"] = position.x;
j["y"] = position.y;
j["z"] = position.z;
}
static void to_json(nlohmann::json &j, const Ogre::Quaternion &orientation)
{
j["w"] = orientation.w;
j["x"] = orientation.x;
j["y"] = orientation.y;
j["z"] = orientation.z;
}
static void to_json(nlohmann::json &j, const Ogre::ColourValue &colour)
{
j["r"] = colour.r;
j["g"] = colour.g;
j["b"] = colour.b;
j["a"] = colour.a;
}
static void from_json(const nlohmann::json &j, Ogre::Vector3 &position)
{
position.x = j["x"].get<float>();
position.y = j["y"].get<float>();
position.z = j["z"].get<float>();
}
static void from_json(const nlohmann::json &j, Ogre::Quaternion &orientation)
{
orientation.w = j["w"].get<float>();
orientation.x = j["x"].get<float>();
orientation.y = j["y"].get<float>();
orientation.z = j["z"].get<float>();
}
static void from_json(const nlohmann::json &j, Ogre::ColourValue &colour)
{
colour.r = j["r"].get<float>();
colour.g = j["g"].get<float>();
colour.b = j["b"].get<float>();
colour.a = j["a"].get<float>();
}
}
#endif

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

5935
src/gamedata/items/town.cpp Normal file

File diff suppressed because it is too large Load Diff

26
src/gamedata/items/town.h Normal file
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)
@@ -890,8 +901,14 @@ public:
msp.ScaleToMass(mass);
bodySettings.mMassPropertiesOverride = msp;
}
return createBody(bodySettings, listener);
}
JPH::BodyID id = createBody(bodySettings, listener);
if (shape->GetType() == JPH::EShapeType::HeightField) {
JPH::BodyInterface &body_interface =
physics_system.GetBodyInterface();
body_interface.SetFriction(id, 1.0f);
}
return id;
}
JPH::BodyID createBody(const JPH::Shape *shape, float mass,
Ogre::SceneNode *node, JPH::EMotionType motion,
JPH::ObjectLayer layer,
@@ -901,20 +918,8 @@ public:
const Ogre::Quaternion &rotation =
node->_getDerivedOrientation();
std::cout << "body position: " << position << std::endl;
JPH::BodyCreationSettings bodySettings(
shape, JoltPhysics::convert(position),
JoltPhysics::convert(rotation), motion, layer);
if (mass > 0.001f) {
JPH::MassProperties msp;
msp.ScaleToMass(mass);
bodySettings.mMassPropertiesOverride = msp;
}
JPH::BodyID id = createBody(bodySettings, listener);
if (shape->GetType() == JPH::EShapeType::HeightField) {
JPH::BodyInterface &body_interface =
physics_system.GetBodyInterface();
body_interface.SetFriction(id, 1.0f);
}
JPH::BodyID id = createBody(shape, mass, position, rotation,
motion, layer, listener);
id2node[id] = node;
node2id[node] = id;
return id;
@@ -961,7 +966,7 @@ public:
JPH::MutableCompoundShape *master =
static_cast<JPH::MutableCompoundShape *>(
compoundShape.GetPtr());
master->AddShape(JoltPhysics::convert(position),
master->AddShape(JoltPhysics::convert<JPH::Vec3>(position),
JoltPhysics::convert(rotation),
childShape.GetPtr());
}
@@ -1272,8 +1277,9 @@ public:
{
int i;
JPH::HeightFieldShapeSettings heightfieldSettings(
samples, JoltPhysics::convert(offset),
JoltPhysics::convert(scale), (uint32_t)sampleCount);
samples, JoltPhysics::convert<JPH::Vec3>(offset),
JoltPhysics::convert<JPH::Vec3>(scale),
(uint32_t)sampleCount);
for (i = 0; i < sampleCount; i++) {
memcpy(heightfieldSettings.mHeightSamples.data() +
sampleCount * i,
@@ -1296,9 +1302,10 @@ public:
"bad parameters");
JPH::MutableCompoundShapeSettings settings;
for (i = 0; i < shapes.size(); i++)
settings.AddShape(JoltPhysics::convert(positions[i]),
JoltPhysics::convert(rotations[i]),
shapes[i].GetPtr());
settings.AddShape(
JoltPhysics::convert<JPH::Vec3>(positions[i]),
JoltPhysics::convert(rotations[i]),
shapes[i].GetPtr());
JPH::ShapeSettings::ShapeResult result = settings.Create();
OgreAssert(result.Get(), "Can not create compound shape");
return result.Get();
@@ -1314,9 +1321,10 @@ public:
"bad parameters");
JPH::StaticCompoundShapeSettings settings;
for (i = 0; i < shapes.size(); i++)
settings.AddShape(JoltPhysics::convert(positions[i]),
JoltPhysics::convert(rotations[i]),
shapes[i].GetPtr());
settings.AddShape(
JoltPhysics::convert<JPH::Vec3>(positions[i]),
JoltPhysics::convert(rotations[i]),
shapes[i].GetPtr());
JPH::ShapeSettings::ShapeResult result = settings.Create();
OgreAssert(result.Get(), "Can not create compound shape");
return result.Get();
@@ -1326,12 +1334,24 @@ public:
JPH::ShapeRefC shape)
{
JPH::OffsetCenterOfMassShapeSettings settings(
JoltPhysics::convert(offset), shape.GetPtr());
JoltPhysics::convert<JPH::Vec3>(offset),
shape.GetPtr());
JPH::ShapeSettings::ShapeResult result = settings.Create();
OgreAssert(result.Get(), "Can not create com offset shape");
return result.Get();
}
void applyBuoyancyImpulse(JPH::BodyID id,
JPH::ShapeRefC
createRotatedTranslatedShape(const Ogre::Vector3 &offset,
const Ogre::Quaternion rotation,
JPH::ShapeRefC shape)
{
return JPH::RotatedTranslatedShapeSettings(
JoltPhysics::convert<JPH::Vec3>(offset),
JoltPhysics::convert(rotation), shape)
.Create()
.Get();
}
void applyBuoyancyImpulse(JPH::BodyID id,
const Ogre::Vector3 &surfacePosition,
const Ogre::Vector3 &surfaceNormal,
float buoyancy, float linearDrag,
@@ -1342,11 +1362,12 @@ public:
JPH::BodyLockWrite lock(physics_system.GetBodyLockInterface(),
id);
JPH::Body &body = lock.GetBody();
body.ApplyBuoyancyImpulse(JoltPhysics::convert(surfacePosition),
JoltPhysics::convert(surfaceNormal),
buoyancy, linearDrag, angularDrag,
JoltPhysics::convert(fluidVelocity),
JoltPhysics::convert(gravity), dt);
body.ApplyBuoyancyImpulse(
JoltPhysics::convert(surfacePosition),
JoltPhysics::convert<JPH::Vec3>(surfaceNormal),
buoyancy, linearDrag, angularDrag,
JoltPhysics::convert<JPH::Vec3>(fluidVelocity),
JoltPhysics::convert<JPH::Vec3>(gravity), dt);
}
void applyBuoyancyImpulse(JPH::BodyID id,
const Ogre::Vector3 &surfacePosition,
@@ -1358,11 +1379,12 @@ public:
JPH::BodyLockWrite lock(physics_system.GetBodyLockInterface(),
id);
JPH::Body &body = lock.GetBody();
body.ApplyBuoyancyImpulse(JoltPhysics::convert(surfacePosition),
JoltPhysics::convert(surfaceNormal),
buoyancy, linearDrag, angularDrag,
JoltPhysics::convert(fluidVelocity),
physics_system.GetGravity(), dt);
body.ApplyBuoyancyImpulse(
JoltPhysics::convert(surfacePosition),
JoltPhysics::convert<JPH::Vec3>(surfaceNormal),
buoyancy, linearDrag, angularDrag,
JoltPhysics::convert<JPH::Vec3>(fluidVelocity),
physics_system.GetGravity(), dt);
}
bool isActive(JPH::BodyID id)
{
@@ -1385,7 +1407,7 @@ public:
void getPositionAndRotation(JPH::BodyID id, Ogre::Vector3 &position,
Ogre::Quaternion &rotation)
{
JPH::Vec3 _position;
JPH::RVec3 _position;
JPH::Quat _rotation;
physics_system.GetBodyInterface().GetPositionAndRotation(
id, _position, _rotation);
@@ -1443,11 +1465,11 @@ public:
void broadphaseQuery(float dt, const Ogre::Vector3 &position,
std::set<JPH::BodyID> &inWater)
{
JPH::Vec3 surface_point = JoltPhysics::convert(
JPH::RVec3 surface_point = JoltPhysics::convert(
position + Ogre::Vector3(0, -0.1f, 0));
MyCollector collector(&physics_system, surface_point,
JPH::Vec3::sAxisY(), dt);
JPH::Vec3::sAxisY(), dt);
// Apply buoyancy to all bodies that intersect with the water
JPH::AABox water_box(-JPH::Vec3(1000, 1000, 1000),
JPH::Vec3(1000, 0.1f, 1000));
@@ -1469,19 +1491,21 @@ public:
}
}
bool raycastQuery(Ogre::Vector3 startPoint, Ogre::Vector3 endPoint,
Ogre::Vector3 &position)
Ogre::Vector3 &position, JPH::BodyID &id)
{
int i;
Ogre::Vector3 direction = endPoint - startPoint;
JPH::RRayCast ray{ JoltPhysics::convert(startPoint),
JoltPhysics::convert(direction) };
JoltPhysics::convert<JPH::Vec3>(direction) };
JPH::RayCastResult hit;
bool hadHit = physics_system.GetNarrowPhaseQuery().CastRay(
ray, hit, {},
JPH::SpecifiedObjectLayerFilter(Layers::NON_MOVING));
if (hadHit)
if (hadHit) {
position = JoltPhysics::convert(
ray.GetPointOnRay(hit.mFraction));
id = hit.mBodyID;
}
return hadHit;
}
};
@@ -1596,7 +1620,14 @@ JPH::ShapeRefC
JoltPhysicsWrapper::createOffsetCenterOfMassShape(const Ogre::Vector3 &offset,
JPH::ShapeRefC shape)
{
return phys->createOffsetCenterOfMassShape(offset, shape);
return phys->createOffsetCenterOfMassShape(offset, shape);
}
JPH::ShapeRefC JoltPhysicsWrapper::createRotatedTranslatedShape(
const Ogre::Vector3 &offset, const Ogre::Quaternion rotation,
JPH::ShapeRefC shape)
{
return phys->createRotatedTranslatedShape(offset, rotation, shape);
}
JPH::BodyID
@@ -1765,9 +1796,9 @@ void JoltPhysicsWrapper::removeContactListener(const JPH::BodyID &id)
}
bool JoltPhysicsWrapper::raycastQuery(Ogre::Vector3 startPoint,
Ogre::Vector3 endPoint,
Ogre::Vector3 &position)
Ogre::Vector3 &position, JPH::BodyID &id)
{
return phys->raycastQuery(startPoint, endPoint, position);
return phys->raycastQuery(startPoint, endPoint, position, id);
}
template <>
JoltPhysicsWrapper *Ogre::Singleton<JoltPhysicsWrapper>::msSingleton = 0;

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

View File

@@ -9,13 +9,14 @@ add_library(world-build STATIC world-build.cpp)
target_link_libraries(world-build PRIVATE GameData)
target_include_directories(world-build PUBLIC .)
add_executable(test test.cpp)
target_link_libraries(test PRIVATE action world-build lua GameData OgreMain)
#add_executable(test test.cpp)
#target_link_libraries(test PRIVATE action world-build lua GameData OgreMain)
add_executable(test2 test2.cpp)
target_link_libraries(test2 PRIVATE action world-build lua GameData OgreMain)
#add_executable(test2 test2.cpp)
#target_link_libraries(test2 PRIVATE action world-build lua GameData OgreMain)
add_executable(mark_harbors mark_harbors.cpp)
target_link_libraries(mark_harbors PRIVATE lua OgreMain OgreRTShaderSystem)
#add_executable(mark_harbors mark_harbors.cpp)
#target_link_libraries(mark_harbors PRIVATE lua OgreMain OgreRTShaderSystem)
#add_custom_target(world ALL DEPENDS test test2)
add_custom_target(world ALL DEPENDS test test2)