Difference between revisions of "Procedurally generated geometry tutorial"

From Horde3D Wiki
Jump to: navigation, search
Line 46: Line 46:
 
<source lang="cpp" line="1">
 
<source lang="cpp" line="1">
 
#include "StreamGenerator.cpp"
 
#include "StreamGenerator.cpp"
 
 
  
 
StreamGenerator::StreamGenerator()  
 
StreamGenerator::StreamGenerator()  
 
 
{
 
{
 
 
 
 
}
 
}
 
 
  
 
StreamGenerator::~StreamGenerator()
 
StreamGenerator::~StreamGenerator()
 
 
{
 
{
 
 
 
 
}
 
}
 
</source>
 
</source>
Line 74: Line 62:
 
<source lang="cpp" line="1">
 
<source lang="cpp" line="1">
 
#pragma once
 
#pragma once
 
 
 
 
#include "StreamGenerator.h"
 
#include "StreamGenerator.h"
 
 
#include "CustomGeometry.h"
 
#include "CustomGeometry.h"
 
 
  
 
class GeometryStreamGenerator : public StreamGenerator
 
class GeometryStreamGenerator : public StreamGenerator
 
 
{
 
{
 
 
private:
 
private:
 
 
std::stringstream* ss;
 
std::stringstream* ss;
 
 
void generate();
 
void generate();
 
 
int numVertices, numTriangleIndices;
 
int numVertices, numTriangleIndices;
 
 
char* _stream;
 
char* _stream;
 
 
CustomGeometry* _geom;
 
CustomGeometry* _geom;
 
 
  
 
public:
 
public:
 
 
GeometryStreamGenerator(CustomGeometry* geom_);
 
GeometryStreamGenerator(CustomGeometry* geom_);
 
 
~GeometryStreamGenerator();
 
~GeometryStreamGenerator();
 
 
const char* getStream();
 
const char* getStream();
 
 
const std::stringstream* getStringStream() { return ss; };
 
const std::stringstream* getStringStream() { return ss; };
 
 
const int getStreamSize();
 
const int getStreamSize();
 
 
const int getNumVertices();
 
const int getNumVertices();
 
 
const int getNumTriangleIndices();
 
const int getNumTriangleIndices();
 
 
void release();
 
void release();
 
 
};
 
};
 
 
</source>
 
</source>
 
}}
 
}}
Line 129: Line 92:
 
<source lang="cpp" line="1">
 
<source lang="cpp" line="1">
 
#include "GeometryStreamGenerator.h"
 
#include "GeometryStreamGenerator.h"
 
 
#include "time.h"
 
#include "time.h"
 
 
#include "utMath.h"
 
#include "utMath.h"
 
 
  
 
GeometryStreamGenerator::GeometryStreamGenerator(CustomGeometry* geom_) : _geom(geom_)
 
GeometryStreamGenerator::GeometryStreamGenerator(CustomGeometry* geom_) : _geom(geom_)
 
 
{
 
{
 
 
ss = new std::stringstream();
 
ss = new std::stringstream();
 
 
generate();
 
generate();
 
 
}
 
}
 
 
  
 
GeometryStreamGenerator::~GeometryStreamGenerator()
 
GeometryStreamGenerator::~GeometryStreamGenerator()
 
 
{
 
{
 
 
}
 
}
 
 
  
 
void GeometryStreamGenerator::generate()
 
