Difference between revisions of "Procedurally generated geometry tutorial"
m |
|||
(7 intermediate revisions by 3 users not shown) | |||
Line 1: | Line 1: | ||
+ | __NOTOC__ __NOEDITSECTION__{{ContentBlock|color=white|content={{!!}} | ||
'''Work in progress - Due to the current changes in the Horde API this tutorial will be delayed ''' | '''Work in progress - Due to the current changes in the Horde API this tutorial will be delayed ''' | ||
Line 14: | Line 15: | ||
When we have calculated the geometry itself we need to create a char-stream to make Horde believe the geometry has been read from a file and pass it to Horde as a geometry Resource. | When we have calculated the geometry itself we need to create a char-stream to make Horde believe the geometry has been read from a file and pass it to Horde as a geometry Resource. | ||
The final step is to create a Horde3D Scene Node from our procedurally generated mesh and render it into our scene. | The final step is to create a Horde3D Scene Node from our procedurally generated mesh and render it into our scene. | ||
− | |||
− | |||
=StreamGenerator= | =StreamGenerator= | ||
Line 362: | Line 361: | ||
=Simple Square= | =Simple Square= | ||
− | A simple | + | A simple hard-coded square. This is pretty straight forward and demonstrates how to derive from the CustomGeometry class and how the triangle indices work. |
{{CppSourceCode| | {{CppSourceCode| | ||
Line 404: | Line 403: | ||
_size = size_; | _size = size_; | ||
_name = name_; | _name = name_; | ||
− | _numVertices = | + | _numVertices = 4; |
− | _numTriangles = | + | _numTriangles = 2; |
− | _numTriangleIndices = | + | _numTriangleIndices = _numTriangles * 3; |
//generate(); | //generate(); | ||
} | } | ||
Line 438: | Line 437: | ||
_positions[11] = _origin.z * _size + _size/2; | _positions[11] = _origin.z * _size + _size/2; | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
} | } | ||
void DynamicSquare::generateNormals() | void DynamicSquare::generateNormals() | ||
{ | { | ||
− | _normals = new float[_numVertices * 3 | + | _normals = new float[_numVertices * 3]; |
for (int i=0; i< 12; i+=3) { | for (int i=0; i< 12; i+=3) { | ||
_normals[i] = 0; | _normals[i] = 0; | ||
_normals[i+1] = 1; | _normals[i+1] = 1; | ||
− | |||
− | |||
− | |||
− | |||
− | |||
_normals[i+2] = 0; | _normals[i+2] = 0; | ||
} | } | ||
Line 475: | Line 451: | ||
void DynamicSquare::generateTexCoords() | void DynamicSquare::generateTexCoords() | ||
{ | { | ||
− | _texCoords = new float[_numVertices | + | _texCoords = new float[_numVertices * 2]; |
_texCoords[0] = 0.0f; | _texCoords[0] = 0.0f; | ||
_texCoords[1] = 0.0f; | _texCoords[1] = 0.0f; | ||
Line 484: | Line 460: | ||
_texCoords[6] = 1.0f; | _texCoords[6] = 1.0f; | ||
_texCoords[7] = 1.0f; | _texCoords[7] = 1.0f; | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
} | } | ||
Line 503: | Line 471: | ||
_tIndices[4] = 2; | _tIndices[4] = 2; | ||
_tIndices[5] = 3; | _tIndices[5] = 3; | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
} | } | ||
</source> | </source> | ||
Line 515: | Line 477: | ||
=Simple Grid= | =Simple Grid= | ||
− | All the work above was made to create my own interactive terrain generator(screenshot on top of this page) for a A* pathfinding demonstrator. The Grid-class actually generates a | + | All the work above was made to create my own interactive terrain generator(screenshot on top of this page) for a A* pathfinding demonstrator. The Grid-class actually generates a 2-dimensional array of Vec3f(s) and allows to extrude each point and re-generate the mesh in realtime. The internal representation is writeable and in case of a change of any point the overridden update() function will be called to update the Horde3D geometry resource. |
− | |||
− | |||
{{CppSourceCode| | {{CppSourceCode| | ||
description= DynamicGrid.h| | description= DynamicGrid.h| | ||
Line 524: | Line 484: | ||
<source lang="cpp" line="1"> | <source lang="cpp" line="1"> | ||
/** | /** | ||
− | + | * Class representing a grid | |
− | * Class representing a | ||
− | |||
**/ | **/ | ||
− | |||
− | |||
− | |||
#pragma once | #pragma once | ||
− | |||
− | |||
− | |||
#include "CustomGeometry.h" | #include "CustomGeometry.h" | ||
− | |||
− | |||
class DynamicGrid : public CustomGeometry | class DynamicGrid : public CustomGeometry | ||
− | |||
{ | { | ||
− | |||
protected: | protected: | ||
− | |||
virtual void generatePositions(); | virtual void generatePositions(); | ||
− | |||
virtual void generateNormals(); | virtual void generateNormals(); | ||
− | |||
virtual void generateTexCoords(); | virtual void generateTexCoords(); | ||
− | |||
virtual void generateTriangleIndices(); | virtual void generateTriangleIndices(); | ||
− | + | // generate the internal representation of the grid (writeable to allow updates of single points!) | |
void generateGrid(); | void generateGrid(); | ||
− | |||
int _gridSize; | int _gridSize; | ||
− | |||
Vec3f** _grid; | Vec3f** _grid; | ||
− | |||
std::string _fileName; | std::string _fileName; | ||
− | |||
− | |||
public: | public: | ||
− | |||
DynamicGrid(); | DynamicGrid(); | ||
− | |||
virtual void generate(); | virtual void generate(); | ||
− | |||
DynamicGrid(const float size_, const std::string name_, std::string fileName_); | DynamicGrid(const float size_, const std::string name_, std::string fileName_); | ||
− | |||
Vec3f getPointAt(int x_, int y_) { return _grid[y_][x_]; } | Vec3f getPointAt(int x_, int y_) { return _grid[y_][x_]; } | ||
− | + | // update a point of our grid (args: index x, index y, new position) | |
void setPointAt(int x_, int y_, Vec3f newPos_) { _grid[y_][x_] = newPos_; } | void setPointAt(int x_, int y_, Vec3f newPos_) { _grid[y_][x_] = newPos_; } | ||
− | |||
int getGridSize() { return _gridSize; } | int getGridSize() { return _gridSize; } | ||
− | |||
// overwrite update() from parent class | // overwrite update() from parent class | ||
− | |||
void update(); | void update(); | ||
− | + | // read and write grids from/to files | |
void saveGridToFile(std::string fileName_); | void saveGridToFile(std::string fileName_); | ||
− | |||
bool readGridFromFile(std::string fileName_); | bool readGridFromFile(std::string fileName_); | ||
Line 594: | Line 525: | ||
<source lang="cpp" line="1"> | <source lang="cpp" line="1"> | ||
#include "DynamicGrid.h" | #include "DynamicGrid.h" | ||
− | |||
#include "rng.h" | #include "rng.h" | ||
− | |||
#include <iostream> | #include <iostream> | ||
− | |||
#include <string> | #include <string> | ||
− | |||
#include <sstream> | #include <sstream> | ||
− | |||
#include <fstream> | #include <fstream> | ||
− | |||
#include "util.h" | #include "util.h" | ||
− | |||
− | |||
#define GRIDSIZE 50 | #define GRIDSIZE 50 | ||
− | |||
− | |||
DynamicGrid::DynamicGrid() : CustomGeometry() | DynamicGrid::DynamicGrid() : CustomGeometry() | ||
− | |||
{ | { | ||
− | |||
− | |||
− | |||
} | } | ||
− | |||
− | |||
DynamicGrid::DynamicGrid(const float size_, const std::string name_, std::string fileName_) : _fileName(fileName_) | DynamicGrid::DynamicGrid(const float size_, const std::string name_, std::string fileName_) : _fileName(fileName_) | ||
− | |||
{ | { | ||
− | |||
_name = name_; | _name = name_; | ||
− | |||
_positions = 0; | _positions = 0; | ||
− | |||
_normals = 0; | _normals = 0; | ||
− | |||
_texCoords = 0; | _texCoords = 0; | ||
− | |||
_tIndices = 0; | _tIndices = 0; | ||
− | |||
readGridFromFile(_fileName); | readGridFromFile(_fileName); | ||
− | |||
_numVertices = (_gridSize*_gridSize); | _numVertices = (_gridSize*_gridSize); | ||
− | |||
_numTriangles = ((_gridSize - 1) * (_gridSize - 1)) * 2; | _numTriangles = ((_gridSize - 1) * (_gridSize - 1)) * 2; | ||
− | |||
_numTriangleIndices = _numTriangles * 3; | _numTriangleIndices = _numTriangles * 3; | ||
− | |||
_origin = Vec3f(-_size/2*_gridSize, 0, _size/2*_gridSize); | _origin = Vec3f(-_size/2*_gridSize, 0, _size/2*_gridSize); | ||
− | |||
/* | /* | ||
− | |||
std::cout<<"numVerts: "<<_numVertices<<std::endl; | std::cout<<"numVerts: "<<_numVertices<<std::endl; | ||
− | |||
std::cout<<"numTris: "<<_numTriangles<<std::endl; | std::cout<<"numTris: "<<_numTriangles<<std::endl; | ||
− | |||
std::cout<<"numTInds: "<<_numTriangleIndices<<std::endl; | std::cout<<"numTInds: "<<_numTriangleIndices<<std::endl; | ||
− | |||
std::cout<<"size: "<<_size<<std::endl; | std::cout<<"size: "<<_size<<std::endl; | ||
− | |||
*/ | */ | ||
− | |||
//std::cout<<_origin.x<<"/"<<_origin.y<<"/"<<_origin.z<<std::endl; | //std::cout<<_origin.x<<"/"<<_origin.y<<"/"<<_origin.z<<std::endl; | ||
− | |||
− | |||
− | |||
//generate(); | //generate(); | ||
− | |||
} | } | ||
− | |||
− | |||
void DynamicGrid::generate() | void DynamicGrid::generate() | ||
− | |||
{ | { | ||
− | |||
if (!_grid) | if (!_grid) | ||
− | |||
generateGrid(); | generateGrid(); | ||
− | |||
generatePositions(); | generatePositions(); | ||
− | |||
generateNormals(); | generateNormals(); | ||
− | |||
generateTexCoords(); | generateTexCoords(); | ||
− | |||
generateTriangleIndices(); | generateTriangleIndices(); | ||
− | |||
} | } | ||
− | |||
− | |||
// the following two functions could be combined into one single loop, but I left it this way for clarity... | // the following two functions could be combined into one single loop, but I left it this way for clarity... | ||
+ | /** | ||
+ | * This function simply generates a 2-dimensional array of Vec3f(s) with slightly random height values | ||
+ | **/ | ||
void DynamicGrid::generateGrid() | void DynamicGrid::generateGrid() | ||
− | |||
{ | { | ||
− | |||
RNG rng; | RNG rng; | ||
− | |||
_grid = new Vec3f*[_gridSize]; | _grid = new Vec3f*[_gridSize]; | ||
− | |||
for (int y=0; y<_gridSize; ++y) { | for (int y=0; y<_gridSize; ++y) { | ||
− | |||
_grid[y] = new Vec3f[_gridSize]; | _grid[y] = new Vec3f[_gridSize]; | ||
− | |||
for (int x=0; x<_gridSize; ++x) { | for (int x=0; x<_gridSize; ++x) { | ||
− | |||
long rand = (abs((int)rng.rand_int31()) %3); | long rand = (abs((int)rng.rand_int31()) %3); | ||
− | |||
_grid[y][x] = Vec3f(_origin.x/2+_size/2*x, rand, _origin.z/2-_size/2*y); | _grid[y][x] = Vec3f(_origin.x/2+_size/2*x, rand, _origin.z/2-_size/2*y); | ||
− | |||
//std::cout<<_grid[y][x].x<<"/"<<_grid[y][x].y<<"/"<<_grid[y][x].z<<std::endl; | //std::cout<<_grid[y][x].x<<"/"<<_grid[y][x].y<<"/"<<_grid[y][x].z<<std::endl; | ||
− | |||
} | } | ||
− | |||
} | } | ||
− | |||
} | } | ||
− | + | /** | |
− | + | * Convert the grid's positions into a Horde3D vertex array. | |
− | + | **/ | |
− | |||
void DynamicGrid::generatePositions() | void DynamicGrid::generatePositions() | ||
− | |||
{ | { | ||
− | |||
_positions = new float[_numVertices*3]; | _positions = new float[_numVertices*3]; | ||
− | |||
int counter = 0; | int counter = 0; | ||
− | |||
for (int i=0; i<_numVertices*3; i+=3) { | for (int i=0; i<_numVertices*3; i+=3) { | ||
− | |||
int y = counter / _gridSize; | int y = counter / _gridSize; | ||
− | |||
int x = counter % _gridSize; | int x = counter % _gridSize; | ||
− | |||
_positions[i] = _grid[y][x].x; | _positions[i] = _grid[y][x].x; | ||
− | |||
_positions[i+1] = _grid[y][x].y; | _positions[i+1] = _grid[y][x].y; | ||
− | |||
_positions[i+2] = _grid[y][x].z; | _positions[i+2] = _grid[y][x].z; | ||
− | |||
++counter; | ++counter; | ||
− | |||
} | } | ||
− | |||
− | |||
− | |||
} | } | ||
− | |||
− | |||
void DynamicGrid::generateNormals() | void DynamicGrid::generateNormals() | ||
− | |||
{ | { | ||
− | |||
_normals = new float[_numVertices * 3]; | _normals = new float[_numVertices * 3]; | ||
− | |||
for (int i=0; i< _numVertices*3; i+=3) { | for (int i=0; i< _numVertices*3; i+=3) { | ||
− | |||
int pos = i/3; | int pos = i/3; | ||
− | |||
int xPos = pos % _gridSize; | int xPos = pos % _gridSize; | ||
− | |||
int yPos = pos / _gridSize; | int yPos = pos / _gridSize; | ||
− | |||
Vec3f xPred; | Vec3f xPred; | ||
− | |||
Vec3f xSucc; | Vec3f xSucc; | ||
− | |||
Vec3f yPred; | Vec3f yPred; | ||
− | |||
Vec3f ySucc; | Vec3f ySucc; | ||
− | |||
Vec3f normal; | Vec3f normal; | ||
− | |||
if (xPos > 0 && yPos > 0) { | if (xPos > 0 && yPos > 0) { | ||
− | |||
// if the current vertex is inside the grid we will calculate | // if the current vertex is inside the grid we will calculate | ||
− | |||
// two normals from the previous&next point and our current vertex | // two normals from the previous&next point and our current vertex | ||
− | |||
if (xPos < _gridSize-1 && yPos < _gridSize-1) { | if (xPos < _gridSize-1 && yPos < _gridSize-1) { | ||
− | |||
xSucc = _grid[yPos][xPos+1]; | xSucc = _grid[yPos][xPos+1]; | ||
− | |||
xPred = _grid[yPos][xPos-1]; | xPred = _grid[yPos][xPos-1]; | ||
− | |||
ySucc = _grid[yPos+1][xPos]; | ySucc = _grid[yPos+1][xPos]; | ||
− | |||
yPred = _grid[yPos-1][xPos]; | yPred = _grid[yPos-1][xPos]; | ||
− | |||
Vec3f v1 = xSucc - _grid[yPos][xPos]; | Vec3f v1 = xSucc - _grid[yPos][xPos]; | ||
− | |||
if (v1.y < 0) | if (v1.y < 0) | ||
− | |||
v1.y *= -1; | v1.y *= -1; | ||
− | |||
Vec3f v2 = xPred - _grid[yPos][xPos]; | Vec3f v2 = xPred - _grid[yPos][xPos]; | ||
− | |||
if (v2.y < 0) | if (v2.y < 0) | ||
− | |||
v2.y *= -1; | v2.y *= -1; | ||
− | |||
v1 += v2; | v1 += v2; | ||
− | |||
Vec3f v3 = ySucc - _grid[yPos][xPos]; | Vec3f v3 = ySucc - _grid[yPos][xPos]; | ||
− | |||
if (v3.y < 0) | if (v3.y < 0) | ||
− | |||
v3.y *= -1; | v3.y *= -1; | ||
− | |||
Vec3f v4 = yPred - _grid[yPos][xPos]; | Vec3f v4 = yPred - _grid[yPos][xPos]; | ||
− | |||
if (v4.y < 0) | if (v4.y < 0) | ||
− | |||
v4.y *= -1; | v4.y *= -1; | ||
− | |||
v3 += v4; | v3 += v4; | ||
− | |||
normal = getInterpolatedNormal(v1, v3); | normal = getInterpolatedNormal(v1, v3); | ||
− | |||
} | } | ||
− | |||
// if the vertex is on the grid's border the normal will simply be 0/1/0 | // if the vertex is on the grid's border the normal will simply be 0/1/0 | ||
− | |||
else { | else { | ||
− | |||
normal = Vec3f(0,1,0); | normal = Vec3f(0,1,0); | ||
− | |||
} | } | ||
− | |||
} | } | ||
− | |||
else { | else { | ||
− | |||
normal = Vec3f(0,1,0); | normal = Vec3f(0,1,0); | ||
− | |||
} | } | ||
− | |||
_normals[i] = normal.x; | _normals[i] = normal.x; | ||
− | |||
_normals[i+1] = normal.y; | _normals[i+1] = normal.y; | ||
− | |||
_normals[i+2] = normal.z; | _normals[i+2] = normal.z; | ||
− | |||
} | } | ||
− | |||
} | } | ||
− | |||
− | |||
void DynamicGrid::generateTexCoords() | void DynamicGrid::generateTexCoords() | ||
− | |||
{ | { | ||
− | |||
_texCoords = new float[_numVertices * 2]; | _texCoords = new float[_numVertices * 2]; | ||
− | |||
int counter = 0; | int counter = 0; | ||
− | |||
int line = 0; | int line = 0; | ||
− | |||
for (int i=0; i<_numVertices*2; i+=2) { | for (int i=0; i<_numVertices*2; i+=2) { | ||
− | |||
if ( line % 2 == 0) { | if ( line % 2 == 0) { | ||
− | |||
if (counter %2 == 0) { | if (counter %2 == 0) { | ||
− | |||
_texCoords[i] = 0.0f; | _texCoords[i] = 0.0f; | ||
− | |||
_texCoords[i+1] = 0.0f; | _texCoords[i+1] = 0.0f; | ||
− | |||
} | } | ||
− | |||
else { | else { | ||
− | |||
_texCoords[i] = 0.0f; | _texCoords[i] = 0.0f; | ||
− | |||
_texCoords[i+1] = 1.0f; | _texCoords[i+1] = 1.0f; | ||
− | |||
} | } | ||
− | |||
} | } | ||
− | |||
else { | else { | ||
− | |||
if (counter % 2 == 0) { | if (counter % 2 == 0) { | ||
− | |||
_texCoords[i] = 1.0f; | _texCoords[i] = 1.0f; | ||
− | |||
_texCoords[i+1] = 0.0f; | _texCoords[i+1] = 0.0f; | ||
− | |||
} | } | ||
− | |||
else { | else { | ||
− | |||
_texCoords[i] = 1.0f; | _texCoords[i] = 1.0f; | ||
− | |||
_texCoords[i+1] = 1.0f; | _texCoords[i+1] = 1.0f; | ||
− | |||
} | } | ||
− | |||
} | } | ||
− | |||
++counter; | ++counter; | ||
− | |||
if (counter >= _gridSize) { | if (counter >= _gridSize) { | ||
− | |||
++line; | ++line; | ||
− | |||
counter = 0; | counter = 0; | ||
− | |||
} | } | ||
− | |||
} | } | ||
− | |||
} | } | ||
− | |||
− | |||
void DynamicGrid::generateTriangleIndices() | void DynamicGrid::generateTriangleIndices() | ||
− | |||
{ | { | ||
− | |||
_tIndices = new int[ _numTriangleIndices ]; | _tIndices = new int[ _numTriangleIndices ]; | ||
− | |||
int counter = 0; | int counter = 0; | ||
− | |||
int lineBreakCounter = 0; | int lineBreakCounter = 0; | ||
− | |||
int counter3 = 0; | int counter3 = 0; | ||
− | |||
for (int i=0; i<_numTriangleIndices; i+=3) { | for (int i=0; i<_numTriangleIndices; i+=3) { | ||
− | |||
if (i%2 ==0) { | if (i%2 ==0) { | ||
− | |||
_tIndices[i] = counter; | _tIndices[i] = counter; | ||
− | |||
_tIndices[i+1] = counter+1; | _tIndices[i+1] = counter+1; | ||
− | |||
_tIndices[i+2] = counter+_gridSize; | _tIndices[i+2] = counter+_gridSize; | ||
− | |||
} | } | ||
− | |||
else { | else { | ||
− | |||
_tIndices[i] = counter + _gridSize; | _tIndices[i] = counter + _gridSize; | ||
− | |||
_tIndices[i+1] = counter + 1; | _tIndices[i+1] = counter + 1; | ||
− | |||
_tIndices[i+2] = counter+_gridSize+1; | _tIndices[i+2] = counter+_gridSize+1; | ||
− | |||
++counter; | ++counter; | ||
− | |||
} | } | ||
− | |||
++lineBreakCounter; | ++lineBreakCounter; | ||
− | |||
if (lineBreakCounter == _gridSize*2 - 2) { | if (lineBreakCounter == _gridSize*2 - 2) { | ||
− | |||
--counter; | --counter; | ||
− | |||
++counter3; | ++counter3; | ||
− | |||
lineBreakCounter = 0; | lineBreakCounter = 0; | ||
− | |||
counter = counter3*_gridSize; | counter = counter3*_gridSize; | ||
− | |||
} | } | ||
− | |||
//std::cout<<_tIndices[i]<<"/"<<_tIndices[i+1]<<"/"<<_tIndices[i+2]<<std::endl; | //std::cout<<_tIndices[i]<<"/"<<_tIndices[i+1]<<"/"<<_tIndices[i+2]<<std::endl; | ||
− | |||
} | } | ||
− | |||
} | } | ||
− | |||
− | |||
void DynamicGrid::update() | void DynamicGrid::update() | ||
− | |||
{ | { | ||
− | |||
removeCustomModelNode(); | removeCustomModelNode(); | ||
− | |||
delete[] _positions; | delete[] _positions; | ||
− | |||
_positions = 0; | _positions = 0; | ||
− | |||
delete[] _normals; | delete[] _normals; | ||
− | |||
_normals = 0; | _normals = 0; | ||
− | |||
generatePositions(); | generatePositions(); | ||
− | |||
generateNormals(); | generateNormals(); | ||
− | |||
addCustomModelNode(-1); | addCustomModelNode(-1); | ||
− | |||
} | } | ||
− | |||
− | |||
void DynamicGrid::saveGridToFile(std::string fileName_) | void DynamicGrid::saveGridToFile(std::string fileName_) | ||
− | |||
{ | { | ||
− | |||
//std::stringstream fName; | //std::stringstream fName; | ||
− | |||
//fName<<fileName_; | //fName<<fileName_; | ||
− | |||
//fName<<".d2g"; | //fName<<".d2g"; | ||
− | |||
− | |||
− | |||
std::stringstream ss; | std::stringstream ss; | ||
− | |||
// magic header | // magic header | ||
− | |||
ss<<"D2SG\n"; | ss<<"D2SG\n"; | ||
− | |||
// the gridsize (important to know!) | // the gridsize (important to know!) | ||
− | |||
ss<<_gridSize; | ss<<_gridSize; | ||
− | |||
ss<<"\n"; | ss<<"\n"; | ||
− | |||
// the quadsize (important to know!) | // the quadsize (important to know!) | ||
− | |||
ss<<_size; | ss<<_size; | ||
− | |||
ss<<"\n"; | ss<<"\n"; | ||
− | |||
// number of coordinates (most important to know!) | // number of coordinates (most important to know!) | ||
− | |||
ss<<(_gridSize*_gridSize*3); | ss<<(_gridSize*_gridSize*3); | ||
− | |||
ss<<"\n"; | ss<<"\n"; | ||
− | |||
// coordinates | // coordinates | ||
− | |||
for (int y=0; y<_gridSize; ++y) { | for (int y=0; y<_gridSize; ++y) { | ||
− | |||
for (int x=0; x<_gridSize; ++x) { | for (int x=0; x<_gridSize; ++x) { | ||
− | |||
ss<<_grid[y][x].x; | ss<<_grid[y][x].x; | ||
− | |||
ss<<"\n"; | ss<<"\n"; | ||
− | |||
ss<<_grid[y][x].y; | ss<<_grid[y][x].y; | ||
− | |||
ss<<"\n"; | ss<<"\n"; | ||
− | |||
ss<<_grid[y][x].z; | ss<<_grid[y][x].z; | ||
− | |||
ss<<"\n"; | ss<<"\n"; | ||
− | |||
} | } | ||
− | |||
} | } | ||
− | |||
ss<<"\n"; | ss<<"\n"; | ||
− | |||
std::ofstream dest(fileName_.c_str()); | std::ofstream dest(fileName_.c_str()); | ||
− | |||
if (dest.is_open()) { | if (dest.is_open()) { | ||
− | |||
dest<<ss.str(); | dest<<ss.str(); | ||
− | |||
dest.close(); | dest.close(); | ||
− | |||
} | } | ||
− | |||
std::cout<<"Grid successfully saved to file: "<<fileName_<<std::endl; | std::cout<<"Grid successfully saved to file: "<<fileName_<<std::endl; | ||
− | |||
} | } | ||
− | |||
− | |||
bool DynamicGrid::readGridFromFile(std::string fileName_) | bool DynamicGrid::readGridFromFile(std::string fileName_) | ||
− | |||
{ | { | ||
− | |||
int numCoordinates; | int numCoordinates; | ||
− | |||
std::ifstream inFile; | std::ifstream inFile; | ||
− | |||
inFile.open(fileName_.c_str()); | inFile.open(fileName_.c_str()); | ||
− | |||
bool error = false; | bool error = false; | ||
− | |||
if (!inFile) { | if (!inFile) { | ||
− | |||
std::cout<<"Gridfile "<<fileName_<<" could not be found! Creating new grid."<<std::endl; | std::cout<<"Gridfile "<<fileName_<<" could not be found! Creating new grid."<<std::endl; | ||
− | |||
error = true; | error = true; | ||
− | |||
} | } | ||
− | |||
else { | else { | ||
− | |||
std::string line; | std::string line; | ||
− | |||
if (std::getline(inFile, line)) { | if (std::getline(inFile, line)) { | ||
− | |||
std::cout<<"Line: "<<line<<std::endl; | std::cout<<"Line: "<<line<<std::endl; | ||
− | + | if (line != "D2SG") { | |
− | |||
− | |||
− | |||
− | |||
std::cout<<"wrong grid-file format "<<line.compare("D2SG")<<std::endl; | std::cout<<"wrong grid-file format "<<line.compare("D2SG")<<std::endl; | ||
− | |||
error = true; | error = true; | ||
− | |||
} | } | ||
− | |||
else { | else { | ||
− | |||
std::cout<<"Valid grid-file found!"<<std::endl; | std::cout<<"Valid grid-file found!"<<std::endl; | ||
− | |||
} | } | ||
− | |||
} | } | ||
− | |||
if (std::getline(inFile, line)) { | if (std::getline(inFile, line)) { | ||
− | |||
_gridSize = stringToInt(line); | _gridSize = stringToInt(line); | ||
− | |||
//std::cout<<"gridsize: "<<_gridSize<<std::endl; | //std::cout<<"gridsize: "<<_gridSize<<std::endl; | ||
− | |||
} | } | ||
− | |||
else { | else { | ||
− | |||
error = true; | error = true; | ||
− | |||
} | } | ||
− | |||
if (std::getline(inFile, line)) { | if (std::getline(inFile, line)) { | ||
− | |||
_size = stringToInt(line); | _size = stringToInt(line); | ||
− | |||
//std::cout<<"quadsize: "<<_size<<std::endl; | //std::cout<<"quadsize: "<<_size<<std::endl; | ||
− | |||
} | } | ||
− | |||
else { | else { | ||
− | |||
error = true; | error = true; | ||
− | |||
} | } | ||
− | |||
if (std::getline(inFile, line)) { | if (std::getline(inFile, line)) { | ||
− | |||
numCoordinates = stringToInt(line); | numCoordinates = stringToInt(line); | ||
− | |||
//std::cout<<"numCoords: "<<numCoordinates<<std::endl; | //std::cout<<"numCoords: "<<numCoordinates<<std::endl; | ||
− | |||
} | } | ||
− | |||
else { | else { | ||
− | |||
error = true; | error = true; | ||
− | |||
} | } | ||
− | |||
if (!error) { | if (!error) { | ||
− | |||
_grid = new Vec3f*[_gridSize]; | _grid = new Vec3f*[_gridSize]; | ||
− | |||
for(int y=0; y<_gridSize; ++y) { | for(int y=0; y<_gridSize; ++y) { | ||
− | |||
_grid[y] = new Vec3f[_gridSize]; | _grid[y] = new Vec3f[_gridSize]; | ||
− | |||
for (int x=0; x<_gridSize; ++x) { | for (int x=0; x<_gridSize; ++x) { | ||
− | |||
Vec3f v; | Vec3f v; | ||
− | |||
if (std::getline(inFile, line)) { | if (std::getline(inFile, line)) { | ||
− | |||
v.x = stringToFloat(line); | v.x = stringToFloat(line); | ||
− | |||
} | } | ||
− | |||
if (std::getline(inFile, line)) { | if (std::getline(inFile, line)) { | ||
− | |||
v.y = stringToFloat(line); | v.y = stringToFloat(line); | ||
− | |||
} | } | ||
− | |||
if (std::getline(inFile, line)) { | if (std::getline(inFile, line)) { | ||
− | |||
v.z = stringToFloat(line); | v.z = stringToFloat(line); | ||
− | |||
} | } | ||
− | |||
_grid[y][x] = v; | _grid[y][x] = v; | ||
− | |||
} | } | ||
− | |||
} | } | ||
− | |||
} | } | ||
− | |||
} | } | ||
− | |||
inFile.close(); | inFile.close(); | ||
− | |||
if (error) { | if (error) { | ||
− | |||
_gridSize = GRIDSIZE; | _gridSize = GRIDSIZE; | ||
− | |||
_size = 12; | _size = 12; | ||
− | |||
_grid = 0; | _grid = 0; | ||
− | |||
} | } | ||
− | |||
_numVertices = (_gridSize*_gridSize); | _numVertices = (_gridSize*_gridSize); | ||
− | |||
_numTriangles = ((_gridSize - 1) * (_gridSize - 1)) * 2; | _numTriangles = ((_gridSize - 1) * (_gridSize - 1)) * 2; | ||
− | |||
_numTriangleIndices = _numTriangles * 3; | _numTriangleIndices = _numTriangles * 3; | ||
− | |||
_origin = Vec3f(-_size/2*_gridSize, 0, _size/2*_gridSize); | _origin = Vec3f(-_size/2*_gridSize, 0, _size/2*_gridSize); | ||
− | |||
− | |||
− | |||
return true; | return true; | ||
+ | } | ||
+ | </source> | ||
+ | }} | ||
− | + | =Usage example= | |
+ | Finally this is all you have to do to generate the grid and display it in your scene. | ||
+ | {{CppSourceCode| | ||
+ | description= app.cpp| | ||
+ | code= | ||
+ | <source lang="cpp" line="1"> | ||
+ | // initialize a new grid (args: quadsize, model-name, filename (if applicable) | ||
+ | _grid = new DynamicGrid(12.0f, "grid", "grid-filename"); | ||
+ | // do not forget to load a material and apply it to the mesh | ||
+ | _grid->setMaterial(gridMatRes); | ||
+ | // generate the grid | ||
+ | _grid->generate(); | ||
+ | // add the grid-model to the RootNode | ||
+ | _grid->addCustomModelNode(RootNode); | ||
</source> | </source> | ||
}} | }} | ||
− | + | '''TODO''' | |
+ | }} |
Latest revision as of 05:09, 18 March 2011
Work in progress - Due to the current changes in the Horde API this tutorial will be delayed Important notes
OverviewBasic approachTo create and display a procedurally generated mesh that, instead of being loaded from a Horde3D Scene node and a geoemtry file(*.scene.xml, *.geo), will be created in-memory by some algorithm, you have to create a mesh first and then "fake" the .geo-file and the scene node itself and pass it to the engine. To do that you have to create your procedural geometry first. This is easy for simple shapes like single triangles, squares or triangle-strips, but will become way more difficult when we try to generate complex meshes, e.g. when we have a cloud of points in 3D space and have to describe which points make triangles, and which don't (calculating the triangle indices, texture coordinates and normals). When we have calculated the geometry itself we need to create a char-stream to make Horde believe the geometry has been read from a file and pass it to Horde as a geometry Resource. The final step is to create a Horde3D Scene Node from our procedurally generated mesh and render it into our scene. StreamGeneratorThese classes are only for creating the character stream that will contain the geometry resource.
CustomGeometry base classBase class for the custom geometry. This class currently contains lots of deprecated stuff, e.g. the update() function. This function was used to update an in-memory gemoetry resource in the "punk-rock"-way. Until Horde3D Beta3 the geometry resources were read-only, so directmanipulation of vertices was impossible. To modify the vertices I had to unload the geometry resource, re-create it with the new vertex-positions and load it into Horde again. This is not necessary any more because since Beta4 the geometry resources are not read-only anymore. All custom geometry classes should be derived from this class and implement the abstract functions as needed. (See the examples below)
Simple SquareA simple hard-coded square. This is pretty straight forward and demonstrates how to derive from the CustomGeometry class and how the triangle indices work.
Simple GridAll the work above was made to create my own interactive terrain generator(screenshot on top of this page) for a A* pathfinding demonstrator. The Grid-class actually generates a 2-dimensional array of Vec3f(s) and allows to extrude each point and re-generate the mesh in realtime. The internal representation is writeable and in case of a change of any point the overridden update() function will be called to update the Horde3D geometry resource.
Usage exampleFinally this is all you have to do to generate the grid and display it in your scene.
TODO |