Skip to content

Commit b5ffb46

Browse files
committed
Improved FBX model support
1 parent 52503c6 commit b5ffb46

7 files changed

Lines changed: 181 additions & 30 deletions

File tree

BeefLibs/Beefy2D/src/geom/Vector3.bf

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -267,9 +267,9 @@ namespace Beefy.geom
267267

268268
public static Vector3 TransformNormal(Vector3 normal, Matrix4 matrix)
269269
{
270-
return Vector3((normal.mX * matrix.m11) + (normal.mY * matrix.m21) + (normal.mZ * matrix.m31),
271-
(normal.mX * matrix.m12) + (normal.mY * matrix.m22) + (normal.mZ * matrix.m32),
272-
(normal.mX * matrix.m13) + (normal.mY * matrix.m23) + (normal.mZ * matrix.m33));
270+
return Vector3((normal.mX * matrix.m00) + (normal.mY * matrix.m01) + (normal.mZ * matrix.m02),
271+
(normal.mX * matrix.m10) + (normal.mY * matrix.m11) + (normal.mZ * matrix.m12),
272+
(normal.mX * matrix.m20) + (normal.mY * matrix.m21) + (normal.mZ * matrix.m22));
273273
}
274274

275275
public static bool operator ==(Vector3 value1, Vector3 value2)

BeefLibs/Beefy2D/src/gfx/Model.bf

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Collections;
33
using System.Text;
44
using System.Threading.Tasks;
@@ -117,6 +117,9 @@ namespace Beefy.gfx
117117
[CallingConvention(.Stdcall), CLink]
118118
extern static void ModelDef_SetBaseDir(void* nativeModel, char8* baseDir);
119119

120+
[CallingConvention(.Stdcall), CLink]
121+
extern static void ModelDef_Scale(void* nativeModel, Vector3 scale);
122+
120123
[CallingConvention(.Stdcall), CLink]
121124
extern static char8* ModelDef_GetInfo(void* nativeModel);
122125

@@ -129,6 +132,9 @@ namespace Beefy.gfx
129132
[CallingConvention(.Stdcall), CLink]
130133
extern static int32 ModelDef_GetJointCount(void* nativeModel);
131134

135+
[CallingConvention(.Stdcall), CLink]
136+
extern static int32 ModelDef_GetJointParent(void* nativeModel, int32 jointIdx);
137+
132138
[CallingConvention(.Stdcall), CLink]
133139
extern static int32 ModelDef_GetAnimCount(void* nativeModel);
134140

@@ -200,6 +206,11 @@ namespace Beefy.gfx
200206
ModelDef_GetBounds(mNativeModelDef, out min, out max);
201207
}
202208

209+
public int32 GetJointParent(int32 jointIdx)
210+
{
211+
return ModelDef_GetJointParent(mNativeModelDef, jointIdx);
212+
}
213+
203214
public void Compact()
204215
{
205216
ModelDef_Compact(mNativeModelDef);
@@ -210,6 +221,11 @@ namespace Beefy.gfx
210221
ModelDef_SetBaseDir(mNativeModelDef, baseDir.ToScopeCStr!());
211222
}
212223

224+
public void Scale(Vector3 scale)
225+
{
226+
ModelDef_Scale(mNativeModelDef, scale);
227+
}
228+
213229
public void SetTextures(int meshIdx, int primitivesIdx, Span<char8*> paths)
214230
{
215231
ModelDef_SetTextures(mNativeModelDef, (.)meshIdx, (.)primitivesIdx, paths.Ptr, (.)paths.Length);
@@ -224,7 +240,7 @@ namespace Beefy.gfx
224240
public bool RayIntersect(Matrix4 worldMtx, Vector3 origin, Vector3 vec, out Vector3 outIntersect, out float outDistance)
225241
{
226242
return ModelDef_RayIntersect(mNativeModelDef, worldMtx, origin, vec, out outIntersect, out outDistance);
227-
}
243+
}
228244
}
229245

230246
public class ModelInstance : RenderCmd

BeefySysLib/fbx/FBXReader.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1491,6 +1491,9 @@ bool FBXReader::ReadFile(const StringImpl& fileName, bool loadAnims)
14911491
for (int row = 0; row < 4; row++)
14921492
for (int col = 0; col < 4; col++)
14931493
joint->mPoseInvMatrix.mMat[row][col] = (float) invGlobalMtx.Get(col, row);
1494+
joint->mBindPoseLocal.mTrans = Vector3((float)fbxJoint->posx, (float)fbxJoint->posy, (float)fbxJoint->posz);
1495+
joint->mBindPoseLocal.mScale = Vector3(fbxJoint->scalex, fbxJoint->scaley, fbxJoint->scalez);
1496+
joint->mBindPoseLocal.mQuat = Quaternion((float)fbxJoint->quatx, (float)fbxJoint->quaty, (float)fbxJoint->quatz, (float)fbxJoint->quatw);
14941497
}
14951498
}
14961499

