ezResult Mesh::ComputeNormals() { ezStopwatch timer; const VertexDataStream* positionStreamRaw = GetDataStream(ezGALVertexAttributeSemantic::Position); if (positionStreamRaw == nullptr) { ezLog::Error("Can't compute vertex normals for the mesh '{0}', because it doesn't have vertex positions.", m_Name); return EZ_FAILURE; } const TypedVertexDataStreamView<ezVec3> positionStream(*positionStreamRaw); VertexDataStream* normalStreamRaw = AddDataStream(ezGALVertexAttributeSemantic::Normal, 3); TypedVertexDataStreamView_ReadWrite<ezVec3> normalStream(*normalStreamRaw); // Normals have same mapping as positions. normalStream->m_IndexToData = positionStreamRaw->m_IndexToData; // Reset all normals to zero. normalStream->m_Data.SetCountUninitialized(positionStreamRaw->m_Data.GetCount()); ezMemoryUtils::ZeroFill<char>(normalStream->m_Data.GetData(), normalStream->m_Data.GetCount()); // Compute unnormalized triangle normals and add them to all vertices. // This way large triangles have an higher influence on the vertex normal. for (const Triangle& triangle : m_Triangles) { const VertexIndex v0 = triangle.m_Vertices[0]; const VertexIndex v1 = triangle.m_Vertices[1]; const VertexIndex v2 = triangle.m_Vertices[2]; const ezVec3 p0 = positionStream.GetValue(v0); const ezVec3 p1 = positionStream.GetValue(v1); const ezVec3 p2 = positionStream.GetValue(v2); const ezVec3 d01 = p1 - p0; const ezVec3 d02 = p2 - p0; const ezVec3 triNormal = d01.CrossRH(d02); normalStream.SetValue(v0, normalStream.GetValue(v0) + triNormal); // (possible optimization: have a special addValue to avoid unnecessary lookup) normalStream.SetValue(v1, normalStream.GetValue(v1) + triNormal); normalStream.SetValue(v2, normalStream.GetValue(v2) + triNormal); } // Normalize normals. for (ezUInt32 n = 0; n < normalStream->m_Data.GetCount(); n += sizeof(ezVec3)) reinterpret_cast<ezVec3*>(&normalStream->m_Data[n])->NormalizeIfNotZero(); ezLog::Debug("Computed mesh normals ('{0}') in '{1}'s", m_Name, ezArgF(timer.GetRunningTotal().GetSeconds(), 2)); return EZ_SUCCESS; }
void Mesh::AddData(const Mesh& mesh, const ezTransform& transform) { ezMat4 transformMat = transform.GetAsMat4(); ezMat4 normalTransformMat = transformMat.GetInverse(0.0f).GetTranspose(); // Create new triangles. ezUInt32 oldTriangleCount = GetNumTriangles(); AddTriangles(mesh.GetNumTriangles()); ezArrayPtr<const Mesh::Triangle> sourceTriangles = mesh.GetTriangles(); ezArrayPtr<const Mesh::Triangle> targetTriangles = GetTriangles().GetSubArray(oldTriangleCount); EZ_ASSERT_DEBUG(sourceTriangles.GetCount() == targetTriangles.GetCount(), "Something is wrong with triangle allocation!"); for (auto it = mesh.m_VertexDataStreams.GetIterator(); it.IsValid(); ++it) { const VertexDataStream* sourceStream = it.Value(); VertexDataStream* targetStream = AddDataStream(static_cast<ezGALVertexAttributeSemantic::Enum>(it.Key()), sourceStream->GetNumElementsPerVertex(), sourceStream->GetElementType()); if (!targetStream) { ezLog::SeriousWarning("Cannot merge mesh {0} properly since it has a vertex data stream with semantic {1} that uses {2} elements " "instead of 'unkown' which is used by the merge target. Skipping this data stream.", mesh.m_Name, it.Key(), sourceStream->GetNumElementsPerVertex()); continue; } // Copy data. ezUInt32 targetBaseDataIndex = targetStream->m_Data.GetCount(); targetStream->m_Data.PushBackRange(sourceStream->m_Data); // Transform data. if (!transform.IsIdentical(ezTransform::IdentityTransform())) { const ezUInt32 attributeSize = targetStream->GetAttributeSize(); // Positions if (it.Key() == ezGALVertexAttributeSemantic::Position) { for (ezUInt32 i = targetBaseDataIndex; i < targetStream->m_Data.GetCount(); i += attributeSize) { ezVec3& pos = *reinterpret_cast<ezVec3*>(&targetStream->m_Data[i]); pos = transformMat.TransformPosition(pos); } } // Directions else if (it.Key() == ezGALVertexAttributeSemantic::Normal || it.Key() == ezGALVertexAttributeSemantic::Tangent || it.Key() == ezGALVertexAttributeSemantic::BiTangent) { for (ezUInt32 i = targetBaseDataIndex; i < targetStream->m_Data.GetCount(); i += attributeSize) { ezVec3& dir = *reinterpret_cast<ezVec3*>(&targetStream->m_Data[i]); dir = normalTransformMat.TransformDirection(dir); } } } // Set mapping for (ezUInt32 tri = 0; tri < sourceTriangles.GetCount(); ++tri) { for (int v = 0; v < 3; ++v) { VertexDataIndex sourceDataIndex = sourceStream->GetDataIndex(sourceTriangles[tri].m_Vertices[v]); if (sourceDataIndex.IsValid()) targetStream->SetDataIndex(targetTriangles[tri].m_Vertices[v], targetBaseDataIndex + sourceDataIndex.GetValue()); } } } // Add submeshes. ezUInt32 oldSubMeshCount = m_SubMeshes.GetCount(); m_SubMeshes.PushBackRange(mesh.m_SubMeshes); for (ezUInt32 i = oldSubMeshCount; i < m_SubMeshes.GetCount(); ++i) { m_SubMeshes[i].m_uiFirstTriangle += oldTriangleCount; } // Add skeleton if existent // TODO: What if multiple, incompatible skeletons are found(?) // For now: Remove skeleton and import unskinned // if (mesh.m_pSkeleton) //{ // if (m_pSkeleton) // { // if (!m_pSkeleton->IsCompatibleWith(mesh.m_pSkeleton.Borrow())) // { // ezLog::Warning("Found incompatible skeletons during mesh merging in mesh '{0}', import will be without skeletons!", // m_Name.GetData()); m_pSkeleton.Reset(); // } // } // else // { // m_pSkeleton = EZ_DEFAULT_NEW(ezSkeleton, *mesh.m_pSkeleton); // } //} }
int main(int argc, char **argv) { int iRetCode = EXIT_SUCCESS; char *szFilename = NULL; int iOptionIndex, iArgIndex, iMode, iGetoptRet; struct option arLongOpts[] = { { "add", no_argument, &iMode, MODE_ADD }, { "list", no_argument, &iMode, MODE_LIST }, { 0, 0, 0, 0 } }; do { iGetoptRet = getopt_long(argc, argv, "", arLongOpts, &iOptionIndex); if (iGetoptRet == '?') { return EXIT_FAILURE; } } while (iGetoptRet != -1); iArgIndex = 1 + 1; // executable name + option if (iArgIndex == argc) { printf("stream: no filename specified\n"); return EXIT_FAILURE; } szFilename = argv[iArgIndex]; switch (iMode) { case MODE_ADD: { char *szStreamName = NULL; DWORD dwStreamSize; if (++iArgIndex == argc) { printf("stream: no stream specified\n"); return EXIT_FAILURE; } szStreamName = argv[iArgIndex]; if (++iArgIndex == argc) { dwStreamSize = DEFAULT_STREAM_SIZE_MB; } else { dwStreamSize = atoi(argv[iArgIndex]); } iRetCode = AddDataStream(szFilename, szStreamName, dwStreamSize); break; } case MODE_LIST: { iRetCode = ListDataStreams(szFilename); break; } default: { printf("stream: unknown mode %d\n", iMode); return EXIT_FAILURE; } } return iRetCode; }