841 lines
29 KiB
C++
841 lines
29 KiB
C++
#include <iostream>
|
|
#include <algorithm>
|
|
#include <nlohmann/json.hpp>
|
|
#include <OgreTerrainGroup.h>
|
|
#include <OgreFileSystemLayer.h>
|
|
#include <OgreRTShaderSystem.h>
|
|
#include <OgreStaticGeometry.h>
|
|
#include <OgreMeshLodGenerator.h>
|
|
#include <OgreWorkQueue.h>
|
|
#include <Procedural.h>
|
|
#include "Components.h"
|
|
#include "GameData.h"
|
|
#include "TerrainModule.h"
|
|
#include "physics.h"
|
|
#include "PhysicsModule.h"
|
|
#include "CharacterManagerModule.h"
|
|
#include "items.h"
|
|
#include "StaticGeometryModule.h"
|
|
#include "CharacterAIModule.h"
|
|
#include <tracy/Tracy.hpp>
|
|
|
|
namespace ECS
|
|
{
|
|
|
|
static bool itemsLoaded = false;
|
|
static bool furnitureLoaded = false;
|
|
static bool templatesLoaded = false;
|
|
static std::list<std::pair<long, long> > addQueue;
|
|
StaticGeometryModule::StaticGeometryModule(flecs::world &ecs)
|
|
{
|
|
ZoneScoped;
|
|
ecs.module<StaticGeometryModule>();
|
|
ecs.import <CharacterManagerModule>();
|
|
ecs.import <CharacterAIModule>();
|
|
ecs.component<TerrainSlotParent>();
|
|
ecs.component<TerrainItem>();
|
|
ecs.component<FurnitureItem>();
|
|
ecs.component<FurnitureInstance>()
|
|
.on_remove([](flecs::entity e, FurnitureInstance &instance) {
|
|
ZoneScoped;
|
|
if (instance.furniture) {
|
|
instance.furniture
|
|
->destroyAllChildrenAndObjects();
|
|
instance.furniture->getCreator()
|
|
->destroySceneNode(instance.furniture);
|
|
instance.furniture = nullptr;
|
|
}
|
|
})
|
|
.on_set([](flecs::entity e, FurnitureInstance &instance) {
|
|
ZoneScoped;
|
|
if (instance.furniture !=
|
|
e.get<FurnitureInstance>().furniture) {
|
|
FurnitureInstance &f =
|
|
e.get_mut<FurnitureInstance>();
|
|
if (f.furniture) {
|
|
f.furniture
|
|
->destroyAllChildrenAndObjects();
|
|
f.furniture->getCreator()
|
|
->destroySceneNode(f.furniture);
|
|
}
|
|
}
|
|
})
|
|
.on_add([](flecs::entity e, FurnitureInstance &instance) {
|
|
ZoneScoped;
|
|
instance.furniture = nullptr;
|
|
});
|
|
ecs.component<TerrainItemNode>().on_remove([](flecs::entity e,
|
|
TerrainItemNode &item) {
|
|
ZoneScoped;
|
|
if (item.itemNode) {
|
|
item.itemNode->destroyAllChildrenAndObjects();
|
|
item.itemNode->getCreator()->destroySceneNode(
|
|
item.itemNode);
|
|
item.itemNode = nullptr;
|
|
}
|
|
if (item.geo) {
|
|
ECS::get<EngineData>().mScnMgr->destroyStaticGeometry(
|
|
item.geo);
|
|
item.geo = nullptr;
|
|
}
|
|
});
|
|
ecs.component<TerrainItemMeshNode>();
|
|
ecs.component<GeometryUpdateItem>();
|
|
ecs.import <TerrainModule>();
|
|
ecs.observer<const Terrain>("LoadTerrainItems")
|
|
.event(flecs::OnSet)
|
|
.each([&](const Terrain &terrain) {
|
|
ZoneScopedN("LoadTerrainItems");
|
|
if (terrain.mTerrainGroup && !itemsLoaded) {
|
|
loadItems();
|
|
itemsLoaded = true;
|
|
}
|
|
});
|
|
if (!Ogre::MeshLodGenerator::getSingletonPtr())
|
|
new Ogre::MeshLodGenerator();
|
|
ecs.system<TerrainItem>("SetupTowns")
|
|
.kind(flecs::OnUpdate)
|
|
.without<TownNPCs>()
|
|
.without<TownAI>()
|
|
.each([&](flecs::entity e, TerrainItem &item) {
|
|
Ogre::String props = item.properties;
|
|
nlohmann::json jp = nlohmann::json::parse(props);
|
|
if (jp.find("type") == jp.end())
|
|
return;
|
|
Ogre::String itemType = jp["type"].get<Ogre::String>();
|
|
if (itemType == "town")
|
|
Geometry::registerTownItem(e);
|
|
});
|
|
ecs.system("AddGeometryQueue").kind(flecs::OnUpdate).run([&](flecs::iter &it) {
|
|
ZoneScopedN("AddGeometryQueue");
|
|
std::list<flecs::entity> items;
|
|
if (!ECS::get().has<Terrain>())
|
|
return;
|
|
if (!ECS::get<Terrain>().mTerrainGroup)
|
|
return;
|
|
if (!itemsLoaded) {
|
|
loadItems();
|
|
itemsLoaded = true;
|
|
return;
|
|
}
|
|
if (!furnitureLoaded) {
|
|
loadFurniture();
|
|
furnitureLoaded = true;
|
|
}
|
|
if (!templatesLoaded) {
|
|
loadTemplates();
|
|
templatesLoaded = true;
|
|
}
|
|
std::list<std::pair<long, long> > output;
|
|
while (!addQueue.empty()) {
|
|
std::pair<long, long> item = addQueue.front();
|
|
std::pair<long, long> slot = { item.first,
|
|
item.second };
|
|
flecs::entity parent =
|
|
ECS::get()
|
|
.query_builder<const TerrainSlotParent>()
|
|
.build()
|
|
.find([&slot](const TerrainSlotParent
|
|
&parent) {
|
|
return parent.slot == slot;
|
|
});
|
|
if (parent.is_valid()) {
|
|
items.clear();
|
|
ECS::get()
|
|
.query_builder<const TerrainItem>()
|
|
.with(flecs::ChildOf, parent)
|
|
.without<TerrainItemNode>()
|
|
.write<TerrainItemNode>()
|
|
.build()
|
|
.each([&](flecs::entity e,
|
|
const TerrainItem &item) {
|
|
items.push_back(e);
|
|
});
|
|
for (auto e : items)
|
|
createItemGeometry(e);
|
|
addQueue.pop_front();
|
|
} else {
|
|
output.push_back(item);
|
|
addQueue.pop_front();
|
|
}
|
|
}
|
|
OgreAssert(addQueue.empty(), "queue is not empty");
|
|
if (!output.empty())
|
|
addQueue = output;
|
|
});
|
|
}
|
|
void StaticGeometryModule::addGeometryForSlot(long x, long y)
|
|
{
|
|
ZoneScoped;
|
|
addQueue.push_back({ x, y });
|
|
}
|
|
void StaticGeometryModule::removeGeometryForSlot(long x, long y)
|
|
{
|
|
ZoneScoped;
|
|
std::pair<long, long> slot = { x, y };
|
|
flecs::entity parent =
|
|
ECS::get().query_builder<const TerrainSlotParent>().build().find(
|
|
[&slot](const TerrainSlotParent &parent) {
|
|
return parent.slot == slot;
|
|
});
|
|
if (parent.is_valid()) {
|
|
ECS::get()
|
|
.query_builder<const TerrainItem>()
|
|
.with(flecs::ChildOf, parent)
|
|
.without<TerrainItemNode>()
|
|
.build()
|
|
.each([](flecs::entity e, const TerrainItem &item) {
|
|
OgreAssert(false, "Implement item geo destroy" +
|
|
item.properties);
|
|
});
|
|
}
|
|
}
|
|
flecs::entity
|
|
StaticGeometryModule::createItem(const Ogre::Vector3 &position,
|
|
const Ogre::Quaternion &orientation,
|
|
const Ogre::String &type)
|
|
{
|
|
ZoneScoped;
|
|
long x, y;
|
|
ECS::get<Terrain>().mTerrainGroup->convertWorldPositionToTerrainSlot(
|
|
position, &x, &y);
|
|
std::pair<long, long> pos{ x, y };
|
|
flecs::entity slot =
|
|
ECS::get().query_builder<const TerrainSlotParent>().build().find(
|
|
[&](const TerrainSlotParent &slot) -> bool {
|
|
return slot.slot == pos;
|
|
});
|
|
if (!slot.is_valid())
|
|
slot = ECS::get().entity().set<TerrainSlotParent>({ pos });
|
|
flecs::entity item = ECS::get().entity().child_of(slot);
|
|
nlohmann::json jproperties;
|
|
jproperties["type"] = type;
|
|
item.set<TerrainItem>({ position, orientation, jproperties.dump() });
|
|
std::cout << "createItem: " << x << " " << y << " " << item.id()
|
|
<< std::endl;
|
|
return item;
|
|
}
|
|
void StaticGeometryModule::setItemProperties(flecs::entity id,
|
|
Ogre::String properties)
|
|
{
|
|
ZoneScoped;
|
|
OgreAssert(id.is_valid(), "bad id");
|
|
id.get_mut<TerrainItem>().properties = properties;
|
|
id.modified<TerrainItem>();
|
|
}
|
|
const Ogre::String &StaticGeometryModule::getItemProperties(flecs::entity id)
|
|
{
|
|
ZoneScoped;
|
|
OgreAssert(id.is_valid(), "bad id");
|
|
return id.get<TerrainItem>().properties;
|
|
}
|
|
|
|
nlohmann::json templates;
|
|
void StaticGeometryModule::loadTemplates()
|
|
{
|
|
ZoneScoped;
|
|
if (!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(
|
|
"templates.list"))
|
|
return;
|
|
Ogre::String group =
|
|
Ogre::ResourceGroupManager::getSingleton()
|
|
.findGroupContainingResource("templates.list");
|
|
Ogre::DataStreamPtr stream =
|
|
Ogre::ResourceGroupManager::getSingleton().openResource(
|
|
"templates.list", group);
|
|
Ogre::String json = stream->getAsString();
|
|
nlohmann::json jtemplates = nlohmann::json::parse(json);
|
|
templates = jtemplates;
|
|
}
|
|
|
|
void StaticGeometryModule::saveTemplates()
|
|
{
|
|
ZoneScoped;
|
|
Ogre::String path = "resources/buildings/templates.list";
|
|
if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(
|
|
"templates.list")) {
|
|
Ogre::String group =
|
|
Ogre::ResourceGroupManager::getSingleton()
|
|
.findGroupContainingResource("templates.list");
|
|
Ogre::FileInfoListPtr fileInfoList(
|
|
Ogre::ResourceGroupManager::getSingleton()
|
|
.findResourceFileInfo(group, "templates.list"));
|
|
OgreAssert(fileInfoList->size() == 1,
|
|
"templates.list should be there and only once");
|
|
path = fileInfoList->at(0).archive->getName() + "/" +
|
|
"templates.list";
|
|
Ogre::FileSystemLayer::removeFile(path);
|
|
}
|
|
std::fstream fout(path.c_str(), std::ios::out);
|
|
fout << templates.dump();
|
|
fout.close();
|
|
}
|
|
|
|
void StaticGeometryModule::saveItems()
|
|
{
|
|
ZoneScoped;
|
|
Ogre::String path = "resources/buildings/items.list";
|
|
if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(
|
|
"items.list")) {
|
|
Ogre::String group =
|
|
Ogre::ResourceGroupManager::getSingleton()
|
|
.findGroupContainingResource("items.list");
|
|
Ogre::FileInfoListPtr fileInfoList(
|
|
Ogre::ResourceGroupManager::getSingleton()
|
|
.findResourceFileInfo(group, "items.list"));
|
|
OgreAssert(fileInfoList->size() == 1,
|
|
"worpd_map.png should be there and only once");
|
|
path = fileInfoList->at(0).archive->getName() + "/" +
|
|
"items.list";
|
|
Ogre::FileSystemLayer::removeFile(path);
|
|
}
|
|
std::fstream fout(path.c_str(), std::ios::out);
|
|
nlohmann::json jitemlist;
|
|
ECS::get().query_builder<const TerrainItem>().build().each(
|
|
[&](flecs::entity e, const TerrainItem &item) {
|
|
nlohmann::json jitem;
|
|
to_json(jitem["position"], item.position);
|
|
to_json(jitem["orientation"], item.orientation);
|
|
to_json(jitem["properties"], item.properties);
|
|
jitem["objects"] = nlohmann::json::array();
|
|
ECS::get()
|
|
.query_builder<const TerrainItem>()
|
|
.with(flecs::ChildOf, e)
|
|
.build()
|
|
.each([&](flecs::entity obje,
|
|
const TerrainItem &objItem) {
|
|
nlohmann::json jobjitem;
|
|
to_json(jobjitem["position"],
|
|
objItem.position);
|
|
to_json(jobjitem["orientation"],
|
|
objItem.orientation);
|
|
to_json(jobjitem["properties"],
|
|
objItem.properties);
|
|
jitem["objects"].push_back(jobjitem);
|
|
});
|
|
jitemlist.push_back(jitem);
|
|
});
|
|
fout << jitemlist.dump();
|
|
fout.close();
|
|
}
|
|
void StaticGeometryModule::loadItems()
|
|
{
|
|
ZoneScoped;
|
|
if (!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(
|
|
"items.list"))
|
|
return;
|
|
Ogre::String group = Ogre::ResourceGroupManager::getSingleton()
|
|
.findGroupContainingResource("items.list");
|
|
Ogre::DataStreamPtr stream =
|
|
Ogre::ResourceGroupManager::getSingleton().openResource(
|
|
"items.list", group);
|
|
Ogre::String json = stream->getAsString();
|
|
nlohmann::json jlist = nlohmann::json::parse(json);
|
|
ECS::get().delete_with<TerrainItem>();
|
|
ECS::get().delete_with<TerrainSlotParent>();
|
|
|
|
for (const auto &v : jlist) {
|
|
Ogre::Vector3 position;
|
|
Ogre::Quaternion orientation;
|
|
Ogre::String properties;
|
|
from_json(v["position"], position);
|
|
from_json(v["orientation"], orientation);
|
|
properties = v["properties"].get<Ogre::String>();
|
|
std::cout << v.dump(4) << std::endl;
|
|
long x, y;
|
|
ECS::get<Terrain>()
|
|
.mTerrainGroup->convertWorldPositionToTerrainSlot(
|
|
position, &x, &y);
|
|
std::pair<long, long> pos{ x, y };
|
|
flecs::entity slot =
|
|
ECS::get()
|
|
.query_builder<const TerrainSlotParent>()
|
|
.build()
|
|
.find([&](const TerrainSlotParent &slot)
|
|
-> bool {
|
|
return slot.slot == pos;
|
|
});
|
|
if (!slot.is_valid())
|
|
slot = ECS::get().entity().set<TerrainSlotParent>(
|
|
{ pos });
|
|
OgreAssert(slot.is_valid(), "Failed to create slot");
|
|
flecs::entity item =
|
|
ECS::get().entity().child_of(slot).set<TerrainItem>(
|
|
{ position, orientation, properties });
|
|
if (v.find("objects") != v.end()) {
|
|
for (auto &obj : v["objects"]) {
|
|
Ogre::Vector3 objposition;
|
|
Ogre::Quaternion objorientation;
|
|
Ogre::String objproperties;
|
|
from_json(obj["position"], objposition);
|
|
from_json(obj["orientation"], objorientation);
|
|
objproperties =
|
|
obj["properties"].get<Ogre::String>();
|
|
flecs::entity itemObj =
|
|
ECS::get()
|
|
.entity()
|
|
.child_of(item)
|
|
.set<TerrainItem>(
|
|
{ objposition,
|
|
objorientation,
|
|
objproperties });
|
|
}
|
|
}
|
|
std::cout << "createItem: " << x << " " << y << " " << item.id()
|
|
<< std::endl;
|
|
std::cout << "position: " << item.id() << " " << position
|
|
<< std::endl;
|
|
}
|
|
}
|
|
|
|
void StaticGeometryModule::saveFurniture()
|
|
{
|
|
/* No saving - furniture is generated by blender */
|
|
}
|
|
|
|
void StaticGeometryModule::loadFurniture()
|
|
{
|
|
ZoneScoped;
|
|
ECS::get().delete_with<FurnitureItem>();
|
|
static std::vector<Ogre::String> glb_names;
|
|
const std::vector<Ogre::String> &groups =
|
|
Ogre::ResourceGroupManager::getSingleton().getResourceGroups();
|
|
if (glb_names.size() == 0) {
|
|
int i;
|
|
for (i = 0; i < groups.size(); i++) {
|
|
std::vector<Ogre::String> names =
|
|
*Ogre::ResourceGroupManager::getSingleton()
|
|
.findResourceNames(
|
|
groups[i],
|
|
"furniture-*.glb.json");
|
|
glb_names.insert(glb_names.end(), names.begin(),
|
|
names.end());
|
|
}
|
|
}
|
|
for (auto &g : glb_names) {
|
|
Ogre::String group = Ogre::ResourceGroupManager::getSingleton()
|
|
.findGroupContainingResource(g);
|
|
Ogre::DataStreamPtr stream =
|
|
Ogre::ResourceGroupManager::getSingleton().openResource(
|
|
g, group);
|
|
Ogre::String json = stream->getAsString();
|
|
nlohmann::json jdata = nlohmann::json::parse(json);
|
|
std::vector<Ogre::String> tags;
|
|
for (auto &tag : jdata["tags"]) {
|
|
Ogre::String stag = tag.get<Ogre::String>();
|
|
tags.push_back(stag);
|
|
}
|
|
Ogre::String meshName = jdata["mesh"].get<Ogre::String>();
|
|
Ogre::MeshPtr mesh =
|
|
Ogre::MeshManager::getSingleton().getByName(meshName);
|
|
if (mesh) {
|
|
Ogre::LodConfig meshconf(mesh);
|
|
Geometry::setupLods(meshconf);
|
|
}
|
|
ECS::get().entity().set<FurnitureItem>({ json, tags });
|
|
std::cout << "path: " << g << std::endl;
|
|
}
|
|
}
|
|
void StaticGeometryModule::getItemPositionPerSlot(
|
|
long x, long y, std::list<Ogre::Vector3> *positions)
|
|
{
|
|
ZoneScoped;
|
|
std::pair<long, long> pos{ x, y };
|
|
if (!positions)
|
|
return;
|
|
flecs::entity slot =
|
|
ECS::get().query_builder<const TerrainSlotParent>().build().find(
|
|
[&](const TerrainSlotParent &slot) -> bool {
|
|
return slot.slot == pos;
|
|
});
|
|
if (!slot.is_valid())
|
|
return;
|
|
ECS::get()
|
|
.query_builder<const TerrainItem>()
|
|
.with(flecs::ChildOf, slot)
|
|
.build()
|
|
.each([&](flecs::entity e, const TerrainItem &item) {
|
|
positions->push_back(item.position);
|
|
});
|
|
}
|
|
void StaticGeometryModule::getItemPositions(std::list<Ogre::Vector3> *positions)
|
|
{
|
|
ZoneScoped;
|
|
ECS::get().query_builder<const TerrainItem>().build().each(
|
|
[&](flecs::entity e, const TerrainItem &item) {
|
|
positions->push_back(item.position);
|
|
});
|
|
}
|
|
void StaticGeometryModule::getItemPositionAndRotation(
|
|
flecs::entity e, Ogre::Vector3 &position, Ogre::Quaternion &orientation)
|
|
{
|
|
ZoneScoped;
|
|
position = e.get<TerrainItem>().position;
|
|
orientation = e.get<TerrainItem>().orientation;
|
|
}
|
|
void StaticGeometryModule::getItemsProperties(
|
|
std::list<std::pair<flecs::entity, Ogre::String> > *items)
|
|
{
|
|
ZoneScoped;
|
|
ECS::get().query_builder<const TerrainItem>().build().each(
|
|
[&](flecs::entity e, const TerrainItem &item) {
|
|
items->push_back({ e, item.properties });
|
|
});
|
|
}
|
|
void StaticGeometryModule::createItemGeometry(flecs::entity e)
|
|
{
|
|
ZoneScoped;
|
|
Geometry::createItemGeometry(e);
|
|
}
|
|
|
|
void StaticGeometryModule::destroyItemGeometry(flecs::entity e)
|
|
{
|
|
ZoneScoped;
|
|
Geometry::destroyItemGeometry(e);
|
|
}
|
|
|
|
nlohmann::json &StaticGeometryModule::getTemplates()
|
|
{
|
|
return templates;
|
|
}
|
|
|
|
void StaticGeometryModule::updateItemGeometry(flecs::entity e)
|
|
{
|
|
ZoneScoped;
|
|
// We add this as task to reduce UI load
|
|
Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask([e]() {
|
|
ZoneScopedN("updateItemGeometry");
|
|
if (e.has<GeometryUpdateItem>())
|
|
return;
|
|
e.add<GeometryUpdateItem>();
|
|
Geometry::updateItemGeometry(e);
|
|
e.remove<GeometryUpdateItem>();
|
|
});
|
|
}
|
|
|
|
void StaticGeometryModule::addTriangleBufferWork(
|
|
const Ogre::String &meshName, Ogre::StaticGeometry *geo,
|
|
const Ogre::Vector3 &position, const Ogre::Quaternion &rotation,
|
|
const Procedural::TriangleBuffer &tb)
|
|
{
|
|
ZoneScoped;
|
|
struct WorkData {
|
|
Ogre::String meshName;
|
|
Ogre::StaticGeometry *geo;
|
|
Ogre::Vector3 position;
|
|
Ogre::Quaternion rotation;
|
|
Procedural::TriangleBuffer tb;
|
|
};
|
|
WorkData data = { meshName, geo, position, rotation, tb };
|
|
// We add this as task to reduce UI load
|
|
Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask([data]() {
|
|
ZoneScopedN("addTriangleBufferWork");
|
|
Ogre::MeshPtr mesh = data.tb.transformToMesh(data.meshName);
|
|
Ogre::Entity *ent =
|
|
ECS::get<EngineData>().mScnMgr->createEntity(mesh);
|
|
data.geo->addEntity(ent, data.position, data.rotation);
|
|
ECS::get<EngineData>().mScnMgr->destroyEntity(ent);
|
|
});
|
|
}
|
|
struct TiledMeshes {
|
|
struct Tile {
|
|
Ogre::String materialName;
|
|
std::shared_ptr<Ogre::VertexData> vertexData;
|
|
std::shared_ptr<Ogre::IndexData> indexData;
|
|
std::set<uint32_t> positions;
|
|
};
|
|
std::map<Ogre::String, Tile> tiles;
|
|
uint32_t packKey(const Ogre::Vector3i &position)
|
|
{
|
|
uint32_t key = 0;
|
|
key |= (uint32_t)(position[2] + 512) << 20;
|
|
key |= (uint32_t)(position[1] + 512) << 10;
|
|
key |= (uint32_t)(position[0] + 512) << 0;
|
|
return key;
|
|
}
|
|
void unpackKey(uint32_t key, Ogre::Vector3i &position)
|
|
{
|
|
uint32_t mask = 0x3ff;
|
|
position[0] = (int)(key & mask) - 512;
|
|
position[1] = (int)((key >> 10) & mask) - 512;
|
|
position[2] = (int)((key >> 20) & mask) - 512;
|
|
}
|
|
void setTile(const Ogre::String &name, const Ogre::Vector3i &position)
|
|
{
|
|
if (tiles.find(name) == tiles.end())
|
|
return;
|
|
tiles[name].positions.insert(packKey(position));
|
|
}
|
|
void clearTile(const Ogre::String &name, const Ogre::Vector3i &position)
|
|
{
|
|
if (tiles.find(name) == tiles.end())
|
|
return;
|
|
tiles[name].positions.erase(packKey(position));
|
|
}
|
|
void addTile(const Ogre::String &name, Ogre::MeshPtr mesh)
|
|
{
|
|
if (mesh->getSubMeshes().size() != 1)
|
|
return;
|
|
Ogre::SubMesh *submesh = mesh->getSubMesh(0);
|
|
Ogre::VertexData *vertexData;
|
|
if (submesh->useSharedVertices)
|
|
vertexData = mesh->sharedVertexData->clone();
|
|
else
|
|
vertexData = submesh->vertexData->clone();
|
|
tiles[name] = { submesh->getMaterialName(),
|
|
std::shared_ptr<Ogre::VertexData>(vertexData),
|
|
std::shared_ptr<Ogre::IndexData>(
|
|
submesh->indexData->clone()),
|
|
{} };
|
|
}
|
|
struct buildSettings {
|
|
Ogre::String meshName;
|
|
Ogre::String materialName;
|
|
Ogre::MeshPtr mesh;
|
|
Ogre::SubMesh *sm;
|
|
int vertexCount, vertexOffset;
|
|
int indexCount, indexOffset;
|
|
Ogre::VertexDeclaration *vdecl;
|
|
Ogre::HardwareVertexBufferSharedPtr vbuf;
|
|
Ogre::AxisAlignedBox bounds;
|
|
bool setBounds;
|
|
};
|
|
void configureSettings(struct buildSettings &settings)
|
|
{
|
|
settings.mesh = Ogre::MeshManager::getSingleton().createManual(
|
|
settings.meshName,
|
|
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
|
|
settings.mesh->createVertexData();
|
|
settings.sm = settings.mesh->createSubMesh();
|
|
settings.vertexCount = 0;
|
|
settings.indexCount = 0;
|
|
settings.vdecl = nullptr;
|
|
for (const auto &tile : tiles) {
|
|
settings.vertexCount +=
|
|
tile.second.vertexData->vertexCount *
|
|
tile.second.positions.size();
|
|
settings.indexCount +=
|
|
tile.second.indexData->indexCount *
|
|
tile.second.positions.size();
|
|
if (!settings.vdecl) {
|
|
settings.vdecl =
|
|
tile.second.vertexData
|
|
->vertexDeclaration->clone();
|
|
settings.materialName =
|
|
tile.second.materialName;
|
|
}
|
|
}
|
|
settings.mesh->sharedVertexData->vertexStart = 0;
|
|
settings.mesh->sharedVertexData->vertexCount =
|
|
settings.vertexCount;
|
|
settings.mesh->sharedVertexData->vertexDeclaration =
|
|
settings.vdecl;
|
|
settings.vbuf =
|
|
Ogre::HardwareBufferManager::getSingleton()
|
|
.createVertexBuffer(
|
|
settings.vdecl->getVertexSize(0),
|
|
settings.vertexCount,
|
|
Ogre::HBU_GPU_ONLY);
|
|
settings.mesh->sharedVertexData->vertexBufferBinding->setBinding(
|
|
0, settings.vbuf);
|
|
settings.sm->indexData->indexStart = 0;
|
|
settings.sm->indexData->indexCount = settings.indexCount;
|
|
settings.sm->indexData->indexBuffer =
|
|
Ogre::HardwareBufferManager::getSingleton()
|
|
.createIndexBuffer(
|
|
Ogre::HardwareIndexBuffer::IT_32BIT,
|
|
settings.sm->indexData->indexCount * 8,
|
|
Ogre::HBU_GPU_ONLY);
|
|
settings.sm->setMaterialName(settings.materialName);
|
|
settings.setBounds = true;
|
|
}
|
|
void processIndex(struct buildSettings &settings,
|
|
const struct Tile &tile, unsigned int *dstIndices)
|
|
{
|
|
int j;
|
|
std::shared_ptr<Ogre::IndexData> srcIndexData = tile.indexData;
|
|
int srcIndexCount = srcIndexData->indexCount;
|
|
std::shared_ptr<Ogre::VertexData> srcVertexData =
|
|
tile.vertexData;
|
|
int srcVertexCount = srcVertexData->vertexCount;
|
|
Ogre::HardwareIndexBufferSharedPtr srcIbuf =
|
|
srcIndexData->indexBuffer;
|
|
Ogre::HardwareBufferLockGuard srcIndexLock(
|
|
srcIbuf, Ogre::HardwareBuffer::HBL_READ_ONLY);
|
|
if (srcIndexData->indexBuffer->getType() ==
|
|
Ogre::HardwareIndexBuffer::IT_32BIT) {
|
|
unsigned int *indices =
|
|
static_cast<unsigned int *>(srcIndexLock.pData);
|
|
for (j = 0; j < srcIndexCount; j++)
|
|
dstIndices[settings.indexOffset + j] =
|
|
indices[j] + settings.vertexOffset;
|
|
} else if (srcIndexData->indexBuffer->getType() ==
|
|
Ogre::HardwareIndexBuffer::IT_16BIT) {
|
|
unsigned short *indices = static_cast<unsigned short *>(
|
|
srcIndexLock.pData);
|
|
for (j = 0; j < srcIndexCount; j++)
|
|
dstIndices[settings.indexOffset + j] =
|
|
indices[j] + settings.vertexOffset;
|
|
}
|
|
}
|
|
void processSingleVertex(
|
|
struct buildSettings &settings,
|
|
const Ogre::VertexDeclaration::VertexElementList &srcElements,
|
|
uint32_t offset, unsigned char *srcData, unsigned char *dstData)
|
|
{
|
|
for (const auto &srcElement : srcElements) {
|
|
unsigned char *srcPtr, *dstPtr;
|
|
const Ogre::VertexElement *destElement =
|
|
settings.vdecl->findElementBySemantic(
|
|
srcElement.getSemantic(),
|
|
srcElement.getIndex());
|
|
if (!destElement)
|
|
goto out;
|
|
if (srcElement.getType() != destElement->getType() ||
|
|
srcElement.getSize() != destElement->getSize())
|
|
goto out;
|
|
srcPtr = srcData + srcElement.getOffset();
|
|
dstPtr = dstData + destElement->getOffset();
|
|
if (destElement->getSemantic() == Ogre::VES_POSITION) {
|
|
float *srcPositionData =
|
|
reinterpret_cast<float *>(srcPtr);
|
|
float *dstPositionData =
|
|
reinterpret_cast<float *>(dstPtr);
|
|
Ogre::Vector3 position(srcPositionData[0],
|
|
srcPositionData[1],
|
|
srcPositionData[2]);
|
|
Ogre::Vector3i offsetv;
|
|
unpackKey(offset, offsetv);
|
|
position.x += (float)offsetv[0];
|
|
position.y += (float)offsetv[1];
|
|
position.z += (float)offsetv[2];
|
|
dstPositionData[0] = position.x;
|
|
dstPositionData[1] = position.y;
|
|
dstPositionData[2] = position.z;
|
|
if (settings.setBounds) {
|
|
settings.bounds.setMinimum(position);
|
|
settings.bounds.setMaximum(position);
|
|
settings.setBounds = false;
|
|
} else
|
|
settings.bounds.merge(position);
|
|
} else if (destElement->getSemantic() ==
|
|
Ogre::VES_NORMAL) {
|
|
float *srcNormalData =
|
|
reinterpret_cast<float *>(srcPtr);
|
|
float *dstNormalData =
|
|
reinterpret_cast<float *>(dstPtr);
|
|
Ogre::Vector3 normal(srcNormalData[0],
|
|
srcNormalData[1],
|
|
srcNormalData[2]);
|
|
dstNormalData[0] = normal.x;
|
|
dstNormalData[1] = normal.y;
|
|
dstNormalData[2] = normal.z;
|
|
} else
|
|
memcpy(dstPtr, srcPtr, srcElement.getSize());
|
|
out:;
|
|
}
|
|
}
|
|
void processTile(struct buildSettings &settings,
|
|
const struct Tile &tile, uint32_t position,
|
|
unsigned char *dstpData)
|
|
{
|
|
std::shared_ptr<Ogre::VertexData> srcVertexData =
|
|
tile.vertexData;
|
|
const Ogre::VertexDeclaration *srcDecl =
|
|
srcVertexData->vertexDeclaration;
|
|
const Ogre::VertexBufferBinding *srcBind =
|
|
srcVertexData->vertexBufferBinding;
|
|
int srcVertexCount = srcVertexData->vertexCount;
|
|
Ogre::HardwareVertexBufferSharedPtr srcVbuf =
|
|
srcBind->getBuffer(0);
|
|
std::shared_ptr<Ogre::IndexData> srcIndexData = tile.indexData;
|
|
int srcIndexCount = srcIndexData->indexCount;
|
|
|
|
Ogre::HardwareBufferLockGuard srcVertexLock(
|
|
srcVbuf, 0, srcVertexCount * srcDecl->getVertexSize(0),
|
|
Ogre::HardwareBuffer::HBL_READ_ONLY);
|
|
const Ogre::VertexDeclaration::VertexElementList &srcElements =
|
|
srcDecl->getElements();
|
|
int j;
|
|
unsigned char *srcpData =
|
|
static_cast<unsigned char *>(srcVertexLock.pData);
|
|
|
|
for (j = 0; j < srcVertexCount; j++) {
|
|
unsigned char *srcData =
|
|
srcpData + j * srcVbuf->getVertexSize();
|
|
unsigned char *dstData =
|
|
dstpData + (settings.vertexOffset +
|
|
j) * settings.vbuf->getVertexSize();
|
|
processSingleVertex(settings, srcElements, position,
|
|
srcData, dstData);
|
|
}
|
|
}
|
|
Ogre::MeshPtr build(const Ogre::String &meshName)
|
|
{
|
|
buildSettings settings;
|
|
settings.meshName = meshName;
|
|
configureSettings(settings);
|
|
{
|
|
Ogre::HardwareBufferLockGuard vertexLock(
|
|
settings.vbuf, 0,
|
|
settings.vertexCount *
|
|
settings.vdecl->getVertexSize(0),
|
|
Ogre::HardwareBuffer::HBL_NO_OVERWRITE);
|
|
Ogre::HardwareBufferLockGuard indexLock(
|
|
settings.sm->indexData->indexBuffer,
|
|
Ogre::HardwareBuffer::HBL_NO_OVERWRITE);
|
|
settings.vertexOffset = 0;
|
|
settings.indexOffset = 0;
|
|
unsigned char *dstpData =
|
|
static_cast<unsigned char *>(vertexLock.pData);
|
|
unsigned int *dstIndices =
|
|
static_cast<unsigned int *>(indexLock.pData);
|
|
|
|
for (const auto &tile : tiles) {
|
|
std::shared_ptr<Ogre::IndexData> srcIndexData =
|
|
tile.second.indexData;
|
|
int srcIndexCount = srcIndexData->indexCount;
|
|
std::shared_ptr<Ogre::VertexData> srcVertexData =
|
|
tile.second.vertexData;
|
|
int srcVertexCount = srcVertexData->vertexCount;
|
|
for (const auto &position :
|
|
tile.second.positions) {
|
|
processTile(settings, tile.second,
|
|
position, dstpData);
|
|
processIndex(settings, tile.second,
|
|
dstIndices);
|
|
settings.vertexOffset += srcVertexCount;
|
|
settings.indexOffset += srcIndexCount;
|
|
Ogre::Vector3i vposition;
|
|
unpackKey(position, vposition);
|
|
std::cout << "position: " << position
|
|
<< " " << vposition
|
|
<< std::endl;
|
|
}
|
|
}
|
|
settings.mesh->_setBounds(settings.bounds);
|
|
}
|
|
Ogre::LodConfig config(settings.mesh);
|
|
// config.advanced.useCompression = false;
|
|
// config.advanced.useVertexNormals = true;
|
|
config.advanced.preventPunchingHoles = true;
|
|
config.advanced.preventBreakingLines = true;
|
|
config.createGeneratedLodLevel(2, 0.15f);
|
|
config.createGeneratedLodLevel(20, 0.49f);
|
|
config.createGeneratedLodLevel(15, 0.49f);
|
|
config.createGeneratedLodLevel(150, 0.75f);
|
|
config.advanced.useBackgroundQueue = false;
|
|
Ogre::MeshLodGenerator::getSingleton().generateLodLevels(
|
|
config);
|
|
return settings.mesh;
|
|
}
|
|
void removeTile(const Ogre::String &name)
|
|
{
|
|
tiles.erase(name);
|
|
}
|
|
TiledMeshes()
|
|
{
|
|
}
|
|
};
|
|
}
|