Started early work on terrain code (no rendering yet.)

actorid
Nicolay Korslund 14 years ago
parent 3e170002e2
commit 255b0d03a9

@ -0,0 +1,48 @@
#include "esm_land_factory.hpp"
// The first one already includes the others implicitly, but it
// doesn't hurt to be explicit.
#include "../esm_store/store.hpp"
#include "../esm/esm_reader.hpp"
#include "../esm/loadland.hpp"
using namespace Terrain;
static class ESMLandStream : public Mangle::Stream
{
public:
ESMLandStream(ESM::Land *l, ESM::ESMReader &r)
{
}
};
bool ESMLandFactory::has(int x, int y)
{
return store.landscapes.has(x,y);
}
LandDataPtr get(int x, int y, LandInfo &info)
{
assert(has(x,y));
// Set up the info
info.grid = LGT_Quadratic;
info.data = LDT_Float;
const float SIZE = 8192; // CHECK
info.xsize = SIZE;
info.ysize = SIZE;
info.numx = 65;
info.numy = 65;
info.xoffset = SIZE*x;
info.yoffset = SIZE*y;
// Get the Land struct from store
ESM::Land* land = store.landscapes.find(x,y);
assert(land->hasData);
// Create a stream for the data and return it.
LandDataPtr ptr(new ESMLandStream(land, reader));
return ptr;
}

@ -0,0 +1,41 @@
#ifndef TERRAIN_ESM_LAND_FACTORY_H
#define TERRAIN_ESM_LAND_FACTORY_H
#include "land_factory.hpp"
namespace ESMS
{
struct ESMStore;
}
namespace ESM
{
class ESMReader;
}
namespace Terrain
{
/*
Land factory that loads data from ESM files.
*/
class ESMLandFactory
{
ESMS::ESMStore &store;
ESM::ESMReader &reader;
public:
// Initialize the land factory. Note that refrences to the given
// store and reader are stored in the class, so the given objects
// must be valid for a long as you plan to use this factory.
ESMLandFactory(ESMS::ESMStore &st, ESM::ESMReader &rd)
: store(st), reader(rd) {}
// True if this factory has any data for the given grid cell.
bool has(int x, int y);
// Return stream to data for this cell. Additional data about the
// landscape is returned through the LandInfo struct.
LandDataPtr get(int x, int y, LandInfo &info);
};
}
#endif

@ -0,0 +1,38 @@
#ifndef TERRAIN_HEIGHTMAP_H
#define TERRAIN_HEIGHTMAP_H
/*
Generic interface for a structure holding heightmap data.
A HeightMap returns information about landscape data in the form of
a regular grid of float heights.
*/
namespace Terrain
{
struct HeightMap
{
// Get height from grid position, counted from 0 to getNumX/Y().
virtual float getHeight(int x, int y) = 0;
// Get heigth from vertex index, assumed to be y*getNumX() + x.
virtual float getHeight(int index) = 0;
virtual float getMinX() = 0;
virtual float getMaxX() = 0;
virtual float getMinY() = 0;
virtual float getMaxY() = 0;
virtual int getNumX() = 0;
virtual int getNumY() = 0;
// True if the given coordinate is within the grid
bool isWithin(float x, float y)
{
return
x >= getMinX() && x < getMaxX() &&
y >= getMinY() && y < getMaxY();
}
};
}
#endif

@ -0,0 +1,72 @@
#ifndef TERRAIN_HEIGHTMAPBUF_H
#define TERRAIN_HEIGHTMAPBUF_H
/*
A HeightMap implementation that stores heigths in a buffer.
*/
#include "heightmap.hpp"
#include "land_factory.hpp"
#include <vector>
#include <assert.h>
namespace Terrain
{
class HeightMapBuffer : public HeightMap
{
std::vector<float> buf;
float beginX, sizeX, endX;
float beginY, sizeY, endY;
int numX, numY;
public:
void load(LandDataPtr data, const LandInfo &info)
{
// We don't support other kinds of grid data yet.
assert(info.grid == LGT_Quadratic);
assert(info.data == LDT_Float);
// Set up internal data
beginX = info.xoffset;
sizeX = info.xsize;
endX = beginX+sizeX;
numX = info.numx;
beginY = info.yoffset;
sizeY = info.ysize;
endY = beginY+sizeY;
numY = info.numy;
// Prepare the buffer and load it
buf.resize(numX*numY);
data.read(&buf[0], buf.size()*sizeof(float));
}
// Functions inherited from HeightMap:
float getHeight(int x, int y)
{
assert(x>=0 && x<numX);
assert(y>=0 && y<numY);
return getHeight(x + y*numX);
}
float getHeight(int index)
{
assert(index >= 0 && index < buf.size());
return buf[index];
}
float getMinX() { return beginX; }
float getMaxX() { return endX; }
float getMinY() { return beginY; }
float getMaxY() { return endY; }
int getNumX() { return numX; }
int getNumY() { return numY; }
};
}
#endif

