- more work on the terrain. STILL not finished.

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

@ -333,7 +333,7 @@ struct HashTable(Key, Value, Alloc = GCAlloc, Hash = DefHash,
// Get a pointer to value of given key, or insert a new. Useful for
// large value types when you want to avoid copying the value around
// needlessly. Also useful if you want to do in-place
// editing. Returns true if a value was inserted.
// editing. Returns true if a new value was inserted.
bool insertEdit(Key k, out Value *ptr)
{
Node *p;

@ -55,7 +55,7 @@ class NiTexturingProperty : Property
bool inUse;
NiSourceTexture texture;
/* Clamp mode (i don't understand this)
/* Clamp mode
0 - clampS clampT
1 - clampS wrapT
2 - wrapS clampT
@ -89,8 +89,11 @@ class NiTexturingProperty : Property
filter = nifFile.getIntIs(0,1,2);
set = nifFile.getInt;
ps2L = nifFile.getShortIs(0);
ps2K = nifFile.getShortIs(-75,-2);
// The combination 1222, 322, 212 was used in a bump map
// NIF. Might just be bogus numbers, I should probably allow all
// values here since we ignore them anyway.
ps2L = nifFile.getShortIs(0,1222);
ps2K = nifFile.getShortIs(-75,-2,322);
debug(verbose)
{
@ -101,7 +104,7 @@ class NiTexturingProperty : Property
writefln(" ps2K ", ps2K);
}
unknown2 = nifFile.wgetShortIs(0,257);
unknown2 = nifFile.wgetShortIs(0,257,212);
}
}

@ -54,6 +54,20 @@ import input.events;
import terrain.terrain;
// Set up exit handler
alias void function() c_func;
extern(C) int atexit(c_func);
bool cleanExit = false;
void exitHandler()
{
// If we exit uncleanly, print the function stack.
if(!cleanExit)
writefln(dbg.getTrace());
}
//*
import std.gc;
import gcstats;
@ -224,6 +238,10 @@ Try specifying another cell name on the command line, or edit openmw.ini.");
// Set the name for the GUI (temporary hack)
gui_setCellName(cd.inCell.id.ptr);
// Set up the exit handler
atexit(&exitHandler);
scope(exit) cleanExit = true;
if(render)
{
// Warm up OGRE

@ -20,12 +20,17 @@
*/
// This should also be part of the generic cache system.
module terrain.archive;
const float TEX_SCALE = 1.0/16;
// This should be part of the generic cache system.
const int CACHE_MAGIC = 0x345AF815;
import std.mmfile;
import std.stream;
import std.string;
import std.stdio;
import terrain.myfile;
version(Windows)
static int pageSize = 64*1024;
@ -39,19 +44,20 @@ extern(C)
{ 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_fillVertexBuffer(MeshInfo *mi, float *buffer, ulong size)
{ mi.fillVertexBuffer(buffer[0..size]); }
void d_terr_fillIndexBuffer(MeshInfo *mi, ushort *buffer)
{ mi.fillIndexBuffer(buffer); }
void d_terr_fillIndexBuffer(MeshInfo *mi, ushort *buffer, ulong size)
{ mi.fillIndexBuffer(buffer[0..size]); }
void d_terr_fillAlphaBuffer(AlphaInfo *mi, ubyte *buffer)
{ mi.fillAlphaBuffer(buffer); }
void d_terr_fillAlphaBuffer(AlphaInfo *mi, ubyte *buffer, ulong size)
{ mi.fillAlphaBuffer(buffer[0..size]); }
// Get a given alpha map struct belonging to a mesh
AlphaInfo *d_terr_getAlphaInfo(MeshInfo *mi, int index)
{ return mi.getAlphaInfo(index); }
int d_terr_getAlphaSize() { return g_archive.alphaSize; }
}
// Info about the entire quad. TODO: Some of this (such as the texture
@ -69,9 +75,6 @@ struct QuadInfo
float worldWidth;
float boundingRadius;
// Texture scale for this quad
float texScale;
// True if we should make the given child
bool hasChild[4];
@ -92,13 +95,14 @@ struct AlphaInfo
// The texture name for this layer. The actual string is stored in
// the archive's string buffer.
int texName;
int alphaName;
int texName = -1;
int alphaName = -1;
// Fill the alpha texture buffer
void fillAlphaBuffer(ubyte *abuf)
void fillAlphaBuffer(ubyte abuf[])
{
g_archive.copy(abuf, bufOffset, bufSize);
assert(abuf.length == bufSize);
g_archive.copy(abuf.ptr, bufOffset, bufSize);
}
}
static assert(AlphaInfo.sizeof == 6*4);
@ -113,10 +117,6 @@ struct MeshInfo
// Vertex and index numbers
int vertRows, vertCols;
int indexCount;
// Scene node position (relative to the parent node)
float x, y;
// Height offset to apply to all vertices
float heightOffset;
@ -129,21 +129,20 @@ struct MeshInfo
ulong alphaOffset;
// Texture name. Index to the string table.
int texName;
int texName = -1;
// Fill the given vertex buffer
void fillVertexBuffer(float *vbuf)
void fillVertexBuffer(float vdest[])
{
//g_archive.copy(vbuf, vertBufOffset, vertBufSize);
// The height map and normals from the archive
char *hmap = cast(char*)g_archive.getRelSlice(vertBufOffset, vertBufSize).ptr;
int level = getLevel();
byte *hmap = cast(byte*)g_archive.getRelSlice(vertBufOffset, vertBufSize).ptr;
// The generic part, containing the x,y coordinates and the uv
// maps.
float *gmap = g_archive.getVertexBuffer(level).ptr;
float *gmap = g_archive.getVertexBuffer(getLevel()).ptr;
// Destination pointer
float *vbuf = vdest.ptr;
assert(vdest.length == vertRows*vertCols*8);
// Calculate the factor to multiply each height value with. The
// heights are very limited in range as they are stored in a
@ -151,7 +150,7 @@ struct MeshInfo
// double this for each successive level since we're splicing
// several vertices together and need to allow larger differences
// for each vertex. The formula is 8*2^(level-1).
float scale = 4.0 * (1<<level);
float scale = 4.0 * (1<<getLevel());
// Merge the two data sets together into the output buffer.
float offset = heightOffset;
@ -178,7 +177,6 @@ struct MeshInfo
*vbuf++ = rowofs * scale;
// Normal vector.
// TODO: Normalize?
*vbuf++ = *hmap++;
*vbuf++ = *hmap++;
*vbuf++ = *hmap++;
@ -196,12 +194,13 @@ struct MeshInfo
}
// Fill the index buffer
void fillIndexBuffer(ushort *ibuf)
void fillIndexBuffer(ushort ibuf[])
{
// The index buffer is pregenerated. It is identical for all
// meshes on the same level, so just copy it over.
ushort generic[] = g_archive.getIndexBuffer(getLevel());
ibuf[0..generic.length] = generic[];
ushort generic[] = g_archive.getIndexBuffer();
assert(ibuf.length == generic.length);
ibuf[] = generic[];
}
int getLevel()
@ -221,9 +220,9 @@ struct MeshInfo
return res;
}
}
static assert(MeshInfo.sizeof == 17*4);
static assert(MeshInfo.sizeof == 14*4);
// The first part of the .index file
struct ArchiveHeader
{
// "Magic" number to make sure we're actually reading an archive
@ -263,10 +262,13 @@ struct TerrainArchive
0, null, pageSize);
// Read the index file first
File ifile = new File(name ~ ".index");
MyFile ifile = new MyFile(name ~ ".index");
ArchiveHeader head;
ifile.readExact(&head, head.sizeof);
ifile.fill(head);
// Reads data into an array. Would be better if this was part of
// the stream.
// Sanity check
assert(head.magic == CACHE_MAGIC);
@ -277,7 +279,7 @@ struct TerrainArchive
// Read all the quads
quadList = new QuadInfo[head.quads];
ifile.readExact(quadList.ptr, head.quads*QuadInfo.sizeof);
ifile.fillArray(quadList);
// Create an index of all the quads
foreach(int index, qn; quadList)
@ -289,6 +291,7 @@ struct TerrainArchive
assert(l >= 1);
quadMap[l][x][y] = index;
assert(index == quadMap[l][x][y]);
// Store the root quad
if(l == head.rootLevel)
@ -303,24 +306,24 @@ struct TerrainArchive
// Make sure the root was set
assert(rootQuad !is null);
// Next read the string table
// Next read the string table. First read the main string buffer.
stringBuf = new char[head.stringSize];
strings.length = head.stringNum;
// First read the main string buffer
ifile.readExact(stringBuf.ptr, head.stringSize);
ifile.fillArray(stringBuf);
// Then read the string offsets
int[] offsets = new int[head.stringNum];
ifile.readExact(offsets.ptr, offsets.length*int.sizeof);
ifile.fillArray(offsets);
// Set up the string table
char *strptr = stringBuf.ptr;
strings.length = head.stringNum;
foreach(int i, ref str; strings)
{
// toString(char*) returns the string up to the zero
// terminator byte
str = toString(strptr + offsets[i]);
assert(str.ptr + str.length <=
stringBuf.ptr + stringBuf.length);
}
delete offsets;
@ -328,23 +331,16 @@ struct TerrainArchive
int bufNum = head.rootLevel;
assert(bufNum == 7);
vertBufData.length = bufNum;
indexBufData.length = bufNum;
// Fill the buffers. Start at level 1.
// Fill the vertex buffers. Start at level 1.
for(int i=1;i<bufNum;i++)
{
int size;
// Vertex buffer
ifile.read(size);
vertBufData[i].length = size;
ifile.readExact(vertBufData[i].ptr, size);
// Index buffer
ifile.read(size);
indexBufData[i].length = size;
ifile.readExact(indexBufData[i].ptr, size);
ifile.readArray(vertBufData[i]);
}
// Index buffer
ifile.readArray(indexBufData);
}
// Get info about a given quad from the index.
@ -389,10 +385,9 @@ struct TerrainArchive
return vertBufData[level];
}
ushort[] getIndexBuffer(int level)
ushort[] getIndexBuffer()
{
assert(level>=1 && level<indexBufData.length);
return indexBufData[level];
return indexBufData;
}
private:
@ -406,7 +401,7 @@ private:
// These contain pregenerated mesh data that is common for all
// meshes on a given level.
float[][] vertBufData;
ushort[][] indexBufData;
ushort[] indexBufData;
// Used for the mmapped file
MmFile mmf;
@ -448,10 +443,10 @@ private:
// Copy a given buffer from the file. The buffer might be a
// compressed stream, so it's important that the buffers are written
// the same as they are read. (Ie. you can't write a buffer as one
// operation and read it as two, or vice versa. Also, buffers cannot
// overlap.) The offset is relative to the current mapped file
// window.
// in the same block sizes as they are read. (Ie. you can't write a
// buffer as one operation and read it as two, or vice versa. Also,
// buffers cannot overlap.) The offset is relative to the current
// mapped file window.
void copy(void *dst, size_t offset, size_t inSize)
{
ubyte source[] = getRelSlice(offset, inSize);

@ -4,6 +4,7 @@ alias void *SceneNode;
alias void *Bounds;
alias void *MeshObj;
// These are all defined in cpp_terrain.cpp:
extern(C):
SceneNode terr_createChildNode(float relX, float relY, SceneNode);
@ -16,3 +17,11 @@ void terr_killMesh(MeshObj);
void terr_genData();
void terr_setupRendering();
void terr_makeLandMaterial(char*,float);
ubyte *terr_makeAlphaLayer(char*,int);
void terr_closeAlpha(char*,char*,float);
void terr_cleanupAlpha(char*,void*,int);
void terr_resize(void*,void*,int,int);
void terr_saveImage(void*,int,char*);

@ -23,8 +23,11 @@
import terrain.archive;
import terrain.outbuffer;
import std.stream;
import std.stdio, std.stream, std.string;
import terrain.myfile;
import std.math2;
import monster.util.string;
import monster.vm.dbg;
// Helper structs
struct AlphaHolder
@ -42,9 +45,6 @@ struct MeshHolder
// Actual buffers
byte[] vertexBuffer;
// Texture name
char[] texName;
// Alpha maps (if any)
AlphaHolder alphas[];
}
@ -74,7 +74,6 @@ struct CacheWriter
alphaSize = alphSize;
vertBuf.length = maxLevel;
indexBuf.length = maxLevel;
}
// Closes the main archive file and writes the index.
@ -83,7 +82,7 @@ struct CacheWriter
mainFile.close();
// Write the index file
scope File ofile = new File(iname, FileMode.OutNew);
scope MyFile ofile = new MyFile(iname, FileMode.OutNew);
// Header first
ArchiveHeader head;
@ -93,11 +92,10 @@ struct CacheWriter
head.alphaSize = alphaSize;
head.stringNum = stringList.length;
head.stringSize = totalStringLength;
ofile.writeExact(&head, head.sizeof);
ofile.dump(head);
// Write the quads
foreach(qi; quadList)
ofile.writeExact(&qi, qi.sizeof);
ofile.dumpArray(quadList);
// String table next. We need to sort it in order of the indices
// first.
@ -117,6 +115,10 @@ struct CacheWriter
// Add one byte for the zero terminator
int len = strVector[i].length + 1;
char *ptr = strVector[i].ptr;
if(ptr[len-1] != 0)
ptr = toStringz(strVector[i]);
assert(ptr[len-1] == 0);
ofile.writeExact(ptr, len);
@ -130,30 +132,20 @@ struct CacheWriter
assert(curOffs == head.stringSize);
// Finally, write the offset table itself
ofile.writeExact(offsets.ptr, offsets.length * int.sizeof);
ofile.dumpArray(offsets);
// Write the common vertex and index buffers
assert(maxLevel == 7);
for(int i=1;i<maxLevel;i++)
{
int size;
void *ptr;
// Write vertex buffer
ptr = vertBuf[i].ptr;
size = vertBuf[i].length;
ofile.write(size);
ofile.writeExact(ptr, size);
// Then the index buffer
ptr = indexBuf[i].ptr;
size = indexBuf[i].length;
ofile.write(size);
ofile.writeExact(ptr, size);
ofile.writeArray(vertBuf[i]);
delete vertBuf[i];
delete indexBuf[i];
}
// Then the index buffer
ofile.writeArray(indexBuf);
// Don't need these anymore
delete offsets;
delete strVector;
@ -165,17 +157,16 @@ struct CacheWriter
}
// Add a common vertex buffer for a given level
void addVertexBuffer(int level, void[] buf)
void addVertexBuffer(int level, float[] buf)
{
assert(vertBuf.length > level);
vertBuf[level] = buf;
}
// Add a common vertex buffer for a given level
void addIndexBuffer(int level, void[] buf)
// Add a common index buffer
void setIndexBuffer(ushort[] buf)
{
assert(indexBuf.length > level);
indexBuf[level] = buf;
indexBuf = buf;
}
// Write a finished quad to the archive file. All the offsets and
@ -183,15 +174,16 @@ struct CacheWriter
// the additional data in the Holder structs.
void writeQuad(ref QuadHolder qh)
{
// Make outbuffer a simple struct that uses a region and keeps
// track of all the slices we allocate.
OutBuffer buf;
scope auto _trc = new MTrace("writeQuad");
// Write the MeshInfo's first
int meshNum = qh.meshes.length;
MeshInfo meshes[] = buf.write!(MeshInfo)(meshNum);
float minh = float.infinity;
float maxh = -float.infinity;
// Then write the mesh data in approximately the order it's read
for(int i=0; i<meshNum; i++)
{
@ -202,15 +194,18 @@ struct CacheWriter
// Copy the basic data first
meshes[i] = mh.info;
minh = min(minh,mh.info.minHeight);
maxh = max(maxh,mh.info.maxHeight);
// Set everything else except the offsets
int alphaNum = mh.alphas.length;
meshes[i].alphaNum = alphaNum;
//meshes[i].texName = addString(mh.texName);
// Write the vertex buffer
meshes[i].vertBufOffset = buf.size;
meshes[i].vertBufSize = mh.vertexBuffer.length;
writeBuf(mh.vertexBuffer);
assert(buf.size == meshes[i].vertBufOffset + meshes[i].vertBufSize);
// Next write the alpha maps, if any
meshes[i].alphaOffset = buf.size;
@ -231,13 +226,22 @@ struct CacheWriter
// Finally set up the QuadInfo itself
QuadInfo qi;
// Basic info
// Copy basic info
qi = qh.info;
// Derived info
qi.meshNum = meshNum;
qi.offset = fileOffset;
qi.size = buf.size;
qi.minHeight = minh;
qi.maxHeight = maxh;
// Get the side length, or the height difference if that is bigger
qi.boundingRadius = max(maxh-minh,qi.worldWidth);
// Multiply with roughly sqrt(1/2), converts from side length to
// radius with some extra slack
qi.boundingRadius *= 0.8;
// The quad cache is done, write it to file
buf.writeTo(mainFile);
@ -307,8 +311,8 @@ private:
// Common vertex and index buffers for all quads. One buffer per
// level.
void[][] vertBuf;
void[][] indexBuf;
float[][] vertBuf;
ushort[] indexBuf;
// Variables that must be set during the gen phase
int maxLevel;

@ -8,15 +8,19 @@ public:
: Ogre::Renderable(),
Ogre::MovableObject()
{
TRACE("TerrainMesh()");
mLevel = level;
// This is a bit messy, with everything in one function. We could
// split it up later.
// Use MW coordinates all the way
assert(info.worldWidth > 0);
assert(info.minHeight <= info.maxHeight);
mBounds.setExtents(0,0,info.minHeight,
// was (mWidth-1) * vertexSeparation
info.worldWidth, info.worldWidth,
info.maxHeight);
mCenter = mBounds.getCenter();
mBoundingRadius = mBounds.getHalfSize().length();
@ -56,16 +60,16 @@ public:
// Fill the buffer
float* verts = static_cast<float*>
(mMainBuffer->lock(HardwareBuffer::HBL_DISCARD));
info.fillVertexBuffer(verts);
info.fillVertexBuffer(verts,8*mVertices->vertexCount);
mMainBuffer->unlock();
// Create the index data holder
mIndices = new IndexData();
mIndices->indexCount = info.indexCount;
mIndices->indexCount = 64*64*6; // TODO: Shouldn't be hard-coded
mIndices->indexBuffer =
HardwareBufferManager::getSingleton().createIndexBuffer
( HardwareIndexBuffer::IT_16BIT,
info.indexCount,
mIndices->indexCount,
HardwareBuffer::HBU_STATIC_WRITE_ONLY,
false);
@ -74,17 +78,16 @@ public:
(mIndices->indexBuffer->lock
(0, mIndices->indexBuffer->getSizeInBytes(),
HardwareBuffer::HBL_DISCARD));
info.fillIndexBuffer(indices);
info.fillIndexBuffer(indices,mIndices->indexCount);
mIndices->indexBuffer->unlock();
// Finally, create the material
const std::string texName = info.getTexName();
// TODO: A better thing to do here is to keep the material loaded
// and retrieve it if it exists.
assert(!MaterialManager::getSingleton().resourceExists(texName));
mMaterial = MaterialManager::getSingleton().create
(texName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
// Create or retrieve the material
mMaterial = MaterialManager::getSingleton().createOrRetrieve
(texName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME).first;
Pass* pass = mMaterial->getTechnique(0)->getPass(0);
pass->setLightingEnabled(false);
@ -119,29 +122,32 @@ public:
// Name of the texture
std::string tname = alpha.getTexName();
// TODO: Need to store the result and either delete it in
// the destructor or fetch it again the next time we run.
Ogre::TexturePtr texPtr = Ogre::TextureManager::
getSingleton().createManual
(alphaName,
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
Ogre::TEX_TYPE_2D,
g_alphaSize,g_alphaSize,
1,0, // depth, mipmaps
Ogre::PF_A8, // One-channel alpha
Ogre::TU_STATIC_WRITE_ONLY);
// Get the pointer
Ogre::HardwarePixelBufferSharedPtr pixelBuffer = texPtr->getBuffer();
pixelBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD);
const Ogre::PixelBox& pixelBox = pixelBuffer->getCurrentLock();
Ogre::uint8* pDest = static_cast<Ogre::uint8*>(pixelBox.data);
// Copy alpha data from file
alpha.fillAlphaBuffer(pDest);
// Finish everything up with a lot of Ogre-code
pixelBuffer->unlock();
// Create the alpha texture if it doesn't exist
if(!TextureManager::getSingleton().resourceExists(alphaName))
{
TexturePtr texPtr = Ogre::TextureManager::
getSingleton().createManual
(alphaName,
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
Ogre::TEX_TYPE_2D,
g_alphaSize,g_alphaSize,
1,0, // depth, mipmaps
Ogre::PF_A8, // One-channel alpha
Ogre::TU_STATIC_WRITE_ONLY);
// Get the pointer
Ogre::HardwarePixelBufferSharedPtr pixelBuffer = texPtr->getBuffer();
pixelBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD);
const Ogre::PixelBox& pixelBox = pixelBuffer->getCurrentLock();
Ogre::uint8* pDest = static_cast<Ogre::uint8*>(pixelBox.data);
// Copy alpha data from file
alpha.fillAlphaBuffer(pDest,g_alphaSize*g_alphaSize);
// Close the buffer
pixelBuffer->unlock();
}
pass = mMaterial->getTechnique(0)->createPass();
pass->setSceneBlending(Ogre::SBT_TRANSPARENT_ALPHA);
pass->setLightingEnabled(false);
@ -169,7 +175,7 @@ public:
}
// Finally, set up the scene node.
mNode = parent->createChildSceneNode(Vector3(info.x, info.y, 0.0));
mNode = parent->createChildSceneNode();
mNode->attachObject(this);
}
@ -179,8 +185,8 @@ public:
mNode->detachAllObjects();
mNode->getCreator()->destroySceneNode(mNode);
// TODO: This used to crash. See what happens now.
delete mVertices;
// TODO: This still crashes on level1 meshes. Find out why!
if(mLevel!=1)delete mVertices;
delete mIndices;
}
@ -237,9 +243,11 @@ public:
mLightListDirty = true;
queue->addRenderable(this, mRenderQueueID);
}
const Ogre::AxisAlignedBox& getBoundingBox( void ) const {
const Ogre::AxisAlignedBox& getBoundingBox( void ) const
{
return mBounds;
};
}
Ogre::Real getBoundingRadius(void) const {
return mBoundingRadius;
}
@ -251,6 +259,8 @@ public:
private:
int mLevel;
Ogre::SceneNode* mNode;
Ogre::MaterialPtr mMaterial;

