Terrain code 90% done.

git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@123 ea6a568a-9f4f-0410-981a-c910a81bb256
actorid
nkorslund 16 years ago
parent 5c41ce85d4
commit f739bf90f1

@ -259,9 +259,9 @@ bool isPressed(Keys key)
return false;
}
// Enable superman mode, ie. flight and super-speed. This is getting
// very spaghetti-ish.
extern(C) void d_superman()
// Enable superman mode, ie. flight and super-speed. Only used for
// debugging the terrain mode.
extern(C) void d_terr_superman()
{
bullet_fly();
speed = 8000;

@ -204,8 +204,8 @@ extern "C" void ogre_makeScene()
// Morrowind uses, and it automagically makes everything work as it
// should.
SceneNode *rt = mSceneMgr->getRootSceneNode();
root = rt->createChildSceneNode();
root->pitch(Degree(-90));
mwRoot = rt->createChildSceneNode();
mwRoot->pitch(Degree(-90));
/*
g_light = mSceneMgr->createLight("carry");
@ -397,7 +397,7 @@ extern "C" SceneNode *ogre_insertNode(SceneNode *base, char* name,
float scale)
{
//std::cout << "ogre_insertNode(" << name << ")\n";
SceneNode *node = root->createChildSceneNode(name);
SceneNode *node = mwRoot->createChildSceneNode(name);
// Make a copy of the node
cloneNode(base, node, name);
@ -453,7 +453,7 @@ extern "C" void ogre_createWater(float level)
150000,150000
);
Entity *ent = mSceneMgr->createEntity( "WaterEntity", "water" );
root->createChildSceneNode()->attachObject(ent);
mwRoot->createChildSceneNode()->attachObject(ent);
ent->setCastShadows(false);
}
@ -699,8 +699,8 @@ extern "C" void ogre_createMaterial(char *name, // Name to give
extern "C" SceneNode *ogre_getDetachedNode()
{
SceneNode *node = root->createChildSceneNode();
root->removeChild(node);
SceneNode *node = mwRoot->createChildSceneNode();
mwRoot->removeChild(node);
return node;
}

@ -71,7 +71,7 @@ int32_t guiMode = 0;
// Root node for all objects added to the scene. This is rotated so
// that the OGRE coordinate system matches that used internally in
// Morrowind.
SceneNode *root;
SceneNode *mwRoot;
// Include the other parts of the code, and make one big happy object
// file. This is extremely against the grain of C++ "recomended

@ -132,10 +132,6 @@ void setupOgre(bool debugOut)
OgreException("Configuration abort");
ogre_initWindow();
// We set up the scene manager in a separate function, since we
// might have to do that for every new cell later on, and handle
// exterior cells differently, etc.
ogre_makeScene();
ogreSetup = true;

@ -32,6 +32,28 @@ version(Windows)
else
static int pageSize = 4*1024;
extern(C)
{
// Convert a texture index to string
char *d_terr_getTexName(int index)
{ return g_archive.getString(index).ptr; }
// Fill various hardware buffers from cache
void d_terr_fillVertexBuffer(MeshInfo *mi, float *buffer)
{ mi.fillVertexBuffer(buffer); }
void d_terr_fillIndexBuffer(MeshInfo *mi, ushort *buffer)
{ mi.fillIndexBuffer(buffer); }
void d_terr_fillAlphaBuffer(AlphaInfo *mi, ubyte *buffer)
{ mi.fillAlphaBuffer(buffer); }
// Get a given alpha map struct belonging to a mesh
AlphaInfo *d_terr_getAlphaInfo(MeshInfo *mi, int index)
{ return mi.getAlphaInfo(index); }
}
// Info about the entire quad. TODO: Some of this (such as the texture
// scale and probably the width and radius) can be generated at
// loadtime and is common for all quads on the same level. We could
@ -62,11 +84,11 @@ struct QuadInfo
size_t offset, size;
}
// Info about an alpha map belonging to a mesh
struct AlphaInfo
{
size_t bufSize, bufOffset;
// Position of the actual image data
ulong bufSize, bufOffset;
// The texture name for this layer. The actual string is stored in
// the archive's string buffer.
@ -78,21 +100,11 @@ struct AlphaInfo
{
g_archive.copy(abuf, bufOffset, bufSize);
}
// Get the texture for this alpha layer
char[] getTexName()
{
return g_archive.getString(texName);
}
// Get the material name to give the alpha texture
char[] getAlphaName()
{
return g_archive.getString(alphaName);
}
}
static assert(AlphaInfo.sizeof == 6*4);
// Info about each submesh
align(1)
struct MeshInfo
{
// Bounding box info
@ -110,11 +122,11 @@ struct MeshInfo
float heightOffset;
// Size and offset of the vertex buffer
size_t vertBufSize, vertBufOffset;
ulong vertBufSize, vertBufOffset;
// Number and offset of AlphaInfo blocks
int alphaNum;
size_t alphaOffset;
ulong alphaOffset;
// Texture name. Index to the string table.
int texName;
@ -208,21 +220,9 @@ struct MeshInfo
res += num;
return res;
}
// Get the size of the alpha textures (in pixels).
int getAlphaSize()
{ return g_archive.alphaSize; }
// Get the texture and material name to use for this mesh.
char[] getTexName()
{ return g_archive.getString(texName); }
float getTexScale()
{ return g_archive.curQuad.texScale; }
char[] getBackgroundTex()
{ return "_land_default.dds"; }
}
static assert(MeshInfo.sizeof == 17*4);
struct ArchiveHeader
{

@ -8,9 +8,10 @@ extern(C):
SceneNode terr_createChildNode(float relX, float relY, SceneNode);
void terr_destroyNode(SceneNode);
Bounds terr_makeBounds(float minHeight, float maxHeight, float width);
Bounds terr_makeBounds(float minHeight, float maxHeight, float width, SceneNode);
void terr_killBounds(Bounds);
float terr_getSqCamDist(Bounds);
MeshObj terr_makeMesh(int segment, SceneNode);
MeshObj terr_makeMesh(SceneNode,void*,int,float);
void terr_killMesh(MeshObj);
void terr_genData();

@ -1,16 +1,13 @@
class BaseLand
{
public:
BaseLand(Ogre::SceneNode* s)
: mTerrainSceneNode(s)
BaseLand()
{
createMaterial();
createMesh();
}
~BaseLand()
{
destroyMaterial();
destroyMesh();
}
@ -21,9 +18,7 @@ public:
// Recreate the mesh if the view distance has increased
if ( vd > mMeshDistance )
{
destroyMaterial();
destroyMesh();
createMaterial();
createMesh();
}
@ -31,41 +26,52 @@ public:
p.x -= ((int)p.x % CELL_WIDTH);
p.z -= ((int)p.z % CELL_WIDTH);
float h = p.y + 2048;
h = pow(h/CELL_WIDTH*2,2);
if ( h < 0 ) h = 0;
float h = (p.y + 2048)*2.0/CELL_WIDTH;
h *= h;
mNode->setPosition(p.x, -32 - h, p.z);
mNode->setPosition(p.x, -p.z, -32 - h);
}
private:
void createMesh()
{
float vd = mCamera->getFarClipDistance();
mMeshDistance = vd;
vd = vd/CELL_WIDTH * 32;
mMat = Ogre::MaterialManager::getSingleton().
create("BaseLandMat",
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
Ogre::TextureUnitState* us = mMat->getTechnique(0)->getPass(0)->createTextureUnitState("_land_default.dds");
us->setTextureScale(1.0f/vd,1.0f/vd);
mMat->getTechnique(0)->getPass(0)->setDepthBias(-1);
mObject = mSceneMgr->createManualObject("BaseLand");
mObject->begin("BaseLandMat", Ogre::RenderOperation::OT_TRIANGLE_LIST);
Ogre::Real vd = mCamera->getFarClipDistance();
vd += CELL_WIDTH - ((int)vd % CELL_WIDTH);
mMeshDistance = vd;
vd = mMeshDistance;
mObject->position(-vd,-2048, vd);
mObject->position(-vd,vd,-2048);
mObject->textureCoord(0, 1);
mObject->position(vd,-2048, vd);
mObject->textureCoord(1, 1);
mObject->position(-vd,-vd,-2048);
mObject->textureCoord(0, 0);
mObject->position(vd,-2048, -vd);
mObject->position(vd,-vd,-2048);
mObject->textureCoord(1, 0);
mObject->position(-vd,-2048, -vd);
mObject->textureCoord(0, 0);
mObject->position(vd,vd,-2048);
mObject->textureCoord(1, 1);
mObject->quad(0,1,2,3);
mObject->end();
mNode = mTerrainSceneNode->createChildSceneNode();
mNode = g_rootTerrainNode->createChildSceneNode();
mNode->attachObject(mObject);
}
@ -74,31 +80,7 @@ private:
mNode->detachAllObjects();
mSceneMgr->destroyManualObject(mObject);
mNode->getParentSceneNode()->removeAndDestroyChild(mNode->getName());
}
// FIXME: We destroy and recreate the material (and mesh) when the
// view distance changes. If we make a built-in auto-adjusting FPS
// optimizer, this will happen quite a lot, so it's worth trying to
// optimize this. This might be moot however when we implement
// water, since BaseLand may not be needed anymore.
void createMaterial()
{
float vd = mCamera->getFarClipDistance();
vd += CELL_WIDTH - ((int)vd % CELL_WIDTH);
vd = vd/CELL_WIDTH * 2;
mMat = Ogre::MaterialManager::getSingleton().
create(std::string("BaseLandMat"),
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
Ogre::TextureUnitState* us = mMat->getTechnique(0)->getPass(0)->createTextureUnitState("_land_default.dds");
us->setTextureScale(0.1f/vd,0.1f/vd);
mMat->getTechnique(0)->getPass(0)->setDepthBias(-1);
}
void destroyMaterial()
{
mMat->getCreator()->remove(mMat->getHandle());
mMat = Ogre::MaterialPtr();
}
@ -114,6 +96,4 @@ private:
///In essence, the farViewDistance of the camera last frame
Ogre::Real mMeshDistance;
Ogre::SceneNode* mTerrainSceneNode;
};

@ -3,18 +3,13 @@ class TerrainMesh : public Ogre::Renderable, public Ogre::MovableObject
{
public:
TerrainMesh(int segNum, Ogre::SceneNode *parent)
TerrainMesh(Ogre::SceneNode *parent, const MeshInfo &info,
int level, float scale)
: Ogre::Renderable(),
Ogre::MovableObject()
{
using namespace Ogre;
// Get the mesh properties from the archive. The pointer is only
// valid for the duration of this function.
const MeshInfo &info = *g_archive.getMeshInfo(segNum);
// Split all this off into sub-functions again later when you're
// finished.
// This is a bit messy, with everything in one function. We could
// split it up later.
// Use MW coordinates all the way
mBounds.setExtents(0,0,info.minHeight,
@ -93,8 +88,7 @@ public:
Pass* pass = mMaterial->getTechnique(0)->getPass(0);
pass->setLightingEnabled(false);
int lev = info.getLevel();
if(lev != 1)
if(level != 1)
{
// This material just has a normal texture
pass->createTextureUnitState(texName)
@ -103,16 +97,17 @@ public:
}
else
{
// We have to use alpha splatting
float scale = info.getTexScale();
// Get the background texture
const char *bgTex = info.getBackgroundTex();
// Get the background texture. TODO: We should get this from
// somewhere, no file names should be hard coded. The texture
// might exist as a .tga in earlier versions of the game, and
// we might also want to specify a different background
// texture on some meshes.
//const char *bgTex = info.getBackgroundTex();
const char *bgTex = "_land_default.dds";
pass->createTextureUnitState(bgTex)
->setTextureScale(scale,scale);
int alphaSize = info.getAlphaSize();
// Loop through all the textures in this mesh
for(int tnum=0; tnum<info.alphaNum; tnum++)
{
@ -131,7 +126,7 @@ public:
(alphaName,
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
Ogre::TEX_TYPE_2D,
alphaSize,alphaSize,
g_alphaSize,g_alphaSize,
1,0, // depth, mipmaps
Ogre::PF_A8, // One-channel alpha
Ogre::TU_STATIC_WRITE_ONLY);
@ -180,17 +175,13 @@ public:
~TerrainMesh()
{
delete mIndices;
// TODO: This used to crash. See what happens now.
delete mVertices;
assert(mNode);
// We haven't tried moving this further up - there's an off chance
// it might have something to do with the crash.
mNode->detachAllObjects();
mNode->getCreator()->destroySceneNode(mNode);
// TODO: This used to crash. See what happens now.
delete mVertices;
delete mIndices;
}
//-----------------------------------------------------------------------

@ -22,76 +22,211 @@
const int CELL_WIDTH = 8192;
SceneNode *g_rootTerrainNode;
int g_alphaSize;
struct MeshInfo;
struct AlphaInfo;
// D functions
extern "C"
{
void d_terr_superman();
void d_terr_terrainUpdate();
char *d_terr_getTexName(int32_t);
void d_terr_fillVertexBuffer(const MeshInfo*,float*);
void d_terr_fillIndexBuffer(const MeshInfo*,uint16_t*);
AlphaInfo *d_terr_getAlphaInfo(const MeshInfo*,int32_t);
void d_terr_fillAlphaBuffer(const AlphaInfo*,uint8_t*);
}
// Info about a submesh. This is a clone of the struct defined in
// archive.d. TODO: Make sure the D and C++ structs are of the same
// size and alignment.
struct MeshInfo
{
// Bounding box info
float minHeight, maxHeight;
float worldWidth;
// Vertex and index numbers
int32_t vertRows, vertCols;
int32_t indexCount;
// Scene node position (relative to the parent node)
float x, y;
// Height offset to apply to all vertices
float heightOffset;
// Size and offset of the vertex buffer
int64_t vertBufSize, vertBufOffset;
// Number and offset of AlphaInfo blocks
int32_t alphaNum;
uint64_t alphaOffset;
// Texture name. Index to the string table.
int32_t texName;
inline void fillVertexBuffer(float *buffer) const
{
d_terr_fillVertexBuffer(this, buffer);
}
inline void fillIndexBuffer(uint16_t *buffer) const
{
d_terr_fillIndexBuffer(this, buffer);
}
inline char* getTexName() const
{
return d_terr_getTexName(texName);
}
inline AlphaInfo *getAlphaInfo(int tnum) const
{
return d_terr_getAlphaInfo(this, tnum);
}
};
// Info about an alpha map belonging to a mesh
struct AlphaInfo
{
// Position of the actual image data
uint64_t bufSize, bufOffset;
// The texture name for this layer. The actual string is stored in
// the archive's string buffer.
int32_t texName;
int32_t alphaName;
inline char* getTexName() const
{
return d_terr_getTexName(texName);
}
inline char* getAlphaName() const
{
return d_terr_getTexName(alphaName);
}
inline void fillAlphaBuffer(uint8_t *buffer) const
{
return d_terr_fillAlphaBuffer(this, buffer);
}
};
#include "cpp_baseland.cpp"
//#include "cpp_mesh.cpp"
#include "cpp_mesh.cpp"
BaseLand *g_baseLand;
SceneNode *g_rootTerrainNode;
class TerrainFrameListener : public FrameListener
{
protected:
bool frameEnded(const FrameEvent& evt)
{
//g_rootQuad->update();
d_terr_terrainUpdate();
g_baseLand->update();
return true;
}
};
// Functions called from D
extern "C"
{
void d_superman();
SceneNode* terr_createChildNode(float relX, float relY,
SceneNode* terr_createChildNode(float x, float y,
SceneNode *parent)
{}
{
Ogre::Vector3 pos(x,y,0);
if(parent == NULL)
parent = g_rootTerrainNode;
assert(parent);
return parent->createChildSceneNode(pos);
}
void terr_destroyNode(SceneNode *node)
{}
{
node->removeAndDestroyAllChildren();
mSceneMgr->destroySceneNode(node);
}
// TODO: We could make allocation a little more refined than new and
// delete. But that's true for everything here. A freelist based
// approach is best in most of these cases, as we have continuous
// allocation/deallocation of fixed-size structs.
Ogre::AxisAlignedBox *terr_makeBounds(float minHeight, float maxHeight,
float width, SceneNode* node)
{
AxisAlignedBox *mBounds = new AxisAlignedBox;
mBounds->setExtents(0,0,minHeight,
width,width,maxHeight);
void *terr_makeBounds(float minHeight, float maxHeight,
float width)
{}
// Transform the box to world coordinates, so it can be compared
// with the camera later.
mBounds->transformAffine(node->_getFullTransform());
float terr_getSqCamDist(void*)
{}
return mBounds;
}
void *terr_makeMesh(int segment, SceneNode*)
{}
void terr_killBounds(AxisAlignedBox *bounds)
{
delete bounds;
}
void terr_killMesh(void*)
{}
float terr_getSqCamDist(AxisAlignedBox *mBounds)
{
Ogre::Vector3 cpos = mCamera->getDerivedPosition();
Ogre::Vector3 diff(0, 0, 0);
diff.makeFloor(cpos - mBounds->getMinimum() );
diff.makeCeil(cpos - mBounds->getMaximum() );
return diff.squaredLength();
}
TerrainMesh *terr_makeMesh(SceneNode *parent,
MeshInfo *info,
int level, float scale)
{
return new TerrainMesh(parent, *info, level, scale);
}
void terr_killMesh(TerrainMesh *mesh)
{ delete mesh; }
// Set up the rendering system
void terr_setupRendering()
{
// Make sure the C++ sizes match the D sizes, since the structs
// will be shared between the two.
assert(sizeof(MeshInfo) == 17*4);
assert(sizeof(AlphaInfo) == 6*4);
// Add the terrain directory as a resource location. TODO: Get the
// name from D.
ResourceGroupManager::getSingleton().
addResourceLocation("cache/terrain/", "FileSystem", "General");
// Enter superman mode
mCamera->setFarClipDistance(32*CELL_WIDTH);
//ogre_setFog(0.7, 0.7, 0.7, 200, 32*CELL_WIDTH);
d_terr_superman();
// Create a root scene node first. The 'root' node is rotated to
// match the MW coordinate system
g_rootTerrainNode = root->createChildSceneNode("TERRAIN_ROOT");
g_rootTerrainNode = mwRoot->createChildSceneNode("TERRAIN_ROOT");
// Add the base land. This is the ground beneath the actual
// terrain mesh that makes the terrain look infinite.
g_baseLand = new BaseLand(g_rootTerrainNode);
/*
// Add the terrain directory
ResourceGroupManager::getSingleton().
addResourceLocation(g_cacheDir, "FileSystem", "General");
// Open the archive file
g_archive.openFile(g_cacheFile);
// Create the root quad.
g_rootQuad = new Quad();
*/
g_baseLand = new BaseLand();
// Add the frame listener
mRoot->addFrameListener(new TerrainFrameListener);
// Enter superman mode
mCamera->setFarClipDistance(32*CELL_WIDTH);
//ogre_setFog(0.7, 0.7, 0.7, 200, 32*CELL_WIDTH);
d_superman();
}
}

