Skip to content

Instantly share code, notes, and snippets.

@victorholt
Last active June 27, 2017 03:50
Show Gist options
  • Save victorholt/955ae2f76173d1a5d3b9f65fefcbb47e to your computer and use it in GitHub Desktop.
Save victorholt/955ae2f76173d1a5d3b9f65fefcbb47e to your computer and use it in GitHub Desktop.
CustomMesh Src Refactored
//
// Created by Victor Holt on 3/18/2017.
//
#include <gs-graphics/CustomMesh.h>
using namespace Urho3D;
using namespace Sencha;
//! Constructor.
//! \param context
//! \param isDynamic
CustomMesh::CustomMesh(Context* context, bool isDynamic)
: Object(context)
{
scale_ = 1.0f;
isDynamic_ = isDynamic;
calcNormals_ = false;
calcTangents_ = false;
model_ = nullptr;
geometry_ = nullptr;
vertexBuffer_ = nullptr;
indexBuffer_ = nullptr;
manualAddIndex_ = false;
useLargeIndices_ = false;
useRawDataOnly_ = false;
}
//! Destructor.
CustomMesh::~CustomMesh()
{
// Delete the faces.
for (auto face : faces_) {
delete face;
}
faces_.Clear();
for (auto vert : meshVertexList_) {
delete vert;
}
meshVertexList_.Clear();
vertexData_.Clear();
indexData_.Clear();
}
//! Adds a face to the mesh.
//! \param v0
//! \param v1
//! \param v2
MeshFace* CustomMesh::AddFace(const Vector3& v0, const Vector3& v1, const Vector3& v2, const MeshUVType& uvType)
{
MeshVertex* vert0 = AddVertex(v0, uvType);
MeshVertex* vert1 = AddVertex(v1, uvType);
MeshVertex* vert2 = AddVertex(v2, uvType);
MeshFace* face = CreateFace(vert0, vert1, vert2);
return face;
}
//! Adds a vertex.
//! \param v
//! \param uvType
MeshVertex* CustomMesh::AddVertex(const Vector3& v, const MeshUVType& uvType)
{
MutexLock lock(meshLock_);
MeshVertex* vert = new MeshVertex();
vert->vertex_ = v;
vert->normal_ = Vector3::ZERO;
vert->uv_ = Vector2::ZERO;
vert->tangent_ = Vector4::ZERO;
vert->uvType_ = uvType;
meshVertexList_.Push(vert);
useRawDataOnly_ = false;
return vert;
}
//! Adds an index.
//! \param index
void CustomMesh::AddIndex(uint16_t index)
{
MutexLock lock(meshLock_);
indexData_.Push(index);
manualAddIndex_ = true;
useLargeIndices_ = false;
}
//! Adds an index.
//! \param index
void CustomMesh::AddLargeIndex(uint32_t index)
{
MutexLock lock(meshLock_);
largeIndexData_.Push(index);
useLargeIndices_ = true;
manualAddIndex_ = true;
}
//! Sets the face of the mesh based on the given index.
//! \param i
//! \param face
void CustomMesh::SetFace(unsigned int i, const MeshFace& face)
{
MutexLock lock(meshLock_);
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)
{
MutexLock lock(meshLock_);
if (updateNormals) {
CalculateNormals(false);
}
GenerateVertexData();
if (updateTangents) {
CalculateTangents(false);
UpdateVertexData();
}
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);
indexBuffer_->SetShadowed(true);
uint32_t numIndices = 0;
if (useRawDataOnly_) {
numIndices = rawData_.numIndices_;
PODVector<VertexElement> elements = rawData_.elements_;
vertexBuffer_->SetSize(rawData_.numVerts_, rawData_.elements_, isDynamic_);
indexBuffer_->SetSize(rawData_.numIndices_, useLargeIndices_, isDynamic_);
vertexBuffer_->SetData(rawData_.vertexData_);
if (useLargeIndices_) {
indexBuffer_->SetData(rawData_.largeIndexData_);
} else {
indexBuffer_->SetData(rawData_.indexData_);
}
model_->SetBoundingBox(rawData_.boundingBox_);
} else {
if (useLargeIndices_) {
numIndices = largeIndexData_.Size();
} else {
numIndices = indexData_.Size();
}
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(numIndices, elements, isDynamic_);
indexBuffer_->SetSize(numIndices, useLargeIndices_, isDynamic_);
vertexBuffer_->SetData(vertexData_.Buffer());
if (useLargeIndices_) {
indexBuffer_->SetData(largeIndexData_.Buffer());
} else {
indexBuffer_->SetData(indexData_.Buffer());
}
model_->SetBoundingBox(boundingBox_);
}
geometry_->SetNumVertexBuffers(1);
geometry_->SetVertexBuffer(0, vertexBuffer_);
geometry_->SetIndexBuffer(indexBuffer_);
geometry_->SetDrawRange(TRIANGLE_LIST, 0, numIndices);
model_->SetNumGeometries(1);
model_->SetGeometry(0, 0, geometry_);
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);
}
void CustomMesh::GenerateVertexData()
{
if (useRawDataOnly_) return;
boundingBox_.Clear();
if (!manualAddIndex_) {
if (useLargeIndices_) {
largeIndexData_.Clear();
largeIndexData_.Reserve(meshVertexList_.Size() / 3);
} else {
indexData_.Clear();
indexData_.Reserve(meshVertexList_.Size() / 3);
}
}
// Create faces.
uint16_t lastFaceVertIndexStart = 0;
unsigned faceIndex = 0;
for (unsigned int i = 0; i < meshVertexList_.Size(); i += 3) {
MeshVertex* v0 = meshVertexList_[i];
MeshVertex* v1 = meshVertexList_[i + 1];
MeshVertex* v2 = meshVertexList_[i + 2];
boundingBox_.Merge(v0->vertex_);
boundingBox_.Merge(v1->vertex_);
boundingBox_.Merge(v2->vertex_);
if (faces_.Size() > 0) {
MeshFace* face = faces_[faceIndex];
if (!IsNullObject(face)) {
face->vertIndexStart_ = i * 12;
faceIndex++;
}
}
if (!manualAddIndex_) {
if (useLargeIndices_) {
largeIndexData_.Push(lastFaceVertIndexStart);
largeIndexData_.Push(static_cast<uint32_t>(lastFaceVertIndexStart + 1));
largeIndexData_.Push(static_cast<uint32_t>(lastFaceVertIndexStart + 2));
} else {
indexData_.Push(lastFaceVertIndexStart);
indexData_.Push(static_cast<uint16_t>(lastFaceVertIndexStart + 1));
indexData_.Push(static_cast<uint16_t>(lastFaceVertIndexStart + 2));
}
lastFaceVertIndexStart += 3;
}
}
// Update vertex data.
UpdateVertexData();
}
void CustomMesh::UpdateVertexData()
{
if (useRawDataOnly_) return;
vertexData_.Clear();
// MeshVertex = 12
vertexData_.Reserve(meshVertexList_.Size() * 12);
// Update vertex data.
int numFaces = faces_.Size();
for (unsigned int i = 0; i < numFaces; i++) {
// Update vertices.
// Face #1
vertexData_.Push(faces_[i]->v0_->vertex_.x_);
vertexData_.Push(faces_[i]->v0_->vertex_.y_);
vertexData_.Push(faces_[i]->v0_->vertex_.z_);
vertexData_.Push(faces_[i]->v0_->normal_.x_);
vertexData_.Push(faces_[i]->v0_->normal_.y_);
vertexData_.Push(faces_[i]->v0_->normal_.z_);
vertexData_.Push(faces_[i]->v0_->uv_.x_);
vertexData_.Push(faces_[i]->v0_->uv_.y_);
vertexData_.Push(faces_[i]->v0_->tangent_.x_);
vertexData_.Push(faces_[i]->v0_->tangent_.y_);
vertexData_.Push(faces_[i]->v0_->tangent_.z_);
vertexData_.Push(faces_[i]->v0_->tangent_.w_);
// Face #2
vertexData_.Push(faces_[i]->v1_->vertex_.x_);
vertexData_.Push(faces_[i]->v1_->vertex_.y_);
vertexData_.Push(faces_[i]->v1_->vertex_.z_);
vertexData_.Push(faces_[i]->v1_->normal_.x_);
vertexData_.Push(faces_[i]->v1_->normal_.y_);
vertexData_.Push(faces_[i]->v1_->normal_.z_);
vertexData_.Push(faces_[i]->v1_->uv_.x_);
vertexData_.Push(faces_[i]->v1_->uv_.y_);
vertexData_.Push(faces_[i]->v1_->tangent_.x_);
vertexData_.Push(faces_[i]->v1_->tangent_.y_);
vertexData_.Push(faces_[i]->v1_->tangent_.z_);
vertexData_.Push(faces_[i]->v1_->tangent_.w_);
// Face #3
vertexData_.Push(faces_[i]->v2_->vertex_.x_);
vertexData_.Push(faces_[i]->v2_->vertex_.y_);
vertexData_.Push(faces_[i]->v2_->vertex_.z_);
vertexData_.Push(faces_[i]->v2_->normal_.x_);
vertexData_.Push(faces_[i]->v2_->normal_.y_);
vertexData_.Push(faces_[i]->v2_->normal_.z_);
vertexData_.Push(faces_[i]->v2_->uv_.x_);
vertexData_.Push(faces_[i]->v2_->uv_.y_);
vertexData_.Push(faces_[i]->v2_->tangent_.x_);
vertexData_.Push(faces_[i]->v2_->tangent_.y_);
vertexData_.Push(faces_[i]->v2_->tangent_.z_);
vertexData_.Push(faces_[i]->v2_->tangent_.w_);
}
}
MeshFace* CustomMesh::CreateFace(MeshVertex* v0, MeshVertex* v1, MeshVertex* v2)
{
MeshFace* face = new MeshFace();
face->v0_ = v0;
face->v1_ = v1;
face->v2_ = v2;
MeshUVType uvType = v0->uvType_;
if (uvType == MeshUVType::UV_XY) {
face->v0_->uv_ = Vector2(v0->vertex_.x_ / scale_, v0->vertex_.y_ / scale_);
face->v1_->uv_ = Vector2(v1->vertex_.x_ / scale_, v1->vertex_.y_ / scale_);
face->v2_->uv_ = Vector2(v2->vertex_.x_ / scale_, v2->vertex_.y_ / scale_);
} else if (uvType == MeshUVType::UV_XZ) {
face->v0_->uv_ = Vector2(v0->vertex_.x_ / scale_, v0->vertex_.z_ / scale_);
face->v1_->uv_ = Vector2(v1->vertex_.x_ / scale_, v1->vertex_.z_ / scale_);
face->v2_->uv_ = Vector2(v2->vertex_.x_ / scale_, v2->vertex_.z_ / scale_);
} else if (uvType == MeshUVType::UV_YZ) {
face->v0_->uv_ = Vector2(v0->vertex_.y_ / scale_, v0->vertex_.z_ / scale_);
face->v1_->uv_ = Vector2(v1->vertex_.y_ / scale_, v1->vertex_.z_ / scale_);
face->v2_->uv_ = Vector2(v2->vertex_.y_ / scale_, v2->vertex_.z_ / scale_);
} else if (uvType == MeshUVType::UV_ZY) {
face->v0_->uv_ = Vector2(v0->vertex_.z_ / scale_, v0->vertex_.y_ / scale_);
face->v1_->uv_ = Vector2(v1->vertex_.z_ / scale_, v1->vertex_.y_ / scale_);
face->v2_->uv_ = Vector2(v2->vertex_.z_ / scale_, v2->vertex_.y_ / scale_);
}
face->vertIndexStart_ = 0;
faces_.Push(face);
return face;
}
//! Updates the mesh.
//! \param updateNormals
//! \param updateTangents
void CustomMesh::UpdateMesh(bool updateNormals, bool updateTangents)
{
MutexLock lock(meshLock_);
if (updateNormals) {
CalculateNormals(false);
}
GenerateVertexData();
if (updateTangents) {
CalculateTangents(false);
UpdateVertexData();
}
unsigned char* vertexData = (unsigned char*)vertexBuffer_->Lock(0, vertexBuffer_->GetVertexCount());
if (!vertexData) return;
uint32_t numIndices = 0;
if (useRawDataOnly_) {
PODVector<VertexElement> elements = rawData_.elements_;
vertexBuffer_->SetSize(rawData_.numVerts_, rawData_.elements_, isDynamic_);
indexBuffer_->SetSize(rawData_.numIndices_, useLargeIndices_, isDynamic_);
vertexBuffer_->SetData(rawData_.vertexData_);
if (useLargeIndices_) {
indexBuffer_->SetData(rawData_.largeIndexData_);
} else {
indexBuffer_->SetData(rawData_.indexData_);
}
model_->SetBoundingBox(rawData_.boundingBox_);
} else {
numIndices = faces_.Size() * 3;
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(numIndices, elements, isDynamic_);
indexBuffer_->SetSize(numIndices, useLargeIndices_, isDynamic_);
vertexBuffer_->SetData(vertexData_.Buffer());
if (useLargeIndices_) {
indexBuffer_->SetData(largeIndexData_.Buffer());
} else {
indexBuffer_->SetData(indexData_.Buffer());
}
model_->SetBoundingBox(boundingBox_);
}
vertexBuffer_->Unlock();
}
//! Updates the bounding box for the mesh.
void CustomMesh::UpdateBoundingBox()
{
MutexLock lock(calcMeshLock_);
boundingBox_.Clear();
for (unsigned int i = 0; i < meshVertexList_.Size(); i += 3) {
boundingBox_.Merge(meshVertexList_[i]->vertex_);
boundingBox_.Merge(meshVertexList_[i + 1]->vertex_);
boundingBox_.Merge(meshVertexList_[i + 2]->vertex_);
}
}
//! Calculates the mesh normals.
//! \param check
void CustomMesh::CalculateNormals(bool check)
{
MutexLock lock(calcMeshLock_);
if (check && calcNormals_) return;
calcNormals_ = true;
for (unsigned int i = 0; i < meshVertexList_.Size(); i) {
MeshVertex* v0 = meshVertexList_[i++];
MeshVertex* v1 = meshVertexList_[i++];
MeshVertex* v2 = meshVertexList_[i++];
Vector3 edge1 = v0->vertex_ - v1->vertex_;
Vector3 edge2 = v0->vertex_ - v2->vertex_;
v0->normal_ = v1->normal_ = v2->normal_ = edge1.CrossProduct(edge2).Normalized();
}
}
//! Calculates the mesh tangents.
//! \param check
void CustomMesh::CalculateTangents(bool check)
{
if (useRawDataOnly_) return;
MutexLock lock(calcMeshLock_);
if (check && calcTangents_) return;
calcTangents_ = true;
float* vertData = vertexData_.Buffer();
uint16_t* indexData = indexData_.Buffer();
uint32_t* largeIndexData = largeIndexData_.Buffer();
if (IsNullObject(vertData) || IsNullObject(indexData)) return;
if (!useLargeIndices_) {
GenerateTangents(vertData, 12 * sizeof(float), indexData, sizeof(uint16_t), 0, faces_.Size() * 3,
3 * sizeof(float), 6 * sizeof(float), 8 * sizeof(float));
} else {
GenerateTangents(vertData, 12 * sizeof(float), largeIndexData, sizeof(uint32_t), 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]->v0_->tangent_ = *(reinterpret_cast<Vector4*>(&vertData[faces_[i]->vertIndexStart_ + 8]));
faces_[i]->v1_->tangent_ = *(reinterpret_cast<Vector4*>(&vertData[faces_[i]->vertIndexStart_ + 12]));
faces_[i]->v2_->tangent_ = *(reinterpret_cast<Vector4*>(&vertData[faces_[i]->vertIndexStart_ + 16]));
j += 3;
}
}
//! Updates the texture coordinates.
void CustomMesh::UpdateTexCoords()
{
MutexLock lock(calcMeshLock_);
for (unsigned int i = 0; i < faces_.Size(); i++) {
MeshFace* face = faces_[i];
MeshVertex* v0 = face->v0_;
MeshVertex* v1 = face->v1_;
MeshVertex* v2 = face->v2_;
MeshUVType uvType = face->v0_->uvType_;
if (uvType == MeshUVType::UV_XY) {
face->v0_->uv_ = Vector2(v0->vertex_.x_, v0->vertex_.y_);
face->v1_->uv_ = Vector2(v1->vertex_.x_, v1->vertex_.y_);
face->v2_->uv_ = Vector2(v2->vertex_.x_, v2->vertex_.y_);
} else if (uvType == MeshUVType::UV_XZ) {
face->v0_->uv_ = Vector2(v0->vertex_.x_, v0->vertex_.z_);
face->v1_->uv_ = Vector2(v1->vertex_.x_, v1->vertex_.z_);
face->v2_->uv_ = Vector2(v2->vertex_.x_, v2->vertex_.z_);
} else if (uvType == MeshUVType::UV_YZ) {
face->v0_->uv_ = Vector2(v0->vertex_.y_, v0->vertex_.z_);
face->v1_->uv_ = Vector2(v1->vertex_.y_, v1->vertex_.z_);
face->v2_->uv_ = Vector2(v2->vertex_.y_, v2->vertex_.z_);
} else if (uvType == MeshUVType::UV_ZY) {
face->v0_->uv_ = Vector2(v0->vertex_.z_, v0->vertex_.y_);
face->v1_->uv_ = Vector2(v1->vertex_.z_, v1->vertex_.y_);
face->v2_->uv_ = Vector2(v2->vertex_.z_, v2->vertex_.y_);
}
// if (uvType == MeshUVType::UV_XY) {
// face->v0_->uv_ = Vector2(face->v0_->vertex_.x_, face->v0_->vertex_.y_);
// face->v1_->uv_ = Vector2(face->v1_->vertex_.x_, face->v1_->vertex_.y_);
// face->v2_->uv_ = Vector2(face->v2_->vertex_.x_, face->v2_->vertex_.y_);
// } else if (uvType == MeshUVType::UV_XZ) {
// face->v0_->uv_ = Vector2(face->v0_->vertex_.x_, face->v0_->vertex_.z_);
// face->v1_->uv_ = Vector2(face->v1_->vertex_.x_, face->v1_->vertex_.z_);
// face->v2_->uv_ = Vector2(face->v2_->vertex_.x_, face->v2_->vertex_.z_);
// } else if (uvType == MeshUVType::UV_YZ) {
// face->v0_->uv_ = Vector2(face->v0_->vertex_.y_, face->v0_->vertex_.z_);
// face->v1_->uv_ = Vector2(face->v1_->vertex_.y_, face->v1_->vertex_.z_);
// face->v2_->uv_ = Vector2(face->v2_->vertex_.y_, face->v2_->vertex_.z_);
// } else if (uvType == MeshUVType::UV_ZY) {
// face->v0_->uv_ = Vector2(face->v0_->vertex_.z_, face->v0_->vertex_.y_);
// face->v1_->uv_ = Vector2(face->v1_->vertex_.z_, face->v1_->vertex_.y_);
// face->v2_->uv_ = Vector2(face->v2_->vertex_.z_, face->v2_->vertex_.y_);
// }
}
}
//! Clears all vertex data of the mesh.
void CustomMesh::Clear()
{
MutexLock lock(meshLock_);
for (auto face : faces_) {
delete face;
}
for (auto vert : meshVertexList_) {
delete vert;
}
faces_.Clear();
meshVertexList_.Clear();
vertexData_.Clear();
indexData_.Clear();
largeIndexData_.Clear();
boundingBox_.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_;
}
void CustomMesh::SetRawData(const MeshRawData& rawData)
{
rawData_ = rawData;
}
//! Returns the mesh vertex buffer.
//! \return
VertexBuffer* CustomMesh::GetVertexBuffer()
{
return vertexBuffer_;
}
//! Returns the mesh index buffer.
//! \return
IndexBuffer* CustomMesh::GetIndexBuffer()
{
return indexBuffer_;
}
void CustomMesh::RotateFace(MeshFace* face, const Quaternion& rot)
{
if (IsNullObject(face)) return;
MeshVertex* v0 = face->v0_;
MeshVertex* v1 = face->v1_;
MeshVertex* v2 = face->v2_;
Matrix3x4 transform = Matrix3x4::IDENTITY;
transform.SetRotation(rot.RotationMatrix());
BoundingBox boundingBox;
boundingBox.Merge(v0->vertex_);
boundingBox.Merge(v1->vertex_);
boundingBox.Merge(v2->vertex_);
Vector3 center = boundingBox.Center();
v0->vertex_ = transform.Rotation().Inverse() * (v0->vertex_ - center);
v1->vertex_ = transform.Rotation().Inverse() * (v1->vertex_ - center);
v2->vertex_ = transform.Rotation().Inverse() * (v2->vertex_ - center);
}
void CustomMesh::RotateFaceByIndex(unsigned faceIndex, const Quaternion& rot)
{
if (faces_.Size() < faceIndex) return;
MeshFace* face = faces_[faceIndex];
RotateFace(face, rot);
}
void CustomMesh::Rotate(const Quaternion& rot)
{
// Go through the vertices and rotate them.
transform_.SetRotation(rot.RotationMatrix());
for (unsigned int i = 0; i < meshVertexList_.Size(); i) {
MeshVertex* v0 = meshVertexList_[i++];
MeshVertex* v1 = meshVertexList_[i++];
MeshVertex* v2 = meshVertexList_[i++];
BoundingBox boundingBox;
boundingBox.Merge(v0->vertex_);
boundingBox.Merge(v1->vertex_);
boundingBox.Merge(v2->vertex_);
Vector3 center = boundingBox.Center();
v0->vertex_ = transform_.Rotation().Inverse() * (v0->vertex_ - center);
v1->vertex_ = transform_.Rotation().Inverse() * (v1->vertex_ - center);
v2->vertex_ = transform_.Rotation().Inverse() * (v2->vertex_ - center);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment