#include "Model3D.h" #include "Model2D.h" #include "DXBuffer.h" #include "Textur.h" #include "Animation3D.h" #include "Welt3D.h" #include "Textur.h" #include "Bild.h" #ifdef WIN32 #include #endif #include using namespace Framework; // Inhalt der Knochen Klasse // Konstruktor Knochen::Knochen( int id ) { pos = Vec3< float >( 0, 0, 0 ); winkel = Vec3< float >( 0, 0, 0 ); geschwister = 0; kinder = 0; this->id = id; } // Destruktor Knochen::~Knochen() { delete geschwister; delete kinder; } // public // Setzt die Position des Knochens relativ zum Model Ursprung // pos: Die Position void Knochen::setPosition( Vec3< float > &pos ) { this->pos = pos; } // Setzt die Drehung des Knochens relativ zum Model Ursprung // winkel: Ein Vektor der die Drehung um die verschiedenen Achsen als Komponenten hat void Knochen::setDrehung( Vec3< float > &winkel ) { this->winkel = winkel; } // Fügt dem Knochen ein Geschwister Knochen hinzu // k: Der Knochen, der hinzugefügt werden soll void Knochen::addGeschwisterKnochen( Knochen *k ) { if( !geschwister ) geschwister = k; else geschwister->addGeschwisterKnochen( k ); } // Fügt einem bestimmten Knochen ein Kind Knochen hinzu // id: Die id des Knochens, wo der Knochen als Kind hinzugefügt werden soll // k: Der Knochen, der hinzugefügt werden soll void Knochen::addKind( int id, Knochen *k ) { if( this->id == id ) { if( !kinder ) kinder = k; else kinder->addGeschwisterKnochen( k ); } else { if( kinder ) kinder->addKind( id, k ); else { Text err = "Es wurde kein Knochen mit der Id: "; err += id; err += " im Skelett gefunden, um ein Kind Knochen hinzuzufügen. Datei:" __FILE__ ", Zeile: "; err += __LINE__; err += "!"; delete k; throw std::out_of_range( (const char *)err ); } } } // Berechnet die Matrizen des Knochen und die von all seinen Geschwister Knochen und Kind Knochen // elternMat: Die fertig berechnete Matrix des Elternknochens // matBuffer: Ein Array, in dem alle berechneten Matrizen gespeichert werden sollen // scaleFactor: Die Skallierung des Modells // kamMatrix: Die vereiniegung der view und projektions Matrizen void Knochen::kalkulateMatrix( Mat4< float > &elternMat, Mat4< float > *matBuffer, float scaleFactor, Mat4< float > &kamMat ) { if( geschwister ) geschwister->kalkulateMatrix( elternMat, matBuffer, scaleFactor, kamMat ); matBuffer[ id ] = matBuffer[ id ].translation( pos * scaleFactor ) * matBuffer[ id ].rotationZ( winkel.z ) * matBuffer[ id ].rotationX( winkel.x ) * matBuffer[ id ].rotationY( winkel.y ) * matBuffer[ id ].scaling( scaleFactor ); matBuffer[ id ] = elternMat * matBuffer[ id ]; if( kinder ) kinder->kalkulateMatrix( matBuffer[ id ], matBuffer, scaleFactor, kamMat ); matBuffer[ id ] = kamMat * matBuffer[ id ]; } Knochen *Framework::Knochen::zGeschwister() const { return geschwister; } Knochen *Framework::Knochen::zKind() const { return kinder; } // Kopiert den Knochen mit allen Geschwister Knochen und Kind Knochen Knochen *Knochen::kopiereKnochen() const { Knochen *ret = new Knochen( id ); ret->pos = pos; ret->winkel = winkel; if( geschwister ) ret->geschwister = geschwister->kopiereKnochen(); if( kinder ) ret->kinder = kinder->kopiereKnochen(); return ret; } // Gibt die Id des Knochens zurück int Knochen::getId() const { return id; } // Gibt die Drehung des Knochens zurück Vec3< float > Knochen::getDrehung() const { return winkel; } // Gibt die Position des Knochens zurück Vec3< float > Knochen::getPosition() const { return pos; } // Gibt den Radius des Knochens zurück float Knochen::getRadius() const { float r = pos.getLength(); if( geschwister ) r = MAX( r, geschwister->getRadius() ); if( kinder ) r += kinder->getRadius(); return r; } // Inhalt der Skelett Klasse // Konstruktor Skelett::Skelett() : ReferenceCounter() { k = 0; nextId = 0; } // Destruktor Skelett::~Skelett() { if( k ) delete k; } // Gibt die Id des nächsten Knochens zurück und berechnet die neue Id für den Knochen danach // Es können maximal MAX_KNOCHEN_ANZ Knochen für ein Skelett existieren. Wenn diese Zahl überschritten wird, so wird -1 zurückgegeben int Skelett::getNextKnochenId() { return nextId++; } void Framework::Skelett::setNextKnochenId( int id ) { nextId = id; } // Fügt dem Skellet einen Knochen hinzu // k: Der Knochen // elternId: Die Id des Eltern Knochens. Wenn der Knochen kein Elternknochen besitzt, kannder Parameter weggelassen werden. void Skelett::addKnochen( Knochen *k, int elternId ) { if( !this->k ) this->k = k; else this->k->addKind( elternId, k ); if( k->getId() >= nextId ) nextId = k->getId() + 1; } // Berechnet die Matrizen der Knochen // modelMatrix: Die Matrix, die das Skelett in den Raum der Welt transformiert // matBuffer: Ein Array von Matrizen, der durch die Knochen Matrizen gefüllt wird // scaleFactor: Die skallierung des Objektes // kamMatrix: Die vereiniegung der view und projektions Matrizen // return: gibt die Anzahl der verwendeten Matrizen zurück int Skelett::kalkulateMatrix( Mat4< float > &modelMatrix, Mat4< float > *matBuffer, float scaleFactor, Mat4< float > &kamMatrix ) { k->kalkulateMatrix( modelMatrix, matBuffer, scaleFactor, kamMatrix ); return nextId; } // Berechnet den Radius des Skeletts float Skelett::getRadius() const { if( k ) return k->getRadius(); return 0; } // gibt den Wurzel Knochen zurück Knochen *Framework::Skelett::zKnochen() const { return k; } // Kopiert das Skelett Skelett *Skelett::kopiereSkelett() const { Skelett *ret = new Skelett(); ret->nextId = nextId; if( k ) ret->addKnochen( k->kopiereKnochen() ); return ret; } int Framework::Skelett::zNextKnochenId() const { return nextId; } // Inhalt des Polygon3D Struct // Konstruktor Polygon3D::Polygon3D() { indexAnz = 0; indexList = 0; } // Destruktor Polygon3D::~Polygon3D() { delete[] indexList; } // Inhalt der Model3DData Klasse // Konstruktor Model3DData::Model3DData() : ReferenceCounter() { id = -1; skelett = 0; vertexList = 0; vertexCount = 0; polygons = new Array< Polygon3D * >(); ambientFactor = 1.f; diffusFactor = 0.f; specularFactor = 0.f; radius = 0; } // Destruktor Model3DData::~Model3DData() { clearModel(); polygons->release(); } // Löscht alle Model daten void Model3DData::clearModel() { delete[] vertexList; vertexCount = 0; vertexList = 0; for( auto i = polygons->getIterator(); i; i++ ) delete i; polygons->leeren(); if( skelett ) skelett->release(); skelett = 0; radius = 0; } // Berechnet die normalen für die Eckpunkte des Modells void Model3DData::calculateNormals() { for( int i = 0; i < vertexCount; i++ ) { Vec3< float > normal( 0, 0, 0 ); for( auto p = polygons->getIterator(); p; p++ ) { int begin = 0; for( int j = 0; j < p->indexAnz; j++ ) { if( j % 3 == 0 ) begin = j; if( p->indexList[ j ] == i ) { Vec3< float > a = vertexList[ p->indexList[ begin ] ].pos; Vec3< float > b = vertexList[ p->indexList[ begin + 1 ] ].pos; Vec3< float > c = vertexList[ p->indexList[ begin + 2 ] ].pos; normal += ( b - a ).crossProduct( c - a ).normalize(); normal.normalize(); } } } vertexList[ i ].normal = normal; } } // Setzt den Zeiger auf ein standartmäßig verwendete Skelett // s: Das Skelett, das verwendet werden soll void Model3DData::setSkelettZ( Skelett *s ) { if( skelett ) skelett->release(); skelett = s; } // Setzt einen Zeiger auf eine Liste mit allen Vertecies des Models // vertexList: Ein Array mit Vertecies // anz: Die Anzahl der Vertecies im Array void Model3DData::setVertecies( Vertex3D *vertexList, int anz ) { delete[] this->vertexList; this->vertexList = vertexList; vertexCount = anz; radius = 0; for( int i = 0; i < anz; i++ ) { float r = vertexList[ i ].pos.getLength(); if( r > radius ) radius = r; } } // Fügt ein Polygon zum Model hinzu // polygon: Das Polygon, das hinzugefügt erden soll void Model3DData::addPolygon( Polygon3D *polygon ) { polygons->add( polygon ); } // Git den Factor an, mit dem das umgebungslicht (textur farbe) multipliziert wird // f: der neue Faktor (von 0 bis 1, ambient + specular + diffuse = 1) void Model3DData::setAmbientFactor( float f ) { ambientFactor = f; } // Git den Factor an, mit dem die Lichtfarbe von Lichtquellen multipliziert wird // f: der neue Faktor (von 0 bis 1, ambient + specular + diffuse = 1) void Model3DData::setDiffusFactor( float f ) { diffusFactor = f; } // Git den Factor an, mit dem die Reflektion von Lichtquellen multipliziert wird // f: der neue Faktor (von 0 bis 1, ambient + specular + diffuse = 1) void Model3DData::setSpecularFactor( float f ) { specularFactor = f; } // Konvertiert ein 2d Model zu 3D // model: Das 2d Model, das zu 3d konvertiert werden soll // z: Die z koordinate aller punkte des Models void Model3DData::copyModel2D( Model2DData *model, float z ) { if( model && model->vListen && model->polygons ) { clearModel(); int vAnz = 0; for( auto i = model->polygons->getIterator(); i; i++ ) vAnz += i._.vertex->getEintragAnzahl(); vertexList = new Vertex3D[ vAnz ]; int index = 0; for( auto i = model->vListen->getIterator(); i; i++ ) { Polygon3D *p = new Polygon3D(); p->indexAnz = 0; for( auto j = i->getIterator(); j; j++ ) { for( auto k = j->zListe()->getIterator(); k.hasNext() && k.next().hasNext(); k++ ) p->indexAnz += 3; } p->indexList = new int[ p->indexAnz ]; p->indexAnz = 0; for( auto j = i->getIterator(); j; j++ ) { for( auto k = j->zListe()->getIterator(); k; k++ ) { vertexList[ index ].pos = Vec3< float >( k->punkt->x, k->punkt->y, z ); vertexList[ index ].tPos = ( Vec2< float > ) * k->textur; if( k.hasNext() && k.next().hasNext() ) { p->indexList[ p->indexAnz ] = index; p->indexAnz++; p->indexList[ p->indexAnz ] = index + 1; p->indexAnz++; p->indexList[ p->indexAnz ] = index + 2; p->indexAnz++; } index++; } } addPolygon( p ); } } } // Entfernt ein Polygon // index: Der Index des Polygons void Model3DData::removePolygon( int index ) { if( !polygons->hat( index ) ) return; delete polygons->get( index ); polygons->remove( index ); } // Berechnet die Matrizen der Knochen // modelMatrix: Die Matrix, die das Skelett in den Raum der Welt transformiert // matBuffer: Ein Array von Matrizen, der durch die Knochen Matrizen gefüllt wird // scaleFactor: Die Skallierung des Modells // kamMatrix: Die vereiniegung der view und projektions Matrizen // return: gibt die Anzahl der verwendeten Matrizen zurück int Model3DData::kalkulateMatrix( Mat4< float > &modelMatrix, Mat4< float > *matBuffer, float scaleFactor, Mat4< float > &kamMatrix ) const { if( !skelett ) return 0; return skelett->kalkulateMatrix( modelMatrix, matBuffer, scaleFactor, kamMatrix ); } // Gibt die Anzahl an Polygonen zurück int Model3DData::getPolygonAnzahl() const { return polygons->getEintragAnzahl(); } // Gibt ein bestimmtes Polygon zurück // index: Der Index des Polygons Polygon3D *Model3DData::getPolygon( int index ) const { if( !polygons->hat( index ) ) return 0; return polygons->get( index ); } // Gibt einen Iterator zurück, mit dem sich die Polygons auflisten lassen Iterator< Polygon3D * > Model3DData::getPolygons() const { return polygons->getIterator(); } // Gibt den radius einer Kugel zurück, die das gesammte Model umschließt float Model3DData::getRadius() const { return radius; } // Gibt die Id der Daten zurück, wenn sie in einer Model3DList registriert wurden. (siehe Framework::zM3DRegister()) int Model3DData::getId() const { return id; } // Git den Factor an, mit dem das umgebungslicht (textur farbe) multipliziert wird float Model3DData::getAmbientFactor() const { return ambientFactor; } // Git den Factor an, mit dem die Lichtfarbe von Lichtquellen multipliziert wird float Model3DData::getDiffusFactor() const { return diffusFactor; } // Git den Factor an, mit dem die Reflektion von Lichtquellen multipliziert wird float Model3DData::getSpecularFactor() const { return specularFactor; } // Gibt eine Kopie des Skeletts zurück, welches für annimationen verwendet werden kann Skelett *Model3DData::copySkelett() const { return skelett ? skelett->kopiereSkelett() : 0; } // Gibt die Anzahl an Vertices zurück int Model3DData::getVertexAnzahl() const { return vertexCount; } // Gibt einen Buffer mit allen Vertecies des Models zurück const Vertex3D *Model3DData::zVertexBuffer() const { return vertexList; } // Inhalt der Model3DTextur // Konstruktor Model3DTextur::Model3DTextur() : ReferenceCounter() { textures = new RCArray< Textur >(); } // Destruktor Model3DTextur::~Model3DTextur() { textures->release(); } // Legt fest, welche Textur für welches Polygon ist // pI: Der Index des Polygons // txt: Die Textur des Polygons void Model3DTextur::setPolygonTextur( int pI, Textur *txt ) { textures->set( txt, pI ); } // Gibt einen Zeiger auf die Textur eines Polygons zurück ohne erhöhten Reference Counter // i: Der Index des Polygons Textur *Model3DTextur::zPolygonTextur( int i ) const { return textures->z( i ); } // Inhalt der AnimationData Struktur Model3D::AnimationData *Model3D::AnimationData::getThis() { return this; } Model3D::AnimationData *Model3D::AnimationData::release() { a->release(); delete this; return 0; } // Inhalt der Model3D Klasse // Konstruktor Model3D::Model3D() : Zeichnung3D() { model = 0; textur = 0; skelett = 0; animations = new RCArray< AnimationData >(); ambientFactor = 1.f; diffusFactor = 0.f; specularFactor = 0.f; } // Destruktor Model3D::~Model3D() { if( model ) model->release(); if( textur ) textur->release(); if( skelett ) skelett->release(); animations->release(); } // Fügt eine Animation hinzu // a: Die neue Animation void Model3D::addAnimation( Animation3D *a, double speed ) { AnimationData *d = new AnimationData(); d->a = a; d->speed = speed; d->offset = 0; animations->add( d ); } // Entfernt eine Animation // zA: Die zu entfernende Animation void Model3D::removeAnimation( Animation3D *zA ) { for( int i = 0; i < animations->getEintragAnzahl(); i++ ) { if( animations->z( i )->a == zA ) { animations->remove( i ); return; } } } // Setzt die Daten des Models // data: Die Daten void Model3D::setModelDaten( Model3DData *data ) { if( model ) model->release(); if( skelett ) skelett = (Skelett *)skelett->release(); model = data; if( model ) { skelett = model->copySkelett(); this->ambientFactor = model->getAmbientFactor(); this->specularFactor = model->getSpecularFactor(); this->diffusFactor = model->getDiffusFactor(); } } // Setzt die zum Zeichnen zu benutzenden Texturen // txt: Ein Liste mit Texturen zu den verschiedenen Polygonen zugeordnet void Model3D::setModelTextur( Model3DTextur *txt ) { if( textur ) textur->release(); textur = txt; } // Git den Factor an, mit dem das umgebungslicht (textur farbe) multipliziert wird // f: der neue Faktor (von 0 bis 1, ambient + specular + diffuse = 1) void Framework::Model3D::setAmbientFactor( float f ) { this->ambientFactor = f; } // Git den Factor an, mit dem die Lichtfarbe von Lichtquellen multipliziert wird // f: der neue Faktor (von 0 bis 1, ambient + specular + diffuse = 1) void Framework::Model3D::setDiffusFactor( float f ) { diffusFactor = f; } // Git den Factor an, mit dem das umgebungslicht (textur farbe) multipliziert wird // f: der neue Faktor (von 0 bis 1, ambient + specular + diffuse = 1) void Framework::Model3D::setSpecularFactor( float f ) { specularFactor = f; } // Errechnet die Matrizen aller Knochen des Skeletts des Models // viewProj: Die miteinander multiplizierten Kameramatrizen // matBuffer: Ein Array mit Matrizen, der gefüllt werden soll // return: Die Anzahl der Matrizen, die das Model benötigt int Model3D::errechneMatrizen( Mat4< float > &viewProj, Mat4< float > *matBuffer ) { int ret = 0; if( skelett ) ret = skelett->kalkulateMatrix( welt, matBuffer, size, viewProj ); else if( model ) ret = model->kalkulateMatrix( welt, matBuffer, size, viewProj ); if( !ret ) return Zeichnung3D::errechneMatrizen( viewProj, matBuffer ); return ret; } // Verarbeitet die vergangene Zeit // tickval: Die zeit in sekunden, die seit dem letzten Aufruf der Funktion vergangen ist // return: true, wenn sich das Objekt verändert hat, false sonnst. bool Model3D::tick( double tickval ) { radius = model ? model->getRadius() : 0; if( skelett ) { radius += skelett->getRadius(); for( auto i = animations->getIterator(); i && i._; i++ ) { rend = i->speed > 0; i->a->apply( skelett, i->offset, tickval * i->speed ); } } return Zeichnung3D::tick( tickval ); } // Gibt die Textur zurück Model3DTextur *Model3D::getTextur() { return textur ? (Model3DTextur *)textur->getThis() : 0; } // Gibt die Textur zurück (ohne erhöhten Reference Counter) Model3DTextur *Model3D::zTextur() { return textur; } // Gibt die ModelDaten zurück Model3DData *Model3D::getModelData() { return model ? (Model3DData *)model->getThis() : 0; } // Gibt die ModelDaten zurück (ohne erhöhten Reference Counter) Model3DData *Model3D::zModelData() { return model; } // prüft, ob ein Strahl dieses Objekt trifft // point: der startpunkt des Strahls in Weltkoordinaten // dir: die Richtung des Strahls in Weltkoordinaten // maxSqDist: Die maximale quadratische distanz die erlaubt ist // pId: die Id des Polygons, zu dem der Schnittpunkt gehört // return: den quadratischen Abstand des Schnittpunktes zum Ursprung des Strahls oder -1, wenn kein schnittpunkt existiert float Model3D::traceRay( Vec3< float > &p, Vec3< float > &d, float maxSqDist, int &pId ) const { if( !model ) return -1; Vec3< float > dir = d; dir.rotateY( -angle.y ); dir.rotateX( -angle.x ); dir.rotateZ( -angle.z ); Vec3< float > point = p; point.rotateY( -angle.y ); point.rotateX( -angle.x ); point.rotateZ( -angle.z ); point -= pos; float nearest = ( -dir.x * point.x - dir.y * point.y - dir.z * point.z ) / ( dir.x * dir.x + dir.y * dir.y + dir.z * dir.z ); float dist = ( point + dir * nearest ).getLengthSq(); if( dist > ( radius * size ) * ( radius * size ) || ( dir * nearest ).getLength() - radius * size > sqrt( maxSqDist ) || ( nearest < 0 && ( dir * nearest ).getLengthSq() > radius * size * radius * size ) ) // es gibt kein schnittpunkt return -1; bool existsHit = 0; if( skelett ) { // todo } else { int index = 0; for( auto p = model->getPolygons(); p; p++ ) { for( int j = 0; j < p->indexAnz; j++ ) { if( j % 3 == 0 ) { Vec3< float > a = model->zVertexBuffer()[ p->indexList[ j ] ].pos; Vec3< float > b = model->zVertexBuffer()[ p->indexList[ j + 1 ] ].pos; Vec3< float > c = model->zVertexBuffer()[ p->indexList[ j + 2 ] ].pos; Vec3< float > normal = ( b - a ).crossProduct( c - a ).normalize(); if( normal * dir < 0 ) // Prüfe ob die Normale in Richtung des Strahl ursprungs zeigt { nearest = ( a * normal - point * normal ) / ( dir * normal ); Vec3< float > hit = point + dir * nearest; if( ( b - a ).angle( hit - a ) <= ( b - a ).angle( c - a ) && ( c - a ).angle( hit - a ) <= ( b - a ).angle( c - a ) && ( a - b ).angle( hit - b ) <= ( a - b ).angle( c - b ) ) { maxSqDist = ( hit - point ).getLengthSq(); pId = index; existsHit = 1; } } index++; } } } } return existsHit ? maxSqDist : -1; } // berechnet die Farbe des Schnittpunktes deines Strahls // point: der startpunkt des Strahls in Weltkoordinaten // dir: die Richtung des Strahls in Weltkoordinaten // zWelt: die Welt, aus der der Strahl kommt // return: die Farbe des Schnittpunktes int Model3D::traceRay( Vec3< float > &p, Vec3< float > &d, int pId, Welt3D *zWelt ) const { Vec3< float > dir = d; dir.rotateY( -angle.y ); dir.rotateX( -angle.x ); dir.rotateZ( -angle.z ); Vec3< float > point = p; point.rotateY( -angle.y ); point.rotateX( -angle.x ); point.rotateZ( -angle.z ); point -= pos; int index = 0; for( auto p = model->getPolygons(); p; p++, index++ ) { for( int j = 0; j < p->indexAnz; j++ ) { if( j % 3 == 0 ) { if( pId == 0 ) { const Vec3< float > &a = model->zVertexBuffer()[ p->indexList[ j ] ].pos; const Vec3< float > &b = model->zVertexBuffer()[ p->indexList[ j + 1 ] ].pos; const Vec3< float > &c = model->zVertexBuffer()[ p->indexList[ j + 2 ] ].pos; Vertex at = model->zVertexBuffer()[ p->indexList[ j ] ].tPos; Vertex bt = model->zVertexBuffer()[ p->indexList[ j + 1 ] ].tPos; Vertex ct = model->zVertexBuffer()[ p->indexList[ j + 2 ] ].tPos; Vec3< float > normal = ( b - a ).crossProduct( c - a ).normalize(); float t = ( a * normal - point * normal ) / ( dir * normal ); Vec3< float > hit = point + dir * t; float a0 = ( a - b ).crossProduct( a - c ).getLength() / 2; float a1 = ( b - hit ).crossProduct( c - hit ).getLength() / 2 / a0; float a2 = ( c - hit ).crossProduct( a - hit ).getLength() / 2 / a0; float a3 = ( a - hit ).crossProduct( b - hit ).getLength() / 2 / a0; Vertex ht = at * a1 + bt * a2 + ct * a3; Bild *tex = textur->zPolygonTextur( index )->zBild(); if( ht.x >= 0 && ht.y >= 0 && ht.x <= 1 && ht.y <= 1 ) return tex->getPixel( (int)( ht.x * ( (float)tex->getBreite() - 1.f ) + 0.5f ), (int)( ht.y * ( (float)tex->getHeight() - 1.f ) + 0.5f ) ); return 0xFF000000; } pId--; } } } return 0xFF000000; } // Gibt die Id der Daten zurück, wenn sie in einer Model3DList registriert wurden. (siehe Framework::zM3DRegister()) int Model3D::getDatenId() const { return model ? model->getId() : -1; } // Git den Factor an, mit dem das umgebungslicht (textur farbe) multipliziert wird float Model3D::getAmbientFactor() const { return ambientFactor; } // Git den Factor an, mit dem die Lichtfarbe von Lichtquellen multipliziert wird float Model3D::getDiffusFactor() const { return diffusFactor; } // Git den Factor an, mit dem die Reflektion von Lichtquellen multipliziert wird float Model3D::getSpecularFactor() const { return specularFactor; } // Gibt die Anzahl an Vertices zurück int Model3D::getVertexAnzahl() const { return model ? model->getVertexAnzahl() : 0; } // Gibt einen Buffer mit allen Vertecies des Models zurück const Vertex3D *Model3D::zVertexBuffer() const { return model ? model->zVertexBuffer() : 0; }