BeefySysLib/gfx/ModelDef.cpp

Lines changed: 123 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,14 @@ BF_EXPORT void ModelDef_GetBounds(ModelDef* modelDef, Vector3& min, Vector3& max
6969
modelDef->GetBounds(min, max);
7070
}
7171

72-
BF_EXPORT void ModelDef_SetBaseDir(ModelDef* modelDef, char* baseDIr)
72+
BF_EXPORT void ModelDef_SetBaseDir(ModelDef* modelDef, char* baseDir)
7373
{
74-
modelDef->mLoadDir = baseDIr;
74+
modelDef->mLoadDir = baseDir;
75+
}
76+
77+
BF_EXPORT void ModelDef_Scale(ModelDef* modelDef, Vector3 scale)
78+
{
79+
modelDef->Scale(scale);
7580
}
7681

7782
BF_EXPORT const char* BF_CALLTYPE ModelDef_GetInfo(ModelDef* modelDef)
@@ -145,6 +150,11 @@ BF_EXPORT const char* BF_CALLTYPE ModelDefAnimation_GetName(ModelAnimation* mode
145150
return modelAnimation->mName.c_str();
146151
}
147152

153+
BF_EXPORT int BF_CALLTYPE ModelDef_GetJointParent(ModelDef* modelDef, int jointIdx)
154+
{
155+
return modelDef->mJoints[jointIdx].mParentIdx;
156+
}
157+
148158
BF_EXPORT void BF_CALLTYPE ModelDefAnimation_Clip(ModelAnimation* modelAnimation, int startFrame, int numFrames)
149159
{
150160
modelAnimation->mFrames.RemoveRange(0, startFrame);
@@ -164,6 +174,115 @@ ModelDef::~ModelDef()
164174
}
165175
}
166176

177+
void ModelDef::Scale(const Vector3& scale)
178+
{
179+
// For quaternion component flipping under axis reflection: sign of each scale component.
180+
float signX = (scale.mX < 0.0f) ? -1.0f : 1.0f;
181+
float signY = (scale.mY < 0.0f) ? -1.0f : 1.0f;
182+
float signZ = (scale.mZ < 0.0f) ? -1.0f : 1.0f;
183+
184+
// Mesh vertices: positions scale directly; normals transform by S^{-T} = S^{-1} (inverse
185+
// of a diagonal scale); tangents are geometric directions and transform the same as positions.
186+
for (auto& mesh : mMeshes)
187+
{
188+
for (auto& prims : mesh.mPrimitives)
189+
{
190+
for (auto& vtx : prims.mVertices)
191+
{
192+
vtx.mPosition *= scale;
193+
vtx.mNormal = Vector3::Normalize(Vector3(
194+
vtx.mNormal.mX / scale.mX,
195+
vtx.mNormal.mY / scale.mY,
196+
vtx.mNormal.mZ / scale.mZ));
197+
vtx.mTangent = Vector3::Normalize(vtx.mTangent * scale);
198+
}
199+
}
200+
}
201+
202+
// Bone inverse bind-pose matrices transform as S * M * S^{-1}.
203+
// For a diagonal scale S, element (i,j) scales by scale[i] / scale[j],
204+
// where scale[3] = 1 (homogeneous row/column left unchanged).
205+
float scaleArr[4] = { scale.mX, scale.mY, scale.mZ, 1.0f };
206+
for (auto& joint : mJoints)
207+
{
208+
for (int row = 0; row < 4; row++)
209+
for (int col = 0; col < 4; col++)
210+
joint.mPoseInvMatrix.mMat[row][col] *= scaleArr[row] / scaleArr[col];
211+
212+
joint.mBindPoseLocal.mTrans *= scale;
213+
joint.mBindPoseLocal.mQuat = Quaternion(
214+
signY * signZ * joint.mBindPoseLocal.mQuat.mX,
215+
signX * signZ * joint.mBindPoseLocal.mQuat.mY,
216+
signX * signY * joint.mBindPoseLocal.mQuat.mZ,
217+
joint.mBindPoseLocal.mQuat.mW);
218+
}
219+
220+
// Animation frame data: local joint translations are the translation column of
221+
// S * L * S^{-1}, which equals S * mTrans. Under axis reflections the rotation
222+
// changes as S * R * S^{-1}; its quaternion form is q_new = (w, sy*sz*x, sx*sz*y, sx*sy*z).
223+
for (auto& anim : mAnims)
224+
{
225+
for (auto& frame : anim.mFrames)
226+
{
227+
for (auto& jt : frame.mJointTranslations)
228+
{
229+
jt.mTrans *= scale;
230+
jt.mQuat = Quaternion(
231+
signY * signZ * jt.mQuat.mX,
232+
signX * signZ * jt.mQuat.mY,
233+
signX * signY * jt.mQuat.mZ,
234+
jt.mQuat.mW);
235+
}
236+
}
237+
}
238+
239+
// Scene node translations.
240+
for (auto& node : mNodes)
241+
node.mTranslation *= scale;
242+
243+
// BVH geometry and derived bounds.
244+
if ((mFlags & Flags_HasBVH) != 0)
245+
{
246+
for (auto& bvVtx : mBVVertices)
247+
bvVtx *= scale;
248+
249+
float maxAbsScale = fabs(scale.mX);
250+
if (fabs(scale.mY) > maxAbsScale) maxAbsScale = fabs(scale.mY);
251+
if (fabs(scale.mZ) > maxAbsScale) maxAbsScale = fabs(scale.mZ);
252+
253+
for (auto& bvNode : mBVNodes)
254+
{
255+
bvNode.mBoundSphere.mCenter *= scale;
256+
bvNode.mBoundSphere.mRadius *= maxAbsScale;
257+
258+
Vector3 newMin = bvNode.mBoundAABB.mMin * scale;
259+
Vector3 newMax = bvNode.mBoundAABB.mMax * scale;
260+
bvNode.mBoundAABB.mMin = Vector3(
261+
(newMin.mX < newMax.mX) ? newMin.mX : newMax.mX,
262+
(newMin.mY < newMax.mY) ? newMin.mY : newMax.mY,
263+
(newMin.mZ < newMax.mZ) ? newMin.mZ : newMax.mZ);
264+
bvNode.mBoundAABB.mMax = Vector3(
265+
(newMin.mX > newMax.mX) ? newMin.mX : newMax.mX,
266+
(newMin.mY > newMax.mY) ? newMin.mY : newMax.mY,
267+
(newMin.mZ > newMax.mZ) ? newMin.mZ : newMax.mZ);
268+
}
269+
}
270+
271+
if ((mFlags & Flags_HasBounds) != 0)
272+
{
273+
Vector3 newMin = mBounds.mMin * scale;
274+
Vector3 newMax = mBounds.mMax * scale;
275+
mBounds.mMin = Vector3(
276+
(newMin.mX < newMax.mX) ? newMin.mX : newMax.mX,
277+
(newMin.mY < newMax.mY) ? newMin.mY : newMax.mY,
278+
(newMin.mZ < newMax.mZ) ? newMin.mZ : newMax.mZ);
279+
mBounds.mMax = Vector3(
280+
(newMin.mX > newMax.mX) ? newMin.mX : newMax.mX,
281+
(newMin.mY > newMax.mY) ? newMin.mY : newMax.mY,
282+
(newMin.mZ > newMax.mZ) ? newMin.mZ : newMax.mZ);
283+
}
284+
}
285+
167286
void ModelDef::Compact()
168287
{
169288
for (auto& mesh : mMeshes)
@@ -276,9 +395,9 @@ static int partition(float a[], int left, int right, int pIndex)
276395
return pIndex;
277396
}
278397

279-
// Returns the k'th smallest element in the list within `leftright`
398+
// Returns the k'th smallest element in the list within `leftright`
280399
// (i.e., `left <= k <= right`). The search space within the array is
281-
// changing for each round but the list is still the same size.
400+
// changing for each round but the list is still the same size.
282401
// Thus, `k` does not need to be updated with each round.
283402
static float quickselect(float A[], int left, int right, int k)
284403
{

BeefySysLib/gfx/ModelDef.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,16 @@ class ModelJoint
5757
String mName;
5858
int mParentIdx;
5959
Matrix4 mPoseInvMatrix;
60+
ModelJointTranslation mBindPoseLocal;
61+
62+
public:
63+
ModelJoint()
64+
{
65+
mParentIdx = -1;
66+
mBindPoseLocal.mTrans = Vector3(0, 0, 0);
67+
mBindPoseLocal.mScale = Vector3(1, 1, 1);
68+
mBindPoseLocal.mQuat = Quaternion(0, 0, 0, 1);
69+
}
6070
};
6171

6272
class ModelMetalicRoughness
@@ -238,6 +248,7 @@ class ModelDef
238248
ModelDef();
239249
~ModelDef();
240250

251+
void Scale(const Vector3& scale);
241252
void Compact();
242253
void GetBounds(Vector3& min, Vector3& max);
243254

BeefySysLib/gfx/ModelInstance.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ ModelInstance::ModelInstance(ModelDef* modelDef)
66
{
77
mNext = NULL;
88
mModelDef = modelDef;
9-
mJointTranslations.Resize(mModelDef->mJoints.size());
9+
mJointTranslations.Resize(mModelDef->mJoints.size());
10+
for (int i = 0; i < (int)mModelDef->mJoints.size(); i++)
11+
mJointTranslations[i] = mModelDef->mJoints[i].mBindPoseLocal;
1012
mMeshesVisible.Insert(0, true, mModelDef->mMeshes.size());
1113
}
1214

BeefySysLib/platform/win/DXRenderDevice.cpp

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1469,11 +1469,7 @@ void Beefy::DXModelInstance::CommandQueued(DrawLayer* drawLayer)
14691469
BF_ASSERT(mRenderState->mShader->mVertexSize == sizeof(DXModelVertex));
14701470
drawLayer->mCurTextures[0] = NULL;
14711471

1472-
#ifndef BF_NO_FBX
1473-
if (mModelDef->mAnims.IsEmpty())
1474-
return;
1475-
ModelAnimation* fbxAnim = &mModelDef->mAnims[0];
1476-
1472+
#ifndef BF_NO_FBX
14771473
Matrix4 jointsMatrices[BF_MAX_NUM_BONES];
14781474
for (int jointIdx = 0; jointIdx < (int)mJointTranslations.size(); jointIdx++)
14791475
{
@@ -1522,40 +1518,44 @@ void Beefy::DXModelInstance::CommandQueued(DrawLayer* drawLayer)
15221518
ModelVertex* srcVtxData = &modelPrims->mVertices[vtxIdx];
15231519

15241520
Vector3 vtx(0, 0, 0);
1521+
Vector3 normal(0, 0, 0);
1522+
Vector3 tangent(0, 0, 0);
15251523

15261524
float totalWeight = 0;
15271525

1528-
//TODO:
1529-
vtx = srcVtxData->mPosition;
1530-
1531-
/*for (int weightIdx = 0; weightIdx < srcVtxData->mNumBoneWeights; weightIdx++)
1526+
for (int weightIdx = 0; weightIdx < srcVtxData->mNumBoneWeights; weightIdx++)
15321527
{
15331528
int jointIdx = srcVtxData->mBoneIndices[weightIdx];
15341529
float boneWeight = srcVtxData->mBoneWeights[weightIdx];
15351530

15361531
Matrix4* mtx = &jointsMatrices[jointIdx];
1537-
Vector3 origVec = srcVtxData->mPosition;
15381532

1539-
Vector3 transVec = Vector3::Transform(origVec, *mtx);
1540-
transVec = transVec * boneWeight;
1541-
vtx = vtx + transVec;
1533+
vtx = vtx + Vector3::Transform(srcVtxData->mPosition, *mtx) * boneWeight;
1534+
1535+
Vector3 origNormal = srcVtxData->mNormal;
1536+
normal = normal + Vector3(
1537+
mtx->m00 * origNormal.mX + mtx->m01 * origNormal.mY + mtx->m02 * origNormal.mZ,
1538+
mtx->m10 * origNormal.mX + mtx->m11 * origNormal.mY + mtx->m12 * origNormal.mZ,
1539+
mtx->m20 * origNormal.mX + mtx->m21 * origNormal.mY + mtx->m22 * origNormal.mZ) * boneWeight;
1540+
1541+
Vector3 origTangent = srcVtxData->mTangent;
1542+
tangent = tangent + Vector3(
1543+
mtx->m00 * origTangent.mX + mtx->m01 * origTangent.mY + mtx->m02 * origTangent.mZ,
1544+
mtx->m10 * origTangent.mX + mtx->m11 * origTangent.mY + mtx->m12 * origTangent.mZ,
1545+
mtx->m20 * origTangent.mX + mtx->m21 * origTangent.mY + mtx->m22 * origTangent.mZ) * boneWeight;
15421546

15431547
totalWeight += boneWeight;
15441548
}
15451549
BF_ASSERT(fabs(totalWeight - 1.0) < 0.1f);
1546-
*/
1547-
1548-
1549-
15501550

15511551
DXModelVertex* destVtx = dxVtxData + vtxIdx;
15521552

1553-
//destVtx->mPosition = srcVtxData->mPosition;
15541553
destVtx->mPosition = vtx;
1554+
destVtx->mNormal = Vector3::Normalize(normal);
1555+
destVtx->mTangent = Vector3::Normalize(tangent);
15551556
destVtx->mTexCoords = srcVtxData->mTexCoords;
1556-
destVtx->mBumpTexCoords = srcVtxData->mTexCoords;
1557+
destVtx->mBumpTexCoords = srcVtxData->mBumpTexCoords;
15571558
destVtx->mColor = 0xFFFFFFFF; //TODO: Color
1558-
destVtx->mTangent = srcVtxData->mTangent;
15591559
}
15601560

15611561
dxRenderDevice->mD3DDeviceContext->Unmap(dxPrims->mD3DVertexBuffer, 0);

0 commit comments

Comments
 (0)