Upload files to "src"

This commit is contained in:
2026-03-15 21:15:09 -04:00
parent 8ae6d52ae5
commit 42d4ef07f0
2 changed files with 161 additions and 0 deletions

130
src/MorphManager.cpp Normal file
View File

@@ -0,0 +1,130 @@
#include "MorphManager.h"
#include <algorithm>
#include <iostream>
#include <cstring>
// ─────────────────────────────────────────────────────────────────────────────
void MorphManager::registerMesh(osg::Geometry* geom,
osg::ref_ptr<osg::Vec3Array> baseVerts,
osg::ref_ptr<osg::Vec3Array> 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<osg::Vec3Array> deltaVerts,
osg::ref_ptr<osg::Vec3Array> 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<osg::Vec3Array*>(
entry.geom->getVertexArray());
auto* liveNorms = dynamic_cast<osg::Vec3Array*>(
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());
}

31
src/main.cpp Normal file
View File

@@ -0,0 +1,31 @@
#include <iostream>
#include <string>
#include "Application.h"
static void printUsage(const char* prog) {
std::cerr << "Usage: " << prog << " <model_file>\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();
}