@@ -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
7782BF_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+
148158BF_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+
167286void 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 `left… right`
398+ // Returns the k'th smallest element in the list within `left� right`
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.
283402static float quickselect (float A[], int left, int right, int k)
284403{
0 commit comments