void GeometryStreamGenerator::generate()
 
 
{
 
{
 
 
numVertices = _geom->getNumVertices();
 
numVertices = _geom->getNumVertices();
 
 
numTriangleIndices = _geom->getNumTriangleIndices();
 
numTriangleIndices = _geom->getNumTriangleIndices();
 
 
 
 
const std::string _magicHeader = "H3DG";
 
const std::string _magicHeader = "H3DG";
 
 
const int _version = 5;
 
const int _version = 5;
 
 
const int _numJoints = 1;
 
const int _numJoints = 1;
 
 
const float _identityMatrix[] = { 1.0f, 0.0f, 0.0f, 0.0f,   
 
const float _identityMatrix[] = { 1.0f, 0.0f, 0.0f, 0.0f,   
 
 
0.0f, 1.0f, 0.0f, 0.0f,   
 
0.0f, 1.0f, 0.0f, 0.0f,   
 
 
0.0f, 0.0f, 1.0f, 0.0f,   
 
0.0f, 0.0f, 1.0f, 0.0f,   
 
 
0.0f, 0.0f, 0.0f, 1.0f};
 
0.0f, 0.0f, 0.0f, 1.0f};
 
 
const int _numVertexStreams = 3;
 
const int _numVertexStreams = 3;
 
 
const int _magicVertices = 0;
 
const int _magicVertices = 0;
 
 
const int _vertexStreamElementSize = 12;
 
const int _vertexStreamElementSize = 12;
 
 
const float* _positions = _geom->getPositions();
 
const float* _positions = _geom->getPositions();
 
 
const int _magicNormals = 1;
 
const int _magicNormals = 1;
 
 
const int _normalsStreamElementSize = 6;
 
const int _normalsStreamElementSize = 6;
 
 
const float* _normals = _geom->getNormals();
 
const float* _normals = _geom->getNormals();
 
 
const int _magicTextureCoords1 = 6;
 
const int _magicTextureCoords1 = 6;
 
 
const int _texCoordStreamElementSize = 8;
 
const int _texCoordStreamElementSize = 8;
 
 
const float* _texCoords = _geom->getTexCoords();
 
const float* _texCoords = _geom->getTexCoords();
 
 
const int* _triangleIndices = _geom->getTriangleIndices();
 
const int* _triangleIndices = _geom->getTriangleIndices();
 
 
const int _numMorphTragets = 0;
 
const int _numMorphTragets = 0;
 
 
 
 
// header
 
// header
 
 
ss->write(_magicHeader.c_str(), 4*sizeof(char));
 
ss->write(_magicHeader.c_str(), 4*sizeof(char));
 
 
ss->write((char*)&_version, sizeof(int));
 
ss->write((char*)&_version, sizeof(int));
 
 
// joints (mandatory)
 
// joints (mandatory)
 
 
ss->write((char*)&_numJoints, sizeof(int));
 
ss->write((char*)&_numJoints, sizeof(int));
 
 
for (int i=0; i<16; ++i) {
 
for (int i=0; i<16; ++i) {
 
 
ss->write((char*)&_identityMatrix[i], sizeof(float));
 
ss->write((char*)&_identityMatrix[i], sizeof(float));
 
 
}
 
}
 
 
// vertices
 
// vertices
 
 
ss->write((char*)&_numVertexStreams, sizeof(int));
 
ss->write((char*)&_numVertexStreams, sizeof(int));
 
 
const int numVerts = _geom->getNumVertices();
 
const int numVerts = _geom->getNumVertices();
 
 
ss->write((char*)&numVerts, sizeof(int));
 
ss->write((char*)&numVerts, sizeof(int));
 
 
ss->write((char*)&_magicVertices, sizeof(int));
 
ss->write((char*)&_magicVertices, sizeof(int));
 
 
ss->write((char*)&_vertexStreamElementSize, sizeof(int));
 
ss->write((char*)&_vertexStreamElementSize, sizeof(int));
 
 
for (int i=0; i<_geom->getNumVertices()*3; ++i) {
 
for (int i=0; i<_geom->getNumVertices()*3; ++i) {
 
 
ss->write((char*)&_positions[i], sizeof(float));
 
ss->write((char*)&_positions[i], sizeof(float));
 
 
}
 
}
 
 
// normals
 
// normals
 
 
ss->write((char*)&_magicNormals, sizeof(int));
 
ss->write((char*)&_magicNormals, sizeof(int));
 
 
ss->write((char*)&_normalsStreamElementSize, sizeof(int));
 
ss->write((char*)&_normalsStreamElementSize, sizeof(int));
 
 
for (int i=0; i<_geom->getNumVertices()*3; ++i) {
 
for (int i=0; i<_geom->getNumVertices()*3; ++i) {
 
 
// do not forget to mutliply the value with 32767 and  
 
// do not forget to mutliply the value with 32767 and  
 
 
// then convert it to short before writing to the stream
 
// then convert it to short before writing to the stream
 
 
const short f = (short)(_normals[i]*32767);
 
const short f = (short)(_normals[i]*32767);
 
 
ss->write((char*)&f, sizeof(short));
 
ss->write((char*)&f, sizeof(short));
 
 
}
 
}
 
 
// texture coordinates
 
// texture coordinates
 
 
ss->write((char*)&_magicTextureCoords1, sizeof(int));
 
ss->write((char*)&_magicTextureCoords1, sizeof(int));
 
 
ss->write((char*)&_texCoordStreamElementSize, sizeof(int));
 
ss->write((char*)&_texCoordStreamElementSize, sizeof(int));
 
 
for (int i=0; i<_geom->getNumVertices()*2; ++i) {
 
for (int i=0; i<_geom->getNumVertices()*2; ++i) {
 
 
ss->write((char*)&_texCoords[i], sizeof(float));
 
ss->write((char*)&_texCoords[i], sizeof(float));
 
 
}
 
}
 
 
// triangle indices
 
// triangle indices
 
 
const int numTriangleIndices = _geom->getNumTriangleIndices();
 
const int numTriangleIndices = _geom->getNumTriangleIndices();
 
 
ss->write((char*)&numTriangleIndices, sizeof(int));
 
ss->write((char*)&numTriangleIndices, sizeof(int));
 
 
for (int i=0; i<_geom->getNumTriangleIndices(); ++i) {
 
for (int i=0; i<_geom->getNumTriangleIndices(); ++i) {
 
 
ss->write((char*)&_triangleIndices[i], sizeof(int));
 
ss->write((char*)&_triangleIndices[i], sizeof(int));
 
 
}
 
}
 
 
ss->write((char*)&_numMorphTragets, sizeof(int));
 
ss->write((char*)&_numMorphTragets, sizeof(int));
 
 
 
 
_stream = new char[ss->tellp()];
 
_stream = new char[ss->tellp()];
 
 
ss->read(_stream, ss->tellp());
 
ss->read(_stream, ss->tellp());
 
 
}
 
}
 
 
  
 
const char* GeometryStreamGenerator::getStream()
 
