#include "Model3D.h" #include "Animation3D.h" #include "Image.h" #include "DXBuffer.h" #include "Model2D.h" #include "Texture.h" #include "World3D.h" #ifdef WIN32 # include #endif #include using namespace Framework; // Constructor // \param id the id of the bone Bone::Bone(int id) { pos = Vec3(0, 0, 0); rot = Vec3(0, 0, 0); sibling = 0; child = 0; this->id = id; } //! Destructor Bone::~Bone() { delete sibling; delete child; } //! set the position of the bone relative to the parent bone //! \param pos the position void Bone::setPosition(const Vec3& pos) { this->pos = pos; } //! Set the rotation of the bone relative to the parent bone //! \param rot the rotation void Bone::setRotation(const Vec3& rot) { this->rot = rot; } //! add a sibling bone to this bone that shares the same parent bone //! \param b The bone to be added void Bone::addSiblingBone(Bone* b) { if (!sibling) sibling = b; else sibling->addSiblingBone(b); } //! add a child bone to a specific child bone //! \param id the id of the bone the new bone should be a child of //! \param b the bone that should be added bool Bone::addChildBone(int id, Bone* b) { if (this->id == id) { if (!child) { child = b; return 1; } else { child->addSiblingBone(b); return 1; } } else { if (child) { if (child->addChildBone(id, b)) { return 1; } } else if (sibling) { return sibling->addChildBone(id, b); } return 0; } } //! calculates the matrixes of this bone, all child bones and sibling //! bones //! \param elternMat the already calculated matrix of the parent bone //! \param matBuffer the array to store the calculated matrixes //! \param scaleFactor the scaling of the object //! \param camMatrix the view-projection matrix of the used camera void Bone::calculateMatrix(const Mat4& elternMat, Mat4* matBuffer, float scaleFactor, const Mat4& kamMat) { if (sibling) sibling->calculateMatrix(elternMat, matBuffer, scaleFactor, kamMat); matBuffer[id] = matBuffer[id].translation(pos * scaleFactor) * matBuffer[id].rotationZ(rot.z) * matBuffer[id].rotationX(rot.x) * matBuffer[id].rotationY(rot.y) * matBuffer[id].scaling(scaleFactor); matBuffer[id] = elternMat * matBuffer[id]; if (child) child->calculateMatrix(matBuffer[id], matBuffer, scaleFactor, kamMat); matBuffer[id] = kamMat * matBuffer[id]; } Bone* Framework::Bone::zFirstSibling() const { return sibling; } Bone* Framework::Bone::zFirstChild() const { return child; } //! returns a copy of this bone with copies of all child and //! sibling bones Bone* Bone::copyBone() const { Bone* ret = new Bone(id); ret->pos = pos; ret->rot = rot; if (sibling) ret->sibling = sibling->copyBone(); if (child) ret->child = child->copyBone(); return ret; } //! \return the id of this bone int Bone::getId() const { return id; } //! \return the rotation of this bone Vec3 Bone::getRotation() const { return rot; } //! \return the position of this bone Vec3 Bone::getPosition() const { return pos; } //! \return the radius of this bone float Bone::getRadius() const { float r = pos.getLength(); if (sibling) r = MAX(r, sibling->getRadius()); if (child) r += child->getRadius(); return r; } // Contents of the Skeleton class // Constructor Skeleton::Skeleton() : ReferenceCounter() { rootBone = 0; nextId = 0; } // Destructor Skeleton::~Skeleton() { if (rootBone) delete rootBone; } Bone* Skeleton::zBone(Bone* zCurrent, int id) const { while (zCurrent) { if (zCurrent->getId() == id) { return zCurrent; } if (zCurrent->zFirstChild()) { Bone* ret = zBone(zCurrent->zFirstChild(), id); if (ret) return ret; } zCurrent = zCurrent->zFirstSibling(); } return 0; } //! add a bone to the sceleton //! \param pos the position of the bone //! \param rot the rotation of the bone //! \param the id of the parent bone where the new bone should be added //! as a child //! \return the id of the added bone or -1 if the bone could not be //! added int Skeleton::addBone(Vec3 pos, Vec3 rot, int parentId) { if (parentId == -1) { if (!this->rootBone) { this->rootBone = new Bone(nextId++); this->rootBone->setPosition(pos); this->rootBone->setRotation(rot); return this->rootBone->getId(); } else { Bone* bone = new Bone(nextId++); bone->setPosition(pos); bone->setRotation(rot); this->rootBone->addSiblingBone(bone); return bone->getId(); } } else { if (!this->rootBone) return -1; Bone* bone = new Bone(nextId++); bone->setPosition(pos); bone->setRotation(rot); if (rootBone->addChildBone(parentId, bone)) { return bone->getId(); } else { nextId--; return -1; } } } //! calculates the matrices of all bones in this sceleton //! \param modelMatrix the already calculated matrix of the used 3d //! model \param matBuffer the array to store the calculated matrixes //! \param scaleFactor the scaling of the object //! \param camMatrix the view-projection matrix of the used camera int Skeleton::calculateMatrix(const Mat4& modelMatrix, Mat4* matBuffer, float scaleFactor, const Mat4& kamMatrix) { rootBone->calculateMatrix(modelMatrix, matBuffer, scaleFactor, kamMatrix); return nextId; } //! \return the radius of the sceleton float Skeleton::getRadius() const { if (rootBone) return rootBone->getRadius(); return 0; } //! \return the root bone of the sceleton Bone* Framework::Skeleton::zRootBone() const { return rootBone; } //! \return the bone with a specific id Bone* Framework::Skeleton::zBone(int id) const { if (!rootBone) return 0; return zBone(rootBone, id); } //! \return a deep copy of the sceleton Skeleton* Skeleton::copySceleton() const { Skeleton* ret = new Skeleton(); ret->nextId = nextId; if (rootBone) ret->rootBone = rootBone->copyBone(); return ret; } //! \return the next id for a bone ther can be only MAX_KNOCHEN_ANZ //! bones in a sceleton. if the sceleton is full -1 is returned int Framework::Skeleton::getNextBoneId() const { return nextId; } // Contents of the Polygon3D struct // Constructor Polygon3D::Polygon3D() { indexAnz = 0; indexList = 0; } // Destructor Polygon3D::~Polygon3D() { delete[] indexList; } // Contents of the Model3DData class // Constructor Model3DData::Model3DData( DXBuffer* dxVertexBuffer, DXBuffer* dxIndexBuffer, int id) : ReferenceCounter(), dxIndexBuffer(dxIndexBuffer), dxVertexBuffer(dxVertexBuffer), id(id) { skelett = 0; vertexList = 0; vertexCount = 0; polygons = new Array(); ambientFactor = 1.f; diffusFactor = 0.f; specularFactor = 0.f; indexCount = 0; indexBuffer = 0; radius = 0; } // Destructor Model3DData::~Model3DData() { clearModel(); polygons->release(); if (dxIndexBuffer) { dxIndexBuffer->release(); } if (dxVertexBuffer) { dxVertexBuffer->release(); } delete[] indexBuffer; } // updates the DX Buffer gpu memory if changed DLLEXPORT void Model3DData::updateGPUMemory() { dxIndexBuffer->copieren(); dxVertexBuffer->copieren(); } // Deletes all model data void Model3DData::clearModel() { delete[] vertexList; vertexCount = 0; vertexList = 0; for (Polygon3D* i : *polygons) delete i; polygons->leeren(); if (skelett) skelett->release(); skelett = 0; radius = 0; delete[] indexBuffer; indexBuffer = 0; indexCount = 0; } // Calculates the normals for the vertices of the model void Model3DData::calculateNormals() { for (int i = 0; i < vertexCount; i++) { Vec3 normal(0, 0, 0); for (Polygon3D* p : *polygons) { int begin = 0; for (int j = 0; j < p->indexAnz; j++) { if (j % 3 == 0) begin = j; if (p->indexList[j] == i) { Vec3 a = vertexList[p->indexList[begin]].pos; Vec3 b = vertexList[p->indexList[begin + 1]].pos; Vec3 c = vertexList[p->indexList[begin + 2]].pos; normal += (b - a).crossProduct(c - a).normalize(); normal.normalize(); } } } vertexList[i].normal = normal; } } //! Creates a buffer for all polygon indices void Model3DData::buildIndexBuffer() { delete[] indexBuffer; indexCount = 0; for (Polygon3D* p : *polygons) indexCount += p->indexAnz; indexBuffer = new int[indexCount]; int current = 0; for (Polygon3D* p : *polygons) { memcpy(indexBuffer + current, p->indexList, sizeof(int) * p->indexAnz); current += p->indexAnz; } if (dxIndexBuffer) { dxIndexBuffer->setLength((int)(indexCount * sizeof(int))); dxIndexBuffer->setData(indexBuffer); } } // Sets the pointer to a default skeleton // s: The skeleton to be used void Model3DData::setSkelettZ(Skeleton* s) { if (skelett) skelett->release(); skelett = s; } // Sets a pointer to a list with all vertices of the model // vertexList: An array of vertices // anz: The number of vertices in the array void Model3DData::setVertecies(Vertex3D* vertexList, int anz) { delete[] this->vertexList; this->vertexList = vertexList; vertexCount = anz; maxPos = {-INFINITY, -INFINITY, -INFINITY}; minPos = {INFINITY, INFINITY, INFINITY}; radius = 0; for (int i = 0; i < anz; i++) { float r = vertexList[i].pos.getLength(); if (r > radius) radius = r; if (vertexList[i].pos.x < minPos.x) minPos.x = vertexList[i].pos.x; if (vertexList[i].pos.y < minPos.y) minPos.y = vertexList[i].pos.y; if (vertexList[i].pos.z < minPos.z) minPos.z = vertexList[i].pos.z; if (vertexList[i].pos.x > maxPos.x) maxPos.x = vertexList[i].pos.x; if (vertexList[i].pos.y > maxPos.y) maxPos.y = vertexList[i].pos.y; if (vertexList[i].pos.z > maxPos.z) maxPos.z = vertexList[i].pos.z; vertexList[i].id = i; } if (dxVertexBuffer) { dxVertexBuffer->setLength((int)(anz * sizeof(Vertex3D))); dxVertexBuffer->setData(vertexList); } } // Adds a polygon to the model // polygon: The polygon to be added void Model3DData::addPolygon(Polygon3D* polygon) { polygons->add(polygon); buildIndexBuffer(); } // Sets the factor by which the ambient light (texture color) is multiplied // f: the new factor (from 0 to 1, ambient + specular + diffuse = 1) void Model3DData::setAmbientFactor(float f) { ambientFactor = f; } // Sets the factor by which the light color from light sources is multiplied // f: the new factor (from 0 to 1, ambient + specular + diffuse = 1) void Model3DData::setDiffusFactor(float f) { diffusFactor = f; } // Sets the factor by which the reflection from light sources is multiplied // f: the new factor (from 0 to 1, ambient + specular + diffuse = 1) void Model3DData::setSpecularFactor(float f) { specularFactor = f; } // Converts a 2D model to 3D // model: The 2D model to be converted to 3D // z: The z coordinate of all points of the model void Model3DData::copyModel2D(Model2DData* model, float z) { if (model && model->vListen && model->polygons) { clearModel(); int vAnz = 0; for (const Polygon2D& p : *model->polygons) vAnz += p.vertex->getEintragAnzahl(); Vertex3D* vertexList = new Vertex3D[vAnz]; int index = 0; for (auto i : *model->vListen) { Polygon3D* p = new Polygon3D(); p->indexAnz = 0; for (auto j : *i) { for (auto k = j->zListe()->begin(); k && k.hasNext() && k.next().hasNext(); k++) p->indexAnz += 3; } p->indexList = new int[p->indexAnz]; p->indexAnz = 0; for (auto j : *i) { for (auto k = j->zListe()->begin(); k; k++) { assert(index < vAnz); if (index < vAnz) { vertexList[index].pos = Vec3(k->punkt->x, k->punkt->y, z); vertexList[index].tPos = (Vec2)*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++; } } else break; index++; } } addPolygon(p); } this->setVertecies(vertexList, vAnz); buildIndexBuffer(); calculateNormals(); } } // Removes a polygon // index: The index of the polygon void Model3DData::removePolygon(int index) { if (!polygons->hat(index)) return; delete polygons->get(index); polygons->remove(index); buildIndexBuffer(); } // Calculates the bone matrices // modelMatrix: The matrix that transforms the skeleton into world space // matBuffer: An array of matrices to be filled with bone matrices // scaleFactor: The scaling of the model kamMatrix: The combination of // the view and projection matrices return: returns the number of used // matrices int Model3DData::kalkulateMatrix(const Mat4& modelMatrix, Mat4* matBuffer, float scaleFactor, const Mat4& kamMatrix) const { if (!skelett) return 0; return skelett->calculateMatrix( modelMatrix, matBuffer, scaleFactor, kamMatrix); } // Returns the number of polygons int Model3DData::getPolygonAnzahl() const { return polygons->getEintragAnzahl(); } // Returns a specific polygon // index: The index of the polygon Polygon3D* Model3DData::getPolygon(int index) const { if (!polygons->hat(index)) return 0; return polygons->get(index); } // Returns an iterator to list the polygons ArrayIterator Model3DData::getPolygons() const { return polygons->begin(); } // Returns the radius of a sphere that encloses the entire model float Model3DData::getRadius() const { return radius; } // Returns the id of the data if registered in a Model3DList // (see Framework::zM3DRegister()) int Model3DData::getId() const { return id; } // Returns the factor by which the ambient light (texture color) is multiplied float Model3DData::getAmbientFactor() const { return ambientFactor; } // Returns the factor by which the light color from light sources is multiplied float Model3DData::getDiffusFactor() const { return diffusFactor; } // Returns the factor by which the reflection from light sources is multiplied float Model3DData::getSpecularFactor() const { return specularFactor; } // Returns a copy of the skeleton that can be used for animations Skeleton* Model3DData::copySkelett() const { return skelett ? skelett->copySceleton() : 0; } // Returns the number of vertices int Model3DData::getVertexAnzahl() const { return vertexCount; } // Returns a buffer with all vertices of the model const Vertex3D* Model3DData::zVertexBuffer() const { return vertexList; } //! Returns a reference to the beginning of the index buffer const int* Model3DData::getIndexBuffer() const { return indexBuffer; } //! Returns the number of indices in the index buffer int Model3DData::getIndexCount() const { return indexCount; } //! Returns the index buffer DXBuffer* Model3DData::zDXIndexBuffer() const { return dxIndexBuffer; } //! Returns the vertex buffer DXBuffer* Model3DData::zDXVertexBuffer() const { return dxVertexBuffer; } //! Returns the minimum point of the bounding box of the model Vec3 Model3DData::getMinPos() const { return minPos; } //! Returns the maximum point of the bounding box of the model Vec3 Model3DData::getMaxPos() const { return maxPos; } // Contents of the Model3DTextur class // Constructor Model3DTextur::Model3DTextur() : ReferenceCounter() { textures = new Textur*[1]; textures[0] = 0; textureCount = 1; } // Destructor Model3DTextur::~Model3DTextur() { for (int i = 0; i < textureCount; i++) { if (textures[i]) textures[i]->release(); } delete[] textures; } // Sets which texture is for which polygon // pI: The index of the polygon // txt: The texture of the polygon void Model3DTextur::setPolygonTextur(int pI, Textur* txt) { if (pI >= textureCount) { Textur** tmp = textures; textures = new Textur*[pI + 1]; memcpy(textures, tmp, sizeof(Textur*) * textureCount); memset(textures + textureCount, 0, sizeof(Textur*) * (pI + 1 - textureCount)); delete[] tmp; textureCount = pI + 1; } if (textures[pI]) textures[pI]->release(); textures[pI] = txt; } // Returns a pointer to the texture of a polygon without increased // reference counter // i: The index of the polygon Textur* Model3DTextur::zPolygonTextur(int i) const { if (i >= textureCount) return 0; return textures[i]; } // Contents of the Model3D class // Constructor Model3D::Model3D() : Zeichnung3D() { model = 0; textur = 0; skelett = 0; ambientFactor = 1.f; diffusFactor = 0.f; specularFactor = 0.f; } // Destructor Model3D::~Model3D() { if (model) model->release(); if (textur) textur->release(); if (skelett) skelett->release(); } // Sets the model data // data: The data void Model3D::setModelDaten(Model3DData* data) { if (model) model->release(); if (skelett) skelett = (Skeleton*)skelett->release(); model = data; if (model) { skelett = model->copySkelett(); this->ambientFactor = model->getAmbientFactor(); this->specularFactor = model->getSpecularFactor(); this->diffusFactor = model->getDiffusFactor(); } } // Sets the textures to be used for drawing // txt: A list of textures assigned to the different polygons void Model3D::setModelTextur(Model3DTextur* txt) { if (textur) textur->release(); textur = txt; } // Sets the factor by which the ambient light (texture color) is multiplied // f: the new factor (from 0 to 1, ambient + specular + diffuse = 1) void Framework::Model3D::setAmbientFactor(float f) { this->ambientFactor = f; } // Sets the factor by which the light color from light sources is multiplied // f: the new factor (from 0 to 1, ambient + specular + diffuse = 1) void Framework::Model3D::setDiffusFactor(float f) { diffusFactor = f; } // Sets the factor by which the ambient light (texture color) is multiplied // f: the new factor (from 0 to 1, ambient + specular + diffuse = 1) void Framework::Model3D::setSpecularFactor(float f) { specularFactor = f; } // Calculates the matrices of all bones of the model's skeleton // viewProj: The multiplied camera matrices // matBuffer: An array of matrices to be filled // return: The number of matrices the model requires int Model3D::errechneMatrizen( const Mat4& viewProj, Mat4* matBuffer) { int ret = 0; if (skelett) ret = skelett->calculateMatrix(welt, matBuffer, size, viewProj); else if (model) ret = model->kalkulateMatrix(welt, matBuffer, size, viewProj); if (!ret) return Zeichnung3D::errechneMatrizen(viewProj, matBuffer); return ret; } // Processes elapsed time // tickval: The time in seconds that has passed since the last call // return: true if the object has changed, false otherwise. bool Model3D::tick(double tickval) { radius = model ? model->getRadius() : 0; if (skelett) { radius += skelett->getRadius(); } return Zeichnung3D::tick(tickval); } //! for updating shader data void Model3D::beforeRender( GraphicsApi* api, Shader* zVertexShader, Shader* zPixelShader) {} void Model3D::afterRender( GraphicsApi* api, Shader* zVertexShader, Shader* zPixelShader) {} // Returns the texture Model3DTextur* Model3D::getTextur() { return textur ? dynamic_cast(textur->getThis()) : 0; } // Returns the texture (without increased reference counter) Model3DTextur* Model3D::zTextur() { return textur; } // Returns the model data Model3DData* Model3D::getModelData() { return model ? dynamic_cast(model->getThis()) : 0; } // Returns the model data (without increased reference counter) Model3DData* Model3D::zModelData() { return model; } // Checks if a ray hits this object // point: the start point of the ray in world coordinates // dir: the direction of the ray in world coordinates // maxSqDist: The maximum squared distance allowed // pId: the id of the polygon the intersection belongs to // return: the squared distance of the intersection to the ray origin // or -1 if no intersection exists float Model3D::traceRay( const Vec3& p, const Vec3& d, float maxSqDist, int& pId) const { if (!model) return -1; Vec3 dir = d; dir.rotateY(-angle.y); dir.rotateX(-angle.x); dir.rotateZ(-angle.z); Vec3 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)) // no intersection exists 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 a = model->zVertexBuffer()[p->indexList[j]].pos; Vec3 b = model->zVertexBuffer()[p->indexList[j + 1]].pos; Vec3 c = model->zVertexBuffer()[p->indexList[j + 2]].pos; Vec3 normal = (b - a).crossProduct(c - a).normalize(); if (normal * dir < 0) // Check if the normal points towards // the ray origin { nearest = (a * normal - point * normal) / (dir * normal); Vec3 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; } // Calculates the color of the intersection point of a ray // point: the start point of the ray in world coordinates // dir: the direction of the ray in world coordinates // zWelt: the world the ray comes from // return: the color of the intersection point int Model3D::traceRay( Vec3& p, Vec3& d, int pId, Welt3D* zWelt) const { Vec3 dir = d; dir.rotateY(-angle.y); dir.rotateX(-angle.x); dir.rotateZ(-angle.z); Vec3 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& a = model->zVertexBuffer()[p->indexList[j]].pos; const Vec3& b = model->zVertexBuffer()[p->indexList[j + 1]].pos; const Vec3& 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 normal = (b - a).crossProduct(c - a).normalize(); float t = (a * normal - point * normal) / (dir * normal); Vec3 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; } // Returns the id of the data if registered in a Model3DList // (see Framework::zM3DRegister()) int Model3D::getDatenId() const { return model ? model->getId() : -1; } // Returns the factor by which the ambient light (texture color) is multiplied float Model3D::getAmbientFactor() const { return ambientFactor; } // Returns the factor by which the light color from light sources is multiplied float Model3D::getDiffusFactor() const { return diffusFactor; } // Returns the factor by which the reflection from light sources is multiplied float Model3D::getSpecularFactor() const { return specularFactor; } // Returns the number of vertices int Model3D::getVertexAnzahl() const { return model ? model->getVertexAnzahl() : 0; } // Returns a buffer with all vertices of the model const Vertex3D* Model3D::zVertexBuffer() const { return model ? model->zVertexBuffer() : 0; } //! Returns true if a specific polygon needs to be rendered bool Model3D::needRenderPolygon(int index) { return 1; } Textur* Model3D::zEffectTextur() { return 0; } float Model3D::getEffectPercentage() { return 0; }