Fixup skeletal animation

This commit is contained in:
James Lambert 2022-08-07 13:55:50 -06:00
parent ec0c6a79dc
commit 18a879d7fa
24 changed files with 186 additions and 61 deletions

View File

@ -161,6 +161,7 @@ build/src/scene/elevator.o: build/assets/models/props/round_elevator_collision.h
MODEL_LIST = assets/models/cube/cube.blend \
assets/models/portal_gun/v_portalgun.blend \
assets/models/portal_gun/w_portalgun.blend \
assets/models/props/button.blend \
assets/models/props/door_01.blend \
assets/models/props/cylinder_test.blend \

View File

@ -127,9 +127,30 @@ materials:
color: ["PRIMITIVE", "0", "SHADE", "0"]
blue_glow:
gDPSetPrimColor:
r: 89
g: 197
b: 240
gDPSetCombineMode:
color: ["0", "0", "0", "PRIMITIVE"]
solid_black_two_sided:
gDPSetPrimColor:
r: 32
g: 32
b: 32
gSPGeometryMode:
set: [G_LIGHTING, G_SHADE]
clear: [G_CULL_BACK]
gDPSetCombineMode:
color: ["0", "0", "0", "PRIMITIVE"]
color: ["PRIMITIVE", "0", "SHADE", "0"]
blue_glow_two_sided:
gDPSetPrimColor:
r: 89
g: 197
b: 240
gSPGeometryMode:
clear: [G_CULL_BACK]
gDPSetCombineMode:
color: ["0", "0", "0", "PRIMITIVE"]

Binary file not shown.

View File

@ -1 +1 @@
-r 90,0,0 -m assets/materials/static.skm.yaml -m assets/materials/objects.skm.yaml --default-material default
-r 0,0,0 -m assets/materials/static.skm.yaml -m assets/materials/objects.skm.yaml --default-material default

Binary file not shown.

View File

@ -0,0 +1 @@
-m assets/materials/objects.skm.yaml

View File

