Work on skinned meshes

This commit is contained in:
James Lambert 2022-06-03 21:56:19 -06:00
parent 31bb18af90
commit 4a9986a75b
16 changed files with 280 additions and 98 deletions

Binary file not shown.

View file

@ -27,16 +27,11 @@ Bone* Bone::GetParent() {
return mParent;
}
std::unique_ptr<DataChunk> Bone::GenerateRestPosiitonData(float scale, aiQuaternion rotation) {
aiVector3D restPosition = rotation.Rotate(mRestPosition);
aiQuaternion restRotation = rotation * mRestRotation;
std::unique_ptr<DataChunk> Bone::GenerateRestPosiitonData() {
std::unique_ptr<StructureDataChunk> result(new StructureDataChunk());
restPosition = restPosition * scale;
result->Add(std::unique_ptr<DataChunk>(new StructureDataChunk(restPosition)));
result->Add(std::unique_ptr<DataChunk>(new StructureDataChunk(restRotation)));
result->Add(std::unique_ptr<DataChunk>(new StructureDataChunk(mRestPosition)));
result->Add(std::unique_ptr<DataChunk>(new StructureDataChunk(mRestRotation)));
result->Add(std::unique_ptr<DataChunk>(new StructureDataChunk(mRestScale)));
return std::move(result);
@ -98,6 +93,41 @@ int Bone::GetBoneIndex(Bone* a) {
}
}
void BoneHierarchy::PopulateWithAnimationNodeInfo(const NodeAnimationInfo& animInfo) {
for (auto& node : animInfo.nodesWithAnimation) {
Bone* parent = nullptr;
if (node->parent) {
auto parentFind = mBoneByName.find(node->parent->mName.C_Str());
if (parentFind != mBoneByName.end()) {
parent = parentFind->second;
}
}
std::string boneName = node->node->mName.C_Str();
aiVector3D restPosition;
aiQuaternion restRotation;
aiVector3D restScale;
aiMatrix4x4 fullRestTransform = node->relativeTransform * node->node->mTransformation;
fullRestTransform.Decompose(restScale, restRotation, restPosition);
mBones.push_back(std::unique_ptr<Bone>(new Bone(
mBones.size(),
boneName,
parent,
restPosition,
restRotation,
restScale
)));
mBoneByName.insert(std::pair<std::string, Bone*>(boneName, mBones.back().get()));
}
}
void BoneHierarchy::SearchForBones(aiNode* node, Bone* currentBoneParent, std::set<std::string>& knownBones, bool parentIsBone) {
if (knownBones.find(node->mName.C_Str()) != knownBones.end()) {
aiVector3D restPosition;
@ -153,17 +183,13 @@ Bone* BoneHierarchy::BoneForName(std::string name) {
}
}
void BoneHierarchy::GenerateRestPosiitonData(CFileDefinition& fileDef, const std::string& variableName, float scale, aiQuaternion rotation) {
void BoneHierarchy::GenerateRestPosiitonData(CFileDefinition& fileDef, const std::string& variableName) {
if (mBones.size() == 0) return;
std::unique_ptr<StructureDataChunk> transformData(new StructureDataChunk());
for (unsigned int boneIndex = 0; boneIndex < mBones.size(); ++boneIndex) {
if (mBones[boneIndex]->GetParent()) {
transformData->Add(std::move(mBones[boneIndex]->GenerateRestPosiitonData(scale, aiQuaternion())));
} else {
transformData->Add(std::move(mBones[boneIndex]->GenerateRestPosiitonData(scale, rotation)));
}
transformData->Add(std::move(mBones[boneIndex]->GenerateRestPosiitonData()));
std::string boneName = fileDef.GetUniqueName(mBones[boneIndex]->GetName() + "_BONE");
std::transform(boneName.begin(), boneName.end(), boneName.begin(), ::toupper);
@ -171,7 +197,9 @@ void BoneHierarchy::GenerateRestPosiitonData(CFileDefinition& fileDef, const std
fileDef.AddMacro(boneName, std::to_string(boneIndex));
}
fileDef.AddDefinition(std::unique_ptr<FileDefinition>(new DataFileDefinition("struct Transform", variableName, true, "_anim", std::move(transformData))));
std::unique_ptr<FileDefinition> restPosDef(new DataFileDefinition("struct Transform", variableName, true, "_geo", std::move(transformData)));
restPosDef->AddTypeHeader("\"math/transform.h\"");
fileDef.AddDefinition(std::move(restPosDef));
}
bool BoneHierarchy::HasData() const {

View file

@ -15,6 +15,17 @@
#include "ErrorCode.h"
#include "./definitions/DataChunk.h"
struct AnimationNodeInfo {
const aiNode* node;
// the ancestor of this node that is also a bone
const aiNode* parent;
aiMatrix4x4 relativeTransform;
};
struct NodeAnimationInfo {
std::vector<std::unique_ptr<AnimationNodeInfo>> nodesWithAnimation;
};
class CFileDefinition;
class Bone {
@ -25,7 +36,7 @@ public:
const std::string& GetName();
Bone* GetParent();
std::unique_ptr<DataChunk> GenerateRestPosiitonData(float scale, aiQuaternion rotation);
std::unique_ptr<DataChunk> GenerateRestPosiitonData();
static Bone* FindCommonAncestor(Bone* a, Bone* b);
/**
@ -44,7 +55,7 @@ private:
Bone* mParent;
aiVector3D mRestPosition;
aiQuaternion mRestRotation;
aiQuaternion mRestScale;
aiVector3D mRestScale;
std::vector<Bone*> mChildren;
};
@ -53,12 +64,15 @@ class BoneHierarchy {
public:
void SearchForBones(aiNode* node, Bone* currentBoneParent, std::set<std::string>& knownBones, bool parentIsBone);
void SearchForBonesInScene(const aiScene* scene);
void PopulateWithAnimationNodeInfo(const NodeAnimationInfo& animInfo);
Bone* BoneByIndex(unsigned index);
Bone* BoneForName(std::string name);
bool HasData() const;
unsigned int GetBoneCount() const;
void GenerateRestPosiitonData(CFileDefinition& fileDef, const std::string& variableName, float scale, aiQuaternion rotation);
void GenerateRestPosiitonData(CFileDefinition& fileDef, const std::string& variableName);
private:
std::vector<std::unique_ptr<Bone>> mBones;
std::map<std::string, Bone*> mBoneByName;

View file

@ -402,4 +402,8 @@ std::shared_ptr<ExtendedMesh> CFileDefinition::GetExtendedMesh(aiMesh* mesh) {
mMeshes[mesh] = result;
return result;
}
BoneHierarchy& CFileDefinition::GetBoneHierarchy() {
return mBoneHierarchy;
}

View file

@ -53,6 +53,8 @@ public:
bool GetResourceName(const void* resource, std::string& result) const;
std::shared_ptr<ExtendedMesh> GetExtendedMesh(aiMesh* mesh);
BoneHierarchy& GetBoneHierarchy();
private:
std::string mPrefix;
float mModelScale;

View file

@ -137,6 +137,8 @@ std::unique_ptr<DataChunk> PushMatrixCommand::GenerateCommand() {
} else {
flags += "G_MTX_PUSH";
}
result->AddPrimitive(flags);
return std::move(result);
}

View file

@ -63,6 +63,12 @@ ExtendedMesh::ExtendedMesh(const ExtendedMesh& other):
mBoneSpanningFaces[it.first] = faces;
}
for (auto& it : mNormalInverseTransform) {
if (it) {
it = new aiMatrix3x3(*it);
}
}
}
ExtendedMesh::ExtendedMesh(aiMesh* mesh, BoneHierarchy& boneHierarchy) :

View file

@ -125,6 +125,12 @@ std::string generateMesh(const aiScene* scene, CFileDefinition& fileDefinition,
DisplayList displayList(fileDefinition.GetUniqueName("model_gfx"));
generateMeshIntoDL(scene, fileDefinition, renderChunks, settings, displayList, fileSuffix);
std::unique_ptr<FileDefinition> dlResult = displayList.Generate(fileSuffix);
if (fileDefinition.GetBoneHierarchy().HasData()) {
dlResult->AddTypeHeader("\"sk64/skelatool_defs.h\"");
}
fileDefinition.AddDefinition(std::move(dlResult));
return displayList.GetName();

View file

@ -15,28 +15,7 @@
#include "AnimationTranslator.h"
#include "MeshWriter.h"
#include "FileUtils.h"
std::vector<SKAnimationHeader> generateAnimationData(const aiScene* scene, BoneHierarchy& bones, CFileDefinition& fileDef, float modelScale, unsigned short targetTicksPerSecond, aiQuaternion rotate) {
std::vector<SKAnimationHeader> animations;
for (unsigned i = 0; i < scene->mNumAnimations; ++i) {
SKAnimation animation;
if (translateAnimationToSK(*scene->mAnimations[i], animation, bones, modelScale, targetTicksPerSecond, rotate)) {
std::string animationName = fileDef.GetUniqueName(scene->mAnimations[i]->mName.C_Str());
unsigned short firstChunkSize = formatAnimationChunks(animationName, animation.chunks, fileDef);
SKAnimationHeader header;
header.firstChunkSize = firstChunkSize;
header.ticksPerSecond = targetTicksPerSecond;
header.maxTicks = animation.maxTicks;
header.animationName = animationName;
animations.push_back(header);
}
}
return animations;
}
#include "./definition_generator/AnimationGenerator.h"
void generateMeshFromScene(const aiScene* scene, CFileDefinition& fileDefinition, DisplayListSettings& settings) {
BoneHierarchy bones;
@ -67,51 +46,7 @@ void generateMeshFromScene(const aiScene* scene, CFileDefinition& fileDefinition
}
if (shouldExportAnimations) {
std::string bonesName = fileDefinition.GetUniqueName("default_bones");
std::string boneParentName = fileDefinition.GetUniqueName("bone_parent");
bones.GenerateRestPosiitonData(fileDefinition, bonesName, settings.mGraphicsScale, settings.mRotateModel);
std::string boneCountName = bonesName + "_COUNT";
std::transform(boneCountName.begin(), boneCountName.end(), boneCountName.begin(), ::toupper);
fileDefinition.AddMacro(boneCountName, std::to_string(bones.GetBoneCount()));
std::string animationsName = fileDefinition.GetUniqueName("animations");
auto animations = generateAnimationData(scene, bones, fileDefinition, settings.mGraphicsScale, settings.mTicksPerSecond, settings.mRotateModel);
std::unique_ptr<StructureDataChunk> animationNameData(new StructureDataChunk());
int index = 0;
for (auto it = animations.begin(); it != animations.end(); ++it) {
std::unique_ptr<StructureDataChunk> animationChunk(new StructureDataChunk());
animationChunk->AddPrimitive(it->firstChunkSize);
animationChunk->AddPrimitive(it->ticksPerSecond);
animationChunk->AddPrimitive(it->maxTicks);
animationChunk->AddPrimitive(0);
animationChunk->AddPrimitive(std::string("(struct SKAnimationChunk*)") + it->animationName);
animationChunk->AddPrimitive(0);
animationNameData->Add(std::move(animationChunk));
std::string animationIndex = fileDefinition.GetUniqueName(it->animationName + "_INDEX");
std::transform(animationIndex.begin(), animationIndex.end(), animationIndex.begin(), ::toupper);
fileDefinition.AddMacro(animationIndex, std::to_string(index));
++index;
}
fileDefinition.AddDefinition(std::unique_ptr<FileDefinition>(new DataFileDefinition("struct SKAnimationHeader", animationsName, true, "_anim", std::move(animationNameData))));
std::unique_ptr<StructureDataChunk> boneParentDataChunk(new StructureDataChunk());
for (unsigned int boneIndex = 0; boneIndex < bones.GetBoneCount(); ++boneIndex) {
Bone* bone = bones.BoneByIndex(boneIndex);
if (bone->GetParent()) {
boneParentDataChunk->AddPrimitive(bone->GetParent()->GetIndex());
} else {
boneParentDataChunk->AddPrimitive(0xFFFF);
}
}
fileDefinition.AddDefinition(std::unique_ptr<FileDefinition>(new DataFileDefinition("unsigned short", boneParentName, true, "_anim", std::move(boneParentDataChunk))));
generateAnimationForScene(scene, fileDefinition, settings);
}
}

View file

@ -0,0 +1,141 @@
#include "AnimationGenerator.h"
#include "./DefinitionGenerator.h"
#include "../AnimationTranslator.h"
#include <set>
#include <string>
#include <map>
std::shared_ptr<NodeAnimationInfo> findNodesForWithAnimation(const aiScene* scene, const aiMatrix4x4& baseTransform) {
std::set<std::string> animatedNodeNames;
for (unsigned animIndex = 0; animIndex < scene->mNumAnimations; ++animIndex) {
aiAnimation* animation = scene->mAnimations[animIndex];
for (unsigned channelIndex = 0; channelIndex < animation->mNumChannels; ++channelIndex) {
animatedNodeNames.insert(animation->mChannels[channelIndex]->mNodeName.C_Str());
}
}
std::set<aiNode*> nodesWithAnimationData;
for (unsigned meshIndex = 0; meshIndex < scene->mNumMeshes; ++meshIndex) {
aiMesh* mesh = scene->mMeshes[meshIndex];
for (unsigned boneIndex = 0; boneIndex < mesh->mNumBones; ++boneIndex) {
animatedNodeNames.insert(mesh->mBones[boneIndex]->mName.C_Str());
}
}
std::map<const aiNode*, int> nodeOrder;
forEachNode(scene->mRootNode, [&](aiNode* node) -> void {
if (animatedNodeNames.find(node->mName.C_Str()) != animatedNodeNames.end()) {
nodesWithAnimationData.insert(node);
}
nodeOrder.insert(std::make_pair(node, nodeOrder.size()));
});
std::shared_ptr<NodeAnimationInfo> result(new NodeAnimationInfo());
for (auto node : nodesWithAnimationData) {
std::unique_ptr<AnimationNodeInfo> nodeInfo(new AnimationNodeInfo());
aiNode* currentNode = node;
while (currentNode->mParent && nodesWithAnimationData.find(currentNode->mParent) == nodesWithAnimationData.end()) {
currentNode = currentNode->mParent;
nodeInfo->relativeTransform = currentNode->mTransformation * nodeInfo->relativeTransform;
}
if (!currentNode->mParent) {
nodeInfo->relativeTransform = baseTransform * nodeInfo->relativeTransform;
}
nodeInfo->node = node;
nodeInfo->parent = currentNode->mParent;
result->nodesWithAnimation.push_back(std::move(nodeInfo));
}
std::sort(result->nodesWithAnimation.begin(), result->nodesWithAnimation.end(), [&](const std::unique_ptr<AnimationNodeInfo>& a, const std::unique_ptr<AnimationNodeInfo>& b) -> bool {
return nodeOrder[a->node] < nodeOrder[b->node];
});
return result;
}
std::vector<SKAnimationHeader> generateAnimationData(const aiScene* scene, BoneHierarchy& bones, CFileDefinition& fileDef, float modelScale, unsigned short targetTicksPerSecond, aiQuaternion rotate) {
std::vector<SKAnimationHeader> animations;
for (unsigned i = 0; i < scene->mNumAnimations; ++i) {
SKAnimation animation;
if (translateAnimationToSK(*scene->mAnimations[i], animation, bones, modelScale, targetTicksPerSecond, rotate)) {
std::string animationName = fileDef.GetUniqueName(scene->mAnimations[i]->mName.C_Str());
unsigned short firstChunkSize = formatAnimationChunks(animationName, animation.chunks, fileDef);
SKAnimationHeader header;
header.firstChunkSize = firstChunkSize;
header.ticksPerSecond = targetTicksPerSecond;
header.maxTicks = animation.maxTicks;
header.animationName = animationName;
animations.push_back(header);
}
}
return animations;
}
void generateAnimationForScene(const aiScene* scene, CFileDefinition &fileDefinition, DisplayListSettings& settings) {
BoneHierarchy& bones = fileDefinition.GetBoneHierarchy();
std::string bonesName = fileDefinition.GetUniqueName("default_bones");
std::string boneParentName = fileDefinition.GetUniqueName("bone_parent");
bones.GenerateRestPosiitonData(fileDefinition, bonesName);
std::string boneCountName = bonesName + "_COUNT";
std::transform(boneCountName.begin(), boneCountName.end(), boneCountName.begin(), ::toupper);
fileDefinition.AddMacro(boneCountName, std::to_string(bones.GetBoneCount()));
std::string animationsName = fileDefinition.GetUniqueName("animations");
auto animations = generateAnimationData(scene, bones, fileDefinition, settings.mGraphicsScale, settings.mTicksPerSecond, settings.mRotateModel);
std::unique_ptr<StructureDataChunk> animationNameData(new StructureDataChunk());
int index = 0;
for (auto it = animations.begin(); it != animations.end(); ++it) {
std::unique_ptr<StructureDataChunk> animationChunk(new StructureDataChunk());
animationChunk->AddPrimitive(it->firstChunkSize);
animationChunk->AddPrimitive(it->ticksPerSecond);
animationChunk->AddPrimitive(it->maxTicks);
animationChunk->AddPrimitive(0);
animationChunk->AddPrimitive(std::string("(struct SKAnimationChunk*)") + it->animationName);
animationChunk->AddPrimitive(0);
animationNameData->Add(std::move(animationChunk));
std::string animationIndex = fileDefinition.GetUniqueName(it->animationName + "_INDEX");
std::transform(animationIndex.begin(), animationIndex.end(), animationIndex.begin(), ::toupper);
fileDefinition.AddMacro(animationIndex, std::to_string(index));
++index;
}
std::unique_ptr<DataFileDefinition> headerDef(new DataFileDefinition("struct SKAnimationHeader", animationsName, true, "_geo", std::move(animationNameData)));
headerDef->AddTypeHeader("\"sk64/skelatool_clip.h\"");
fileDefinition.AddDefinition(std::move(headerDef));
std::unique_ptr<StructureDataChunk> boneParentDataChunk(new StructureDataChunk());
for (unsigned int boneIndex = 0; boneIndex < bones.GetBoneCount(); ++boneIndex) {
Bone* bone = bones.BoneByIndex(boneIndex);
if (bone->GetParent()) {
boneParentDataChunk->AddPrimitive(bone->GetParent()->GetIndex());
} else {
boneParentDataChunk->AddPrimitive(0xFFFF);
}
}
fileDefinition.AddDefinition(std::unique_ptr<FileDefinition>(new DataFileDefinition("unsigned short", boneParentName, true, "_geo", std::move(boneParentDataChunk))));
}

View file

@ -0,0 +1,17 @@
#ifndef __ANIMATION_GENERATOR_H__
#define __ANIMATION_GENERATOR_H__
#include <assimp/scene.h>
#include <vector>
#include <memory>
#include "../CFileDefinition.h"
#include "../DisplayListSettings.h"
#include "../Animation.h"
std::shared_ptr<NodeAnimationInfo> findNodesForWithAnimation(const aiScene* scene, const aiMatrix4x4& baseTransform);
std::vector<SKAnimationHeader> generateAnimationData(const aiScene* scene, BoneHierarchy& bones, CFileDefinition& fileDef, float modelScale, unsigned short targetTicksPerSecond, aiQuaternion rotate);
void generateAnimationForScene(const aiScene* scene, CFileDefinition &fileDefinition, DisplayListSettings& settings);
#endif

View file

@ -9,21 +9,25 @@ void DefinitionGenerator::TraverseScene(const aiScene* scene) {
}
BeforeTraversal(scene);
TraverseNodes(scene->mRootNode);
forEachNode(scene->mRootNode, [&](aiNode* node) -> void {
if (ShouldIncludeNode(node)) {
mIncludedNodes.push_back(node);
}
});
}
void DefinitionGenerator::BeforeTraversal(const aiScene* scene) {}
void DefinitionGenerator::TraverseNodes(aiNode* node) {
void forEachNode(aiNode* node, const std::function<void(aiNode*)>& callback) {
if (!node) {
return;
}
if (ShouldIncludeNode(node)) {
mIncludedNodes.push_back(node);
}
callback(node);
for (unsigned int i = 0; i < node->mNumChildren; ++i) {
TraverseNodes(node->mChildren[i]);
forEachNode(node->mChildren[i], callback);
}
}

View file

@ -4,6 +4,7 @@
#include <assimp/scene.h>
#include <vector>
#include <functional>
#include "../CFileDefinition.h"
@ -20,8 +21,8 @@ public:
virtual void GenerateDefinitions(const aiScene* scene, CFileDefinition& fileDefinition) = 0;
protected:
std::vector<aiNode*> mIncludedNodes;
private:
void TraverseNodes(aiNode* node);
};
void forEachNode(aiNode* node, const std::function<void(aiNode*)>& callback);
#endif

View file

@ -2,6 +2,7 @@
#include "../RenderChunk.h"
#include "../MeshWriter.h"
#include "AnimationGenerator.h"
bool extractMaterialAutoTileParameters(Material* material, double& sTile, double& tTile) {
if (!material) {
@ -58,20 +59,34 @@ void MeshDefinitionGenerator::AppendRenderChunks(const aiScene* scene, aiNode* n
);
}
renderChunks.push_back(RenderChunk(
std::pair<Bone*, Bone*>(NULL, NULL),
mesh,
materialPtr
));
for (auto boneSegment = mesh->mFacesForBone.begin(); boneSegment != mesh->mFacesForBone.end(); ++boneSegment) {
renderChunks.push_back(RenderChunk(
std::make_pair(boneSegment->first, boneSegment->first),
mesh,
materialPtr
));
}
for (auto pairSegment = mesh->mBoneSpanningFaces.begin(); pairSegment != mesh->mBoneSpanningFaces.end(); ++pairSegment) {
renderChunks.push_back(RenderChunk(pairSegment->first, mesh, materialPtr));
}
}
}
void MeshDefinitionGenerator::GenerateDefinitions(const aiScene* scene, CFileDefinition& fileDefinition) {
std::vector<RenderChunk> renderChunks;
auto animInfo = findNodesForWithAnimation(scene, mSettings.CreateCollisionTransform());
fileDefinition.GetBoneHierarchy().PopulateWithAnimationNodeInfo(*animInfo);
for (auto node = mIncludedNodes.begin(); node != mIncludedNodes.end(); ++node) {
AppendRenderChunks(scene, *node, fileDefinition, mSettings, renderChunks);
}
generateMesh(scene, fileDefinition, renderChunks, mSettings, "_geo");
if (fileDefinition.GetBoneHierarchy().HasData()) {
generateAnimationForScene(scene, fileDefinition, mSettings);
}
}

View file

@ -20,6 +20,8 @@ void StaticGenerator::GenerateDefinitions(const aiScene* scene, CFileDefinition&
std::vector<StaticContentElement> elements;
BoneHierarchy unusedBones;
sortNodesByRoom(mIncludedNodes, mRoomMapping);
for (auto node = mIncludedNodes.begin(); node != mIncludedNodes.end(); ++node) {

View file

@ -55,6 +55,11 @@ int collisionSphereCollideQuad(void* data, struct Transform* boxTransform, struc
contact->tangentImpulse[0] = 0.0f;
contact->tangentImpulse[1] = 0.0f;
contact->contactALocal = contact->contactAWorld;
struct Quaternion inverseRotation;
quatConjugate(&boxTransform->rotation, &inverseRotation);
quatMultVector(&inverseRotation, &contact->contactBWorld, &contact->contactBLocal);
return 1;
}