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; }
// IM-2014-07-31: [[ ImageLoader ]] Use image loader method to identify stream format uint32_t MCEncodedImageRep::GetDataCompression() { if (m_have_compression) return m_compression; IO_handle t_stream; t_stream = nil; MCImageLoaderFormat t_format; if (GetDataStream(t_stream) && MCImageLoader::IdentifyFormat(t_stream, t_format)) /* UNCHECKED */ MCImageLoaderFormatToCompression(t_format, m_compression); if (t_stream != nil) MCS_close(t_stream); return m_compression; }
bool MCEncodedImageRep::SetupImageLoader() { if (m_loader != nil) return true; bool t_success; t_success = true; IO_handle t_stream; t_stream = nil; MCImageLoader *t_loader; t_loader = nil; if (t_success) t_success = GetDataStream(t_stream); if (t_success) t_success = MCImageLoader::LoaderForStream(t_stream, t_loader); if (t_success) { m_stream = t_stream; m_loader = t_loader; m_have_compression = MCImageLoaderFormatToCompression(m_loader->GetFormat(), m_compression); } else { if (t_loader != nil) delete t_loader; // PM-2015-10-20: [[ Bug 15256 ]] Prevent crash when loading a remote image and there is no internet connection if (t_stream != nil) MCS_close(t_stream); } return t_success; }
ezResult Mesh::ComputeTangents() { ezStopwatch timer; struct MikkInterfaceImpl { MikkInterfaceImpl(Mesh& mesh, const VertexDataStream& position, const VertexDataStream& normal, const VertexDataStream& tex) : triangles(mesh.m_Triangles) , positionStream(position) , normalStream(normal) , texStream(tex) , tangentStream(*mesh.AddDataStream(ezGALVertexAttributeSemantic::Tangent, 3)) // Make sure tangent stream exists. , bitangentStream(*mesh.AddDataStream(ezGALVertexAttributeSemantic::BiTangent, 1)) , bitangentIndexNegative(0) , bitangentIndexPositive(sizeof(float)) { float biTangentSignValues[] = {-1.0f, 1.0f}; bitangentStream.AddValues(ezMakeArrayPtr(biTangentSignValues)); } ezArrayPtr<Triangle> triangles; const TypedVertexDataStreamView<ezVec3> positionStream; const TypedVertexDataStreamView<ezVec3> normalStream; const TypedVertexDataStreamView<ezVec2> texStream; TypedVertexDataStreamView_ReadWrite<ezVec3> tangentStream; TypedVertexDataStreamView_ReadWrite<float> bitangentStream; VertexDataIndex bitangentIndexPositive; VertexDataIndex bitangentIndexNegative; ezHashTable<ezVec3, VertexDataIndex> tangentDataMap; int GetNumFaces() const { return triangles.GetCount(); } int GetNumVerticesOfFace(const int iFace) const { return 3; } void GetPosition(float fvPosOut[], const int iFace, const int iVert) const { ezVec3 p = positionStream.GetValue(triangles[iFace].m_Vertices[iVert]); fvPosOut[0] = p.x; fvPosOut[1] = p.y; fvPosOut[2] = p.z; } void GetNormal(float fvNormOut[], const int iFace, const int iVert) const { ezVec3 n = normalStream.GetValue(triangles[iFace].m_Vertices[iVert]); fvNormOut[0] = n.x; fvNormOut[1] = n.y; fvNormOut[2] = n.z; } void GetTexCoord(float fvTexcOut[], const int iFace, const int iVert) const { ezVec2 uv = texStream.GetValue(triangles[iFace].m_Vertices[iVert]); fvTexcOut[0] = uv.x; fvTexcOut[1] = uv.y; } void SetTSpaceBasic(const float fvTangent[], const float fSign, const int iFace, const int iVert) { // Need to reconstruct indexing using hashmap lookups. // (will get a call to SetTSpaceBasic for every triangle vertex, not for every data vertex.) VertexIndex v = triangles[iFace].m_Vertices[iVert]; ezVec3 key = ezVec3(fvTangent[0], fvTangent[1], fvTangent[2]); VertexDataIndex existingTangentIndex; if (tangentDataMap.TryGetValue(key, existingTangentIndex)) { tangentStream->SetDataIndex(v, existingTangentIndex); } else { tangentStream.SetValue(v, ezVec3(fvTangent[0], fvTangent[1], fvTangent[2])); tangentDataMap.Insert(key, tangentStream->GetDataIndex(v)); } // For bitangent sign its easy: There are only 2 signs and we've set the data already. if (fSign >= 1.0f) bitangentStream->SetDataIndex(v, bitangentIndexPositive); else bitangentStream->SetDataIndex(v, bitangentIndexNegative); } }; // If there is already a data stream with 3 component bitangents, remove it. { VertexDataStream* bitangents = GetDataStream(ezGALVertexAttributeSemantic::BiTangent); if (bitangents && bitangents->GetNumElementsPerVertex() != 1) RemoveDataStream(ezGALVertexAttributeSemantic::BiTangent); } const VertexDataStream* positionStream = GetDataStream(ezGALVertexAttributeSemantic::Position); if (positionStream == nullptr) { ezLog::Error("Can't compute vertex tangents for the mesh '{0}', because it doesn't have vertex positions.", m_Name); return EZ_FAILURE; } const VertexDataStream* normalStream = GetDataStream(ezGALVertexAttributeSemantic::Normal); if (normalStream == nullptr) { ezLog::Error("Can't compute tangents for the mesh '{0}', because it doesn't have vertex normals.", m_Name); return EZ_FAILURE; } const VertexDataStream* texStream = GetDataStream(ezGALVertexAttributeSemantic::TexCoord0); if (texStream == nullptr || texStream->GetNumElementsPerVertex() != 2) { ezLog::Error("Can't compute tangents for the mesh '{0}', because it doesn't have TexCoord0 stream with two components.", m_Name); return EZ_FAILURE; } MikkInterfaceImpl mikkInterface(*this, *positionStream, *normalStream, *texStream); // Use Morton S. Mikkelsen's tangent calculation. SMikkTSpaceContext context; SMikkTSpaceInterface functions; context.m_pUserData = &mikkInterface; context.m_pInterface = &functions; functions.m_getNumFaces = [](const SMikkTSpaceContext* pContext) { return static_cast<MikkInterfaceImpl*>(pContext->m_pUserData)->GetNumFaces(); }; functions.m_getNumVerticesOfFace = [](const SMikkTSpaceContext* pContext, const int iFace) { return static_cast<MikkInterfaceImpl*>(pContext->m_pUserData)->GetNumVerticesOfFace(iFace); }; functions.m_getPosition = [](const SMikkTSpaceContext* pContext, float fvPosOut[], const int iFace, const int iVert) { return static_cast<MikkInterfaceImpl*>(pContext->m_pUserData)->GetPosition(fvPosOut, iFace, iVert); }; functions.m_getNormal = [](const SMikkTSpaceContext* pContext, float fvPosOut[], const int iFace, const int iVert) { return static_cast<MikkInterfaceImpl*>(pContext->m_pUserData)->GetNormal(fvPosOut, iFace, iVert); }; functions.m_getTexCoord = [](const SMikkTSpaceContext* pContext, float fvPosOut[], const int iFace, const int iVert) { return static_cast<MikkInterfaceImpl*>(pContext->m_pUserData)->GetTexCoord(fvPosOut, iFace, iVert); }; functions.m_setTSpaceBasic = [](const SMikkTSpaceContext* pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert) { return static_cast<MikkInterfaceImpl*>(pContext->m_pUserData)->SetTSpaceBasic(fvTangent, fSign, iFace, iVert); }; functions.m_setTSpace = nullptr; if (!genTangSpaceDefault(&context)) { ezLog::Error("Failed to compute compute tangents for the mesh {0}.", m_Name); return EZ_FAILURE; } ezLog::Debug("Computed mesh normals ('{0}') in '{1}'s", m_Name, ezArgF(timer.GetRunningTotal().GetSeconds(), 2)); return EZ_SUCCESS; }
bool MCEncodedImageRep::LoadImageFrames(MCImageFrame *&r_frames, uindex_t &r_frame_count, bool &r_frames_premultiplied) { bool t_success = true; // IM-2013-02-18 - switching this back to using MCImageImport as we need to // determine the compression type for m_compression IO_handle t_stream = nil; IO_handle t_mask_stream = nil; MCImageCompressedBitmap *t_compressed = nil; MCImageBitmap *t_bitmap = nil; MCPoint t_hotspot = {1, 1}; char *t_name = nil; t_success = GetDataStream(t_stream) && MCImageImport(t_stream, t_mask_stream, t_hotspot, t_name, t_compressed, t_bitmap); if (t_stream != nil) MCS_close(t_stream); MCImageFrame *t_frames; t_frames = nil; uindex_t t_frame_count; t_frame_count = 0; if (t_success) { if (t_compressed != nil) t_success = MCImageDecompress(t_compressed, t_frames, t_frame_count); else { t_success = MCMemoryNewArray(1, t_frames); if (t_success) { t_frames[0].image = t_bitmap; t_frames[0].density = 1.0; t_bitmap = nil; t_frame_count = 1; } } } if (t_success) { m_width = t_frames[0].image->width; m_height = t_frames[0].image->height; if (t_compressed != nil) m_compression = t_compressed->compression; m_have_geometry = true; r_frames = t_frames; r_frame_count = t_frame_count; r_frames_premultiplied = false; } MCCStringFree(t_name); MCImageFreeBitmap(t_bitmap); MCImageFreeCompressedBitmap(t_compressed); return t_success; }