@ -0,0 +1,55 @@
#ifndef TERRAIN_LAND_FACTORY_H
#define TERRAIN_LAND_FACTORY_H
#include <mangle/stream/stream.hpp>
namespace Terrain
{
enum LandInfoGridType
{
LGT_Quadratic
};
enum LandInfoDataType
{
LDT_Float
};
struct LandInfo
{
// Type information
LandInfoGridType grid;
LandInfoDataType data;
// Landscape size and number of vertices. Note that xsize and
// ysize may be negative, signaling a flipped landscape in that
// direction.
float xsize, ysize;
int numx, numy;
// World offset along the same x/y axes. Whether these are set or
// used depends on the client implementation.
float xoffset, yoffset;
};
/* We use normal streams for data. This allows us to just pass (for
example) a file stream as height map input later, with no extra
fuzz.
*/
typedef Mangle::Stream::StreamPtr LandDataPtr;
/*
Factory class that provides streams to land data cells. Each
"cell" has a unique integer coordinate in the plane.
*/
struct LandFactory
{
// True if this factory has any data for the given grid cell.
virtual bool has(int x, int y) = 0;
// Return stream to data for this cell. Additional data about the
// landscape is returned through the LandInfo struct.
virtual LandDataPtr get(int x, int y, LandInfo &info) = 0;
};
}
#endif

@ -0,0 +1,14 @@
GCC=g++
all: triangle_test esm_test
LIB_INC=-I../../../libs/
triangle_test: triangle_test.cpp
$(GCC) $^ -o $@
esm_test: esm_test.cpp
$(GCC) $^ -o $@ $(LIB_INC)
clean:
rm *_test

@ -0,0 +1,10 @@
#include <iostream>
using namespace std;
#include "../esm_land_factory.hpp"
int main()
{
cout << "under development\n";
return 0;
}

@ -0,0 +1,55 @@
Cell types:
\ / \ /
/ \ / \
\ / \ /
/ \ / \
Full index list:
0
6
5
0
1
6
1
2
6
6
2
7
2
8
7
2
3
8
3
4
8
8
4
9
5
6
10
10
6
11
6
12
11
6
7
12
7
8
12
12
8
13
8
14
13
8
9
14

@ -0,0 +1,18 @@
#!/bin/bash
make || exit
mkdir -p output
PROGS=*_test
for a in $PROGS; do
if [ -f "output/$a.out" ]; then
echo "Running $a:"
./$a | diff output/$a.out -
else
echo "Creating $a.out"
./$a > "output/$a.out"
git add "output/$a.out"
fi
done

@ -0,0 +1,93 @@
#include <iostream>
using namespace std;
#include "../triangulator.hpp"
const int X = 4;
const int Y = 4;
typedef Terrain::Triangulator<short,X,Y> Triangles4x4;
int main()
{
Triangles4x4 t;
cout << "Cell types:\n";
for(int y=0;y<Y;y++)
{
for(int x=0;x<X;x++)
{
if(t.cellType(x,y)) cout << "/ ";
else cout << "\\ ";
}
cout << endl;
}
cout << endl;
cout << "Full index list:\n";
for(int i=0; i<X*Y*3; i++)
cout << t.getData()[i] << endl;
return 0;
}
/* Code we might add later:
// Get the vertex indices belonging to a given triangle
void getTriangle(int trinum, Index &p1, Index &p2, Index &p3)
{
assert(trinum >= 0 && trinum < TriNum);
trinum *= 3;
p1 = array[trinum++];
p2 = array[trinum++];
p3 = array[trinum];
}
/*
Get height interpolation weights for a given grid square. The
input is the grid square number (x,y) and the relative position
within that square (xrel,yrel = [0.0..1.0].) The weights are
returned as three vertex index + weight factor pairs.
A more user-friendly version for HeightMap structs is given
below.
* /
void getWeights(int x, int y, float xrel, float yrel,
Index &p1, float w1,
Index &p2, float w2,
Index &p3, float w3)
{
// Find cell index
int index = y*SizeX + x;
// First triangle in cell
index *= 2;
// The rest depends on how the cell is triangulated
if(cellType(x,y))
{
}
else
{
// Cell is divided as \ from 0,0 to 1,1
if(xrel < yrel)
{
// Bottom left triangle.
// Order is (0,0),(1,1),(0,1).
getTriangle(index, p1,p2,p3);
}
else
{
// Top right triangle
// Order is (0,0),(1,0),(1,1).
getTriangle(index+1, p1,p2,p3);
}
}
}
*/