@ -93,7 +93,7 @@ int Bone::GetBoneIndex(Bone* a) {
}
}
void BoneHierarchy::PopulateWithAnimationNodeInfo(const NodeAnimationInfo& animInfo, float fixedPointScale) {
void BoneHierarchy::PopulateWithAnimationNodeInfo(const NodeAnimationInfo& animInfo, float fixedPointScale, aiQuaternion& rotation) {
for (auto& node : animInfo.nodesWithAnimation) {
Bone* parent = nullptr;

View File

@ -65,7 +65,7 @@ public:
void SearchForBones(aiNode* node, Bone* currentBoneParent, std::set<std::string>& knownBones, bool parentIsBone, float fixedPointScale);
void SearchForBonesInScene(const aiScene* scene, float fixedPointScale);
void PopulateWithAnimationNodeInfo(const NodeAnimationInfo& animInfo, float fixedPointScale);
void PopulateWithAnimationNodeInfo(const NodeAnimationInfo& animInfo, float fixedPointScale, aiQuaternion& rotation);
Bone* BoneByIndex(unsigned index);
Bone* BoneForName(std::string name);

View File

@ -61,12 +61,10 @@ unsigned convertByteRange(float value) {
aiVector3D pos = mTargetMesh->mMesh->mVertices[i];
pos = pos * modelScale;
if (mTargetMesh->mPointInverseTransform[i]) {
pos = (*mTargetMesh->mPointInverseTransform[i]) * pos;
if (mTargetMesh->mPointInverseTransform.size()) {
pos = mTargetMesh->mPointInverseTransform[i] * pos;
} else {
pos = rotate.Rotate(pos);
pos = rotate.Rotate(pos) * modelScale;
}
pos = pos * fixedPointScale;
@ -151,9 +149,9 @@ unsigned convertByteRange(float value) {
break;
}
if (mTargetMesh->mPointInverseTransform[i]) {
normal = (*mTargetMesh->mNormalInverseTransform[i]) * normal;
normal.Normalize();
if (mTargetMesh->mPointInverseTransform.size()) {
normal = mTargetMesh->mNormalInverseTransform[i] * normal;
normal.NormalizeSafe();
} else {
normal = rotate.Rotate(normal);
}

View File

@ -134,19 +134,16 @@ 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) :
mMesh(mesh) {
mVertexBones.resize(mMesh->mNumVertices);
mPointInverseTransform.resize(mMesh->mNumVertices);
mNormalInverseTransform.resize(mMesh->mNumVertices);
if (mesh->mNumBones) {
mPointInverseTransform.resize(mMesh->mNumVertices);
mNormalInverseTransform.resize(mMesh->mNumVertices);
}
std::set<Bone*> bonesAsSet;
@ -154,15 +151,15 @@ ExtendedMesh::ExtendedMesh(aiMesh* mesh, BoneHierarchy& boneHierarchy) :
aiBone* bone = mMesh->mBones[boneIndex];
Bone* hierarchyBone = boneHierarchy.BoneForName(bone->mName.C_Str());
bonesAsSet.insert(hierarchyBone);
aiMatrix3x3 normalTransform(bone->mOffsetMatrix);
normalTransform = normalTransform.Transpose().Inverse();
for (unsigned int vertexIndex = 0; vertexIndex < bone->mNumWeights; ++vertexIndex) {
unsigned int vertexId = bone->mWeights[vertexIndex].mVertexId;
mVertexBones[vertexId] = hierarchyBone;
mPointInverseTransform[vertexId] = &bone->mOffsetMatrix;
mNormalInverseTransform[vertexId] = new aiMatrix3x3(normalTransform);
mPointInverseTransform[vertexId] = bone->mOffsetMatrix;
mNormalInverseTransform[vertexId] = normalTransform;
}
}
@ -171,11 +168,7 @@ ExtendedMesh::ExtendedMesh(aiMesh* mesh, BoneHierarchy& boneHierarchy) :
}
ExtendedMesh::~ExtendedMesh() {
for (unsigned int i = 0; i < mNormalInverseTransform.size(); ++i) {
if (mNormalInverseTransform[i]) {
delete mNormalInverseTransform[i];
}
}
}
void ExtendedMesh::RecalcBB() {
@ -234,6 +227,10 @@ std::shared_ptr<ExtendedMesh> ExtendedMesh::Transform(const aiMatrix4x4& transfo
std::shared_ptr<ExtendedMesh> result(new ExtendedMesh(*this));
aiMatrix3x3 rotationOnly(transform);
aiMatrix4x4 inverseTransform = transform;
inverseTransform.Inverse();
for (unsigned i = 0; i < result->mMesh->mNumVertices; ++i) {
result->mMesh->mVertices[i] = transform * result->mMesh->mVertices[i];
@ -241,7 +238,12 @@ std::shared_ptr<ExtendedMesh> ExtendedMesh::Transform(const aiMatrix4x4& transfo
result->mMesh->mNormals[i] = rotationOnly * result->mMesh->mNormals[i];
result->mMesh->mNormals[i].NormalizeSafe();
}
if (result->mPointInverseTransform.size()) {
result->mPointInverseTransform[i] = result->mPointInverseTransform[i] * inverseTransform;
}
}
result->RecalcBB();
return result;

View File

@ -22,8 +22,8 @@ public:
ExtendedMesh(aiMesh* mesh, BoneHierarchy& boneHierarchy);
~ExtendedMesh();
aiMesh* mMesh;
std::vector<aiMatrix4x4*> mPointInverseTransform;
std::vector<aiMatrix3x3*> mNormalInverseTransform;
std::vector<aiMatrix4x4> mPointInverseTransform;
std::vector<aiMatrix3x3> mNormalInverseTransform;
std::vector<Bone*> mVertexBones;
std::map<Bone*, std::vector<aiFace*>> mFacesForBone;
// first bone in pair is always the parent of the second

View File

@ -71,7 +71,7 @@ void generateMeshIntoDLWithMaterials(const aiScene* scene, CFileDefinition& file
for (auto chunk = renderChunks.begin(); chunk != renderChunks.end(); ++chunk) {
if (materials) {
std::string materialName = ExtendedMesh::GetMaterialName(scene->mMaterials[chunk->mMesh->mMesh->mMaterialIndex], settings.mForceMaterialName);
std::string materialName = chunk->mMesh ? ExtendedMesh::GetMaterialName(scene->mMaterials[chunk->mMesh->mMesh->mMaterialIndex], settings.mForceMaterialName) : settings.mDefaultMaterialName;
displayList.AddCommand(std::unique_ptr<DisplayListCommand>(new CommentCommand("Material " + materialName)));
auto mappedMaterialName = materials->mMaterialNameMapping.find(materialName);
@ -98,14 +98,20 @@ void generateMeshIntoDLWithMaterials(const aiScene* scene, CFileDefinition& file
displayList.AddCommand(std::unique_ptr<DisplayListCommand>(new CommentCommand("End Material " + materialName)));
}
std::string vertexBuffer = fileDefinition.GetVertexBuffer(
chunk->mMesh,
Material::GetVertexType(chunk->mMaterial),
Material::TextureWidth(chunk->mMaterial),
Material::TextureHeight(chunk->mMaterial),
modelSuffix
);
generateGeometry(*chunk, rcpState, vertexBuffer, displayList, settings.mHasTri2);
if (chunk->mMesh) {
std::string vertexBuffer = fileDefinition.GetVertexBuffer(
chunk->mMesh,
Material::GetVertexType(chunk->mMaterial),
Material::TextureWidth(chunk->mMaterial),
Material::TextureHeight(chunk->mMaterial),
modelSuffix
);
generateGeometry(*chunk, rcpState, vertexBuffer, displayList, settings.mHasTri2);
} else if (chunk->mAttachedDLIndex != -1) {
rcpState.TraverseToBone(chunk->mBonePair.first, displayList);
displayList.AddCommand(std::unique_ptr<DisplayListCommand>(new CallDisplayListByNameCommand(std::string("(Gfx*)BONE_ATTACHMENT_SEGMENT_ADDRESS + " + std::to_string(chunk->mAttachedDLIndex)))));
}
}
rcpState.TraverseToBone(nullptr, displayList);

View File

@ -2,9 +2,20 @@
#include "RenderChunk.h"
#include <algorithm>
RenderChunk::RenderChunk(std::pair<Bone*, Bone*> bonePair, std::shared_ptr<ExtendedMesh> mesh, Material* material):
RenderChunk::RenderChunk(std::pair<Bone*, Bone*> bonePair, std::shared_ptr<ExtendedMesh> mesh, aiNode* meshRoot, Material* material):
mBonePair(bonePair),
mMesh(mesh),
mMeshRoot(meshRoot),
mAttachedDLIndex(-1),
mMaterial(material) {
}
RenderChunk::RenderChunk(std::pair<Bone*, Bone*> bonePair, int attachedDLIndex, Material* material):
mBonePair(bonePair),
mMesh(NULL),
mMeshRoot(nullptr),
mAttachedDLIndex(attachedDLIndex),
mMaterial(material) {
}
@ -45,12 +56,13 @@ void extractChunks(const aiScene* scene, std::vector<std::shared_ptr<ExtendedMes
result.push_back(RenderChunk(
std::make_pair(boneSegment->first, boneSegment->first),
*it,
nullptr,
materialPtr
));
}
for (auto pairSegment = (*it)->mBoneSpanningFaces.begin(); pairSegment != (*it)->mBoneSpanningFaces.end(); ++pairSegment) {
result.push_back(RenderChunk(pairSegment->first, *it, materialPtr));
result.push_back(RenderChunk(pairSegment->first, *it, nullptr, materialPtr));
}
}
}

View File

@ -13,11 +13,14 @@
class RenderChunk {
public:
RenderChunk(std::pair<Bone*, Bone*> bonePair, std::shared_ptr<ExtendedMesh> mesh, Material* material);
RenderChunk(std::pair<Bone*, Bone*> bonePair, std::shared_ptr<ExtendedMesh> mesh, aiNode* meshRoot, Material* material);
RenderChunk(std::pair<Bone*, Bone*> bonePair, int attachedDLIndex, Material* material);
// if bones are the same, chunk cooresponds to a single bone
// the bones can be null
std::pair<Bone*, Bone*> mBonePair;
std::shared_ptr<ExtendedMesh> mMesh;
aiNode* mMeshRoot;
int mAttachedDLIndex;
Material* mMaterial;
VertexType GetVertexType();

View File

@ -6,7 +6,7 @@
#include <string>
#include <map>
std::shared_ptr<NodeAnimationInfo> findNodesForWithAnimation(const aiScene* scene, const std::vector<aiNode*>& usedNodes, const aiMatrix4x4& baseTransform) {
std::shared_ptr<NodeAnimationInfo> findNodesForWithAnimation(const aiScene* scene, const std::vector<aiNode*>& usedNodes, float modelScale) {
std::set<std::string> animatedNodeNames;
for (unsigned animIndex = 0; animIndex < scene->mNumAnimations; ++animIndex) {
@ -56,7 +56,7 @@ std::shared_ptr<NodeAnimationInfo> findNodesForWithAnimation(const aiScene* scen
}
if (!currentNode->mParent) {
nodeInfo->relativeTransform = baseTransform * nodeInfo->relativeTransform;
nodeInfo->relativeTransform = aiMatrix4x4(aiVector3D(1, 1, 1) * modelScale, aiQuaternion(), aiVector3D()) * nodeInfo->relativeTransform;
}
nodeInfo->node = node;

View File

@ -9,7 +9,7 @@
#include "../DisplayListSettings.h"
#include "../Animation.h"
std::shared_ptr<NodeAnimationInfo> findNodesForWithAnimation(const aiScene* scene, const std::vector<aiNode*>& usedNodes, const aiMatrix4x4& baseTransform);
std::shared_ptr<NodeAnimationInfo> findNodesForWithAnimation(const aiScene* scene, const std::vector<aiNode*>& usedNodes, float modelScale);
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);

View File

@ -3,6 +3,7 @@
#include "../RenderChunk.h"
#include "../MeshWriter.h"
#include "AnimationGenerator.h"
#include "../StringUtils.h"
bool extractMaterialAutoTileParameters(Material* material, double& sTile, double& tTile) {
if (!material) {
@ -64,21 +65,38 @@ void MeshDefinitionGenerator::AppendRenderChunks(const aiScene* scene, aiNode* n
renderChunks.push_back(RenderChunk(
std::make_pair(boneSegment->first, boneSegment->first),
mesh,
node,
materialPtr
));
}
for (auto pairSegment = mesh->mBoneSpanningFaces.begin(); pairSegment != mesh->mBoneSpanningFaces.end(); ++pairSegment) {
renderChunks.push_back(RenderChunk(pairSegment->first, mesh, materialPtr));
renderChunks.push_back(RenderChunk(pairSegment->first, mesh, node, materialPtr));
}
}
BoneHierarchy& bones = fileDefinition.GetBoneHierarchy();
int attachmentCount = 0;
for (unsigned i = 0; i < bones.GetBoneCount(); ++i) {
Bone* bone = bones.BoneByIndex(i);
if (StartsWith(bone->GetName(), "attachment ")) {
fileDefinition.AddMacro(fileDefinition.GetMacroName(std::string("ATTACHMENT_") + bone->GetName().substr(strlen("attachment "))), std::to_string(attachmentCount));
renderChunks.push_back(RenderChunk(std::make_pair(bone, bone), attachmentCount, NULL));
++attachmentCount;
}
}
fileDefinition.AddMacro(fileDefinition.GetMacroName("ATTACHMENT_COUNT"), std::to_string(attachmentCount));
}
void MeshDefinitionGenerator::GenerateDefinitions(const aiScene* scene, CFileDefinition& fileDefinition) {
std::vector<RenderChunk> renderChunks;
auto animInfo = findNodesForWithAnimation(scene, mIncludedNodes, mSettings.CreateCollisionTransform());
fileDefinition.GetBoneHierarchy().PopulateWithAnimationNodeInfo(*animInfo, mSettings.mFixedPointScale);
auto animInfo = findNodesForWithAnimation(scene, mIncludedNodes, mSettings.mModelScale);
fileDefinition.GetBoneHierarchy().PopulateWithAnimationNodeInfo(*animInfo, mSettings.mFixedPointScale, mSettings.mRotateModel);
for (auto node = mIncludedNodes.begin(); node != mIncludedNodes.end(); ++node) {
AppendRenderChunks(scene, *node, fileDefinition, mSettings, renderChunks);

View File

@ -149,7 +149,7 @@ static void gameProc(void* arg) {
dynamicSceneInit();
contactSolverInit(&gContactSolver);
levelLoad(0);
levelLoad(1);
controllersInit();
initAudio();
soundPlayerInit();

View File

@ -3,6 +3,7 @@
#include "sk64/skelatool_animator.h"
#include "../../build/assets/models/portal_gun/v_portalgun.h"
#include "../../build/assets/models/portal_gun/w_portalgun.h"
#include "../../build/assets/materials/static.h"
#include "../../build/assets/models/props/button.h"
#include "../../build/assets/models/props/door_01.h"
@ -10,6 +11,8 @@
Gfx* v_portal_gun_gfx = &portal_gun_v_portalgun_model_gfx[0];
Gfx* w_portal_gun_gfx = &portal_gun_w_portalgun_model_gfx[0];
Gfx* button_gfx = &props_button_model_gfx[0];
short button_material_index = BUTTON_INDEX;

View File

@ -3,6 +3,8 @@
extern Gfx* v_portal_gun_gfx;
extern Gfx* w_portal_gun_gfx;
extern Gfx* button_gfx;
extern short button_material_index;

View File

@ -2,6 +2,8 @@
#include "../scene/dynamic_scene.h"
#include "../defs.h"
#include "../models/models.h"
#include "../util/time.h"
#include "../build/assets/materials/static.h"
#include "../build/assets/models/pedestal.h"
@ -9,6 +11,8 @@
void pedestalRender(void* data, struct RenderScene* renderScene) {
struct Pedestal* pedestal = (struct Pedestal*)data;
quatAxisAngle(&gUp, gTimePassed, &pedestal->armature.boneTransforms[PEDESTAL_HOLDER_BONE].rotation);
Mtx* matrix = renderStateRequestMatrices(renderScene->renderState, 1);
transformToMatrixL(&pedestal->transform, matrix, SCENE_SCALE);
@ -16,13 +20,25 @@ void pedestalRender(void* data, struct RenderScene* renderScene) {
skCalculateTransforms(&pedestal->armature, armature);
Gfx* attachments = skBuildAttachments(&pedestal->armature, &w_portal_gun_gfx, renderScene->renderState);
Gfx* objectRender = renderStateAllocateDLChunk(renderScene->renderState, 4);
Gfx* dl = objectRender;
if (attachments) {
gSPSegment(dl++, BONE_ATTACHMENT_SEGMENT, osVirtualToPhysical(attachments));
}
gSPSegment(dl++, MATRIX_TRANSFORM_SEGMENT, osVirtualToPhysical(armature));
gSPDisplayList(dl++, pedestal_model_gfx);
gSPEndDisplayList(dl++);
renderSceneAdd(
renderScene,
pedestal->armature.displayList,
objectRender,
matrix,
DEFAULT_INDEX,
&pedestal->transform.position,
armature
NULL
);
}
@ -37,7 +53,8 @@ void pedestalInit(struct Pedestal* pedestal, struct PedestalDefinition* definiti
pedestal_model_gfx,
PEDESTAL_DEFAULT_BONES_COUNT,
pedestal_default_bones,
pedestal_bone_parent
pedestal_bone_parent,
PEDESTAL_ATTACHMENT_COUNT
);
pedestal->dynamicId = dynamicSceneAdd(pedestal, pedestalRender, &pedestal->transform, 0.8f);

View File

@ -4,9 +4,10 @@
#include "util/memory.h"
#include "util/rom.h"
void skArmatureInit(struct SKArmature* object, Gfx* displayList, u32 numberOfBones, struct Transform* initialPose, unsigned short* boneParentIndex) {
void skArmatureInit(struct SKArmature* object, Gfx* displayList, u32 numberOfBones, struct Transform* initialPose, unsigned short* boneParentIndex, u32 numberOfAttachments) {
object->displayList = displayList;
object->numberOfBones = numberOfBones;
object->numberOfAttachments = numberOfAttachments;
unsigned transformSize = sizeof(Mtx) * numberOfBones;
@ -27,18 +28,53 @@ void skCleanupObject(struct SKArmature* object) {
object->numberOfBones = 0;
}
void skRenderObject(struct SKArmature* object, struct RenderState* intoState) {
Gfx* skBuildAttachments(struct SKArmature* object, Gfx** attachments, struct RenderState* renderState) {
if (object->numberOfAttachments == 0) {
return NULL;
}
if (object->numberOfAttachments == 1 && attachments) {
return *attachments;
}
Gfx* jumpTable = renderStateAllocateDLChunk(renderState, object->numberOfAttachments);
Gfx* dl = jumpTable;
for (unsigned i = 0; i < object->numberOfAttachments; ++i) {
if (attachments && attachments[i]) {
gSPBranchList(dl++, attachments[i]);
} else {
gSPEndDisplayList(dl++);
}
}
return jumpTable;
}
void skRenderObject(struct SKArmature* object, Gfx** attachements, struct RenderState* intoState) {
if (!object->displayList) {
return;
}
Mtx* boneMatrices = renderStateRequestMatrices(intoState, object->numberOfBones);
if (boneMatrices) {
skCalculateTransforms(object, boneMatrices);
gSPSegment(intoState->dl++, MATRIX_TRANSFORM_SEGMENT, osVirtualToPhysical(boneMatrices));
gSPDisplayList(intoState->dl++, object->displayList);
if (!boneMatrices) {
return;
}
Gfx* jumpTable = skBuildAttachments(object, attachements, intoState);
if (!jumpTable && object->numberOfAttachments) {
return;
}
if (jumpTable) {
gSPSegment(intoState->dl++, BONE_ATTACHMENT_SEGMENT, osVirtualToPhysical(jumpTable));
}
skCalculateTransforms(object, boneMatrices);
gSPSegment(intoState->dl++, MATRIX_TRANSFORM_SEGMENT, osVirtualToPhysical(boneMatrices));
gSPDisplayList(intoState->dl++, object->displayList);
}
void skCalculateTransforms(struct SKArmature* object, Mtx* into) {

View File

@ -10,12 +10,14 @@
struct SKArmature {
Gfx* displayList;
struct Transform* boneTransforms;
u32 numberOfBones;
u16 numberOfBones;
u16 numberOfAttachments;
unsigned short* boneParentIndex;
};
void skArmatureInit(struct SKArmature* object, Gfx* displayList, u32 numberOfBones, struct Transform* initialPose, unsigned short* boneParentIndex);
void skRenderObject(struct SKArmature* object, struct RenderState* intoState);
void skArmatureInit(struct SKArmature* object, Gfx* displayList, u32 numberOfBones, struct Transform* initialPose, unsigned short* boneParentIndex, u32 numberOfAttachments);
Gfx* skBuildAttachments(struct SKArmature* object, Gfx** attachments, struct RenderState* renderState);
void skRenderObject(struct SKArmature* object, Gfx** attachements, struct RenderState* intoState);
void skCalculateTransforms(struct SKArmature* object, Mtx* into);
void skCleanupObject(struct SKArmature* object);
void skCalculateBonePosition(struct SKArmature* object, unsigned short boneIndex, struct Vector3* bonePosition, struct Vector3* out);

View File

@ -5,4 +5,7 @@
#define CHARACTER_ANIMATION_SEGMENT 0xD
#define CHARACTER_ANIMATION_SEGMENT_ADDRESS (CHARACTER_ANIMATION_SEGMENT << 24)
#define BONE_ATTACHMENT_SEGMENT 0xE
#define BONE_ATTACHMENT_SEGMENT_ADDRESS (BONE_ATTACHMENT_SEGMENT << 24)
#define ANIM_DATA_ROM_ADDRESS(segmentStart, segmentedAddress) ((void*)((u32)(segmentedAddress) - CHARACTER_ANIMATION_SEGMENT_ADDRESS + (u32)segmentStart))