Updated almost all stuff

This commit is contained in:
2025-06-14 16:20:47 +03:00
parent 756fb3bdd0
commit a1f1e09af6
152 changed files with 53441 additions and 26 deletions

432
src/terrain/terrain.cpp Normal file
View File

@@ -0,0 +1,432 @@
#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
#include <Ogre.h>
#include <OgrePageManager.h>
#include <OgreTerrainMaterialGeneratorA.h>
#include <OgreTerrainQuadTreeNode.h>
#include <OgreMaterialManager.h>
#include <OgreTerrainAutoUpdateLod.h>
#include <OgreTerrainPagedWorldSection.h>
#include "terrain.h"
#define ENDLESS_TERRAIN_FILE_PREFIX Ogre::String("EndlessWorldTerrain")
#define ENDLESS_TERRAIN_FILE_SUFFIX Ogre::String("dat")
#define TERRAIN_WORLD_SIZE 12000.0f
#define TERRAIN_SIZE 513
#define HOLD_LOD_DISTANCE 3000.0
#define USE_PERLIN_DEFINER 0
class FlatTerrainDefiner
: public Ogre::TerrainPagedWorldSection::TerrainDefiner {
public:
void define(Ogre::TerrainGroup *terrainGroup, long x, long y) override
{
#if 0
Ogre::Image img;
img.load(
"terrain.png",
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
int terrainSize = terrainGroup->getTerrainSize();
float *heightMap = OGRE_ALLOC_T(float, terrainSize *terrainSize,
MEMCATEGORY_GEOMETRY);
Ogre::Vector2 worldOffset(Ogre::Real(x * (terrainSize - 1)),
Ogre::Real(y * (terrainSize - 1)));
float mCycle = 1.0f;
// worldOffset += mOriginPoint;
Ogre::Vector2 revisedValuePoint;
for (int i = 0; i < terrainSize; i++)
for (int j = 0; j < terrainSize; j++) {
revisedValuePoint =
(worldOffset + Ogre::Vector2(j, i)) /
mCycle;
heightMap[i * terrainSize + j] =
img.getColourAt(j * img.getWidth() /
terrainSize,
i * img.getHeight() /
terrainSize,
0)
.r *
0.2f -
0.1f;
}
terrainGroup->defineTerrain(x, y, heightMap);
OGRE_FREE(heightMap, MEMCATEGORY_GEOMETRY);
#endif
terrainGroup->defineTerrain(x, y, -0.2f);
}
};
void TerrainSetup::setupTerrain(Ogre::Camera *camera, Ogre::Light *sun,
Ogre::Bullet::DynamicsWorld *dynamicsWorld,
Ogre::Bullet::DebugDrawer *dbgDraw)
{
mDynWorld.reset(dynamicsWorld);
mDbgDraw.reset(dbgDraw);
mScnMgr = camera->getSceneManager();
mTerrainGlobals = OGRE_NEW Ogre::TerrainGlobalOptions();
#if 0
mCameraMan->setTopSpeed(100);
setDragLook(true);
#endif
#if 0
#if OGRE_PLATFORM != OGRE_PLATFORM_ANDROID
Ogre::MaterialManager::getSingleton().setDefaultTextureFiltering(
Ogre::TFO_ANISOTROPIC);
Ogre::MaterialManager::getSingleton().setDefaultAnisotropy(8);
#endif
#endif
// mScnMgr->setFog(Ogre::FOG_LINEAR, Ogre::ColourValue(0.7, 0.7, 0.8), 0,
// 4000, 10000);
Ogre::LogManager::getSingleton().setMinLogLevel(Ogre::LML_TRIVIAL);
#if 0
Ogre::Light *l = mScnMgr->createLight("tstLight");
l->setType(Ogre::Light::LT_DIRECTIONAL);
l->setDiffuseColour(ColourValue::White);
l->setSpecularColour(ColourValue(0.4, 0.4, 0.4));
auto ln = mSceneMgr->getRootSceneNode()->createChildSceneNode();
ln->setDirection(Vector3(0.55, -0.3, 0.75).normalisedCopy());
ln->attachObject(l);
mScnMgr->setAmbientLight(Ogre::ColourValue(0.2, 0.2, 0.2));
#endif
mTerrainGroup =
OGRE_NEW Ogre::TerrainGroup(mScnMgr, Ogre::Terrain::ALIGN_X_Z,
TERRAIN_SIZE, TERRAIN_WORLD_SIZE);
mTerrainGroup->setFilenameConvention(ENDLESS_TERRAIN_FILE_PREFIX,
ENDLESS_TERRAIN_FILE_SUFFIX);
mTerrainGroup->setOrigin(mTerrainPos);
// mTerrainGroup->setAutoUpdateLod(
// Ogre::TerrainAutoUpdateLodFactory::getAutoUpdateLod(
// Ogre::BY_DISTANCE));
configureTerrainDefaults(sun);
// Paging setup
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
mPageManager->setPageProvider(&mDummyPageProvider);
mPageManager->addCamera(camera);
mPageManager->setDebugDisplayLevel(0);
mTerrainPaging = OGRE_NEW Ogre::TerrainPaging(mPageManager);
mPagedWorld = mPageManager->createWorld();
mTerrainPagedWorldSection = mTerrainPaging->createWorldSection(
mPagedWorld, mTerrainGroup, 300, 500, ENDLESS_PAGE_MIN_X,
ENDLESS_PAGE_MIN_Y, ENDLESS_PAGE_MAX_X, ENDLESS_PAGE_MAX_Y);
#if USE_PERLIN_DEFINER == 1
mPerlinNoiseTerrainGenerator = OGRE_NEW PerlinNoiseTerrainGenerator;
mTerrainPagedWorldSection->setDefiner(mPerlinNoiseTerrainGenerator);
#else
mTerrainPagedWorldSection->setDefiner(OGRE_NEW FlatTerrainDefiner);
#endif
mTerrainGroup->freeTemporaryResources();
// mSceneMgr->setSkyBox(true, "Examples/CloudyNoonSkyBox");
// setup LOD info overlay
// mLodInfoOverlay =
// OverlayManager::getSingleton().create("LODInfoOverlay");
// mLodInfoOverlay->setZOrder(10);
// mLodInfoOverlayContainer =
// (OverlayContainer *)OverlayManager::getSingleton()
// .createOverlayElement("Panel", "LODInfoOverlayPanel");
// mLodInfoOverlayContainer->setDimensions(1.0, 1.0);
// mLodInfoOverlayContainer->setPosition(0.0, 0.0);
// mLodInfoOverlay->add2D(mLodInfoOverlayContainer);
// mLodInfoOverlay->show();
// setupControls();
}
#if 0
void TerrainSetup::setupTerrain(Ogre::Camera *camera)
{
//! [global_opts]
mTerrainGlobals = new Ogre::TerrainGlobalOptions();
//! [global_opts]
mScnMgr = camera->getSceneManager();
#if 0
mEditMarker = m_app->getSceneManager()->createEntity("editMarker",
"sphere.mesh");
#endif
mEditNode = mScnMgr->getRootSceneNode()->createChildSceneNode(
"TerrainEditNode");
// mEditNode->attachObject(mEditMarker);
mEditNode->setScale(0.05, 0.05, 0.05);
#if 0
setupControls();
#endif
// mCameraMan->setTopSpeed(50);
// setDragLook(true);
#if OGRE_PLATFORM != OGRE_PLATFORM_ANDROID
Ogre::MaterialManager::getSingleton().setDefaultTextureFiltering(
Ogre::TFO_ANISOTROPIC);
Ogre::MaterialManager::getSingleton().setDefaultAnisotropy(8);
#endif
Ogre::ColourValue fadeColour(0.7, 0.7, 0.8);
//! [linear_fog]
mScnMgr->setFog(Ogre::FOG_LINEAR, fadeColour, 0, 2000, 10000);
//! [linear_fog]
Ogre::LogManager::getSingleton().setMinLogLevel(Ogre::LML_TRIVIAL);
//! [light]
Ogre::Light *l = mScnMgr->createLight();
l->setType(Ogre::Light::LT_DIRECTIONAL);
l->setDiffuseColour(Ogre::ColourValue::White);
l->setSpecularColour(Ogre::ColourValue(0.4, 0.4, 0.4));
Ogre::SceneNode *ln =
mScnMgr->getRootSceneNode()->createChildSceneNode();
ln->setDirection(Ogre::Vector3(0.55, -0.3, 0.75).normalisedCopy());
ln->attachObject(l);
//! [light]
mScnMgr->setAmbientLight(Ogre::ColourValue(0.2, 0.2, 0.2));
//! [terrain_create]
mTerrainGroup =
new Ogre::TerrainGroup(mScnMgr, Ogre::Terrain::ALIGN_X_Z,
TERRAIN_SIZE, TERRAIN_WORLD_SIZE);
mTerrainGroup->setOrigin(mTerrainPos);
//! [terrain_create]
#if OGRE_PLATFORM != OGRE_PLATFORM_APPLE_IOS
//! [terrain_save_config]
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
".", "FileSystem", mTerrainGroup->getResourceGroup(), false,
true);
mTerrainGroup->setFilenameConvention("TerrainSample", "bin");
//! [terrain_save_config]
#endif
configureTerrainDefaults(l);
#ifdef PAGING
// Paging setup
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
mPageManager->setPageProvider(&mDummyPageProvider);
mPageManager->addCamera(camera);
mTerrainPaging = OGRE_NEW Ogre::TerrainPaging(mPageManager);
Ogre::PagedWorld *world = mPageManager->createWorld();
mTerrainPaging->createWorldSection(
world, mTerrainGroup, 2000, 3000, TERRAIN_PAGE_MIN_X,
TERRAIN_PAGE_MIN_Y, TERRAIN_PAGE_MAX_X, TERRAIN_PAGE_MAX_Y);
#else
//! [define_loop]
for (long x = TERRAIN_PAGE_MIN_X; x <= TERRAIN_PAGE_MAX_X; ++x)
for (long y = TERRAIN_PAGE_MIN_Y; y <= TERRAIN_PAGE_MAX_Y; ++y)
defineTerrain(x, y);
// sync load since we want everything in place when we start
mTerrainGroup->loadAllTerrains(true);
//! [define_loop]
#endif
//! [init_blend]
if (mTerrainsImported) {
for (const auto &ti : mTerrainGroup->getTerrainSlots()) {
initBlendMaps(ti.second->instance);
}
}
mTerrainGroup->freeTemporaryResources();
//! [init_blend]
#if 0
// create a few entities on the terrain
Entity *e = mSceneMgr->createEntity("tudorhouse.mesh");
Vector3 entPos(mTerrainPos.x + 2043, 0, mTerrainPos.z + 1715);
Quaternion rot;
entPos.y = mTerrainGroup->getHeightAtWorldPosition(entPos) +
65.5 + mTerrainPos.y;
rot.FromAngleAxis(Degree(Math::RangeRandom(-180, 180)),
Vector3::UNIT_Y);
SceneNode *sn =
mSceneMgr->getRootSceneNode()->createChildSceneNode(
entPos, rot);
sn->setScale(Vector3(0.12, 0.12, 0.12));
sn->attachObject(e);
mHouseList.push_back(e);
e = mSceneMgr->createEntity("tudorhouse.mesh");
entPos = Vector3(mTerrainPos.x + 1850, 0, mTerrainPos.z + 1478);
entPos.y = mTerrainGroup->getHeightAtWorldPosition(entPos) +
65.5 + mTerrainPos.y;
rot.FromAngleAxis(Degree(Math::RangeRandom(-180, 180)),
Vector3::UNIT_Y);
sn = mSceneMgr->getRootSceneNode()->createChildSceneNode(entPos,
rot);
sn->setScale(Vector3(0.12, 0.12, 0.12));
sn->attachObject(e);
mHouseList.push_back(e);
e = mSceneMgr->createEntity("tudorhouse.mesh");
entPos = Vector3(mTerrainPos.x + 1970, 0, mTerrainPos.z + 2180);
entPos.y = mTerrainGroup->getHeightAtWorldPosition(entPos) +
65.5 + mTerrainPos.y;
rot.FromAngleAxis(Degree(Math::RangeRandom(-180, 180)),
Vector3::UNIT_Y);
sn = mSceneMgr->getRootSceneNode()->createChildSceneNode(entPos,
rot);
sn->setScale(Vector3(0.12, 0.12, 0.12));
sn->attachObject(e);
mHouseList.push_back(e);
//! [skybox]
mSceneMgr->setSkyBox(true, "Examples/CloudyNoonSkyBox");
//! [skybox]
#endif
}
#endif
#if 0
void TerrainSetup::configureTerrainDefaults(Ogre::Light *l)
{
//! [configure_lod]
mTerrainGlobals->setMaxPixelError(8);
mTerrainGlobals->setCompositeMapDistance(3000);
//! [configure_lod]
// mTerrainGlobals->setUseRayBoxDistanceCalculation(true);
// mTerrainGlobals->getDefaultMaterialGenerator()->setDebugLevel(1);
// mTerrainGlobals->setLightMapSize(256);
Ogre::TerrainMaterialGeneratorA::SM2Profile *matProfile =
static_cast<Ogre::TerrainMaterialGeneratorA::SM2Profile *>(
mTerrainGlobals->getDefaultMaterialGenerator()
->getActiveProfile());
// Disable the lightmap for OpenGL ES 2.0. The minimum number of samplers allowed is 8(as opposed to 16 on
// desktop). Otherwise we will run over the limit by just one. The minimum was raised to 16 in GL ES 3.0.
if (Ogre::Root::getSingletonPtr()
->getRenderSystem()
->getCapabilities()
->getNumTextureUnits() < 9) {
matProfile->setLightmapEnabled(false);
}
// Disable steep parallax by default
matProfile->setLayerParallaxOcclusionMappingEnabled(false);
//! [composite_lighting]
// Important to set these so that the terrain knows what to use for baked (non-realtime) data
mTerrainGlobals->setLightMapDirection(l->getDerivedDirection());
mTerrainGlobals->setCompositeMapAmbient(mScnMgr->getAmbientLight());
mTerrainGlobals->setCompositeMapDiffuse(l->getDiffuseColour());
//! [composite_lighting]
// mTerrainGlobals->setCompositeMapAmbient(ColourValue::Red);
// Configure default import settings for if we use imported image
//! [import_settings]
Ogre::Terrain::ImportData &defaultimp =
mTerrainGroup->getDefaultImportSettings();
defaultimp.inputScale = 600;
defaultimp.minBatchSize = 33;
defaultimp.maxBatchSize = 65;
//! [import_settings]
//! [tex_from_src]
Ogre::Image combined;
combined.loadTwoImagesAsRGBA("Ground23_col.jpg", "Ground23_spec.png",
"General");
Ogre::TextureManager::getSingleton().loadImage("Ground23_diffspec",
"General", combined);
//! [tex_from_src]
//! [textures]
defaultimp.layerList.resize(3);
defaultimp.layerList[0].worldSize = 200;
defaultimp.layerList[0].textureNames.push_back("Ground37_diffspec.dds");
defaultimp.layerList[0].textureNames.push_back(
"Ground37_normheight.dds");
defaultimp.layerList[1].worldSize = 200;
defaultimp.layerList[1].textureNames.push_back(
"Ground23_diffspec"); // loaded from memory
defaultimp.layerList[1].textureNames.push_back(
"Ground23_normheight.dds");
defaultimp.layerList[2].worldSize = 400;
defaultimp.layerList[2].textureNames.push_back("Rock20_diffspec.dds");
defaultimp.layerList[2].textureNames.push_back("Rock20_normheight.dds");
//! [textures]
}
#endif
void TerrainSetup::configureTerrainDefaults(Ogre::Light *l)
{
// Configure global
mTerrainGlobals->setMaxPixelError(1);
// testing composite map
mTerrainGlobals->setCompositeMapDistance(30);
//mTerrainGlobals->setUseRayBoxDistanceCalculation(true);
mTerrainGlobals->getDefaultMaterialGenerator()->setLightmapEnabled(
false);
mTerrainGlobals->setCompositeMapAmbient(mScnMgr->getAmbientLight());
mTerrainGlobals->setCompositeMapDiffuse(l->getDiffuseColour());
mTerrainGlobals->setLightMapDirection(l->getDerivedDirection());
// Configure default import settings for if we use imported image
Ogre::Terrain::ImportData &defaultimp =
mTerrainGroup->getDefaultImportSettings();
defaultimp.terrainSize = TERRAIN_SIZE;
defaultimp.worldSize = TERRAIN_WORLD_SIZE;
defaultimp.inputScale = 600;
defaultimp.minBatchSize = 33;
defaultimp.maxBatchSize = 65;
#if 0
// textures
defaultimp.layerList.resize(1);
defaultimp.layerList[0].worldSize = 200;
defaultimp.layerList[0].textureNames.push_back("Ground37_diffspec.dds");
defaultimp.layerList[0].textureNames.push_back(
"Ground37_normheight.dds");
#endif
Ogre::Image combined;
combined.loadTwoImagesAsRGBA("Ground23_col.jpg", "Ground23_spec.png",
"General");
Ogre::TextureManager::getSingleton().loadImage("Ground23_diffspec",
"General", combined);
//! [tex_from_src]
//! [textures]
defaultimp.layerList.resize(1);
#if 0
defaultimp.layerList[0].worldSize = 200;
defaultimp.layerList[0].textureNames.push_back("Ground37_diffspec.dds");
defaultimp.layerList[0].textureNames.push_back(
"Ground37_normheight.dds");
#endif
#if 0
defaultimp.layerList[0].worldSize = 200;
defaultimp.layerList[0].textureNames.push_back(
"Ground23_diffspec"); // loaded from memory
defaultimp.layerList[0].textureNames.push_back(
"Ground23_normheight.dds");
#endif
defaultimp.layerList[0].worldSize = 60;
defaultimp.layerList[0].textureNames.push_back("Ground23_diffspec");
// defaultimp.layerList[0].textureNames.push_back("Rock20_normheight.png");
//! [textures]
}