@ -0,0 +1,104 @@
#ifndef TERRAIN_TRIANGULATOR_H
#define TERRAIN_TRIANGULATOR_H
/*
The triangulator is a simple math helper class, used for dividing a
regular square grid into alternating set of triangles. It divides a
grid like this:
+----+----+
| | |
| | |
+----+----+
| | |
| | |
+----+----+
into this:
+----+----+
| \ 2|3 / |
|1 \ | / 4|
+----+----+
|5 / | \ 8|
| / 6|7 \ |
+----+----+
Since the triangulation information is typically the same for all
terrains of the same size, once instance can usually be shared.
*/
#include <assert.h>
namespace Terrain
{
// Index number type, number of grid cells (not vertices) in X and Y
// directions.
template <typename Index, int SizeX, int SizeY>
class Triangulator
{
// Number of triangles
static const int TriNum = SizeX * SizeY * 2;
// 3 indices per triangle
Index array[TriNum * 3];
public:
// Get raw triangle data pointer. Typically used for creating
// meshes.
const Index *getData() { return array; }
// Return whether a given cell is divided as / (true) or \
// (false).
static bool cellType(int x, int y)
{
assert(x >= 0 && x < SizeX);
assert(y >= 0 && y < SizeY);
bool even = (x & 1) == 1;
if((y & 1) == 1) even = !even;
return even;
}
// Constructor sets up the index buffer
Triangulator()
{
int index = 0;
for ( int y = 0; y < SizeX; y++ )
for ( int x = 0; x < SizeY; x++ )
{
// Get vertex indices
Index line1 = y*(SizeX+1) + x;
Index line2 = line1 + SizeX+1;
if(cellType(x,y))
{
// Top left
array[index++] = line1;
array[index++] = line1 + 1;
array[index++] = line2;
// Bottom right
array[index++] = line2;
array[index++] = line1 + 1;
array[index++] = line2 + 1;
}
else
{
// Bottom left
array[index++] = line1;
array[index++] = line2 + 1;
array[index++] = line2;
// Top right
array[index++] = line1;
array[index++] = line1 + 1;
array[index++] = line2 + 1;
}
}
assert(index == TriNum*3);
}
};
} // Namespace
#endif

@ -352,46 +352,7 @@ void genIndexData()
cache.addVertexBuffer(lev,vertList);
}
// Pregenerate triangle indices
int size = 64*64*6;
auto indList = new ushort[size];
int index = 0;
bool flag = false;
for ( int y = 0; y < 64; y++ )
{
for ( int x = 0; x < 64; x++ )
{
int line1 = y*65 + x;
int line2 = (y+1)*65 + x;
if ( flag )
{
indList[index++] = line1;
indList[index++] = line1 + 1;
indList[index++] = line2;
indList[index++] = line2;
indList[index++] = line1 + 1;
indList[index++] = line2 + 1;
}
else
{
indList[index++] = line1;
indList[index++] = line2 + 1;
indList[index++] = line2;
indList[index++] = line1;
indList[index++] = line1 + 1;
indList[index++] = line2 + 1;
}
flag = !flag; //flip tris for next time
}
flag = !flag; //flip tries for next row
}
assert(index == indList.length);
cache.setIndexBuffer(indList);
// index stuff already ported
}
void genLevel(int level, int X, int Y, ref GenLevelResult result,

Loading…
Cancel
Save