diff --git a/components/terrain/.gitignore b/components/terrain/.gitignore new file mode 100644 index 000000000..3367afdbb --- /dev/null +++ b/components/terrain/.gitignore @@ -0,0 +1 @@ +old diff --git a/components/terrain/esm_land_factory.cpp b/components/terrain/esm_land_factory.cpp new file mode 100644 index 000000000..315188234 --- /dev/null +++ b/components/terrain/esm_land_factory.cpp @@ -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; +} diff --git a/components/terrain/esm_land_factory.hpp b/components/terrain/esm_land_factory.hpp new file mode 100644 index 000000000..4fd95caa9 --- /dev/null +++ b/components/terrain/esm_land_factory.hpp @@ -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 diff --git a/components/terrain/heightmap.hpp b/components/terrain/heightmap.hpp new file mode 100644 index 000000000..e395b541e --- /dev/null +++ b/components/terrain/heightmap.hpp @@ -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 diff --git a/components/terrain/heightmapbuf.hpp b/components/terrain/heightmapbuf.hpp new file mode 100644 index 000000000..82154ce88 --- /dev/null +++ b/components/terrain/heightmapbuf.hpp @@ -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 +#include + +namespace Terrain +{ + class HeightMapBuffer : public HeightMap + { + std::vector 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=0 && y= 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 diff --git a/components/terrain/land_factory.hpp b/components/terrain/land_factory.hpp new file mode 100644 index 000000000..f41946b49 --- /dev/null +++ b/components/terrain/land_factory.hpp @@ -0,0 +1,55 @@ +#ifndef TERRAIN_LAND_FACTORY_H +#define TERRAIN_LAND_FACTORY_H + +#include + +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 diff --git a/components/terrain/tests/.gitignore b/components/terrain/tests/.gitignore new file mode 100644 index 000000000..814490404 --- /dev/null +++ b/components/terrain/tests/.gitignore @@ -0,0 +1 @@ +*_test diff --git a/components/terrain/tests/Makefile b/components/terrain/tests/Makefile new file mode 100644 index 000000000..c886f392f --- /dev/null +++ b/components/terrain/tests/Makefile @@ -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 diff --git a/components/terrain/tests/esm_test.cpp b/components/terrain/tests/esm_test.cpp new file mode 100644 index 000000000..509aa8aa9 --- /dev/null +++ b/components/terrain/tests/esm_test.cpp @@ -0,0 +1,10 @@ +#include +using namespace std; + +#include "../esm_land_factory.hpp" + +int main() +{ + cout << "under development\n"; + return 0; +} diff --git a/components/terrain/tests/output/esm_test.out b/components/terrain/tests/output/esm_test.out new file mode 100644 index 000000000..c6fec4b4d --- /dev/null +++ b/components/terrain/tests/output/esm_test.out @@ -0,0 +1 @@ +under development diff --git a/components/terrain/tests/output/triangle_test.out b/components/terrain/tests/output/triangle_test.out new file mode 100644 index 000000000..001215043 --- /dev/null +++ b/components/terrain/tests/output/triangle_test.out @@ -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 diff --git a/components/terrain/tests/test.sh b/components/terrain/tests/test.sh new file mode 100755 index 000000000..2d07708ad --- /dev/null +++ b/components/terrain/tests/test.sh @@ -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 diff --git a/components/terrain/tests/triangle_test.cpp b/components/terrain/tests/triangle_test.cpp new file mode 100644 index 000000000..464bc8709 --- /dev/null +++ b/components/terrain/tests/triangle_test.cpp @@ -0,0 +1,93 @@ +#include +using namespace std; + +#include "../triangulator.hpp" + +const int X = 4; +const int Y = 4; + +typedef Terrain::Triangulator Triangles4x4; + +int main() +{ + Triangles4x4 t; + + cout << "Cell types:\n"; + for(int y=0;y= 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); + } + } + } + + */ diff --git a/components/terrain/triangulator.hpp b/components/terrain/triangulator.hpp new file mode 100644 index 000000000..cedf0c6a2 --- /dev/null +++ b/components/terrain/triangulator.hpp @@ -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 + +namespace Terrain +{ + // Index number type, number of grid cells (not vertices) in X and Y + // directions. + template + 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 diff --git a/old_d_version/terrain/generator.d b/old_d_version/terrain/generator.d index fac7b1fd5..f1645c9ab 100644 --- a/old_d_version/terrain/generator.d +++ b/old_d_version/terrain/generator.d @@ -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,