541
src/terrain/terrain.h Normal file
View File

@@ -0,0 +1,541 @@
#ifndef X_TERRAIN_H_
#define X_TARRAIN_H_
#include <unordered_set>
#include <iostream>
#include <Ogre.h>
#include <OgreBullet.h>
#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>
#include <OgreTerrain.h>
#include <OgreTerrainGroup.h>
#include <OgrePageManager.h>
#include <OgreTerrainPagedWorldSection.h>
#include <OgrePage.h>
#include <OgreTerrainPaging.h>
class TerrainSetup {
Ogre::SceneManager *mScnMgr;
std::unique_ptr<Ogre::Bullet::DynamicsWorld> mDynWorld;
std::unique_ptr<Ogre::Bullet::DebugDrawer> mDbgDraw;
Ogre::TerrainGlobalOptions *mTerrainGlobals;
Ogre::TerrainGroup *mTerrainGroup;
Ogre::TerrainPaging *mTerrainPaging;
Ogre::PageManager *mPageManager;
Ogre::PagedWorld *mPagedWorld;
Ogre::TerrainPagedWorldSection *mTerrainPagedWorldSection;
bool mLodStatus;
class DummyPageProvider : public Ogre::PageProvider {
public:
DummyPageProvider(btDynamicsWorld *world)
: Ogre::PageProvider()
, mBtWorld(world)
{
}
std::unordered_map<Ogre::PageID, btRigidBody *> body;
btDynamicsWorld *mBtWorld;
bool prepareProceduralPage(Ogre::Page *page,
Ogre::PagedWorldSection *section)
{
Ogre::TerrainGroup *pGroup =
((Ogre::TerrainPagedWorldSection *)section)
->getTerrainGroup();
long x, y;
pGroup->unpackIndex(page->getID(), &x, &y);
Ogre::Terrain *pTerrain = pGroup->getTerrain(x, y);
if (!pTerrain)
return true;
float *terrainHeightData = pTerrain->getHeightData();
Ogre::Vector3 terrainPosition = pTerrain->getPosition();
float *pDataConvert = new float[pTerrain->getSize() *
pTerrain->getSize()];
for (int i = 0; i < pTerrain->getSize(); i++)
memcpy(pDataConvert + pTerrain->getSize() *
i, // source
terrainHeightData +
pTerrain->getSize() *
(pTerrain->getSize() -
i - 1), // target
sizeof(float) *
(pTerrain->getSize()) // size
);
float metersBetweenVertices =
pTerrain->getWorldSize() /
(pTerrain->getSize() -
1); //edit: fixed 0 -> 1 on 2010-08-13
btVector3 localScaling(metersBetweenVertices, 1,
metersBetweenVertices);
btHeightfieldTerrainShape *groundShape =
new btHeightfieldTerrainShape(
pTerrain->getSize(),
pTerrain->getSize(), pDataConvert,
1 /*ignore*/, pTerrain->getMinHeight(),
pTerrain->getMaxHeight(), 1, PHY_FLOAT,
true);
groundShape->setUseDiamondSubdivision(true);
groundShape->setLocalScaling(localScaling);
btRigidBody *groundBody = new btRigidBody(
0, new btDefaultMotionState(), groundShape);
groundBody->getWorldTransform().setOrigin(btVector3(
terrainPosition.x,
terrainPosition.y + (pTerrain->getMaxHeight() -
pTerrain->getMinHeight()) /
2,
terrainPosition.z));
groundBody->getWorldTransform().setRotation(
btQuaternion(Ogre::Quaternion::IDENTITY.x,
Ogre::Quaternion::IDENTITY.y,
Ogre::Quaternion::IDENTITY.z,
Ogre::Quaternion::IDENTITY.w));
mBtWorld->addRigidBody(groundBody);
body[page->getID()] = groundBody;
return true;
}
bool loadProceduralPage(Ogre::Page *page,
Ogre::PagedWorldSection *section)
{
return true;
}
bool unloadProceduralPage(Ogre::Page *page,
Ogre::PagedWorldSection *section)
{
return true;
}
bool unprepareProceduralPage(Ogre::Page *page,
Ogre::PagedWorldSection *section)
{
return true;
}
};
DummyPageProvider mDummyPageProvider;
bool mFly;
bool mParallaxOcclusion;
Ogre::Real mFallVelocity;
enum Mode {
MODE_NORMAL = 0,
MODE_EDIT_HEIGHT = 1,
MODE_EDIT_BLEND = 2,
MODE_COUNT = 3
};
enum ShadowMode {
SHADOWS_NONE = 0,
SHADOWS_COLOUR = 1,
SHADOWS_DEPTH = 2,
SHADOWS_COUNT = 3
};
Mode mMode;
ShadowMode mShadowMode;
Ogre::uint8 mLayerEdit;
Ogre::Real mBrushSizeTerrainSpace;
Ogre::SceneNode *mEditNode;
Ogre::Entity *mEditMarker;
Ogre::Real mHeightUpdateCountDown;
Ogre::Real mHeightUpdateRate;
Ogre::Vector3 mTerrainPos;
/*
OgreBites::SelectMenu *mEditMenu;
OgreBites::SelectMenu *mShadowsMenu;
OgreBites::CheckBox *mFlyBox;
OgreBites::CheckBox *mParallaxOcclusionBox;
OgreBites::Label *mInfoLabel = nullptr;
*/
bool mTerrainsImported;
Ogre::ShadowCameraSetupPtr mPSSMSetup;
typedef std::list<Ogre::Entity *> EntityList;
EntityList mHouseList;
public:
TerrainSetup(btDynamicsWorld *world)
: mScnMgr(nullptr)
, mTerrainPos(Ogre::Vector3(0, 0, 0))
, mDummyPageProvider(world)
{
}
void defineTerrain(long x, long y, bool flat = false)
{
// if a file is available, use it
// if not, generate file from import
// Usually in a real project you'll know whether the compact terrain data is
// available or not; I'm doing it this way to save distribution size
if (flat) {
mTerrainGroup->defineTerrain(x, y, 0.0f);
return;
}
//! [define]
Ogre::String filename = mTerrainGroup->generateFilename(x, y);
if (Ogre::ResourceGroupManager::getSingleton().resourceExists(
mTerrainGroup->getResourceGroup(), filename)) {
mTerrainGroup->defineTerrain(x, y, filename);
} else {
Ogre::Image img;
getTerrainImage(x % 2 != 0, y % 2 != 0, img);
mTerrainGroup->defineTerrain(x, y, &img);
mTerrainsImported = true;
}
//! [define]
}
void getTerrainImage(bool flipX, bool flipY, Ogre::Image &img)
{
//! [heightmap]
img.load("terrain.png", mTerrainGroup->getResourceGroup());
if (flipX)
img.flipAroundY();
if (flipY)
img.flipAroundX();
//! [heightmap]
}
void initBlendMaps(Ogre::Terrain *terrain)
{
//! [blendmap]
using namespace Ogre;
TerrainLayerBlendMap *blendMap0 = terrain->getLayerBlendMap(1);
TerrainLayerBlendMap *blendMap1 = terrain->getLayerBlendMap(2);
float minHeight0 = 20;
float fadeDist0 = 15;
float minHeight1 = 70;
float fadeDist1 = 15;
float *pBlend0 = blendMap0->getBlendPointer();
float *pBlend1 = blendMap1->getBlendPointer();
for (uint16 y = 0; y < terrain->getLayerBlendMapSize(); ++y) {
for (uint16 x = 0; x < terrain->getLayerBlendMapSize();
++x) {
Real tx, ty;
blendMap0->convertImageToTerrainSpace(x, y, &tx,
&ty);
float height =
terrain->getHeightAtTerrainPosition(tx,
ty);
*pBlend0++ = Math::saturate(
(height - minHeight0) / fadeDist0);
*pBlend1++ = Math::saturate(
(height - minHeight1) / fadeDist1);
}
}
blendMap0->dirty();
blendMap1->dirty();
blendMap0->update();
blendMap1->update();
//! [blendmap]
// set up a colour map
/*
if (!terrain->getGlobalColourMapEnabled())
{
terrain->setGlobalColourMapEnabled(true);
Image colourMap;
colourMap.load("testcolourmap.jpg", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
terrain->getGlobalColourMap()->loadImage(colourMap);
}
*/
}
void configureTerrainDefaults(Ogre::Light *l);
#if 0
void addTextureShadowDebugOverlay(Ogre::TrayLocation loc, size_t num)
{
for (size_t i = 0; i < num; ++i) {
Ogre::TexturePtr shadowTex = m_app->getSceneManager()->getShadowTexture(i);
addTextureDebugOverlay(loc, shadowTex, i);
}
}
#endif
#if 0
void changeShadows()
{
configureShadows(mShadowMode != SHADOWS_NONE,
mShadowMode == SHADOWS_DEPTH);
}
void configureShadows(bool enabled, bool depthShadows)
{
auto matProfile =
static_cast<Ogre::TerrainMaterialGeneratorA::SM2Profile *>(
mTerrainGlobals->getDefaultMaterialGenerator()
->getActiveProfile());
matProfile->setReceiveDynamicShadowsEnabled(
false); // force regen if colour/ depth shadows change
matProfile->setReceiveDynamicShadowsEnabled(enabled);
matProfile->setReceiveDynamicShadowsLowLod(
SHADOWS_IN_LOW_LOD_MATERIAL);
Ogre::RTShader::RenderState *schemRenderState =
mShaderGenerator->getRenderState(MSN_SHADERGEN);
if (auto srs = schemRenderState->getSubRenderState(
RTShader::SRS_SHADOW_MAPPING)) {
schemRenderState->removeSubRenderState(srs);
}
if (enabled) {
// General scene setup
mSceneMgr->setShadowTechnique(
SHADOWTYPE_TEXTURE_ADDITIVE_INTEGRATED);
mSceneMgr->setShadowFarDistance(3000);
// 3 textures per directional light (PSSM)
mSceneMgr->setShadowTextureCountPerLightType(
Ogre::Light::LT_DIRECTIONAL, 3);
if (!mPSSMSetup) {
// shadow camera setup
PSSMShadowCameraSetup *pssmSetup =
new PSSMShadowCameraSetup();
pssmSetup->setSplitPadding(
mCamera->getNearClipDistance() * 2);
pssmSetup->calculateSplitPoints(
3, mCamera->getNearClipDistance(),
mSceneMgr->getShadowFarDistance());
pssmSetup->setOptimalAdjustFactor(0, 2);
pssmSetup->setOptimalAdjustFactor(1, 1);
pssmSetup->setOptimalAdjustFactor(2, 0.5);
mPSSMSetup.reset(pssmSetup);
}
mSceneMgr->setShadowCameraSetup(mPSSMSetup);
if (depthShadows) {
mSceneMgr->setShadowTextureCount(3);
mSceneMgr->setShadowTextureConfig(0, 2048, 2048,
PF_DEPTH16);
mSceneMgr->setShadowTextureConfig(1, 1024, 1024,
PF_DEPTH16);
mSceneMgr->setShadowTextureConfig(2, 1024, 1024,
PF_DEPTH16);
mSceneMgr->setShadowTextureSelfShadow(true);
mSceneMgr->setShadowCasterRenderBackFaces(true);
auto subRenderState =
mShaderGenerator->createSubRenderState(
RTShader::SRS_SHADOW_MAPPING);
subRenderState->setParameter(
"split_points",
static_cast<PSSMShadowCameraSetup *>(
mPSSMSetup.get())
->getSplitPoints());
schemRenderState->addTemplateSubRenderState(
subRenderState);
} else {
mSceneMgr->setShadowTextureCount(3);
mSceneMgr->setShadowTextureConfig(0, 2048, 2048,
PF_X8B8G8R8);
mSceneMgr->setShadowTextureConfig(1, 1024, 1024,
PF_X8B8G8R8);
mSceneMgr->setShadowTextureConfig(2, 1024, 1024,
PF_X8B8G8R8);
mSceneMgr->setShadowTextureSelfShadow(false);
mSceneMgr->setShadowCasterRenderBackFaces(
false);
}
matProfile->setReceiveDynamicShadowsPSSM(
static_cast<PSSMShadowCameraSetup *>(
mPSSMSetup.get()));
// addTextureShadowDebugOverlay(TL_RIGHT, 3);
} else {
mSceneMgr->setShadowTechnique(SHADOWTYPE_NONE);
}
// Update parallax occlusion
matProfile->setLayerParallaxOcclusionMappingEnabled(
mParallaxOcclusion);
mShaderGenerator->invalidateScheme(MSN_SHADERGEN);
}
#endif
#if 0
void setupControls()
{
mTrayMgr->showCursor();
// make room for the controls
mTrayMgr->showLogo(TL_TOPRIGHT);
mTrayMgr->showFrameStats(TL_TOPRIGHT);
mTrayMgr->toggleAdvancedFrameStats();
//! [infolabel_create]
mInfoLabel = mTrayMgr->createLabel(TL_TOP, "TInfo", "", 350);
//! [infolabel_create]
mEditMenu = mTrayMgr->createLongSelectMenu(
TL_BOTTOM, "EditMode", "Edit Mode", 370, 250, 3);
mEditMenu->addItem("None");
mEditMenu->addItem("Elevation");
mEditMenu->addItem("Blend");
mEditMenu->selectItem(0); // no edit mode
mFlyBox = mTrayMgr->createCheckBox(TL_BOTTOM, "Fly", "Fly");
mFlyBox->setChecked(false, false);
mParallaxOcclusionBox =
mTrayMgr->createCheckBox(TL_BOTTOM, "ParallaxOcclusion",
"Parallax Occlusion Mapping");
mParallaxOcclusionBox->setChecked(false, false);
mShadowsMenu = mTrayMgr->createLongSelectMenu(
TL_BOTTOM, "Shadows", "Shadows", 370, 250, 3);
mShadowsMenu->addItem("None");
mShadowsMenu->addItem("Colour Shadows");
mShadowsMenu->addItem("Depth Shadows");
mShadowsMenu->selectItem(0); // no edit mode
// a friendly reminder
StringVector names;
names.push_back("Help");
mTrayMgr->createParamsPanel(TL_TOPLEFT, "Help", 100, names)
->setParamValue(0, "H/F1");
}
#endif
void setupTerrain(Ogre::Camera *camera, Ogre::Light *sun,
Ogre::Bullet::DynamicsWorld *dynamicsWorld,
Ogre::Bullet::DebugDrawer *dbgDraw);
float get_height(const Ogre::Vector3 &pos)
{
return mTerrainGroup->getHeightAtWorldPosition(pos);
}
std::unordered_set<long> col_terrains;
std::unordered_map<long, btRigidBody *> col_bodies;
void create_colliders()
{
btDynamicsWorld *world = mDynWorld->getBtWorld();
std::unordered_set<long> new_terrains;
Ogre::TerrainGroup::TerrainSlotMap slots =
mTerrainGroup->getTerrainSlots();
auto slot_it = slots.begin();
while (slot_it != slots.end()) {
Ogre::Terrain *terrain = slot_it->second->instance;
long x = slot_it->second->x;
long y = slot_it->second->y;
long key = y * 1024 + y;
std::cout << "x: " << x << "\n";
std::cout << "y: " << y << "\n";
if (!terrain) {
slot_it++;
continue;
}
if (new_terrains.find(key) != new_terrains.end()) {
slot_it++;
continue;
}
if (col_terrains.find(key) != col_terrains.end()) {
slot_it++;
continue;
}
new_terrains.insert(key);
col_terrains.insert(key);
btRigidBody *body = createHeightfieldShape(
terrain->getSize(), terrain->getHeightData(),
terrain->getMinHeight(),
terrain->getMaxHeight(), terrain->getPosition(),
terrain->getWorldSize() /
(terrain->getSize() - 1),
world);
OgreAssert(body, "Could not allocate body");
col_bodies[key] = body;
std::cout << "adding terrain body: " << terrain << " "
<< col_bodies[key] << "\n";
slot_it++;
}
auto col_it = col_terrains.begin();
std::list<long> rm_bodies;
while (col_it != col_terrains.end()) {
std::cout
<< "checking: terrain still exists: " << *col_it
<< "\n";
/* no terrain */
if (new_terrains.find(*col_it) == new_terrains.end()) {
std::cout << "was removed: "
<< col_bodies[*col_it] << " "
<< *col_it << "\n";
rm_bodies.push_back(*col_it);
/* free shapes and bodies */
}
col_it++;
}
#if 0
while (!rm_bodies.empty()) {
long key = rm_bodies.front();
rm_bodies.pop_front();
btRigidBody *body = col_bodies[key];
OgreAssert(body, "No body :(");
btCollisionShape *shape = body->getCollisionShape();
col_terrains.erase(key);
mDynWorld->getBtWorld()->removeRigidBody(body);
delete shape;
delete body;
}
#endif
}
btRigidBody *createHeightfieldShape(int size, float *data,
const Ogre::Real &minHeight,
const Ogre::Real &maxHeight,
const Ogre::Vector3 &position,
const Ogre::Real &scale,
btDynamicsWorld *world)
{
Ogre::SceneNode *terrainNode =
mScnMgr->getRootSceneNode()->createChildSceneNode(
position, Ogre::Quaternion::IDENTITY);
// Convert height data in a format suitable for the physics engine
float *terrainHeights = new float[size * size];
assert(terrainHeights != 0);
for (int i = 0; i < size; i++) {
memcpy(terrainHeights + size * i,
data + size * (size - i - 1),
sizeof(float) * size);
}
btScalar heightScale = 1.f;
btVector3 localScaling(scale, heightScale, scale);
btHeightfieldTerrainShape *terrainShape =
new btHeightfieldTerrainShape(
size, size, terrainHeights, 1 /*ignore*/,
minHeight, maxHeight, 1, PHY_FLOAT, true);
terrainShape->setUseDiamondSubdivision(true);
terrainShape->setLocalScaling(localScaling);
//Create Rigid Body using 0 mass so it is static
btRigidBody *body = new btRigidBody(
0, new Ogre::Bullet::RigidBodyState(terrainNode),
terrainShape);
body->setFriction(0.8f);
body->setHitFraction(0.8f);
body->setRestitution(0.6f);
body->getWorldTransform().setOrigin(btVector3(
position.x, position.y + (maxHeight - minHeight) / 2,
position.z));
body->getWorldTransform().setRotation(
Ogre::Bullet::convert(Ogre::Quaternion::IDENTITY));
body->setCollisionFlags(body->getCollisionFlags() |
btCollisionObject::CF_STATIC_OBJECT);
world->addRigidBody(body);
return body;
}
};
#endif