#include #include #include #include #include #include #include #include #include #include "scene/GeometrySceneAssetBuilder.hpp" #include "scene/GeometrySceneAsset.hpp" #include "Log.hpp" namespace thermion { GeometrySceneAssetBuilder::GeometrySceneAssetBuilder(filament::Engine *engine) : mEngine(engine) { } GeometrySceneAssetBuilder &GeometrySceneAssetBuilder::vertices(const float *vertices, uint32_t count) { if (count > 0) { mVertices->resize(count / 3); for (int i = 0; i < mVertices->size(); i++) { mVertices->at(i) = filament::math::float3{vertices[(i * 3)], vertices[(i * 3) + 1], vertices[(i * 3) + 2]}; } } return *this; } GeometrySceneAssetBuilder &GeometrySceneAssetBuilder::normals(const float *normals, uint32_t count) { if (count > 0) { mNormals->resize(count / 3); for (int i = 0; i < mNormals->size(); i++) { mNormals->at(i) = filament::math::float3{normals[i * 3], normals[(i * 3) + 1], normals[(i * 3) + 2]}; } } return *this; } GeometrySceneAssetBuilder &GeometrySceneAssetBuilder::uvs(const float *uvs, uint32_t count) { if (count > 0) { mUVs->resize(count / 2); for (int i = 0; i < mUVs->size(); i++) { mUVs->at(i) = filament::math::float2{uvs[i * 2], uvs[(i * 2) + 1]}; } } return *this; } GeometrySceneAssetBuilder &GeometrySceneAssetBuilder::indices(const uint16_t *indices, uint32_t count) { if (count > 0) { mIndices->resize(count); for (int i = 0; i < mIndices->size(); i++) { mIndices->at(i) = indices[i]; } } return *this; } GeometrySceneAssetBuilder &GeometrySceneAssetBuilder::materials(filament::MaterialInstance **materials, size_t materialInstanceCount) { mMaterialInstances = materials; mMaterialInstanceCount = materialInstanceCount; return *this; } GeometrySceneAssetBuilder &GeometrySceneAssetBuilder::primitiveType(filament::RenderableManager::PrimitiveType type) { mPrimitiveType = type; return *this; } std::unique_ptr GeometrySceneAssetBuilder::build() { Log("Starting build. Validating inputs..."); if (!validate()) { Log("Validation failed!"); return nullptr; } TRACE("Creating buffers..."); auto [vertexBuffer, indexBuffer] = createBuffers(); if (!vertexBuffer || !indexBuffer) { Log("Failed to create buffers: VB=%p, IB=%p", vertexBuffer, indexBuffer); return nullptr; } TRACE("Buffers created successfully: VB=%p, IB=%p", vertexBuffer, indexBuffer); Box boundingBox = computeBoundingBox(); TRACE("Computed bounding box: min={%f,%f,%f}, max={%f,%f,%f}", boundingBox.getMin().x, boundingBox.getMin().y, boundingBox.getMin().z, boundingBox.getMax().x, boundingBox.getMax().y, boundingBox.getMax().z); auto asset = std::make_unique( mEngine, vertexBuffer, indexBuffer, mMaterialInstances, mMaterialInstanceCount, mPrimitiveType, boundingBox); TRACE("Asset created: %p", asset.get()); return asset; } Box GeometrySceneAssetBuilder::computeBoundingBox() { float minX = FLT_MAX, minY = FLT_MAX, minZ = FLT_MAX; float maxX = -FLT_MAX, maxY = -FLT_MAX, maxZ = -FLT_MAX; Box box; for (auto &vertex : *mVertices) { minX = std::min(vertex.x, minX); minY = std::min(vertex.y, minY); minZ = std::min(vertex.z, minZ); maxX = std::max(vertex.x, maxX); maxY = std::max(vertex.y, maxY); maxZ = std::max(vertex.z, maxZ); } const filament::math::float3 min{minX, minY, minZ}; const filament::math::float3 max{maxX, maxY, maxZ}; box.set(min, max); return box; } std::pair GeometrySceneAssetBuilder::createBuffers() { auto indexBuffer = IndexBuffer::Builder() .indexCount(mIndices->size()) .bufferType(IndexBuffer::IndexType::USHORT) .build(*mEngine); indexBuffer->setBuffer(*mEngine, IndexBuffer::BufferDescriptor( mIndices->data(), mIndices->size() * sizeof(uint16_t), [](void *, size_t, void *data) { delete static_cast *>(data); }, mIndices)); if (mUVs->empty()) { mUVs->resize(mVertices->size()); std::fill(mUVs->begin(), mUVs->end(), filament::math::float2{0.0f, 0.0f}); } auto vertexBufferBuilder = VertexBuffer::Builder() .vertexCount(mVertices->size()) .attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT3) .attribute(VertexAttribute::UV0, 1, VertexBuffer::AttributeType::FLOAT2) .attribute(VertexAttribute::COLOR, 2, VertexBuffer::AttributeType::FLOAT4); if (mNormals->empty()) { vertexBufferBuilder = vertexBufferBuilder.bufferCount(3); } else { vertexBufferBuilder.bufferCount(4) .attribute(VertexAttribute::TANGENTS, 3, VertexBuffer::AttributeType::FLOAT4); } auto vertexBuffer = vertexBufferBuilder.build(*mEngine); vertexBuffer->setBufferAt(*mEngine, 0, VertexBuffer::BufferDescriptor( mVertices->data(), mVertices->size() * sizeof(filament::math::float3), [](void *, size_t, void *) { })); vertexBuffer->setBufferAt(*mEngine, 1, VertexBuffer::BufferDescriptor( mUVs->data(), mUVs->size() * sizeof(filament::math::float2), [](void *, size_t, void *data) { }, mUVs)); auto dummyColors = new std::vector( mVertices->size(), filament::math::float4{1.0f, 1.0f, 1.0f, 1.0f}); vertexBuffer->setBufferAt(*mEngine, 2, VertexBuffer::BufferDescriptor( dummyColors->data(), dummyColors->size() * sizeof(math::float4), [](void *, size_t, void *data) { delete static_cast *>(data); }, dummyColors)); if (mNormals->size() > 0) { std::vector triangles; for (uint32_t i = 0; i < mIndices->size(); i += 3) { triangles.push_back({mIndices->at(i), mIndices->at(i + 1), mIndices->at(i + 2)}); } geometry::SurfaceOrientation::Builder builder; builder.vertexCount(mVertices->size()); builder.normals(mNormals->data()); builder.positions(mVertices->data()); builder.triangleCount(triangles.size()); builder.triangles(triangles.data()); auto orientation = builder.build(); auto quats = new std::vector(mVertices->size()); orientation->getQuats(quats->data(), mVertices->size()); vertexBuffer->setBufferAt(*mEngine, 3, VertexBuffer::BufferDescriptor( quats->data(), quats->size() * sizeof(math::quatf), [](void *, size_t, void *data) { delete static_cast *>(data); }, quats)); } return {vertexBuffer, indexBuffer}; } bool GeometrySceneAssetBuilder::validate() const { if (!mEngine) { Log("Validation failed: No engine"); return false; } if (mVertices->empty()) { Log("Validation failed: No vertices (empty=%d, count=%d)", mVertices->empty(), mVertices->size()); return false; } if (!mNormals->empty()) { assert(mPrimitiveType == RenderableManager::PrimitiveType::TRIANGLES); } if (!mNormals->empty() && mNormals->size() != mVertices->size()) { Log("Validation failed: Normal count mismatch (normals=%d, vertices=%d)", mNormals->size(), mVertices->size()); return false; } if (!mUVs->empty() && mUVs->size() != mVertices->size()) { Log("Validation failed: UV count mismatch (uvs=%d, vertices=%d)", mUVs->size(), mVertices->size()); return false; } if (mIndices->empty()) { Log("Validation failed: No indices (empty=%d, count=%d)", mIndices->empty(), mIndices->size()); return false; } TRACE("Validation passed: vertices=%d, normals=%s, uvs=%d, indices=%d", mVertices->size(), (!mNormals->empty() ? "yes" : "no"), mUVs->size(), mIndices->size()); return true; } } // namespace thermion