diff --git a/hecl/blender/BlenderConnection.cpp b/hecl/blender/BlenderConnection.cpp index 1ffbf58a5..ffbd4b17e 100644 --- a/hecl/blender/BlenderConnection.cpp +++ b/hecl/blender/BlenderConnection.cpp @@ -503,8 +503,8 @@ void BlenderConnection::PyOutStream::linkBackground(const char* target, } BlenderConnection::DataStream::Mesh::Mesh -(BlenderConnection& conn, OutputMode outMode, int skinSlotCount, SurfProgFunc& surfProg) -: outputMode(outMode), aabbMin(conn), aabbMax(conn) +(BlenderConnection& conn, HMDLTopology topologyIn, int skinSlotCount, SurfProgFunc& surfProg) +: topology(topologyIn), aabbMin(conn), aabbMax(conn) { uint32_t matSetCount; conn._readBuf(&matSetCount, 4); @@ -587,12 +587,12 @@ BlenderConnection::DataStream::Mesh::Mesh { for (Surface& surf : surfaces) { - std::vector& bank = skinBanks.banks[surf.skinBankIdx]; + SkinBanks::Bank& bank = skinBanks.banks[surf.skinBankIdx]; for (Surface::Vert& vert : surf.verts) { - for (uint32_t i=0 ; i& bank, uint32_t sIdx) } uint32_t BlenderConnection::DataStream::Mesh::SkinBanks::addSurface -(const Surface& surf, int skinSlotCount) +(const Mesh& mesh, const Surface& surf, int skinSlotCount) { if (banks.empty()) addSkinBank(skinSlotCount); std::vector toAdd; if (skinSlotCount > 0) toAdd.reserve(skinSlotCount); - std::vector>::iterator bankIt = banks.begin(); + std::vector::iterator bankIt = banks.begin(); for (;;) { bool done = true; for (; bankIt != banks.end() ; ++bankIt) { - std::vector& bank = *bankIt; + Bank& bank = *bankIt; done = true; for (const Surface::Vert& v : surf.verts) { - if (!VertInBank(bank, v.iSkin) && !VertInBank(toAdd, v.iSkin)) + if (!VertInBank(bank.m_skinIdxs, v.iSkin) && !VertInBank(toAdd, v.iSkin)) { toAdd.push_back(v.iSkin); - if (skinSlotCount > 0 && bank.size() + toAdd.size() > skinSlotCount) + if (skinSlotCount > 0 && bank.m_skinIdxs.size() + toAdd.size() > skinSlotCount) { toAdd.clear(); done = false; @@ -760,8 +760,7 @@ uint32_t BlenderConnection::DataStream::Mesh::SkinBanks::addSurface } if (toAdd.size()) { - for (uint32_t a : toAdd) - bank.push_back(a); + bank.addSkins(mesh, toAdd); toAdd.clear(); } if (done) diff --git a/hecl/blender/BlenderConnection.hpp b/hecl/blender/BlenderConnection.hpp index 180fde1dd..0f3224a64 100644 --- a/hecl/blender/BlenderConnection.hpp +++ b/hecl/blender/BlenderConnection.hpp @@ -21,13 +21,16 @@ #include #include "HECL/HECL.hpp" +#include "HECL/HMDLMeta.hpp" #include +#include namespace HECL { extern LogVisor::LogModule BlenderLog; extern class BlenderConnection* SharedBlenderConnection; +class HMDLBuffers; class BlenderConnection { @@ -374,11 +377,7 @@ public: /** Intermediate mesh representation prepared by blender from a single mesh object */ struct Mesh { - enum OutputMode - { - OutputTriangles, - OutputTriStrips, - } outputMode; + enum HMDLTopology topology; /* Cumulative AABB */ Vector3f aabbMin; @@ -436,6 +435,23 @@ public: uint32_t iBankSkin = -1; Vert(BlenderConnection& conn, const Mesh& parent); + + bool operator==(const Vert& other) const + { + if (iPos != other.iPos) + return false; + if (iNorm != other.iNorm) + return false; + for (int i=0 ; i<4 ; ++i) + if (iColor[i] != other.iColor[i]) + return false; + for (int i=0 ; i<8 ; ++i) + if (iUv[i] != other.iUv[i]) + return false; + if (iSkin != other.iSkin) + return false; + return true; + } }; std::vector verts; @@ -445,33 +461,73 @@ public: struct SkinBanks { - std::vector> banks; - std::vector>::iterator addSkinBank(int skinSlotCount) + struct Bank + { + std::vector m_skinIdxs; + std::vector m_boneIdxs; + + void addSkins(const Mesh& parent, const std::vector& skinIdxs) + { + for (uint32_t sidx : skinIdxs) + { + m_skinIdxs.push_back(sidx); + for (const SkinBind& bind : parent.skins[sidx]) + { + bool found = false; + for (uint32_t bidx : m_boneIdxs) + { + if (bidx == bind.boneIdx) + { + found = true; + break; + } + } + if (!found) + m_boneIdxs.push_back(bind.boneIdx); + } + } + } + + size_t lookupLocalBoneIdx(uint32_t boneIdx) const + { + for (size_t i=0 ; i banks; + std::vector::iterator addSkinBank(int skinSlotCount) { banks.emplace_back(); if (skinSlotCount > 0) - banks.back().reserve(skinSlotCount); + banks.back().m_skinIdxs.reserve(skinSlotCount); return banks.end() - 1; } - uint32_t addSurface(const Surface& surf, int skinSlotCount); + uint32_t addSurface(const Mesh& mesh, const Surface& surf, int skinSlotCount); } skinBanks; using SurfProgFunc = std::function; - Mesh(BlenderConnection& conn, OutputMode outMode, int skinSlotCount, SurfProgFunc& surfProg); + Mesh(BlenderConnection& conn, HMDLTopology topology, int skinSlotCount, SurfProgFunc& surfProg); Mesh getContiguousSkinningVersion() const; + + /** Prepares mesh representation for indexed access on modern APIs. + * Mesh must remain resident for accessing reference members + */ + HMDLBuffers getHMDLBuffers() const; }; - static const char* MeshOutputModeString(Mesh::OutputMode mode) + static const char* MeshOutputModeString(HMDLTopology topology) { static const char* STRS[] = {"TRIANGLES", "TRISTRIPS"}; - return STRS[mode]; + return STRS[topology]; } /** Compile mesh by context (MESH blends only) */ - Mesh compileMesh(Mesh::OutputMode outMode, int skinSlotCount=10, + Mesh compileMesh(HMDLTopology topology, int skinSlotCount=10, Mesh::SurfProgFunc surfProg=[](int){}) { if (m_parent->m_loadedType != TypeMesh) @@ -480,7 +536,7 @@ public: char req[128]; snprintf(req, 128, "MESHCOMPILE %s %d", - MeshOutputModeString(outMode), skinSlotCount); + MeshOutputModeString(topology), skinSlotCount); m_parent->_writeLine(req); char readBuf[256]; @@ -488,11 +544,11 @@ public: if (strcmp(readBuf, "OK")) BlenderLog.report(LogVisor::FatalError, "unable to cook mesh: %s", readBuf); - return Mesh(*m_parent, outMode, skinSlotCount, surfProg); + return Mesh(*m_parent, topology, skinSlotCount, surfProg); } /** Compile mesh by name (AREA blends only) */ - Mesh compileMesh(const std::string& name, Mesh::OutputMode outMode, int skinSlotCount=10, + Mesh compileMesh(const std::string& name, HMDLTopology topology, int skinSlotCount=10, Mesh::SurfProgFunc surfProg=[](int){}) { if (m_parent->m_loadedType != TypeArea) @@ -501,7 +557,7 @@ public: char req[128]; snprintf(req, 128, "MESHCOMPILENAME %s %s %d", name.c_str(), - MeshOutputModeString(outMode), skinSlotCount); + MeshOutputModeString(topology), skinSlotCount); m_parent->_writeLine(req); char readBuf[256]; @@ -509,11 +565,11 @@ public: if (strcmp(readBuf, "OK")) BlenderLog.report(LogVisor::FatalError, "unable to cook mesh '%s': %s", name.c_str(), readBuf); - return Mesh(*m_parent, outMode, skinSlotCount, surfProg); + return Mesh(*m_parent, topology, skinSlotCount, surfProg); } /** Compile all meshes into one (AREA blends only) */ - Mesh compileAllMeshes(Mesh::OutputMode outMode, int skinSlotCount=10, float maxOctantLength=5.0, + Mesh compileAllMeshes(HMDLTopology topology, int skinSlotCount=10, float maxOctantLength=5.0, Mesh::SurfProgFunc surfProg=[](int){}) { if (m_parent->m_loadedType != TypeArea) @@ -522,7 +578,7 @@ public: char req[128]; snprintf(req, 128, "MESHCOMPILEALL %s %d %f", - MeshOutputModeString(outMode), + MeshOutputModeString(topology), skinSlotCount, maxOctantLength); m_parent->_writeLine(req); @@ -531,7 +587,7 @@ public: if (strcmp(readBuf, "OK")) BlenderLog.report(LogVisor::FatalError, "unable to cook all meshes: %s", readBuf); - return Mesh(*m_parent, outMode, skinSlotCount, surfProg); + return Mesh(*m_parent, topology, skinSlotCount, surfProg); } /** Intermediate actor representation prepared by blender from a single HECL actor blend */ @@ -651,6 +707,52 @@ public: }; +class HMDLBuffers +{ +public: + struct Surface; +private: + friend struct BlenderConnection::DataStream::Mesh; + HMDLBuffers(const HMDLMeta& meta, + size_t vboSz, const std::vector& iboData, + std::vector&& surfaces, + const BlenderConnection::DataStream::Mesh::SkinBanks& skinBanks) + : m_metaSz(HECL_HMDL_META_SZ), m_metaData(new uint8_t[HECL_HMDL_META_SZ]), + m_vboSz(vboSz), m_vboData(new uint8_t[vboSz]), + m_iboSz(iboData.size()*4), m_iboData(new uint8_t[iboData.size()*4]), + m_surfaces(std::move(surfaces)), m_skinBanks(skinBanks) + { + { + Athena::io::MemoryWriter w(m_metaData.get(), HECL_HMDL_META_SZ); + meta.write(w); + } + { + Athena::io::MemoryWriter w(m_iboData.get(), m_iboSz); + w.enumerateLittle(iboData); + } + } +public: + size_t m_metaSz; + std::unique_ptr m_metaData; + size_t m_vboSz; + std::unique_ptr m_vboData; + size_t m_iboSz; + std::unique_ptr m_iboData; + + struct Surface + { + Surface(const BlenderConnection::DataStream::Mesh::Surface& origSurf, + atUint32 start, atUint32 count) + : m_origSurf(origSurf), m_start(start), m_count(count) {} + const BlenderConnection::DataStream::Mesh::Surface& m_origSurf; + atUint32 m_start; + atUint32 m_count; + }; + std::vector m_surfaces; + + const BlenderConnection::DataStream::Mesh::SkinBanks& m_skinBanks; +}; + } #endif // BLENDERCONNECTION_HPP diff --git a/hecl/blender/CMakeLists.txt b/hecl/blender/CMakeLists.txt index 234684f56..810a8c566 100644 --- a/hecl/blender/CMakeLists.txt +++ b/hecl/blender/CMakeLists.txt @@ -25,6 +25,7 @@ bintoc(hecl_startup.c hecl_startup.blend HECL_STARTUP) add_library(HECLBlender BlenderConnection.cpp BlenderConnection.hpp + HMDL.cpp hecl_blendershell.py hecl_blendershell.c zip_package.py diff --git a/hecl/blender/HMDL.cpp b/hecl/blender/HMDL.cpp new file mode 100644 index 000000000..df991fb24 --- /dev/null +++ b/hecl/blender/HMDL.cpp @@ -0,0 +1,121 @@ +#include "BlenderConnection.hpp" + +namespace HECL +{ + +HMDLBuffers BlenderConnection::DataStream::Mesh::getHMDLBuffers() const +{ + /* If skinned, compute max weight vec count */ + size_t weightCount = 0; + for (const SkinBanks::Bank& bank : skinBanks.banks) + weightCount = std::max(weightCount, bank.m_boneIdxs.size()); + size_t weightVecCount = weightCount / 4; + if (weightCount % 4) + ++weightVecCount; + + /* Prepare HMDL meta */ + HMDLMeta metaOut; + metaOut.topology = topology; + metaOut.vertStride = (3 + 3 + colorLayerCount + uvLayerCount * 2 + weightVecCount * 4) * 4; + metaOut.colorCount = colorLayerCount; + metaOut.uvCount = uvLayerCount; + metaOut.weightCount = weightVecCount; + + /* Total all verts from all surfaces (for ibo length) */ + size_t boundVerts = 0; + for (const Surface& surf : surfaces) + boundVerts += surf.verts.size(); + + /* Maintain unique vert pool for VBO */ + std::vector> vertPool; + vertPool.reserve(boundVerts); + + /* Target surfaces representation */ + std::vector outSurfaces; + outSurfaces.reserve(surfaces.size()); + + /* Index buffer */ + std::vector iboData; + iboData.reserve(boundVerts); + + for (const Surface& surf : surfaces) + { + size_t iboStart = iboData.size(); + for (const Surface::Vert& v : surf.verts) + { + size_t ti = 0; + bool found = false; + for (const std::pair& tv : vertPool) + { + if (v == *tv.second) + { + iboData.push_back(ti); + found = true; + break; + } + ++ti; + } + if (!found) + { + iboData.push_back(vertPool.size()); + vertPool.emplace_back(&surf, &v); + } + } + outSurfaces.emplace_back(surf, iboStart, iboData.size() - iboStart); + } + + metaOut.vertCount = vertPool.size(); + metaOut.indexCount = iboData.size(); + + size_t vboSz = metaOut.vertCount * metaOut.vertStride; + HMDLBuffers ret(metaOut, vboSz, iboData, std::move(outSurfaces), skinBanks); + Athena::io::MemoryWriter vboW(ret.m_vboData.get(), vboSz); + for (const std::pair& sv : vertPool) + { + const Surface& s = *sv.first; + const Surface::Vert& v = *sv.second; + + vboW.writeVec3fLittle(pos[v.iPos]); + vboW.writeVec3fLittle(norm[v.iNorm]); + + for (int i=0 ; i& binds = skins[v.iSkin]; + auto it = bank.m_boneIdxs.cbegin(); + for (int i=0 ; i +#include + +namespace HECL +{ + +enum HMDLTopology : atUint32 +{ + TopologyTriangles, + TopologyTriStrips, +}; + +#define HECL_HMDL_META_SZ 32 + +struct HMDLMeta : Athena::io::DNA +{ + DECL_DNA + Value magic = SBIG('TACO'); + Value topology; + Value vertStride; + Value vertCount; + Value indexCount; + Value colorCount; + Value uvCount; + Value weightCount; +}; + +} + +#endif // HMDLMETA_HPP diff --git a/hecl/include/HECL/Runtime.hpp b/hecl/include/HECL/Runtime.hpp index d4703b65e..4628bcf71 100644 --- a/hecl/include/HECL/Runtime.hpp +++ b/hecl/include/HECL/Runtime.hpp @@ -115,10 +115,14 @@ public: /** * @brief Integrated reader/constructor/container for HMDL data */ -class HMDLData +struct HMDLData { -public: - HMDLData(boo::IGraphicsDataFactory* factory, const void* data); + boo::IGraphicsBufferS* m_vbo; + boo::IGraphicsBufferS* m_ibo; + boo::IVertexFormat* m_vtxFmt; + + HMDLData(boo::IGraphicsDataFactory* factory, + const void* metaData, const void* vbo, const void* ibo); }; } diff --git a/hecl/lib/CMakeLists.txt b/hecl/lib/CMakeLists.txt index 781bc27d5..4aa49b59d 100644 --- a/hecl/lib/CMakeLists.txt +++ b/hecl/lib/CMakeLists.txt @@ -7,6 +7,7 @@ if(WIN32) list(APPEND PLAT_SRCS winsupport.cpp ../include/HECL/winsupport.hpp) endif() +atdna(atdna_HMDLMeta.cpp ../include/HECL/HMDLMeta.hpp) atdna(atdna_Frontend.cpp ../include/HECL/Frontend.hpp) atdna(atdna_Runtime.cpp ../include/HECL/Runtime.hpp) @@ -15,6 +16,7 @@ add_library(HECLCommon ProjectPath.cpp WideStringConvert.cpp ../include/HECL/HECL.hpp + ../include/HECL/HMDLMeta.hpp ../include/HECL/Backend/Backend.hpp ../include/HECL/Backend/GX.hpp ../include/HECL/Backend/ProgrammableCommon.hpp @@ -22,6 +24,7 @@ add_library(HECLCommon ../include/HECL/Frontend.hpp ../include/HECL/Database.hpp ../include/HECL/Runtime.hpp + atdna_HMDLMeta.cpp atdna_Frontend.cpp atdna_Runtime.cpp ${PLAT_SRCS}) diff --git a/hecl/lib/Runtime/HMDL.cpp b/hecl/lib/Runtime/HMDL.cpp index 2f6c208f9..14a2d0fb4 100644 --- a/hecl/lib/Runtime/HMDL.cpp +++ b/hecl/lib/Runtime/HMDL.cpp @@ -1,12 +1,58 @@ +#include "HECL/HMDLMeta.hpp" #include "HECL/Runtime.hpp" +#include namespace HECL { namespace Runtime { +static LogVisor::LogModule Log("HMDL"); -HMDLData::HMDLData(boo::IGraphicsDataFactory* factory, const void *data) +HMDLData::HMDLData(boo::IGraphicsDataFactory* factory, + const void* metaData, const void* vbo, const void* ibo) { + HMDLMeta meta; + { + Athena::io::MemoryReader r((atUint8*)metaData, HECL_HMDL_META_SZ); + meta.read(r); + } + if (meta.magic != FOURCC('TACO')) + Log.report(LogVisor::FatalError, "invalid HMDL magic"); + + m_vbo = factory->newStaticBuffer(boo::BufferUseVertex, vbo, meta.vertStride, meta.vertCount); + m_ibo = factory->newStaticBuffer(boo::BufferUseIndex, ibo, 4, meta.indexCount); + + size_t elemCount = 2 + meta.colorCount + meta.uvCount + meta.weightCount; + std::unique_ptr vdescs(new boo::VertexElementDescriptor[elemCount]); + for (size_t i=0 ; inewVertexFormat(elemCount, vdescs.get()); } }