Working on morph targets

This commit is contained in:
2025-10-07 23:14:15 +03:00
parent 19a1275a8a
commit 3645557520
14 changed files with 731 additions and 85 deletions

9
morph/CMakeLists.txt Normal file
View File

@@ -0,0 +1,9 @@
project(morph)
add_executable(MorphTargetsResearch MorphTargetsResearch.cpp)
target_link_libraries(MorphTargetsResearch OgreBites OgreBullet OgrePaging ${BULLET_DYNAMICS_LIBRARY} ${BULLET_COLLISION_LIBRARY} ${BULLET_MATH_LIBRARY} ${ASSIMP_LIBRARIES}
-Wl,--as-needed
)
if(OGRE_STATIC)
target_link_options(MorphTargetsResearch PRIVATE -static-libstdc++ -static-libgcc)
endif()
add_dependencies(MorphTargetsResearch stage_files import_vrm)

View File

@@ -0,0 +1,641 @@
#include <iostream>
#include <Ogre.h>
#include <OgreApplicationContext.h>
class App;
static void getSubmeshNormals(const Ogre::Mesh *mesh,
const Ogre::SubMesh *submesh,
std::vector<Ogre::Vector3> &normals)
{
int j;
float *pReal;
int vertex_count = 0;
if (submesh->useSharedVertices)
vertex_count += mesh->sharedVertexData->vertexCount;
else
vertex_count += submesh->vertexData->vertexCount;
Ogre::HardwareVertexBufferSharedPtr vbuf;
Ogre::VertexData *vertex_data = submesh->useSharedVertices ?
mesh->sharedVertexData :
submesh->vertexData;
const Ogre::VertexElement *normalsElem =
vertex_data->vertexDeclaration->findElementBySemantic(
Ogre::VES_NORMAL);
if (!normalsElem)
return;
OgreAssert(normals.size() == 0 || normals.size() == vertex_count,
"bad vertex count");
normals.resize(vertex_count);
vbuf = vertex_data->vertexBufferBinding->getBuffer(
normalsElem->getSource());
unsigned char *vertex = static_cast<unsigned char *>(
vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
for (j = 0; j < vertex_data->vertexCount;
++j, vertex += vbuf->getVertexSize()) {
normalsElem->baseVertexPointerToElement(vertex, &pReal);
normals[j] = Ogre::Vector3(pReal[0], pReal[1], pReal[2]);
}
vbuf->unlock();
}
static void getSubmeshUVs(const Ogre::Mesh *mesh, const Ogre::SubMesh *submesh,
std::vector<Ogre::Vector2> &uvs, int index)
{
int j;
float *pReal;
Ogre::HardwareVertexBufferSharedPtr vbuf;
Ogre::VertexData *vertex_data = submesh->useSharedVertices ?
mesh->sharedVertexData :
submesh->vertexData;
const Ogre::VertexElement *uvElem =
vertex_data->vertexDeclaration->findElementBySemantic(
Ogre::VES_TEXTURE_COORDINATES, index);
int vertex_count = 0;
if (submesh->useSharedVertices)
vertex_count += mesh->sharedVertexData->vertexCount;
else
vertex_count += submesh->vertexData->vertexCount;
if (!uvElem)
return;
OgreAssert(uvs.size() == 0 || uvs.size() == vertex_count,
"bad vertex count");
uvs.resize(vertex_count);
vbuf = vertex_data->vertexBufferBinding->getBuffer(uvElem->getSource());
unsigned char *uv = static_cast<unsigned char *>(
vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
for (j = 0; j < vertex_data->vertexCount; ++j) {
uvElem->baseVertexPointerToElement(uv, &pReal);
uvs[j] = Ogre::Vector2(pReal[0], pReal[1]);
uv += vbuf->getVertexSize();
}
vbuf->unlock();
}
static void getSubmeshVertices(const Ogre::Mesh *mesh,
const Ogre::SubMesh *submesh,
std::vector<Ogre::Vector3> &vertices)
{
int j;
float *pReal;
int vertex_count = 0;
if (submesh->useSharedVertices)
vertex_count += mesh->sharedVertexData->vertexCount;
else
vertex_count += submesh->vertexData->vertexCount;
Ogre::HardwareVertexBufferSharedPtr vbuf;
Ogre::VertexData *vertex_data = submesh->useSharedVertices ?
mesh->sharedVertexData :
submesh->vertexData;
const Ogre::VertexElement *posElem =
vertex_data->vertexDeclaration->findElementBySemantic(
Ogre::VES_POSITION);
if (!posElem)
return;
OgreAssert(vertices.size() == 0 || vertices.size() == vertex_count,
"bad vertex count");
vertices.resize(vertex_count);
vbuf = vertex_data->vertexBufferBinding->getBuffer(
posElem->getSource());
unsigned char *vertex = static_cast<unsigned char *>(
vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
for (j = 0; j < vertex_data->vertexCount;
++j, vertex += vbuf->getVertexSize()) {
posElem->baseVertexPointerToElement(vertex, &pReal);
vertices[j] = Ogre::Vector3(pReal[0], pReal[1], pReal[2]);
}
vbuf->unlock();
}
static void getSubmeshIndices(const Ogre::Mesh *mesh,
const Ogre::SubMesh *submesh,
std::vector<unsigned long> &indices)
{
int index_count = 0;
index_count += submesh->indexData->indexCount;
int index_offset = 0;
indices.resize(index_count);
Ogre::IndexData *index_data = submesh->indexData;
size_t numTris = index_data->indexCount / 3;
Ogre::HardwareIndexBufferSharedPtr ibuf = index_data->indexBuffer;
bool use32bitindexes =
(ibuf->getType() == Ogre::HardwareIndexBuffer::IT_32BIT);
unsigned long *pLong = static_cast<unsigned long *>(
ibuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
unsigned short *pShort = reinterpret_cast<unsigned short *>(pLong);
size_t offset = 0;
if (use32bitindexes) {
for (size_t k = 0; k < numTris * 3; ++k) {
indices[index_offset++] =
pLong[k] + static_cast<unsigned long>(offset);
}
} else {
for (size_t k = 0; k < numTris * 3; ++k) {
indices[index_offset++] =
static_cast<unsigned long>(pShort[k]) +
static_cast<unsigned long>(offset);
}
}
ibuf->unlock();
}
struct SubMeshInformation {
Ogre::String materialName;
bool sharedVertices;
std::vector<Ogre::Vector3> vertices;
std::vector<Ogre::Vector3> normals;
std::vector<Ogre::Vector2> uvs;
std::vector<Ogre::Vector2> uv2s;
std::vector<unsigned long> indices;
Ogre::SubMesh::LODFaceList lodFaceList;
Ogre::Mesh::VertexBoneAssignmentList boneList;
void createSubmesh(Ogre::Mesh *mesh)
{
int i;
Ogre::SubMesh *out = mesh->createSubMesh();
out->useSharedVertices = false;
out->vertexData = new Ogre::VertexData();
size_t currOffset = 0;
Ogre::VertexDeclaration *vertexDecl =
out->vertexData->vertexDeclaration;
vertexDecl->addElement(0, currOffset, Ogre::VET_FLOAT3,
Ogre::VES_POSITION);
currOffset +=
Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);
vertexDecl->addElement(0, currOffset, Ogre::VET_FLOAT3,
Ogre::VES_NORMAL);
currOffset +=
Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);
vertexDecl->addElement(0, currOffset, Ogre::VET_FLOAT2,
Ogre::VES_TEXTURE_COORDINATES, 0);
currOffset +=
Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2);
out->vertexData->vertexCount = vertices.size();
Ogre::HardwareVertexBufferSharedPtr vbuf =
Ogre::HardwareBufferManager::getSingleton()
.createVertexBuffer(
vertexDecl->getVertexSize(0),
out->vertexData->vertexCount,
Ogre::HardwareBuffer::
HBU_STATIC_WRITE_ONLY, // only GPU side
false);
Ogre::VertexBufferBinding *binding =
out->vertexData->vertexBufferBinding;
binding->setBinding(0, vbuf);
float *pvertex = static_cast<float *>(
vbuf->lock(Ogre::HardwareBuffer::HBL_DISCARD));
out->indexData->indexCount = indices.size();
out->indexData->indexBuffer =
Ogre::HardwareBufferManager::getSingleton().createIndexBuffer(
vertices.size() < 32768 ?
Ogre::HardwareIndexBuffer::IT_16BIT :
Ogre::HardwareIndexBuffer::IT_32BIT,
out->indexData->indexCount,
Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY,
false);
OgreAssert(normals.size() == vertices.size(), "bad normals");
for (i = 0; i < vertices.size(); i++) {
*pvertex++ = vertices[i].x;
*pvertex++ = vertices[i].y;
*pvertex++ = vertices[i].z;
if (normals.size() > 0) {
*pvertex++ = normals[i].x;
*pvertex++ = normals[i].y;
*pvertex++ = normals[i].z;
} else {
*pvertex++ = 0.0f;
*pvertex++ = 1.0f;
*pvertex++ = 0.0f;
}
if (uvs.size() > 0) {
*pvertex++ = uvs[i].x;
*pvertex++ = uvs[i].y;
} else {
*pvertex++ = 0.0f;
*pvertex++ = 1.0f;
}
}
vbuf->unlock();
Ogre::HardwareIndexBufferSharedPtr ibuf =
out->indexData->indexBuffer;
if (vertices.size() < 32768) {
unsigned short *pindices =
static_cast<unsigned short *>(ibuf->lock(
Ogre::HardwareBuffer::HBL_DISCARD));
for (i = 0; i < indices.size(); i++)
*pindices++ = (unsigned short)indices[i];
ibuf->unlock();
} else {
unsigned long *pindices = static_cast<unsigned long *>(
ibuf->lock(Ogre::HardwareBuffer::HBL_DISCARD));
for (i = 0; i < indices.size(); i++)
*pindices++ = indices[i];
ibuf->unlock();
}
out->clearBoneAssignments();
auto vbass_it = boneList.begin();
while (vbass_it != boneList.end()) {
out->addBoneAssignment(vbass_it->second);
vbass_it++;
}
out->setMaterialName(materialName);
}
};
static void getSubMeshInformation(const Ogre::Mesh *mesh,
const Ogre::SubMesh *submesh,
struct SubMeshInformation *info)
{
info->materialName = submesh->getMaterialName();
info->sharedVertices = submesh->useSharedVertices;
info->lodFaceList = submesh->mLodFaceList;
info->boneList = submesh->getBoneAssignments();
getSubmeshIndices(mesh, submesh, info->indices);
getSubmeshVertices(mesh, submesh, info->vertices);
getSubmeshNormals(mesh, submesh, info->normals);
getSubmeshUVs(mesh, submesh, info->uvs, 0);
getSubmeshUVs(mesh, submesh, info->uv2s, 1);
}
struct MeshInformation {
struct MeshMorphTarget {
Ogre::SkeletonPtr skelp;
struct DataChange {
int index;
int submesh;
Ogre::Vector3 vertex;
Ogre::Vector3 normal;
};
std::vector<DataChange> vertices;
float weight;
};
std::map<Ogre::String, struct MeshMorphTarget> targets;
std::vector<struct SubMeshInformation> submesh;
Ogre::SkeletonPtr skelp;
Ogre::String skelName;
MeshInformation(Ogre::Mesh *prototype)
{
int i;
skelName = prototype->getSkeletonName();
skelp = Ogre::SkeletonManager::getSingleton().getByName(
prototype->getSkeletonName(), "Characters");
OgreAssert(skelp, "Could not load skeleton " +
prototype->getSkeletonName());
submesh.resize(prototype->getNumSubMeshes());
for (i = 0; i < submesh.size(); i++) {
getSubMeshInformation(prototype,
prototype->getSubMesh(i),
&submesh[i]);
std::cout << i << " " << "mesh material name: "
<< submesh[i].materialName << std::endl;
std::cout << i << " "
<< "shared: " << submesh[i].sharedVertices
<< std::endl;
std::cout << i << " " << "vertex_count: "
<< submesh[i].vertices.size() << std::endl;
std::cout << i << " " << "index_count: "
<< submesh[i].indices.size() << std::endl;
}
}
Ogre::MeshPtr create(const Ogre::String &meshName)
{
int j;
Ogre::MeshPtr baseMesh =
Ogre::MeshManager::getSingleton().createManual(
meshName, Ogre::ResourceGroupManager::
DEFAULT_RESOURCE_GROUP_NAME);
applyMorphTargets();
for (j = 0; j < submesh.size(); j++)
submesh[j].createSubmesh(baseMesh.get());
//define a extreme boundary values
Ogre::Real max_x = -1e+8;
Ogre::Real min_x = 1e+8;
Ogre::Real max_y = -1e+8;
Ogre::Real min_y = 1e+8;
Ogre::Real max_z = -1e+8;
Ogre::Real min_z = +1e+8;
// Setting bounding box
Ogre::Mesh::SubMeshList mlist = baseMesh->getSubMeshes();
for (j = 0; j < mlist.size(); j++) {
Ogre::SubMesh *in = mlist[j];
Ogre::VertexData *vertex_data = in->vertexData;
const Ogre::VertexElement *posElem =
vertex_data->vertexDeclaration
->findElementBySemantic(
Ogre::VES_POSITION);
Ogre::HardwareVertexBufferSharedPtr hwvb =
in->vertexData->vertexBufferBinding->getBuffer(
posElem->getSource());
unsigned char *hbuff =
static_cast<unsigned char *>(hwvb->lock(
Ogre::HardwareBuffer::HBL_READ_ONLY));
Ogre::Real *pValue;
Ogre::Real value;
for (size_t idx = 0; idx < vertex_data->vertexCount;
++idx, hbuff += hwvb->getVertexSize()) {
posElem->baseVertexPointerToElement(hbuff,
&pValue);
value = (*pValue++);
if (value > max_x)
max_x = value;
if (value < min_x)
min_x = value;
value = (*pValue++);
if (value > max_y)
max_y = value;
if (value < min_y)
min_y = value;
value = (*pValue++);
if (value > max_z)
max_z = value;
if (value < min_z)
min_z = value;
}
hwvb->unlock();
}
baseMesh->setSkeletonName(skelName);
baseMesh->_setBounds(Ogre::AxisAlignedBox(min_x, min_y, min_z,
max_x, max_y, max_z));
return baseMesh;
}
void createTarget(const Ogre::String &targetName,
const Ogre::Mesh *target)
{
MeshMorphTarget mtarget;
int i, j, k, l;
std::vector<SubMeshInformation> targetInfo;
mtarget.skelp = Ogre::SkeletonManager::getSingleton().getByName(
target->getSkeletonName(), "Characters");
targetInfo.resize(target->getNumSubMeshes());
for (i = 0; i < target->getNumSubMeshes(); i++) {
getSubMeshInformation(target, target->getSubMesh(i),
&targetInfo[i]);
}
std::vector<Ogre::Vector3> vertices;
std::vector<Ogre::Vector3> normals;
std::vector<Ogre::Vector2> uvs;
std::vector<int> uv_submeshes;
std::vector<int> uv_indices;
std::vector<Ogre::Vector2> change_uvs;
std::vector<MeshMorphTarget::DataChange> changes;
for (i = 0; i < targetInfo.size(); i++) {
const std::vector<Ogre::Vector2> &puv =
targetInfo[i].uvs;
const std::vector<Ogre::Vector3> &pvertices =
targetInfo[i].vertices;
const std::vector<Ogre::Vector3> &pnormals =
targetInfo[i].normals;
for (j = 0; j < pvertices.size(); j++) {
MeshMorphTarget::DataChange dc;
dc.index = -1;
dc.submesh = -1;
change_uvs.push_back(puv[j]);
dc.normal = pnormals[j];
dc.vertex = pvertices[j];
changes.push_back(dc);
}
}
for (i = 0; i < submesh.size(); i++) {
int submesh_id = i;
for (j = 0; j < submesh[i].vertices.size(); j++) {
int index_id = j;
const Ogre::Vector2 &xuv = submesh[i].uvs[j];
uvs.push_back(xuv);
uv_submeshes.push_back(submesh_id);
uv_indices.push_back(index_id);
}
}
for (i = 0; i < changes.size(); i++) {
float l = 1.0f;
int sub = -1, index = -1;
for (j = 0; j < uvs.size(); j++) {
float xl =
change_uvs[i].squaredDistance(uvs[j]);
if (xl < l) {
l = xl;
sub = uv_submeshes[j];
index = uv_indices[j];
}
}
OgreAssert(sub >= 0 && index >= 0, "Indexing error");
if (l < 0.000000005f) {
changes[i].submesh = sub;
changes[i].index = index;
}
}
mtarget.vertices = changes;
mtarget.weight = 0.0f;
targets[targetName] = mtarget;
}
void setTargetWeight(const Ogre::String &targetName, float weight)
{
if (targets.find(targetName) == targets.end())
return;
targets[targetName].weight = weight;
}
void applyMorphTargets()
{
auto it = targets.begin();
float sum = 0.0f;
while (it != targets.end()) {
sum += it->second.weight;
it++;
}
if (sum > 1.0f) {
it = targets.begin();
while (it != targets.end()) {
it->second.weight = it->second.weight / sum;
it++;
}
}
it = targets.begin();
while (it != targets.end()) {
if (it->second.weight > 0.001f) {
int i;
for (i = 0; i < it->second.vertices.size();
i++) {
int sub_id =
it->second.vertices[i].submesh;
int index_id =
it->second.vertices[i].index;
if (sub_id < 0 || index_id < 0)
continue;
Ogre::Vector3 orig_vertex =
submesh[sub_id]
.vertices[index_id];
Ogre::Vector3 new_vertex =
it->second.vertices[i].vertex;
Ogre::Vector3 vertex_update =
Ogre::Math::lerp(
orig_vertex, new_vertex,
it->second.weight);
Ogre::Vector3 orig_normal =
submesh[sub_id]
.normals[index_id];
Ogre::Vector3 new_normal =
it->second.vertices[i].normal;
Ogre::Vector3 normal_update =
Ogre::Math::lerp(
orig_normal, new_normal,
it->second.weight);
submesh[sub_id].vertices[index_id] =
vertex_update;
submesh[sub_id].normals[index_id] =
normal_update;
}
}
it++;
}
}
};
struct SimpleListener : public OgreBites::InputListener {
App *mApp;
SimpleListener(App *app)
: mApp(app)
{
}
void frameRendered(const Ogre::FrameEvent &evt) override;
};
class App : public OgreBites::ApplicationContext {
public:
App()
: OgreBites::ApplicationContext("MorphTargetsResearch")
{
}
void locateResources() override
{
Ogre::ResourceGroupManager::getSingleton().createResourceGroup(
"Characters", true);
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
"./characters", "FileSystem", "Characters", true, true);
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
"../characters", "FileSystem", "Characters", true,
true);
OgreBites::ApplicationContext::locateResources();
}
void loadResources() override
{
}
void meshProcessing()
{
int i;
//code to test and create entity based off of sub-meshes
//vector of meshes
std::vector<Ogre::MeshPtr> m_Meshes;
//define meshes
Ogre::MeshPtr base = Ogre::MeshManager::getSingleton().load(
"male/normal-male.glb", "Characters");
m_Meshes.push_back(base);
Ogre::MeshPtr shape = Ogre::MeshManager::getSingleton().load(
"shapes/male/edited-shape-test-male.glb", "Characters");
OgreAssert(shape, "no shape");
MeshInformation meshinfo(base.get());
std::string m_ModelId = "MergedMesh";
meshinfo.createTarget("shape", shape.get());
meshinfo.setTargetWeight("shape", 1.0f);
Ogre::MeshPtr m_BaseMesh = meshinfo.create(m_ModelId);
Ogre::Entity *ent =
mScnMgr->createEntity("entName", m_BaseMesh->getName());
// ent->setMaterialName("TestMat");
//code here is temporary and only ment as a test for the modelers
//Entity* testEntity = mSceneMgr->createEntity("cc", source->getName());
//testEntity->setMaterialName("TestMat");
Ogre::SceneNode *thisSceneNode =
mScnMgr->getRootSceneNode()->createChildSceneNode();
// thisSceneNode->setPosition(100, 100, 100);
thisSceneNode->attachObject(ent);
mIdle = ent->getAnimationState("idle");
mIdle->setEnabled(true);
mIdle->setLoop(true);
mIdle->setWeight(1.0f);
}
void setup() override
{
OgreBites::ApplicationContext::setup();
Ogre::Root *root = getRoot();
Ogre::SceneManager *scnMgr = root->createSceneManager();
Ogre::RTShader::ShaderGenerator *shadergen =
Ogre::RTShader::ShaderGenerator::getSingletonPtr();
shadergen->addSceneManager(scnMgr);
// also need to tell where we are
mScnMgr.reset(scnMgr);
mCameraNode.reset(
mScnMgr->getRootSceneNode()->createChildSceneNode());
mCameraNode->setPosition(0, 2, 3);
mCameraNode->lookAt(Ogre::Vector3(0, 1, -1),
Ogre::Node::TS_PARENT);
Ogre::Light *light = mScnMgr->createLight("MainLight");
Ogre::SceneNode *lightNode =
mScnMgr->getRootSceneNode()->createChildSceneNode();
// lightNode->setPosition(0, 10, 15);
lightNode->setDirection(
Ogre::Vector3(0.55, -0.3, 0.75).normalisedCopy());
lightNode->attachObject(light);
light->setType(Ogre::Light::LT_DIRECTIONAL);
light->setDiffuseColour(Ogre::ColourValue::White);
light->setSpecularColour(Ogre::ColourValue(0.4, 0.4, 0.4));
mScnMgr->setAmbientLight(
Ogre::ColourValue(0.5f, 0.5f, 0.5f, 1.0f));
// create the camera
Ogre::Camera *cam = mScnMgr->createCamera("normal_camera");
cam->setNearClipDistance(0.1f); // specific to this sample
cam->setAutoAspectRatio(true);
mCameraNode->attachObject(cam);
mCamera.reset(cam);
// and tell it to render into the main window
getRenderWindow()->addViewport(cam);
meshProcessing();
#if 0
Ogre::Entity *ent_base =
mScnMgr->createEntity("male/normal-male.glb");
Ogre::Entity *ent_shape = mScnMgr->createEntity(
"shapes/male/chibi/vroid-normal-male-chibi.glb");
Ogre::SceneNode *characterNode =
mScnMgr->getRootSceneNode()->createChildSceneNode("ch");
characterNode->attachObject(ent_base);
#endif
addInputListener(OGRE_NEW SimpleListener(this));
}
void animationTime(float time)
{
mIdle->addTime(time);
}
private:
std::unique_ptr<Ogre::SceneNode> mCameraNode;
std::unique_ptr<Ogre::Camera> mCamera;
std::unique_ptr<Ogre::SceneManager> mScnMgr;
Ogre::AnimationState *mIdle;
};
int main(int argc, char *argv[])
{
App ctx;
ctx.initApp();
ctx.setWindowGrab(true);
ctx.getRoot()->startRendering();
ctx.setWindowGrab(false);
ctx.closeApp();
return 0;
}
void SimpleListener::frameRendered(const Ogre::FrameEvent &evt)
{
mApp->animationTime(evt.timeSinceLastFrame);
}