forked from mirror/openmw-tes3mp
- 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
This commit is contained in:
parent
f739bf90f1
commit
367b9754b2
12 changed files with 796 additions and 619 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
18
openmw.d
18
openmw.d
|
@ -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);
|
||||
// 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);
|
||||
// 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);
|
||||
// Copy alpha data from file
|
||||
alpha.fillAlphaBuffer(pDest,g_alphaSize*g_alphaSize);
|
||||
|
||||
// Close the buffer
|
||||
pixelBuffer->unlock();
|
||||
}
|
||||
|
||||
// Finish everything up with a lot of Ogre-code
|
||||
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(¤tLand.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…
Reference in a new issue