Skip to content

Instantly share code, notes, and snippets.

@victorholt
Last active May 14, 2018 03:23
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save victorholt/bf2f50584095a47da6f3ca3654e93168 to your computer and use it in GitHub Desktop.
Save victorholt/bf2f50584095a47da6f3ca3654e93168 to your computer and use it in GitHub Desktop.
Urho3D Custom Mesh Source
//
// 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