const char* GeometryStreamGenerator::getStream()
 
 
{
 
{
 
 
return _stream;
 
return _stream;
 
 
}
 
}
 
 
  
 
const int GeometryStreamGenerator::getStreamSize()  
 
const int GeometryStreamGenerator::getStreamSize()  
 
 
{
 
{
 
 
int t = (int)ss->tellp();
 
int t = (int)ss->tellp();
 
 
return t;
 
return t;
 
 
}
 
}
 
 
  
 
const int GeometryStreamGenerator::getNumVertices()
 
const int GeometryStreamGenerator::getNumVertices()
 
 
{
 
{
 
 
return numVertices;
 
return numVertices;
 
 
}
 
}
 
 
  
 
const int GeometryStreamGenerator::getNumTriangleIndices()
 
const int GeometryStreamGenerator::getNumTriangleIndices()
 
 
{
 
{
 
 
return numTriangleIndices;
 
return numTriangleIndices;
 
 
}
 
}
 
 
  
 
void GeometryStreamGenerator::release()
 
void GeometryStreamGenerator::release()
 
 
{
 
{
 
 
ss->flush();
 
ss->flush();
 
 
ss->clear();
 
ss->clear();
 
 
delete ss;
 
delete ss;
 
 
ss = 0;
 
ss = 0;
 
 
delete[] _stream;
 
delete[] _stream;
 
 
_stream = 0;
 
_stream = 0;
 
 
}
 
}
 
</source>
 
</source>
 
}}
 
}}
 
  
 
=CustomGeometry base class=
 
=CustomGeometry base class=

Revision as of 11:38, 26 October 2009

Due to the current changes in the Horde API this tutorial will be delayed

Work in progress


Important notes

  • The code used here has been tested with Horde3D 1.0.0 beta2 and beta3 and will NOT work with the latest release (beta4)


Overview

Basic approach

To 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. [musicracer_intro.jpg This is how it could look like]

StreamGenerator

StreamGenerator.h
#pragma once
#include <sstream>
#include <string>
#include <iostream>

class StreamGenerator 
{
public:
	StreamGenerator();
	virtual ~StreamGenerator();
	virtual const char* getStream() = 0;
	virtual const int getStreamSize() = 0;
};
StreamGenerator.h
#include "StreamGenerator.cpp"

StreamGenerator::StreamGenerator() 
{
}

StreamGenerator::~StreamGenerator()
{
}
GeometryStreamGenerator.h
#pragma once
#include "StreamGenerator.h"
#include "CustomGeometry.h"

