Upload files to "src"
This commit is contained in:
130
src/MorphManager.cpp
Normal file
130
src/MorphManager.cpp
Normal 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
31
src/main.cpp
Normal 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();
|
||||
}
|
||||
Reference in New Issue
Block a user