818 lines
24 KiB
C++
818 lines
24 KiB
C++
#include <unordered_set>
|
|
#include <iostream>
|
|
#include <Ogre.h>
|
|
#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>
|
|
#include <OgreTerrain.h>
|
|
#include <OgreTerrainGroup.h>
|
|
#include <OgrePageManager.h>
|
|
#include <OgreTerrainPagedWorldSection.h>
|
|
#include <OgrePage.h>
|
|
#include <OgreTerrainPaging.h>
|
|
#include <OgreFileSystemLayer.h>
|
|
#include <nlohmann/json.hpp>
|
|
|
|
#include "GameData.h"
|
|
#include "Components.h"
|
|
#include "CharacterModule.h"
|
|
#include "SunModule.h"
|
|
#include "PhysicsModule.h"
|
|
#include "StaticGeometryModule.h"
|
|
#include "TerrainModule.h"
|
|
|
|
#define TERRAIN_SIZE 65
|
|
#define TERRAIN_WORLD_SIZE 500.0f
|
|
#define ENDLESS_TERRAIN_FILE_PREFIX Ogre::String("EndlessWorldTerrain")
|
|
#define ENDLESS_TERRAIN_FILE_SUFFIX Ogre::String("dat")
|
|
|
|
#define ENDLESS_PAGING
|
|
|
|
// max range for a int16
|
|
#define ENDLESS_PAGE_MIN_X (-0x7FFF)
|
|
#define ENDLESS_PAGE_MIN_Y (-0x7FFF)
|
|
#define ENDLESS_PAGE_MAX_X 0x7FFF
|
|
#define ENDLESS_PAGE_MAX_Y 0x7FFF
|
|
namespace ECS
|
|
{
|
|
class DummyPageProvider;
|
|
/* Components */
|
|
struct TerrainPrivate {
|
|
DummyPageProvider *mDummyPageProvider;
|
|
Ogre::Timer mSunUpdate;
|
|
};
|
|
|
|
#define BRUSH_SIZE 64
|
|
struct HeightData {
|
|
Ogre::Image img;
|
|
Ogre::Image img_brushes;
|
|
Ogre::Image img_noise;
|
|
static HeightData *singleton;
|
|
HeightData()
|
|
{
|
|
img.load(
|
|
"world_map.png",
|
|
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
|
|
img_brushes.load(
|
|
"brushes.png",
|
|
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
|
|
img_noise.load(
|
|
"terrain.png",
|
|
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
|
|
}
|
|
static HeightData *get_singleton()
|
|
{
|
|
if (!singleton)
|
|
singleton = new HeightData();
|
|
return singleton;
|
|
}
|
|
float get_brush_height(int id, int x, int y)
|
|
{
|
|
int m = 0;
|
|
switch (id) {
|
|
case 0:
|
|
m = 0;
|
|
break;
|
|
case 1:
|
|
m = BRUSH_SIZE;
|
|
break;
|
|
default:
|
|
OgreAssert(false, "bad brush id");
|
|
break;
|
|
}
|
|
return img_brushes.getColourAt(x, y + m, 0).r;
|
|
}
|
|
float get_world_x(int x)
|
|
{
|
|
return (float)(x - (int)img.getWidth() / 2) * BRUSH_SIZE;
|
|
}
|
|
float get_world_y(int y)
|
|
{
|
|
return (float)(y - (int)img.getHeight() / 2) * BRUSH_SIZE;
|
|
}
|
|
int get_img_x(float world_x)
|
|
{
|
|
float world_img_x = world_x + (float)img.getWidth() *
|
|
(float)BRUSH_SIZE / 2.0f;
|
|
int ret = (world_img_x + BRUSH_SIZE - 1) / BRUSH_SIZE;
|
|
// ret = Ogre::Math::Clamp(ret, 0, (int)img.getWidth() - 1);
|
|
return ret;
|
|
}
|
|
int get_img_y(float world_z)
|
|
{
|
|
float world_img_y = world_z + (float)img.getHeight() *
|
|
(float)BRUSH_SIZE / 2.0f;
|
|
int ret = (world_img_y + BRUSH_SIZE - 1) / BRUSH_SIZE;
|
|
// ret = Ogre::Math::Clamp(ret, 0, (int)img.getHeight() - 1);
|
|
return ret;
|
|
}
|
|
void update_heightmap(const Ogre::Image &heightmap)
|
|
{
|
|
img = heightmap;
|
|
}
|
|
void save_heightmap()
|
|
{
|
|
Ogre::String group =
|
|
Ogre::ResourceGroupManager::getSingleton()
|
|
.findGroupContainingResource("world_map.png");
|
|
Ogre::FileInfoListPtr fileInfoList(
|
|
Ogre::ResourceGroupManager::getSingleton()
|
|
.findResourceFileInfo(group, "world_map.png"));
|
|
OgreAssert(fileInfoList->size() == 1,
|
|
"worpd_map.png should be there and only once");
|
|
Ogre::String path = fileInfoList->at(0).archive->getName() +
|
|
"/" + "world_map.png";
|
|
Ogre::FileSystemLayer::removeFile(path);
|
|
img.save(path);
|
|
}
|
|
float get_base_height(long world_x, long world_y)
|
|
{
|
|
float height = 0.0f;
|
|
int world_img_x =
|
|
world_x + (int)img.getWidth() * BRUSH_SIZE / 2;
|
|
int world_img_y =
|
|
world_y + (int)img.getHeight() * BRUSH_SIZE / 2;
|
|
Ogre::ColourValue color, colorb1, colorb2;
|
|
// float d;
|
|
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 ||
|
|
world_img_y >= img.getWidth() * BRUSH_SIZE) {
|
|
height = 0.0f;
|
|
goto out;
|
|
}
|
|
color = img.getColourAt(map_img_x, map_img_y, 0);
|
|
colorb1 = img_brushes.getColourAt(brush_img_x,
|
|
brush_img_y + BRUSH_SIZE, 0);
|
|
colorb2 = img_brushes.getColourAt(brush_img_x, brush_img_y, 0);
|
|
// d = Ogre::Math::saturate(color.r - 0.05f);
|
|
height = color.r;
|
|
out:
|
|
return height;
|
|
}
|
|
float get_noise_height(long world_x, long world_y)
|
|
{
|
|
int h;
|
|
Ogre::Vector2 noisePoint;
|
|
|
|
struct noise_types {
|
|
Ogre::Vector2 noiseOfft;
|
|
float noiseMul;
|
|
float noiseBias;
|
|
float noiseAmp;
|
|
};
|
|
static struct noise_types noise_pass[] = {
|
|
{ { -100.0f, 70.0f }, 10.2f, -0.55f, 5.0f },
|
|
{ { -130.0f, 55.0f }, 5.35f, -0.55f, 1.0f }
|
|
};
|
|
static float noise_values[] = { 0.0f, 0.0f };
|
|
for (h = 0; h < (int)sizeof(noise_values) /
|
|
(int)sizeof(noise_values[0]);
|
|
h++) {
|
|
noisePoint = (Ogre::Vector2(world_x, world_y) +
|
|
noise_pass[h].noiseOfft) *
|
|
noise_pass[h].noiseMul;
|
|
int noise_x =
|
|
(int)(noisePoint.x + img_noise.getWidth() / 2) %
|
|
img_noise.getWidth();
|
|
int noise_y = (int)(noisePoint.y +
|
|
img_noise.getHeight() / 2) %
|
|
img_noise.getHeight();
|
|
Ogre::ColourValue noise_color =
|
|
img_noise.getColourAt(noise_x, noise_y, 0);
|
|
noise_values[h] =
|
|
(noise_color.r + noise_pass[h].noiseBias) *
|
|
noise_pass[h].noiseAmp;
|
|
}
|
|
return noise_values[0] + noise_values[1];
|
|
}
|
|
float get_height(Ogre::TerrainGroup *terrainGroup, long world_x,
|
|
long world_y)
|
|
{
|
|
long grid_center_x = img.getWidth() * BRUSH_SIZE / 2;
|
|
long grid_center_y = img.getHeight() * BRUSH_SIZE / 2;
|
|
long world_grid_x = world_x + grid_center_x;
|
|
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");
|
|
OgreAssert(world_grid_y >= 0, "bad world y");
|
|
long cell = 8;
|
|
long b1 = (world_grid_x / cell) * cell + cell / 2;
|
|
long b2 = (world_grid_y / cell) * cell + cell / 2;
|
|
long o1 = Ogre::Math::Abs(world_grid_x - b1);
|
|
long o2 = Ogre::Math::Abs(world_grid_y - b2);
|
|
// std::cout << "h: " << o1 << " " << o2 << std::endl;
|
|
// std::cout << "h: " << (o1 * o1 + o2 * o2) << std::endl;
|
|
float hbase = 1.0 / (1.0f + 0.02f * (o1 * o1 + o2 * o2));
|
|
float h1 = hbase * 0.5f + 0.5f;
|
|
float h2 = (1.0f - hbase) * 0.5f;
|
|
float mheight =
|
|
(Ogre::Math::lerp(h2, h1,
|
|
get_base_height(world_x, world_y)) -
|
|
0.51f) *
|
|
amplitude;
|
|
float height = mheight;
|
|
if (mheight > 0.5f)
|
|
height += 2.0f + get_noise_height(world_x, world_y);
|
|
else if (mheight < -0.5f)
|
|
height -= 2.0f + get_noise_height(world_x, world_y);
|
|
// OgreAssert(false, "height");
|
|
return height;
|
|
}
|
|
};
|
|
HeightData *HeightData::singleton = nullptr;
|
|
|
|
class FlatTerrainDefiner
|
|
: public Ogre::TerrainPagedWorldSection::TerrainDefiner,
|
|
public Ogre::FrameListener {
|
|
Ogre::SceneManager *mScnMgr;
|
|
// Ogre::Bullet::DynamicsWorld *mWorld;
|
|
struct gen_collider {
|
|
Ogre::TerrainGroup *group;
|
|
long x;
|
|
long y;
|
|
};
|
|
std::deque<struct gen_collider> collider_queue;
|
|
std::deque<struct gen_collider> colliderRemove_queue;
|
|
|
|
public:
|
|
FlatTerrainDefiner(Ogre::SceneManager *
|
|
scm /*, Ogre::Bullet::DynamicsWorld *world */)
|
|
: Ogre::TerrainPagedWorldSection::TerrainDefiner()
|
|
, Ogre::FrameListener()
|
|
, mScnMgr(scm)
|
|
// , mWorld(world)
|
|
{
|
|
Ogre::Root::getSingleton().addFrameListener(this);
|
|
}
|
|
|
|
private:
|
|
std::mutex mtx;
|
|
|
|
public:
|
|
void createTerrainChunk(Ogre::TerrainGroup *terrainGroup, long x,
|
|
long y)
|
|
{
|
|
Ogre::Terrain *terrain = terrainGroup->getTerrain(x, y);
|
|
float minH = terrain->getMinHeight();
|
|
float maxH = terrain->getMaxHeight();
|
|
uint16_t terrainSize = terrainGroup->getTerrainSize();
|
|
OgreAssert(terrain && terrain->getHeightData() &&
|
|
terrain->isLoaded(),
|
|
"invalid terrain supplied");
|
|
uint16_t size = terrain->getSize();
|
|
float *heightData = terrain->getHeightData();
|
|
Ogre::SceneNode *node = terrain->_getRootSceneNode();
|
|
float worldSize = terrain->getWorldSize();
|
|
float scaled = worldSize / (size - 1);
|
|
Ogre::Vector3 bodyPosition = terrain->getPosition();
|
|
// bodyPosition.y += (maxH + minH) / 2.0f;
|
|
bodyPosition.x += worldSize / 2.0f;
|
|
bodyPosition.z += worldSize / 2.0f;
|
|
Ogre::Vector3 offset =
|
|
node->_getDerivedPosition() - bodyPosition;
|
|
flecs::entity e = PhysicsModule::createTerrainChunkBody(
|
|
node, heightData, offset,
|
|
Ogre::Vector3(scaled, 1, scaled), size);
|
|
node->getUserObjectBindings().setUserAny("_collider", e);
|
|
}
|
|
void define(Ogre::TerrainGroup *terrainGroup, long x, long y) override
|
|
{
|
|
std::lock_guard<std::mutex> guard(mtx);
|
|
uint16_t terrainSize = terrainGroup->getTerrainSize();
|
|
float *heightMap = OGRE_ALLOC_T(float, terrainSize *terrainSize,
|
|
MEMCATEGORY_GEOMETRY);
|
|
// float *heightMapCollider = OGRE_ALLOC_T(
|
|
// float, terrainSize *terrainSize, MEMCATEGORY_GEOMETRY);
|
|
// Ogre::Vector2 worldOrigin =
|
|
// Ogre::Vector2(img.getWidth(), img.getHeight()) * 0.5f;
|
|
float chunk = 128.0f;
|
|
Ogre::Vector2 revisedValuePoint;
|
|
Ogre::Vector3 worldPos;
|
|
terrainGroup->convertTerrainSlotToWorldPosition(x, y,
|
|
&worldPos);
|
|
for (int i = 0; i < terrainSize; i++)
|
|
for (int j = 0; j < terrainSize; j++) {
|
|
long world_x = (long)(worldPos.x + j -
|
|
(terrainSize - 1) / 2);
|
|
long world_y = (long)(worldPos.z + i -
|
|
(terrainSize - 1) / 2);
|
|
float height = 0.0f;
|
|
height +=
|
|
HeightData::get_singleton()->get_height(
|
|
terrainGroup, world_x, world_y);
|
|
|
|
// height = -2.0f;
|
|
heightMap[i * terrainSize + j] = height;
|
|
// heightMapCollider[(terrainSize - i - 1) *
|
|
// terrainSize +
|
|
// j] = height;
|
|
}
|
|
terrainGroup->defineTerrain(x, y, heightMap);
|
|
Ogre::LogManager::getSingleton().logError(
|
|
"defined terrain at " +
|
|
Ogre::StringConverter::toString(x) + " " +
|
|
Ogre::StringConverter::toString(y));
|
|
// collider_queue.push_back(
|
|
// { terrainGroup, x, y, heightMapCollider });
|
|
delete[] heightMap;
|
|
collider_queue.push_back({ terrainGroup, x, y });
|
|
}
|
|
bool frameStarted(const Ogre::FrameEvent &evt) override
|
|
{
|
|
(void)evt;
|
|
update();
|
|
return true;
|
|
}
|
|
void update()
|
|
{
|
|
std::lock_guard<std::mutex> guard(mtx);
|
|
static bool created = false;
|
|
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()) {
|
|
Ogre::LogManager::getSingleton().logError(
|
|
"can create collider for " +
|
|
Ogre::StringConverter::toString(x) +
|
|
" " +
|
|
Ogre::StringConverter::toString(y));
|
|
// float minH = terrain->getMinHeight();
|
|
// float maxH = terrain->getMaxHeight();
|
|
int size = terrain->getSize();
|
|
float worldSize = terrain->getWorldSize();
|
|
{
|
|
createTerrainChunk(group, x, y);
|
|
}
|
|
#if 0
|
|
if (true) {
|
|
btRigidBody *body =
|
|
mWorld->addTerrainRigidBody(
|
|
group, x, y, 2,
|
|
0x7ffffffd & (~16));
|
|
OgreAssert(
|
|
body,
|
|
"Could not create RigidBody");
|
|
Ogre::LogManager::getSingleton().logError(
|
|
"created rigid body " +
|
|
Ogre::StringConverter::toString(
|
|
Ogre::Bullet::convert(
|
|
body->getWorldTransform()
|
|
.getOrigin())));
|
|
Ogre::LogManager::getSingleton().logError(
|
|
"minHeight " +
|
|
Ogre::StringConverter::toString(
|
|
minH));
|
|
Ogre::LogManager::getSingleton().logError(
|
|
"maxHeight " +
|
|
Ogre::StringConverter::toString(
|
|
maxH));
|
|
Ogre::LogManager::getSingleton().logError(
|
|
"size " +
|
|
Ogre::StringConverter::toString(
|
|
size));
|
|
Ogre::LogManager::getSingleton().logError(
|
|
"world size " +
|
|
Ogre::StringConverter::toString(
|
|
worldSize));
|
|
Ogre::LogManager::getSingleton().logError(
|
|
"created collider for " +
|
|
Ogre::StringConverter::toString(
|
|
x) +
|
|
" " +
|
|
Ogre::StringConverter::toString(
|
|
y));
|
|
created = true;
|
|
}
|
|
#endif
|
|
// FIXME: create entities and components instead
|
|
#if 0
|
|
Ogre::SceneNode *items =
|
|
terrain->_getRootSceneNode()
|
|
->createChildSceneNode();
|
|
for (int i = 0; i < ECS::get<PlacementObjects>()
|
|
.altar_items.size();
|
|
i++) {
|
|
const struct PlacementObjects::item &item =
|
|
ECS::get<PlacementObjects>()
|
|
.altar_items[i];
|
|
Ogre::Entity *ent =
|
|
group->getSceneManager()
|
|
->createEntity(
|
|
item.entity);
|
|
Ogre::SceneNode *what =
|
|
items->createChildSceneNode();
|
|
what->attachObject(ent);
|
|
what->setOrientation(item.rotation);
|
|
what->setPosition(item.position);
|
|
}
|
|
#endif
|
|
/* Spawn items */
|
|
StaticGeometryModule::addGeometryForSlot(x, y);
|
|
collider_queue.pop_front();
|
|
|
|
} else {
|
|
/* Terrain data not ready maybe move to next terrain */
|
|
gen_collider m = collider_queue.front();
|
|
collider_queue.pop_front();
|
|
collider_queue.push_back(m);
|
|
break; // allow system to move on
|
|
}
|
|
}
|
|
if (collider_queue.empty() &&
|
|
!ECS::get<Terrain>().mTerrainReady) {
|
|
ECS::get_mut<Terrain>().mTerrainReady = true;
|
|
ECS::modified<Terrain>();
|
|
}
|
|
}
|
|
};
|
|
class DummyPageProvider : public Ogre::PageProvider {
|
|
public:
|
|
DummyPageProvider(/* btDynamicsWorld *world */)
|
|
: Ogre::PageProvider()
|
|
{
|
|
}
|
|
bool prepareProceduralPage(Ogre::Page *page,
|
|
Ogre::PagedWorldSection *section)
|
|
{
|
|
return true;
|
|
}
|
|
bool loadProceduralPage(Ogre::Page *page,
|
|
Ogre::PagedWorldSection *section)
|
|
{
|
|
return true;
|
|
}
|
|
bool unloadProceduralPage(Ogre::Page *page,
|
|
Ogre::PagedWorldSection *section)
|
|
{
|
|
long x, y;
|
|
ECS::get<Terrain>().mTerrainGroup->unpackIndex(page->CHUNK_ID,
|
|
&x, &y);
|
|
StaticGeometryModule::removeGeometryForSlot(x, y);
|
|
return true;
|
|
}
|
|
bool unprepareProceduralPage(Ogre::Page *page,
|
|
Ogre::PagedWorldSection *section)
|
|
{
|
|
return true;
|
|
}
|
|
};
|
|
TerrainModule::TerrainModule(flecs::world &ecs)
|
|
{
|
|
struct CanSetPlayerPosition {};
|
|
ecs.module<TerrainModule>();
|
|
ecs.component<CanSetPlayerPosition>().add(flecs::Singleton);
|
|
ecs.component<Terrain>().add(flecs::Singleton);
|
|
ecs.component<TerrainPrivate>().add(flecs::Singleton);
|
|
ecs.component<PlacementObjects>();
|
|
ecs.component<TerrainReady>().add(flecs::Singleton);
|
|
ecs.import <CharacterModule>();
|
|
ecs.import <SunModule>();
|
|
ecs.import <StaticGeometryModule>();
|
|
ecs.set<TerrainPrivate>({ nullptr, {} });
|
|
ecs.system<const EngineData, const Camera, const Sun, Terrain,
|
|
TerrainPrivate>("SetupUpdateTerrain")
|
|
.kind(flecs::OnUpdate)
|
|
.each([](const EngineData &eng, const Camera &camera,
|
|
const Sun &sun, Terrain &terrain,
|
|
TerrainPrivate &priv) {
|
|
if (!terrain.mTerrainGroup && sun.mSun && eng.mScnMgr) {
|
|
std::cout << "Terrain setup\n";
|
|
if (!priv.mDummyPageProvider)
|
|
priv.mDummyPageProvider =
|
|
new DummyPageProvider(
|
|
/* eng.mWorld
|
|
->getBtWorld() */);
|
|
terrain.mTerrainGlobals =
|
|
OGRE_NEW Ogre::TerrainGlobalOptions();
|
|
|
|
OgreAssert(terrain.mTerrainGlobals,
|
|
"Failed to allocate global options");
|
|
|
|
terrain.mTerrainGroup =
|
|
OGRE_NEW Ogre::TerrainGroup(
|
|
eng.mScnMgr,
|
|
Ogre::Terrain::ALIGN_X_Z,
|
|
TERRAIN_SIZE,
|
|
TERRAIN_WORLD_SIZE);
|
|
terrain.mTerrainGroup->setFilenameConvention(
|
|
ENDLESS_TERRAIN_FILE_PREFIX,
|
|
ENDLESS_TERRAIN_FILE_SUFFIX);
|
|
terrain.mTerrainGroup->setOrigin(
|
|
terrain.mTerrainPos);
|
|
// Configure global
|
|
terrain.mTerrainGlobals->setMaxPixelError(1);
|
|
// testing composite map
|
|
// mTerrainGlobals->setCompositeMapDistance(30);
|
|
terrain.mTerrainGlobals->setCompositeMapDistance(
|
|
300);
|
|
//mTerrainGlobals->setUseRayBoxDistanceCalculation(true);
|
|
terrain.mTerrainGlobals
|
|
->getDefaultMaterialGenerator()
|
|
->setLightmapEnabled(false);
|
|
|
|
terrain.mTerrainGlobals->setCompositeMapAmbient(
|
|
eng.mScnMgr->getAmbientLight());
|
|
terrain.mTerrainGlobals->setCompositeMapDiffuse(
|
|
sun.mSun->getDiffuseColour());
|
|
terrain.mTerrainGlobals->setLightMapDirection(
|
|
sun.mSun->getDerivedDirection());
|
|
|
|
// Configure default import settings for if we use imported image
|
|
Ogre::Terrain::ImportData &defaultimp =
|
|
terrain.mTerrainGroup
|
|
->getDefaultImportSettings();
|
|
defaultimp.terrainSize = TERRAIN_SIZE;
|
|
defaultimp.worldSize = TERRAIN_WORLD_SIZE;
|
|
defaultimp.inputScale = 1.0f;
|
|
// defaultimp.minBatchSize = 33;
|
|
defaultimp.minBatchSize = 5;
|
|
defaultimp.maxBatchSize = 65;
|
|
Ogre::Image combined;
|
|
combined.loadTwoImagesAsRGBA(
|
|
"Ground23_col.jpg", "Ground23_spec.png",
|
|
"General");
|
|
Ogre::TextureManager::getSingleton().loadImage(
|
|
"Ground23_diffspec", "General",
|
|
combined);
|
|
defaultimp.layerList.resize(1);
|
|
|
|
defaultimp.layerList[0].worldSize = 60;
|
|
defaultimp.layerList[0].textureNames.push_back(
|
|
"Ground23_diffspec");
|
|
// Paging setup
|
|
terrain.mPageManager =
|
|
OGRE_NEW Ogre::PageManager();
|
|
// Since we're not loading any pages from .page files, we need a way just
|
|
// to say we've loaded them without them actually being loaded
|
|
terrain.mPageManager->setPageProvider(
|
|
priv.mDummyPageProvider);
|
|
terrain.mPageManager->addCamera(camera.mCamera);
|
|
terrain.mPageManager->setDebugDisplayLevel(0);
|
|
terrain.mTerrainPaging =
|
|
OGRE_NEW Ogre::TerrainPaging(
|
|
terrain.mPageManager);
|
|
terrain.mPagedWorld =
|
|
terrain.mPageManager->createWorld();
|
|
#if 0
|
|
terrain.mTerrainGroup->setAutoUpdateLod(
|
|
Ogre::TerrainAutoUpdateLodFactory::
|
|
getAutoUpdateLod(
|
|
Ogre::BY_DISTANCE));
|
|
#endif
|
|
|
|
terrain.mTerrainPagedWorldSection =
|
|
terrain.mTerrainPaging
|
|
->createWorldSection(
|
|
terrain.mPagedWorld,
|
|
terrain.mTerrainGroup,
|
|
300, 500,
|
|
ENDLESS_PAGE_MIN_X,
|
|
ENDLESS_PAGE_MIN_Y,
|
|
ENDLESS_PAGE_MAX_X,
|
|
ENDLESS_PAGE_MAX_Y);
|
|
terrain.definer = OGRE_NEW FlatTerrainDefiner(
|
|
eng.mScnMgr /*, eng.mWorld */);
|
|
|
|
terrain.mTerrainPagedWorldSection->setDefiner(
|
|
terrain.definer);
|
|
|
|
terrain.mTerrainGroup->freeTemporaryResources();
|
|
std::cout << "Terrain setup done\n";
|
|
ECS::get().set<PlacementObjects>({});
|
|
terrain.mTerrainGroup->loadAllTerrains(true);
|
|
}
|
|
if (sun.mSun &&
|
|
priv.mSunUpdate.getMilliseconds() > 1000) {
|
|
terrain.mTerrainGlobals->setCompositeMapAmbient(
|
|
eng.mScnMgr->getAmbientLight());
|
|
terrain.mTerrainGlobals->setCompositeMapDiffuse(
|
|
sun.mSun->getDiffuseColour());
|
|
terrain.mTerrainGlobals->setLightMapDirection(
|
|
sun.mSun->getDerivedDirection());
|
|
std::cout << "sun pitch: "
|
|
<< sun.mSunNode->getOrientation()
|
|
.getPitch()
|
|
<< "\n";
|
|
priv.mSunUpdate.reset();
|
|
//terrain.mTerrainGroup->autoUpdateLodAll()
|
|
}
|
|
});
|
|
ecs.system<const ECS::Camera, const Terrain>("UpdateTerrainStatus")
|
|
.kind(flecs::OnUpdate)
|
|
.without<TerrainReady>()
|
|
.each([](const ECS::Camera &cam, const Terrain &terrain) {
|
|
std::cout << "mTerrainReady: " << terrain.mTerrainReady
|
|
<< "\n";
|
|
if (cam.mCameraNode && terrain.mTerrainReady) {
|
|
long x, y;
|
|
Ogre::Vector3 pos =
|
|
cam.mCameraNode->getPosition();
|
|
terrain.mTerrainGroup
|
|
->convertWorldPositionToTerrainSlot(
|
|
pos, &x, &y);
|
|
if (terrain.mTerrainGroup->getTerrain(x, y) &&
|
|
terrain.mTerrainGroup->getTerrain(x, y)
|
|
->isLoaded())
|
|
ECS::get().add<TerrainReady>();
|
|
}
|
|
});
|
|
ecs.system<const Terrain, PlacementObjects>("UpdatePlacementObjects")
|
|
.kind(flecs::OnUpdate)
|
|
.each([](const Terrain &terrain, PlacementObjects &placement) {
|
|
if (placement.altar_items.size() == 0) {
|
|
struct PlacementObjects::item item;
|
|
int i, j;
|
|
int worldSize = terrain.mTerrainGroup
|
|
->getTerrainWorldSize();
|
|
uint16_t terrainSize =
|
|
terrain.mTerrainGroup->getTerrainSize();
|
|
item.entity = "altar.glb";
|
|
item.rotation = Ogre::Quaternion(0, 0, 0, 1);
|
|
item.position = Ogre::Vector3(0, 0, 0);
|
|
float height =
|
|
HeightData::get_singleton()->get_height(
|
|
terrain.mTerrainGroup, 0, 0);
|
|
item.position.y = height;
|
|
placement.altar_items.push_back(item);
|
|
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);
|
|
long xslot, yslot;
|
|
terrain.mTerrainGroup
|
|
->convertWorldPositionToTerrainSlot(
|
|
position,
|
|
&xslot, &yslot);
|
|
Ogre::Vector3 slotpos;
|
|
terrain.mTerrainGroup
|
|
->convertTerrainSlotToWorldPosition(
|
|
xslot, yslot,
|
|
&slotpos);
|
|
Ogre::Vector3 offset =
|
|
(position - slotpos) *
|
|
terrainSize / worldSize;
|
|
height =
|
|
HeightData::get_singleton()
|
|
->get_height(
|
|
terrain.mTerrainGroup,
|
|
(long)position
|
|
.x,
|
|
(long)position
|
|
.z);
|
|
#if 0
|
|
height =
|
|
terrain.mTerrainGroup
|
|
->getHeightAtWorldPosition(
|
|
position);
|
|
#endif
|
|
if (height > -9.0f)
|
|
continue;
|
|
#ifdef VDEBUG
|
|
std::cout << "worldSize: "
|
|
<< worldSize - 1
|
|
<< std::endl;
|
|
std::cout << "height: " << i
|
|
<< " " << j << " "
|
|
<< height
|
|
<< std::endl;
|
|
#endif
|
|
item.entity = "altar.glb";
|
|
item.rotation =
|
|
Ogre::Quaternion(0, 0,
|
|
0, 1);
|
|
position.y = height;
|
|
item.position = position;
|
|
placement.altar_items.push_back(
|
|
item);
|
|
}
|
|
#ifdef VDEBUG
|
|
for (i = 0; i < placement.altar_items.size();
|
|
i++) {
|
|
std::cout << "placement: " << i << " "
|
|
<< placement.altar_items[i]
|
|
.position
|
|
<< std::endl;
|
|
}
|
|
#endif
|
|
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::get<CharacterM;
|
|
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>();
|
|
});
|
|
#endif
|
|
ecs.system<const Terrain>("UpdateTerrainGroup")
|
|
.kind(flecs::OnUpdate)
|
|
.interval(2.0f)
|
|
.each([](const Terrain &terrain) {
|
|
if (!terrain.mTerrainGroup
|
|
->isDerivedDataUpdateInProgress())
|
|
terrain.mTerrainGroup->update(false);
|
|
});
|
|
}
|
|
float TerrainModule::get_height(Ogre::TerrainGroup *group,
|
|
const Ogre::Vector3 &position)
|
|
{
|
|
int worldSize = group->getTerrainWorldSize();
|
|
uint16_t terrainSize = group->getTerrainSize();
|
|
long xslot, yslot;
|
|
group->convertWorldPositionToTerrainSlot(position, &xslot, &yslot);
|
|
Ogre::Vector3 slotpos;
|
|
group->convertTerrainSlotToWorldPosition(xslot, yslot, &slotpos);
|
|
Ogre::Vector3 offset = (position - slotpos) * terrainSize / worldSize;
|
|
float height = HeightData::get_singleton()->get_height(
|
|
group, (long)position.x, (long)position.z);
|
|
return height;
|
|
}
|
|
float TerrainModule::get_world_x(int x)
|
|
{
|
|
return HeightData::get_singleton()->get_world_x(x);
|
|
}
|
|
float TerrainModule::get_world_y(int y)
|
|
{
|
|
return HeightData::get_singleton()->get_world_y(y);
|
|
}
|
|
int TerrainModule::get_img_x(float world_x)
|
|
{
|
|
return HeightData::get_singleton()->get_img_x(world_x);
|
|
}
|
|
int TerrainModule::get_img_y(float world_z)
|
|
{
|
|
return HeightData::get_singleton()->get_img_y(world_z);
|
|
}
|
|
void TerrainModule::update_heightmap(const Ogre::Image &heightmap)
|
|
{
|
|
return HeightData::get_singleton()->update_heightmap(heightmap);
|
|
}
|
|
void TerrainModule::save_heightmap()
|
|
{
|
|
HeightData::get_singleton()->save_heightmap();
|
|
}
|
|
void TerrainModule::defineTerrain(long x, long y)
|
|
{
|
|
ECS::get<Terrain>().definer->define(ECS::get<Terrain>().mTerrainGroup,
|
|
x, y);
|
|
}
|
|
}
|