class GeometryStreamGenerator : public StreamGenerator
{
private:
	std::stringstream* ss;
	void generate();
	int numVertices, numTriangleIndices;
	char* _stream;
	CustomGeometry* _geom;

public:
	GeometryStreamGenerator(CustomGeometry* geom_);
	~GeometryStreamGenerator();
	const char* getStream();
	const std::stringstream* getStringStream() { return ss; };
	const int getStreamSize();
	const int getNumVertices();
	const int getNumTriangleIndices();
	void release();
};
GeometryStreamGenerator.cpp
#include "GeometryStreamGenerator.h"
#include "time.h"
#include "utMath.h"

GeometryStreamGenerator::GeometryStreamGenerator(CustomGeometry* geom_) : _geom(geom_)
{
	ss = new std::stringstream();
	generate();
}

GeometryStreamGenerator::~GeometryStreamGenerator()
{
}

void GeometryStreamGenerator::generate()
{
	numVertices = _geom->getNumVertices();
	numTriangleIndices = _geom->getNumTriangleIndices();
	const std::string _magicHeader = "H3DG";
	const int _version = 5;
	const int _numJoints = 1;
	const float _identityMatrix[] = {	1.0f, 0.0f, 0.0f, 0.0f,  
										0.0f, 1.0f, 0.0f, 0.0f,  
										0.0f, 0.0f, 1.0f, 0.0f,  
										0.0f, 0.0f, 0.0f, 1.0f};
	const int _numVertexStreams = 3;
	const int _magicVertices = 0;
	const int _vertexStreamElementSize = 12;
	const float* _positions = _geom->getPositions();
	const int _magicNormals = 1;
	const int _normalsStreamElementSize = 6;
	const float* _normals = _geom->getNormals();
	const int _magicTextureCoords1 = 6;
	const int _texCoordStreamElementSize = 8;
	const float* _texCoords = _geom->getTexCoords();
	const int* _triangleIndices = _geom->getTriangleIndices();
	const int _numMorphTragets = 0;
	// header
	ss->write(_magicHeader.c_str(), 4*sizeof(char));
	ss->write((char*)&_version, sizeof(int));
	// joints (mandatory)
	ss->write((char*)&_numJoints, sizeof(int));
	for (int i=0; i<16; ++i) {
		ss->write((char*)&_identityMatrix[i], sizeof(float));
	}
	// vertices
	ss->write((char*)&_numVertexStreams, sizeof(int));
	const int numVerts = _geom->getNumVertices();
	ss->write((char*)&numVerts, sizeof(int));
	ss->write((char*)&_magicVertices, sizeof(int));
	ss->write((char*)&_vertexStreamElementSize, sizeof(int));
	for (int i=0; i<_geom->getNumVertices()*3; ++i) {
		ss->write((char*)&_positions[i], sizeof(float));
	}
	// normals
	ss->write((char*)&_magicNormals, sizeof(int));
	ss->write((char*)&_normalsStreamElementSize, sizeof(int));
	for (int i=0; i<_geom->getNumVertices()*3; ++i) {
		// do not forget to mutliply the value with 32767 and 
		// then convert it to short before writing to the stream
		const short f = (short)(_normals[i]*32767);
		ss->write((char*)&f, sizeof(short));
	}
	// texture coordinates
	ss->write((char*)&_magicTextureCoords1, sizeof(int));
	ss->write((char*)&_texCoordStreamElementSize, sizeof(int));
	for (int i=0; i<_geom->getNumVertices()*2; ++i) {
		ss->write((char*)&_texCoords[i], sizeof(float));
	}
	// triangle indices
	const int numTriangleIndices = _geom->getNumTriangleIndices();
	ss->write((char*)&numTriangleIndices, sizeof(int));
	for (int i=0; i<_geom->getNumTriangleIndices(); ++i) {
		ss->write((char*)&_triangleIndices[i], sizeof(int));
	}
	ss->write((char*)&_numMorphTragets, sizeof(int));
	_stream = new char[ss->tellp()];
	ss->read(_stream, ss->tellp());
}

const char* GeometryStreamGenerator::getStream()
{
	return _stream;
}

const int GeometryStreamGenerator::getStreamSize() 
{
	int t = (int)ss->tellp();
	return t;
}

const int GeometryStreamGenerator::getNumVertices()
{
	return numVertices;
}

const int GeometryStreamGenerator::getNumTriangleIndices()
{
	return numTriangleIndices;
}

void GeometryStreamGenerator::release()
{
	ss->flush();
	ss->clear();
	delete ss;
	ss = 0;
	delete[] _stream;
	_stream = 0;
}

CustomGeometry base class

Simple Square

Simple Grid

Usage example