diff --git a/CMakeLists.txt b/CMakeLists.txt index 00a53ef122..9fb2ee1fde 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -270,6 +270,7 @@ add_subdirectory(${EXTERNAL}/libgtest/tnt) add_subdirectory(${LIBRARIES}/filabridge) add_subdirectory(${LIBRARIES}/filaflat) add_subdirectory(${LIBRARIES}/filamat) +add_subdirectory(${LIBRARIES}/filameshio) add_subdirectory(${LIBRARIES}/image) add_subdirectory(${LIBRARIES}/math) add_subdirectory(${LIBRARIES}/utils) diff --git a/README.md b/README.md index be963f650f..4a0b744e01 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,7 @@ Many other features have been either prototyped or planned: - `filaflat`: Serialization/deserialization library used for materials - `filagui`: Helper library for [Dear ImGui](https://github.com/ocornut/imgui) - `filamat`: Material generation library + - `filameshio`: Tiny mesh parsing library (see also `tools/filamesh`) - `image`: Image filtering and simple transforms - `imageio`: Image file reading / writing, only intended for internal use - `math`: Math library @@ -132,7 +133,7 @@ Many other features have been either prototyped or planned: - `matinfo` Displays information about materials compiled with `matc` - `mipgen` Generates a series of miplevels from a source image. - `normal-blending`: Tool to blend normal maps - - `roughness-prefilter`: Pre-filters a roughness map from a normal map to reduce aliasing + - `roughness-prefilter`: Pre-filters a roughness map from a normal map to reduce aliasing - `skygen`: Physically-based sky environment texture generator - `specular-color`: Computes the specular color of conductors based on spectral data @@ -730,7 +731,7 @@ in your build directory). These sample apps expect a path to a directory contain for the IBL. To generate an IBL simply use this command: ``` -cmgen -x ./ibls/ my_ibl.exr +cmgen -x ./ibls/ my_ibl.exr ``` The source environment map can be a PNG (8 or 16 bit), a PSD (16 or 32 bit), an HDR or an OpenEXR @@ -870,7 +871,7 @@ Host tools (such as `matc` or `cmgen`) can use external dependencies freely. ## How to make contributions Please read and follow the steps in [CONTRIBUTING.md](/CONTRIBUTING.md). Make sure you are -familiar with the [code style](/CODE_STYLE.md). +familiar with the [code style](/CODE_STYLE.md). ## License diff --git a/libs/filamentjs/CMakeLists.txt b/libs/filamentjs/CMakeLists.txt index 56b7334588..2d3433c2ca 100644 --- a/libs/filamentjs/CMakeLists.txt +++ b/libs/filamentjs/CMakeLists.txt @@ -18,4 +18,4 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COPTS}") add_executable(filamentjs jsenums.cpp jsbindings.cpp) set_target_properties(filamentjs PROPERTIES OUTPUT_NAME filament) -target_link_libraries(filamentjs PRIVATE filament math utils image) +target_link_libraries(filamentjs PRIVATE filament math utils image filameshio) diff --git a/libs/filameshio/CMakeLists.txt b/libs/filameshio/CMakeLists.txt new file mode 100644 index 0000000000..57d9bf770b --- /dev/null +++ b/libs/filameshio/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.1) +project(filameshio) + +set(TARGET filameshio) +set(PUBLIC_HDR_DIR include) + +# ================================================================================================== +# Sources and headers +# ================================================================================================== +set(PUBLIC_HDRS ${PUBLIC_HDR_DIR}/${TARGET}/MeshIO.h) +set(DIST_HDRS ${PUBLIC_HDR_DIR}/${TARGET}/MeshIO.h) +set(SRCS src/MeshIO.cpp) + +# ================================================================================================== +# Includes and target definition +# ================================================================================================== +include_directories(${PUBLIC_HDR_DIR}) +add_library(${TARGET} STATIC ${PUBLIC_HDRS} ${SRCS}) +target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR}) +target_link_libraries(${TARGET} PUBLIC filament) + +# ================================================================================================== +# Installation +# ================================================================================================== +set(INSTALL_TYPE ARCHIVE) +install(TARGETS ${TARGET} ${INSTALL_TYPE} DESTINATION lib/${DIST_DIR}) +install(FILES ${DIST_HDRS} DESTINATION include/${TARGET}) diff --git a/samples/app/MeshIO.h b/libs/filameshio/include/filameshio/MeshIO.h similarity index 54% rename from samples/app/MeshIO.h rename to libs/filameshio/include/filameshio/MeshIO.h index 3499db76f8..b82b52fbd6 100644 --- a/samples/app/MeshIO.h +++ b/libs/filameshio/include/filameshio/MeshIO.h @@ -14,34 +14,44 @@ * limitations under the License. */ -#ifndef TNT_FILAMENT_SAMPLE_MESHIO_H -#define TNT_FILAMENT_SAMPLE_MESHIO_H +#ifndef TNT_FILAMENT_FILAMESHIO_MESHIO_H +#define TNT_FILAMENT_FILAMESHIO_MESHIO_H + +#include +#include #include -#include +#include namespace filament { class Engine; - class Renderable; class VertexBuffer; class IndexBuffer; class MaterialInstance; } -namespace utils { - class Path; -} - class MeshIO { public: + using Callback = void(*)(void* buffer, size_t size, void* user); + using MaterialRegistry = std::map; + struct Mesh { utils::Entity renderable; filament::VertexBuffer* vertexBuffer = nullptr; filament::IndexBuffer* indexBuffer = nullptr; }; - static Mesh loadMeshFromFile(filament::Engine* engine, const utils::Path& path, - const std::map& materials); + static Mesh loadMeshFromFile(filament::Engine* engine, + const utils::Path& path, + const MaterialRegistry& materials); + + static Mesh loadMeshFromBuffer(filament::Engine* engine, + void const* data, Callback destructor, void* user, + const MaterialRegistry& materials); + + static Mesh loadMeshFromBuffer(filament::Engine* engine, + void const* data, Callback destructor, void* user, + filament::MaterialInstance* defaultMaterial); }; -#endif //TNT_FILAMENT_SAMPLE_MESHIO_H +#endif // TNT_FILAMENT_FILAMESHIO_MESHIO_H diff --git a/samples/app/MeshIO.cpp b/libs/filameshio/src/MeshIO.cpp similarity index 64% rename from samples/app/MeshIO.cpp rename to libs/filameshio/src/MeshIO.cpp index b1780337e0..fef95f66fa 100644 --- a/samples/app/MeshIO.cpp +++ b/libs/filameshio/src/MeshIO.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "MeshIO.h" +#include #include #include @@ -44,6 +44,8 @@ using namespace filament; using namespace math; +#define DEFAULT_MATERIAL "DefaultMaterial" + static size_t fileSize(int fd) { size_t filesize; filesize = (size_t) lseek(fd, 0, SEEK_END); @@ -90,7 +92,7 @@ struct Part { }; MeshIO::Mesh MeshIO::loadMeshFromFile(filament::Engine* engine, const utils::Path& path, - const std::map& materials) { + const MaterialRegistry& materials) { Mesh mesh; @@ -181,7 +183,7 @@ MeshIO::Mesh MeshIO::loadMeshFromFile(filament::Engine* engine, const utils::Pat if (m != materials.end()) { builder.material(i, m->second); } else { - builder.material(i, materials.at("DefaultMaterial")); + builder.material(i, materials.at(DEFAULT_MATERIAL)); } } @@ -196,3 +198,89 @@ MeshIO::Mesh MeshIO::loadMeshFromFile(filament::Engine* engine, const utils::Pat return mesh; } + +MeshIO::Mesh MeshIO::loadMeshFromBuffer(filament::Engine* engine, + void const* data, Callback destructor, void* user, + MaterialInstance* defaultMaterial) { + MaterialRegistry reg; + reg[DEFAULT_MATERIAL] = defaultMaterial; + return loadMeshFromBuffer(engine, data, destructor, user, reg); +} + +MeshIO::Mesh MeshIO::loadMeshFromBuffer(filament::Engine* engine, + void const* data, Callback destructor, void* user, + const MaterialRegistry& materials) { + const char* p = (const char *) data; + if (strncmp("FILAMESH", p, 8)) { + puts("Magic string not found."); + abort(); + } + p += 8; + + Header* header = (Header*) p; + p += sizeof(Header); + + char const* vertexData = p; + p += header->vertexSize; + + char const* indices = p; + p += header->indexSize; + + Part* parts = (Part*) p; + p += header->parts * sizeof(Part); + + uint32_t materialCount = (uint32_t) *p; + p += sizeof(uint32_t); + + std::vector partsMaterial(materialCount); + for (size_t i = 0; i < materialCount; i++) { + uint32_t nameLength = (uint32_t) *p; + p += sizeof(uint32_t); + partsMaterial[i] = p; + p += nameLength + 1; // null terminated + } + + Mesh mesh; + + mesh.indexBuffer = IndexBuffer::Builder() + .indexCount(header->indexCount) + .bufferType(header->indexType ? IndexBuffer::IndexType::USHORT + : IndexBuffer::IndexType::UINT) + .build(*engine); + + mesh.indexBuffer->setBuffer(*engine, + IndexBuffer::BufferDescriptor(indices, header->indexSize, destructor, user)); + + VertexBuffer::Builder vbb; + vbb.vertexCount(header->vertexCount) + .bufferCount(1) + .normalized(VertexAttribute::TANGENTS); + + mesh.vertexBuffer = vbb + .attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::HALF4, + header->offsetPosition, uint8_t(header->stridePosition)) + .attribute(VertexAttribute::TANGENTS, 0, VertexBuffer::AttributeType::SHORT4, + header->offsetTangents, uint8_t(header->strideTangents)) + .attribute(VertexAttribute::UV0, 0, VertexBuffer::AttributeType::HALF2, + header->offsetUV0, uint8_t(header->strideUV0)) + .build(*engine); + + mesh.vertexBuffer->setBufferAt(*engine, 0, + VertexBuffer::BufferDescriptor(vertexData, header->vertexSize, destructor, user)); + + mesh.renderable = utils::EntityManager::get().create(); + + RenderableManager::Builder builder(header->parts); + builder.boundingBox(header->aabb); + const auto defaultmi = materials.at(DEFAULT_MATERIAL); + for (size_t i = 0; i < header->parts; i++) { + builder.geometry(i, RenderableManager::PrimitiveType::TRIANGLES, + mesh.vertexBuffer, mesh.indexBuffer, parts[i].offset, + parts[i].minIndex, parts[i].maxIndex, parts[i].indexCount); + const auto miter = materials.find(partsMaterial[i]); + builder.material(i, miter == materials.end() ? defaultmi : miter->second); + } + builder.build(*engine, mesh.renderable); + + return mesh; +} diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 6a85778420..ee340a6088 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -127,10 +127,9 @@ function(add_filamesh_demo NAME) include_directories(${GENERATION_ROOT}) add_executable( ${NAME} - ${NAME}.cpp - app/MeshIO.cpp) + ${NAME}.cpp) add_dependencies(${NAME} sample_materials) - target_link_libraries(${NAME} PRIVATE ${APP_LIBS}) + target_link_libraries(${NAME} PRIVATE ${APP_LIBS} filameshio) target_compile_options(${NAME} PRIVATE ${COMPILER_FLAGS}) endfunction() diff --git a/samples/sample_cloth.cpp b/samples/sample_cloth.cpp index 0fb114d1f0..29f7e9169c 100644 --- a/samples/sample_cloth.cpp +++ b/samples/sample_cloth.cpp @@ -37,13 +37,13 @@ #include "app/Config.h" #include "app/FilamentApp.h" -#include "app/MeshIO.h" #include #include #include +#include using namespace math; using namespace filament; diff --git a/samples/sample_normal_map.cpp b/samples/sample_normal_map.cpp index b6dae57304..9d766e1288 100644 --- a/samples/sample_normal_map.cpp +++ b/samples/sample_normal_map.cpp @@ -37,13 +37,12 @@ #include "app/Config.h" #include "app/FilamentApp.h" -#include "app/MeshIO.h" - #include #include #include +#include using namespace math; using namespace filament; diff --git a/samples/sample_opacity_mask.cpp b/samples/sample_opacity_mask.cpp index 2a51636b25..87249b6f85 100644 --- a/samples/sample_opacity_mask.cpp +++ b/samples/sample_opacity_mask.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -36,13 +37,12 @@ #include "app/Config.h" #include "app/FilamentApp.h" -#include "app/MeshIO.h" #include #include #include -#include +#include using namespace math; using namespace filament; diff --git a/samples/sample_subsurface.cpp b/samples/sample_subsurface.cpp index ef8e703daf..5e913001f3 100644 --- a/samples/sample_subsurface.cpp +++ b/samples/sample_subsurface.cpp @@ -37,13 +37,13 @@ #include "app/Config.h" #include "app/FilamentApp.h" -#include "app/MeshIO.h" #include #include #include +#include using namespace math; using namespace filament; diff --git a/samples/web/CMakeLists.txt b/samples/web/CMakeLists.txt index 4092268999..987a51fec9 100644 --- a/samples/web/CMakeLists.txt +++ b/samples/web/CMakeLists.txt @@ -183,12 +183,10 @@ function(add_demo NAME) include_directories(${GENERATION_ROOT}) add_executable(${NAME} ${NAME}.cpp ../app/CameraManipulator.cpp - filamesh.h - filamesh.cpp filaweb.h filaweb.cpp) add_dependencies(${NAME} sample_materials sample_assets) - target_link_libraries(${NAME} PRIVATE filament filagui image math utils) + target_link_libraries(${NAME} PRIVATE filament filagui filameshio image math utils) # Copy the generated js and wasm files into the public folder, as well as the app-specific # manually-written HTML file. diff --git a/samples/web/filamesh.cpp b/samples/web/filamesh.cpp deleted file mode 100644 index dc32f2038a..0000000000 --- a/samples/web/filamesh.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "filamesh.h" - -#include -#include - -#include - -#include -#include - -#include -#include - -using namespace filament; -using namespace math; -using namespace utils; -using namespace std; - -struct Header { - uint32_t version; - uint32_t parts; - Box aabb; - uint32_t interleaved; - uint32_t offsetPosition; - uint32_t stridePosition; - uint32_t offsetTangents; - uint32_t strideTangents; - uint32_t offsetColor; - uint32_t strideColor; - uint32_t offsetUV0; - uint32_t strideUV0; - uint32_t offsetUV1; - uint32_t strideUV1; - uint32_t vertexCount; - uint32_t vertexSize; - uint32_t indexType; - uint32_t indexCount; - uint32_t indexSize; -}; - -struct Part { - uint32_t offset; - uint32_t indexCount; - uint32_t minIndex; - uint32_t maxIndex; - uint32_t materialID; - Box aabb; -}; - -Filamesh decodeMesh(Engine& engine, void const* data, off_t offset, MaterialInstance* mi, - driver::BufferDescriptor::Callback destructor, void* user) { - const char* p = (const char *) data + offset; - if (strncmp("FILAMESH", p, 8)) { - puts("Magic string not found."); - abort(); - } - p += 8; - - Header* header = (Header*) p; - p += sizeof(Header); - - char const* vertexData = p; - p += header->vertexSize; - - char const* indices = p; - p += header->indexSize; - - Part* parts = (Part*) p; - p += header->parts * sizeof(Part); - - uint32_t materialCount = (uint32_t) *p; - p += sizeof(uint32_t); - - vector partsMaterial; - partsMaterial.resize(materialCount); - - for (size_t i = 0; i < materialCount; i++) { - uint32_t nameLength = (uint32_t) *p; - p += sizeof(uint32_t); - partsMaterial[i] = p; - p += nameLength + 1; // null terminated - } - - Mesh* mesh = new Mesh(); - - mesh->indexBuffer = IndexBuffer::Builder() - .indexCount(header->indexCount) - .bufferType(header->indexType ? IndexBuffer::IndexType::USHORT - : IndexBuffer::IndexType::UINT) - .build(engine); - - mesh->indexBuffer->setBuffer( - engine, IndexBuffer::BufferDescriptor(indices, header->indexSize)); - - VertexBuffer::Builder vbb; - vbb.vertexCount(header->vertexCount) - .bufferCount(1) - .normalized(VertexAttribute::TANGENTS); - - mesh->vertexBuffer = vbb - .attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::HALF4, - header->offsetPosition, uint8_t(header->stridePosition)) - .attribute(VertexAttribute::TANGENTS, 0, VertexBuffer::AttributeType::SHORT4, - header->offsetTangents, uint8_t(header->strideTangents)) - .attribute(VertexAttribute::UV0, 0, VertexBuffer::AttributeType::HALF2, - header->offsetUV0, uint8_t(header->strideUV0)) - .build(engine); - - VertexBuffer::BufferDescriptor buffer(vertexData, header->vertexSize, - destructor, user); - mesh->vertexBuffer->setBufferAt(engine, 0, move(buffer)); - - mesh->renderable = EntityManager::get().create(); - - RenderableManager::Builder builder(header->parts); - builder.boundingBox(header->aabb); - for (size_t i = 0; i < header->parts; i++) { - builder.geometry(i, RenderableManager::PrimitiveType::TRIANGLES, - mesh->vertexBuffer, mesh->indexBuffer, parts[i].offset, - parts[i].minIndex, parts[i].maxIndex, parts[i].indexCount); - builder.material(i, mi); - } - builder.build(engine, mesh->renderable); - - return Filamesh(mesh); -} diff --git a/samples/web/filamesh.h b/samples/web/filamesh.h deleted file mode 100644 index ad4c251988..0000000000 --- a/samples/web/filamesh.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include - -#include - -#include - -#include - -struct Mesh { - utils::Entity renderable; - filament::VertexBuffer* vertexBuffer = {}; - filament::IndexBuffer* indexBuffer = {}; -}; - -using Filamesh = std::unique_ptr; - -Filamesh decodeMesh(filament::Engine& engine, void const* data, off_t offset, - filament::MaterialInstance* mi, filament::driver::BufferDescriptor::Callback destructor, - void* user); diff --git a/samples/web/sandbox.cpp b/samples/web/sandbox.cpp index 651636d971..e8238713f6 100644 --- a/samples/web/sandbox.cpp +++ b/samples/web/sandbox.cpp @@ -16,7 +16,6 @@ #include "../material_sandbox.h" -#include "filamesh.h" #include "filaweb.h" #include @@ -26,6 +25,8 @@ #include #include +#include + #include #include @@ -44,7 +45,7 @@ using WrapMode = TextureSampler::WrapMode; using Format = Texture::InternalFormat; struct SandboxApp { - Filamesh filamesh; + MeshIO::Mesh filamesh; Camera* cam; SandboxParameters params; filaweb::SkyLight skylight; @@ -81,12 +82,18 @@ void setup(Engine* engine, View* view, Scene* scene) { // Create mesh. const uint8_t* mdata = mesh.rawData.get(); const auto destructor = [](void* buffer, size_t size, void* user) { - auto asset = (filaweb::Asset*) user; - asset->rawData.reset(); + // This destruction lambda is called for the vertex buffer AND index buffer, so release CPU + // memory only after both have been uploaded to the GPU. The static count trick here works + // only because we create a single mesh in this demo. + static int count = 0; + if (++count == 2) { + auto asset = (filaweb::Asset*) user; + asset->rawData.reset(); + } }; MaterialInstance* materialInstance = app.params.materialInstance[MATERIAL_LIT]; - app.filamesh = decodeMesh(*engine, mdata, 0, materialInstance, destructor, &mesh); - scene->addEntity(app.filamesh->renderable); + app.filamesh = MeshIO::loadMeshFromBuffer(engine, mdata, destructor, &mesh, materialInstance); + scene->addEntity(app.filamesh.renderable); // Create the sun. scene->addEntity(app.params.light); @@ -155,7 +162,7 @@ void ui(Engine* engine, View* view) { MaterialInstance* materialInstance = updateInstances(params, *engine); auto& rcm = engine->getRenderableManager(); - auto instance = rcm.getInstance(app.filamesh->renderable); + auto instance = rcm.getInstance(app.filamesh.renderable); for (size_t i = 0; i < rcm.getPrimitiveCount(instance); i++) { rcm.setMaterialInstanceAt(instance, i, materialInstance); } diff --git a/samples/web/suzanne.cpp b/samples/web/suzanne.cpp index 9c66a6dfac..9be6542b11 100644 --- a/samples/web/suzanne.cpp +++ b/samples/web/suzanne.cpp @@ -14,7 +14,6 @@ * limitations under the License. */ -#include "filamesh.h" #include "filaweb.h" #include @@ -28,6 +27,8 @@ #include #include +#include + #include #include @@ -43,7 +44,7 @@ using WrapMode = TextureSampler::WrapMode; using Format = Texture::InternalFormat; struct SuzanneApp { - Filamesh filamesh; + MeshIO::Mesh filamesh; Material* mat; MaterialInstance* mi; Camera* cam; @@ -162,11 +163,18 @@ void setup(Engine* engine, View* view, Scene* scene) { printf("%s: %d bytes\n", "mesh", mesh.rawSize); const uint8_t* mdata = mesh.rawData.get(); const auto destructor = [](void* buffer, size_t size, void* user) { - auto asset = (filaweb::Asset*) user; - asset->rawData.reset(); + // This destruction lambda is called for the vertex buffer AND index buffer, so release CPU + // memory only after both have been uploaded to the GPU. The static count trick here works + // only because we create a single mesh in this demo. + static int count = 0; + if (++count == 2) { + auto asset = (filaweb::Asset*) user; + asset->rawData.reset(); + } }; - app.filamesh = decodeMesh(*engine, mdata, 0, app.mi, destructor, &mesh); - scene->addEntity(app.filamesh->renderable); + + app.filamesh = MeshIO::loadMeshFromBuffer(engine, mdata, destructor, &mesh, app.mi); + scene->addEntity(app.filamesh.renderable); // Create textures. TextureSampler sampler(TextureSampler::MinFilter::LINEAR_MIPMAP_LINEAR, MagFilter::LINEAR, @@ -243,7 +251,7 @@ void animate(Engine* engine, View* view, double now) { double ratio = double(width) / height; app.cam->setProjection(45.0, ratio, 0.1, 50.0, ratio < 1 ? Fov::HORIZONTAL : Fov::VERTICAL); auto& tcm = engine->getTransformManager(); - tcm.setTransform(tcm.getInstance(app.filamesh->renderable), + tcm.setTransform(tcm.getInstance(app.filamesh.renderable), mat4f{mat3f{1.0}, float3{0.0f, 0.0f, -4.0f}} * mat4f::rotate(now, math::float3{0, 1, 0})); };