@ -36,11 +36,13 @@ extern "C"
char *d_terr_getTexName(int32_t);
void d_terr_fillVertexBuffer(const MeshInfo*,float*);
void d_terr_fillIndexBuffer(const MeshInfo*,uint16_t*);
void d_terr_fillVertexBuffer(const MeshInfo*,float*,uint64_t);
void d_terr_fillIndexBuffer(const MeshInfo*,uint16_t*,uint64_t);
AlphaInfo *d_terr_getAlphaInfo(const MeshInfo*,int32_t);
void d_terr_fillAlphaBuffer(const AlphaInfo*,uint8_t*);
void d_terr_fillAlphaBuffer(const AlphaInfo*,uint8_t*,uint64_t);
int32_t d_terr_getAlphaSize();
}
// Info about a submesh. This is a clone of the struct defined in
@ -54,10 +56,6 @@ struct MeshInfo
// 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;
@ -72,14 +70,14 @@ struct MeshInfo
// Texture name. Index to the string table.
int32_t texName;
inline void fillVertexBuffer(float *buffer) const
inline void fillVertexBuffer(float *buffer, uint64_t size) const
{
d_terr_fillVertexBuffer(this, buffer);
d_terr_fillVertexBuffer(this, buffer, size);
}
inline void fillIndexBuffer(uint16_t *buffer) const
inline void fillIndexBuffer(uint16_t *buffer, uint64_t size) const
{
d_terr_fillIndexBuffer(this, buffer);
d_terr_fillIndexBuffer(this, buffer, size);
}
inline char* getTexName() const
@ -114,9 +112,9 @@ struct AlphaInfo
return d_terr_getTexName(alphaName);
}
inline void fillAlphaBuffer(uint8_t *buffer) const
inline void fillAlphaBuffer(uint8_t *buffer, uint64_t size) const
{
return d_terr_fillAlphaBuffer(this, buffer);
return d_terr_fillAlphaBuffer(this, buffer, size);
}
};
@ -130,12 +128,67 @@ class TerrainFrameListener : public FrameListener
protected:
bool frameEnded(const FrameEvent& evt)
{
TRACE("Terrain frame");
d_terr_terrainUpdate();
g_baseLand->update();
return true;
}
};
// Renders a material into a texture
Ogre::TexturePtr getRenderedTexture(Ogre::MaterialPtr mp,
const std::string& name,
int texSize, Ogre::PixelFormat tt)
{
Ogre::CompositorPtr cp = Ogre::CompositorManager::getSingleton().
create("Rtt_Comp",
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
Ogre::CompositionTargetPass* ctp = cp->createTechnique()->getOutputTargetPass();
Ogre::CompositionPass* cpass = ctp->createPass();
cpass->setType(Ogre::CompositionPass::PT_RENDERQUAD);
cpass->setMaterial(mp);
// Create the destination texture
Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().
createManual(name + "_T",
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
Ogre::TEX_TYPE_2D,
texSize,
texSize,
0,
tt,
Ogre::TU_RENDERTARGET
);
Ogre::RenderTexture* renderTexture = texture->getBuffer()->getRenderTarget();
Ogre::Viewport* vp = renderTexture->addViewport(mCamera);
Ogre::CompositorManager::getSingleton().addCompositor(vp, "Rtt_Comp");
Ogre::CompositorManager::getSingleton().setCompositorEnabled(vp,"Rtt_Comp", true);
renderTexture->update();
// Call the OGRE renderer.
Ogre::Root::getSingleton().renderOneFrame();
Ogre::CompositorManager::getSingleton().removeCompositor(vp, "Rtt_Comp");
Ogre::CompositorManager::getSingleton().remove(cp->getHandle());
renderTexture->removeAllViewports();
return texture;
}
// These are used between some functions below. Kinda messy. Since
// these are GLOBAL instances, they are terminated at program
// exit. However, OGRE itself is terminated before that, so we have to
// make sure we have no 'active' shared pointers after OGRE is
// finished (otherwise we get a segfault at exit.)
std::list<Ogre::ResourcePtr> createdResources;
Ogre::HardwarePixelBuffer *pixelBuffer;
MaterialPtr mat;
// Functions called from D
extern "C"
{
@ -163,8 +216,11 @@ extern "C"
Ogre::AxisAlignedBox *terr_makeBounds(float minHeight, float maxHeight,
float width, SceneNode* node)
{
TRACE("terr_makeBounds");
AxisAlignedBox *mBounds = new AxisAlignedBox;
assert(maxHeight >= minHeight);
mBounds->setExtents(0,0,minHeight,
width,width,maxHeight);
@ -177,11 +233,13 @@ extern "C"
void terr_killBounds(AxisAlignedBox *bounds)
{
TRACE("terr_killBounds");
delete bounds;
}
float terr_getSqCamDist(AxisAlignedBox *mBounds)
{
TRACE("terr_getSqCamDist");
Ogre::Vector3 cpos = mCamera->getDerivedPosition();
Ogre::Vector3 diff(0, 0, 0);
diff.makeFloor(cpos - mBounds->getMinimum() );
@ -193,19 +251,22 @@ extern "C"
MeshInfo *info,
int level, float scale)
{
return new TerrainMesh(parent, *info, level, scale);
}
void terr_killMesh(TerrainMesh *mesh)
{ delete mesh; }
{
TRACE("terr_killMesh");
delete mesh;
}
// Set up the rendering system
void terr_setupRendering()
{
TRACE("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);
// are shared between the two.
assert(sizeof(MeshInfo) == 14*4);
assert(sizeof(AlphaInfo) == 6*4);
// Add the terrain directory as a resource location. TODO: Get the
@ -226,7 +287,126 @@ extern "C"
// terrain mesh that makes the terrain look infinite.
g_baseLand = new BaseLand();
g_alphaSize = d_terr_getAlphaSize();
// Add the frame listener
mRoot->addFrameListener(new TerrainFrameListener);
}
// The next four functions are called in the function genLevel2Map()
// only. This is very top-down-programming-ish and a bit messy, but
// that's what I get for mixing C++ and D like this.
void terr_makeLandMaterial(const char* name, float scale)
{
// Get a new material
mat = Ogre::MaterialManager::getSingleton().
create(name,
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
// Put the default texture in the bottom 'layer', so that we don't
// end up seeing through the landscape.
Ogre::Pass* np = mat->getTechnique(0)->getPass(0);
np->setLightingEnabled(false);
np->createTextureUnitState("_land_default.dds")
->setTextureScale(scale,scale);
}
uint8_t *terr_makeAlphaLayer(const char* name, int32_t width)
{
// Create alpha map for this texture.
Ogre::TexturePtr texPtr = Ogre::TextureManager::getSingleton().
createManual(name,
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
Ogre::TEX_TYPE_2D,
width, width,
1,0, // depth, mipmaps
Ogre::PF_A8, // One-channel alpha
Ogre::TU_STATIC_WRITE_ONLY);
createdResources.push_back(texPtr);
pixelBuffer = texPtr->getBuffer().get();
pixelBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD);
const Ogre::PixelBox& pixelBox = pixelBuffer->getCurrentLock();
return static_cast<Ogre::uint8*>(pixelBox.data);
}
void terr_closeAlpha(const char *alphaName,
const char *texName, float scale)
{
// Close the alpha pixel buffer opened in the previous function
pixelBuffer->unlock();
// Create a pass containing the alpha map
Pass *np = mat->getTechnique(0)->createPass();
np->setSceneBlending(Ogre::SBT_TRANSPARENT_ALPHA);
np->setLightingEnabled(false);
np->setDepthFunction(Ogre::CMPF_EQUAL);
Ogre::TextureUnitState* tus = np->createTextureUnitState(alphaName);
tus->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP);
// Set various blending options
tus->setAlphaOperation(Ogre::LBX_BLEND_TEXTURE_ALPHA,
Ogre::LBS_TEXTURE,
Ogre::LBS_TEXTURE);
tus->setColourOperationEx(Ogre::LBX_BLEND_DIFFUSE_ALPHA,
Ogre::LBS_TEXTURE,
Ogre::LBS_TEXTURE);
tus->setIsAlpha(true);
// Add the terrain texture to the pass and scale it.
tus = np->createTextureUnitState(texName);
tus->setColourOperationEx(Ogre::LBX_BLEND_DIFFUSE_ALPHA,
Ogre::LBS_TEXTURE,
Ogre::LBS_CURRENT);
tus->setTextureScale(scale, scale);
}
// Clean up after the above functions, render the material to
// texture and save the data in outdata and in the file outname.
void terr_cleanupAlpha(const char *outname,
void *outData, int32_t toSize)
{
TexturePtr tex1 = getRenderedTexture(mat,outname,
toSize,Ogre::PF_R8G8B8);
// Blit the texture into the given memory buffer
PixelBox pb = PixelBox(toSize, toSize, 1, PF_R8G8B8);
pb.data = outData;
tex1->getBuffer()->blitToMemory(pb);
// Clean up
TextureManager::getSingleton().remove(tex1->getHandle());
const std::list<Ogre::ResourcePtr>::const_iterator iend = createdResources.end();
for ( std::list<Ogre::ResourcePtr>::const_iterator itr = createdResources.begin();
itr != iend;
++itr)
(*itr)->getCreator()->remove((*itr)->getHandle());
createdResources.clear();
MaterialManager::getSingleton().remove(mat->getHandle());
mat.setNull();
}
void terr_resize(void* srcPtr, void* dstPtr, int32_t fromW, int32_t toW)
{
// Create pixelboxes
PixelBox src = PixelBox(fromW, fromW, 1, PF_R8G8B8);
PixelBox dst = PixelBox(toW, toW, 1, PF_R8G8B8);
src.data = srcPtr;
dst.data = dstPtr;
// Resize the image. The nearest neighbour filter makes sure
// there is no blurring.
Image::scale(src, dst, Ogre::Image::FILTER_NEAREST);
}
void terr_saveImage(void *data, int32_t width, const char* name)
{
Image img;
img.loadDynamicImage((uchar*)data, width, width, PF_R8G8B8);
img.save(name);
}
}

@ -5,6 +5,8 @@ import esm.loadcell;
import util.regions;
import esm.filereader;
import std.stdio;
const int LAND_NUM_VERTS = 65*65;
MWLand mwland;
@ -49,23 +51,27 @@ struct MWLand
// Texture data for one cell
struct LTEXData
{
// TODO: Store the source file here too, so we can get the list
// from the right file. The source file is the same as the one we
// load the landscape from in loadCell().
// Global list of land textures from the source ES file
LandTextureList.TextureList source;
// Texture indices for this cell
VTEX vtex;
// Get the texture x2,y2 from the sub map x1,x2
char[] getTexture(int x1, int y1, int x2, int y2)
{
// Get the texture index relative to the current esm/esp file
short texID = vtex[y1][x1][y2][x2];
short texID = vtex[y1][x1][y2][x2] - 1;
// Hack, will only work for Morrowind.esm. Fix this later.
auto tl = landTextures.files["Morrowind.esm"];
if(texID == -1)
return null;
// Return the 'new' texture name. This name has automatically
// been converted to .dds if the .tga file was not found.
return tl[texID].getNewName();
// Return the 'new' texture name. This name was automatically
// been converted to .dds at load time if the .tga file was not
// found.
assert(source !is null);
assert(texID >= 0 && texID < source.length);
return source[texID].getNewName();
}
// Get a texture from the 16x16 grid in one cell
@ -127,6 +133,9 @@ struct MWLand
// skip to the right part of the ESM file.
auto cont = cells.getExt(x,y).land.context;
// Get the land texture list from the file
currentLtex.source = landTextures.files[cont.filename];
// We should use an existing region later, or at least delete this
// once we're done with the gen process.
if(reg is null)
@ -135,7 +144,7 @@ struct MWLand
// Open the ESM at this cell
esFile.restoreContext(cont, reg);
// Store the data
// Store the cell-specific data
esFile.readHNExact(currentLand.normals.ptr,
currentLand.normals.length, "VNML");
esFile.readHNExact(&currentLand.vhgt, VHGT.sizeof, "VHGT");

File diff suppressed because it is too large Load Diff

@ -2,6 +2,8 @@ module terrain.quad;
import terrain.archive;
import terrain.bindings;
import std.stdio;
import monster.vm.dbg;
const int CELL_WIDTH = 8192;
const float SPLIT_FACTOR = 0.5;
@ -11,6 +13,8 @@ class Quad
{
this(int cellX=0, int cellY=0, Quad parent = null)
{
scope auto _trc = new MTrace("Quad.this");
mCellX = cellX;
mCellY = cellY;
@ -19,13 +23,6 @@ class Quad
{
mLevel = parent.mLevel-1;
if(mLevel == 1)
{
// Create the terrain and leave it there.
buildTerrain();
isStatic = true;
}
// Coordinates relative to our parent
int relX = cellX - parent.mCellX;
int relY = cellY - parent.mCellY;
@ -43,12 +40,36 @@ class Quad
// Get the archive data for this quad.
mInfo = g_archive.getQuad(mCellX,mCellY,mLevel);
// Set up the bounding box. Use MW coordinates all the
// way.
mBounds = terr_makeBounds(mInfo.minHeight,
mInfo.maxHeight,
mInfo.worldWidth,
mNode);
float radius = mInfo.boundingRadius;
mSplitDistance = radius * SPLIT_FACTOR;
mUnsplitDistance = radius * UNSPLIT_FACTOR;
// Square the distances
mSplitDistance *= mSplitDistance;
mUnsplitDistance *= mUnsplitDistance;
if(mLevel == 1)
{
// Create the terrain and leave it there.
buildTerrain();
isStatic = true;
}
}
else
{
// No parent, this is the top-most quad. Get all the info from
// the archive.
mInfo = g_archive.rootQuad;
assert(mInfo);
mLevel = mInfo.level;
cellX = mCellX = mInfo.cellX;
@ -68,21 +89,6 @@ class Quad
assert(mLevel >= 1);
assert(mNode !is null);
// Set up the bounding box. Use MW coordinates all the way
mBounds = terr_makeBounds(mInfo.minHeight,
mInfo.maxHeight,
mInfo.worldWidth,
mNode);
float radius = mInfo.boundingRadius;
mSplitDistance = radius * SPLIT_FACTOR;
mUnsplitDistance = radius * UNSPLIT_FACTOR;
// Square the distances
mSplitDistance *= mSplitDistance;
mUnsplitDistance *= mUnsplitDistance;
// Update the terrain. This will create the mesh or children if
// necessary.
update();
@ -90,6 +96,8 @@ class Quad
~this()
{
scope auto _trc = new MTrace("Quad.~this");
// TODO: We might rewrite the code so that the quads are never
// actually destroyed, just 'inactivated' by hiding their scene
// node. We only call update on our children if we don't have a
@ -101,12 +109,14 @@ class Quad
delete mChildren[i];
terr_destroyNode(mNode);
terr_killBounds(mBounds);
if(mBounds !is null)
terr_killBounds(mBounds);
}
// Remove the landscape for this quad, and create children.
void split()
{
scope auto _trc = new MTrace("split");
// Never split a static quad or a quad that already has children.
assert(!isStatic);
assert(!hasChildren);
@ -136,6 +146,7 @@ class Quad
// Removes children and rebuilds terrain
void unsplit()
{
scope auto _trc = new MTrace("unsplit");
// Never unsplit the root quad
assert(mLevel < g_archive.rootQuad.level);
// Never unsplit a static or quad that isn't split.
@ -158,6 +169,8 @@ class Quad
// does it.
void update()
{
scope auto _trc = new MTrace("Quad.update()");
// Static quads don't change
if(!isStatic)
{
@ -165,6 +178,7 @@ class Quad
// Get (squared) camera distance. TODO: shouldn't this just
// be a simple vector difference from the mesh center?
assert(mBounds !is null);
float camDist = terr_getSqCamDist(mBounds);
// No children?
@ -209,10 +223,13 @@ class Quad
// Build the terrain for this quad
void buildTerrain()
{
scope auto _trc = new MTrace("buildTerrain");
assert(!hasMesh);
assert(!isStatic);
// Map the terrain data into memory.
assert(mInfo);
g_archive.mapQuad(mInfo);
// Create one mesh for each segment in the quad. TerrainMesh takes
@ -221,7 +238,7 @@ class Quad
foreach(i, ref m; meshList)
{
MeshInfo *mi = g_archive.getMeshInfo(i);
m = terr_makeMesh(mNode, mi, mInfo.level, mInfo.texScale);
m = terr_makeMesh(mNode, mi, mInfo.level, TEX_SCALE);
}
hasMesh = true;
@ -229,6 +246,8 @@ class Quad
void destroyTerrain()
{
scope auto _trc = new MTrace("destroyTerrain");
assert(hasMesh);
foreach(m; meshList)

@ -31,6 +31,9 @@ import std.file, std.stdio;
char[] cacheDir = "cache/terrain/";
// Enable this to render one single terrain mesh
//debug=singleMesh;
void initTerrain(bool doGen)
{
char[] fname = cacheDir ~ "landscape.cache";
@ -50,12 +53,26 @@ void initTerrain(bool doGen)
terr_setupRendering();
// Create the root quad
rootQuad = new Quad;
debug(singleMesh)
{
// Used for debugging single terrain meshes
auto node = terr_createChildNode(20000,-60000,null);
auto info = g_archive.getQuad(0,0,1);
g_archive.mapQuad(info);
auto mi = g_archive.getMeshInfo(0);
auto m = terr_makeMesh(node, mi, info.level, TEX_SCALE);
}
else
{
// Create the root quad
rootQuad = new Quad;
}
}
extern(C) void d_terr_terrainUpdate()
{
debug(singleMesh) return;
// Update the root quad each frame.
assert(rootQuad !is null);
rootQuad.update();

Loading…
Cancel
Save