@ -24,18 +24,16 @@
// This module is responsible for generating the cache files.
module terrain.generator;
/+
import std.stdio;
import std.string;
import terrain.cachewriter;
import terrain.esmland;
import terrain.terrain;
import util.cachefile;
const float TEX_SCALE = 1.0/16;
char[] cacheDir = "cache/terrain/";
int mCount;
// Texture sizes for the various levels. For the most detailed level
@ -43,21 +41,18 @@ int mCount;
// than a final texture.
int[] texSizes;
// Default textures
GenLevelResult[] defaults;
CacheWriter cache;
void generate()
void generate(char[] filename)
{
makePath(cacheDir);
cache.openFile(filename);
//cache.openFile(filename);
// Find the maxiumum distance from (0,0) in any direction
int max = mwland.getMaxCoord();
// Round up to nearest binary
// Round up to nearest power of 2
int depth=1;
while(max)
{
@ -73,7 +68,7 @@ void generate()
// Set the texture sizes. TODO: These should be config options,
// perhaps - or maybe a result of some higher-level detail setting.
texSizes.resize(depth+1, 0);
texSizes.length = depth+1;
texSizes[6] = 1024;
texSizes[5] = 512;
texSizes[4] = 256;
@ -81,9 +76,12 @@ void generate()
texSizes[2] = 256;
texSizes[1] = 64;
writefln("Data generation not implemented yet");
// Set some general parameters for the runtime
cache.setParams(depth+1, texSizes[1]);
/*
// Create some common data first
writefln("Generating common data");
genDefaults();
@ -97,8 +95,14 @@ void generate()
writefln("Writing index file");
cache.finish();
writefln("Pregeneration done. Results written to ", filename);
*/
}
/+
// Default textures
GenLevelResult[] defaults;
// Generates the default texture images "2_default.png" etc
void genDefaults()
{

@ -40,12 +40,6 @@ class Quad
mNode = terr_createChildNode(relX*CELL_WIDTH,
relY*CELL_WIDTH,
parent.mNode);
/*
Ogre::Vector3 pos(relX * CELL_WIDTH,
relY * CELL_WIDTH,
0);
mNode = parent.mNode.createChildSceneNode(pos);
*/
// Get the archive data for this quad.
mInfo = g_archive.getQuad(mCellX,mCellY,mLevel);
@ -63,10 +57,6 @@ class Quad
mNode = terr_createChildNode(cellX*CELL_WIDTH,
cellY*CELL_WIDTH,
null);
/*
mNode = g_rootTerrainNode.
createChildSceneNode(pos);
*/
// Split up
split();
@ -78,20 +68,12 @@ class Quad
assert(mLevel >= 1);
assert(mNode !is null);
// TODO: How do we store the C++ bounding box?
// Set up the bounding box. Use MW coordinates all the way
mBounds = terr_makeBounds(mInfo.minHeight,
mInfo.maxHeight,
mInfo.worldWidth);
/*
// Set up the bounding box. Use MW coordinates all the way
mBounds.setExtents(0,0,mInfo.minHeight,
mInfo.worldWidth,mInfo.worldWidth,
mInfo.maxHeight);
// Transform the box to world coordinates, so it can be compared
// with the camera later.
mBounds.transformAffine(mNode._getFullTransform());
*/
mInfo.worldWidth,
mNode);
float radius = mInfo.boundingRadius;
mSplitDistance = radius * SPLIT_FACTOR;
@ -119,10 +101,7 @@ class Quad
delete mChildren[i];
terr_destroyNode(mNode);
/*
mNode.removeAndDestroyAllChildren();
mSceneMgr.destroySceneNode(mNode);
*/
terr_killBounds(mBounds);
}
// Remove the landscape for this quad, and create children.
@ -187,15 +166,6 @@ class Quad
// Get (squared) camera distance. TODO: shouldn't this just
// be a simple vector difference from the mesh center?
float camDist = terr_getSqCamDist(mBounds);
/*
{
Ogre::Vector3 cpos = mCamera.getDerivedPosition();
Ogre::Vector3 diff(0, 0, 0);
diff.makeFloor(cpos - mBounds.getMinimum() );
diff.makeCeil(cpos - mBounds.getMaximum() );
camDist = diff.squaredLength();
}
*/
// No children?
if(!hasChildren)
@ -249,7 +219,10 @@ class Quad
// care of the loading.
meshList.length = mInfo.meshNum;
foreach(i, ref m; meshList)
m = terr_makeMesh(i, mNode);
{
MeshInfo *mi = g_archive.getMeshInfo(i);
m = terr_makeMesh(mNode, mi, mInfo.level, mInfo.texScale);
}
hasMesh = true;
}
@ -275,7 +248,6 @@ class Quad
// Bounding box, transformed to world coordinates. Used to calculate
// camera distance.
//Ogre::AxisAlignedBox mBounds;
Bounds mBounds;
float mSplitDistance,mUnsplitDistance;

@ -24,14 +24,41 @@
module terrain.terrain;
import terrain.generator;
import terrain.archive;
import terrain.bindings;
import terrain.quad;
import std.file, std.stdio;
char[] cacheDir = "cache/terrain/";
void initTerrain(bool doGen)
{
/*
char[] fname = cacheDir ~ "landscape.cache";
if(!exists(fname))
{
writefln("Cache file '%s' not found. Creating:",
fname);
doGen = true;
}
if(doGen)
generate();
*/
generate(fname);
// Load the archive file
g_archive.openFile(fname);
terr_setupRendering();
// Create the root quad
rootQuad = new Quad;
}
extern(C) void d_terr_terrainUpdate()
{
// Update the root quad each frame.
assert(rootQuad !is null);
rootQuad.update();
}
Quad rootQuad;

Loading…
Cancel
Save