From 42d4ef07f0f676ba7e4cebd3f686633496c6d81c Mon Sep 17 00:00:00 2001 From: owner Date: Sun, 15 Mar 2026 21:15:09 -0400 Subject: [PATCH] Upload files to "src" --- src/MorphManager.cpp | 130 +++++++++++++++++++++++++++++++++++++++++++ src/main.cpp | 31 +++++++++++ 2 files changed, 161 insertions(+) create mode 100644 src/MorphManager.cpp create mode 100644 src/main.cpp diff --git a/src/MorphManager.cpp b/src/MorphManager.cpp new file mode 100644 index 0000000..4ae7895 --- /dev/null +++ b/src/MorphManager.cpp @@ -0,0 +1,130 @@ +#include "MorphManager.h" + +#include +#include +#include + +// ───────────────────────────────────────────────────────────────────────────── + +void MorphManager::registerMesh(osg::Geometry* geom, + osg::ref_ptr baseVerts, + osg::ref_ptr baseNormals) { + MeshEntry entry; + entry.geom = geom; + entry.baseVerts = baseVerts; + entry.baseNormals = baseNormals; + m_meshes.push_back(std::move(entry)); +} + +// ───────────────────────────────────────────────────────────────────────────── + +void MorphManager::addTarget(osg::Geometry* geom, + const std::string& name, + osg::ref_ptr deltaVerts, + osg::ref_ptr deltaNormals) { + // Find the mesh entry for this geometry + for (auto& entry : m_meshes) { + if (entry.geom == geom) { + Target t; + t.name = name; + t.deltaVerts = deltaVerts; + t.deltaNormals = deltaNormals; + entry.targets.push_back(std::move(t)); + + // Register weight at 0 if not already known + if (m_weights.find(name) == m_weights.end()) + m_weights[name] = 0.f; + + rebuildNameList(); + return; + } + } + std::cerr << "[morph] addTarget: geometry not registered\n"; +} + +// ───────────────────────────────────────────────────────────────────────────── + +void MorphManager::setWeight(const std::string& name, float weight) { + weight = std::max(0.f, std::min(1.f, weight)); + m_weights[name] = weight; +} + +float MorphManager::getWeight(const std::string& name) const { + auto it = m_weights.find(name); + return it != m_weights.end() ? it->second : 0.f; +} + +void MorphManager::resetAll() { + for (auto& [name, w] : m_weights) + w = 0.f; +} + +// ───────────────────────────────────────────────────────────────────────────── + +void MorphManager::applyWeights() { + static bool firstCall = true; + if (firstCall) { + std::cout << "[morph] applyWeights: tracking " << m_meshes.size() + << " meshes, " << m_weights.size() << " named morphs\n"; + firstCall = false; + } + for (auto& entry : m_meshes) { + if (!entry.geom || !entry.baseVerts) continue; + + // Get the live arrays that OSG is currently rendering + auto* liveVerts = dynamic_cast( + entry.geom->getVertexArray()); + auto* liveNorms = dynamic_cast( + entry.geom->getNormalArray()); + + if (!liveVerts) continue; + + const unsigned numVerts = entry.baseVerts->size(); + if (liveVerts->size() != numVerts) continue; + + // Start from base pose + std::copy(entry.baseVerts->begin(), entry.baseVerts->end(), + liveVerts->begin()); + + if (liveNorms && entry.baseNormals && + liveNorms->size() == entry.baseNormals->size()) { + std::copy(entry.baseNormals->begin(), entry.baseNormals->end(), + liveNorms->begin()); + } + + // Accumulate weighted deltas + for (const auto& target : entry.targets) { + float w = getWeight(target.name); + if (w < 1e-5f) continue; // skip inactive morphs + + // Vertex deltas + if (target.deltaVerts && target.deltaVerts->size() == numVerts) { + for (unsigned i = 0; i < numVerts; ++i) + (*liveVerts)[i] += (*target.deltaVerts)[i] * w; + } + + // Normal deltas (optional — not all exporters write them) + if (liveNorms && target.deltaNormals && + target.deltaNormals->size() == liveNorms->size()) { + for (unsigned i = 0; i < liveNorms->size(); ++i) + (*liveNorms)[i] += (*target.deltaNormals)[i] * w; + } + } + + // Signal OSG to re-upload the VBO data on the next draw. + // dirty() increments the modified count which the VBO manager checks. + // dirtyDisplayList() is a no-op when display lists are off, but harmless. + liveVerts->dirty(); + if (liveNorms) liveNorms->dirty(); + entry.geom->dirtyBound(); + } +} + +// ───────────────────────────────────────────────────────────────────────────── + +void MorphManager::rebuildNameList() { + m_morphNames.clear(); + for (auto& [name, _] : m_weights) + m_morphNames.push_back(name); + std::sort(m_morphNames.begin(), m_morphNames.end()); +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..fcbde41 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,31 @@ +#include +#include +#include "Application.h" + +static void printUsage(const char* prog) { + std::cerr << "Usage: " << prog << " \n" + << " Supported: .pmx .fbx .obj .glb .gltf (anything Assimp reads)\n"; +} + +int main(int argc, char** argv) { + if (argc < 2) { + printUsage(argv[0]); + return 1; + } + + const std::string modelPath = argv[1]; + + Application app; + + if (!app.init()) { + std::cerr << "[fatal] Failed to initialise application.\n"; + return 1; + } + + if (!app.loadModel(modelPath)) { + std::cerr << "[fatal] Could not load model: " << modelPath << "\n"; + return 1; + } + + return app.run(); +}