Last active
May 14, 2018 03:23
-
-
Save victorholt/bf2f50584095a47da6f3ca3654e93168 to your computer and use it in GitHub Desktop.
Urho3D Custom Mesh Source
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Created by Victor Holt on 3/18/2017. | |
// | |
#include <rogue-game/graphics/CustomMesh.h> | |
using namespace Urho3D; | |
using namespace Sencha; | |
//! Constructor. | |
//! \param context | |
//! \param isDynamic | |
CustomMesh::CustomMesh(Context* context, bool isDynamic) | |
: Object(context) | |
{ | |
isDynamic_ = isDynamic; | |
calcNormals_ = false; | |
calcTangents_ = false; | |
model_ = nullptr; | |
geometry_ = nullptr; | |
model_ = nullptr; | |
vertexBuffer_ = nullptr; | |
indexBuffer_ = nullptr; | |
} | |
//! Adds a face to the mesh. | |
//! \param v0 | |
//! \param v1 | |
//! \param v2 | |
void CustomMesh::AddFace(Vector3 v0, Vector3 v1, Vector3 v2, const MeshUVType& uvType) | |
{ | |
MeshFace* face = new MeshFace(); | |
face->v0_ = v0; | |
face->v1_ = v1; | |
face->v2_ = v2; | |
face->n0_ = Vector3::ZERO; | |
face->n1_ = Vector3::ZERO; | |
face->n2_ = Vector3::ZERO; | |
if (uvType == MeshUVType::UV_XY) { | |
face->uv0_ = Vector2(v0.x_, v0.y_); | |
face->uv1_ = Vector2(v1.x_, v1.y_); | |
face->uv2_ = Vector2(v2.x_, v2.y_); | |
} else if (uvType == MeshUVType::UV_XZ) { | |
face->uv0_ = Vector2(v0.x_, v0.z_); | |
face->uv1_ = Vector2(v1.x_, v1.z_); | |
face->uv2_ = Vector2(v2.x_, v2.z_); | |
} else if (uvType == MeshUVType::UV_YZ) { | |
face->uv0_ = Vector2(v0.y_, v0.z_); | |
face->uv1_ = Vector2(v1.y_, v1.z_); | |
face->uv2_ = Vector2(v2.y_, v2.z_); | |
} else if (uvType == MeshUVType::UV_ZY) { | |
face->uv0_ = Vector2(v0.z_, v0.y_); | |
face->uv1_ = Vector2(v1.z_, v1.y_); | |
face->uv2_ = Vector2(v2.z_, v2.y_); | |
} | |
face->tan0_ = Vector4::ZERO; | |
face->tan1_ = Vector4::ZERO; | |
face->tan2_ = Vector4::ZERO; | |
face->uvType_ = uvType; | |
AddFace(face); | |
} | |
//! Adds a face to the mesh. | |
//! \param face | |
void CustomMesh::AddFace(MeshFace* face) | |
{ | |
UpdateVertexData(face); | |
faces_.Push(face); | |
} | |
//! Sets the face of the mesh based on the given index. | |
//! \param i | |
//! \param face | |
void CustomMesh::SetFace(unsigned int i, const MeshFace& face) | |
{ | |
faces_[i]->v0_ = face.v0_; | |
faces_[i]->v1_ = face.v1_; | |
faces_[i]->v2_ = face.v2_; | |
} | |
//! Returns the face of the mesh from a given index. | |
//! \param i | |
//! \return | |
MeshFace* CustomMesh::GetFace(unsigned int i) | |
{ | |
return faces_[i]; | |
} | |
//! Commits changes to the mesh. | |
//! \param updateNormals | |
//! \param updateTangents | |
void CustomMesh::Commit(bool updateNormals, bool updateTangents) | |
{ | |
if (IsNullObject(model_)) { | |
CreateMesh(updateNormals, updateTangents); | |
} else { | |
UpdateMesh(updateNormals, updateTangents); | |
} | |
} | |
//! Builds the mesh. | |
//! \param updateNormals | |
//! \param updateTangents | |
void CustomMesh::CreateMesh(bool updateNormals, bool updateTangents) | |
{ | |
if (updateNormals) { | |
CalculateNormals(false); | |
UpdateVertexData(); | |
} | |
if (updateTangents) CalculateTangents(false); | |
if(IsNullObject(model_)) | |
model_ = new Model(context_); | |
if (IsNullObject(vertexBuffer_)) | |
vertexBuffer_ = new VertexBuffer(context_); | |
if (IsNullObject(indexBuffer_)) | |
indexBuffer_ = new IndexBuffer(context_); | |
if (IsNullObject(geometry_)) | |
geometry_ = new Geometry(context_); | |
vertexBuffer_->SetShadowed(true); | |
PODVector<VertexElement> elements; | |
elements.Push(VertexElement(TYPE_VECTOR3, SEM_POSITION)); | |
elements.Push(VertexElement(TYPE_VECTOR3, SEM_NORMAL)); | |
elements.Push(VertexElement(TYPE_VECTOR2, SEM_TEXCOORD)); | |
elements.Push(VertexElement(TYPE_VECTOR4, SEM_TANGENT)); | |
vertexBuffer_->SetSize(faces_.Size() * 3, elements, isDynamic_); | |
vertexBuffer_->SetData(vertexData_.Buffer()); | |
indexBuffer_->SetShadowed(true); | |
indexBuffer_->SetSize(faces_.Size() * 3, false); | |
indexBuffer_->SetData(indexData_.Buffer()); | |
geometry_->SetNumVertexBuffers(1); | |
geometry_->SetVertexBuffer(0, vertexBuffer_); | |
geometry_->SetIndexBuffer(indexBuffer_); | |
geometry_->SetDrawRange(TRIANGLE_LIST, 0, faces_.Size() * 3); | |
model_->SetNumGeometries(1); | |
model_->SetGeometry(0, 0, geometry_); | |
model_->SetBoundingBox(boundingBox_); | |
Vector<SharedPtr<VertexBuffer>> vertexBuffers; | |
Vector<SharedPtr<IndexBuffer>> indexBuffers; | |
vertexBuffers.Push(vertexBuffer_); | |
indexBuffers.Push(indexBuffer_); | |
PODVector<unsigned> morphRangeStarts; | |
PODVector<unsigned> morphRangeCounts; | |
morphRangeStarts.Push(0); | |
morphRangeCounts.Push(0); | |
model_->SetVertexBuffers(vertexBuffers, morphRangeStarts, morphRangeCounts); | |
model_->SetIndexBuffers(indexBuffers); | |
} | |
//! Updates the vertex data from the face data. | |
void CustomMesh::UpdateVertexData() | |
{ | |
for (unsigned int i = 0; i < faces_.Size(); i++) { | |
// Update vertices. | |
vertexData_[faces_[i]->vertIndexStart_] = faces_[i]->v0_.x_; | |
vertexData_[faces_[i]->vertIndexStart_ + 1] = faces_[i]->v0_.y_; | |
vertexData_[faces_[i]->vertIndexStart_ + 2] = faces_[i]->v0_.z_; | |
vertexData_[(faces_[i]->vertIndexStart_ + 12)] = faces_[i]->v1_.x_; | |
vertexData_[(faces_[i]->vertIndexStart_ + 12) + 1] = faces_[i]->v1_.y_; | |
vertexData_[(faces_[i]->vertIndexStart_ + 12) + 2] = faces_[i]->v1_.z_; | |
vertexData_[(faces_[i]->vertIndexStart_ + 24)] = faces_[i]->v2_.x_; | |
vertexData_[(faces_[i]->vertIndexStart_ + 24) + 1] = faces_[i]->v2_.y_; | |
vertexData_[(faces_[i]->vertIndexStart_ + 24) + 2] = faces_[i]->v2_.z_; | |
// Update normals. | |
vertexData_[faces_[i]->vertIndexStart_ + 3] = faces_[i]->n0_.x_; | |
vertexData_[faces_[i]->vertIndexStart_ + 4] = faces_[i]->n0_.y_; | |
vertexData_[faces_[i]->vertIndexStart_ + 5] = faces_[i]->n0_.z_; | |
vertexData_[(faces_[i]->vertIndexStart_ + 12) + 3] = faces_[i]->n1_.x_; | |
vertexData_[(faces_[i]->vertIndexStart_ + 12) + 4] = faces_[i]->n1_.y_; | |
vertexData_[(faces_[i]->vertIndexStart_ + 12) + 5] = faces_[i]->n1_.z_; | |
vertexData_[(faces_[i]->vertIndexStart_ + 24) + 3] = faces_[i]->n2_.x_; | |
vertexData_[(faces_[i]->vertIndexStart_ + 24) + 4] = faces_[i]->n2_.y_; | |
vertexData_[(faces_[i]->vertIndexStart_ + 24) + 5] = faces_[i]->n2_.z_; | |
// Update texture coords. | |
vertexData_[faces_[i]->vertIndexStart_ + 6] = faces_[i]->uv0_.x_; | |
vertexData_[faces_[i]->vertIndexStart_ + 7] = faces_[i]->uv0_.y_; | |
vertexData_[(faces_[i]->vertIndexStart_ + 12) + 6] = faces_[i]->uv1_.x_; | |
vertexData_[(faces_[i]->vertIndexStart_ + 12) + 7] = faces_[i]->uv1_.y_; | |
vertexData_[(faces_[i]->vertIndexStart_ + 24) + 6] = faces_[i]->uv2_.x_; | |
vertexData_[(faces_[i]->vertIndexStart_ + 24) + 7] = faces_[i]->uv2_.y_; | |
} | |
} | |
//! Updates the vertex data from a new mesh face. | |
//! \param face | |
void CustomMesh::UpdateVertexData(MeshFace*& newMeshFace) | |
{ | |
unsigned lastFace = faces_.Size() * 3; | |
newMeshFace->vertIndexStart_ = vertexData_.Size(); | |
//Calculate BoundingBox | |
boundingBox_.Merge(newMeshFace->v0_); | |
boundingBox_.Merge(newMeshFace->v1_); | |
boundingBox_.Merge(newMeshFace->v2_); | |
//Assign Vertices | |
vertexData_.Push(newMeshFace->v0_.x_); | |
vertexData_.Push(newMeshFace->v0_.y_); | |
vertexData_.Push(newMeshFace->v0_.z_); | |
vertexData_.Push(newMeshFace->n0_.x_); | |
vertexData_.Push(newMeshFace->n0_.y_); | |
vertexData_.Push(newMeshFace->n0_.z_); | |
vertexData_.Push(newMeshFace->uv0_.x_); | |
vertexData_.Push(newMeshFace->uv0_.y_); | |
vertexData_.Push(newMeshFace->tan0_.x_); | |
vertexData_.Push(newMeshFace->tan0_.y_); | |
vertexData_.Push(newMeshFace->tan0_.z_); | |
vertexData_.Push(newMeshFace->tan0_.w_); | |
vertexData_.Push(newMeshFace->v1_.x_); | |
vertexData_.Push(newMeshFace->v1_.y_); | |
vertexData_.Push(newMeshFace->v1_.z_); | |
vertexData_.Push(newMeshFace->n1_.x_); | |
vertexData_.Push(newMeshFace->n1_.y_); | |
vertexData_.Push(newMeshFace->n1_.z_); | |
vertexData_.Push(newMeshFace->uv1_.x_); | |
vertexData_.Push(newMeshFace->uv1_.y_); | |
vertexData_.Push(newMeshFace->tan1_.x_); | |
vertexData_.Push(newMeshFace->tan1_.y_); | |
vertexData_.Push(newMeshFace->tan1_.z_); | |
vertexData_.Push(newMeshFace->tan1_.w_); | |
vertexData_.Push(newMeshFace->v2_.x_); | |
vertexData_.Push(newMeshFace->v2_.y_); | |
vertexData_.Push(newMeshFace->v2_.z_); | |
vertexData_.Push(newMeshFace->n2_.x_); | |
vertexData_.Push(newMeshFace->n2_.y_); | |
vertexData_.Push(newMeshFace->n2_.z_); | |
vertexData_.Push(newMeshFace->uv2_.x_); | |
vertexData_.Push(newMeshFace->uv2_.y_); | |
vertexData_.Push(newMeshFace->tan2_.x_); | |
vertexData_.Push(newMeshFace->tan2_.y_); | |
vertexData_.Push(newMeshFace->tan2_.z_); | |
vertexData_.Push(newMeshFace->tan2_.w_); | |
indexData_.Push(lastFace); | |
indexData_.Push(lastFace + 1); | |
indexData_.Push(lastFace + 2); | |
} | |
//! Updates the mesh. | |
//! \param updateNormals | |
//! \param updateTangents | |
void CustomMesh::UpdateMesh(bool updateNormals, bool updateTangents) | |
{ | |
if (updateNormals) CalculateNormals(false); | |
if (updateTangents) CalculateTangents(false); | |
unsigned char* vertexData = (unsigned char*)vertexBuffer_->Lock(0, vertexBuffer_->GetVertexCount()); | |
if (!vertexData) return; | |
UpdateVertexData(); | |
vertexBuffer_->SetSize(faces_.Size() * 3, vertexBuffer_->GetElements(), isDynamic_); | |
vertexBuffer_->SetData(vertexData_.Buffer()); | |
indexBuffer_->SetSize(faces_.Size() * 3, false); | |
indexBuffer_->SetData(indexData_.Buffer()); | |
vertexBuffer_->Unlock(); | |
UpdateBoundingBox(); | |
model_->SetBoundingBox(boundingBox_); | |
} | |
//! Updates the bounding box for the mesh. | |
void CustomMesh::UpdateBoundingBox() | |
{ | |
boundingBox_.Clear(); | |
for (unsigned int i = 0; i < faces_.Size(); i++) { | |
MeshFace* face = faces_[i]; | |
//Calculate BoundingBox | |
boundingBox_.Merge(face->v0_); | |
boundingBox_.Merge(face->v1_); | |
boundingBox_.Merge(face->v2_); | |
} | |
} | |
//! Calculates the mesh normals. | |
//! \param check | |
void CustomMesh::CalculateNormals(bool check) | |
{ | |
if (check && calcNormals_) return; | |
calcNormals_ = true; | |
for (unsigned int i = 0; i < faces_.Size(); i++) { | |
Vector3 edge1 = faces_[i]->v0_ - faces_[i]->v1_; | |
Vector3 edge2 = faces_[i]->v0_ - faces_[i]->v2_; | |
faces_[i]->n0_ = faces_[i]->n1_ = faces_[i]->n2_ = edge1.CrossProduct(edge2).Normalized(); | |
} | |
} | |
//! Calculates the mesh tangents. | |
//! \param check | |
void CustomMesh::CalculateTangents(bool check) | |
{ | |
if (check && calcTangents_) return; | |
calcTangents_ = true; | |
float* vertData = vertexData_.Buffer(); | |
unsigned short* indexData = indexData_.Buffer(); | |
if (IsNullObject(vertData) || IsNullObject(indexData)) return; | |
GenerateTangents(vertData, 12 * sizeof(float), indexData, sizeof(unsigned short), 0, faces_.Size() * 3, 3 * sizeof(float), 6 * sizeof(float), 8 * sizeof(float)); | |
unsigned j = 0; | |
for (unsigned int i = 0; i < faces_.Size(); i++) { | |
faces_[i]->tan0_ = *(reinterpret_cast<Vector4*>(&vertData[faces_[i]->vertIndexStart_ + 8])); | |
faces_[i]->tan1_ = *(reinterpret_cast<Vector4*>(&vertData[faces_[i]->vertIndexStart_ + 12])); | |
faces_[i]->tan2_ = *(reinterpret_cast<Vector4*>(&vertData[faces_[i]->vertIndexStart_ + 16])); | |
j += 3; | |
} | |
} | |
//! Updates the texture coordinates. | |
void CustomMesh::UpdateTexCoords() | |
{ | |
for (unsigned int i = 0; i < faces_.Size(); i++) { | |
MeshFace* face = faces_[i]; | |
if (face->uvType_ == MeshUVType::UV_XY) { | |
face->uv0_ = Vector2(face->v0_.x_, face->v0_.y_); | |
face->uv1_ = Vector2(face->v1_.x_, face->v1_.y_); | |
face->uv2_ = Vector2(face->v2_.x_, face->v2_.y_); | |
} else if (face->uvType_ == MeshUVType::UV_XZ) { | |
face->uv0_ = Vector2(face->v0_.x_, face->v0_.z_); | |
face->uv1_ = Vector2(face->v1_.x_, face->v1_.z_); | |
face->uv2_ = Vector2(face->v2_.x_, face->v2_.z_); | |
} else if (face->uvType_ == MeshUVType::UV_YZ) { | |
face->uv0_ = Vector2(face->v0_.y_, face->v0_.z_); | |
face->uv1_ = Vector2(face->v1_.y_, face->v1_.z_); | |
face->uv2_ = Vector2(face->v2_.y_, face->v2_.z_); | |
} else if (face->uvType_ == MeshUVType::UV_ZY) { | |
face->uv0_ = Vector2(face->v0_.z_, face->v0_.y_); | |
face->uv1_ = Vector2(face->v1_.z_, face->v1_.y_); | |
face->uv2_ = Vector2(face->v2_.z_, face->v2_.y_); | |
} | |
} | |
} | |
//! Clears all vertex data of the mesh. | |
void CustomMesh::Clear() | |
{ | |
for (unsigned int i = 0; i < faces_.Size(); i++) { | |
delete faces_[i]; | |
} | |
faces_.Clear(); | |
vertexData_.Clear(); | |
indexData_.Clear(); | |
} | |
//! Saves the mesh to a file. | |
//! \param path | |
void CustomMesh::Save(const String& path) | |
{ | |
if (IsNullObject(model_)) | |
{ | |
URHO3D_LOGERROR("CustomMesh::Save Mesh is not built"); | |
return; | |
} | |
File file(context_, path, FILE_WRITE); | |
if (file.IsOpen()) { | |
model_->Save(file); | |
} | |
file.Close(); | |
} | |
//! Returns the mesh geometry. | |
//! \return | |
Geometry* CustomMesh::GetGeometry() | |
{ | |
return geometry_; | |
} | |
//! Returns the mesh model. | |
//! \return | |
Model* CustomMesh::GetModel() | |
{ | |
return model_; | |
} | |
//! Returns the mesh vertex buffer. | |
//! \return | |
VertexBuffer* CustomMesh::GetVertexBuffer() | |
{ | |
return vertexBuffer_; | |
} | |
//! Returns the mesh index buffer. | |
//! \return | |
IndexBuffer* CustomMesh::GetIndexBuffer() | |
{ | |
return indexBuffer_; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment