// Copyright (c) Wojciech Figat. All rights reserved. #if COMPILE_WITH_MODEL_TOOL #include "MeshAccelerationStructure.h" #include "Engine/Core/Log.h" #include "Engine/Core/Math/Math.h" #include "Engine/Content/Content.h" #include "Engine/Content/Assets/Model.h" #include "Engine/Graphics/GPUBuffer.h" #include "Engine/Graphics/Models/ModelData.h" #include "Engine/Profiler/ProfilerCPU.h" PACK_STRUCT(struct GPUBVH { Float3 BoundsMin; uint32 Index; Float3 BoundsMax; int32 Count; // Negative for non-leaf nodes }); static_assert(sizeof(GPUBVH) == sizeof(Float4) * 2, "Invalid BVH structure size for GPU."); void MeshAccelerationStructure::BuildBVH(int32 node, BVHBuild& build) { auto* root = &_bvh[node]; ASSERT_LOW_LAYER(root->Leaf.IsLeaf); if (build.MaxLeafSize > 0 && root->Leaf.TriangleCount <= build.MaxLeafSize) return; if (build.MaxDepth > 0 && build.NodeDepth >= build.MaxDepth) return; // Spawn two leaves const int32 childIndex = _bvh.Count(); _bvh.AddDefault(2); root = &_bvh[node]; auto& left = _bvh.Get()[childIndex]; auto& right = _bvh.Get()[childIndex + 1]; left.Leaf.IsLeaf = 1; right.Leaf.IsLeaf = 1; left.Leaf.MeshIndex = root->Leaf.MeshIndex; right.Leaf.MeshIndex = root->Leaf.MeshIndex; // Mid-point splitting based on the largest axis const Float3 boundsSize = root->Bounds.GetSize(); int32 axisCount = 0; int32 axis = 0; RETRY: if (axisCount == 0) { // Pick the highest axis axis = 0; if (boundsSize.Y > boundsSize.X && boundsSize.Y >= boundsSize.Z) axis = 1; else if (boundsSize.Z > boundsSize.X) axis = 2; } else if (axisCount == 3) { // Failed to split _bvh.Resize(childIndex); return; } else { // Go to the next axis axis = (axis + 1) % 3; } const float midPoint = (float)(root->Bounds.Minimum.Raw[axis] + boundsSize.Raw[axis] * 0.5f); const Mesh& meshData = _meshes[root->Leaf.MeshIndex]; const Float3* vb = meshData.VertexBuffer.Get(); int32 indexStart = root->Leaf.TriangleIndex * 3; int32 indexEnd = indexStart + root->Leaf.TriangleCount * 3; left.Leaf.TriangleCount = 0; right.Leaf.TriangleCount = 0; if (meshData.Use16BitIndexBuffer) { struct Tri { uint16 I0, I1, I2; }; build.Scratch.Resize(root->Leaf.TriangleCount * sizeof(Tri)); auto dst = (Tri*)build.Scratch.Get(); auto ib16 = meshData.IndexBuffer.Get(); for (int32 i = indexStart; i < indexEnd;) { const Tri tri = { ib16[i++], ib16[i++], ib16[i++] }; const float v0 = vb[tri.I0].Raw[axis]; const float v1 = vb[tri.I1].Raw[axis]; const float v2 = vb[tri.I2].Raw[axis]; const float centroid = (v0 + v1 + v2) * 0.333f; if (centroid <= midPoint) dst[left.Leaf.TriangleCount++] = tri; // Left else dst[root->Leaf.TriangleCount - ++right.Leaf.TriangleCount] = tri; // Right } Platform::MemoryCopy(ib16 + indexStart, dst, root->Leaf.TriangleCount * 3 * sizeof(uint16)); if (left.Leaf.TriangleCount == 0 || right.Leaf.TriangleCount == 0) { axisCount++; goto RETRY; } left.Bounds = BoundingBox(vb[dst[0].I0]); indexStart = 0; indexEnd = left.Leaf.TriangleCount * 3; for (int32 i = indexStart; i < indexEnd; i++) left.Bounds.Merge(vb[((uint16*)build.Scratch.Get())[i]]); right.Bounds = BoundingBox(vb[dst[root->Leaf.TriangleCount - 1].I0]); indexStart = left.Leaf.TriangleCount; indexEnd = root->Leaf.TriangleCount * 3; for (int32 i = indexStart; i < indexEnd; i++) right.Bounds.Merge(vb[((uint16*)build.Scratch.Get())[i]]); } else { struct Tri { uint32 I0, I1, I2; }; build.Scratch.Resize(root->Leaf.TriangleCount * sizeof(Tri)); auto dst = (Tri*)build.Scratch.Get(); auto ib32 = meshData.IndexBuffer.Get(); for (int32 i = indexStart; i < indexEnd;) { const Tri tri = { ib32[i++], ib32[i++], ib32[i++] }; const float v0 = vb[tri.I0].Raw[axis]; const float v1 = vb[tri.I1].Raw[axis]; const float v2 = vb[tri.I2].Raw[axis]; const float centroid = (v0 + v1 + v2) * 0.333f; if (centroid <= midPoint) dst[left.Leaf.TriangleCount++] = tri; // Left else dst[root->Leaf.TriangleCount - ++right.Leaf.TriangleCount] = tri; // Right } Platform::MemoryCopy(ib32 + indexStart, dst, root->Leaf.TriangleCount * 3 * sizeof(uint32)); if (left.Leaf.TriangleCount == 0 || right.Leaf.TriangleCount == 0) { axisCount++; goto RETRY; } left.Bounds = BoundingBox(vb[dst[0].I0]); indexStart = 0; indexEnd = left.Leaf.TriangleCount * 3; for (int32 i = indexStart; i < indexEnd; i++) left.Bounds.Merge(vb[((uint32*)build.Scratch.Get())[i]]); right.Bounds = BoundingBox(vb[dst[root->Leaf.TriangleCount - 1].I0]); indexStart = left.Leaf.TriangleCount; indexEnd = root->Leaf.TriangleCount * 3; for (int32 i = indexStart; i < indexEnd; i++) right.Bounds.Merge(vb[((uint32*)build.Scratch.Get())[i]]); } ASSERT_LOW_LAYER(left.Leaf.TriangleCount + right.Leaf.TriangleCount == root->Leaf.TriangleCount); left.Leaf.TriangleIndex = root->Leaf.TriangleIndex; right.Leaf.TriangleIndex = left.Leaf.TriangleIndex + left.Leaf.TriangleCount; build.MaxNodeTriangles = Math::Max(build.MaxNodeTriangles, (int32)right.Leaf.TriangleCount); build.MaxNodeTriangles = Math::Max(build.MaxNodeTriangles, (int32)right.Leaf.TriangleCount); // Convert into a node root->Node.IsLeaf = 0; root->Node.ChildIndex = childIndex; root->Node.ChildrenCount = 2; // Split children build.NodeDepth++; build.MaxNodeDepth = Math::Max(build.NodeDepth, build.MaxNodeDepth); BuildBVH(childIndex, build); BuildBVH(childIndex + 1, build); build.NodeDepth--; } bool MeshAccelerationStructure::PointQueryBVH(int32 node, const Vector3& point, Real& hitDistance, Vector3& hitPoint, Triangle& hitTriangle) const { const auto& root = _bvh[node]; bool hit = false; if (root.Leaf.IsLeaf) { // Find closest triangle Vector3 p; const Mesh& meshData = _meshes.Get()[root.Leaf.MeshIndex]; const Float3* vb = meshData.VertexBuffer.Get(); const int32 indexStart = root.Leaf.TriangleIndex * 3; const int32 indexEnd = indexStart + root.Leaf.TriangleCount * 3; if (meshData.Use16BitIndexBuffer) { const uint16* ib16 = meshData.IndexBuffer.Get(); for (int32 i = indexStart; i < indexEnd;) { Vector3 v0 = vb[ib16[i++]]; Vector3 v1 = vb[ib16[i++]]; Vector3 v2 = vb[ib16[i++]]; CollisionsHelper::ClosestPointPointTriangle(point, v0, v1, v2, p); const Real distance = Vector3::Distance(point, p); if (distance < hitDistance) { hitDistance = distance; hitPoint = p; hitTriangle = Triangle(v0, v1, v2); hit = true; } } } else { const uint32* ib32 = meshData.IndexBuffer.Get(); for (int32 i = indexStart; i < indexEnd;) { Vector3 v0 = vb[ib32[i++]]; Vector3 v1 = vb[ib32[i++]]; Vector3 v2 = vb[ib32[i++]]; CollisionsHelper::ClosestPointPointTriangle(point, v0, v1, v2, p); const Real distance = Vector3::Distance(point, p); if (distance < hitDistance) { hitDistance = distance; hitPoint = p; hitTriangle = Triangle(v0, v1, v2); hit = true; } } } } else { // Check all nested nodes for (uint32 i = 0; i < root.Node.ChildrenCount; i++) { const int32 index = root.Node.ChildIndex + i; if (_bvh[index].Bounds.Distance(point) >= hitDistance) continue; if (PointQueryBVH(index, point, hitDistance, hitPoint, hitTriangle)) hit = true; } } return hit; } bool MeshAccelerationStructure::RayCastBVH(int32 node, const Ray& ray, Real& hitDistance, Vector3& hitNormal, Triangle& hitTriangle) const { const auto& root = _bvh[node]; if (!root.Bounds.Intersects(ray)) return false; Vector3 normal; Real distance; bool hit = false; if (root.Leaf.IsLeaf) { // Ray cast along triangles in the leaf const Mesh& meshData = _meshes.Get()[root.Leaf.MeshIndex]; const Float3* vb = meshData.VertexBuffer.Get(); const int32 indexStart = root.Leaf.TriangleIndex * 3; const int32 indexEnd = indexStart + root.Leaf.TriangleCount * 3; if (meshData.Use16BitIndexBuffer) { const uint16* ib16 = meshData.IndexBuffer.Get(); for (int32 i = indexStart; i < indexEnd;) { Vector3 v0 = vb[ib16[i++]]; Vector3 v1 = vb[ib16[i++]]; Vector3 v2 = vb[ib16[i++]]; if (CollisionsHelper::RayIntersectsTriangle(ray, v0, v1, v2, distance, normal) && distance < hitDistance) { hitDistance = distance; hitNormal = normal; hitTriangle = Triangle(v0, v1, v2); hit = true; } } } else { const uint32* ib32 = meshData.IndexBuffer.Get(); for (int32 i = indexStart; i < indexEnd;) { Vector3 v0 = vb[ib32[i++]]; Vector3 v1 = vb[ib32[i++]]; Vector3 v2 = vb[ib32[i++]]; if (CollisionsHelper::RayIntersectsTriangle(ray, v0, v1, v2, distance, normal) && distance < hitDistance) { hitDistance = distance; hitNormal = normal; hitTriangle = Triangle(v0, v1, v2); hit = true; } } } } else { // Ray cast all child nodes Triangle triangle; for (uint32 i = 0; i < root.Node.ChildrenCount; i++) { const int32 index = root.Node.ChildIndex + i; distance = hitDistance; if (RayCastBVH(index, ray, distance, normal, triangle) && distance < hitDistance) { hitDistance = distance; hitNormal = normal; hitTriangle = triangle; hit = true; } } } return hit; } MeshAccelerationStructure::~MeshAccelerationStructure() { for (auto& e : _meshes) { if (e.Asset) e.Asset->RemoveReference(); } } void MeshAccelerationStructure::Add(Model* model, int32 lodIndex) { PROFILE_CPU(); lodIndex = Math::Clamp(lodIndex, model->HighestResidentLODIndex(), model->LODs.Count() - 1); ModelLOD& lod = model->LODs[lodIndex]; _meshes.EnsureCapacity(_meshes.Count() + lod.Meshes.Count()); bool failed = false; for (int32 i = 0; i < lod.Meshes.Count(); i++) { auto& mesh = lod.Meshes[i]; const MaterialSlot& materialSlot = model->MaterialSlots[mesh.GetMaterialSlotIndex()]; if (materialSlot.Material && !materialSlot.Material->WaitForLoaded()) { // Skip transparent materials if (materialSlot.Material->GetInfo().BlendMode != MaterialBlendMode::Opaque) continue; } auto& meshData = _meshes.AddOne(); meshData.Asset = model; model->AddReference(); if (model->IsVirtual()) { meshData.Indices = mesh.GetTriangleCount() * 3; meshData.Vertices = mesh.GetVertexCount(); failed |= mesh.DownloadDataGPU(MeshBufferType::Index, meshData.IndexBuffer); failed |= mesh.DownloadDataGPU(MeshBufferType::Vertex0, meshData.VertexBuffer); } else { failed |= mesh.DownloadDataCPU(MeshBufferType::Index, meshData.IndexBuffer, meshData.Indices); failed |= mesh.DownloadDataCPU(MeshBufferType::Vertex0, meshData.VertexBuffer, meshData.Vertices); } if (failed) return; if (!meshData.IndexBuffer.IsAllocated() && meshData.IndexBuffer.Length() != 0) { // BVH nodes modifies index buffer (sorts data in-place) so clone it meshData.IndexBuffer.Copy(meshData.IndexBuffer.Get(), meshData.IndexBuffer.Length()); } meshData.Use16BitIndexBuffer = mesh.Use16BitIndexBuffer(); meshData.Bounds = mesh.GetBox(); } } void MeshAccelerationStructure::Add(const ModelData* modelData, int32 lodIndex, bool copy) { PROFILE_CPU(); lodIndex = Math::Clamp(lodIndex, 0, modelData->LODs.Count() - 1); const ModelLodData& lod = modelData->LODs[lodIndex]; _meshes.EnsureCapacity(_meshes.Count() + lod.Meshes.Count()); for (int32 i = 0; i < lod.Meshes.Count(); i++) { MeshData* mesh = lod.Meshes[i]; const MaterialSlotEntry& materialSlot = modelData->Materials[mesh->MaterialSlotIndex]; auto material = Content::LoadAsync(materialSlot.AssetID); if (material && !material->WaitForLoaded()) { // Skip transparent materials if (material->GetInfo().BlendMode != MaterialBlendMode::Opaque) continue; } auto& meshData = _meshes.AddOne(); meshData.Asset = nullptr; meshData.Indices = mesh->Indices.Count(); meshData.Vertices = mesh->Positions.Count(); if (copy) { meshData.IndexBuffer.Copy((const byte*)mesh->Indices.Get(), meshData.Indices * sizeof(uint32)); meshData.VertexBuffer.Copy((const byte*)mesh->Positions.Get(), meshData.Vertices * sizeof(Float3)); } else { meshData.IndexBuffer.Link((const byte*)mesh->Indices.Get(), meshData.Indices * sizeof(uint32)); meshData.VertexBuffer.Link((const byte*)mesh->Positions.Get(), meshData.Vertices * sizeof(Float3)); } meshData.Use16BitIndexBuffer = false; mesh->CalculateBox(meshData.Bounds); } } void MeshAccelerationStructure::Add(Float3* vb, int32 vertices, void* ib, int32 indices, bool use16BitIndex, bool copy) { ASSERT(vertices % 3 == 0); auto& meshData = _meshes.AddOne(); meshData.Asset = nullptr; if (copy) { meshData.VertexBuffer.Copy((const byte*)vb, vertices * sizeof(Float3)); } else { meshData.VertexBuffer.Link((const byte*)vb, vertices * sizeof(Float3)); } meshData.IndexBuffer.Copy((const byte*)ib, indices * (use16BitIndex ? sizeof(uint16) : sizeof(uint32))); meshData.Vertices = vertices; meshData.Indices = indices; meshData.Use16BitIndexBuffer = use16BitIndex; BoundingBox::FromPoints(meshData.VertexBuffer.Get(), vertices, meshData.Bounds); } void MeshAccelerationStructure::MergeMeshes(bool force16BitIndexBuffer) { if (_meshes.Count() == 0) return; if (_meshes.Count() == 1 && (!force16BitIndexBuffer || !_meshes[0].Use16BitIndexBuffer)) return; PROFILE_CPU(); auto meshes = _meshes; _meshes.Clear(); _meshes.Resize(1); auto& mesh = _meshes[0]; mesh.Asset = nullptr; mesh.Use16BitIndexBuffer = true; mesh.Indices = 0; mesh.Vertices = 0; mesh.Bounds = meshes[0].Bounds; for (auto& e : meshes) { if (!e.Use16BitIndexBuffer) mesh.Use16BitIndexBuffer = false; mesh.Vertices += e.Vertices; mesh.Indices += e.Indices; BoundingBox::Merge(mesh.Bounds, e.Bounds, mesh.Bounds); } mesh.Use16BitIndexBuffer &= mesh.Indices <= MAX_uint16 && !force16BitIndexBuffer; mesh.VertexBuffer.Allocate(mesh.Vertices * sizeof(Float3)); mesh.IndexBuffer.Allocate(mesh.Indices * sizeof(uint32)); int32 vertexCounter = 0, indexCounter = 0; for (auto& e : meshes) { Platform::MemoryCopy(mesh.VertexBuffer.Get() + vertexCounter * sizeof(Float3), e.VertexBuffer.Get(), e.Vertices * sizeof(Float3)); if (e.Use16BitIndexBuffer) { for (int32 i = 0; i < e.Indices; i++) { uint16 index = ((uint16*)e.IndexBuffer.Get())[i]; ((uint32*)mesh.IndexBuffer.Get())[indexCounter + i] = vertexCounter + index; } } else { for (int32 i = 0; i < e.Indices; i++) { uint16 index = ((uint32*)e.IndexBuffer.Get())[i]; ((uint32*)mesh.IndexBuffer.Get())[indexCounter + i] = vertexCounter + index; } } vertexCounter += e.Vertices; indexCounter += e.Indices; if (e.Asset) e.Asset->RemoveReference(); } } void MeshAccelerationStructure::BuildBVH(int32 maxLeafSize, int32 maxDepth) { if (_meshes.Count() == 0) return; PROFILE_CPU(); BVHBuild build; build.MaxLeafSize = maxLeafSize; build.MaxDepth = maxDepth; // Estimate memory usage int32 trianglesCount = 0; for (const Mesh& meshData : _meshes) trianglesCount += meshData.Indices / 3; _bvh.Clear(); _bvh.EnsureCapacity(trianglesCount / Math::Max(maxLeafSize, 16)); // Skip using root node if BVH contains only one mesh if (_meshes.Count() == 1) { const Mesh& meshData = _meshes.First(); auto& child = _bvh.AddOne(); child.Leaf.IsLeaf = 1; child.Leaf.MeshIndex = 0; child.Leaf.TriangleIndex = 0; child.Leaf.TriangleCount = meshData.Indices / 3; child.Bounds = meshData.Bounds; Array scratch; BuildBVH(0, build); } else { // Init with the root node and all meshes as leaves auto& root = _bvh.AddOne(); root.Node.IsLeaf = 0; root.Node.ChildIndex = 1; root.Node.ChildrenCount = _meshes.Count(); root.Bounds = _meshes[0].Bounds; for (int32 i = 0; i < _meshes.Count(); i++) { const Mesh& meshData = _meshes[i]; auto& child = _bvh.AddOne(); child.Leaf.IsLeaf = 1; child.Leaf.MeshIndex = i; child.Leaf.TriangleIndex = 0; child.Leaf.TriangleCount = meshData.Indices / 3; child.Bounds = meshData.Bounds; BoundingBox::Merge(root.Bounds, meshData.Bounds, root.Bounds); } // Sub-divide mesh nodes into smaller leaves build.MaxNodeDepth = build.MaxDepth = 2; Array scratch; for (int32 i = 0; i < _meshes.Count(); i++) BuildBVH(i + 1, build); build.NodeDepth = 0; } LOG(Info, "BVH nodes: {}, max depth: {}, max triangles: {}", _bvh.Count(), build.MaxNodeDepth, build.MaxNodeTriangles); } bool MeshAccelerationStructure::PointQuery(const Vector3& point, Real& hitDistance, Vector3& hitPoint, Triangle& hitTriangle, Real maxDistance) const { hitDistance = maxDistance >= MAX_Real ? maxDistance : maxDistance * maxDistance; bool hit = false; // BVH if (_bvh.Count() != 0) { Array> stack; stack.Push(0); while (stack.HasItems()) { const int32 node = stack.Pop(); auto& root = _bvh[node]; // Skip too far nodes if (root.Bounds.Distance(point) >= hitDistance) continue; if (root.Leaf.IsLeaf) { // Check this leaf hit |= PointQueryBVH(node, point, hitDistance, hitPoint, hitTriangle); } else { // Check this node children for (uint32 i = 0; i < root.Node.ChildrenCount; i++) stack.Push(root.Node.ChildIndex + i); } } //hit = PointQueryBVH(0, point, hitDistance, hitPoint, hitTriangle); return hit; } // Brute-force { Vector3 p; for (const Mesh& meshData : _meshes) { const Float3* vb = meshData.VertexBuffer.Get(); if (meshData.Use16BitIndexBuffer) { const uint16* ib16 = meshData.IndexBuffer.Get(); for (int32 i = 0; i < meshData.Indices;) { Vector3 v0 = vb[ib16[i++]]; Vector3 v1 = vb[ib16[i++]]; Vector3 v2 = vb[ib16[i++]]; CollisionsHelper::ClosestPointPointTriangle(point, v0, v1, v2, p); const Real distance = Vector3::DistanceSquared(point, p); if (distance < hitDistance) { hitDistance = distance; hitPoint = p; hitTriangle = Triangle(v0, v1, v2); hit = true; } } } else { const uint32* ib32 = meshData.IndexBuffer.Get(); for (int32 i = 0; i < meshData.Indices;) { Vector3 v0 = vb[ib32[i++]]; Vector3 v1 = vb[ib32[i++]]; Vector3 v2 = vb[ib32[i++]]; CollisionsHelper::ClosestPointPointTriangle(point, v0, v1, v2, p); const Real distance = Vector3::DistanceSquared(point, p); if (distance < hitDistance) { hitDistance = distance; hitPoint = p; hitTriangle = Triangle(v0, v1, v2); hit = true; } } } } if (hit) hitDistance = Math::Sqrt(hitDistance); return hit; } } bool MeshAccelerationStructure::RayCast(const Ray& ray, Real& hitDistance, Vector3& hitNormal, Triangle& hitTriangle, Real maxDistance) const { hitDistance = maxDistance; // BVH if (_bvh.Count() != 0) { return RayCastBVH(0, ray, hitDistance, hitNormal, hitTriangle); } // Brute-force { Vector3 normal; Real distance; bool hit = false; for (const Mesh& meshData : _meshes) { if (!meshData.Bounds.Intersects(ray)) continue; const Float3* vb = meshData.VertexBuffer.Get(); if (meshData.Use16BitIndexBuffer) { const uint16* ib16 = meshData.IndexBuffer.Get(); for (int32 i = 0; i < meshData.Indices;) { Vector3 v0 = vb[ib16[i++]]; Vector3 v1 = vb[ib16[i++]]; Vector3 v2 = vb[ib16[i++]]; if (CollisionsHelper::RayIntersectsTriangle(ray, v0, v1, v2, distance, normal) && distance < hitDistance) { hitDistance = distance; hitNormal = normal; hitTriangle = Triangle(v0, v1, v2); hit = true; } } } else { const uint32* ib32 = meshData.IndexBuffer.Get(); for (int32 i = 0; i < meshData.Indices;) { Vector3 v0 = vb[ib32[i++]]; Vector3 v1 = vb[ib32[i++]]; Vector3 v2 = vb[ib32[i++]]; if (CollisionsHelper::RayIntersectsTriangle(ray, v0, v1, v2, distance, normal) && distance < hitDistance) { hitDistance = distance; hitNormal = normal; hitTriangle = Triangle(v0, v1, v2); hit = true; } } } } return hit; } } MeshAccelerationStructure::GPU::~GPU() { SAFE_DELETE_GPU_RESOURCE(BVHBuffer); SAFE_DELETE_GPU_RESOURCE(VertexBuffer); SAFE_DELETE_GPU_RESOURCE(IndexBuffer); } MeshAccelerationStructure::GPU::operator bool() const { // Index buffer is initialized as last one so all other buffers are fine too return IndexBuffer && IndexBuffer->GetSize() != 0; } MeshAccelerationStructure::GPU MeshAccelerationStructure::ToGPU() { PROFILE_CPU(); GPU gpu; // GPU BVH operates on a single mesh with 32-bit indices MergeMeshes(true); // Construct BVH const int32 BVH_STACK_SIZE = 32; // This must match HLSL shader BuildBVH(0, BVH_STACK_SIZE); // Upload BVH { Array bvhData; bvhData.Resize(_bvh.Count()); for (int32 i = 0; i < _bvh.Count(); i++) { const auto& src = _bvh.Get()[i]; auto& dst = bvhData.Get()[i]; dst.BoundsMin = src.Bounds.Minimum; dst.BoundsMax = src.Bounds.Maximum; if (src.Leaf.IsLeaf) { dst.Index = src.Leaf.TriangleIndex * 3; dst.Count = src.Leaf.TriangleCount * 3; } else { dst.Index = src.Node.ChildIndex; dst.Count = -(int32)src.Node.ChildrenCount; // Mark as non-leaf ASSERT(src.Node.ChildrenCount == 2); // GPU shader is hardcoded for 2 children per node } } gpu.BVHBuffer = GPUBuffer::New(); auto desc =GPUBufferDescription::Structured(_bvh.Count(), sizeof(GPUBVH)); desc.InitData = bvhData.Get(); if (gpu.BVHBuffer->Init(desc)) return gpu; } // Upload vertex buffer { const Mesh& mesh = _meshes[0]; gpu.VertexBuffer = GPUBuffer::New(); auto desc = GPUBufferDescription::Raw(mesh.Vertices * sizeof(Float3), GPUBufferFlags::ShaderResource); desc.InitData = mesh.VertexBuffer.Get(); if (gpu.VertexBuffer->Init(desc)) return gpu; } // Upload index buffer { const Mesh& mesh = _meshes[0]; gpu.IndexBuffer = GPUBuffer::New(); auto desc = GPUBufferDescription::Raw(mesh.Indices * sizeof(uint32), GPUBufferFlags::ShaderResource); desc.InitData = mesh.IndexBuffer.Get(); gpu.IndexBuffer->Init(desc); } return gpu; } #endif