/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. #include "CryLegacy_precompiled.h" #include "Tile.h" #include "TypeInfo_impl.h" #include "DebugDrawContext.h" STRUCT_INFO_BEGIN(MNM::Tile::Triangle) STRUCT_VAR_INFO(vertex, TYPE_ARRAY(3, TYPE_INFO(Index))) STRUCT_BITFIELD_INFO(linkCount, TYPE_INFO(uint16), 4) STRUCT_BITFIELD_INFO(firstLink, TYPE_INFO(uint16), 12) STRUCT_VAR_INFO(islandID, TYPE_INFO(StaticIslandID)) STRUCT_INFO_END(MNM::Tile::Triangle) STRUCT_INFO_BEGIN(MNM::Tile::BVNode) STRUCT_BITFIELD_INFO(leaf, TYPE_INFO(uint16), 1) STRUCT_BITFIELD_INFO(offset, TYPE_INFO(uint16), 15) STRUCT_VAR_INFO(aabb, TYPE_INFO(AABB)) STRUCT_INFO_END(MNM::Tile::BVNode) STRUCT_INFO_BEGIN(MNM::Tile::Link) STRUCT_BITFIELD_INFO(side, TYPE_INFO(uint32), 4) STRUCT_BITFIELD_INFO(edge, TYPE_INFO(uint32), 2) STRUCT_BITFIELD_INFO(triangle, TYPE_INFO(uint32), 10) STRUCT_INFO_END(MNM::Tile::Link) #if DEBUG_MNM_DATA_CONSISTENCY_ENABLED #define TILE_VALIDATE() ValidateTriangleLinks() #else #define TILE_VALIDATE() #endif namespace MNM { Tile::Tile() : triangles(0) , vertices(0) , nodes(0) , links(0) , triangleCount(0) , vertexCount(0) , nodeCount(0) , linkCount(0) , hashValue(0) { } void Tile::CopyTriangles(Triangle* _triangles, uint16 count) { #if MNM_USE_EXPORT_INFORMATION InitConnectivity(triangleCount, count); #endif if (triangleCount != count) { delete[] triangles; triangles = 0; triangleCount = count; if (count) { triangles = new Triangle[count]; } } if (count) { memcpy(triangles, _triangles, sizeof(Triangle) * count); } } void Tile::CopyVertices(Vertex* _vertices, uint16 count) { if (vertexCount != count) { delete[] vertices; vertices = 0; vertexCount = count; if (count) { vertices = new Vertex[count]; } } if (count) { memcpy(vertices, _vertices, sizeof(Vertex) * count); } TILE_VALIDATE(); } void Tile::CopyNodes(BVNode* _nodes, uint16 count) { if (nodeCount != count) { delete[] nodes; nodes = 0; nodeCount = count; if (count) { nodes = new BVNode[count]; } } if (count) { memcpy(nodes, _nodes, sizeof(BVNode) * count); } } void Tile::CopyLinks(Link* _links, uint16 count) { if (linkCount != count) { delete[] links; links = 0; linkCount = count; if (count) { links = new Link[count]; } } if (count) { memcpy(links, _links, sizeof(Link) * count); } } void Tile::AddOffMeshLink(const TriangleID triangleID, const uint16 offMeshIndex) { TILE_VALIDATE(); uint16 triangleIdx = ComputeTriangleIndex(triangleID); assert(triangleIdx < triangleCount); if (triangleIdx < triangleCount) { //Figure out if this triangle has already off-mesh connections //Off-mesh link is always the first if exists Triangle& triangle = triangles[triangleIdx]; const size_t MaxLinkCount = 1024 * 6; Tile::Link tempLinks[MaxLinkCount]; bool hasOffMeshLink = links && (triangle.linkCount > 0) && (triangle.firstLink < linkCount) && (links[triangle.firstLink].side == Link::OffMesh); // Try enabling DEBUG_MNM_DATA_CONSISTENCY_ENABLED if you get this CRY_ASSERT_MESSAGE(!hasOffMeshLink, "Not adding offmesh link, already exists"); if (!hasOffMeshLink) { //Add off-mesh link for triangle { if (triangle.firstLink) { assert(links); PREFAST_ASSUME(links); memcpy(tempLinks, links, sizeof(Link) * triangle.firstLink); } tempLinks[triangle.firstLink].side = Link::OffMesh; tempLinks[triangle.firstLink].triangle = offMeshIndex; //Note this is not used tempLinks[triangle.firstLink].edge = 0; const int countDiff = linkCount - triangle.firstLink; if (countDiff > 0) { assert(links); memcpy(&tempLinks[triangle.firstLink + 1], &links[triangle.firstLink], sizeof(Link) * countDiff); } } CopyLinks(tempLinks, linkCount + 1); //Re-arrange link indices for triangles triangle.linkCount++; for (uint16 tIdx = (triangleIdx + 1); tIdx < triangleCount; ++tIdx) { triangles[tIdx].firstLink++; } } } TILE_VALIDATE(); } void Tile::UpdateOffMeshLink(const TriangleID triangleID, const uint16 offMeshIndex) { TILE_VALIDATE(); uint16 triangleIndex = ComputeTriangleIndex(triangleID); assert(triangleIndex < triangleCount); if (triangleIndex < triangleCount) { //First link is always off-mesh if exists const uint16 linkIdx = triangles[triangleIndex].firstLink; assert(linkIdx < linkCount); if (linkIdx < linkCount) { Link& link = links[linkIdx]; assert(link.side == Link::OffMesh); if (link.side == Link::OffMesh) { link.triangle = offMeshIndex; } } } TILE_VALIDATE(); } void Tile::RemoveOffMeshLink(const TriangleID triangleID) { TILE_VALIDATE(); // Find link to be removed uint16 linkToRemoveIdx = 0xFFFF; uint16 boundTriangleIdx = ComputeTriangleIndex(triangleID); if (boundTriangleIdx < triangleCount) { const uint16 firstLink = triangles[boundTriangleIdx].firstLink; if ((triangles[boundTriangleIdx].linkCount > 0) && (firstLink < linkCount) && (links[firstLink].side == Link::OffMesh)) { linkToRemoveIdx = firstLink; } } // Try enabling DEBUG_MNM_DATA_CONSISTENCY_ENABLED if you get this CRY_ASSERT_MESSAGE(linkToRemoveIdx != 0xFFFF, "Trying to remove off mesh link that doesn't exist"); if (linkToRemoveIdx != 0xFFFF) { assert(linkCount > 1); const size_t MaxLinkCount = 1024 * 6; Tile::Link tempLinks[MaxLinkCount]; if (linkToRemoveIdx) { memcpy(tempLinks, links, sizeof(Link) * linkToRemoveIdx); } const int diffCount = linkCount - (linkToRemoveIdx + 1); if (diffCount > 0) { memcpy(&tempLinks[linkToRemoveIdx], &links[linkToRemoveIdx + 1], sizeof(Link) * diffCount); } CopyLinks(tempLinks, linkCount - 1); //Re-arrange link indices for triangles triangles[boundTriangleIdx].linkCount--; for (uint16 tIdx = (boundTriangleIdx + 1); tIdx < triangleCount; ++tIdx) { triangles[tIdx].firstLink--; } } TILE_VALIDATE(); } void Tile::Swap(Tile& other) { std::swap(triangles, other.triangles); std::swap(vertices, other.vertices); std::swap(nodes, other.nodes); std::swap(links, other.links); #if MNM_USE_EXPORT_INFORMATION InitConnectivity(triangleCount, other.triangleCount); #endif std::swap(triangleCount, other.triangleCount); std::swap(vertexCount, other.vertexCount); std::swap(nodeCount, other.nodeCount); std::swap(linkCount, other.linkCount); std::swap(hashValue, other.hashValue); TILE_VALIDATE(); } void Tile::Destroy() { delete[] triangles; triangles = 0; delete[] vertices; vertices = 0; delete[] nodes; nodes = 0; delete[] links; links = 0; #if MNM_USE_EXPORT_INFORMATION SAFE_DELETE_ARRAY(connectivity.trianglesAccessible); connectivity.tileAccessible = 0; #endif triangleCount = 0; vertexCount = 0; nodeCount = 0; linkCount = 0; hashValue = 0; } ColorF CalculateColorForIsland(StaticIslandID islandID, uint16 totalIslands) { if (totalIslands == 0) { return Col_White; } const float hueValue = islandID / (float) totalIslands; ColorF color; color.fromHSV(hueValue, 1.0f, 1.0f); color.a = 1.0f; return color; } void Tile::Draw(size_t drawFlags, vector3_t origin, TileID tileID, const std::vector& islandAreas) const { IRenderAuxGeom* renderAuxGeom = gEnv->pRenderer->GetIRenderAuxGeom(); const ColorB triangleColorConnected(Col_Azure, 0.65f); const ColorB triangleColorDisconnected(Col_Red, 0.65f); const ColorB boundaryColor(Col_Black); const Vec3 offset = origin.GetVec3() + Vec3(0.0f, 0.0f, 0.05f); const Vec3 loffset(offset + Vec3(0.0f, 0.0f, 0.0005f)); SAuxGeomRenderFlags oldFlags = renderAuxGeom->GetRenderFlags(); SAuxGeomRenderFlags renderFlags(oldFlags); renderFlags.SetAlphaBlendMode(e_AlphaBlended); renderFlags.SetDepthTestFlag(e_DepthTestOn); renderFlags.SetDepthWriteFlag(e_DepthWriteOn); renderAuxGeom->SetRenderFlags(renderFlags); if (drawFlags & DrawTriangles) { for (size_t i = 0; i < triangleCount; ++i) { const Triangle& triangle = triangles[i]; const Vec3 v0 = vertices[triangle.vertex[0]].GetVec3() + offset; const Vec3 v1 = vertices[triangle.vertex[1]].GetVec3() + offset; const Vec3 v2 = vertices[triangle.vertex[2]].GetVec3() + offset; #if MNM_USE_EXPORT_INFORMATION ColorB triangleColor = ((drawFlags & DrawAccessibility) && (connectivity.trianglesAccessible != NULL) && !connectivity.trianglesAccessible[i]) ? triangleColorDisconnected : triangleColorConnected; #else ColorB triangleColor = triangleColorConnected; #endif // Islands bool drawIslandData = ((drawFlags & DrawIslandsId) && triangle.islandID > MNM::Constants::eStaticIsland_InvalidIslandID && triangle.islandID < islandAreas.size()); if (drawFlags & DrawIslandsId) { triangleColor = CalculateColorForIsland(triangle.islandID, islandAreas.size()); } renderAuxGeom->DrawTriangle(v0, triangleColor, v1, triangleColor, v2, triangleColor); if ((drawFlags & DrawTrianglesId) || drawIslandData) { const Vec3 triCenter = ((v0 + v1 + v2) / 3.0f) + Vec3(.0f, .0f, .1f); stack_string text; if ((drawFlags & DrawTrianglesId) && drawIslandData) { text.Format("id: %d, area: %f", ComputeTriangleID(tileID, i), islandAreas[triangle.islandID - 1]); } else if (drawFlags & DrawTrianglesId) { text.Format("id: %d", ComputeTriangleID(tileID, i)); } else { text.Format("area: %f", islandAreas[triangle.islandID - 1]); } CDebugDrawContext dc; dc->Draw3dLabelEx(triCenter, 1.0f, ColorB(255, 255, 255), true, true, true, false, text.c_str()); } } } renderAuxGeom->SetRenderFlags(oldFlags); for (size_t i = 0; i < triangleCount; ++i) { const Triangle& triangle = triangles[i]; size_t linkedEdges = 0; for (size_t l = 0; l < triangle.linkCount; ++l) { const Link& link = links[triangle.firstLink + l]; const size_t edge = link.edge; linkedEdges |= static_cast((size_t)1 << edge); const uint16 vi0 = link.edge; const uint16 vi1 = (link.edge + 1) % 3; assert(vi0 < 3); assert(vi1 < 3); const Vec3 v0 = vertices[triangle.vertex[vi0]].GetVec3() + loffset; const Vec3 v1 = vertices[triangle.vertex[vi1]].GetVec3() + loffset; if (link.side == Link::OffMesh) { if (drawFlags & DrawOffMeshLinks) { const Vec3 a = vertices[triangle.vertex[0]].GetVec3() + offset; const Vec3 b = vertices[triangle.vertex[1]].GetVec3() + offset; const Vec3 c = vertices[triangle.vertex[2]].GetVec3() + offset; renderAuxGeom->DrawLine(a, Col_Red, b, Col_Red, 8.0f); renderAuxGeom->DrawLine(b, Col_Red, c, Col_Red, 8.0f); renderAuxGeom->DrawLine(c, Col_Red, a, Col_Red, 8.0f); } } else if (link.side != Link::Internal) { if (drawFlags & DrawExternalLinks) { // TODO: compute clipped edge renderAuxGeom->DrawLine(v0, Col_Green, v1, Col_ForestGreen); } } else { if (drawFlags & DrawInternalLinks) { renderAuxGeom->DrawLine(v0, Col_White, v1, Col_White); } } } if (drawFlags & DrawMeshBoundaries) { for (size_t e = 0; e < 3; ++e) { if ((linkedEdges & static_cast((size_t)1 << e)) == 0) { const Vec3 a = vertices[triangle.vertex[e]].GetVec3() + loffset; const Vec3 b = vertices[triangle.vertex[(e + 1) % 3]].GetVec3() + loffset; renderAuxGeom->DrawLine(a, Col_Black, b, Col_Black, 8.0f); } } } } } #if DEBUG_MNM_DATA_CONSISTENCY_ENABLED void Tile::ValidateTriangleLinks() { uint16 nextLink = 0; for (uint16 i = 0; i < triangleCount; ++i) { const Triangle& triangle = triangles[i]; CRY_ASSERT_MESSAGE(triangle.firstLink <= linkCount || linkCount == 0, "Out of range link"); CRY_ASSERT_MESSAGE(nextLink == triangle.firstLink, "Links are not contiguous"); nextLink += triangle.linkCount; for (uint16 l = 0; l < triangle.linkCount; ++l) { uint16 linkIdx = triangle.firstLink + l; CRY_ASSERT_MESSAGE(links[linkIdx].side != Link::OffMesh || l == 0, "Off mesh links should always be first"); } } CRY_ASSERT(nextLink == linkCount); } #endif vector3_t::value_type Tile::GetTriangleArea(const TriangleID triangleID) const { const Tile::Triangle& triangle = triangles[ComputeTriangleIndex(triangleID)]; const vector3_t v0 = vector3_t(vertices[triangle.vertex[0]]); const vector3_t v1 = vector3_t(vertices[triangle.vertex[1]]); const vector3_t v2 = vector3_t(vertices[triangle.vertex[2]]); const vector3_t::value_type len0 = (v0 - v1).len(); const vector3_t::value_type len1 = (v0 - v2).len(); const vector3_t::value_type len2 = (v1 - v2).len(); const vector3_t::value_type s = (len0 + len1 + len2) / vector3_t::value_type(2); return sqrtf(s * (s - len0) * (s - len1) * (s - len2)); } ////////////////////////////////////////////////////////////////////////// #if MNM_USE_EXPORT_INFORMATION bool Tile::ConsiderExportInformation() const { // TODO FrancescoR: Remove if it's not necessary anymore or refactor it. return true; } void Tile::InitConnectivity(uint16 oldTriangleCount, uint16 newTriangleCount) { if (ConsiderExportInformation()) { // By default all is accessible connectivity.tileAccessible = 1; if (oldTriangleCount != newTriangleCount) { SAFE_DELETE_ARRAY(connectivity.trianglesAccessible); if (newTriangleCount) { connectivity.trianglesAccessible = new uint8[newTriangleCount]; } } if (newTriangleCount) { memset(connectivity.trianglesAccessible, 1, sizeof(uint8) * newTriangleCount); } connectivity.triangleCount = newTriangleCount; } } void Tile::ResetConnectivity(uint8 accessible) { if (ConsiderExportInformation()) { assert(connectivity.triangleCount == triangleCount); connectivity.tileAccessible = accessible; if (connectivity.trianglesAccessible != NULL) { memset(connectivity.trianglesAccessible, accessible, sizeof(uint8) * connectivity.triangleCount); } } } #endif }