Lots of updates - lua, narrator, logic, models

This commit is contained in:
2025-09-19 04:35:20 +03:00
parent 4249a0238b
commit 62e14cf075
19 changed files with 924 additions and 157 deletions

View File

@@ -195,6 +195,12 @@ endif()
add_dependencies(TerrainTest stage_lua_scripts stage_files)
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/stories
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_BINARY_DIR}/lua-scripts/stories ${CMAKE_BINARY_DIR}/stories
DEPENDS ${CMAKE_BINARY_DIR}/lua-scripts/stories
)
add_custom_target(stage_stories ALL DEPENDS stage_lua_scripts ${CMAKE_BINARY_DIR}/stories)
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/resources.cfg
COMMAND cp ${CMAKE_SOURCE_DIR}/resources.cfg ${CMAKE_BINARY_DIR}/resources.cfg

View File

@@ -282,10 +282,6 @@ public:
control &= ~256;
return false;
}
void update(float delta)
{
return;
}
void frameRendered(const Ogre::FrameEvent &evt) override;
};
class App : public OgreBites::ApplicationContext {
@@ -432,18 +428,22 @@ public:
}
void updateWorld(float delta)
{
mDynWorld->getBtWorld()->stepSimulation(delta, 4);
mDynWorld->getBtWorld()->stepSimulation(delta, 3);
if (!ECS::get().has<ECS::GUI>())
return;
/* Update window grab */
if (ECS::get().has<ECS::GUI>() &&
ECS::get().get<ECS::GUI>().grabChanged) {
setWindowGrab(ECS::get().get<ECS::GUI>().grab);
ECS::get().get_mut<ECS::GUI>().grabChanged = false;
ECS::GUI &gui = ECS::get().get_mut<ECS::GUI>();
if (gui.grabChanged) {
setWindowGrab(gui.grab);
gui.grabChanged = false;
ECS::get().modified<ECS::GUI>();
}
ECS::update(delta);
/*
if (enabldDbgDraw)
mDbgDraw->update();
*/
}
class InputListenerChainFlexible : public OgreBites::InputListener {
protected:
@@ -761,7 +761,6 @@ void KeyboardListener::frameRendered(const Ogre::FrameEvent &evt)
<< "\n";
fps_timer.reset();
}
update(evt.timeSinceLastFrame);
if (!isGuiEnabled() ||
(isGuiEnabled() && ECS::get<ECS::GUI>().narrationBox)) {
mApp->updateWorld(evt.timeSinceLastFrame);

BIN
assets/blender/edited-normal-male.blend (Stored with Git LFS)

Binary file not shown.

View File

@@ -54,6 +54,20 @@ class ExportMappingMale:
self.files.append({"name": fobj})
class ExportMappingMaleEdited:
blend_path = "assets/blender/" + "edited-normal-male.blend"
gltf_path = "characters/male/normal-male.gltf"
ogre_scene = "characters/male/vroid-normal-male.scene"
inner_path = "Object"
objs = ["male", "Body", "Hair", "Face", "BackHair"]
armature_name = "male"
outfile = "tmp-male.blend"
default_action = 'default'
def __init__(self):
self.files = []
for fobj in self.objs:
self.files.append({"name": fobj})
class ExportMappingMaleClothedEdited:
blend_path = "assets/blender/" + "edited-normal-male.blend"
gltf_path = "characters/male/normal-male.gltf"
ogre_scene = "characters/male/vroid-normal-male.scene"

BIN
assets/blender/vehicles/boat-sails.blend (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/blender/vehicles/boat.blend (Stored with Git LFS)

Binary file not shown.

View File

@@ -63,15 +63,6 @@ dropped you into the sea. Last thing you heard before you hit the water was happ
]]--
local narrator = require('narrator.narrator')
-- Parse a book from the Ink file.
local book = narrator.parse_file('stories.game')
-- Init a story from the book
local story = narrator.init_story(book)
-- Begin the story
story:begin()
function dump(o)
if type(o) == 'table' then
local s = '{ '
@@ -85,26 +76,45 @@ function dump(o)
end
end
--[[
function _narration()
local ret = ""
local choices = {}
if story:can_continue() then
local paragraphs = story:continue()
for _, paragraph in ipairs(paragraphs) do
local text = paragraph.text
if paragraph.tags then
text = text .. ' #' .. table.concat(paragraph.tags, ' #')
print("can continue")
local paragraph = 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 == "mc_is_free" then
ecs_character_set("player", "gravity", true)
ecs_character_set("player", "buoyancy", true)
end
end
ret = ret .. text
end
ret = text
if story:can_choose() then
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
story:choose(1)
choices = {}
end
end
else
print("can NOT continue")
end
print(ret)
if (#choices > 0) then
print("choices!!!")
narrate(ret, choices)
@@ -112,9 +122,134 @@ function _narration()
narrate(ret)
end
end
]]--
function Quest(name, book)
local quest = {
name = name,
active = false,
activate = function(this)
this.active = true
this.story:begin()
this:_narration()
end,
event = function(this, event)
if not this.active then
return
end
if event == "narration_progress" then
this:_narration()
elseif event == "narration_answered" then
local answer = narration_get_answer()
this.story:choose(answer)
print("answered:", answer)
this:_narration()
end
end,
_handle_tag = function(this, tag)
if tag == "mc_is_free" then
ecs_character_set("player", "gravity", true)
ecs_character_set("player", "buoyancy", true)
end
end,
_narration = function(this)
local ret = ""
local choices = {}
local have_choice = false
local have_paragraph = false
if not this.active then
return
end
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
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
if not have_choice and not have_paragraph then
this:complete()
this.active = false
end
print(ret)
if (#choices > 0) then
print("choices!!!")
narrate(ret, choices)
else
narrate(ret)
end
end,
complete = function(this)
print(this.name .. 'complete')
end,
story = narrator.init_story(book)
}
-- Begin the story
return quest
end
function StartGameQuest()
-- Parse a book from the Ink file.
local book = narrator.parse_file('stories.initiation')
local quest = Quest('start game', book)
quest.base = {}
quest.base.activate = quest.activate
quest.base.complete = quest.complete
quest.boat = false
quest.activate = function(this)
print('activate...')
local mc_is_free = function()
ecs_character_params_set("player", "gravity", true)
ecs_character_params_set("player", "buoyancy", true)
end
this.story:bind('mc_is_free', mc_is_free)
this.base.activate(this)
end
quest.complete = function(this)
this.base.complete(this)
this.active = false
if not this.boat then
local boat = ecs_vehicle_set("boat", 0, 0, -20, 1.75)
local trigger = ecs_child_character_trigger(boat, "entered_boat", 0, 0, 0, 3, 3)
local npc = ecs_npc_set("normal-female.glb", 0, 2, -20, 1.75)
this.boat = true
end
end
return quest
end
quests = {}
setup_handler(function(event)
print(event)
for k, v in pairs(quests) do
if v.active then
v:event(event)
end
end
if event == "startup" then
main_menu()
elseif event == "narration_progress" then
@@ -122,12 +257,16 @@ setup_handler(function(event)
elseif event == "narration_answered" then
local answer = narration_get_answer()
story:choose(answer)
print("answered:", answer)
_narration()
elseif event == "new_game" then
local ret = ""
story = narrator.init_story(book)
story:begin()
_narration()
ecs_character_params_set("player", "gravity", true)
ecs_character_params_set("player", "buoyancy", false)
local quest = StartGameQuest()
quests[quest.name] = quest
for k, v in pairs(quests) do
print(k, v.active)
end
quest:activate()
end
end)

View File

@@ -0,0 +1,35 @@
- The party was hot, girls were sexy the wine, beer and whisky were in <>
enormous numbers. It was anniversary since you set sail with your friends.
The sea was calm and everything promised you another great night. <>
The whole year with your friends on your ship over vast seas of the world in decay was almost over. <>
It was so good year full of adventure, romance, indecency and luxury.
You did not know what future had for you and you never thought much about it. However <>
you had zero insights on what to follow even if you cared enough about it. You thought you could not <>
wish for brigher future far away from troubles of this world.
One day (a few days ago) your trusted friends decided that you have too much, they have too much of you and you owe them a lot.<>
They waited for opportunity to get rid of you. You totally missed the point when people started conspiring against you<>
and the following events came at total surprize.
They found you drunk in your room and moved to the deck and used ropes to <>
restrain you and attach a bucket of stones or something else heavy to your body. <>
They left you lying on the deck while were checking your room, looking for papers and something of value.
After a few hours passed two strong people pulled you to the side and <>
dropped you into the sea. You did not see their faces. Last thing you heard before you hit the water was happy laugher...
Suddenly next you found yourself under water out of breath in agony. You did not know nor care about time and place, you just cared for <>
last air leaving your body. All you wished for was quick end.
Suddenly the suffering stopped and your mind cleared. Some impactful presence <>
subdued everything in your mind leaving you with question. The only thing you could see was a strange altar for some ancient deity nearby.
You had very bad feeling about this.
'You have just a few seconds, so be quick' - the voice sounded directly somewhere inside your head.
'WILL you serve me?' Something told me my life depends on the answer.
* Sure
Of course you will.
* Do I have a choice?
You have or you have not. That depends on what you expect.
* I will
I know.
- An abrupt silence.
Then I feel my restraints removed and I started rising to the surface. 'We'll talk again soon enough' - the voice in my head told me.
'Your service will be rewarded. Go enjoy your new life.'
* [Ascend]
~ mc_is_free()
- ->END

View File

@@ -0,0 +1,35 @@
#include <iostream>
#include <OgreMeshManager.h>
#include "Components.h"
#include "GameData.h"
#include "BoatModule.h"
namespace ECS
{
BoatModule::BoatModule(flecs::world &ecs)
{
ecs.module<BoatModule>();
ecs.component<BoatBase>();
ecs.component<BoatBody>();
ecs.component<BoatType>().on_set([](flecs::entity e, BoatType &type) {
int i;
if (e.has<BoatBody>() || e.has<BoatBase>())
return;
BoatBase &boat = e.ensure<BoatBase>();
boat.mEnt = ECS::get<EngineData>().mScnMgr->createEntity(
type.resourceName);
boat.mNode = ECS::get<EngineData>()
.mScnMgr->getRootSceneNode()
->createChildSceneNode(type.position,
type.orientation);
boat.mNode->attachObject(boat.mEnt);
BoatBody &body = e.ensure<BoatBody>();
body.body = ECS::get<EngineData>().mWorld->addRigidBody(
0, boat.mEnt, Ogre::Bullet::CT_HULL, nullptr, 2,
0x7fffffff);
e.modified<BoatBody>();
});
}
}

28
src/gamedata/BoatModule.h Normal file
View File

@@ -0,0 +1,28 @@
#ifndef BOAT_MODULE_H_
#define BOAT_MODULE_H_
#include <flecs.h>
#include <Ogre.h>
namespace Ogre
{
class Entity;
class SceneNode;
}
namespace ECS
{
struct BoatType {
Ogre::String resourceName;
Ogre::Vector3 position;
Ogre::Quaternion orientation;
};
struct BoatBase {
Ogre::Entity *mEnt;
Ogre::SceneNode *mNode;
};
struct BoatBody {
btRigidBody *body;
};
struct BoatModule {
BoatModule(flecs::world &ecs);
};
}
#endif

View File

@@ -1,5 +1,6 @@
project(gamedata)
find_package(OGRE REQUIRED COMPONENTS Bites Bullet Paging Terrain Overlay CONFIG)
add_library(GameData STATIC GameData.cpp CharacterModule.cpp WaterModule.cpp SunModule.cpp TerrainModule.cpp GUIModule.cpp LuaData.cpp WorldMapModule.cpp)
add_library(GameData STATIC GameData.cpp CharacterModule.cpp WaterModule.cpp SunModule.cpp TerrainModule.cpp GUIModule.cpp LuaData.cpp WorldMapModule.cpp
BoatModule.cpp EventTriggerModule.cpp)
target_link_libraries(GameData PUBLIC OgreMain OgreBites OgreBullet OgrePaging OgreTerrain OgreOverlay flecs::flecs_static lua)
target_include_directories(GameData PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

View File

@@ -15,16 +15,7 @@ CharacterModule::CharacterModule(flecs::world &ecs)
ecs.component<Player>();
ecs.component<CharacterBase>();
ecs.component<CharacterVelocity>();
ecs.component<CharacterBody>().on_add(
[](flecs::entity e, CharacterBody &body) {
e.set<CharacterVelocity>(
{ { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } });
body.checkGround = false;
body.checkGroundResult = false;
body.mCollisionShape = nullptr;
body.mGhostObject = nullptr;
body.mController = nullptr;
});
ecs.component<CharacterBody>();
ecs.system<EngineData, CharacterBase>("UpdateTimer")
.kind(flecs::OnUpdate)
.each([this](EngineData &eng, CharacterBase &ch) {
@@ -211,15 +202,19 @@ CharacterModule::CharacterModule(flecs::world &ecs)
ch.is_submerged = false;
});
ecs.system<const EngineData, const CharacterBase, CharacterVelocity>(
"HandleGravity")
"HandleGravityBouyanceWater")
.kind(flecs::OnUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.with<InWater>()
.each([this](flecs::entity e, const EngineData &eng,
const CharacterBase &ch, CharacterVelocity &gr) {
Ogre::Vector3 gravity(0, -9.8f, 0);
Ogre::Vector3 pos = ch.mBodyNode->getPosition();
if (e.has<InWater>()) {
Ogre::Vector3 v(0, 0, 0);
if (e.has<CharacterGravity>())
v += gravity;
if (e.has<CharacterBuoyancy>()) {
float volume = 2.0f * 0.5f * 0.5f;
float density = 900.0f;
float full_subm = 2.0f;
@@ -235,22 +230,39 @@ CharacterModule::CharacterModule(flecs::world &ecs)
Ogre::Vector3 b = -gravity * density * volume *
multiplier * current_subm /
full_subm / mass;
gr.gvelocity += (gravity + b) * eng.delta;
gr.gvelocity.y = Ogre::Math::Clamp(
gr.gvelocity.y, -2.5f, 2.5f);
} else {
gr.gvelocity += gravity * eng.delta;
if (pos.y < -1.2) {
gr.gvelocity.y = 0.0f;
}
v += b;
}
gr.gvelocity *= 0.99;
gr.gvelocity += v * eng.delta;
gr.gvelocity.y =
Ogre::Math::Clamp(gr.gvelocity.y, -2.5f, 1.5f);
gr.gvelocity *= (1.0 - eng.delta);
gr.velocity.y *= (1.0 - eng.delta);
});
ecs.system<const EngineData, const CharacterBase, CharacterVelocity>(
"HandleGravityNoWater")
.kind(flecs::OnUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.without<InWater>()
.with<CharacterGravity>()
.each([this](flecs::entity e, const EngineData &eng,
const CharacterBase &ch, CharacterVelocity &gr) {
Ogre::Vector3 gravity(0, -9.8f, 0);
Ogre::Vector3 pos = ch.mBodyNode->getPosition();
gr.gvelocity += gravity * eng.delta;
if (pos.y < -1.2) {
gr.gvelocity.y = 0.0f;
}
gr.gvelocity *= (1.0 - eng.delta);
gr.velocity.y *= (1.0 - eng.delta);
});
ecs.system<const EngineData, const AnimationControl,
const CharacterBase, CharacterVelocity>("HandleSwimming")
.kind(flecs::OnUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.with<InWater>()
.with<CharacterBuoyancy>()
.each([this](flecs::entity e, const EngineData &eng,
const AnimationControl &anim,
const CharacterBase &ch, CharacterVelocity &gr) {
@@ -479,65 +491,11 @@ CharacterModule::CharacterModule(flecs::world &ecs)
CharacterBody>("UpdateCharacterBase")
.kind(flecs::OnUpdate)
.with<Character>()
.with<CharacterBody>()
.with<CharacterBase>()
.each([](const EngineData &eng, CharacterLocation &loc,
CharacterBase &ch, CharacterBody &body) {
if (!ch.mBodyNode) {
body.mController = nullptr;
ch.mBodyEnt = eng.mScnMgr->createEntity(
"normal-male.glb");
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();
body.mGhostObject =
new btPairCachingGhostObject();
body.mCollisionShape =
new btCompoundShape(false);
body.mGhostObject->setCollisionShape(
body.mCollisionShape);
{
btVector3 inertia(0, 0, 0);
// mCollisionShape = new btCompoundShape();
btScalar height = 1.0f;
btScalar radius = 0.3f;
btCapsuleShape *shape =
new btCapsuleShape(
radius,
2 * height -
2 * radius);
btTransform transform;
transform.setIdentity();
transform.setOrigin(btVector3(0, 1, 0));
static_cast<btCompoundShape *>(
body.mCollisionShape)
->addChildShape(transform,
shape);
btScalar masses[1] = { 0 };
btTransform principal;
static_cast<btCompoundShape *>(
body.mCollisionShape)
->calculatePrincipalAxisTransform(
masses, principal,
inertia);
}
body.mGhostObject->setCollisionFlags(
btCollisionObject::CF_KINEMATIC_OBJECT /*|
btCollisionObject::CF_NO_CONTACT_RESPONSE */);
body.mGhostObject->setActivationState(
DISABLE_DEACTIVATION);
eng.mWorld->attachCollisionObject(
body.mGhostObject, ch.mBodyEnt, 1,
0x7FFFFFFF);
OgreAssert(body.mGhostObject,
"Need GhostObject");
OgreAssert(body.mCollisionShape,
"No collision shape");
OgreAssert(ch.mSkeleton->hasBone("Root"),
"No root bone");
ch.mRootBone = ch.mSkeleton->getBone("Root");
OgreAssert(ch.mRootBone, "No root bone");
} else {
loc.orientation =
ch.mBodyNode->_getDerivedOrientation();
@@ -545,6 +503,77 @@ CharacterModule::CharacterModule(flecs::world &ecs)
ch.mBodyNode->_getDerivedPosition();
}
});
ecs.system<const EngineData, const CharacterLocation,
const CharacterConf>("SetupCharacter")
.kind(flecs::OnUpdate)
.with<Character>()
.without<CharacterBase>()
.without<CharacterBody>()
.each([](flecs::entity e, const EngineData &eng,
const CharacterLocation &loc,
const CharacterConf &conf) {
CharacterBase &ch = e.ensure<CharacterBase>();
CharacterBody &body = e.ensure<CharacterBody>();
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.mSkeleton->hasBone("Root"),
"No root bone");
ch.mRootBone = ch.mSkeleton->getBone("Root");
OgreAssert(ch.mRootBone, "No root bone");
body.mController = nullptr;
e.set<CharacterVelocity>(
{ { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } });
body.checkGround = false;
body.checkGroundResult = false;
body.mCollisionShape = nullptr;
body.mGhostObject = nullptr;
body.mController = nullptr;
body.mGhostObject = new btPairCachingGhostObject();
body.mCollisionShape = new btCompoundShape(false);
body.mGhostObject->setCollisionShape(
body.mCollisionShape);
{
btVector3 inertia(0, 0, 0);
// mCollisionShape = new btCompoundShape();
btScalar height = 1.0f;
btScalar radius = 0.3f;
btCapsuleShape *shape = new btCapsuleShape(
radius, 2 * height - 2 * radius);
btTransform transform;
transform.setIdentity();
transform.setOrigin(btVector3(0, 1, 0));
static_cast<btCompoundShape *>(
body.mCollisionShape)
->addChildShape(transform, shape);
btScalar masses[1] = { 0 };
btTransform principal;
static_cast<btCompoundShape *>(
body.mCollisionShape)
->calculatePrincipalAxisTransform(
masses, principal, inertia);
}
body.mGhostObject->setCollisionFlags(
btCollisionObject::CF_KINEMATIC_OBJECT /*|
btCollisionObject::CF_NO_CONTACT_RESPONSE */);
body.mGhostObject->setActivationState(
DISABLE_DEACTIVATION);
eng.mWorld->attachCollisionObject(
body.mGhostObject, ch.mBodyEnt, 1, 0x7FFFFFFF);
OgreAssert(body.mGhostObject, "Need GhostObject");
OgreAssert(body.mCollisionShape, "No collision shape");
e.add<CharacterGravity>();
e.add<CharacterBuoyancy>();
anim.currentAnim = AnimationControl::ANIM_NONE;
anim.nextAnim = AnimationControl::ANIM_NONE;
anim.reset = false;
anim.configured = false;
});
ecs.system<const EngineData, CharacterBase, CharacterBody>(
"UpdateCharacterPhysics")
.kind(flecs::OnUpdate)

View File

@@ -7,6 +7,8 @@ struct Camera;
/* character */
struct Character {}; /* tag */
struct Player {}; /* tag */
struct CharacterGravity {};
struct CharacterBuoyancy {};
struct CharacterBase {
Ogre::String type;
float mTimer;
@@ -22,6 +24,9 @@ struct CharacterLocation {
Ogre::Quaternion orientation;
Ogre::Vector3 position;
};
struct CharacterConf {
Ogre::String type;
};
struct CharacterBody {
btPairCachingGhostObject *mGhostObject;
btCompoundShape *mCollisionShape;

View File

@@ -0,0 +1,255 @@
#include <iostream>
#include <OgreBullet.h>
#include <OgreMeshManager.h>
#include "Components.h"
#include "GameData.h"
#include "LuaData.h"
#include "EventTriggerModule.h"
struct TriggerBody {
btPairCachingGhostObject *mBody;
btCylinderShape *shape;
Ogre::SceneNode *mSceneNode;
std::set<const btCollisionObject *> contactBodies;
};
struct DeepPenetrationContactResultCallback : public btManifoldResult {
DeepPenetrationContactResultCallback(
const btCollisionObjectWrapper *body0Wrap,
const btCollisionObjectWrapper *body1Wrap)
: btManifoldResult(body0Wrap, body1Wrap)
, mPenetrationDistance(0)
, mOtherIndex(0)
{
}
float mPenetrationDistance;
int mOtherIndex;
btVector3 mNormal, mPoint;
void reset()
{
mPenetrationDistance = 0.0f;
}
bool hasHit()
{
return mPenetrationDistance < 0.0f;
}
virtual void addContactPoint(const btVector3 &normalOnBInWorld,
const btVector3 &pointInWorldOnB,
btScalar depth)
{
#ifdef VDEBUG
std::cout
<< "contact: " << Ogre::Bullet::convert(pointInWorldOnB)
<< " " << Ogre::Bullet::convert(normalOnBInWorld)
<< "\n";
#endif
if (mPenetrationDistance > depth) { // Has penetration?
const bool isSwapped =
m_manifoldPtr->getBody0() !=
m_body0Wrap->getCollisionObject();
mPenetrationDistance = depth;
mOtherIndex = isSwapped ? m_index0 : m_index1;
mPoint = isSwapped ? (pointInWorldOnB +
(normalOnBInWorld * depth)) :
pointInWorldOnB;
mNormal = isSwapped ? normalOnBInWorld * -1 :
normalOnBInWorld;
}
}
};
ECS::EventTriggerModule::EventTriggerModule(flecs::world &ecs)
{
ecs.module<EventTriggerModule>();
ecs.component<EventTriggerExit>();
ecs.component<TriggerBody>().on_add([](flecs::entity e,
TriggerBody &body) {
bool kinematic = false;
if (e.get<EventTrigger>().parent) {
body.mSceneNode =
e.get<EventTrigger>()
.parent->createChildSceneNode(
e.get<EventTrigger>().position,
Ogre::Quaternion(0, 0, 0, 1));
kinematic = true;
} else {
body.mSceneNode =
ECS::get<EngineData>()
.mScnMgr->getRootSceneNode()
->createChildSceneNode(
e.get<EventTrigger>().position,
Ogre::Quaternion(0, 0, 0, 1));
}
Ogre::MeshPtr mesh =
Ogre::MeshManager::getSingleton().createManual(
"trigger", "General");
Ogre::Entity *ent =
ECS::get<EngineData>().mScnMgr->createEntity(mesh);
body.mSceneNode->attachObject(ent);
body.mBody = new btPairCachingGhostObject();
body.mBody->getWorldTransform().setOrigin(Ogre::Bullet::convert(
body.mSceneNode->_getDerivedPosition()));
float h = e.get<EventTrigger>().halfheight;
float r = e.get<EventTrigger>().radius;
Ogre::Vector3 position = e.get<EventTrigger>().position;
body.shape = new btCylinderShape(btVector3(r, h, r));
body.mBody->setCollisionShape(body.shape);
int flags = body.mBody->getCollisionFlags();
flags |= btCollisionObject::CF_NO_CONTACT_RESPONSE;
if (kinematic)
flags |= btCollisionObject::CF_STATIC_OBJECT;
body.mBody->setCollisionFlags(flags);
ECS::get<EngineData>().mWorld->attachCollisionObject(
body.mBody, ent, 16, 0x1);
});
ecs.component<EventTrigger>().on_set(
[](flecs::entity e, EventTrigger &ev) {
e.add<TriggerBody>();
});
ecs.system<const EngineData, const EventTrigger, TriggerBody>(
"CheckCollisions")
.kind(flecs::OnUpdate)
.each([](flecs::entity e, const EngineData &eng,
const EventTrigger &evt, TriggerBody &body) {
btDispatcher *dispatch =
eng.mWorld->getBtWorld()->getDispatcher();
btHashedOverlappingPairCache *cache =
body.mBody->getOverlappingPairCache();
int i;
int count = cache->getNumOverlappingPairs();
std::set<const btCollisionObject *> currentContactBodies;
if (count > 0) {
btManifoldArray contacts;
btBroadphasePairArray &collisionPairs =
cache->getOverlappingPairArray();
for (i = 0; i < count; i++) {
contacts.resize(0);
if (collisionPairs[i].m_algorithm) {
collisionPairs[i]
.m_algorithm
->getAllContactManifolds(
contacts);
OgreAssert(false,
"Not implemented");
} else {
const btBroadphasePair *collisionPairPtr =
eng.mWorld->getBtWorld()
->getBroadphase()
->getOverlappingPairCache()
->findPair(
collisionPairs[i]
.m_pProxy0,
collisionPairs[i]
.m_pProxy1);
if (collisionPairPtr) {
const btBroadphasePair
&collisionPair =
*collisionPairPtr;
const btCollisionObject *objA = static_cast<
btCollisionObject
*>(
collisionPair
.m_pProxy0
->m_clientObject);
const btCollisionObject *objB = static_cast<
btCollisionObject
*>(
collisionPair
.m_pProxy1
->m_clientObject);
const btCollisionObject
*me,
*other;
if (objA ==
static_cast<
btCollisionObject
*>(
body.mBody)) {
me = objA;
other = objB;
} else {
me = objB;
other = objA;
}
const btCollisionShape *my_shape =
me->getCollisionShape();
const btCollisionShape *other_shape =
other->getCollisionShape();
btCollisionObjectWrapper obA(
NULL, my_shape,
body.mBody,
body.mBody
->getWorldTransform(),
-1, i);
btCollisionObjectWrapper obB(
NULL,
other_shape,
other,
other->getWorldTransform(),
-1, 0);
btCollisionAlgorithm *algorithm =
dispatch->findAlgorithm(
&obA,
&obB,
NULL,
BT_CONTACT_POINT_ALGORITHMS);
DeepPenetrationContactResultCallback
contactPointResult(
&obA,
&obB);
algorithm->processCollision(
&obA, &obB,
eng.mWorld
->getBtWorld()
->getDispatchInfo(),
&contactPointResult);
algorithm
->~btCollisionAlgorithm();
dispatch->freeCollisionAlgorithm(
algorithm);
if (contactPointResult
.hasHit()) {
currentContactBodies
.insert(other);
if (body.contactBodies
.find(other) ==
body.contactBodies
.end()) {
body.contactBodies
.insert(other);
ECS::get<
LuaBase>()
.mLua
->call_handler(
evt.event);
}
}
}
}
}
}
std::set<const btCollisionObject *>::iterator it =
body.contactBodies.begin();
while (it != body.contactBodies.end()) {
if (currentContactBodies.find(*it) ==
currentContactBodies.end()) {
if (e.has<EventTriggerExit>()) {
const Ogre::String &exit_event =
ECS::get<
EventTriggerExit>()
.event;
ECS::get<LuaBase>()
.mLua->call_handler(
exit_event);
} else {
std::cout << "body exited"
<< std::endl;
}
body.contactBodies.erase(*it);
}
it++;
}
});
}

View File

@@ -0,0 +1,21 @@
#ifndef EVENT_TRIGGER_MODULE_H_
#define EVENT_TRIGGER_MODULE_H_
#include <flecs.h>
#include <Ogre.h>
namespace ECS
{
struct EventTrigger {
Ogre::SceneNode *parent;
Ogre::Vector3 position;
float halfheight;
float radius;
Ogre::String event;
};
struct EventTriggerExit {
Ogre::String event;
};
struct EventTriggerModule {
EventTriggerModule(flecs::world &ecs);
};
}
#endif

View File

@@ -9,6 +9,8 @@
#include "GUIModule.h"
#include "LuaData.h"
#include "WorldMapModule.h"
#include "BoatModule.h"
#include "EventTriggerModule.h"
namespace ECS
{
@@ -43,6 +45,9 @@ void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world,
ecs.import <LuaModule>();
ecs.import <WorldMapModule>();
ecs.import <LuaModule>();
ecs.import <BoatModule>();
ecs.import <EventTriggerModule>();
ecs.system<EngineData>("UpdateDelta")
.kind(flecs::OnUpdate)
.each([](EngineData &eng) {
@@ -95,19 +100,7 @@ void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world,
player = ecs.entity("player");
Ogre::Vector3 playerPos(0, 0, 4);
player.set<CharacterLocation>({ { 0, 0, 0, 1 }, playerPos });
player.set<AnimationControl>({ AnimationControl::ANIM_NONE,
AnimationControl::ANIM_NONE, false,
false });
player.set<CharacterBase>({ "normal-male.glb",
0.0f,
nullptr,
nullptr,
nullptr,
nullptr,
{ 0, 0, 0 },
{ 0, 0, 0 },
false });
player.set<CharacterBody>({ nullptr, nullptr, nullptr, false, false });
player.set<CharacterConf>({ "normal-male.glb" });
player.add<Character>();
player.add<Player>();
}

View File

@@ -2,6 +2,9 @@
#include "GameData.h"
#include "Components.h"
#include "GUIModule.h"
#include "CharacterModule.h"
#include "BoatModule.h"
#include "EventTriggerModule.h"
#include "LuaData.h"
extern "C" {
@@ -9,6 +12,32 @@ int luaopen_lpeg(lua_State *L);
}
namespace ECS
{
struct idmap {
std::unordered_map<int, flecs::entity> id2entity;
int next_id;
idmap()
: id2entity({})
, next_id(0)
{
}
int get_next_id()
{
next_id++;
return next_id;
}
int add_entity(flecs::entity e)
{
int id = get_next_id();
id2entity[id] = e;
return id;
}
flecs::entity get_entity(int id)
{
OgreAssert(id2entity[id].is_valid(), "Invalid entity");
return id2entity[id];
}
};
struct idmap idmap;
int LuaData::setup_handler()
{
@@ -161,9 +190,6 @@ LuaData::LuaData()
ECS::modified<GUI>();
std::cout << "narration ended\n";
} else if (message.length() > 0) {
std::replace(message.begin(), message.end(), '\n', ' ');
std::replace(message.begin(), message.end(), '\r', ' ');
ECS::get_mut<GUI>().enabled = true;
ECS::get_mut<GUI>().grab = false;
ECS::get_mut<GUI>().grabChanged = true;
@@ -191,6 +217,134 @@ LuaData::LuaData()
return 0;
});
lua_setglobal(L, "main_menu");
lua_pushcfunction(L, [](lua_State *L) -> int {
OgreAssert(lua_gettop(L) == 3, "Invalid parameters");
luaL_checktype(L, 1, LUA_TSTRING);
luaL_checktype(L, 2, LUA_TSTRING);
luaL_checktype(L, 3, LUA_TBOOLEAN);
bool enable = lua_toboolean(L, 3);
flecs::entity e = ECS::get().lookup(lua_tostring(L, 1));
Ogre::String what = lua_tostring(L, 2);
OgreAssert(e.is_valid(), "Invalid character");
OgreAssert(e.has<Character>(), "Not a character");
if (what == "gravity") {
/* clear momentum */
e.get_mut<CharacterVelocity>().gvelocity.y = 0.0f;
e.get_mut<CharacterVelocity>().velocity.y = 0.0f;
e.modified<CharacterVelocity>();
if (enable)
e.add<CharacterGravity>();
else
e.remove<CharacterGravity>();
} else if (what == "buoyancy") {
if (enable)
e.add<CharacterBuoyancy>();
else
e.remove<CharacterBuoyancy>();
} else
OgreAssert(false, "Bad parameter " + what);
return 0;
});
lua_setglobal(L, "ecs_character_params_set");
lua_pushcfunction(L, [](lua_State *L) -> int {
luaL_checktype(L, 1, LUA_TSTRING);
luaL_checktype(L, 2, LUA_TNUMBER);
luaL_checktype(L, 3, LUA_TNUMBER);
luaL_checktype(L, 4, LUA_TNUMBER);
luaL_checktype(L, 5, LUA_TNUMBER);
Ogre::String what = lua_tostring(L, 1);
if (what == "boat") {
float yaw = lua_tonumber(L, 5);
float x = lua_tonumber(L, 2);
float y = lua_tonumber(L, 3);
float z = lua_tonumber(L, 4);
flecs::entity e = ECS::get().entity();
Ogre::Quaternion orientation(Ogre::Radian(yaw),
Ogre::Vector3(0, 1, 0));
Ogre::Vector3 position(x, y, z);
e.set<BoatType>({ "boat.glb", position, orientation });
lua_pushinteger(L, idmap.add_entity(e));
return 1;
}
lua_pushinteger(L, -1);
return 1;
});
lua_setglobal(L, "ecs_vehicle_set");
lua_pushcfunction(L, [](lua_State *L) -> int {
OgreAssert(lua_gettop(L) == 6, "bad parameters");
luaL_checktype(L, 1, LUA_TSTRING); // event
luaL_checktype(L, 2, LUA_TNUMBER); // x
luaL_checktype(L, 3, LUA_TNUMBER); // y
luaL_checktype(L, 4, LUA_TNUMBER); // z
luaL_checktype(L, 5, LUA_TNUMBER); // halfh
luaL_checktype(L, 6, LUA_TNUMBER); // radius
flecs::entity e = ECS::get().entity();
Ogre::String event = lua_tostring(L, 1);
float x = lua_tonumber(L, 2);
float y = lua_tonumber(L, 3);
float z = lua_tonumber(L, 4);
float h = lua_tonumber(L, 5);
float r = lua_tonumber(L, 6);
Ogre::Vector3 position(x, y, z);
e.set<EventTrigger>({ nullptr, position, h, r, event });
lua_pushinteger(L, idmap.add_entity(e));
return 1;
});
lua_setglobal(L, "ecs_character_trigger");
lua_pushcfunction(L, [](lua_State *L) -> int {
OgreAssert(lua_gettop(L) == 7, "bad parameters");
luaL_checktype(L, 1, LUA_TNUMBER); // parent
luaL_checktype(L, 2, LUA_TSTRING); // event
luaL_checktype(L, 3, LUA_TNUMBER); // x
luaL_checktype(L, 4, LUA_TNUMBER); // y
luaL_checktype(L, 5, LUA_TNUMBER); // z
luaL_checktype(L, 6, LUA_TNUMBER); // halfh
luaL_checktype(L, 7, LUA_TNUMBER); // radius
int parent = lua_tointeger(L, 1);
flecs::entity parent_e = idmap.get_entity(parent);
Ogre::SceneNode *parentNode = nullptr;
if (parent_e.has<CharacterBase>())
parentNode = parent_e.get<CharacterBase>().mBodyNode;
else if (parent_e.has<BoatBase>())
parentNode = parent_e.get<BoatBase>().mNode;
flecs::entity e = ECS::get().entity().child_of(parent_e);
Ogre::String event = lua_tostring(L, 2);
float x = lua_tonumber(L, 3);
float y = lua_tonumber(L, 4);
float z = lua_tonumber(L, 5);
float h = lua_tonumber(L, 6);
float r = lua_tonumber(L, 7);
OgreAssert(parentNode, "bad parent");
Ogre::Vector3 position(x, y, z);
e.set<EventTrigger>({ parentNode, position, h, r, event });
lua_pushinteger(L, idmap.add_entity(e));
return 1;
});
lua_setglobal(L, "ecs_child_character_trigger");
lua_pushcfunction(L, [](lua_State *L) -> int {
OgreAssert(lua_gettop(L) == 5, "Invalid parameters");
luaL_checktype(L, 1, LUA_TSTRING); // type
luaL_checktype(L, 2, LUA_TNUMBER);
luaL_checktype(L, 3, LUA_TNUMBER);
luaL_checktype(L, 4, LUA_TNUMBER);
luaL_checktype(L, 5, LUA_TNUMBER);
Ogre::String type = lua_tostring(L, 1);
float yaw = lua_tonumber(L, 5);
float x = lua_tonumber(L, 2);
float y = lua_tonumber(L, 3);
float z = lua_tonumber(L, 4);
flecs::entity e = ECS::get().entity();
Ogre::Quaternion orientation(Ogre::Radian(yaw),
Ogre::Vector3(0, 1, 0));
Ogre::Vector3 npcPos(x, y, z);
e.set<CharacterLocation>({ orientation, npcPos });
e.set<CharacterConf>({ type });
e.add<Character>();
lua_pushinteger(L, idmap.add_entity(e));
return 1;
});
lua_setglobal(L, "ecs_npc_set");
}
LuaData::~LuaData()
@@ -200,6 +354,7 @@ LuaData::~LuaData()
void LuaData::lateSetup()
{
#if 0
Ogre::DataStreamList streams =
Ogre::ResourceGroupManager::getSingleton().openResources(
"*.lua", "LuaScripts");
@@ -212,6 +367,16 @@ void LuaData::lateSetup()
OgreAssert(false, "Script failure");
}
}
#endif
Ogre::DataStreamPtr stream =
Ogre::ResourceGroupManager::getSingleton().openResource(
"data.lua", "LuaScripts");
std::cout << "stream: " << stream->getAsString() << "\n";
if (luaL_dostring(L, stream->getAsString().c_str()) != LUA_OK) {
std::cout << "error: " << lua_tostring(L, -1) << "\n";
OgreAssert(false, "Script failure");
}
const char *lua_code = "\n\
function stuff()\n\
return 4\n\
@@ -226,8 +391,14 @@ void LuaData::lateSetup()
LuaModule::LuaModule(flecs::world &ecs)
{
ecs.component<LuaBase>().add(flecs::Singleton);
ecs.set<LuaBase>({ OGRE_NEW LuaData, false, false });
ecs.component<LuaBase>()
.on_add([](LuaBase &lua) {
lua.mLua = new LuaData;
lua.setup_called = false;
lua.startup_called = false;
})
.add(flecs::Singleton);
ecs.add<LuaBase>();
ecs.system<const EngineData, LuaBase>("LuaUpdate")
.kind(flecs::OnUpdate)
.each([](const EngineData &eng, LuaBase &lua) {

View File

@@ -17,7 +17,7 @@
#include "TerrainModule.h"
#define TERRAIN_SIZE 129
#define TERRAIN_WORLD_SIZE 4000.0f
#define TERRAIN_WORLD_SIZE 1000.0f
#define ENDLESS_TERRAIN_FILE_PREFIX Ogre::String("EndlessWorldTerrain")
#define ENDLESS_TERRAIN_FILE_SUFFIX Ogre::String("dat")
@@ -82,10 +82,11 @@ struct HeightData {
world_y + (int)img.getHeight() * BRUSH_SIZE / 2;
Ogre::ColourValue color, colorb1, colorb2;
// float d;
int map_img_x = world_img_x / (BRUSH_SIZE);
int map_img_y = world_img_y / (BRUSH_SIZE);
int brush_img_x = world_img_x % BRUSH_SIZE;
int brush_img_y = world_img_y % BRUSH_SIZE;
int div = 1;
int map_img_x = world_img_x / (BRUSH_SIZE) / div;
int map_img_y = world_img_y / (BRUSH_SIZE) / div;
int brush_img_x = (world_img_x / div) % BRUSH_SIZE;
int brush_img_y = (world_img_y / div) % BRUSH_SIZE;
if (world_img_x < 0 ||
world_img_x >= img.getWidth() * BRUSH_SIZE ||
world_img_y < 0 ||
@@ -316,6 +317,11 @@ public:
what->attachObject(ent);
what->setOrientation(item.rotation);
what->setPosition(item.position);
ECS::get<EngineData>()
.mWorld->addRigidBody(
0, ent,
Ogre::Bullet::CT_TRIMESH,
nullptr, 2, 0x7fffffff);
}
} else {
output.push_back(collider_queue.front());
@@ -367,6 +373,8 @@ struct TerrainPrivate {
TerrainModule::TerrainModule(flecs::world &ecs)
{
struct CanSetPlayerPosition {};
ecs.component<CanSetPlayerPosition>().add(flecs::Singleton);
ecs.component<Terrain>().add(flecs::Singleton);
ecs.component<TerrainPrivate>().add(flecs::Singleton);
ecs.set<TerrainPrivate>({ nullptr, {} });
@@ -528,8 +536,8 @@ TerrainModule::TerrainModule(flecs::world &ecs)
0);
item.position.y = height;
placement.altar_items.push_back(item);
for (i = -64000; i < 64000; i += 1000)
for (j = -64000; j < 64000; j += 1000) {
for (i = -64000; i < 64000; i += 2000)
for (j = -64000; j < 64000; j += 2000) {
if (i == 0 && j == 0)
continue;
Ogre::Vector3 position(i, 0, j);
@@ -566,6 +574,7 @@ TerrainModule::TerrainModule(flecs::world &ecs)
#endif
if (height > -9.0f)
continue;
#ifdef VDEBUG
std::cout << "worldSize: "
<< worldSize - 1
<< std::endl;
@@ -573,6 +582,7 @@ TerrainModule::TerrainModule(flecs::world &ecs)
<< " " << j << " "
<< height
<< std::endl;
#endif
item.entity = "altar.glb";
item.rotation =
Ogre::Quaternion(0, 0,
@@ -582,6 +592,7 @@ TerrainModule::TerrainModule(flecs::world &ecs)
placement.altar_items.push_back(
item);
}
#ifdef VDEBUG
for (i = 0; i < placement.altar_items.size();
i++) {
std::cout << "placement: " << i << " "
@@ -589,20 +600,38 @@ TerrainModule::TerrainModule(flecs::world &ecs)
.position
<< std::endl;
}
flecs::entity player = ECS::player;
CharacterLocation &loc =
player.get_mut<CharacterLocation>();
height = get_height(terrain.mTerrainGroup,
loc.position);
loc.position.y = height + 0.0f;
player.get<CharacterBase>()
.mBodyNode->setPosition(loc.position);
player.get<CharacterBase>()
.mBodyNode->setOrientation(
Ogre::Quaternion());
player.modified<CharacterLocation>();
#endif
ECS::get().add<CanSetPlayerPosition>();
}
});
ecs.system<const Terrain>("SetPlayerPosition")
.kind(flecs::OnUpdate)
.with<CanSetPlayerPosition>()
.each([this](const Terrain &terrain) {
flecs::entity player = ECS::player;
if (!player.is_valid())
return;
if (!player.has<CharacterLocation>())
return;
if (!player.has<CharacterBase>())
return;
if (!player.has<Player>())
return;
CharacterLocation &loc =
player.get_mut<CharacterLocation>();
const CharacterBase &ch = player.get<CharacterBase>();
if (!ch.mBodyNode) {
std::cout << "no player yet";
return;
}
float height =
get_height(terrain.mTerrainGroup, loc.position);
loc.position.y = height + 0.0f;
ch.mBodyNode->setPosition(loc.position);
ch.mBodyNode->setOrientation(Ogre::Quaternion());
player.modified<CharacterLocation>();
ECS::get().remove<CanSetPlayerPosition>();
});
}
float TerrainModule::get_height(Ogre::TerrainGroup *group,
const Ogre::Vector3 &position)

View File

@@ -349,7 +349,9 @@ WaterModule::WaterModule(flecs::world &ecs)
water.mReflectionCamera->setNearClipDistance(
camera.mCamera->getNearClipDistance());
water.mReflectionCamera->setFarClipDistance(
camera.mCamera->getFarClipDistance());
Ogre::Math::Clamp(
camera.mCamera->getFarClipDistance(),
10.0f, 10.0f));
water.mReflectionCamera->enableCustomNearClipPlane(
water.mReflectionClipPlaneAbove);
water.mReflectionCamera->enableReflection(
@@ -410,7 +412,9 @@ WaterModule::WaterModule(flecs::world &ecs)
water.mRefractionCamera->setNearClipDistance(
camera.mCamera->getNearClipDistance());
water.mRefractionCamera->setFarClipDistance(
camera.mCamera->getFarClipDistance());
Ogre::Math::Clamp(
camera.mCamera->getFarClipDistance(),
1.0f, 10.0f));
water.mRefractionCamera->enableCustomNearClipPlane(
water.mRefractionClipPlaneAbove);
@@ -507,14 +511,14 @@ WaterModule::WaterModule(flecs::world &ecs)
water.mWaterNode->translate(d * 3.0f * delta);
else
water.mWaterNode->translate(d);
water.mWaterEnt->setVisible(false);
// water.mWaterEnt->setVisible(false);
water.mViewports[0]->update();
water.mViewports[1]->update();
water.mRenderTargetListener.mInDepth = true;
water.mViewports[2]->update();
water.mViewports[3]->update();
water.mRenderTargetListener.mInDepth = false;
water.mWaterEnt->setVisible(true);
// water.mRenderTargetListener.mInDepth = true;
// water.mViewports[2]->update();
// water.mViewports[3]->update();
// water.mRenderTargetListener.mInDepth = false;
// water.mWaterEnt->setVisible(true);
});
ecs.system<const EngineData, const WaterSurface, WaterBody>(
"UpdateWaterBody")