mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-04-19 15:52:53 +00:00
Merge pull request #244 from Laupetin/feature/t6-xmodel-raw
feat: gltf xmodel loading from raw for t6
This commit is contained in:
commit
8bab112afe
21
raw/t5/partclassification.csv
Normal file
21
raw/t5/partclassification.csv
Normal file
@ -0,0 +1,21 @@
|
||||
J_Hip_RI,right_leg_upper
|
||||
J_Hip_LE,left_leg_upper
|
||||
J_Knee_RI,right_leg_lower
|
||||
J_SpineUpper,torso_upper
|
||||
J_Knee_LE,left_leg_lower
|
||||
J_Ankle_RI,right_foot
|
||||
J_Ankle_LE,left_foot
|
||||
J_Clavicle_RI,torso_upper
|
||||
J_Clavicle_LE,torso_upper
|
||||
J_Shoulder_RI,right_arm_upper
|
||||
J_Shoulder_LE,left_arm_upper
|
||||
J_Neck,neck
|
||||
J_Head,head
|
||||
J_Elbow_RI,right_arm_lower
|
||||
J_Elbow_LE,left_arm_lower
|
||||
J_Wrist_RI,right_hand
|
||||
J_Wrist_LE,left_hand
|
||||
J_MainRoot,torso_lower
|
||||
TAG_WEAPON_LEFT,gun
|
||||
TAG_WEAPON_RIGHT,gun
|
||||
J_Helmet,helmet
|
|
19
raw/t5/partclassification_mp.csv
Normal file
19
raw/t5/partclassification_mp.csv
Normal file
@ -0,0 +1,19 @@
|
||||
J_Hip_RI,right_leg_upper
|
||||
J_Hip_LE,left_leg_upper
|
||||
J_Knee_RI,right_leg_lower
|
||||
J_SpineUpper,torso_lower
|
||||
J_SpineLower,torso_lower
|
||||
J_MainRoot,torso_lower
|
||||
J_Knee_LE,left_leg_lower
|
||||
J_Ankle_RI,right_foot
|
||||
J_Ankle_LE,left_foot
|
||||
J_Clavicle_RI,torso_upper
|
||||
J_Clavicle_LE,torso_upper
|
||||
J_Shoulder_RI,right_arm_upper
|
||||
J_Shoulder_LE,left_arm_upper
|
||||
J_Neck,neck
|
||||
J_Head,head
|
||||
J_Elbow_RI,right_arm_lower
|
||||
J_Elbow_LE,left_arm_lower
|
||||
J_Wrist_RI,right_hand
|
||||
J_Wrist_LE,left_hand
|
|
23
raw/t6/partclassification.csv
Normal file
23
raw/t6/partclassification.csv
Normal file
@ -0,0 +1,23 @@
|
||||
j_helmet,helmet
|
||||
j_head,head
|
||||
j_neck,neck
|
||||
j_clavicle_le,torso_upper
|
||||
j_clavicle_ri,torso_upper
|
||||
j_spineupper,torso_upper
|
||||
j_mainroot,torso_lower
|
||||
j_shoulder_ri,right_arm_upper
|
||||
j_shoulder_le,left_arm_upper
|
||||
j_elbow_ri,right_arm_lower
|
||||
j_elbow_le,left_arm_lower
|
||||
j_wrist_ri,right_hand
|
||||
j_wrist_le,left_hand
|
||||
j_hip_ri,right_leg_upper
|
||||
j_hip_le,left_leg_upper
|
||||
j_knee_ri,right_leg_lower
|
||||
j_knee_le,left_leg_lower
|
||||
j_ankle_ri,right_foot
|
||||
j_ankle_le,left_foot
|
||||
tag_weapon_left,gun
|
||||
tag_weapon_right,gun
|
||||
tag_stowed_back,shield
|
||||
tag_weapon_left,shield
|
|
23
raw/t6/partclassification_mp.csv
Normal file
23
raw/t6/partclassification_mp.csv
Normal file
@ -0,0 +1,23 @@
|
||||
j_helmet,helmet
|
||||
j_head,head
|
||||
j_neck,neck
|
||||
j_clavicle_le,torso_upper
|
||||
j_clavicle_ri,torso_upper
|
||||
j_spineupper,torso_middle
|
||||
j_mainroot,torso_lower
|
||||
j_shoulder_ri,right_arm_upper
|
||||
j_shoulder_le,left_arm_upper
|
||||
j_elbow_ri,right_arm_lower
|
||||
j_elbow_le,left_arm_lower
|
||||
j_wrist_ri,right_hand
|
||||
j_wrist_le,left_hand
|
||||
j_hip_ri,right_leg_upper
|
||||
j_hip_le,left_leg_upper
|
||||
j_knee_ri,right_leg_lower
|
||||
j_knee_le,left_leg_lower
|
||||
j_ankle_ri,right_foot
|
||||
j_ankle_le,left_foot
|
||||
tag_weapon_left,gun
|
||||
tag_weapon_right,gun
|
||||
tag_stowed_back,shield
|
||||
tag_weapon_left,shield
|
|
@ -4,32 +4,32 @@
|
||||
|
||||
using namespace IW3;
|
||||
|
||||
PackedTexCoords Common::Vec2PackTexCoords(const vec2_t* in)
|
||||
PackedTexCoords Common::Vec2PackTexCoords(const float (&in)[2])
|
||||
{
|
||||
return PackedTexCoords{Pack32::Vec2PackTexCoords(reinterpret_cast<const float*>(in))};
|
||||
return PackedTexCoords{pack32::Vec2PackTexCoordsVU(in)};
|
||||
}
|
||||
|
||||
PackedUnitVec Common::Vec3PackUnitVec(const vec3_t* in)
|
||||
PackedUnitVec Common::Vec3PackUnitVec(const float (&in)[3])
|
||||
{
|
||||
return PackedUnitVec{Pack32::Vec3PackUnitVec(reinterpret_cast<const float*>(in))};
|
||||
return PackedUnitVec{pack32::Vec3PackUnitVecScaleBased(in)};
|
||||
}
|
||||
|
||||
GfxColor Common::Vec4PackGfxColor(const vec4_t* in)
|
||||
GfxColor Common::Vec4PackGfxColor(const float (&in)[4])
|
||||
{
|
||||
return GfxColor{Pack32::Vec4PackGfxColor(reinterpret_cast<const float*>(in))};
|
||||
return GfxColor{pack32::Vec4PackGfxColor(in)};
|
||||
}
|
||||
|
||||
void Common::Vec2UnpackTexCoords(const PackedTexCoords& in, vec2_t* out)
|
||||
void Common::Vec2UnpackTexCoords(const PackedTexCoords& in, float (&out)[2])
|
||||
{
|
||||
Pack32::Vec2UnpackTexCoordsVU(in.packed, reinterpret_cast<float*>(out));
|
||||
pack32::Vec2UnpackTexCoordsVU(in.packed, out);
|
||||
}
|
||||
|
||||
void Common::Vec3UnpackUnitVec(const PackedUnitVec& in, vec3_t* out)
|
||||
void Common::Vec3UnpackUnitVec(const PackedUnitVec& in, float (&out)[3])
|
||||
{
|
||||
Pack32::Vec3UnpackUnitVecScaleBased(in.packed, reinterpret_cast<float*>(out));
|
||||
pack32::Vec3UnpackUnitVecScaleBased(in.packed, out);
|
||||
}
|
||||
|
||||
void Common::Vec4UnpackGfxColor(const GfxColor& in, vec4_t* out)
|
||||
void Common::Vec4UnpackGfxColor(const GfxColor& in, float (&out)[4])
|
||||
{
|
||||
Pack32::Vec4UnpackGfxColor(in.packed, reinterpret_cast<float*>(out));
|
||||
pack32::Vec4UnpackGfxColor(in.packed, out);
|
||||
}
|
||||
|
@ -21,11 +21,11 @@ namespace IW3
|
||||
return result;
|
||||
}
|
||||
|
||||
static PackedTexCoords Vec2PackTexCoords(const vec2_t* in);
|
||||
static PackedUnitVec Vec3PackUnitVec(const vec3_t* in);
|
||||
static GfxColor Vec4PackGfxColor(const vec4_t* in);
|
||||
static void Vec2UnpackTexCoords(const PackedTexCoords& in, vec2_t* out);
|
||||
static void Vec3UnpackUnitVec(const PackedUnitVec& in, vec3_t* out);
|
||||
static void Vec4UnpackGfxColor(const GfxColor& in, vec4_t* out);
|
||||
static PackedTexCoords Vec2PackTexCoords(const float (&in)[2]);
|
||||
static PackedUnitVec Vec3PackUnitVec(const float (&in)[3]);
|
||||
static GfxColor Vec4PackGfxColor(const float (&in)[4]);
|
||||
static void Vec2UnpackTexCoords(const PackedTexCoords& in, float (&out)[2]);
|
||||
static void Vec3UnpackUnitVec(const PackedUnitVec& in, float (&out)[3]);
|
||||
static void Vec4UnpackGfxColor(const GfxColor& in, float (&out)[4]);
|
||||
};
|
||||
} // namespace IW3
|
||||
|
@ -20,32 +20,32 @@ int Common::StringTable_HashString(const char* str)
|
||||
return result;
|
||||
}
|
||||
|
||||
PackedTexCoords Common::Vec2PackTexCoords(const vec2_t* in)
|
||||
PackedTexCoords Common::Vec2PackTexCoords(const float (&in)[2])
|
||||
{
|
||||
return PackedTexCoords{Pack32::Vec2PackTexCoords(reinterpret_cast<const float*>(in))};
|
||||
return PackedTexCoords{pack32::Vec2PackTexCoordsVU(in)};
|
||||
}
|
||||
|
||||
PackedUnitVec Common::Vec3PackUnitVec(const vec3_t* in)
|
||||
PackedUnitVec Common::Vec3PackUnitVec(const float (&in)[3])
|
||||
{
|
||||
return PackedUnitVec{Pack32::Vec3PackUnitVec(reinterpret_cast<const float*>(in))};
|
||||
return PackedUnitVec{pack32::Vec3PackUnitVecScaleBased(in)};
|
||||
}
|
||||
|
||||
GfxColor Common::Vec4PackGfxColor(const vec4_t* in)
|
||||
GfxColor Common::Vec4PackGfxColor(const float (&in)[4])
|
||||
{
|
||||
return GfxColor{Pack32::Vec4PackGfxColor(reinterpret_cast<const float*>(in))};
|
||||
return GfxColor{pack32::Vec4PackGfxColor(in)};
|
||||
}
|
||||
|
||||
void Common::Vec2UnpackTexCoords(const PackedTexCoords& in, vec2_t* out)
|
||||
void Common::Vec2UnpackTexCoords(const PackedTexCoords& in, float (&out)[2])
|
||||
{
|
||||
Pack32::Vec2UnpackTexCoordsVU(in.packed, reinterpret_cast<float*>(out));
|
||||
pack32::Vec2UnpackTexCoordsVU(in.packed, out);
|
||||
}
|
||||
|
||||
void Common::Vec3UnpackUnitVec(const PackedUnitVec& in, vec3_t* out)
|
||||
void Common::Vec3UnpackUnitVec(const PackedUnitVec& in, float (&out)[3])
|
||||
{
|
||||
Pack32::Vec3UnpackUnitVecScaleBased(in.packed, reinterpret_cast<float*>(out));
|
||||
pack32::Vec3UnpackUnitVecScaleBased(in.packed, out);
|
||||
}
|
||||
|
||||
void Common::Vec4UnpackGfxColor(const GfxColor& in, vec4_t* out)
|
||||
void Common::Vec4UnpackGfxColor(const GfxColor& in, float (&out)[4])
|
||||
{
|
||||
Pack32::Vec4UnpackGfxColor(in.packed, reinterpret_cast<float*>(out));
|
||||
pack32::Vec4UnpackGfxColor(in.packed, out);
|
||||
}
|
||||
|
@ -28,11 +28,11 @@ namespace IW4
|
||||
|
||||
static int StringTable_HashString(const char* str);
|
||||
|
||||
static PackedTexCoords Vec2PackTexCoords(const vec2_t* in);
|
||||
static PackedUnitVec Vec3PackUnitVec(const vec3_t* in);
|
||||
static GfxColor Vec4PackGfxColor(const vec4_t* in);
|
||||
static void Vec2UnpackTexCoords(const PackedTexCoords& in, vec2_t* out);
|
||||
static void Vec3UnpackUnitVec(const PackedUnitVec& in, vec3_t* out);
|
||||
static void Vec4UnpackGfxColor(const GfxColor& in, vec4_t* out);
|
||||
static PackedTexCoords Vec2PackTexCoords(const float (&in)[2]);
|
||||
static PackedUnitVec Vec3PackUnitVec(const float (&in)[3]);
|
||||
static GfxColor Vec4PackGfxColor(const float (&in)[4]);
|
||||
static void Vec2UnpackTexCoords(const PackedTexCoords& in, float (&out)[2]);
|
||||
static void Vec3UnpackUnitVec(const PackedUnitVec& in, float (&out)[3]);
|
||||
static void Vec4UnpackGfxColor(const GfxColor& in, float (&out)[4]);
|
||||
};
|
||||
} // namespace IW4
|
||||
|
@ -4082,7 +4082,7 @@ namespace IW4
|
||||
HITLOC_GUN = 0x12,
|
||||
HITLOC_SHIELD = 0x13,
|
||||
|
||||
HITLOC_NUM,
|
||||
HITLOC_COUNT,
|
||||
};
|
||||
|
||||
struct snd_alias_list_name
|
||||
|
@ -20,32 +20,32 @@ int Common::StringTable_HashString(const char* str)
|
||||
return result;
|
||||
}
|
||||
|
||||
PackedTexCoords Common::Vec2PackTexCoords(const vec2_t* in)
|
||||
PackedTexCoords Common::Vec2PackTexCoords(const float (&in)[2])
|
||||
{
|
||||
return PackedTexCoords{Pack32::Vec2PackTexCoords(reinterpret_cast<const float*>(in))};
|
||||
return PackedTexCoords{pack32::Vec2PackTexCoordsVU(in)};
|
||||
}
|
||||
|
||||
PackedUnitVec Common::Vec3PackUnitVec(const vec3_t* in)
|
||||
PackedUnitVec Common::Vec3PackUnitVec(const float (&in)[3])
|
||||
{
|
||||
return PackedUnitVec{Pack32::Vec3PackUnitVec(reinterpret_cast<const float*>(in))};
|
||||
return PackedUnitVec{pack32::Vec3PackUnitVecScaleBased(in)};
|
||||
}
|
||||
|
||||
GfxColor Common::Vec4PackGfxColor(const vec4_t* in)
|
||||
GfxColor Common::Vec4PackGfxColor(const float (&in)[4])
|
||||
{
|
||||
return GfxColor{Pack32::Vec4PackGfxColor(reinterpret_cast<const float*>(in))};
|
||||
return GfxColor{pack32::Vec4PackGfxColor(in)};
|
||||
}
|
||||
|
||||
void Common::Vec2UnpackTexCoords(const PackedTexCoords& in, vec2_t* out)
|
||||
void Common::Vec2UnpackTexCoords(const PackedTexCoords& in, float (&out)[2])
|
||||
{
|
||||
Pack32::Vec2UnpackTexCoordsVU(in.packed, reinterpret_cast<float*>(out));
|
||||
pack32::Vec2UnpackTexCoordsVU(in.packed, out);
|
||||
}
|
||||
|
||||
void Common::Vec3UnpackUnitVec(const PackedUnitVec& in, vec3_t* out)
|
||||
void Common::Vec3UnpackUnitVec(const PackedUnitVec& in, float (&out)[3])
|
||||
{
|
||||
Pack32::Vec3UnpackUnitVecScaleBased(in.packed, reinterpret_cast<float*>(out));
|
||||
pack32::Vec3UnpackUnitVecScaleBased(in.packed, out);
|
||||
}
|
||||
|
||||
void Common::Vec4UnpackGfxColor(const GfxColor& in, vec4_t* out)
|
||||
void Common::Vec4UnpackGfxColor(const GfxColor& in, float (&out)[4])
|
||||
{
|
||||
Pack32::Vec4UnpackGfxColor(in.packed, reinterpret_cast<float*>(out));
|
||||
pack32::Vec4UnpackGfxColor(in.packed, out);
|
||||
}
|
||||
|
@ -9,11 +9,11 @@ namespace IW5
|
||||
public:
|
||||
static int StringTable_HashString(const char* str);
|
||||
|
||||
static PackedTexCoords Vec2PackTexCoords(const vec2_t* in);
|
||||
static PackedUnitVec Vec3PackUnitVec(const vec3_t* in);
|
||||
static GfxColor Vec4PackGfxColor(const vec4_t* in);
|
||||
static void Vec2UnpackTexCoords(const PackedTexCoords& in, vec2_t* out);
|
||||
static void Vec3UnpackUnitVec(const PackedUnitVec& in, vec3_t* out);
|
||||
static void Vec4UnpackGfxColor(const GfxColor& in, vec4_t* out);
|
||||
static PackedTexCoords Vec2PackTexCoords(const float (&in)[2]);
|
||||
static PackedUnitVec Vec3PackUnitVec(const float (&in)[3]);
|
||||
static GfxColor Vec4PackGfxColor(const float (&in)[4]);
|
||||
static void Vec2UnpackTexCoords(const PackedTexCoords& in, float (&out)[2]);
|
||||
static void Vec3UnpackUnitVec(const PackedUnitVec& in, float (&out)[3]);
|
||||
static void Vec4UnpackGfxColor(const GfxColor& in, float (&out)[4]);
|
||||
};
|
||||
} // namespace IW5
|
||||
|
@ -3452,7 +3452,7 @@ namespace IW5
|
||||
HITLOC_GUN = 0x12,
|
||||
HITLOC_SHIELD = 0x13,
|
||||
|
||||
HITLOC_NUM
|
||||
HITLOC_COUNT
|
||||
};
|
||||
|
||||
enum materialSurfType_t
|
||||
|
@ -58,32 +58,32 @@ int Common::Com_HashString(const char* str, const int len)
|
||||
return result;
|
||||
}
|
||||
|
||||
PackedTexCoords Common::Vec2PackTexCoords(const vec2_t* in)
|
||||
PackedTexCoords Common::Vec2PackTexCoords(const float (&in)[2])
|
||||
{
|
||||
return PackedTexCoords{Pack32::Vec2PackTexCoords(reinterpret_cast<const float*>(in))};
|
||||
return PackedTexCoords{pack32::Vec2PackTexCoordsVU(in)};
|
||||
}
|
||||
|
||||
PackedUnitVec Common::Vec3PackUnitVec(const vec3_t* in)
|
||||
PackedUnitVec Common::Vec3PackUnitVec(const float (&in)[3])
|
||||
{
|
||||
return PackedUnitVec{Pack32::Vec3PackUnitVec(reinterpret_cast<const float*>(in))};
|
||||
return PackedUnitVec{pack32::Vec3PackUnitVecScaleBased(in)};
|
||||
}
|
||||
|
||||
GfxColor Common::Vec4PackGfxColor(const vec4_t* in)
|
||||
GfxColor Common::Vec4PackGfxColor(const float (&in)[4])
|
||||
{
|
||||
return GfxColor{Pack32::Vec4PackGfxColor(reinterpret_cast<const float*>(in))};
|
||||
return GfxColor{pack32::Vec4PackGfxColor(in)};
|
||||
}
|
||||
|
||||
void Common::Vec2UnpackTexCoords(const PackedTexCoords& in, vec2_t* out)
|
||||
void Common::Vec2UnpackTexCoords(const PackedTexCoords& in, float (&out)[2])
|
||||
{
|
||||
Pack32::Vec2UnpackTexCoordsVU(in.packed, reinterpret_cast<float*>(out));
|
||||
pack32::Vec2UnpackTexCoordsVU(in.packed, out);
|
||||
}
|
||||
|
||||
void Common::Vec3UnpackUnitVec(const PackedUnitVec& in, vec3_t* out)
|
||||
void Common::Vec3UnpackUnitVec(const PackedUnitVec& in, float (&out)[3])
|
||||
{
|
||||
Pack32::Vec3UnpackUnitVecScaleBased(in.packed, reinterpret_cast<float*>(out));
|
||||
pack32::Vec3UnpackUnitVecScaleBased(in.packed, out);
|
||||
}
|
||||
|
||||
void Common::Vec4UnpackGfxColor(const GfxColor& in, vec4_t* out)
|
||||
void Common::Vec4UnpackGfxColor(const GfxColor& in, float (&out)[4])
|
||||
{
|
||||
Pack32::Vec4UnpackGfxColor(in.packed, reinterpret_cast<float*>(out));
|
||||
pack32::Vec4UnpackGfxColor(in.packed, out);
|
||||
}
|
||||
|
@ -11,11 +11,11 @@ namespace T5
|
||||
static int Com_HashString(const char* str);
|
||||
static int Com_HashString(const char* str, int len);
|
||||
|
||||
static PackedTexCoords Vec2PackTexCoords(const vec2_t* in);
|
||||
static PackedUnitVec Vec3PackUnitVec(const vec3_t* in);
|
||||
static GfxColor Vec4PackGfxColor(const vec4_t* in);
|
||||
static void Vec2UnpackTexCoords(const PackedTexCoords& in, vec2_t* out);
|
||||
static void Vec3UnpackUnitVec(const PackedUnitVec& in, vec3_t* out);
|
||||
static void Vec4UnpackGfxColor(const GfxColor& in, vec4_t* out);
|
||||
static PackedTexCoords Vec2PackTexCoords(const float (&in)[2]);
|
||||
static PackedUnitVec Vec3PackUnitVec(const float (&in)[3]);
|
||||
static GfxColor Vec4PackGfxColor(const float (&in)[4]);
|
||||
static void Vec2UnpackTexCoords(const PackedTexCoords& in, float (&out)[2]);
|
||||
static void Vec3UnpackUnitVec(const PackedUnitVec& in, float (&out)[3]);
|
||||
static void Vec4UnpackGfxColor(const GfxColor& in, float (&out)[4]);
|
||||
};
|
||||
} // namespace T5
|
||||
|
@ -108,9 +108,49 @@ namespace T5
|
||||
MAX_XFILE_COUNT
|
||||
};
|
||||
|
||||
typedef float vec2_t[2];
|
||||
typedef float vec3_t[3];
|
||||
typedef float vec4_t[4];
|
||||
union vec2_t
|
||||
{
|
||||
float v[2];
|
||||
|
||||
struct
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
};
|
||||
|
||||
union vec3_t
|
||||
{
|
||||
struct
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
};
|
||||
|
||||
float v[3];
|
||||
};
|
||||
|
||||
union vec4_t
|
||||
{
|
||||
float v[4];
|
||||
|
||||
struct
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
float w;
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
float r;
|
||||
float g;
|
||||
float b;
|
||||
float a;
|
||||
};
|
||||
};
|
||||
|
||||
union XAssetHeader
|
||||
{
|
||||
@ -166,8 +206,6 @@ namespace T5
|
||||
typedef tdef_align(128) float float_align128;
|
||||
|
||||
typedef char cbrushedge_t;
|
||||
typedef float vec2_t[2];
|
||||
typedef float vec3_t[3];
|
||||
typedef tdef_align(128) unsigned int raw_uint128;
|
||||
|
||||
struct PhysPreset
|
||||
@ -619,6 +657,11 @@ namespace T5
|
||||
PhysGeomList* geomList;
|
||||
};
|
||||
|
||||
struct XModelQuat
|
||||
{
|
||||
int16_t v[4];
|
||||
};
|
||||
|
||||
struct XModel
|
||||
{
|
||||
const char* name;
|
||||
@ -628,8 +671,8 @@ namespace T5
|
||||
char lodRampType;
|
||||
uint16_t* boneNames;
|
||||
char* parentList;
|
||||
int16_t (*quats)[4];
|
||||
float (*trans)[4];
|
||||
XModelQuat* quats;
|
||||
float* trans;
|
||||
char* partClassification;
|
||||
DObjAnimMat* baseMat;
|
||||
XSurface* surfs;
|
||||
@ -3288,7 +3331,7 @@ namespace T5
|
||||
HITLOC_L_FOOT = 0x11,
|
||||
HITLOC_GUN = 0x12,
|
||||
|
||||
HITLOC_NUM
|
||||
HITLOC_COUNT
|
||||
};
|
||||
|
||||
struct flameTable
|
||||
|
@ -58,32 +58,32 @@ int Common::Com_HashString(const char* str, const int len)
|
||||
return result;
|
||||
}
|
||||
|
||||
PackedTexCoords Common::Vec2PackTexCoords(const vec2_t* in)
|
||||
PackedTexCoords Common::Vec2PackTexCoords(const float (&in)[2])
|
||||
{
|
||||
return PackedTexCoords{Pack32::Vec2PackTexCoords(in->v)};
|
||||
return PackedTexCoords{pack32::Vec2PackTexCoordsUV(in)};
|
||||
}
|
||||
|
||||
PackedUnitVec Common::Vec3PackUnitVec(const vec3_t* in)
|
||||
PackedUnitVec Common::Vec3PackUnitVec(const float (&in)[3])
|
||||
{
|
||||
return PackedUnitVec{Pack32::Vec3PackUnitVec(in->v)};
|
||||
return PackedUnitVec{pack32::Vec3PackUnitVecThirdBased(in)};
|
||||
}
|
||||
|
||||
GfxColor Common::Vec4PackGfxColor(const vec4_t* in)
|
||||
GfxColor Common::Vec4PackGfxColor(const float (&in)[4])
|
||||
{
|
||||
return GfxColor{Pack32::Vec4PackGfxColor(in->v)};
|
||||
return GfxColor{pack32::Vec4PackGfxColor(in)};
|
||||
}
|
||||
|
||||
void Common::Vec2UnpackTexCoords(const PackedTexCoords& in, vec2_t* out)
|
||||
void Common::Vec2UnpackTexCoords(const PackedTexCoords& in, float (&out)[2])
|
||||
{
|
||||
Pack32::Vec2UnpackTexCoordsUV(in.packed, out->v);
|
||||
pack32::Vec2UnpackTexCoordsUV(in.packed, out);
|
||||
}
|
||||
|
||||
void Common::Vec3UnpackUnitVec(const PackedUnitVec& in, vec3_t* out)
|
||||
void Common::Vec3UnpackUnitVec(const PackedUnitVec& in, float (&out)[3])
|
||||
{
|
||||
Pack32::Vec3UnpackUnitVecThirdBased(in.packed, out->v);
|
||||
pack32::Vec3UnpackUnitVecThirdBased(in.packed, out);
|
||||
}
|
||||
|
||||
void Common::Vec4UnpackGfxColor(const GfxColor& in, vec4_t* out)
|
||||
void Common::Vec4UnpackGfxColor(const GfxColor& in, float (&out)[4])
|
||||
{
|
||||
Pack32::Vec4UnpackGfxColor(in.packed, out->v);
|
||||
pack32::Vec4UnpackGfxColor(in.packed, out);
|
||||
}
|
||||
|
@ -41,11 +41,11 @@ namespace T6
|
||||
return result;
|
||||
}
|
||||
|
||||
static PackedTexCoords Vec2PackTexCoords(const vec2_t* in);
|
||||
static PackedUnitVec Vec3PackUnitVec(const vec3_t* in);
|
||||
static GfxColor Vec4PackGfxColor(const vec4_t* in);
|
||||
static void Vec2UnpackTexCoords(const PackedTexCoords& in, vec2_t* out);
|
||||
static void Vec3UnpackUnitVec(const PackedUnitVec& in, vec3_t* out);
|
||||
static void Vec4UnpackGfxColor(const GfxColor& in, vec4_t* out);
|
||||
static PackedTexCoords Vec2PackTexCoords(const float (&in)[2]);
|
||||
static PackedUnitVec Vec3PackUnitVec(const float (&in)[3]);
|
||||
static GfxColor Vec4PackGfxColor(const float (&in)[4]);
|
||||
static void Vec2UnpackTexCoords(const PackedTexCoords& in, float (&out)[2]);
|
||||
static void Vec3UnpackUnitVec(const PackedUnitVec& in, float (&out)[3]);
|
||||
static void Vec4UnpackGfxColor(const GfxColor& in, float (&out)[4]);
|
||||
};
|
||||
} // namespace T6
|
||||
|
@ -590,7 +590,7 @@ namespace T6
|
||||
float dist;
|
||||
uint16_t numsurfs;
|
||||
uint16_t surfIndex;
|
||||
int partBits[5];
|
||||
unsigned int partBits[5];
|
||||
};
|
||||
|
||||
enum XModelLodRampType : unsigned char
|
||||
@ -601,6 +601,11 @@ namespace T6
|
||||
XMODEL_LOD_RAMP_COUNT
|
||||
};
|
||||
|
||||
struct XModelQuat
|
||||
{
|
||||
int16_t v[4];
|
||||
};
|
||||
|
||||
struct XModel
|
||||
{
|
||||
const char* name;
|
||||
@ -610,9 +615,9 @@ namespace T6
|
||||
XModelLodRampType lodRampType;
|
||||
ScriptString* boneNames;
|
||||
unsigned char* parentList;
|
||||
uint16_t (*quats)[4];
|
||||
float (*trans)[4];
|
||||
char* partClassification;
|
||||
XModelQuat* quats;
|
||||
float* trans;
|
||||
unsigned char* partClassification;
|
||||
DObjAnimMat* baseMat;
|
||||
XSurface* surfs;
|
||||
Material** materialHandles;
|
||||
@ -625,7 +630,7 @@ namespace T6
|
||||
vec3_t mins;
|
||||
vec3_t maxs;
|
||||
uint16_t numLods;
|
||||
uint16_t collLod;
|
||||
int16_t collLod;
|
||||
float* himipInvSqRadii;
|
||||
int memUsage;
|
||||
unsigned int flags;
|
||||
@ -2744,7 +2749,12 @@ namespace T6
|
||||
float* tensionData;
|
||||
};
|
||||
|
||||
typedef tdef_align(16) unsigned short r_index16_t;
|
||||
struct XSurfaceTri
|
||||
{
|
||||
uint16_t i[3];
|
||||
};
|
||||
|
||||
typedef tdef_align(16) XSurfaceTri XSurfaceTri16;
|
||||
|
||||
struct type_align(16) XSurface
|
||||
{
|
||||
@ -2754,13 +2764,13 @@ namespace T6
|
||||
uint16_t vertCount;
|
||||
uint16_t triCount;
|
||||
uint16_t baseVertIndex;
|
||||
r_index16_t(*triIndices)[3];
|
||||
XSurfaceTri16* triIndices;
|
||||
XSurfaceVertexInfo vertInfo;
|
||||
GfxPackedVertex* verts0;
|
||||
void /*ID3D11Buffer*/* vb0;
|
||||
XRigidVertList* vertList;
|
||||
void /*ID3D11Buffer*/* indexBuffer;
|
||||
int partBits[5];
|
||||
unsigned int partBits[5];
|
||||
};
|
||||
|
||||
struct XModelCollSurf_s
|
||||
@ -4324,7 +4334,8 @@ namespace T6
|
||||
MISSILE_GUIDANCE_TVGUIDED = 0x6,
|
||||
MISSILE_GUIDANCE_DRONE = 0x7,
|
||||
MISSILE_GUIDANCE_HEATSEEKING = 0x8,
|
||||
MISSILE_GUIDANCE_COUNT = 0x9,
|
||||
|
||||
MISSILE_GUIDANCE_COUNT
|
||||
};
|
||||
|
||||
enum hitLocation_t
|
||||
@ -4351,7 +4362,7 @@ namespace T6
|
||||
HITLOC_GUN = 0x13,
|
||||
HITLOC_SHIELD = 0x14,
|
||||
|
||||
HITLOC_NUM,
|
||||
HITLOC_COUNT,
|
||||
};
|
||||
|
||||
struct WeaponDef
|
||||
|
@ -17,7 +17,28 @@ float HalfFloat::ToFloat(const half_float_t half)
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
half_float_t HalfFloat::ToHalf(float f)
|
||||
half_float_t HalfFloat::ToHalf(const float f)
|
||||
{
|
||||
return 0;
|
||||
half_float_t v3;
|
||||
int v6;
|
||||
|
||||
union
|
||||
{
|
||||
uint32_t u;
|
||||
float f;
|
||||
} result{};
|
||||
|
||||
result.f = f;
|
||||
|
||||
if (static_cast<int>((2 * result.u) ^ 0x80000000) >> 14 < 0x3FFF)
|
||||
v6 = static_cast<int>((2 * result.u) ^ 0x80000000) >> 14;
|
||||
else
|
||||
v6 = 0x3FFF;
|
||||
|
||||
if (v6 > -16384)
|
||||
v3 = static_cast<half_float_t>(v6);
|
||||
else
|
||||
v3 = 0xC000;
|
||||
|
||||
return (v3 & 0x3FFFu) | ((result.u >> 16) & 0xC000u);
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
|
||||
union PackUtil32
|
||||
{
|
||||
@ -14,54 +15,80 @@ union PackUtil32
|
||||
uint8_t uc[4];
|
||||
};
|
||||
|
||||
uint32_t Pack32::Vec2PackTexCoords(const float* in)
|
||||
namespace pack32
|
||||
{
|
||||
uint32_t Vec2PackTexCoordsUV(const float (&in)[2])
|
||||
{
|
||||
return static_cast<uint32_t>(HalfFloat::ToHalf(in[1])) << 16 | HalfFloat::ToHalf(in[0]);
|
||||
}
|
||||
|
||||
uint32_t Vec2PackTexCoordsVU(const float (&in)[2])
|
||||
{
|
||||
return static_cast<uint32_t>(HalfFloat::ToHalf(in[0])) << 16 | HalfFloat::ToHalf(in[1]);
|
||||
}
|
||||
|
||||
uint32_t Pack32::Vec3PackUnitVec(const float* in)
|
||||
uint32_t Vec3PackUnitVecScaleBased(const float (&in)[3])
|
||||
{
|
||||
// TODO
|
||||
// TODO: Implement
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t Pack32::Vec4PackGfxColor(const float* in)
|
||||
uint32_t Vec3PackUnitVecThirdBased(const float (&in)[3])
|
||||
{
|
||||
return static_cast<uint8_t>(std::clamp(in[0], 0.0f, 1.0f) * 255.0f) | static_cast<uint8_t>(std::clamp(in[1], 0.0f, 1.0f) * 255.0f) << 8
|
||||
| static_cast<uint8_t>(std::clamp(in[2], 0.0f, 1.0f) * 255.0f) << 16 | static_cast<uint8_t>(std::clamp(in[3], 0.0f, 1.0f) * 255.0f) << 24;
|
||||
// This is based on the game's reversed code, the original code may have made a bit more sense
|
||||
PackUtil32 x;
|
||||
x.f = (in[0] - -24624.0939334638f) * 0.0001218318939208984f;
|
||||
PackUtil32 y;
|
||||
y.f = (in[1] - -24624.0939334638f) * 0.0001218318939208984f;
|
||||
PackUtil32 z;
|
||||
z.f = (in[2] - -24624.0939334638f) * 0.0001218318939208984f;
|
||||
|
||||
return x.u | y.u << 10u | z.u << 20u;
|
||||
}
|
||||
|
||||
void Pack32::Vec2UnpackTexCoordsUV(const uint32_t in, float* out)
|
||||
uint32_t Vec4PackGfxColor(const float (&in)[4])
|
||||
{
|
||||
const auto inHiDw = static_cast<half_float_t>((in >> 16) & UINT16_MAX);
|
||||
const auto inLoDw = static_cast<half_float_t>(in & UINT16_MAX);
|
||||
// clang-format off
|
||||
return static_cast<uint8_t>(std::clamp(in[0], 0.0f, 1.0f) * 255.0f)
|
||||
| static_cast<uint8_t>(std::clamp(in[1], 0.0f, 1.0f) * 255.0f) << 8
|
||||
| static_cast<uint8_t>(std::clamp(in[2], 0.0f, 1.0f) * 255.0f) << 16
|
||||
| static_cast<uint8_t>(std::clamp(in[3], 0.0f, 1.0f) * 255.0f) << 24;
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
void Vec2UnpackTexCoordsUV(const uint32_t in, float (&out)[2])
|
||||
{
|
||||
const auto inHiDw = static_cast<half_float_t>((in >> 16) & std::numeric_limits<uint16_t>::max());
|
||||
const auto inLoDw = static_cast<half_float_t>(in & std::numeric_limits<uint16_t>::max());
|
||||
|
||||
out[0] = HalfFloat::ToFloat(inLoDw);
|
||||
out[1] = HalfFloat::ToFloat(inHiDw);
|
||||
}
|
||||
|
||||
void Pack32::Vec2UnpackTexCoordsVU(const uint32_t in, float* out)
|
||||
void Vec2UnpackTexCoordsVU(const uint32_t in, float (&out)[2])
|
||||
{
|
||||
const auto inHiDw = static_cast<half_float_t>((in >> 16) & UINT16_MAX);
|
||||
const auto inLoDw = static_cast<half_float_t>(in & UINT16_MAX);
|
||||
const auto inHiDw = static_cast<half_float_t>((in >> 16) & std::numeric_limits<uint16_t>::max());
|
||||
const auto inLoDw = static_cast<half_float_t>(in & std::numeric_limits<uint16_t>::max());
|
||||
|
||||
out[0] = HalfFloat::ToFloat(inHiDw);
|
||||
out[1] = HalfFloat::ToFloat(inLoDw);
|
||||
}
|
||||
|
||||
void Pack32::Vec3UnpackUnitVecScaleBased(const uint32_t in, float* out)
|
||||
void Vec3UnpackUnitVecScaleBased(const uint32_t in, float (&out)[3])
|
||||
{
|
||||
assert(out != nullptr);
|
||||
|
||||
PackUtil32 _in{in};
|
||||
const float decodeScale = (static_cast<float>(_in.uc[3]) - -192.0f) / 32385.0f;
|
||||
out[0] = (static_cast<float>(_in.uc[0]) + -127.0f) * decodeScale;
|
||||
out[1] = (static_cast<float>(_in.uc[1]) + -127.0f) * decodeScale;
|
||||
out[2] = (static_cast<float>(_in.uc[2]) + -127.0f) * decodeScale;
|
||||
const PackUtil32 inUtil{in};
|
||||
const float decodeScale = (static_cast<float>(inUtil.uc[3]) - -192.0f) / 32385.0f;
|
||||
out[0] = (static_cast<float>(inUtil.uc[0]) + -127.0f) * decodeScale;
|
||||
out[1] = (static_cast<float>(inUtil.uc[1]) + -127.0f) * decodeScale;
|
||||
out[2] = (static_cast<float>(inUtil.uc[2]) + -127.0f) * decodeScale;
|
||||
}
|
||||
|
||||
void Pack32::Vec3UnpackUnitVecThirdBased(const uint32_t in, float* out)
|
||||
void Vec3UnpackUnitVecThirdBased(const uint32_t in, float (&out)[3])
|
||||
{
|
||||
// This is based on the game's reversed code, the original code may have made a bit more sense
|
||||
PackUtil32 v0{(in >> 0) & 0x3FF};
|
||||
PackUtil32 v1{(in >> 10) & 0x3FF};
|
||||
PackUtil32 v2{(in >> 20) & 0x3FF};
|
||||
@ -74,10 +101,12 @@ void Pack32::Vec3UnpackUnitVecThirdBased(const uint32_t in, float* out)
|
||||
out[2] = (v2.f - 3.0f) * 8208.0312f;
|
||||
}
|
||||
|
||||
void Pack32::Vec4UnpackGfxColor(uint32_t in, float* out)
|
||||
void Vec4UnpackGfxColor(const uint32_t in, float (&out)[4])
|
||||
{
|
||||
out[0] = static_cast<float>(in & UINT8_MAX) / 255.0f;
|
||||
out[1] = static_cast<float>((in >> 8) & UINT8_MAX) / 255.0f;
|
||||
out[2] = static_cast<float>((in >> 16) & UINT8_MAX) / 255.0f;
|
||||
out[3] = static_cast<float>((in >> 24) & UINT8_MAX) / 255.0f;
|
||||
out[0] = static_cast<float>(in & std::numeric_limits<uint8_t>::max()) / 255.0f;
|
||||
out[1] = static_cast<float>((in >> 8) & std::numeric_limits<uint8_t>::max()) / 255.0f;
|
||||
out[2] = static_cast<float>((in >> 16) & std::numeric_limits<uint8_t>::max()) / 255.0f;
|
||||
out[3] = static_cast<float>((in >> 24) & std::numeric_limits<uint8_t>::max()) / 255.0f;
|
||||
}
|
||||
|
||||
} // namespace pack32
|
||||
|
@ -2,17 +2,17 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class Pack32
|
||||
namespace pack32
|
||||
{
|
||||
Pack32() = default;
|
||||
uint32_t Vec2PackTexCoordsUV(const float (&in)[2]);
|
||||
uint32_t Vec2PackTexCoordsVU(const float (&in)[2]);
|
||||
uint32_t Vec3PackUnitVecScaleBased(const float (&in)[3]);
|
||||
uint32_t Vec3PackUnitVecThirdBased(const float (&in)[3]);
|
||||
uint32_t Vec4PackGfxColor(const float (&in)[4]);
|
||||
|
||||
public:
|
||||
static uint32_t Vec2PackTexCoords(const float* in);
|
||||
static uint32_t Vec3PackUnitVec(const float* in);
|
||||
static uint32_t Vec4PackGfxColor(const float* in);
|
||||
static void Vec2UnpackTexCoordsUV(uint32_t in, float* out);
|
||||
static void Vec2UnpackTexCoordsVU(uint32_t in, float* out);
|
||||
static void Vec3UnpackUnitVecScaleBased(uint32_t in, float* out);
|
||||
static void Vec3UnpackUnitVecThirdBased(uint32_t in, float* out);
|
||||
static void Vec4UnpackGfxColor(uint32_t in, float* out);
|
||||
};
|
||||
void Vec2UnpackTexCoordsUV(uint32_t in, float (&out)[2]);
|
||||
void Vec2UnpackTexCoordsVU(uint32_t in, float (&out)[2]);
|
||||
void Vec3UnpackUnitVecScaleBased(uint32_t in, float (&out)[3]);
|
||||
void Vec3UnpackUnitVecThirdBased(uint32_t in, float (&out)[3]);
|
||||
void Vec4UnpackGfxColor(uint32_t in, float (&out)[4]);
|
||||
}; // namespace pack32
|
||||
|
@ -16,15 +16,16 @@ namespace T6
|
||||
{
|
||||
public:
|
||||
std::string file;
|
||||
float distance;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(JsonXModelLod, file);
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(JsonXModelLod, file, distance);
|
||||
|
||||
class JsonXModel
|
||||
{
|
||||
public:
|
||||
std::vector<JsonXModelLod> lods;
|
||||
unsigned collLod;
|
||||
std::optional<int> collLod;
|
||||
std::optional<std::string> physPreset;
|
||||
std::optional<std::string> physConstraints;
|
||||
unsigned flags;
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "Utils/FileUtils.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace gltf
|
||||
{
|
||||
@ -18,6 +19,7 @@ namespace gltf
|
||||
constexpr auto GLTF_JSON_CHUNK_DATA_OFFSET = 20u;
|
||||
|
||||
constexpr auto GLTF_DATA_URI_PREFIX = "data:application/octet-stream;base64,";
|
||||
constexpr auto URI_PREFIX_LENGTH = std::char_traits<char>::length(GLTF_DATA_URI_PREFIX);
|
||||
|
||||
constexpr auto GLTF_ATTRIBUTE_POSITION = "POSITION";
|
||||
constexpr auto GLTF_ATTRIBUTE_NORMAL = "NORMAL";
|
||||
|
@ -258,6 +258,7 @@ namespace gltf
|
||||
public:
|
||||
std::optional<unsigned> POSITION;
|
||||
std::optional<unsigned> NORMAL;
|
||||
std::optional<unsigned> COLOR_0;
|
||||
std::optional<unsigned> TEXCOORD_0;
|
||||
std::optional<unsigned> JOINTS_0;
|
||||
std::optional<unsigned> WEIGHTS_0;
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include "Utils/DistinctMapper.h"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@ -16,7 +17,7 @@ struct XModelQuaternion
|
||||
struct XModelBone
|
||||
{
|
||||
std::string name;
|
||||
int parentIndex;
|
||||
std::optional<unsigned> parentIndex;
|
||||
float scale[3];
|
||||
float globalOffset[3];
|
||||
float localOffset[3];
|
||||
@ -26,7 +27,7 @@ struct XModelBone
|
||||
|
||||
struct XModelBoneWeight
|
||||
{
|
||||
int boneIndex;
|
||||
unsigned boneIndex;
|
||||
float weight;
|
||||
};
|
||||
|
||||
@ -37,8 +38,8 @@ struct XModelVertexBoneWeightCollection
|
||||
|
||||
struct XModelVertexBoneWeights
|
||||
{
|
||||
const XModelBoneWeight* weights;
|
||||
size_t weightCount;
|
||||
unsigned weightOffset;
|
||||
unsigned weightCount;
|
||||
};
|
||||
|
||||
struct XModelVertex
|
||||
@ -51,7 +52,7 @@ struct XModelVertex
|
||||
|
||||
struct XModelFace
|
||||
{
|
||||
int vertexIndex[3];
|
||||
unsigned vertexIndex[3];
|
||||
};
|
||||
|
||||
struct XModelMaterial
|
||||
@ -97,7 +98,7 @@ struct XModelMaterial
|
||||
struct XModelObject
|
||||
{
|
||||
std::string name;
|
||||
int materialIndex;
|
||||
unsigned materialIndex;
|
||||
std::vector<XModelFace> m_faces;
|
||||
};
|
||||
|
||||
|
@ -18,6 +18,7 @@ function ObjLoading:link(links)
|
||||
links:linkto(minilzo)
|
||||
links:linkto(minizip)
|
||||
links:linkto(zlib)
|
||||
links:linkto(libtomcrypt)
|
||||
end
|
||||
|
||||
function ObjLoading:use()
|
||||
@ -55,6 +56,7 @@ function ObjLoading:project()
|
||||
minilzo:include(includes)
|
||||
minizip:include(includes)
|
||||
zlib:include(includes)
|
||||
json:include(includes)
|
||||
eigen:include(includes)
|
||||
json:include(includes)
|
||||
libtomcrypt:include(includes)
|
||||
end
|
||||
|
@ -38,7 +38,7 @@ bool AssetLoaderXModel::LoadFromRaw(
|
||||
if (LoadXModelAsJson(*file.m_stream, *xmodel, memory, manager, dependencies))
|
||||
manager->AddAsset<AssetXModel>(assetName, xmodel, std::move(dependencies));
|
||||
else
|
||||
std::cerr << "Failed to load xmodel \"" << assetName << "\"\n";
|
||||
std::cerr << std::format("Failed to load xmodel \"{}\"\n", assetName);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "AssetLoaders/AssetLoaderWeaponAttachment.h"
|
||||
#include "AssetLoaders/AssetLoaderWeaponAttachmentUnique.h"
|
||||
#include "AssetLoaders/AssetLoaderWeaponCamo.h"
|
||||
#include "AssetLoaders/AssetLoaderXModel.h"
|
||||
#include "AssetLoaders/AssetLoaderZBarrier.h"
|
||||
#include "AssetLoading/AssetLoadingManager.h"
|
||||
#include "Game/T6/CommonT6.h"
|
||||
@ -50,7 +51,7 @@ namespace T6
|
||||
REGISTER_ASSET_LOADER(AssetLoaderPhysConstraints)
|
||||
REGISTER_ASSET_LOADER(BasicAssetLoader<AssetDestructibleDef>)
|
||||
REGISTER_ASSET_LOADER(BasicAssetLoader<AssetXAnim>)
|
||||
REGISTER_ASSET_LOADER(BasicAssetLoader<AssetXModel>)
|
||||
REGISTER_ASSET_LOADER(AssetLoaderXModel)
|
||||
REGISTER_ASSET_LOADER(AssetLoaderMaterial)
|
||||
REGISTER_ASSET_LOADER(BasicAssetLoader<AssetTechniqueSet>)
|
||||
REGISTER_ASSET_LOADER(AssetLoaderGfxImage)
|
||||
|
@ -1,31 +1,209 @@
|
||||
#include "JsonXModelLoader.h"
|
||||
|
||||
#include "Csv/CsvStream.h"
|
||||
#include "Game/T6/CommonT6.h"
|
||||
#include "Game/T6/Json/JsonXModel.h"
|
||||
#include "ObjLoading.h"
|
||||
#include "Utils/QuatInt16.h"
|
||||
#include "Utils/StringUtils.h"
|
||||
#include "XModel/Gltf/GltfBinInput.h"
|
||||
#include "XModel/Gltf/GltfLoader.h"
|
||||
#include "XModel/Gltf/GltfTextInput.h"
|
||||
#include "XModel/XModelCommon.h"
|
||||
|
||||
#pragma warning(push, 0)
|
||||
#include <Eigen>
|
||||
#pragma warning(pop)
|
||||
|
||||
#include "XModel/Tangentspace.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
|
||||
using namespace nlohmann;
|
||||
using namespace T6;
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
const char* HITLOC_NAMES[]{
|
||||
// clang-format off
|
||||
"none",
|
||||
"helmet",
|
||||
"head",
|
||||
"neck",
|
||||
"torso_upper",
|
||||
"torso_middle",
|
||||
"torso_lower",
|
||||
"right_arm_upper",
|
||||
"left_arm_upper",
|
||||
"right_arm_lower",
|
||||
"left_arm_lower",
|
||||
"right_hand",
|
||||
"left_hand",
|
||||
"right_leg_upper",
|
||||
"left_leg_upper",
|
||||
"right_leg_lower",
|
||||
"left_leg_lower",
|
||||
"right_foot",
|
||||
"left_foot",
|
||||
"gun",
|
||||
"shield",
|
||||
// clang-format on
|
||||
};
|
||||
static_assert(std::extent_v<decltype(HITLOC_NAMES)> == HITLOC_COUNT);
|
||||
|
||||
class PartClassificationState final : public IZoneAssetLoaderState
|
||||
{
|
||||
// TODO: Use MP part classifications when building an mp fastfile
|
||||
static constexpr auto PART_CLASSIFICATION_FILE = "partclassification.csv";
|
||||
|
||||
public:
|
||||
PartClassificationState()
|
||||
: m_loaded(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool Load(const IAssetLoadingManager& manager)
|
||||
{
|
||||
if (m_loaded)
|
||||
return true;
|
||||
|
||||
if (ObjLoading::Configuration.Verbose)
|
||||
std::cout << "Loading part classification...\n";
|
||||
|
||||
const auto file = manager.GetAssetLoadingContext()->m_raw_search_path->Open(PART_CLASSIFICATION_FILE);
|
||||
if (!file.IsOpen())
|
||||
{
|
||||
std::cerr << std::format("Could not load part classification: Failed to open {}\n", PART_CLASSIFICATION_FILE);
|
||||
return false;
|
||||
}
|
||||
|
||||
CsvInputStream csvStream(*file.m_stream);
|
||||
std::vector<std::string> row;
|
||||
auto rowIndex = 0u;
|
||||
while (csvStream.NextRow(row))
|
||||
{
|
||||
if (!LoadRow(rowIndex++, row))
|
||||
return false;
|
||||
}
|
||||
|
||||
m_loaded = true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] unsigned GetPartClassificationForBoneName(const std::string& boneName) const
|
||||
{
|
||||
const auto entry = m_part_classifications.find(boneName);
|
||||
|
||||
return entry != m_part_classifications.end() ? entry->second : HITLOC_NONE;
|
||||
}
|
||||
|
||||
private:
|
||||
bool LoadRow(const unsigned rowIndex, std::vector<std::string>& row)
|
||||
{
|
||||
if (row.empty())
|
||||
return true;
|
||||
|
||||
if (row.size() != 2)
|
||||
{
|
||||
std::cerr << "Could not load part classification: Invalid row\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
utils::MakeStringLowerCase(row[0]);
|
||||
utils::MakeStringLowerCase(row[1]);
|
||||
|
||||
const auto foundHitLoc = std::ranges::find(HITLOC_NAMES, row[1]);
|
||||
if (foundHitLoc == std::end(HITLOC_NAMES))
|
||||
{
|
||||
std::cerr << std::format("Invalid hitloc name in row {}: {}\n", rowIndex + 1, row[1]);
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto hitLocNum = std::distance(std::begin(HITLOC_NAMES), foundHitLoc);
|
||||
|
||||
m_part_classifications.emplace(row[0], hitLocNum);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool m_loaded;
|
||||
std::unordered_map<std::string, unsigned> m_part_classifications;
|
||||
};
|
||||
|
||||
class TangentData
|
||||
{
|
||||
public:
|
||||
void CreateTangentData(const XModelCommon& common)
|
||||
{
|
||||
if (common.m_vertices.empty())
|
||||
return;
|
||||
|
||||
const auto vertexCount = common.m_vertices.size();
|
||||
m_tangents.resize(vertexCount);
|
||||
m_binormals.resize(vertexCount);
|
||||
|
||||
auto triCount = 0u;
|
||||
for (const auto& object : common.m_objects)
|
||||
triCount += object.m_faces.size();
|
||||
|
||||
std::vector<uint16_t> indices(triCount * 3u);
|
||||
auto triOffset = 0u;
|
||||
for (const auto& object : common.m_objects)
|
||||
{
|
||||
for (const auto& face : object.m_faces)
|
||||
{
|
||||
indices[triOffset++] = static_cast<uint16_t>(face.vertexIndex[0]);
|
||||
indices[triOffset++] = static_cast<uint16_t>(face.vertexIndex[1]);
|
||||
indices[triOffset++] = static_cast<uint16_t>(face.vertexIndex[2]);
|
||||
}
|
||||
}
|
||||
|
||||
const auto& firstVertex = common.m_vertices[0];
|
||||
|
||||
tangent_space::VertexData vertexData{
|
||||
firstVertex.coordinates,
|
||||
sizeof(XModelVertex),
|
||||
firstVertex.normal,
|
||||
sizeof(XModelVertex),
|
||||
firstVertex.uv,
|
||||
sizeof(XModelVertex),
|
||||
m_tangents.data(),
|
||||
sizeof(float) * 3,
|
||||
m_binormals.data(),
|
||||
sizeof(float) * 3,
|
||||
indices.data(),
|
||||
};
|
||||
|
||||
tangent_space::CalculateTangentSpace(vertexData, triCount, vertexCount);
|
||||
}
|
||||
|
||||
std::vector<std::array<float, 3>> m_tangents;
|
||||
std::vector<std::array<float, 3>> m_binormals;
|
||||
};
|
||||
|
||||
class JsonLoader
|
||||
{
|
||||
public:
|
||||
JsonLoader(std::istream& stream, MemoryManager& memory, IAssetLoadingManager& manager, std::set<XAssetInfoGeneric*>& dependencies)
|
||||
: m_stream(stream),
|
||||
m_memory(memory),
|
||||
m_script_strings(manager.GetAssetLoadingContext()->m_zone->m_script_strings),
|
||||
m_manager(manager),
|
||||
m_part_classification_state(*m_manager.GetAssetLoadingContext()->GetZoneAssetLoaderState<PartClassificationState>()),
|
||||
m_dependencies(dependencies)
|
||||
|
||||
{
|
||||
}
|
||||
|
||||
bool Load(XModel& xmodel) const
|
||||
bool Load(XModel& xmodel)
|
||||
{
|
||||
const auto jRoot = json::parse(m_stream);
|
||||
std::string type;
|
||||
@ -36,7 +214,7 @@ namespace
|
||||
|
||||
if (type != "xmodel" || version != 1u)
|
||||
{
|
||||
std::cerr << "Tried to load xmodel \"" << xmodel.name << "\" but did not find expected type material of version 1\n";
|
||||
std::cerr << std::format("Tried to load xmodel \"{}\" but did not find expected type material of version 1\n", xmodel.name);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -56,12 +234,604 @@ namespace
|
||||
private:
|
||||
static void PrintError(const XModel& xmodel, const std::string& message)
|
||||
{
|
||||
std::cerr << "Cannot load xmodel \"" << xmodel.name << "\": " << message << "\n";
|
||||
std::cerr << std::format("Cannot load xmodel \"{}\": {}\n", xmodel.name, message);
|
||||
}
|
||||
|
||||
bool CreateXModelFromJson(const JsonXModel& jXModel, XModel& xmodel) const
|
||||
static std::unique_ptr<XModelCommon> LoadModelByExtension(std::istream& stream, const std::string& extension)
|
||||
{
|
||||
xmodel.collLod = static_cast<uint16_t>(jXModel.collLod);
|
||||
if (extension == ".glb")
|
||||
{
|
||||
gltf::BinInput input;
|
||||
if (!input.ReadGltfData(stream))
|
||||
return nullptr;
|
||||
|
||||
const auto loader = gltf::Loader::CreateLoader(&input);
|
||||
return loader->Load();
|
||||
}
|
||||
|
||||
if (extension == ".gltf")
|
||||
{
|
||||
gltf::TextInput input;
|
||||
if (!input.ReadGltfData(stream))
|
||||
return nullptr;
|
||||
|
||||
const auto loader = gltf::Loader::CreateLoader(&input);
|
||||
return loader->Load();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void ApplyBasePose(DObjAnimMat& baseMat, const XModelBone& bone)
|
||||
{
|
||||
baseMat.trans.x = bone.globalOffset[0];
|
||||
baseMat.trans.y = bone.globalOffset[1];
|
||||
baseMat.trans.z = bone.globalOffset[2];
|
||||
baseMat.quat.x = bone.globalRotation.x;
|
||||
baseMat.quat.y = bone.globalRotation.y;
|
||||
baseMat.quat.z = bone.globalRotation.z;
|
||||
baseMat.quat.w = bone.globalRotation.w;
|
||||
|
||||
const auto quatNormSquared = Eigen::Quaternionf(baseMat.quat.w, baseMat.quat.x, baseMat.quat.y, baseMat.quat.z).squaredNorm();
|
||||
if (std::abs(quatNormSquared) < std::numeric_limits<float>::epsilon())
|
||||
{
|
||||
baseMat.quat.w = 1.0f;
|
||||
baseMat.transWeight = 2.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
baseMat.transWeight = 2.0f / quatNormSquared;
|
||||
}
|
||||
}
|
||||
|
||||
static void CalculateBoneBounds(XBoneInfo& info, const unsigned boneIndex, const XModelCommon& common)
|
||||
{
|
||||
if (common.m_bone_weight_data.weights.empty())
|
||||
return;
|
||||
|
||||
info.bounds[0].x = 0.0f;
|
||||
info.bounds[0].y = 0.0f;
|
||||
info.bounds[0].z = 0.0f;
|
||||
info.bounds[1].x = 0.0f;
|
||||
info.bounds[1].y = 0.0f;
|
||||
info.bounds[1].z = 0.0f;
|
||||
info.offset.x = 0.0f;
|
||||
info.offset.y = 0.0f;
|
||||
info.offset.z = 0.0f;
|
||||
info.radiusSquared = 0.0f;
|
||||
|
||||
const auto vertexCount = common.m_vertex_bone_weights.size();
|
||||
for (auto vertexIndex = 0u; vertexIndex < vertexCount; vertexIndex++)
|
||||
{
|
||||
const auto& vertex = common.m_vertices[vertexIndex];
|
||||
const auto& vertexWeights = common.m_vertex_bone_weights[vertexIndex];
|
||||
const auto* weights = &common.m_bone_weight_data.weights[vertexWeights.weightOffset];
|
||||
for (auto weightIndex = 0u; weightIndex < vertexWeights.weightCount; weightIndex++)
|
||||
{
|
||||
const auto& weight = weights[weightIndex];
|
||||
if (weight.boneIndex != boneIndex)
|
||||
continue;
|
||||
|
||||
info.bounds[0].x = std::min(info.bounds[0].x, vertex.coordinates[0]);
|
||||
info.bounds[0].y = std::min(info.bounds[0].y, vertex.coordinates[1]);
|
||||
info.bounds[0].z = std::min(info.bounds[0].z, vertex.coordinates[2]);
|
||||
info.bounds[1].x = std::max(info.bounds[1].x, vertex.coordinates[0]);
|
||||
info.bounds[1].y = std::max(info.bounds[1].y, vertex.coordinates[1]);
|
||||
info.bounds[1].z = std::max(info.bounds[1].z, vertex.coordinates[2]);
|
||||
}
|
||||
}
|
||||
|
||||
const Eigen::Vector3f minEigen(info.bounds[0].x, info.bounds[0].y, info.bounds[0].z);
|
||||
const Eigen::Vector3f maxEigen(info.bounds[1].x, info.bounds[1].y, info.bounds[1].z);
|
||||
const Eigen::Vector3f boundsCenter = (minEigen + maxEigen) * 0.5f;
|
||||
info.offset.x = boundsCenter.x();
|
||||
info.offset.y = boundsCenter.y();
|
||||
info.offset.z = boundsCenter.z();
|
||||
info.radiusSquared = Eigen::Vector3f(maxEigen - boundsCenter).squaredNorm();
|
||||
}
|
||||
|
||||
bool ApplyCommonBonesToXModel(const JsonXModelLod& jLod, XModel& xmodel, unsigned lodNumber, const XModelCommon& common) const
|
||||
{
|
||||
if (common.m_bones.empty())
|
||||
return true;
|
||||
|
||||
m_part_classification_state.Load(m_manager);
|
||||
|
||||
const auto boneCount = common.m_bones.size();
|
||||
constexpr auto maxBones = std::numeric_limits<decltype(XModel::numBones)>::max();
|
||||
if (boneCount > maxBones)
|
||||
{
|
||||
PrintError(xmodel, std::format("Model \"{}\" for lod {} contains too many bones ({} -> max={})", jLod.file, lodNumber, boneCount, maxBones));
|
||||
return false;
|
||||
}
|
||||
|
||||
xmodel.numRootBones = 0u;
|
||||
xmodel.numBones = 0u;
|
||||
for (const auto& bone : common.m_bones)
|
||||
{
|
||||
if (!bone.parentIndex)
|
||||
{
|
||||
// Make sure root bones are at the beginning
|
||||
assert(xmodel.numRootBones == xmodel.numBones);
|
||||
xmodel.numRootBones++;
|
||||
}
|
||||
|
||||
xmodel.numBones++;
|
||||
}
|
||||
|
||||
xmodel.boneNames = m_memory.Alloc<ScriptString>(xmodel.numBones);
|
||||
xmodel.partClassification = m_memory.Alloc<unsigned char>(xmodel.numBones);
|
||||
xmodel.baseMat = m_memory.Alloc<DObjAnimMat>(xmodel.numBones);
|
||||
xmodel.boneInfo = m_memory.Alloc<XBoneInfo>(xmodel.numBones);
|
||||
|
||||
if (xmodel.numBones > xmodel.numRootBones)
|
||||
{
|
||||
xmodel.parentList = m_memory.Alloc<unsigned char>(xmodel.numBones - xmodel.numRootBones);
|
||||
|
||||
// For some reason Treyarch games allocate for a vec4 here. it is treated as a vec3 though?
|
||||
xmodel.trans = m_memory.Alloc<float>((xmodel.numBones - xmodel.numRootBones) * 4u);
|
||||
xmodel.quats = m_memory.Alloc<XModelQuat>(xmodel.numBones - xmodel.numRootBones);
|
||||
}
|
||||
else
|
||||
{
|
||||
xmodel.parentList = nullptr;
|
||||
xmodel.trans = nullptr;
|
||||
xmodel.quats = nullptr;
|
||||
}
|
||||
|
||||
for (auto boneIndex = 0u; boneIndex < boneCount; boneIndex++)
|
||||
{
|
||||
const auto& bone = common.m_bones[boneIndex];
|
||||
xmodel.boneNames[boneIndex] = m_script_strings.AddOrGetScriptString(bone.name);
|
||||
xmodel.partClassification[boneIndex] = static_cast<unsigned char>(m_part_classification_state.GetPartClassificationForBoneName(bone.name));
|
||||
|
||||
ApplyBasePose(xmodel.baseMat[boneIndex], bone);
|
||||
CalculateBoneBounds(xmodel.boneInfo[boneIndex], boneIndex, common);
|
||||
|
||||
// Other boneInfo data is filled when calculating bone bounds
|
||||
xmodel.boneInfo[boneIndex].collmap = -1;
|
||||
|
||||
if (xmodel.numRootBones <= boneIndex)
|
||||
{
|
||||
const auto nonRootIndex = boneIndex - xmodel.numRootBones;
|
||||
const auto parentBoneIndex = static_cast<unsigned char>(bone.parentIndex.value_or(0u));
|
||||
assert(parentBoneIndex < boneIndex);
|
||||
|
||||
xmodel.parentList[nonRootIndex] = static_cast<unsigned char>(boneIndex - parentBoneIndex);
|
||||
|
||||
auto* trans = &xmodel.trans[nonRootIndex * 3];
|
||||
trans[0] = bone.localOffset[0];
|
||||
trans[1] = bone.localOffset[1];
|
||||
trans[2] = bone.localOffset[2];
|
||||
|
||||
auto& quats = xmodel.quats[nonRootIndex];
|
||||
quats.v[0] = QuatInt16::ToInt16(bone.localRotation.x);
|
||||
quats.v[1] = QuatInt16::ToInt16(bone.localRotation.y);
|
||||
quats.v[2] = QuatInt16::ToInt16(bone.localRotation.z);
|
||||
quats.v[3] = QuatInt16::ToInt16(bone.localRotation.w);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool VerifyBones(const JsonXModelLod& jLod, const XModel& xmodel, unsigned lodNumber, const XModelCommon& common) const
|
||||
{
|
||||
// This method currently only checks names
|
||||
// This does not necessarily verify correctness entirely.
|
||||
// It is most likely enough to catch accidental errors, however.
|
||||
|
||||
const auto commonBoneCount = common.m_bones.size();
|
||||
if (xmodel.numBones != commonBoneCount)
|
||||
{
|
||||
PrintError(xmodel,
|
||||
std::format(R"(Model "{}" for lod "{}" has different bone count compared to lod 0 ({} != {}))",
|
||||
jLod.file,
|
||||
lodNumber,
|
||||
xmodel.numBones,
|
||||
commonBoneCount));
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto boneIndex = 0u; boneIndex < commonBoneCount; boneIndex++)
|
||||
{
|
||||
const auto& commonBone = common.m_bones[boneIndex];
|
||||
|
||||
const auto& boneName = m_script_strings[xmodel.boneNames[boneIndex]];
|
||||
if (commonBone.name != boneName)
|
||||
{
|
||||
PrintError(xmodel,
|
||||
std::format(R"(Model "{}" for lod "{}" has different bone names compared to lod 0 (Index {}: {} != {}))",
|
||||
jLod.file,
|
||||
lodNumber,
|
||||
boneIndex,
|
||||
boneName,
|
||||
commonBone.name));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
CreateVertex(GfxPackedVertex& vertex, const XModelVertex& commonVertex, const std::array<float, 3>& tangent, const std::array<float, 3>& binormal)
|
||||
{
|
||||
float tangent_[]{tangent[0], tangent[1], tangent[2]};
|
||||
|
||||
vertex.xyz.x = commonVertex.coordinates[0];
|
||||
vertex.xyz.y = commonVertex.coordinates[1];
|
||||
vertex.xyz.z = commonVertex.coordinates[2];
|
||||
vertex.binormalSign = binormal[0] > 0.0f ? 1.0f : -1.0f;
|
||||
vertex.color = Common::Vec4PackGfxColor(commonVertex.color);
|
||||
vertex.texCoord = Common::Vec2PackTexCoords(commonVertex.uv);
|
||||
vertex.normal = Common::Vec3PackUnitVec(commonVertex.normal);
|
||||
vertex.tangent = Common::Vec3PackUnitVec(tangent_);
|
||||
}
|
||||
|
||||
static size_t GetRigidBoneForVertex(const size_t vertexIndex, const XModelCommon& common)
|
||||
{
|
||||
return common.m_bone_weight_data.weights[common.m_vertex_bone_weights[vertexIndex].weightOffset].boneIndex;
|
||||
}
|
||||
|
||||
static std::vector<std::optional<size_t>>
|
||||
GetRigidBoneIndicesForTris(const std::vector<size_t>& vertexIndices, XSurface& surface, const XModelCommon& common)
|
||||
{
|
||||
std::vector<std::optional<size_t>> rigidBoneIndexForTri;
|
||||
rigidBoneIndexForTri.reserve(surface.triCount);
|
||||
for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++)
|
||||
{
|
||||
const auto& tri = surface.triIndices[triIndex];
|
||||
const auto vert0Bone = GetRigidBoneForVertex(vertexIndices[tri.i[0]], common);
|
||||
const auto vert1Bone = GetRigidBoneForVertex(vertexIndices[tri.i[1]], common);
|
||||
const auto vert2Bone = GetRigidBoneForVertex(vertexIndices[tri.i[2]], common);
|
||||
|
||||
const auto hasSameBone = vert0Bone == vert1Bone && vert1Bone == vert2Bone;
|
||||
if (hasSameBone)
|
||||
rigidBoneIndexForTri.emplace_back(vert0Bone);
|
||||
else
|
||||
rigidBoneIndexForTri.emplace_back(std::nullopt);
|
||||
}
|
||||
|
||||
return rigidBoneIndexForTri;
|
||||
}
|
||||
|
||||
static void ReorderRigidTrisByBoneIndex(const std::vector<size_t>& vertexIndices, XSurface& surface, const XModelCommon& common)
|
||||
{
|
||||
const auto rigidBoneIndexForTri = GetRigidBoneIndicesForTris(vertexIndices, surface, common);
|
||||
|
||||
std::vector<size_t> triSortList(surface.triCount);
|
||||
std::iota(triSortList.begin(), triSortList.end(), 0);
|
||||
|
||||
std::ranges::sort(triSortList,
|
||||
[&rigidBoneIndexForTri](const size_t triIndex0, const size_t triIndex1)
|
||||
{
|
||||
const auto rigidBone0 = rigidBoneIndexForTri[triIndex0];
|
||||
const auto rigidBone1 = rigidBoneIndexForTri[triIndex1];
|
||||
|
||||
if (rigidBone0.has_value() != rigidBone1.has_value())
|
||||
return rigidBone0.has_value();
|
||||
if (!rigidBone0.has_value())
|
||||
return true;
|
||||
|
||||
return *rigidBone0 < *rigidBone1;
|
||||
});
|
||||
|
||||
std::vector<std::remove_pointer_t<decltype(XSurface::triIndices)>> sortedTris(surface.triCount);
|
||||
for (auto i = 0u; i < surface.triCount; i++)
|
||||
memcpy(&sortedTris[i], &surface.triIndices[triSortList[i]], sizeof(std::remove_pointer_t<decltype(XSurface::triIndices)>));
|
||||
memcpy(surface.triIndices, sortedTris.data(), sizeof(std::remove_pointer_t<decltype(XSurface::triIndices)>) * surface.triCount);
|
||||
}
|
||||
|
||||
static void AddBoneToXSurfacePartBits(XSurface& surface, const size_t boneIndex)
|
||||
{
|
||||
const auto partBitsIndex = boneIndex / 32u;
|
||||
const auto shiftValue = 31u - (boneIndex % 32u);
|
||||
surface.partBits[partBitsIndex] |= 1 << shiftValue;
|
||||
}
|
||||
|
||||
void CreateVertListData(XSurface& surface, const std::vector<size_t>& vertexIndices, const XModelCommon& common)
|
||||
{
|
||||
ReorderRigidTrisByBoneIndex(vertexIndices, surface, common);
|
||||
const auto rigidBoneIndexForTri = GetRigidBoneIndicesForTris(vertexIndices, surface, common);
|
||||
|
||||
std::vector<XRigidVertList> vertLists;
|
||||
|
||||
auto currentVertexTail = 0u;
|
||||
auto currentTriTail = 0u;
|
||||
|
||||
const auto vertexCount = vertexIndices.size();
|
||||
const auto triCount = static_cast<size_t>(surface.triCount);
|
||||
const auto boneCount = common.m_bones.size();
|
||||
for (auto boneIndex = 0u; boneIndex < boneCount; boneIndex++)
|
||||
{
|
||||
XRigidVertList boneVertList{};
|
||||
boneVertList.boneOffset = static_cast<uint16_t>(boneIndex * sizeof(DObjSkelMat));
|
||||
|
||||
auto currentVertexHead = currentVertexTail;
|
||||
while (currentVertexHead < vertexCount && GetRigidBoneForVertex(currentVertexHead, common) == boneIndex)
|
||||
currentVertexHead++;
|
||||
|
||||
auto currentTriHead = currentTriTail;
|
||||
while (currentTriHead < triCount && rigidBoneIndexForTri[currentTriHead] && *rigidBoneIndexForTri[currentTriHead] == boneIndex)
|
||||
currentTriHead++;
|
||||
|
||||
boneVertList.vertCount = static_cast<uint16_t>(currentVertexHead - currentVertexTail);
|
||||
boneVertList.triOffset = static_cast<uint16_t>(currentTriTail);
|
||||
boneVertList.triCount = static_cast<uint16_t>(currentTriHead - currentTriTail);
|
||||
|
||||
if (boneVertList.triCount > 0 || boneVertList.vertCount > 0)
|
||||
{
|
||||
boneVertList.collisionTree = nullptr; // TODO
|
||||
vertLists.emplace_back(boneVertList);
|
||||
|
||||
currentVertexTail = currentVertexHead;
|
||||
currentTriTail = currentTriHead;
|
||||
|
||||
AddBoneToXSurfacePartBits(surface, boneIndex);
|
||||
}
|
||||
}
|
||||
|
||||
if (!vertLists.empty())
|
||||
{
|
||||
surface.vertListCount = static_cast<unsigned char>(vertLists.size());
|
||||
surface.vertList = m_memory.Alloc<XRigidVertList>(surface.vertListCount);
|
||||
|
||||
memcpy(surface.vertList, vertLists.data(), sizeof(XRigidVertList) * surface.vertListCount);
|
||||
}
|
||||
}
|
||||
|
||||
void CreateVertsBlendData(XSurface& surface, const std::vector<size_t>& vertexIndices, const XModelCommon& common)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
static void ReorderVerticesByWeightCount(std::vector<size_t>& vertexIndices, XSurface& surface, const XModelCommon& common)
|
||||
{
|
||||
if (common.m_bone_weight_data.weights.empty())
|
||||
return;
|
||||
|
||||
const auto vertexCount = vertexIndices.size();
|
||||
std::vector<size_t> reorderLookup(vertexCount);
|
||||
std::iota(reorderLookup.begin(), reorderLookup.end(), 0);
|
||||
|
||||
std::ranges::sort(reorderLookup,
|
||||
[&common, &vertexIndices](const size_t& i0, const size_t& i1)
|
||||
{
|
||||
const auto& weights0 = common.m_vertex_bone_weights[vertexIndices[i0]];
|
||||
const auto& weights1 = common.m_vertex_bone_weights[vertexIndices[i1]];
|
||||
|
||||
if (weights0.weightCount < weights1.weightCount)
|
||||
return true;
|
||||
|
||||
// If there is only one weight, make sure all vertices of the same bone follow another
|
||||
if (weights0.weightCount == 1)
|
||||
{
|
||||
const auto bone0 = common.m_bone_weight_data.weights[weights0.weightOffset].boneIndex;
|
||||
const auto bone1 = common.m_bone_weight_data.weights[weights1.weightOffset].boneIndex;
|
||||
return bone0 < bone1;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++)
|
||||
{
|
||||
auto& triIndices = surface.triIndices[triIndex];
|
||||
|
||||
triIndices.i[0] = static_cast<uint16_t>(reorderLookup[triIndices.i[0]]);
|
||||
triIndices.i[1] = static_cast<uint16_t>(reorderLookup[triIndices.i[1]]);
|
||||
triIndices.i[2] = static_cast<uint16_t>(reorderLookup[triIndices.i[2]]);
|
||||
}
|
||||
|
||||
for (auto& entry : reorderLookup)
|
||||
entry = vertexIndices[entry];
|
||||
|
||||
vertexIndices = std::move(reorderLookup);
|
||||
}
|
||||
|
||||
bool CreateXSurface(
|
||||
XSurface& surface, const XModelObject& commonObject, const XModelCommon& common, const TangentData& tangentData, unsigned& vertexOffset)
|
||||
{
|
||||
std::vector<size_t> xmodelToCommonVertexIndexLookup;
|
||||
std::unordered_map<size_t, size_t> usedVertices;
|
||||
|
||||
surface.triCount = static_cast<uint16_t>(commonObject.m_faces.size());
|
||||
surface.triIndices = m_memory.Alloc<XSurfaceTri>(surface.triCount);
|
||||
|
||||
for (auto faceIndex = 0u; faceIndex < surface.triCount; faceIndex++)
|
||||
{
|
||||
const auto& face = commonObject.m_faces[faceIndex];
|
||||
auto& tris = surface.triIndices[faceIndex];
|
||||
|
||||
for (auto triVertIndex = 0u; triVertIndex < std::extent_v<decltype(XModelFace::vertexIndex)>; triVertIndex++)
|
||||
{
|
||||
const auto commonVertexIndex = face.vertexIndex[triVertIndex];
|
||||
const auto existingVertex = usedVertices.find(commonVertexIndex);
|
||||
if (existingVertex == usedVertices.end())
|
||||
{
|
||||
const auto xmodelVertexIndex = xmodelToCommonVertexIndexLookup.size();
|
||||
tris.i[triVertIndex] = static_cast<uint16_t>(xmodelVertexIndex);
|
||||
|
||||
xmodelToCommonVertexIndexLookup.emplace_back(commonVertexIndex);
|
||||
usedVertices.emplace(commonVertexIndex, xmodelVertexIndex);
|
||||
}
|
||||
else
|
||||
tris.i[triVertIndex] = static_cast<uint16_t>(existingVertex->second);
|
||||
}
|
||||
}
|
||||
|
||||
ReorderVerticesByWeightCount(xmodelToCommonVertexIndexLookup, surface, common);
|
||||
|
||||
surface.baseVertIndex = static_cast<uint16_t>(vertexOffset);
|
||||
surface.vertCount = static_cast<uint16_t>(xmodelToCommonVertexIndexLookup.size());
|
||||
surface.verts0 = m_memory.Alloc<GfxPackedVertex>(surface.vertCount);
|
||||
vertexOffset += surface.vertCount;
|
||||
|
||||
for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++)
|
||||
{
|
||||
const auto commonVertexIndex = xmodelToCommonVertexIndexLookup[vertexIndex];
|
||||
const auto& commonVertex = common.m_vertices[commonVertexIndex];
|
||||
CreateVertex(surface.verts0[vertexIndex], commonVertex, tangentData.m_binormals[commonVertexIndex], tangentData.m_binormals[commonVertexIndex]);
|
||||
}
|
||||
|
||||
if (!common.m_bone_weight_data.weights.empty())
|
||||
{
|
||||
// Since bone weights are sorted by weight count, the last must have the highest weight count
|
||||
const auto hasVertsBlend =
|
||||
common.m_vertex_bone_weights[xmodelToCommonVertexIndexLookup[xmodelToCommonVertexIndexLookup.size() - 1]].weightCount > 1;
|
||||
if (!hasVertsBlend)
|
||||
CreateVertListData(surface, xmodelToCommonVertexIndexLookup, common);
|
||||
else
|
||||
{
|
||||
CreateVertsBlendData(surface, xmodelToCommonVertexIndexLookup, common);
|
||||
|
||||
std::cerr << "Only rigid models are supported at the moment\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadLod(const JsonXModelLod& jLod, XModel& xmodel, unsigned lodNumber)
|
||||
{
|
||||
const auto file = m_manager.GetAssetLoadingContext()->m_raw_search_path->Open(jLod.file);
|
||||
if (!file.IsOpen())
|
||||
{
|
||||
PrintError(xmodel, std::format("Failed to open file for lod {}: \"{}\"", lodNumber, jLod.file));
|
||||
return false;
|
||||
}
|
||||
|
||||
auto extension = fs::path(jLod.file).extension().string();
|
||||
utils::MakeStringLowerCase(extension);
|
||||
|
||||
const auto common = LoadModelByExtension(*file.m_stream, extension);
|
||||
if (!common)
|
||||
{
|
||||
PrintError(xmodel, std::format("Failure while trying to load model for lod {}: \"{}\"", lodNumber, jLod.file));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lodNumber == 0u)
|
||||
{
|
||||
if (!ApplyCommonBonesToXModel(jLod, xmodel, lodNumber, *common))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!VerifyBones(jLod, xmodel, lodNumber, *common))
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& lodInfo = xmodel.lodInfo[lodNumber];
|
||||
lodInfo.dist = jLod.distance;
|
||||
lodInfo.surfIndex = static_cast<uint16_t>(m_surfaces.size());
|
||||
lodInfo.numsurfs = static_cast<uint16_t>(common->m_objects.size());
|
||||
|
||||
std::vector<Material*> materialAssets;
|
||||
materialAssets.reserve(common->m_materials.size());
|
||||
for (const auto& commonMaterial : common->m_materials)
|
||||
{
|
||||
auto* assetInfo = m_manager.LoadDependency<AssetMaterial>(commonMaterial.name);
|
||||
if (!assetInfo)
|
||||
return false;
|
||||
|
||||
m_dependencies.emplace(assetInfo);
|
||||
materialAssets.push_back(assetInfo->Asset());
|
||||
}
|
||||
|
||||
auto vertexOffset = 0u;
|
||||
TangentData tangentData;
|
||||
tangentData.CreateTangentData(*common);
|
||||
const auto surfaceCreationSuccessful =
|
||||
std::ranges::all_of(common->m_objects,
|
||||
[this, &common, &materialAssets, &tangentData, &vertexOffset](const XModelObject& commonObject)
|
||||
{
|
||||
XSurface surface{};
|
||||
if (!CreateXSurface(surface, commonObject, *common, tangentData, vertexOffset))
|
||||
return false;
|
||||
|
||||
m_surfaces.emplace_back(surface);
|
||||
m_materials.push_back(materialAssets[commonObject.materialIndex]);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!surfaceCreationSuccessful)
|
||||
return false;
|
||||
|
||||
// Lod part bits are the sum of part bits of all of its surfaces
|
||||
static_assert(std::extent_v<decltype(XModelLodInfo::partBits)> == std::extent_v<decltype(XSurface::partBits)>);
|
||||
for (auto surfaceOffset = 0u; surfaceOffset < lodInfo.numsurfs; surfaceOffset++)
|
||||
{
|
||||
const auto& surface = m_surfaces[lodInfo.surfIndex + surfaceOffset];
|
||||
for (auto i = 0u; i < std::extent_v<decltype(XModelLodInfo::partBits)>; i++)
|
||||
lodInfo.partBits[i] |= surface.partBits[i];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void CalculateModelBounds(XModel& xmodel)
|
||||
{
|
||||
if (!xmodel.surfs)
|
||||
return;
|
||||
|
||||
for (auto surfaceIndex = 0u; surfaceIndex < xmodel.lodInfo[0].numsurfs; surfaceIndex++)
|
||||
{
|
||||
const auto& surface = xmodel.surfs[surfaceIndex + xmodel.lodInfo[0].surfIndex];
|
||||
|
||||
if (!surface.verts0)
|
||||
continue;
|
||||
|
||||
for (auto vertIndex = 0u; vertIndex < surface.vertCount; vertIndex++)
|
||||
{
|
||||
const auto& vertex = surface.verts0[vertIndex];
|
||||
|
||||
xmodel.mins.x = std::min(xmodel.mins.x, vertex.xyz.v[0]);
|
||||
xmodel.mins.y = std::min(xmodel.mins.y, vertex.xyz.v[1]);
|
||||
xmodel.mins.z = std::min(xmodel.mins.z, vertex.xyz.v[2]);
|
||||
xmodel.maxs.x = std::max(xmodel.maxs.x, vertex.xyz.v[0]);
|
||||
xmodel.maxs.y = std::max(xmodel.maxs.y, vertex.xyz.v[1]);
|
||||
xmodel.maxs.z = std::max(xmodel.maxs.z, vertex.xyz.v[2]);
|
||||
}
|
||||
}
|
||||
|
||||
const auto maxX = std::max(std::abs(xmodel.mins.x), std::abs(xmodel.maxs.x));
|
||||
const auto maxY = std::max(std::abs(xmodel.mins.y), std::abs(xmodel.maxs.y));
|
||||
const auto maxZ = std::max(std::abs(xmodel.mins.z), std::abs(xmodel.maxs.z));
|
||||
xmodel.radius = Eigen::Vector3f(maxX, maxY, maxZ).norm();
|
||||
}
|
||||
|
||||
bool CreateXModelFromJson(const JsonXModel& jXModel, XModel& xmodel)
|
||||
{
|
||||
auto lodNumber = 0u;
|
||||
for (const auto& jLod : jXModel.lods)
|
||||
{
|
||||
if (!LoadLod(jLod, xmodel, lodNumber++))
|
||||
return false;
|
||||
}
|
||||
xmodel.numLods = static_cast<uint16_t>(jXModel.lods.size());
|
||||
|
||||
xmodel.numsurfs = static_cast<unsigned char>(m_surfaces.size());
|
||||
xmodel.surfs = m_memory.Alloc<XSurface>(xmodel.numsurfs);
|
||||
xmodel.materialHandles = m_memory.Alloc<Material*>(xmodel.numsurfs);
|
||||
memcpy(xmodel.surfs, m_surfaces.data(), sizeof(XSurface) * xmodel.numsurfs);
|
||||
memcpy(xmodel.materialHandles, m_materials.data(), sizeof(Material*) * xmodel.numsurfs);
|
||||
|
||||
CalculateModelBounds(xmodel);
|
||||
|
||||
if (jXModel.collLod && jXModel.collLod.value() >= 0)
|
||||
{
|
||||
if (static_cast<unsigned>(jXModel.collLod.value()) >= jXModel.lods.size())
|
||||
{
|
||||
PrintError(xmodel, "Collision lod is not a valid lod");
|
||||
return false;
|
||||
}
|
||||
xmodel.collLod = static_cast<int16_t>(jXModel.collLod.value());
|
||||
}
|
||||
else
|
||||
xmodel.collLod = -1;
|
||||
|
||||
if (jXModel.physPreset)
|
||||
{
|
||||
@ -104,9 +874,14 @@ namespace
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<XSurface> m_surfaces;
|
||||
std::vector<Material*> m_materials;
|
||||
|
||||
std::istream& m_stream;
|
||||
MemoryManager& m_memory;
|
||||
ZoneScriptStrings& m_script_strings;
|
||||
IAssetLoadingManager& m_manager;
|
||||
PartClassificationState& m_part_classification_state;
|
||||
std::set<XAssetInfoGeneric*>& m_dependencies;
|
||||
};
|
||||
} // namespace
|
||||
@ -117,7 +892,7 @@ namespace T6
|
||||
std::istream& stream, XModel& xmodel, MemoryManager* memory, IAssetLoadingManager* manager, std::vector<XAssetInfoGeneric*>& dependencies)
|
||||
{
|
||||
std::set<XAssetInfoGeneric*> dependenciesSet;
|
||||
const JsonLoader loader(stream, *memory, *manager, dependenciesSet);
|
||||
JsonLoader loader(stream, *memory, *manager, dependenciesSet);
|
||||
|
||||
dependencies.assign(dependenciesSet.cbegin(), dependenciesSet.cend());
|
||||
|
||||
|
129
src/ObjLoading/XModel/Gltf/GltfBinInput.cpp
Normal file
129
src/ObjLoading/XModel/Gltf/GltfBinInput.cpp
Normal file
@ -0,0 +1,129 @@
|
||||
#include "GltfBinInput.h"
|
||||
|
||||
#include "XModel/Gltf/GltfConstants.h"
|
||||
|
||||
#include <exception>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using namespace gltf;
|
||||
|
||||
BinInput::BinInput()
|
||||
: m_buffer_size(0u)
|
||||
{
|
||||
}
|
||||
|
||||
bool BinInput::GetEmbeddedBuffer(const void*& buffer, size_t& bufferSize) const
|
||||
{
|
||||
if (!m_buffer || !m_buffer_size)
|
||||
return false;
|
||||
|
||||
buffer = m_buffer.get();
|
||||
bufferSize = m_buffer_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const nlohmann::json& BinInput::GetJson() const
|
||||
{
|
||||
if (!m_json)
|
||||
throw std::exception();
|
||||
|
||||
return *m_json;
|
||||
}
|
||||
|
||||
bool BinInput::ReadGltfData(std::istream& stream)
|
||||
{
|
||||
uint32_t magic;
|
||||
if (!Read(stream, &magic, sizeof(magic), "magic"))
|
||||
return false;
|
||||
|
||||
if (magic != GLTF_MAGIC)
|
||||
{
|
||||
std::cerr << "Invalid magic when trying to read GLB\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t version;
|
||||
if (!Read(stream, &version, sizeof(version), "version"))
|
||||
return false;
|
||||
|
||||
if (version != GLTF_VERSION)
|
||||
{
|
||||
std::cerr << std::format("Unsupported version {} when trying to read GLB: Expected version {}\n", version, GLTF_VERSION);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t fileLength;
|
||||
if (!Read(stream, &fileLength, sizeof(fileLength), "file length"))
|
||||
return false;
|
||||
|
||||
while (true)
|
||||
{
|
||||
uint32_t chunkLength;
|
||||
uint32_t chunkMagic;
|
||||
if (!Read(stream, &chunkLength, sizeof(chunkLength), "chunk length", false))
|
||||
break;
|
||||
|
||||
if (!Read(stream, &chunkMagic, sizeof(chunkMagic), "chunk magic"))
|
||||
return false;
|
||||
|
||||
if (chunkMagic == CHUNK_MAGIC_JSON)
|
||||
{
|
||||
const auto jsonBuffer = std::make_unique<uint8_t[]>(chunkLength + 1);
|
||||
if (!Read(stream, jsonBuffer.get(), chunkLength, "json"))
|
||||
return false;
|
||||
jsonBuffer[chunkLength] = 0u;
|
||||
|
||||
try
|
||||
{
|
||||
m_json = std::make_unique<nlohmann::json>(nlohmann::json::parse(jsonBuffer.get(), &jsonBuffer[chunkLength]));
|
||||
}
|
||||
catch (const nlohmann::json::exception& e)
|
||||
{
|
||||
std::cerr << std::format("Failed trying to parse JSON of GLB: {}\n", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (chunkMagic == CHUNK_MAGIC_BIN)
|
||||
{
|
||||
m_buffer = std::make_unique<uint8_t[]>(chunkLength);
|
||||
m_buffer_size = chunkLength;
|
||||
|
||||
if (!Read(stream, m_buffer.get(), m_buffer_size, "bin buffer"))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
Skip(stream, chunkLength);
|
||||
|
||||
if (chunkLength % 4u > 0)
|
||||
Skip(stream, 4u - (chunkLength % 4u));
|
||||
}
|
||||
|
||||
if (!m_json)
|
||||
{
|
||||
std::cerr << "Failed to load GLB due to missing JSON\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinInput::Read(std::istream& stream, void* dest, const size_t dataSize, const char* readTypeName, const bool errorWhenFailed)
|
||||
{
|
||||
stream.read(static_cast<char*>(dest), dataSize);
|
||||
if (stream.gcount() != dataSize)
|
||||
{
|
||||
if (errorWhenFailed)
|
||||
std::cerr << std::format("Unexpected EOF while reading GLB {}\n", readTypeName);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BinInput::Skip(std::istream& stream, const size_t skipLength)
|
||||
{
|
||||
stream.seekg(skipLength, std::ios::cur);
|
||||
}
|
26
src/ObjLoading/XModel/Gltf/GltfBinInput.h
Normal file
26
src/ObjLoading/XModel/Gltf/GltfBinInput.h
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "GltfInput.h"
|
||||
|
||||
#include <istream>
|
||||
|
||||
namespace gltf
|
||||
{
|
||||
class BinInput final : public Input
|
||||
{
|
||||
public:
|
||||
BinInput();
|
||||
|
||||
bool ReadGltfData(std::istream& stream) override;
|
||||
bool GetEmbeddedBuffer(const void*& buffer, size_t& bufferSize) const override;
|
||||
[[nodiscard]] const nlohmann::json& GetJson() const override;
|
||||
|
||||
private:
|
||||
static bool Read(std::istream& stream, void* dest, size_t dataSize, const char* readTypeName, bool errorWhenFailed = true);
|
||||
static void Skip(std::istream& stream, size_t skipLength);
|
||||
|
||||
std::unique_ptr<nlohmann::json> m_json;
|
||||
std::unique_ptr<uint8_t[]> m_buffer;
|
||||
size_t m_buffer_size;
|
||||
};
|
||||
} // namespace gltf
|
24
src/ObjLoading/XModel/Gltf/GltfInput.h
Normal file
24
src/ObjLoading/XModel/Gltf/GltfInput.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
|
||||
namespace gltf
|
||||
{
|
||||
class Input
|
||||
{
|
||||
protected:
|
||||
Input() = default;
|
||||
|
||||
public:
|
||||
virtual ~Input() = default;
|
||||
Input(const Input& other) = default;
|
||||
Input(Input&& other) noexcept = default;
|
||||
Input& operator=(const Input& other) = default;
|
||||
Input& operator=(Input&& other) noexcept = default;
|
||||
|
||||
virtual bool ReadGltfData(std::istream& stream) = 0;
|
||||
virtual bool GetEmbeddedBuffer(const void*& buffer, size_t& bufferSize) const = 0;
|
||||
[[nodiscard]] virtual const nlohmann::json& GetJson() const = 0;
|
||||
};
|
||||
} // namespace gltf
|
705
src/ObjLoading/XModel/Gltf/GltfLoader.cpp
Normal file
705
src/ObjLoading/XModel/Gltf/GltfLoader.cpp
Normal file
@ -0,0 +1,705 @@
|
||||
#include "GltfLoader.h"
|
||||
|
||||
#include "Internal/GltfAccessor.h"
|
||||
#include "Internal/GltfBuffer.h"
|
||||
#include "Internal/GltfBufferView.h"
|
||||
|
||||
#pragma warning(push, 0)
|
||||
#include <Eigen>
|
||||
#pragma warning(pop)
|
||||
|
||||
#include <deque>
|
||||
#include <exception>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <string>
|
||||
|
||||
using namespace gltf;
|
||||
|
||||
namespace
|
||||
{
|
||||
struct AccessorsForVertex
|
||||
{
|
||||
unsigned positionAccessor;
|
||||
std::optional<unsigned> normalAccessor;
|
||||
std::optional<unsigned> colorAccessor;
|
||||
std::optional<unsigned> uvAccessor;
|
||||
std::optional<unsigned> jointsAccessor;
|
||||
std::optional<unsigned> weightsAccessor;
|
||||
|
||||
friend bool operator==(const AccessorsForVertex& lhs, const AccessorsForVertex& rhs)
|
||||
{
|
||||
return lhs.positionAccessor == rhs.positionAccessor && lhs.normalAccessor == rhs.normalAccessor && lhs.colorAccessor == rhs.colorAccessor
|
||||
&& lhs.uvAccessor == rhs.uvAccessor && lhs.jointsAccessor == rhs.jointsAccessor && lhs.weightsAccessor == rhs.weightsAccessor;
|
||||
}
|
||||
|
||||
friend bool operator!=(const AccessorsForVertex& lhs, const AccessorsForVertex& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
template<> struct std::hash<AccessorsForVertex>
|
||||
{
|
||||
std::size_t operator()(const AccessorsForVertex& v) const noexcept
|
||||
{
|
||||
std::size_t seed = 0x7E42C0E6;
|
||||
seed ^= (seed << 6) + (seed >> 2) + 0x47B15429 + static_cast<std::size_t>(v.positionAccessor);
|
||||
seed ^= (seed << 6) + (seed >> 2) + 0x66847B5C + std::hash<std::optional<unsigned>>()(v.normalAccessor);
|
||||
seed ^= (seed << 6) + (seed >> 2) + 0x77399D60 + std::hash<std::optional<unsigned>>()(v.colorAccessor);
|
||||
seed ^= (seed << 6) + (seed >> 2) + 0x477AF9AB + std::hash<std::optional<unsigned>>()(v.uvAccessor);
|
||||
seed ^= (seed << 6) + (seed >> 2) + 0x4421B4D9 + std::hash<std::optional<unsigned>>()(v.jointsAccessor);
|
||||
seed ^= (seed << 6) + (seed >> 2) + 0x13C2EBA1 + std::hash<std::optional<unsigned>>()(v.weightsAccessor);
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
class GltfLoadException final : std::exception
|
||||
{
|
||||
public:
|
||||
explicit GltfLoadException(std::string message)
|
||||
: m_message(std::move(message))
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string& Str() const
|
||||
{
|
||||
return m_message;
|
||||
}
|
||||
|
||||
[[nodiscard]] const char* what() const noexcept override
|
||||
{
|
||||
return m_message.c_str();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_message;
|
||||
};
|
||||
|
||||
struct ObjectToLoad
|
||||
{
|
||||
unsigned meshIndex;
|
||||
std::optional<unsigned> skinIndex;
|
||||
|
||||
ObjectToLoad(const unsigned meshIndex, const std::optional<unsigned> skinIndex)
|
||||
: meshIndex(meshIndex),
|
||||
skinIndex(skinIndex)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class GltfLoaderImpl final : public Loader
|
||||
{
|
||||
public:
|
||||
explicit GltfLoaderImpl(const Input* input)
|
||||
: m_input(input)
|
||||
{
|
||||
}
|
||||
|
||||
static std::vector<unsigned> GetRootNodes(const JsonRoot& jRoot)
|
||||
{
|
||||
if (!jRoot.nodes || jRoot.nodes->empty())
|
||||
return {};
|
||||
|
||||
const auto nodeCount = jRoot.nodes->size();
|
||||
std::vector<unsigned> rootNodes;
|
||||
std::vector<bool> isChild(nodeCount);
|
||||
|
||||
for (const auto& node : jRoot.nodes.value())
|
||||
{
|
||||
if (!node.children)
|
||||
continue;
|
||||
|
||||
for (const auto childIndex : node.children.value())
|
||||
{
|
||||
if (childIndex >= nodeCount)
|
||||
throw GltfLoadException("Illegal child index");
|
||||
|
||||
if (isChild[childIndex])
|
||||
throw GltfLoadException("Node hierarchy is not a set of disjoint strict trees");
|
||||
|
||||
isChild[childIndex] = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto nodeIndex = 0u; nodeIndex < nodeCount; nodeIndex++)
|
||||
{
|
||||
if (!isChild[nodeIndex])
|
||||
rootNodes.emplace_back(nodeIndex);
|
||||
}
|
||||
|
||||
return rootNodes;
|
||||
}
|
||||
|
||||
void TraverseNodes(const JsonRoot& jRoot)
|
||||
{
|
||||
// Make sure there are any nodes to traverse
|
||||
if (!jRoot.nodes || jRoot.nodes->empty())
|
||||
return;
|
||||
|
||||
std::deque<unsigned> nodeQueue;
|
||||
std::vector<unsigned> rootNodes = GetRootNodes(jRoot);
|
||||
|
||||
for (const auto rootNode : rootNodes)
|
||||
nodeQueue.emplace_back(rootNode);
|
||||
|
||||
while (!nodeQueue.empty())
|
||||
{
|
||||
const auto& node = jRoot.nodes.value()[nodeQueue.front()];
|
||||
nodeQueue.pop_front();
|
||||
|
||||
if (node.children)
|
||||
{
|
||||
for (const auto childIndex : *node.children)
|
||||
nodeQueue.emplace_back(childIndex);
|
||||
}
|
||||
|
||||
if (node.mesh)
|
||||
m_load_objects.emplace_back(*node.mesh, node.skin);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<Accessor*> GetAccessorForIndex(const char* attributeName,
|
||||
const std::optional<unsigned> index,
|
||||
std::initializer_list<JsonAccessorType> allowedAccessorTypes,
|
||||
std::initializer_list<JsonAccessorComponentType> allowedAccessorComponentTypes) const
|
||||
{
|
||||
if (!index)
|
||||
return std::nullopt;
|
||||
|
||||
if (*index > m_accessors.size())
|
||||
throw GltfLoadException(std::format("Index for {} accessor out of bounds", attributeName));
|
||||
|
||||
auto* accessor = m_accessors[*index].get();
|
||||
|
||||
const auto maybeType = accessor->GetType();
|
||||
if (maybeType)
|
||||
{
|
||||
if (std::ranges::find(allowedAccessorTypes, *maybeType) == allowedAccessorTypes.end())
|
||||
throw GltfLoadException(std::format("Accessor for {} has unsupported type {}", attributeName, static_cast<unsigned>(*maybeType)));
|
||||
}
|
||||
|
||||
const auto maybeComponentType = accessor->GetComponentType();
|
||||
if (maybeComponentType)
|
||||
{
|
||||
if (std::ranges::find(allowedAccessorComponentTypes, *maybeComponentType) == allowedAccessorComponentTypes.end())
|
||||
throw GltfLoadException(
|
||||
std::format("Accessor for {} has unsupported component type {}", attributeName, static_cast<unsigned>(*maybeComponentType)));
|
||||
}
|
||||
|
||||
return accessor;
|
||||
}
|
||||
|
||||
static void VerifyAccessorVertexCount(const char* accessorType, const Accessor* accessor, const size_t vertexCount)
|
||||
{
|
||||
if (accessor->GetCount() != vertexCount)
|
||||
throw GltfLoadException(std::format("Element count of {} accessor does not match expected vertex count of {}", accessorType, vertexCount));
|
||||
}
|
||||
|
||||
unsigned CreateVertices(XModelCommon& common, const AccessorsForVertex& accessorsForVertex)
|
||||
{
|
||||
// clang-format off
|
||||
auto* positionAccessor = GetAccessorForIndex(
|
||||
"POSITION",
|
||||
accessorsForVertex.positionAccessor,
|
||||
{JsonAccessorType::VEC3},
|
||||
{JsonAccessorComponentType::FLOAT}
|
||||
).value_or(nullptr);
|
||||
// clang-format on
|
||||
|
||||
const auto vertexCount = positionAccessor->GetCount();
|
||||
NullAccessor nullAccessor(vertexCount);
|
||||
OnesAccessor onesAccessor(vertexCount);
|
||||
|
||||
// clang-format off
|
||||
auto* normalAccessor = GetAccessorForIndex(
|
||||
"NORMAL",
|
||||
accessorsForVertex.normalAccessor,
|
||||
{JsonAccessorType::VEC3},
|
||||
{JsonAccessorComponentType::FLOAT}
|
||||
).value_or(&nullAccessor);
|
||||
VerifyAccessorVertexCount("NORMAL", normalAccessor, vertexCount);
|
||||
|
||||
auto* uvAccessor = GetAccessorForIndex(
|
||||
"TEXCOORD_0",
|
||||
accessorsForVertex.uvAccessor,
|
||||
{JsonAccessorType::VEC2},
|
||||
{JsonAccessorComponentType::FLOAT, JsonAccessorComponentType::UNSIGNED_BYTE, JsonAccessorComponentType::UNSIGNED_SHORT}
|
||||
).value_or(&nullAccessor);
|
||||
VerifyAccessorVertexCount("TEXCOORD_0", uvAccessor, vertexCount);
|
||||
|
||||
auto* colorAccessor = GetAccessorForIndex(
|
||||
"COLOR_0",
|
||||
accessorsForVertex.colorAccessor,
|
||||
{JsonAccessorType::VEC3, JsonAccessorType::VEC4},
|
||||
{JsonAccessorComponentType::FLOAT, JsonAccessorComponentType::UNSIGNED_BYTE, JsonAccessorComponentType::UNSIGNED_SHORT}
|
||||
).value_or(&onesAccessor);
|
||||
VerifyAccessorVertexCount("COLOR_0", colorAccessor, vertexCount);
|
||||
|
||||
auto* jointsAccessor = GetAccessorForIndex(
|
||||
"JOINTS_0",
|
||||
accessorsForVertex.jointsAccessor,
|
||||
{JsonAccessorType::VEC4},
|
||||
{JsonAccessorComponentType::UNSIGNED_BYTE, JsonAccessorComponentType::UNSIGNED_SHORT}
|
||||
).value_or(&nullAccessor);
|
||||
VerifyAccessorVertexCount("JOINTS_0", jointsAccessor, vertexCount);
|
||||
|
||||
auto* weightsAccessor = GetAccessorForIndex(
|
||||
"WEIGHTS_0",
|
||||
accessorsForVertex.weightsAccessor,
|
||||
{JsonAccessorType::VEC4},
|
||||
{JsonAccessorComponentType::FLOAT, JsonAccessorComponentType::UNSIGNED_BYTE, JsonAccessorComponentType::UNSIGNED_SHORT}
|
||||
).value_or(&nullAccessor);
|
||||
VerifyAccessorVertexCount("WEIGHTS_0", weightsAccessor, vertexCount);
|
||||
// clang-format on
|
||||
|
||||
const auto vertexOffset = common.m_vertices.size();
|
||||
common.m_vertices.reserve(vertexOffset + vertexCount);
|
||||
common.m_vertex_bone_weights.reserve(vertexOffset + vertexCount);
|
||||
for (auto vertexIndex = 0u; vertexIndex < vertexCount; vertexIndex++)
|
||||
{
|
||||
XModelVertex vertex;
|
||||
|
||||
unsigned joints[4];
|
||||
float weights[4];
|
||||
|
||||
float coordinates[3];
|
||||
float normal[3];
|
||||
if (!positionAccessor->GetFloatVec3(vertexIndex, coordinates) || !normalAccessor->GetFloatVec3(vertexIndex, normal)
|
||||
|| !colorAccessor->GetFloatVec4(vertexIndex, vertex.color) || !uvAccessor->GetFloatVec2(vertexIndex, vertex.uv)
|
||||
|| !jointsAccessor->GetUnsignedVec4(vertexIndex, joints) || !weightsAccessor->GetFloatVec4(vertexIndex, weights))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
vertex.coordinates[0] = coordinates[0];
|
||||
vertex.coordinates[1] = -coordinates[2];
|
||||
vertex.coordinates[2] = coordinates[1];
|
||||
vertex.normal[0] = normal[0];
|
||||
vertex.normal[1] = -normal[2];
|
||||
vertex.normal[2] = normal[1];
|
||||
|
||||
common.m_vertices.emplace_back(vertex);
|
||||
|
||||
XModelVertexBoneWeights vertexWeights{common.m_bone_weight_data.weights.size(), 0u};
|
||||
for (auto i = 0u; i < std::extent_v<decltype(joints)>; i++)
|
||||
{
|
||||
if (std::abs(weights[i]) < std::numeric_limits<float>::epsilon())
|
||||
continue;
|
||||
|
||||
common.m_bone_weight_data.weights.emplace_back(joints[i], weights[i]);
|
||||
vertexWeights.weightCount++;
|
||||
}
|
||||
|
||||
common.m_vertex_bone_weights.emplace_back(vertexWeights);
|
||||
}
|
||||
|
||||
m_vertex_offset_for_accessors.emplace(accessorsForVertex, vertexOffset);
|
||||
return vertexOffset;
|
||||
}
|
||||
|
||||
bool ConvertObject(const JsonRoot& jRoot, const JsonMeshPrimitives& primitives, XModelCommon& common, XModelObject& object)
|
||||
{
|
||||
if (!primitives.indices)
|
||||
throw GltfLoadException("Requires primitives indices");
|
||||
if (!primitives.material)
|
||||
throw GltfLoadException("Requires primitives material");
|
||||
if (primitives.mode.value_or(JsonMeshPrimitivesMode::TRIANGLES) != JsonMeshPrimitivesMode::TRIANGLES)
|
||||
throw GltfLoadException("Only triangles are supported");
|
||||
if (!primitives.attributes.POSITION)
|
||||
throw GltfLoadException("Requires primitives attribute POSITION");
|
||||
|
||||
AccessorsForVertex accessorsForVertex{
|
||||
*primitives.attributes.POSITION,
|
||||
primitives.attributes.NORMAL,
|
||||
primitives.attributes.COLOR_0,
|
||||
primitives.attributes.TEXCOORD_0,
|
||||
primitives.attributes.JOINTS_0,
|
||||
primitives.attributes.WEIGHTS_0,
|
||||
};
|
||||
|
||||
const auto existingVertices = m_vertex_offset_for_accessors.find(accessorsForVertex);
|
||||
const auto vertexOffset =
|
||||
existingVertices == m_vertex_offset_for_accessors.end() ? CreateVertices(common, accessorsForVertex) : existingVertices->second;
|
||||
|
||||
// clang-format off
|
||||
auto* indexAccessor = GetAccessorForIndex(
|
||||
"INDICES",
|
||||
primitives.indices,
|
||||
{JsonAccessorType::SCALAR},
|
||||
{JsonAccessorComponentType::UNSIGNED_BYTE, JsonAccessorComponentType::UNSIGNED_SHORT, JsonAccessorComponentType::UNSIGNED_INT}
|
||||
).value_or(nullptr);
|
||||
// clang-format on
|
||||
|
||||
const auto indexCount = indexAccessor->GetCount();
|
||||
if (indexCount % 3 != 0)
|
||||
throw GltfLoadException("Index count must be dividable by 3 for triangles");
|
||||
const auto faceCount = indexCount / 3u;
|
||||
object.m_faces.reserve(faceCount);
|
||||
|
||||
for (auto faceIndex = 0u; faceIndex < faceCount; faceIndex++)
|
||||
{
|
||||
unsigned indices[3];
|
||||
if (!indexAccessor->GetUnsigned(faceIndex * 3u + 0u, indices[0]) || !indexAccessor->GetUnsigned(faceIndex * 3u + 1u, indices[1])
|
||||
|| !indexAccessor->GetUnsigned(faceIndex * 3u + 2u, indices[2]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
object.m_faces.emplace_back(XModelFace{
|
||||
vertexOffset + indices[2],
|
||||
vertexOffset + indices[1],
|
||||
vertexOffset + indices[0],
|
||||
});
|
||||
}
|
||||
|
||||
if (!jRoot.materials || *primitives.material >= jRoot.materials->size())
|
||||
throw GltfLoadException("Invalid material index");
|
||||
|
||||
object.materialIndex = static_cast<int>(*primitives.material);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::optional<unsigned> GetRootNodeForSkin(const JsonRoot& jRoot, const JsonSkin& skin)
|
||||
{
|
||||
if (!jRoot.nodes || skin.joints.empty())
|
||||
return std::nullopt;
|
||||
|
||||
const auto jointCount = skin.joints.size();
|
||||
auto rootCount = jointCount;
|
||||
std::vector<bool> isRoot(jointCount, true);
|
||||
|
||||
for (const auto joint : skin.joints)
|
||||
{
|
||||
if (jRoot.nodes->size() <= joint)
|
||||
throw GltfLoadException("Invalid joint index");
|
||||
|
||||
const auto& node = jRoot.nodes.value()[joint];
|
||||
if (node.children)
|
||||
{
|
||||
for (const auto child : *node.children)
|
||||
{
|
||||
const auto foundChildJoint = std::ranges::find(skin.joints, child);
|
||||
if (foundChildJoint != skin.joints.end())
|
||||
{
|
||||
const auto foundChildJointIndex = std::distance(skin.joints.begin(), foundChildJoint);
|
||||
if (isRoot[foundChildJointIndex])
|
||||
{
|
||||
isRoot[foundChildJointIndex] = false;
|
||||
rootCount--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rootCount != 1)
|
||||
throw GltfLoadException("Skins must have exactly one common root node");
|
||||
|
||||
for (auto index = 0u; index < jointCount; index++)
|
||||
{
|
||||
if (isRoot[index])
|
||||
return skin.joints[index];
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static bool ConvertJoint(const JsonRoot& jRoot,
|
||||
const JsonSkin& skin,
|
||||
XModelCommon& common,
|
||||
const unsigned skinBoneOffset,
|
||||
const unsigned nodeIndex,
|
||||
const std::optional<unsigned> parentIndex,
|
||||
const float (&parentOffset)[3],
|
||||
const XModelQuaternion& parentRotation,
|
||||
const float (&parentScale)[3])
|
||||
{
|
||||
if (!jRoot.nodes || nodeIndex >= jRoot.nodes->size())
|
||||
return false;
|
||||
|
||||
const auto& node = jRoot.nodes.value()[nodeIndex];
|
||||
const auto boneInSkin = std::ranges::find(skin.joints, nodeIndex);
|
||||
if (boneInSkin == skin.joints.end())
|
||||
throw GltfLoadException("Bone node is not part of skin");
|
||||
const auto boneIndexInSkin = std::distance(skin.joints.begin(), boneInSkin);
|
||||
|
||||
const auto commonBoneOffset = skinBoneOffset + boneIndexInSkin;
|
||||
auto& bone = common.m_bones[commonBoneOffset];
|
||||
bone.name = node.name.value_or(std::string());
|
||||
bone.parentIndex = parentIndex;
|
||||
|
||||
if (node.scale)
|
||||
{
|
||||
bone.scale[0] = parentScale[0] * (*node.scale)[0];
|
||||
bone.scale[1] = parentScale[1] * (*node.scale)[1];
|
||||
bone.scale[2] = parentScale[2] * (*node.scale)[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
bone.scale[0] = parentScale[0];
|
||||
bone.scale[1] = parentScale[1];
|
||||
bone.scale[2] = parentScale[2];
|
||||
}
|
||||
|
||||
if (node.translation)
|
||||
{
|
||||
bone.localOffset[0] = (*node.translation)[0];
|
||||
bone.localOffset[1] = -(*node.translation)[2];
|
||||
bone.localOffset[2] = (*node.translation)[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
bone.localOffset[0] = 0.0f;
|
||||
bone.localOffset[1] = 0.0f;
|
||||
bone.localOffset[2] = 0.0f;
|
||||
}
|
||||
|
||||
bone.globalOffset[0] = bone.localOffset[0] + parentOffset[0];
|
||||
bone.globalOffset[1] = bone.localOffset[1] + parentOffset[1];
|
||||
bone.globalOffset[2] = bone.localOffset[2] + parentOffset[2];
|
||||
|
||||
if (node.rotation)
|
||||
{
|
||||
bone.localRotation.x = (*node.rotation)[0];
|
||||
bone.localRotation.y = -(*node.rotation)[2];
|
||||
bone.localRotation.z = (*node.rotation)[1];
|
||||
bone.localRotation.w = (*node.rotation)[3];
|
||||
}
|
||||
else
|
||||
{
|
||||
bone.localRotation.x = 0.0f;
|
||||
bone.localRotation.y = 0.0f;
|
||||
bone.localRotation.z = 0.0f;
|
||||
bone.localRotation.w = 1.0f;
|
||||
}
|
||||
|
||||
const auto localRotationEigen = Eigen::Quaternionf(bone.localRotation.w, bone.localRotation.x, bone.localRotation.y, bone.localRotation.z);
|
||||
const auto parentRotationEigen = Eigen::Quaternionf(parentRotation.w, parentRotation.x, parentRotation.y, parentRotation.z);
|
||||
const auto globalRotationEigen = localRotationEigen * parentRotationEigen;
|
||||
|
||||
bone.globalRotation.x = globalRotationEigen.x();
|
||||
bone.globalRotation.y = globalRotationEigen.y();
|
||||
bone.globalRotation.z = globalRotationEigen.z();
|
||||
bone.globalRotation.w = globalRotationEigen.w();
|
||||
|
||||
if (node.children)
|
||||
{
|
||||
for (const auto childIndex : *node.children)
|
||||
{
|
||||
if (!ConvertJoint(jRoot, skin, common, skinBoneOffset, childIndex, commonBoneOffset, bone.globalOffset, bone.globalRotation, bone.scale))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ConvertSkin(const JsonRoot& jRoot, const JsonSkin& skin, XModelCommon& common)
|
||||
{
|
||||
if (skin.joints.empty())
|
||||
return true;
|
||||
if (!jRoot.nodes)
|
||||
return false;
|
||||
|
||||
if (!common.m_bones.empty())
|
||||
throw GltfLoadException("Only scenes with at most one skin are supported");
|
||||
|
||||
const auto rootNode = GetRootNodeForSkin(jRoot, skin).value_or(skin.joints[0]);
|
||||
const auto skinBoneOffset = common.m_bones.size();
|
||||
common.m_bones.resize(skinBoneOffset + skin.joints.size());
|
||||
|
||||
constexpr float defaultTranslation[3]{0.0f, 0.0f, 0.0f};
|
||||
constexpr XModelQuaternion defaultRotation{0.0f, 0.0f, 0.0f, 1.0f};
|
||||
constexpr float defaultScale[3]{1.0f, 1.0f, 1.0f};
|
||||
|
||||
return ConvertJoint(jRoot, skin, common, skinBoneOffset, rootNode, std::nullopt, defaultTranslation, defaultRotation, defaultScale);
|
||||
}
|
||||
|
||||
void ConvertObjects(const JsonRoot& jRoot, XModelCommon& common)
|
||||
{
|
||||
if (!jRoot.meshes)
|
||||
return;
|
||||
|
||||
for (const auto& loadObject : m_load_objects)
|
||||
{
|
||||
if (loadObject.skinIndex && jRoot.skins)
|
||||
{
|
||||
const auto& skin = jRoot.skins.value()[*loadObject.skinIndex];
|
||||
if (!ConvertSkin(jRoot, skin, common))
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& mesh = jRoot.meshes.value()[loadObject.meshIndex];
|
||||
|
||||
common.m_objects.reserve(common.m_objects.size() + mesh.primitives.size());
|
||||
for (const auto& primitives : mesh.primitives)
|
||||
{
|
||||
XModelObject object;
|
||||
if (!ConvertObject(jRoot, primitives, common, object))
|
||||
return;
|
||||
|
||||
common.m_objects.emplace_back(std::move(object));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ConvertMaterials(const JsonRoot& jRoot, XModelCommon& common)
|
||||
{
|
||||
if (!jRoot.materials)
|
||||
return;
|
||||
|
||||
common.m_materials.reserve(jRoot.materials->size());
|
||||
for (const auto& jMaterial : *jRoot.materials)
|
||||
{
|
||||
XModelMaterial material;
|
||||
material.ApplyDefaults();
|
||||
|
||||
if (jMaterial.name)
|
||||
material.name = *jMaterial.name;
|
||||
else
|
||||
throw GltfLoadException("Materials must have a name");
|
||||
|
||||
common.m_materials.emplace_back(std::move(material));
|
||||
}
|
||||
}
|
||||
|
||||
void CreateBuffers(const JsonRoot& jRoot)
|
||||
{
|
||||
if (!jRoot.buffers)
|
||||
return;
|
||||
|
||||
m_buffers.reserve(jRoot.buffers->size());
|
||||
for (const auto& jBuffer : *jRoot.buffers)
|
||||
{
|
||||
if (!jBuffer.uri)
|
||||
{
|
||||
const void* embeddedBufferPtr = nullptr;
|
||||
size_t embeddedBufferSize = 0u;
|
||||
if (!m_input->GetEmbeddedBuffer(embeddedBufferPtr, embeddedBufferSize) || embeddedBufferSize == 0u)
|
||||
throw GltfLoadException("Buffer tried to access embedded data when there is none");
|
||||
|
||||
m_buffers.emplace_back(std::make_unique<EmbeddedBuffer>(embeddedBufferPtr, embeddedBufferSize));
|
||||
}
|
||||
else if (DataUriBuffer::IsDataUri(*jBuffer.uri))
|
||||
{
|
||||
auto dataUriBuffer = std::make_unique<DataUriBuffer>();
|
||||
if (!dataUriBuffer->ReadDataFromUri(*jBuffer.uri))
|
||||
throw GltfLoadException("Buffer has invalid data uri");
|
||||
|
||||
m_buffers.emplace_back(std::move(dataUriBuffer));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw GltfLoadException("File buffers are not supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CreateBufferViews(const JsonRoot& jRoot)
|
||||
{
|
||||
if (!jRoot.bufferViews)
|
||||
return;
|
||||
|
||||
m_buffer_views.reserve(jRoot.bufferViews->size());
|
||||
for (const auto& jBufferView : *jRoot.bufferViews)
|
||||
{
|
||||
if (jBufferView.buffer >= m_buffers.size())
|
||||
throw GltfLoadException("Buffer view references invalid buffer");
|
||||
|
||||
const auto* buffer = m_buffers[jBufferView.buffer].get();
|
||||
const auto offset = jBufferView.byteOffset.value_or(0u);
|
||||
const auto length = jBufferView.byteLength;
|
||||
const auto stride = jBufferView.byteStride.value_or(0u);
|
||||
|
||||
if (offset + length > buffer->GetSize())
|
||||
throw GltfLoadException("Buffer view is defined larger as underlying buffer");
|
||||
|
||||
m_buffer_views.emplace_back(std::make_unique<BufferView>(buffer, offset, length, stride));
|
||||
}
|
||||
}
|
||||
|
||||
void CreateAccessors(const JsonRoot& jRoot)
|
||||
{
|
||||
if (!jRoot.accessors)
|
||||
return;
|
||||
|
||||
m_accessors.reserve(jRoot.accessors->size());
|
||||
for (const auto& jAccessor : *jRoot.accessors)
|
||||
{
|
||||
if (!jAccessor.bufferView)
|
||||
{
|
||||
m_accessors.emplace_back(std::make_unique<NullAccessor>(jAccessor.count));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*jAccessor.bufferView >= m_buffer_views.size())
|
||||
throw GltfLoadException("Accessor references invalid buffer view");
|
||||
|
||||
const auto* bufferView = m_buffer_views[*jAccessor.bufferView].get();
|
||||
const auto byteOffset = jAccessor.byteOffset.value_or(0u);
|
||||
if (jAccessor.componentType == JsonAccessorComponentType::FLOAT)
|
||||
m_accessors.emplace_back(std::make_unique<FloatAccessor>(bufferView, jAccessor.type, byteOffset, jAccessor.count));
|
||||
else if (jAccessor.componentType == JsonAccessorComponentType::UNSIGNED_BYTE)
|
||||
m_accessors.emplace_back(std::make_unique<UnsignedByteAccessor>(bufferView, jAccessor.type, byteOffset, jAccessor.count));
|
||||
else if (jAccessor.componentType == JsonAccessorComponentType::UNSIGNED_SHORT)
|
||||
m_accessors.emplace_back(std::make_unique<UnsignedShortAccessor>(bufferView, jAccessor.type, byteOffset, jAccessor.count));
|
||||
else
|
||||
throw GltfLoadException(std::format("Accessor has unsupported component type {}", static_cast<unsigned>(jAccessor.componentType)));
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<XModelCommon> Load() override
|
||||
{
|
||||
JsonRoot jRoot;
|
||||
try
|
||||
{
|
||||
jRoot = m_input->GetJson().get<JsonRoot>();
|
||||
}
|
||||
catch (const nlohmann::json::exception& e)
|
||||
{
|
||||
std::cerr << std::format("Failed to parse GLTF JSON: {}\n", e.what());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
CreateBuffers(jRoot);
|
||||
CreateBufferViews(jRoot);
|
||||
CreateAccessors(jRoot);
|
||||
|
||||
TraverseNodes(jRoot);
|
||||
|
||||
auto common = std::make_unique<XModelCommon>();
|
||||
ConvertObjects(jRoot, *common);
|
||||
ConvertMaterials(jRoot, *common);
|
||||
|
||||
return common;
|
||||
}
|
||||
catch (const GltfLoadException& e)
|
||||
{
|
||||
std::cerr << std::format("Failed to load GLTF: {}\n", e.Str());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const Input* m_input;
|
||||
std::vector<ObjectToLoad> m_load_objects;
|
||||
std::unordered_map<AccessorsForVertex, unsigned> m_vertex_offset_for_accessors;
|
||||
std::vector<std::unique_ptr<Accessor>> m_accessors;
|
||||
std::vector<std::unique_ptr<BufferView>> m_buffer_views;
|
||||
std::vector<std::unique_ptr<Buffer>> m_buffers;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<Loader> Loader::CreateLoader(const Input* input)
|
||||
{
|
||||
return std::make_unique<GltfLoaderImpl>(input);
|
||||
}
|
24
src/ObjLoading/XModel/Gltf/GltfLoader.h
Normal file
24
src/ObjLoading/XModel/Gltf/GltfLoader.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "GltfInput.h"
|
||||
#include "XModel/Gltf/JsonGltf.h"
|
||||
#include "XModel/XModelLoader.h"
|
||||
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
|
||||
namespace gltf
|
||||
{
|
||||
class Loader : public XModelLoader
|
||||
{
|
||||
public:
|
||||
Loader() = default;
|
||||
~Loader() override = default;
|
||||
Loader(const Loader& other) = default;
|
||||
Loader(Loader&& other) noexcept = default;
|
||||
Loader& operator=(const Loader& other) = default;
|
||||
Loader& operator=(Loader&& other) noexcept = default;
|
||||
|
||||
static std::unique_ptr<Loader> CreateLoader(const Input* input);
|
||||
};
|
||||
} // namespace gltf
|
48
src/ObjLoading/XModel/Gltf/GltfTextInput.cpp
Normal file
48
src/ObjLoading/XModel/Gltf/GltfTextInput.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
#include "GltfTextInput.h"
|
||||
|
||||
#include <exception>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using namespace gltf;
|
||||
|
||||
TextInput::TextInput()
|
||||
: m_buffer_size(0u)
|
||||
{
|
||||
}
|
||||
|
||||
bool TextInput::GetEmbeddedBuffer(const void*& buffer, size_t& bufferSize) const
|
||||
{
|
||||
if (!m_buffer || !m_buffer_size)
|
||||
return false;
|
||||
|
||||
buffer = m_buffer.get();
|
||||
bufferSize = m_buffer_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const nlohmann::json& TextInput::GetJson() const
|
||||
{
|
||||
if (!m_json)
|
||||
throw std::exception();
|
||||
|
||||
return *m_json;
|
||||
}
|
||||
|
||||
bool TextInput::ReadGltfData(std::istream& stream)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_json = std::make_unique<nlohmann::json>(nlohmann::json::parse(stream));
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (nlohmann::json::exception& e)
|
||||
{
|
||||
std::cerr << std::format("Failed to parse json of GLTF: {}", e.what());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
23
src/ObjLoading/XModel/Gltf/GltfTextInput.h
Normal file
23
src/ObjLoading/XModel/Gltf/GltfTextInput.h
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "GltfInput.h"
|
||||
|
||||
#include <istream>
|
||||
|
||||
namespace gltf
|
||||
{
|
||||
class TextInput final : public Input
|
||||
{
|
||||
public:
|
||||
TextInput();
|
||||
|
||||
bool ReadGltfData(std::istream& stream) override;
|
||||
bool GetEmbeddedBuffer(const void*& buffer, size_t& bufferSize) const override;
|
||||
[[nodiscard]] const nlohmann::json& GetJson() const override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<nlohmann::json> m_json;
|
||||
std::unique_ptr<uint8_t[]> m_buffer;
|
||||
size_t m_buffer_size;
|
||||
};
|
||||
} // namespace gltf
|
437
src/ObjLoading/XModel/Gltf/Internal/GltfAccessor.cpp
Normal file
437
src/ObjLoading/XModel/Gltf/Internal/GltfAccessor.cpp
Normal file
@ -0,0 +1,437 @@
|
||||
#include "GltfAccessor.h"
|
||||
|
||||
using namespace gltf;
|
||||
|
||||
NullAccessor::NullAccessor(const size_t count)
|
||||
: m_count(count)
|
||||
{
|
||||
}
|
||||
|
||||
bool NullAccessor::GetFloatVec2(const size_t index, float (&out)[2]) const
|
||||
{
|
||||
if (index >= m_count)
|
||||
return false;
|
||||
|
||||
out[0] = 0.0f;
|
||||
out[1] = 0.0f;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NullAccessor::GetFloatVec3(const size_t index, float (&out)[3]) const
|
||||
{
|
||||
if (index >= m_count)
|
||||
return false;
|
||||
|
||||
out[0] = 0.0f;
|
||||
out[1] = 0.0f;
|
||||
out[2] = 0.0f;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NullAccessor::GetFloatVec4(const size_t index, float (&out)[4]) const
|
||||
{
|
||||
if (index >= m_count)
|
||||
return false;
|
||||
|
||||
out[0] = 0.0f;
|
||||
out[1] = 0.0f;
|
||||
out[2] = 0.0f;
|
||||
out[3] = 0.0f;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NullAccessor::GetUnsigned(const size_t index, unsigned& out) const
|
||||
{
|
||||
if (index >= m_count)
|
||||
return false;
|
||||
|
||||
out = 0u;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NullAccessor::GetUnsignedVec4(const size_t index, unsigned (&out)[4]) const
|
||||
{
|
||||
if (index >= m_count)
|
||||
return false;
|
||||
|
||||
out[0] = 0u;
|
||||
out[1] = 0u;
|
||||
out[2] = 0u;
|
||||
out[3] = 0u;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<JsonAccessorComponentType> NullAccessor::GetComponentType() const
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<JsonAccessorType> NullAccessor::GetType() const
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
size_t NullAccessor::GetCount() const
|
||||
{
|
||||
return m_count;
|
||||
}
|
||||
|
||||
OnesAccessor::OnesAccessor(const size_t count)
|
||||
: m_count(count)
|
||||
{
|
||||
}
|
||||
|
||||
bool OnesAccessor::GetFloatVec2(const size_t index, float (&out)[2]) const
|
||||
{
|
||||
if (index >= m_count)
|
||||
return false;
|
||||
|
||||
out[0] = 1.0f;
|
||||
out[1] = 1.0f;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnesAccessor::GetFloatVec3(const size_t index, float (&out)[3]) const
|
||||
{
|
||||
if (index >= m_count)
|
||||
return false;
|
||||
|
||||
out[0] = 1.0f;
|
||||
out[1] = 1.0f;
|
||||
out[2] = 1.0f;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnesAccessor::GetFloatVec4(const size_t index, float (&out)[4]) const
|
||||
{
|
||||
if (index >= m_count)
|
||||
return false;
|
||||
|
||||
out[0] = 1.0f;
|
||||
out[1] = 1.0f;
|
||||
out[2] = 1.0f;
|
||||
out[3] = 1.0f;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnesAccessor::GetUnsigned(const size_t index, unsigned& out) const
|
||||
{
|
||||
if (index >= m_count)
|
||||
return false;
|
||||
|
||||
out = 0xFFFFFFFFu;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnesAccessor::GetUnsignedVec4(const size_t index, unsigned (&out)[4]) const
|
||||
{
|
||||
if (index >= m_count)
|
||||
return false;
|
||||
|
||||
out[0] = 0xFFFFFFFFu;
|
||||
out[1] = 0xFFFFFFFFu;
|
||||
out[2] = 0xFFFFFFFFu;
|
||||
out[3] = 0xFFFFFFFFu;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<JsonAccessorComponentType> OnesAccessor::GetComponentType() const
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<JsonAccessorType> OnesAccessor::GetType() const
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
size_t OnesAccessor::GetCount() const
|
||||
{
|
||||
return m_count;
|
||||
}
|
||||
|
||||
FloatAccessor::FloatAccessor(const BufferView* bufferView, const JsonAccessorType type, size_t byteOffset, const size_t count)
|
||||
: m_buffer_view(bufferView),
|
||||
m_type(type),
|
||||
m_byte_offset(byteOffset),
|
||||
m_count(count)
|
||||
{
|
||||
}
|
||||
|
||||
std::optional<JsonAccessorType> FloatAccessor::GetType() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
std::optional<JsonAccessorComponentType> FloatAccessor::GetComponentType() const
|
||||
{
|
||||
return JsonAccessorComponentType::FLOAT;
|
||||
}
|
||||
|
||||
size_t FloatAccessor::GetCount() const
|
||||
{
|
||||
return m_count;
|
||||
}
|
||||
|
||||
bool FloatAccessor::GetFloatVec2(const size_t index, float (&out)[2]) const
|
||||
{
|
||||
assert(m_type == JsonAccessorType::VEC2 || m_type == JsonAccessorType::VEC3 || m_type == JsonAccessorType::VEC4);
|
||||
if (index >= m_count)
|
||||
return false;
|
||||
|
||||
return m_buffer_view->ReadElement(&out, index, sizeof(float[2]), m_byte_offset);
|
||||
}
|
||||
|
||||
bool FloatAccessor::GetFloatVec3(const size_t index, float (&out)[3]) const
|
||||
{
|
||||
assert(m_type == JsonAccessorType::VEC3 || m_type == JsonAccessorType::VEC4);
|
||||
if (index >= m_count)
|
||||
return false;
|
||||
|
||||
return m_buffer_view->ReadElement(&out, index, sizeof(float[3]), m_byte_offset);
|
||||
}
|
||||
|
||||
bool FloatAccessor::GetFloatVec4(const size_t index, float (&out)[4]) const
|
||||
{
|
||||
assert(m_type == JsonAccessorType::VEC4);
|
||||
if (index >= m_count)
|
||||
return false;
|
||||
|
||||
return m_buffer_view->ReadElement(&out, index, sizeof(float[4]), m_byte_offset);
|
||||
}
|
||||
|
||||
bool FloatAccessor::GetUnsigned(size_t index, unsigned& out) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FloatAccessor::GetUnsignedVec4(size_t index, unsigned (&out)[4]) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UnsignedByteAccessor::UnsignedByteAccessor(const BufferView* bufferView, const JsonAccessorType type, size_t byteOffset, const size_t count)
|
||||
: m_buffer_view(bufferView),
|
||||
m_type(type),
|
||||
m_byte_offset(byteOffset),
|
||||
m_count(count)
|
||||
{
|
||||
}
|
||||
|
||||
std::optional<JsonAccessorType> UnsignedByteAccessor::GetType() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
std::optional<JsonAccessorComponentType> UnsignedByteAccessor::GetComponentType() const
|
||||
{
|
||||
return JsonAccessorComponentType::UNSIGNED_BYTE;
|
||||
}
|
||||
|
||||
size_t UnsignedByteAccessor::GetCount() const
|
||||
{
|
||||
return m_count;
|
||||
}
|
||||
|
||||
bool UnsignedByteAccessor::GetFloatVec2(const size_t index, float (&out)[2]) const
|
||||
{
|
||||
assert(m_type == JsonAccessorType::VEC2 || m_type == JsonAccessorType::VEC3 || m_type == JsonAccessorType::VEC4);
|
||||
if (index >= m_count)
|
||||
return false;
|
||||
|
||||
uint8_t temp[2];
|
||||
if (!m_buffer_view->ReadElement(temp, index, sizeof(uint8_t[2]), m_byte_offset))
|
||||
return false;
|
||||
|
||||
// Return as normalized value between 0 and 1
|
||||
out[0] = static_cast<float>(temp[0]) / static_cast<float>(std::numeric_limits<uint8_t>::max());
|
||||
out[1] = static_cast<float>(temp[1]) / static_cast<float>(std::numeric_limits<uint8_t>::max());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnsignedByteAccessor::GetFloatVec3(const size_t index, float (&out)[3]) const
|
||||
{
|
||||
assert(m_type == JsonAccessorType::VEC3 || m_type == JsonAccessorType::VEC4);
|
||||
if (index >= m_count)
|
||||
return false;
|
||||
|
||||
uint8_t temp[3];
|
||||
if (!m_buffer_view->ReadElement(temp, index, sizeof(uint8_t[3]), m_byte_offset))
|
||||
return false;
|
||||
|
||||
// Return as normalized value between 0 and 1
|
||||
out[0] = static_cast<float>(temp[0]) / static_cast<float>(std::numeric_limits<uint8_t>::max());
|
||||
out[1] = static_cast<float>(temp[1]) / static_cast<float>(std::numeric_limits<uint8_t>::max());
|
||||
out[2] = static_cast<float>(temp[2]) / static_cast<float>(std::numeric_limits<uint8_t>::max());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnsignedByteAccessor::GetFloatVec4(const size_t index, float (&out)[4]) const
|
||||
{
|
||||
assert(m_type == JsonAccessorType::VEC4);
|
||||
if (index >= m_count)
|
||||
return false;
|
||||
|
||||
uint8_t temp[4];
|
||||
if (!m_buffer_view->ReadElement(temp, index, sizeof(uint8_t[4]), m_byte_offset))
|
||||
return false;
|
||||
|
||||
// Return as normalized value between 0 and 1
|
||||
out[0] = static_cast<float>(temp[0]) / static_cast<float>(std::numeric_limits<uint8_t>::max());
|
||||
out[1] = static_cast<float>(temp[1]) / static_cast<float>(std::numeric_limits<uint8_t>::max());
|
||||
out[2] = static_cast<float>(temp[2]) / static_cast<float>(std::numeric_limits<uint8_t>::max());
|
||||
out[3] = static_cast<float>(temp[3]) / static_cast<float>(std::numeric_limits<uint8_t>::max());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnsignedByteAccessor::GetUnsigned(const size_t index, unsigned& out) const
|
||||
{
|
||||
if (index >= m_count)
|
||||
return false;
|
||||
|
||||
uint8_t temp;
|
||||
if (!m_buffer_view->ReadElement(&temp, index, sizeof(uint8_t), m_byte_offset))
|
||||
return false;
|
||||
|
||||
out = temp;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnsignedByteAccessor::GetUnsignedVec4(const size_t index, unsigned (&out)[4]) const
|
||||
{
|
||||
assert(m_type == JsonAccessorType::VEC4);
|
||||
if (index >= m_count)
|
||||
return false;
|
||||
|
||||
uint8_t temp[4];
|
||||
if (!m_buffer_view->ReadElement(temp, index, sizeof(uint8_t[4]), m_byte_offset))
|
||||
return false;
|
||||
|
||||
out[0] = static_cast<unsigned>(temp[0]);
|
||||
out[1] = static_cast<unsigned>(temp[1]);
|
||||
out[2] = static_cast<unsigned>(temp[2]);
|
||||
out[3] = static_cast<unsigned>(temp[3]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
UnsignedShortAccessor::UnsignedShortAccessor(const BufferView* bufferView, const JsonAccessorType type, size_t byteOffset, const size_t count)
|
||||
: m_buffer_view(bufferView),
|
||||
m_type(type),
|
||||
m_byte_offset(byteOffset),
|
||||
m_count(count)
|
||||
{
|
||||
}
|
||||
|
||||
std::optional<JsonAccessorType> UnsignedShortAccessor::GetType() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
std::optional<JsonAccessorComponentType> UnsignedShortAccessor::GetComponentType() const
|
||||
{
|
||||
return JsonAccessorComponentType::UNSIGNED_SHORT;
|
||||
}
|
||||
|
||||
size_t UnsignedShortAccessor::GetCount() const
|
||||
{
|
||||
return m_count;
|
||||
}
|
||||
|
||||
bool UnsignedShortAccessor::GetFloatVec2(const size_t index, float (&out)[2]) const
|
||||
{
|
||||
assert(m_type == JsonAccessorType::VEC2 || m_type == JsonAccessorType::VEC3 || m_type == JsonAccessorType::VEC4);
|
||||
if (index >= m_count)
|
||||
return false;
|
||||
|
||||
uint16_t temp[2];
|
||||
if (!m_buffer_view->ReadElement(temp, index, sizeof(uint16_t[2]), m_byte_offset))
|
||||
return false;
|
||||
|
||||
// Return as normalized value between 0 and 1
|
||||
out[0] = static_cast<float>(temp[0]) / static_cast<float>(std::numeric_limits<uint16_t>::max());
|
||||
out[1] = static_cast<float>(temp[1]) / static_cast<float>(std::numeric_limits<uint16_t>::max());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnsignedShortAccessor::GetFloatVec3(const size_t index, float (&out)[3]) const
|
||||
{
|
||||
assert(m_type == JsonAccessorType::VEC3 || m_type == JsonAccessorType::VEC4);
|
||||
if (index >= m_count)
|
||||
return false;
|
||||
|
||||
uint16_t temp[3];
|
||||
if (!m_buffer_view->ReadElement(temp, index, sizeof(uint16_t[3]), m_byte_offset))
|
||||
return false;
|
||||
|
||||
// Return as normalized value between 0 and 1
|
||||
out[0] = static_cast<float>(temp[0]) / static_cast<float>(std::numeric_limits<uint16_t>::max());
|
||||
out[1] = static_cast<float>(temp[1]) / static_cast<float>(std::numeric_limits<uint16_t>::max());
|
||||
out[2] = static_cast<float>(temp[2]) / static_cast<float>(std::numeric_limits<uint16_t>::max());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnsignedShortAccessor::GetFloatVec4(const size_t index, float (&out)[4]) const
|
||||
{
|
||||
assert(m_type == JsonAccessorType::VEC4);
|
||||
if (index >= m_count)
|
||||
return false;
|
||||
|
||||
uint16_t temp[4];
|
||||
if (!m_buffer_view->ReadElement(temp, index, sizeof(uint16_t[4]), m_byte_offset))
|
||||
return false;
|
||||
|
||||
// Return as normalized value between 0 and 1
|
||||
out[0] = static_cast<float>(temp[0]) / static_cast<float>(std::numeric_limits<uint16_t>::max());
|
||||
out[1] = static_cast<float>(temp[1]) / static_cast<float>(std::numeric_limits<uint16_t>::max());
|
||||
out[2] = static_cast<float>(temp[2]) / static_cast<float>(std::numeric_limits<uint16_t>::max());
|
||||
out[3] = static_cast<float>(temp[3]) / static_cast<float>(std::numeric_limits<uint16_t>::max());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnsignedShortAccessor::GetUnsigned(const size_t index, unsigned& out) const
|
||||
{
|
||||
if (index >= m_count)
|
||||
return false;
|
||||
|
||||
uint16_t temp;
|
||||
if (!m_buffer_view->ReadElement(&temp, index, sizeof(uint16_t), m_byte_offset))
|
||||
return false;
|
||||
|
||||
out = temp;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnsignedShortAccessor::GetUnsignedVec4(const size_t index, unsigned (&out)[4]) const
|
||||
{
|
||||
assert(m_type == JsonAccessorType::VEC4);
|
||||
if (index >= m_count)
|
||||
return false;
|
||||
|
||||
uint16_t temp[4];
|
||||
if (!m_buffer_view->ReadElement(temp, index, sizeof(uint8_t[4]), m_byte_offset))
|
||||
return false;
|
||||
|
||||
out[0] = static_cast<unsigned>(temp[0]);
|
||||
out[1] = static_cast<unsigned>(temp[1]);
|
||||
out[2] = static_cast<unsigned>(temp[2]);
|
||||
out[3] = static_cast<unsigned>(temp[3]);
|
||||
|
||||
return true;
|
||||
}
|
133
src/ObjLoading/XModel/Gltf/Internal/GltfAccessor.h
Normal file
133
src/ObjLoading/XModel/Gltf/Internal/GltfAccessor.h
Normal file
@ -0,0 +1,133 @@
|
||||
#pragma once
|
||||
#include "GltfBufferView.h"
|
||||
#include "XModel/Gltf/JsonGltf.h"
|
||||
|
||||
namespace gltf
|
||||
{
|
||||
class Accessor
|
||||
{
|
||||
protected:
|
||||
Accessor() = default;
|
||||
|
||||
public:
|
||||
virtual ~Accessor() = default;
|
||||
Accessor(const Accessor& other) = default;
|
||||
Accessor(Accessor&& other) noexcept = default;
|
||||
Accessor& operator=(const Accessor& other) = default;
|
||||
Accessor& operator=(Accessor&& other) noexcept = default;
|
||||
|
||||
[[nodiscard]] virtual std::optional<JsonAccessorType> GetType() const = 0;
|
||||
[[nodiscard]] virtual std::optional<JsonAccessorComponentType> GetComponentType() const = 0;
|
||||
[[nodiscard]] virtual size_t GetCount() const = 0;
|
||||
|
||||
[[nodiscard]] virtual bool GetFloatVec2(size_t index, float (&out)[2]) const = 0;
|
||||
[[nodiscard]] virtual bool GetFloatVec3(size_t index, float (&out)[3]) const = 0;
|
||||
[[nodiscard]] virtual bool GetFloatVec4(size_t index, float (&out)[4]) const = 0;
|
||||
[[nodiscard]] virtual bool GetUnsigned(size_t index, unsigned& out) const = 0;
|
||||
[[nodiscard]] virtual bool GetUnsignedVec4(size_t index, unsigned (&out)[4]) const = 0;
|
||||
};
|
||||
|
||||
class NullAccessor final : public Accessor
|
||||
{
|
||||
public:
|
||||
explicit NullAccessor(size_t count);
|
||||
|
||||
[[nodiscard]] std::optional<JsonAccessorType> GetType() const override;
|
||||
[[nodiscard]] std::optional<JsonAccessorComponentType> GetComponentType() const override;
|
||||
[[nodiscard]] size_t GetCount() const override;
|
||||
|
||||
[[nodiscard]] bool GetFloatVec2(size_t index, float (&out)[2]) const override;
|
||||
[[nodiscard]] bool GetFloatVec3(size_t index, float (&out)[3]) const override;
|
||||
[[nodiscard]] bool GetFloatVec4(size_t index, float (&out)[4]) const override;
|
||||
[[nodiscard]] bool GetUnsigned(size_t index, unsigned& out) const override;
|
||||
[[nodiscard]] bool GetUnsignedVec4(size_t index, unsigned (&out)[4]) const override;
|
||||
|
||||
private:
|
||||
size_t m_count;
|
||||
};
|
||||
|
||||
class OnesAccessor final : public Accessor
|
||||
{
|
||||
public:
|
||||
explicit OnesAccessor(size_t count);
|
||||
|
||||
[[nodiscard]] std::optional<JsonAccessorType> GetType() const override;
|
||||
[[nodiscard]] std::optional<JsonAccessorComponentType> GetComponentType() const override;
|
||||
[[nodiscard]] size_t GetCount() const override;
|
||||
|
||||
[[nodiscard]] bool GetFloatVec2(size_t index, float (&out)[2]) const override;
|
||||
[[nodiscard]] bool GetFloatVec3(size_t index, float (&out)[3]) const override;
|
||||
[[nodiscard]] bool GetFloatVec4(size_t index, float (&out)[4]) const override;
|
||||
[[nodiscard]] bool GetUnsigned(size_t index, unsigned& out) const override;
|
||||
[[nodiscard]] bool GetUnsignedVec4(size_t index, unsigned (&out)[4]) const override;
|
||||
|
||||
private:
|
||||
size_t m_count;
|
||||
};
|
||||
|
||||
class FloatAccessor final : public Accessor
|
||||
{
|
||||
public:
|
||||
FloatAccessor(const BufferView* bufferView, JsonAccessorType type, size_t byteOffset, size_t count);
|
||||
|
||||
[[nodiscard]] std::optional<JsonAccessorType> GetType() const override;
|
||||
[[nodiscard]] std::optional<JsonAccessorComponentType> GetComponentType() const override;
|
||||
[[nodiscard]] size_t GetCount() const override;
|
||||
|
||||
[[nodiscard]] bool GetFloatVec2(size_t index, float (&out)[2]) const override;
|
||||
[[nodiscard]] bool GetFloatVec3(size_t index, float (&out)[3]) const override;
|
||||
[[nodiscard]] bool GetFloatVec4(size_t index, float (&out)[4]) const override;
|
||||
[[nodiscard]] bool GetUnsigned(size_t index, unsigned& out) const override;
|
||||
[[nodiscard]] bool GetUnsignedVec4(size_t index, unsigned (&out)[4]) const override;
|
||||
|
||||
private:
|
||||
const BufferView* m_buffer_view;
|
||||
JsonAccessorType m_type;
|
||||
size_t m_byte_offset;
|
||||
size_t m_count;
|
||||
};
|
||||
|
||||
class UnsignedByteAccessor final : public Accessor
|
||||
{
|
||||
public:
|
||||
UnsignedByteAccessor(const BufferView* bufferView, JsonAccessorType type, size_t byteOffset, size_t count);
|
||||
|
||||
[[nodiscard]] std::optional<JsonAccessorType> GetType() const override;
|
||||
[[nodiscard]] std::optional<JsonAccessorComponentType> GetComponentType() const override;
|
||||
[[nodiscard]] size_t GetCount() const override;
|
||||
|
||||
[[nodiscard]] bool GetFloatVec2(size_t index, float (&out)[2]) const override;
|
||||
[[nodiscard]] bool GetFloatVec3(size_t index, float (&out)[3]) const override;
|
||||
[[nodiscard]] bool GetFloatVec4(size_t index, float (&out)[4]) const override;
|
||||
[[nodiscard]] bool GetUnsigned(size_t index, unsigned& out) const override;
|
||||
[[nodiscard]] bool GetUnsignedVec4(size_t index, unsigned (&out)[4]) const override;
|
||||
|
||||
private:
|
||||
const BufferView* m_buffer_view;
|
||||
JsonAccessorType m_type;
|
||||
size_t m_byte_offset;
|
||||
size_t m_count;
|
||||
};
|
||||
|
||||
class UnsignedShortAccessor final : public Accessor
|
||||
{
|
||||
public:
|
||||
UnsignedShortAccessor(const BufferView* bufferView, JsonAccessorType type, size_t byteOffset, size_t count);
|
||||
|
||||
[[nodiscard]] std::optional<JsonAccessorType> GetType() const override;
|
||||
[[nodiscard]] std::optional<JsonAccessorComponentType> GetComponentType() const override;
|
||||
[[nodiscard]] size_t GetCount() const override;
|
||||
|
||||
[[nodiscard]] bool GetFloatVec2(size_t index, float (&out)[2]) const override;
|
||||
[[nodiscard]] bool GetFloatVec3(size_t index, float (&out)[3]) const override;
|
||||
[[nodiscard]] bool GetFloatVec4(size_t index, float (&out)[4]) const override;
|
||||
[[nodiscard]] bool GetUnsigned(size_t index, unsigned& out) const override;
|
||||
[[nodiscard]] bool GetUnsignedVec4(size_t index, unsigned (&out)[4]) const override;
|
||||
|
||||
private:
|
||||
const BufferView* m_buffer_view;
|
||||
JsonAccessorType m_type;
|
||||
size_t m_byte_offset;
|
||||
size_t m_count;
|
||||
};
|
||||
} // namespace gltf
|
80
src/ObjLoading/XModel/Gltf/Internal/GltfBuffer.cpp
Normal file
80
src/ObjLoading/XModel/Gltf/Internal/GltfBuffer.cpp
Normal file
@ -0,0 +1,80 @@
|
||||
#include "GltfBuffer.h"
|
||||
|
||||
#include "XModel/Gltf/GltfConstants.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#define LTC_NO_PROTOTYPES
|
||||
#include <tomcrypt.h>
|
||||
|
||||
using namespace gltf;
|
||||
|
||||
EmbeddedBuffer::EmbeddedBuffer(const void* data, const size_t dataSize)
|
||||
: m_data(static_cast<const uint8_t*>(data)),
|
||||
m_data_size(dataSize)
|
||||
{
|
||||
}
|
||||
|
||||
bool EmbeddedBuffer::ReadData(void* dest, const size_t offset, const size_t count) const
|
||||
{
|
||||
assert(m_data);
|
||||
assert(m_data_size > 0u);
|
||||
|
||||
if (offset + count > m_data_size)
|
||||
return false;
|
||||
|
||||
std::memcpy(dest, &m_data[offset], count);
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t EmbeddedBuffer::GetSize() const
|
||||
{
|
||||
return m_data_size;
|
||||
}
|
||||
|
||||
DataUriBuffer::DataUriBuffer()
|
||||
: m_data(nullptr),
|
||||
m_data_size(0u)
|
||||
{
|
||||
}
|
||||
|
||||
bool DataUriBuffer::IsDataUri(const std::string& uri)
|
||||
{
|
||||
return uri.starts_with(GLTF_DATA_URI_PREFIX) && uri.size() > URI_PREFIX_LENGTH;
|
||||
}
|
||||
|
||||
bool DataUriBuffer::ReadDataFromUri(const std::string& uri)
|
||||
{
|
||||
if (!IsDataUri(uri))
|
||||
return false;
|
||||
|
||||
const auto base64DataLength = uri.size() - URI_PREFIX_LENGTH;
|
||||
|
||||
unsigned long outLength = base64DataLength / 4u;
|
||||
m_data = std::make_unique<uint8_t[]>(outLength);
|
||||
const auto result = base64_decode(&uri[URI_PREFIX_LENGTH], base64DataLength, m_data.get(), &outLength);
|
||||
m_data_size = static_cast<size_t>(outLength);
|
||||
|
||||
assert(result == CRYPT_OK);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DataUriBuffer::ReadData(void* dest, const size_t offset, const size_t count) const
|
||||
{
|
||||
assert(m_data);
|
||||
assert(m_data_size > 0u);
|
||||
|
||||
if (offset + count > m_data_size)
|
||||
return false;
|
||||
|
||||
std::memcpy(dest, &m_data[offset], count);
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t DataUriBuffer::GetSize() const
|
||||
{
|
||||
return m_data_size;
|
||||
}
|
54
src/ObjLoading/XModel/Gltf/Internal/GltfBuffer.h
Normal file
54
src/ObjLoading/XModel/Gltf/Internal/GltfBuffer.h
Normal file
@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace gltf
|
||||
{
|
||||
class Buffer
|
||||
{
|
||||
protected:
|
||||
Buffer() = default;
|
||||
|
||||
public:
|
||||
virtual ~Buffer() = default;
|
||||
Buffer(const Buffer& other) = default;
|
||||
Buffer(Buffer&& other) noexcept = default;
|
||||
Buffer& operator=(const Buffer& other) = default;
|
||||
Buffer& operator=(Buffer&& other) noexcept = default;
|
||||
|
||||
virtual bool ReadData(void* dest, size_t offset, size_t count) const = 0;
|
||||
[[nodiscard]] virtual size_t GetSize() const = 0;
|
||||
};
|
||||
|
||||
class EmbeddedBuffer final : public Buffer
|
||||
{
|
||||
public:
|
||||
EmbeddedBuffer(const void* data, size_t dataSize);
|
||||
|
||||
bool ReadData(void* dest, size_t offset, size_t count) const override;
|
||||
[[nodiscard]] size_t GetSize() const override;
|
||||
|
||||
private:
|
||||
const uint8_t* m_data;
|
||||
size_t m_data_size;
|
||||
};
|
||||
|
||||
class DataUriBuffer final : public Buffer
|
||||
{
|
||||
public:
|
||||
DataUriBuffer();
|
||||
|
||||
static bool IsDataUri(const std::string& uri);
|
||||
bool ReadDataFromUri(const std::string& uri);
|
||||
|
||||
bool ReadData(void* dest, size_t offset, size_t count) const override;
|
||||
[[nodiscard]] size_t GetSize() const override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<uint8_t[]> m_data;
|
||||
size_t m_data_size;
|
||||
};
|
||||
} // namespace gltf
|
23
src/ObjLoading/XModel/Gltf/Internal/GltfBufferView.cpp
Normal file
23
src/ObjLoading/XModel/Gltf/Internal/GltfBufferView.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
#include "GltfBufferView.h"
|
||||
|
||||
using namespace gltf;
|
||||
|
||||
BufferView::BufferView(const Buffer* buffer, const size_t offset, const size_t length, const size_t stride)
|
||||
: m_buffer(buffer),
|
||||
m_offset(offset),
|
||||
m_length(length),
|
||||
m_stride(stride)
|
||||
{
|
||||
}
|
||||
|
||||
bool BufferView::ReadElement(void* dest, const size_t elementIndex, const size_t elementSize, const size_t elementOffset) const
|
||||
{
|
||||
const auto stride = std::max(elementOffset + elementSize, m_stride);
|
||||
const auto bufferViewOffset = elementOffset + elementIndex * stride;
|
||||
if (bufferViewOffset + elementSize > m_length)
|
||||
return false;
|
||||
|
||||
const auto bufferOffset = m_offset + bufferViewOffset;
|
||||
|
||||
return m_buffer->ReadData(dest, bufferOffset, elementSize);
|
||||
}
|
19
src/ObjLoading/XModel/Gltf/Internal/GltfBufferView.h
Normal file
19
src/ObjLoading/XModel/Gltf/Internal/GltfBufferView.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "GltfBuffer.h"
|
||||
|
||||
namespace gltf
|
||||
{
|
||||
class BufferView
|
||||
{
|
||||
public:
|
||||
BufferView(const Buffer* buffer, size_t offset, size_t length, size_t stride);
|
||||
|
||||
bool ReadElement(void* dest, size_t elementIndex, size_t elementSize, size_t elementOffset) const;
|
||||
|
||||
private:
|
||||
const Buffer* m_buffer;
|
||||
size_t m_offset;
|
||||
size_t m_length;
|
||||
size_t m_stride;
|
||||
};
|
||||
} // namespace gltf
|
231
src/ObjLoading/XModel/Tangentspace.cpp
Normal file
231
src/ObjLoading/XModel/Tangentspace.cpp
Normal file
@ -0,0 +1,231 @@
|
||||
#include "Tangentspace.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <numbers>
|
||||
|
||||
namespace tangent_space
|
||||
{
|
||||
constexpr float NULL_VEC3[3]{0, 0, 0};
|
||||
|
||||
typedef float tvec2[2];
|
||||
typedef float tvec3[3];
|
||||
|
||||
const tvec2& GetVec2(const void* dest, const size_t index, const size_t stride)
|
||||
{
|
||||
return *reinterpret_cast<const tvec2*>(static_cast<const char*>(dest) + stride * index);
|
||||
}
|
||||
|
||||
const tvec3& GetVec3(const void* dest, const size_t index, const size_t stride)
|
||||
{
|
||||
return *reinterpret_cast<const tvec3*>(static_cast<const char*>(dest) + stride * index);
|
||||
}
|
||||
|
||||
tvec3& GetVec3(void* dest, const size_t index, const size_t stride)
|
||||
{
|
||||
return *reinterpret_cast<tvec3*>(static_cast<char*>(dest) + stride * index);
|
||||
}
|
||||
|
||||
void SetVec3(void* dest, const size_t index, const size_t stride, const tvec3& data)
|
||||
{
|
||||
auto* out = reinterpret_cast<float(*)[3]>(static_cast<char*>(dest) + stride * index);
|
||||
(*out)[0] = data[0];
|
||||
(*out)[1] = data[1];
|
||||
(*out)[2] = data[2];
|
||||
}
|
||||
|
||||
bool Vec3_IsNormalized(const tvec3& a1)
|
||||
{
|
||||
const auto len = a1[0] * a1[0] + a1[1] * a1[1] + a1[2] * a1[2];
|
||||
const auto error = fabs(len - 1.0f);
|
||||
return error < 0.0020000001;
|
||||
}
|
||||
|
||||
float Vec3_Normalize(tvec3& vector)
|
||||
{
|
||||
float length = std::sqrt(vector[0] * vector[0] + vector[1] * vector[1] + vector[2] * vector[2]);
|
||||
if (-length >= 0.0f)
|
||||
length = 1.0f;
|
||||
const auto lengthInv = 1.0f / length;
|
||||
vector[0] = lengthInv * vector[0];
|
||||
vector[1] = lengthInv * vector[1];
|
||||
vector[2] = lengthInv * vector[2];
|
||||
return length;
|
||||
}
|
||||
|
||||
void Vec3_Cross(const tvec3& v0, const tvec3& v1, tvec3& cross)
|
||||
{
|
||||
cross[0] = v0[1] * v1[2] - v0[2] * v1[1];
|
||||
cross[1] = v0[2] * v1[0] - v0[0] * v1[2];
|
||||
cross[2] = v0[0] * v1[1] - v1[0] * v0[1];
|
||||
}
|
||||
|
||||
float AngleBetweenOriginVectors(const tvec3& a1, const tvec3& a2)
|
||||
{
|
||||
const auto v4 = a1[0] * a2[0] + a1[1] * a2[1] + a1[2] * a2[2];
|
||||
if (v4 <= -1.0)
|
||||
return -std::numbers::pi_v<float>;
|
||||
if (v4 >= 1.0)
|
||||
return std::numbers::pi_v<float>;
|
||||
|
||||
return acos(v4);
|
||||
}
|
||||
|
||||
void sub_10022E80(const VertexData& vertexData, const uint16_t i0, const uint16_t i1, const uint16_t i2, tvec3& outVector, tvec3& outCross)
|
||||
{
|
||||
const auto& i0_uv = GetVec2(vertexData.uvData, i0, vertexData.uvDataStride);
|
||||
const auto& i1_uv = GetVec2(vertexData.uvData, i1, vertexData.uvDataStride);
|
||||
const auto& i2_uv = GetVec2(vertexData.uvData, i2, vertexData.uvDataStride);
|
||||
const auto& i0_position = GetVec3(vertexData.positionData, i0, vertexData.positionDataStride);
|
||||
const auto& i1_position = GetVec3(vertexData.positionData, i1, vertexData.positionDataStride);
|
||||
const auto& i2_position = GetVec3(vertexData.positionData, i2, vertexData.positionDataStride);
|
||||
|
||||
const auto uv0_1m0 = i1_uv[0] - i0_uv[0];
|
||||
const auto uv0_2m0 = i2_uv[0] - i0_uv[0];
|
||||
const auto uv1_1m0 = i1_uv[1] - i0_uv[1];
|
||||
const auto uv1_2m0 = i2_uv[1] - i0_uv[1];
|
||||
|
||||
const auto p0_1m0 = i1_position[0] - i0_position[0];
|
||||
const auto p0_2m0 = i2_position[0] - i0_position[0];
|
||||
const auto p1_1m0 = i1_position[1] - i0_position[1];
|
||||
const auto p1_2m0 = i2_position[1] - i0_position[1];
|
||||
const auto p2_1m0 = i1_position[2] - i0_position[2];
|
||||
const auto p2_2m0 = i2_position[2] - i0_position[2];
|
||||
|
||||
if (uv1_2m0 * uv0_1m0 >= uv1_1m0 * uv0_2m0)
|
||||
{
|
||||
outVector[0] = p0_1m0 * uv1_2m0 - p0_2m0 * uv1_1m0;
|
||||
outCross[0] = p0_2m0 * uv0_1m0 - p0_1m0 * uv0_2m0;
|
||||
outVector[1] = p1_1m0 * uv1_2m0 - p1_2m0 * uv1_1m0;
|
||||
outCross[1] = p1_2m0 * uv0_1m0 - p1_1m0 * uv0_2m0;
|
||||
outVector[2] = uv1_2m0 * p2_1m0 - uv1_1m0 * p2_2m0;
|
||||
outCross[2] = uv0_1m0 * p2_2m0 - uv0_2m0 * p2_1m0;
|
||||
}
|
||||
else
|
||||
{
|
||||
outVector[0] = p0_2m0 * uv1_1m0 - p0_1m0 * uv1_2m0;
|
||||
outCross[0] = p0_1m0 * uv0_2m0 - p0_2m0 * uv0_1m0;
|
||||
outVector[1] = p1_2m0 * uv1_1m0 - p1_1m0 * uv1_2m0;
|
||||
outCross[1] = p1_1m0 * uv0_2m0 - p1_2m0 * uv0_1m0;
|
||||
outVector[2] = uv1_1m0 * p2_2m0 - uv1_2m0 * p2_1m0;
|
||||
outCross[2] = uv0_2m0 * p2_1m0 - p2_2m0 * uv0_1m0;
|
||||
}
|
||||
|
||||
Vec3_Normalize(outVector);
|
||||
Vec3_Normalize(outCross);
|
||||
}
|
||||
|
||||
void GetExteriorAnglesOfTri(const VertexData& vertexData, tvec3& outExteriorAngles, const uint16_t i0, const uint16_t i1, const uint16_t i2)
|
||||
{
|
||||
tvec3 L21;
|
||||
tvec3 L02;
|
||||
tvec3 L10;
|
||||
|
||||
const auto* index0Position = GetVec3(vertexData.positionData, i0, vertexData.positionDataStride);
|
||||
const auto* index1Position = GetVec3(vertexData.positionData, i1, vertexData.positionDataStride);
|
||||
const auto* index2Position = GetVec3(vertexData.positionData, i2, vertexData.positionDataStride);
|
||||
L10[0] = index0Position[0] - index1Position[0];
|
||||
L10[1] = index0Position[1] - index1Position[1];
|
||||
L10[2] = index0Position[2] - index1Position[2];
|
||||
L21[0] = index1Position[0] - index2Position[0];
|
||||
L21[1] = index1Position[1] - index2Position[1];
|
||||
L21[2] = index1Position[2] - index2Position[2];
|
||||
L02[0] = index2Position[0] - index0Position[0];
|
||||
L02[1] = index2Position[1] - index0Position[1];
|
||||
L02[2] = index2Position[2] - index0Position[2];
|
||||
Vec3_Normalize(L10);
|
||||
Vec3_Normalize(L21);
|
||||
Vec3_Normalize(L02);
|
||||
outExteriorAngles[0] = AngleBetweenOriginVectors(L10, L02);
|
||||
outExteriorAngles[1] = AngleBetweenOriginVectors(L21, L10);
|
||||
outExteriorAngles[2] = AngleBetweenOriginVectors(L02, L21);
|
||||
}
|
||||
|
||||
void sub_10022CA0(const uint16_t index, void* sourcesDest, const tvec3& ecx0, const float exteriorAngle, const size_t stride)
|
||||
{
|
||||
auto& vec = GetVec3(sourcesDest, index, stride);
|
||||
vec[0] = ecx0[0] * exteriorAngle + vec[0];
|
||||
vec[1] = ecx0[1] * exteriorAngle + vec[1];
|
||||
vec[2] = ecx0[2] * exteriorAngle + vec[2];
|
||||
}
|
||||
|
||||
void sub_10014EE0(const tvec3& src, tvec3& a2)
|
||||
{
|
||||
assert(Vec3_IsNormalized(src));
|
||||
|
||||
const auto v4 = src[0] * src[0];
|
||||
float v5 = src[1] * src[1];
|
||||
float v6 = src[2] * src[2];
|
||||
int v3 = v4 > v5;
|
||||
if (*(&v4 + v3) > v6)
|
||||
v3 = 2;
|
||||
const auto srcc = -src[v3];
|
||||
a2[0] = src[0] * srcc;
|
||||
a2[1] = src[1] * srcc;
|
||||
a2[2] = src[2] * srcc;
|
||||
a2[v3] = a2[v3] + 1.0f;
|
||||
Vec3_Normalize(a2);
|
||||
}
|
||||
|
||||
void CalculateTangentSpace(const VertexData& vertexData, const size_t triCount, const size_t vertexCount)
|
||||
{
|
||||
// Set tangents and binormals to 0
|
||||
for (auto vertexIndex = 0u; vertexIndex < vertexCount; vertexIndex++)
|
||||
{
|
||||
SetVec3(vertexData.tangentData, vertexIndex, vertexData.tangentDataStride, NULL_VEC3);
|
||||
SetVec3(vertexData.binormalData, vertexIndex, vertexData.binormalDataStride, NULL_VEC3);
|
||||
}
|
||||
|
||||
for (auto triIndex = 0u; triIndex < triCount; triIndex++)
|
||||
{
|
||||
const auto i0 = vertexData.triData[triIndex * 3u + 0u];
|
||||
const auto i1 = vertexData.triData[triIndex * 3u + 1u];
|
||||
const auto i2 = vertexData.triData[triIndex * 3u + 2u];
|
||||
|
||||
tvec3 vector, cross, exteriorAngles;
|
||||
sub_10022E80(vertexData, i0, i1, i2, vector, cross);
|
||||
GetExteriorAnglesOfTri(vertexData, exteriorAngles, i0, i1, i2);
|
||||
sub_10022CA0(i0, vertexData.tangentData, vector, exteriorAngles[0], vertexData.tangentDataStride);
|
||||
sub_10022CA0(i1, vertexData.tangentData, vector, exteriorAngles[1], vertexData.tangentDataStride);
|
||||
sub_10022CA0(i2, vertexData.tangentData, vector, exteriorAngles[2], vertexData.tangentDataStride);
|
||||
sub_10022CA0(i0, vertexData.binormalData, cross, exteriorAngles[0], vertexData.binormalDataStride);
|
||||
sub_10022CA0(i1, vertexData.binormalData, cross, exteriorAngles[1], vertexData.binormalDataStride);
|
||||
sub_10022CA0(i2, vertexData.binormalData, cross, exteriorAngles[2], vertexData.binormalDataStride);
|
||||
}
|
||||
|
||||
for (auto vertexIndex = 0u; vertexIndex < vertexCount; vertexIndex++)
|
||||
{
|
||||
const auto& normal = GetVec3(vertexData.normalData, vertexIndex, vertexData.normalDataStride);
|
||||
auto& tangent = GetVec3(vertexData.tangentData, vertexIndex, vertexData.tangentDataStride);
|
||||
auto& binormal = GetVec3(vertexData.binormalData, vertexIndex, vertexData.binormalDataStride);
|
||||
|
||||
const auto dot_normal_tangent = normal[0] * tangent[0] + normal[1] * tangent[1] + normal[2] * tangent[2];
|
||||
|
||||
tangent[0] = normal[0] * -dot_normal_tangent + tangent[0];
|
||||
tangent[1] = normal[1] * -dot_normal_tangent + tangent[1];
|
||||
tangent[2] = normal[2] * -dot_normal_tangent + tangent[2];
|
||||
if (Vec3_Normalize(tangent) < 0.001f)
|
||||
{
|
||||
Vec3_Cross(binormal, normal, tangent);
|
||||
if (Vec3_Normalize(tangent) < 0.001)
|
||||
sub_10014EE0(normal, tangent);
|
||||
}
|
||||
|
||||
tvec3 cross;
|
||||
Vec3_Cross(normal, tangent, cross);
|
||||
const auto sourcesc = binormal[0] * cross[0] + cross[1] * binormal[1] + cross[2] * binormal[2];
|
||||
if (sourcesc >= 0.0)
|
||||
{
|
||||
binormal[0] = cross[0];
|
||||
binormal[1] = cross[1];
|
||||
binormal[2] = cross[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
binormal[0] = -cross[0];
|
||||
binormal[1] = -cross[1];
|
||||
binormal[2] = -cross[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace tangent_space
|
23
src/ObjLoading/XModel/Tangentspace.h
Normal file
23
src/ObjLoading/XModel/Tangentspace.h
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace tangent_space
|
||||
{
|
||||
struct VertexData
|
||||
{
|
||||
const void* positionData;
|
||||
size_t positionDataStride;
|
||||
const void* normalData;
|
||||
size_t normalDataStride;
|
||||
const void* uvData;
|
||||
size_t uvDataStride;
|
||||
void* tangentData;
|
||||
size_t tangentDataStride;
|
||||
void* binormalData;
|
||||
size_t binormalDataStride;
|
||||
const uint16_t* triData;
|
||||
};
|
||||
|
||||
void CalculateTangentSpace(const VertexData& vertexData, size_t triCount, size_t vertexCount);
|
||||
} // namespace tangent_space
|
18
src/ObjLoading/XModel/XModelLoader.h
Normal file
18
src/ObjLoading/XModel/XModelLoader.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "XModel/XModelCommon.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
class XModelLoader
|
||||
{
|
||||
public:
|
||||
XModelLoader() = default;
|
||||
virtual ~XModelLoader() = default;
|
||||
XModelLoader(const XModelLoader& other) = default;
|
||||
XModelLoader(XModelLoader&& other) noexcept = default;
|
||||
XModelLoader& operator=(const XModelLoader& other) = default;
|
||||
XModelLoader& operator=(XModelLoader&& other) noexcept = default;
|
||||
|
||||
virtual std::unique_ptr<XModelCommon> Load() = 0;
|
||||
};
|
@ -112,10 +112,10 @@ namespace
|
||||
else
|
||||
bone.name = "INVALID_BONE_NAME";
|
||||
|
||||
if (boneNum < model->numRootBones)
|
||||
bone.parentIndex = -1;
|
||||
if (boneNum >= model->numRootBones)
|
||||
bone.parentIndex = boneNum - static_cast<unsigned int>(model->parentList[boneNum - model->numRootBones]);
|
||||
else
|
||||
bone.parentIndex = static_cast<int>(boneNum - static_cast<unsigned int>(model->parentList[boneNum - model->numRootBones]));
|
||||
bone.parentIndex = std::nullopt;
|
||||
|
||||
bone.scale[0] = 1.0f;
|
||||
bone.scale[1] = 1.0f;
|
||||
@ -218,27 +218,14 @@ namespace
|
||||
for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++)
|
||||
{
|
||||
const auto& v = surface.verts0[vertexIndex];
|
||||
vec2_t uv;
|
||||
vec3_t normalVec;
|
||||
vec4_t color;
|
||||
|
||||
Common::Vec2UnpackTexCoords(v.texCoord, &uv);
|
||||
Common::Vec3UnpackUnitVec(v.normal, &normalVec);
|
||||
Common::Vec4UnpackGfxColor(v.color, &color);
|
||||
|
||||
XModelVertex vertex{};
|
||||
vertex.coordinates[0] = v.xyz[0];
|
||||
vertex.coordinates[1] = v.xyz[1];
|
||||
vertex.coordinates[2] = v.xyz[2];
|
||||
vertex.normal[0] = normalVec[0];
|
||||
vertex.normal[1] = normalVec[1];
|
||||
vertex.normal[2] = normalVec[2];
|
||||
vertex.color[0] = color[0];
|
||||
vertex.color[1] = color[1];
|
||||
vertex.color[2] = color[2];
|
||||
vertex.color[3] = color[3];
|
||||
vertex.uv[0] = uv[0];
|
||||
vertex.uv[1] = uv[1];
|
||||
Common::Vec3UnpackUnitVec(v.normal, vertex.normal);
|
||||
Common::Vec4UnpackGfxColor(v.color, vertex.color);
|
||||
Common::Vec2UnpackTexCoords(v.texCoord, vertex.uv);
|
||||
|
||||
out.m_vertices.emplace_back(vertex);
|
||||
}
|
||||
@ -295,9 +282,9 @@ namespace
|
||||
for (auto vertListIndex = 0u; vertListIndex < surface.vertListCount; vertListIndex++)
|
||||
{
|
||||
const auto& vertList = surface.vertList[vertListIndex];
|
||||
const auto* boneWeightOffset = &weightCollection.weights[weightOffset];
|
||||
const auto boneWeightOffset = weightOffset;
|
||||
|
||||
weightCollection.weights[weightOffset++] = XModelBoneWeight{static_cast<int>(vertList.boneOffset / sizeof(DObjSkelMat)), 1.0f};
|
||||
weightCollection.weights[weightOffset++] = XModelBoneWeight{vertList.boneOffset / sizeof(DObjSkelMat), 1.0f};
|
||||
|
||||
for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++)
|
||||
{
|
||||
@ -313,8 +300,8 @@ namespace
|
||||
// 1 bone weight
|
||||
for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[0]; vertIndex++)
|
||||
{
|
||||
const auto* boneWeightOffset = &weightCollection.weights[weightOffset];
|
||||
const auto boneIndex0 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat));
|
||||
const auto boneWeightOffset = weightOffset;
|
||||
const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat);
|
||||
weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, 1.0f};
|
||||
|
||||
vertsBlendOffset += 1;
|
||||
@ -325,9 +312,9 @@ namespace
|
||||
// 2 bone weights
|
||||
for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++)
|
||||
{
|
||||
const auto* boneWeightOffset = &weightCollection.weights[weightOffset];
|
||||
const auto boneIndex0 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex1 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat));
|
||||
const auto boneWeightOffset = weightOffset;
|
||||
const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat);
|
||||
const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]);
|
||||
const auto boneWeight0 = 1.0f - boneWeight1;
|
||||
|
||||
@ -342,11 +329,11 @@ namespace
|
||||
// 3 bone weights
|
||||
for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++)
|
||||
{
|
||||
const auto* boneWeightOffset = &weightCollection.weights[weightOffset];
|
||||
const auto boneIndex0 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex1 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat));
|
||||
const auto boneWeightOffset = weightOffset;
|
||||
const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat);
|
||||
const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]);
|
||||
const auto boneIndex2 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]);
|
||||
const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2;
|
||||
|
||||
@ -362,13 +349,13 @@ namespace
|
||||
// 4 bone weights
|
||||
for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++)
|
||||
{
|
||||
const auto* boneWeightOffset = &weightCollection.weights[weightOffset];
|
||||
const auto boneIndex0 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex1 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat));
|
||||
const auto boneWeightOffset = weightOffset;
|
||||
const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat);
|
||||
const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]);
|
||||
const auto boneIndex2 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]);
|
||||
const auto boneIndex3 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex3 = surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight3 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 6]);
|
||||
const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2 - boneWeight3;
|
||||
|
||||
@ -388,7 +375,7 @@ namespace
|
||||
|
||||
for (; handledVertices < surface.vertCount; handledVertices++)
|
||||
{
|
||||
out.m_vertex_bone_weights.emplace_back(nullptr, 0);
|
||||
out.m_vertex_bone_weights.emplace_back(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -107,10 +107,10 @@ namespace
|
||||
else
|
||||
bone.name = "INVALID_BONE_NAME";
|
||||
|
||||
if (boneNum < model->numRootBones)
|
||||
bone.parentIndex = -1;
|
||||
if (boneNum >= model->numRootBones)
|
||||
bone.parentIndex = boneNum - static_cast<unsigned int>(model->parentList[boneNum - model->numRootBones]);
|
||||
else
|
||||
bone.parentIndex = static_cast<int>(boneNum - static_cast<unsigned int>(model->parentList[boneNum - model->numRootBones]));
|
||||
bone.parentIndex = std::nullopt;
|
||||
|
||||
bone.scale[0] = 1.0f;
|
||||
bone.scale[1] = 1.0f;
|
||||
@ -207,27 +207,14 @@ namespace
|
||||
for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++)
|
||||
{
|
||||
const auto& v = surface.verts0[vertexIndex];
|
||||
vec2_t uv;
|
||||
vec3_t normalVec;
|
||||
vec4_t color;
|
||||
|
||||
Common::Vec2UnpackTexCoords(v.texCoord, &uv);
|
||||
Common::Vec3UnpackUnitVec(v.normal, &normalVec);
|
||||
Common::Vec4UnpackGfxColor(v.color, &color);
|
||||
|
||||
XModelVertex vertex{};
|
||||
vertex.coordinates[0] = v.xyz[0];
|
||||
vertex.coordinates[1] = v.xyz[1];
|
||||
vertex.coordinates[2] = v.xyz[2];
|
||||
vertex.normal[0] = normalVec[0];
|
||||
vertex.normal[1] = normalVec[1];
|
||||
vertex.normal[2] = normalVec[2];
|
||||
vertex.color[0] = color[0];
|
||||
vertex.color[1] = color[1];
|
||||
vertex.color[2] = color[2];
|
||||
vertex.color[3] = color[3];
|
||||
vertex.uv[0] = uv[0];
|
||||
vertex.uv[1] = uv[1];
|
||||
Common::Vec3UnpackUnitVec(v.normal, vertex.normal);
|
||||
Common::Vec4UnpackGfxColor(v.color, vertex.color);
|
||||
Common::Vec2UnpackTexCoords(v.texCoord, vertex.uv);
|
||||
|
||||
out.m_vertices.emplace_back(vertex);
|
||||
}
|
||||
@ -278,9 +265,9 @@ namespace
|
||||
for (auto vertListIndex = 0u; vertListIndex < surface.vertListCount; vertListIndex++)
|
||||
{
|
||||
const auto& vertList = surface.vertList[vertListIndex];
|
||||
const auto* boneWeightOffset = &weightCollection.weights[weightOffset];
|
||||
const auto boneWeightOffset = weightOffset;
|
||||
|
||||
weightCollection.weights[weightOffset++] = XModelBoneWeight{static_cast<int>(vertList.boneOffset / sizeof(DObjSkelMat)), 1.0f};
|
||||
weightCollection.weights[weightOffset++] = XModelBoneWeight{vertList.boneOffset / sizeof(DObjSkelMat), 1.0f};
|
||||
|
||||
for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++)
|
||||
{
|
||||
@ -296,8 +283,8 @@ namespace
|
||||
// 1 bone weight
|
||||
for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[0]; vertIndex++)
|
||||
{
|
||||
const auto* boneWeightOffset = &weightCollection.weights[weightOffset];
|
||||
const auto boneIndex0 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat));
|
||||
const auto boneWeightOffset = weightOffset;
|
||||
const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat);
|
||||
weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, 1.0f};
|
||||
|
||||
vertsBlendOffset += 1;
|
||||
@ -308,9 +295,9 @@ namespace
|
||||
// 2 bone weights
|
||||
for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++)
|
||||
{
|
||||
const auto* boneWeightOffset = &weightCollection.weights[weightOffset];
|
||||
const auto boneIndex0 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex1 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat));
|
||||
const auto boneWeightOffset = weightOffset;
|
||||
const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat);
|
||||
const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]);
|
||||
const auto boneWeight0 = 1.0f - boneWeight1;
|
||||
|
||||
@ -325,11 +312,11 @@ namespace
|
||||
// 3 bone weights
|
||||
for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++)
|
||||
{
|
||||
const auto* boneWeightOffset = &weightCollection.weights[weightOffset];
|
||||
const auto boneIndex0 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex1 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat));
|
||||
const auto boneWeightOffset = weightOffset;
|
||||
const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat);
|
||||
const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]);
|
||||
const auto boneIndex2 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]);
|
||||
const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2;
|
||||
|
||||
@ -345,13 +332,13 @@ namespace
|
||||
// 4 bone weights
|
||||
for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++)
|
||||
{
|
||||
const auto* boneWeightOffset = &weightCollection.weights[weightOffset];
|
||||
const auto boneIndex0 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex1 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat));
|
||||
const auto boneWeightOffset = weightOffset;
|
||||
const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat);
|
||||
const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]);
|
||||
const auto boneIndex2 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]);
|
||||
const auto boneIndex3 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex3 = surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight3 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 6]);
|
||||
const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2 - boneWeight3;
|
||||
|
||||
@ -371,7 +358,7 @@ namespace
|
||||
|
||||
for (; handledVertices < surface.vertCount; handledVertices++)
|
||||
{
|
||||
out.m_vertex_bone_weights.emplace_back(nullptr, 0);
|
||||
out.m_vertex_bone_weights.emplace_back(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -652,7 +652,7 @@ void AssetDumperWeapon::CopyToFullDef(const WeaponCompleteDef* weapon, WeaponFul
|
||||
|
||||
if (fullDef->weapDef.locationDamageMultipliers)
|
||||
{
|
||||
static_assert(std::extent_v<decltype(WeaponFullDef::locationDamageMultipliers)> == HITLOC_NUM);
|
||||
static_assert(std::extent_v<decltype(WeaponFullDef::locationDamageMultipliers)> == HITLOC_COUNT);
|
||||
assert(sizeof(WeaponFullDef::locationDamageMultipliers) >= sizeof(float) * std::extent_v<decltype(WeaponFullDef::locationDamageMultipliers)>);
|
||||
memcpy(fullDef->locationDamageMultipliers,
|
||||
fullDef->weapDef.locationDamageMultipliers,
|
||||
|
@ -107,10 +107,10 @@ namespace
|
||||
else
|
||||
bone.name = "INVALID_BONE_NAME";
|
||||
|
||||
if (boneNum < model->numRootBones)
|
||||
bone.parentIndex = -1;
|
||||
if (boneNum >= model->numRootBones)
|
||||
bone.parentIndex = boneNum - static_cast<unsigned int>(model->parentList[boneNum - model->numRootBones]);
|
||||
else
|
||||
bone.parentIndex = static_cast<int>(boneNum - static_cast<unsigned int>(model->parentList[boneNum - model->numRootBones]));
|
||||
bone.parentIndex = std::nullopt;
|
||||
|
||||
bone.scale[0] = 1.0f;
|
||||
bone.scale[1] = 1.0f;
|
||||
@ -207,27 +207,14 @@ namespace
|
||||
for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++)
|
||||
{
|
||||
const auto& v = surface.verts0.packedVerts0[vertexIndex];
|
||||
vec2_t uv;
|
||||
vec3_t normalVec;
|
||||
vec4_t color;
|
||||
|
||||
Common::Vec2UnpackTexCoords(v.texCoord, &uv);
|
||||
Common::Vec3UnpackUnitVec(v.normal, &normalVec);
|
||||
Common::Vec4UnpackGfxColor(v.color, &color);
|
||||
|
||||
XModelVertex vertex{};
|
||||
vertex.coordinates[0] = v.xyz[0];
|
||||
vertex.coordinates[1] = v.xyz[1];
|
||||
vertex.coordinates[2] = v.xyz[2];
|
||||
vertex.normal[0] = normalVec[0];
|
||||
vertex.normal[1] = normalVec[1];
|
||||
vertex.normal[2] = normalVec[2];
|
||||
vertex.color[0] = color[0];
|
||||
vertex.color[1] = color[1];
|
||||
vertex.color[2] = color[2];
|
||||
vertex.color[3] = color[3];
|
||||
vertex.uv[0] = uv[0];
|
||||
vertex.uv[1] = uv[1];
|
||||
Common::Vec3UnpackUnitVec(v.normal, vertex.normal);
|
||||
Common::Vec4UnpackGfxColor(v.color, vertex.color);
|
||||
Common::Vec2UnpackTexCoords(v.texCoord, vertex.uv);
|
||||
|
||||
out.m_vertices.emplace_back(vertex);
|
||||
}
|
||||
@ -278,9 +265,9 @@ namespace
|
||||
for (auto vertListIndex = 0u; vertListIndex < surface.vertListCount; vertListIndex++)
|
||||
{
|
||||
const auto& vertList = surface.vertList[vertListIndex];
|
||||
const auto* boneWeightOffset = &weightCollection.weights[weightOffset];
|
||||
const auto boneWeightOffset = weightOffset;
|
||||
|
||||
weightCollection.weights[weightOffset++] = XModelBoneWeight{static_cast<int>(vertList.boneOffset / sizeof(DObjSkelMat)), 1.0f};
|
||||
weightCollection.weights[weightOffset++] = XModelBoneWeight{vertList.boneOffset / sizeof(DObjSkelMat), 1.0f};
|
||||
|
||||
for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++)
|
||||
{
|
||||
@ -296,8 +283,8 @@ namespace
|
||||
// 1 bone weight
|
||||
for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[0]; vertIndex++)
|
||||
{
|
||||
const auto* boneWeightOffset = &weightCollection.weights[weightOffset];
|
||||
const auto boneIndex0 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat));
|
||||
const auto boneWeightOffset = weightOffset;
|
||||
const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat);
|
||||
weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, 1.0f};
|
||||
|
||||
vertsBlendOffset += 1;
|
||||
@ -308,9 +295,9 @@ namespace
|
||||
// 2 bone weights
|
||||
for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++)
|
||||
{
|
||||
const auto* boneWeightOffset = &weightCollection.weights[weightOffset];
|
||||
const auto boneIndex0 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex1 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat));
|
||||
const auto boneWeightOffset = weightOffset;
|
||||
const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat);
|
||||
const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]);
|
||||
const auto boneWeight0 = 1.0f - boneWeight1;
|
||||
|
||||
@ -325,11 +312,11 @@ namespace
|
||||
// 3 bone weights
|
||||
for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++)
|
||||
{
|
||||
const auto* boneWeightOffset = &weightCollection.weights[weightOffset];
|
||||
const auto boneIndex0 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex1 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat));
|
||||
const auto boneWeightOffset = weightOffset;
|
||||
const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat);
|
||||
const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]);
|
||||
const auto boneIndex2 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]);
|
||||
const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2;
|
||||
|
||||
@ -345,13 +332,13 @@ namespace
|
||||
// 4 bone weights
|
||||
for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++)
|
||||
{
|
||||
const auto* boneWeightOffset = &weightCollection.weights[weightOffset];
|
||||
const auto boneIndex0 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex1 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat));
|
||||
const auto boneWeightOffset = weightOffset;
|
||||
const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat);
|
||||
const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]);
|
||||
const auto boneIndex2 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]);
|
||||
const auto boneIndex3 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex3 = surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight3 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 6]);
|
||||
const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2 - boneWeight3;
|
||||
|
||||
@ -371,7 +358,7 @@ namespace
|
||||
|
||||
for (; handledVertices < surface.vertCount; handledVertices++)
|
||||
{
|
||||
out.m_vertex_bone_weights.emplace_back(nullptr, 0);
|
||||
out.m_vertex_bone_weights.emplace_back(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -111,10 +111,10 @@ namespace
|
||||
else
|
||||
bone.name = "INVALID_BONE_NAME";
|
||||
|
||||
if (boneNum < model->numRootBones)
|
||||
bone.parentIndex = -1;
|
||||
if (boneNum >= model->numRootBones)
|
||||
bone.parentIndex = boneNum - static_cast<unsigned int>(model->parentList[boneNum - model->numRootBones]);
|
||||
else
|
||||
bone.parentIndex = static_cast<int>(boneNum - static_cast<unsigned int>(model->parentList[boneNum - model->numRootBones]));
|
||||
bone.parentIndex = std::nullopt;
|
||||
|
||||
bone.scale[0] = 1.0f;
|
||||
bone.scale[1] = 1.0f;
|
||||
@ -139,14 +139,17 @@ namespace
|
||||
}
|
||||
else
|
||||
{
|
||||
bone.localOffset[0] = model->trans[boneNum - model->numRootBones][0];
|
||||
bone.localOffset[1] = model->trans[boneNum - model->numRootBones][1];
|
||||
bone.localOffset[2] = model->trans[boneNum - model->numRootBones][2];
|
||||
const auto* trans = &model->trans[(boneNum - model->numRootBones) * 3];
|
||||
bone.localOffset[0] = trans[0];
|
||||
bone.localOffset[1] = trans[1];
|
||||
bone.localOffset[2] = trans[2];
|
||||
|
||||
const auto& quat = model->quats[boneNum - model->numRootBones];
|
||||
bone.localRotation = {
|
||||
QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][0]),
|
||||
QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][1]),
|
||||
QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][2]),
|
||||
QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][3]),
|
||||
QuatInt16::ToFloat(quat.v[0]),
|
||||
QuatInt16::ToFloat(quat.v[1]),
|
||||
QuatInt16::ToFloat(quat.v[2]),
|
||||
QuatInt16::ToFloat(quat.v[3]),
|
||||
};
|
||||
}
|
||||
|
||||
@ -217,27 +220,14 @@ namespace
|
||||
for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++)
|
||||
{
|
||||
const auto& v = surface.verts0[vertexIndex];
|
||||
vec2_t uv;
|
||||
vec3_t normalVec;
|
||||
vec4_t color;
|
||||
|
||||
Common::Vec2UnpackTexCoords(v.texCoord, &uv);
|
||||
Common::Vec3UnpackUnitVec(v.normal, &normalVec);
|
||||
Common::Vec4UnpackGfxColor(v.color, &color);
|
||||
|
||||
XModelVertex vertex{};
|
||||
vertex.coordinates[0] = v.xyz[0];
|
||||
vertex.coordinates[1] = v.xyz[1];
|
||||
vertex.coordinates[2] = v.xyz[2];
|
||||
vertex.normal[0] = normalVec[0];
|
||||
vertex.normal[1] = normalVec[1];
|
||||
vertex.normal[2] = normalVec[2];
|
||||
vertex.color[0] = color[0];
|
||||
vertex.color[1] = color[1];
|
||||
vertex.color[2] = color[2];
|
||||
vertex.color[3] = color[3];
|
||||
vertex.uv[0] = uv[0];
|
||||
vertex.uv[1] = uv[1];
|
||||
Common::Vec3UnpackUnitVec(v.normal, vertex.normal);
|
||||
Common::Vec4UnpackGfxColor(v.color, vertex.color);
|
||||
Common::Vec2UnpackTexCoords(v.texCoord, vertex.uv);
|
||||
|
||||
out.m_vertices.emplace_back(vertex);
|
||||
}
|
||||
@ -294,9 +284,9 @@ namespace
|
||||
for (auto vertListIndex = 0u; vertListIndex < surface.vertListCount; vertListIndex++)
|
||||
{
|
||||
const auto& vertList = surface.vertList[vertListIndex];
|
||||
const auto* boneWeightOffset = &weightCollection.weights[weightOffset];
|
||||
const auto boneWeightOffset = weightOffset;
|
||||
|
||||
weightCollection.weights[weightOffset++] = XModelBoneWeight{static_cast<int>(vertList.boneOffset / sizeof(DObjSkelMat)), 1.0f};
|
||||
weightCollection.weights[weightOffset++] = XModelBoneWeight{vertList.boneOffset / sizeof(DObjSkelMat), 1.0f};
|
||||
|
||||
for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++)
|
||||
{
|
||||
@ -312,8 +302,8 @@ namespace
|
||||
// 1 bone weight
|
||||
for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[0]; vertIndex++)
|
||||
{
|
||||
const auto* boneWeightOffset = &weightCollection.weights[weightOffset];
|
||||
const auto boneIndex0 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat));
|
||||
const auto boneWeightOffset = weightOffset;
|
||||
const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat);
|
||||
weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, 1.0f};
|
||||
|
||||
vertsBlendOffset += 1;
|
||||
@ -324,9 +314,9 @@ namespace
|
||||
// 2 bone weights
|
||||
for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++)
|
||||
{
|
||||
const auto* boneWeightOffset = &weightCollection.weights[weightOffset];
|
||||
const auto boneIndex0 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex1 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat));
|
||||
const auto boneWeightOffset = weightOffset;
|
||||
const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat);
|
||||
const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]);
|
||||
const auto boneWeight0 = 1.0f - boneWeight1;
|
||||
|
||||
@ -341,11 +331,11 @@ namespace
|
||||
// 3 bone weights
|
||||
for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++)
|
||||
{
|
||||
const auto* boneWeightOffset = &weightCollection.weights[weightOffset];
|
||||
const auto boneIndex0 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex1 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat));
|
||||
const auto boneWeightOffset = weightOffset;
|
||||
const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat);
|
||||
const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]);
|
||||
const auto boneIndex2 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]);
|
||||
const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2;
|
||||
|
||||
@ -361,13 +351,13 @@ namespace
|
||||
// 4 bone weights
|
||||
for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++)
|
||||
{
|
||||
const auto* boneWeightOffset = &weightCollection.weights[weightOffset];
|
||||
const auto boneIndex0 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex1 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat));
|
||||
const auto boneWeightOffset = weightOffset;
|
||||
const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat);
|
||||
const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]);
|
||||
const auto boneIndex2 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]);
|
||||
const auto boneIndex3 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex3 = surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight3 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 6]);
|
||||
const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2 - boneWeight3;
|
||||
|
||||
@ -387,7 +377,7 @@ namespace
|
||||
|
||||
for (; handledVertices < surface.vertCount; handledVertices++)
|
||||
{
|
||||
out.m_vertex_bone_weights.emplace_back(nullptr, 0);
|
||||
out.m_vertex_bone_weights.emplace_back(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -397,8 +397,8 @@ void AssetDumperWeapon::CopyToFullDef(const WeaponVariantDef* weapon, WeaponFull
|
||||
|
||||
if (fullDef->weapDef.locationDamageMultipliers)
|
||||
{
|
||||
assert(sizeof(WeaponFullDef::locationDamageMultipliers) >= sizeof(float) * HITLOC_NUM);
|
||||
memcpy(fullDef->locationDamageMultipliers, fullDef->weapDef.locationDamageMultipliers, sizeof(float) * HITLOC_NUM);
|
||||
assert(sizeof(WeaponFullDef::locationDamageMultipliers) >= sizeof(float) * HITLOC_COUNT);
|
||||
memcpy(fullDef->locationDamageMultipliers, fullDef->weapDef.locationDamageMultipliers, sizeof(float) * HITLOC_COUNT);
|
||||
fullDef->weapDef.locationDamageMultipliers = fullDef->locationDamageMultipliers;
|
||||
}
|
||||
|
||||
|
@ -101,8 +101,8 @@ void AssetDumperWeaponAttachmentUnique::CopyToFullDef(const WeaponAttachmentUniq
|
||||
|
||||
if (attachment->locationDamageMultipliers)
|
||||
{
|
||||
assert(sizeof(WeaponAttachmentUniqueFull::locationDamageMultipliers) >= sizeof(float) * HITLOC_NUM);
|
||||
memcpy(fullDef->locationDamageMultipliers, attachment->locationDamageMultipliers, sizeof(float) * HITLOC_NUM);
|
||||
assert(sizeof(WeaponAttachmentUniqueFull::locationDamageMultipliers) >= sizeof(float) * HITLOC_COUNT);
|
||||
memcpy(fullDef->locationDamageMultipliers, attachment->locationDamageMultipliers, sizeof(float) * HITLOC_COUNT);
|
||||
fullDef->attachment.locationDamageMultipliers = fullDef->locationDamageMultipliers;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "AssetDumperXModel.h"
|
||||
|
||||
#include "Game/T6/CommonT6.h"
|
||||
#include "Game/T6/XModel/JsonXModelWriter.h"
|
||||
#include "ObjWriting.h"
|
||||
#include "Utils/DistinctMapper.h"
|
||||
#include "Utils/QuatInt16.h"
|
||||
@ -123,23 +124,24 @@ namespace
|
||||
else
|
||||
bone.name = "INVALID_BONE_NAME";
|
||||
|
||||
if (boneNum < model->numRootBones)
|
||||
bone.parentIndex = -1;
|
||||
else
|
||||
if (boneNum >= model->numRootBones)
|
||||
bone.parentIndex = static_cast<int>(boneNum - static_cast<unsigned int>(model->parentList[boneNum - model->numRootBones]));
|
||||
else
|
||||
bone.parentIndex = std::nullopt;
|
||||
|
||||
bone.scale[0] = 1.0f;
|
||||
bone.scale[1] = 1.0f;
|
||||
bone.scale[2] = 1.0f;
|
||||
|
||||
bone.globalOffset[0] = model->baseMat[boneNum].trans.x;
|
||||
bone.globalOffset[1] = model->baseMat[boneNum].trans.y;
|
||||
bone.globalOffset[2] = model->baseMat[boneNum].trans.z;
|
||||
const auto& baseMat = model->baseMat[boneNum];
|
||||
bone.globalOffset[0] = baseMat.trans.x;
|
||||
bone.globalOffset[1] = baseMat.trans.y;
|
||||
bone.globalOffset[2] = baseMat.trans.z;
|
||||
bone.globalRotation = {
|
||||
model->baseMat[boneNum].quat.x,
|
||||
model->baseMat[boneNum].quat.y,
|
||||
model->baseMat[boneNum].quat.z,
|
||||
model->baseMat[boneNum].quat.w,
|
||||
baseMat.quat.x,
|
||||
baseMat.quat.y,
|
||||
baseMat.quat.z,
|
||||
baseMat.quat.w,
|
||||
};
|
||||
|
||||
if (boneNum < model->numRootBones)
|
||||
@ -151,14 +153,17 @@ namespace
|
||||
}
|
||||
else
|
||||
{
|
||||
bone.localOffset[0] = model->trans[boneNum - model->numRootBones][0];
|
||||
bone.localOffset[1] = model->trans[boneNum - model->numRootBones][1];
|
||||
bone.localOffset[2] = model->trans[boneNum - model->numRootBones][2];
|
||||
const auto* trans = &model->trans[(boneNum - model->numRootBones) * 3];
|
||||
bone.localOffset[0] = trans[0];
|
||||
bone.localOffset[1] = trans[1];
|
||||
bone.localOffset[2] = trans[2];
|
||||
|
||||
const auto& quat = model->quats[boneNum - model->numRootBones];
|
||||
bone.localRotation = {
|
||||
QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][0]),
|
||||
QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][1]),
|
||||
QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][2]),
|
||||
QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][3]),
|
||||
QuatInt16::ToFloat(quat.v[0]),
|
||||
QuatInt16::ToFloat(quat.v[1]),
|
||||
QuatInt16::ToFloat(quat.v[2]),
|
||||
QuatInt16::ToFloat(quat.v[3]),
|
||||
};
|
||||
}
|
||||
|
||||
@ -232,27 +237,14 @@ namespace
|
||||
for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++)
|
||||
{
|
||||
const auto& v = surface.verts0[vertexIndex];
|
||||
vec2_t uv{};
|
||||
vec3_t normalVec{};
|
||||
vec4_t color{};
|
||||
|
||||
Common::Vec2UnpackTexCoords(v.texCoord, &uv);
|
||||
Common::Vec3UnpackUnitVec(v.normal, &normalVec);
|
||||
Common::Vec4UnpackGfxColor(v.color, &color);
|
||||
|
||||
XModelVertex vertex{};
|
||||
vertex.coordinates[0] = v.xyz.x;
|
||||
vertex.coordinates[1] = v.xyz.y;
|
||||
vertex.coordinates[2] = v.xyz.z;
|
||||
vertex.normal[0] = normalVec.x;
|
||||
vertex.normal[1] = normalVec.y;
|
||||
vertex.normal[2] = normalVec.z;
|
||||
vertex.color[0] = color.x;
|
||||
vertex.color[1] = color.y;
|
||||
vertex.color[2] = color.z;
|
||||
vertex.color[3] = color.w;
|
||||
vertex.uv[0] = uv.x;
|
||||
vertex.uv[1] = uv.y;
|
||||
Common::Vec3UnpackUnitVec(v.normal, vertex.normal);
|
||||
Common::Vec4UnpackGfxColor(v.color, vertex.color);
|
||||
Common::Vec2UnpackTexCoords(v.texCoord, vertex.uv);
|
||||
|
||||
out.m_vertices.emplace_back(vertex);
|
||||
}
|
||||
@ -315,9 +307,9 @@ namespace
|
||||
for (auto vertListIndex = 0u; vertListIndex < surface.vertListCount; vertListIndex++)
|
||||
{
|
||||
const auto& vertList = surface.vertList[vertListIndex];
|
||||
const auto* boneWeightOffset = &weightCollection.weights[weightOffset];
|
||||
const auto boneWeightOffset = weightOffset;
|
||||
|
||||
weightCollection.weights[weightOffset++] = XModelBoneWeight{static_cast<int>(vertList.boneOffset / sizeof(DObjSkelMat)), 1.0f};
|
||||
weightCollection.weights[weightOffset++] = XModelBoneWeight{vertList.boneOffset / sizeof(DObjSkelMat), 1.0f};
|
||||
|
||||
for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++)
|
||||
{
|
||||
@ -333,8 +325,8 @@ namespace
|
||||
// 1 bone weight
|
||||
for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[0]; vertIndex++)
|
||||
{
|
||||
const auto* boneWeightOffset = &weightCollection.weights[weightOffset];
|
||||
const auto boneIndex0 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat));
|
||||
const auto boneWeightOffset = weightOffset;
|
||||
const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat);
|
||||
weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, 1.0f};
|
||||
|
||||
vertsBlendOffset += 1;
|
||||
@ -345,9 +337,9 @@ namespace
|
||||
// 2 bone weights
|
||||
for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++)
|
||||
{
|
||||
const auto* boneWeightOffset = &weightCollection.weights[weightOffset];
|
||||
const auto boneIndex0 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex1 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat));
|
||||
const auto boneWeightOffset = weightOffset;
|
||||
const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat);
|
||||
const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]);
|
||||
const auto boneWeight0 = 1.0f - boneWeight1;
|
||||
|
||||
@ -362,11 +354,11 @@ namespace
|
||||
// 3 bone weights
|
||||
for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++)
|
||||
{
|
||||
const auto* boneWeightOffset = &weightCollection.weights[weightOffset];
|
||||
const auto boneIndex0 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex1 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat));
|
||||
const auto boneWeightOffset = weightOffset;
|
||||
const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat);
|
||||
const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]);
|
||||
const auto boneIndex2 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]);
|
||||
const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2;
|
||||
|
||||
@ -382,13 +374,13 @@ namespace
|
||||
// 4 bone weights
|
||||
for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++)
|
||||
{
|
||||
const auto* boneWeightOffset = &weightCollection.weights[weightOffset];
|
||||
const auto boneIndex0 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex1 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat));
|
||||
const auto boneWeightOffset = weightOffset;
|
||||
const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat);
|
||||
const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]);
|
||||
const auto boneIndex2 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]);
|
||||
const auto boneIndex3 = static_cast<int>(surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat));
|
||||
const auto boneIndex3 = surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat);
|
||||
const auto boneWeight3 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 6]);
|
||||
const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2 - boneWeight3;
|
||||
|
||||
@ -408,7 +400,7 @@ namespace
|
||||
|
||||
for (; handledVertices < surface.vertCount; handledVertices++)
|
||||
{
|
||||
out.m_vertex_bone_weights.emplace_back(nullptr, 0);
|
||||
out.m_vertex_bone_weights.emplace_back(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -432,9 +424,9 @@ namespace
|
||||
const auto& tri = surface.triIndices[triIndex];
|
||||
|
||||
XModelFace face{};
|
||||
face.vertexIndex[0] = tri[0] + surface.baseVertIndex;
|
||||
face.vertexIndex[1] = tri[1] + surface.baseVertIndex;
|
||||
face.vertexIndex[2] = tri[2] + surface.baseVertIndex;
|
||||
face.vertexIndex[0] = tri.i[0] + surface.baseVertIndex;
|
||||
face.vertexIndex[1] = tri.i[1] + surface.baseVertIndex;
|
||||
face.vertexIndex[2] = tri.i[2] + surface.baseVertIndex;
|
||||
object.m_faces.emplace_back(face);
|
||||
}
|
||||
}
|
||||
@ -546,6 +538,15 @@ namespace
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DumpXModel(AssetDumpingContext& context, XAssetInfo<XModel>* asset)
|
||||
{
|
||||
const auto assetFile = context.OpenAssetFile(std::format("xmodel/{}.json", asset->m_name));
|
||||
if (!assetFile)
|
||||
return;
|
||||
|
||||
DumpXModelAsJson(*assetFile, asset->Asset(), context);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool AssetDumperXModel::ShouldDump(XAssetInfo<XModel>* asset)
|
||||
@ -556,4 +557,5 @@ bool AssetDumperXModel::ShouldDump(XAssetInfo<XModel>* asset)
|
||||
void AssetDumperXModel::DumpAsset(AssetDumpingContext& context, XAssetInfo<XModel>* asset)
|
||||
{
|
||||
DumpXModelSurfs(context, asset);
|
||||
DumpXModel(context, asset);
|
||||
}
|
||||
|
@ -2,7 +2,10 @@
|
||||
|
||||
#include "Game/T6/CommonT6.h"
|
||||
#include "Game/T6/Json/JsonXModel.h"
|
||||
#include "ObjWriting.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <format>
|
||||
#include <iomanip>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@ -40,10 +43,38 @@ namespace
|
||||
return input;
|
||||
}
|
||||
|
||||
void CreateJsonXModel(JsonXModel& jXModel, const XModel& xmodel) const
|
||||
static const char* GetExtensionForModelByConfig()
|
||||
{
|
||||
switch (ObjWriting::Configuration.ModelOutputFormat)
|
||||
{
|
||||
case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT:
|
||||
return ".XMODEL_EXPORT";
|
||||
case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ:
|
||||
return ".OBJ";
|
||||
case ObjWriting::Configuration_t::ModelOutputFormat_e::GLTF:
|
||||
return ".GLTF";
|
||||
case ObjWriting::Configuration_t::ModelOutputFormat_e::GLB:
|
||||
return ".GLB";
|
||||
default:
|
||||
assert(false);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
static void CreateJsonXModel(JsonXModel& jXModel, const XModel& xmodel)
|
||||
{
|
||||
if (xmodel.collLod >= 0)
|
||||
jXModel.collLod = xmodel.collLod;
|
||||
|
||||
for (auto lodNumber = 0u; lodNumber < xmodel.numLods; lodNumber++)
|
||||
{
|
||||
JsonXModelLod lod;
|
||||
lod.file = std::format("model_export/{}_lod{}{}", xmodel.name, lodNumber, GetExtensionForModelByConfig());
|
||||
lod.distance = xmodel.lodInfo[lodNumber].dist;
|
||||
|
||||
jXModel.lods.emplace_back(std::move(lod));
|
||||
}
|
||||
|
||||
if (xmodel.physPreset && xmodel.physPreset->name)
|
||||
jXModel.physPreset = AssetName(xmodel.physPreset->name);
|
||||
|
||||
|
@ -18,12 +18,16 @@ protected:
|
||||
auto vertexOffset = 0u;
|
||||
for (const auto& vertex : xmodel.m_vertices)
|
||||
{
|
||||
XModelVertexBoneWeights weights{nullptr, 0};
|
||||
XModelVertexBoneWeights weights{0, 0};
|
||||
|
||||
if (vertexOffset < xmodel.m_vertex_bone_weights.size())
|
||||
weights = xmodel.m_vertex_bone_weights[vertexOffset];
|
||||
|
||||
m_vertex_merger.Add(VertexMergerPos{vertex.coordinates[0], vertex.coordinates[1], vertex.coordinates[2], weights.weights, weights.weightCount});
|
||||
m_vertex_merger.Add(VertexMergerPos{vertex.coordinates[0],
|
||||
vertex.coordinates[1],
|
||||
vertex.coordinates[2],
|
||||
&xmodel.m_bone_weight_data.weights[weights.weightOffset],
|
||||
weights.weightCount});
|
||||
|
||||
vertexOffset++;
|
||||
}
|
||||
@ -46,10 +50,10 @@ protected:
|
||||
for (const auto& bone : xmodel.m_bones)
|
||||
{
|
||||
m_stream << "BONE " << boneNum << " ";
|
||||
if (bone.parentIndex < 0)
|
||||
m_stream << "-1";
|
||||
if (bone.parentIndex)
|
||||
m_stream << *bone.parentIndex;
|
||||
else
|
||||
m_stream << bone.parentIndex;
|
||||
m_stream << "-1";
|
||||
|
||||
m_stream << " \"" << bone.name << "\"\n";
|
||||
boneNum++;
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include "GltfTextOutput.h"
|
||||
|
||||
#include "Utils/Alignment.h"
|
||||
#include "XModel/Gltf/GltfConstants.h"
|
||||
|
||||
#include <iomanip>
|
||||
@ -18,7 +17,6 @@ TextOutput::TextOutput(std::ostream& stream)
|
||||
|
||||
std::optional<std::string> TextOutput::CreateBufferUri(const void* buffer, const size_t bufferSize) const
|
||||
{
|
||||
static constexpr auto URI_PREFIX_LENGTH = std::char_traits<char>::length(GLTF_DATA_URI_PREFIX);
|
||||
const auto base64Length = 4u * ((bufferSize + 2u) / 3u);
|
||||
const auto base64BufferSize = URI_PREFIX_LENGTH + base64Length;
|
||||
|
||||
|
@ -119,7 +119,7 @@ namespace
|
||||
|
||||
JsonMesh mesh;
|
||||
|
||||
const auto hasBoneWeightData = !xmodel.m_vertex_bone_weights.empty();
|
||||
const auto hasBoneWeightData = !xmodel.m_bone_weight_data.weights.empty();
|
||||
|
||||
auto objectIndex = 0u;
|
||||
for (const auto& object : xmodel.m_objects)
|
||||
@ -210,46 +210,46 @@ namespace
|
||||
return textureIndex;
|
||||
}
|
||||
|
||||
void CreateSkeletonNodes(JsonRoot& gltf, const XModelCommon& xmodel)
|
||||
void CreateSkeletonNodes(JsonRoot& gltf, const XModelCommon& common)
|
||||
{
|
||||
if (xmodel.m_bones.empty())
|
||||
if (common.m_bones.empty())
|
||||
return;
|
||||
|
||||
if (!gltf.nodes.has_value())
|
||||
gltf.nodes.emplace();
|
||||
|
||||
const auto boneCount = xmodel.m_bones.size();
|
||||
const auto boneCount = common.m_bones.size();
|
||||
m_first_bone_node = gltf.nodes->size();
|
||||
for (auto boneIndex = 0u; boneIndex < boneCount; boneIndex++)
|
||||
{
|
||||
JsonNode boneNode;
|
||||
const auto& bone = xmodel.m_bones[boneIndex];
|
||||
const auto& bone = common.m_bones[boneIndex];
|
||||
|
||||
Eigen::Vector3f translation(bone.globalOffset[0], bone.globalOffset[2], -bone.globalOffset[1]);
|
||||
Eigen::Quaternionf rotation(bone.globalRotation.w, bone.globalRotation.x, bone.globalRotation.z, -bone.globalRotation.y);
|
||||
if (bone.parentIndex >= 0)
|
||||
Eigen::Vector3f translation(bone.globalOffset[0], bone.globalOffset[1], bone.globalOffset[2]);
|
||||
Eigen::Quaternionf rotation(bone.globalRotation.w, bone.globalRotation.x, bone.globalRotation.y, bone.globalRotation.z);
|
||||
if (bone.parentIndex)
|
||||
{
|
||||
const auto& parentBone = xmodel.m_bones[bone.parentIndex];
|
||||
const auto& parentBone = common.m_bones[*bone.parentIndex];
|
||||
const auto inverseParentRotation =
|
||||
Eigen::Quaternionf(parentBone.globalRotation.w, parentBone.globalRotation.x, parentBone.globalRotation.z, -parentBone.globalRotation.y)
|
||||
Eigen::Quaternionf(parentBone.globalRotation.w, parentBone.globalRotation.x, parentBone.globalRotation.y, parentBone.globalRotation.z)
|
||||
.normalized()
|
||||
.inverse()
|
||||
.normalized();
|
||||
|
||||
translation -= Eigen::Vector3f(parentBone.globalOffset[0], parentBone.globalOffset[2], -parentBone.globalOffset[1]);
|
||||
translation -= Eigen::Vector3f(parentBone.globalOffset[0], parentBone.globalOffset[1], parentBone.globalOffset[2]);
|
||||
translation = inverseParentRotation * translation;
|
||||
rotation = inverseParentRotation * rotation;
|
||||
}
|
||||
rotation.normalize();
|
||||
|
||||
boneNode.name = bone.name;
|
||||
boneNode.translation = std::to_array({translation.x(), translation.y(), translation.z()});
|
||||
boneNode.rotation = std::to_array({rotation.x(), rotation.y(), rotation.z(), rotation.w()});
|
||||
boneNode.translation = std::to_array({translation.x(), translation.z(), -translation.y()});
|
||||
boneNode.rotation = std::to_array({rotation.x(), rotation.z(), -rotation.y(), rotation.w()});
|
||||
|
||||
std::vector<unsigned> children;
|
||||
for (auto maybeChildIndex = 0u; maybeChildIndex < boneCount; maybeChildIndex++)
|
||||
{
|
||||
if (xmodel.m_bones[maybeChildIndex].parentIndex == static_cast<int>(boneIndex))
|
||||
if (common.m_bones[maybeChildIndex].parentIndex && *common.m_bones[maybeChildIndex].parentIndex == boneIndex)
|
||||
children.emplace_back(maybeChildIndex + m_first_bone_node);
|
||||
}
|
||||
if (!children.empty())
|
||||
@ -275,7 +275,9 @@ namespace
|
||||
for (auto boneIndex = 0u; boneIndex < boneCount; boneIndex++)
|
||||
skin.joints.emplace_back(boneIndex + m_first_bone_node);
|
||||
|
||||
if (!xmodel.m_bone_weight_data.weights.empty())
|
||||
skin.inverseBindMatrices = m_inverse_bind_matrices_accessor;
|
||||
|
||||
skin.skeleton = m_first_bone_node;
|
||||
|
||||
gltf.skins->emplace_back(std::move(skin));
|
||||
@ -313,7 +315,7 @@ namespace
|
||||
m_vertex_buffer_view = gltf.bufferViews->size();
|
||||
gltf.bufferViews->emplace_back(vertexBufferView);
|
||||
|
||||
if (!xmodel.m_vertex_bone_weights.empty())
|
||||
if (!xmodel.m_bone_weight_data.weights.empty())
|
||||
{
|
||||
JsonBufferView jointsBufferView;
|
||||
jointsBufferView.buffer = 0u;
|
||||
@ -391,7 +393,7 @@ namespace
|
||||
m_uv_accessor = gltf.accessors->size();
|
||||
gltf.accessors->emplace_back(uvAccessor);
|
||||
|
||||
if (!xmodel.m_vertex_bone_weights.empty())
|
||||
if (!xmodel.m_bone_weight_data.weights.empty())
|
||||
{
|
||||
JsonAccessor jointsAccessor;
|
||||
jointsAccessor.bufferView = m_joints_buffer_view;
|
||||
@ -488,7 +490,7 @@ namespace
|
||||
gltf.accessors.value()[m_position_accessor].max = std::vector({maxPosition[0], maxPosition[1], maxPosition[2]});
|
||||
}
|
||||
|
||||
if (!xmodel.m_vertex_bone_weights.empty())
|
||||
if (!xmodel.m_bone_weight_data.weights.empty())
|
||||
{
|
||||
assert(xmodel.m_vertex_bone_weights.size() == xmodel.m_vertices.size());
|
||||
|
||||
@ -496,14 +498,15 @@ namespace
|
||||
auto* weights = reinterpret_cast<float*>(&bufferData[currentBufferOffset + sizeof(uint8_t) * 4u * xmodel.m_vertex_bone_weights.size()]);
|
||||
for (const auto& commonVertexWeights : xmodel.m_vertex_bone_weights)
|
||||
{
|
||||
assert(commonVertexWeights.weights != nullptr);
|
||||
assert(commonVertexWeights.weightOffset < xmodel.m_bone_weight_data.weights.size());
|
||||
assert(commonVertexWeights.weightCount <= 4u);
|
||||
|
||||
const auto commonVertexWeightCount = std::min(commonVertexWeights.weightCount, 4u);
|
||||
const auto* commonVertexWeightData = &xmodel.m_bone_weight_data.weights[commonVertexWeights.weightOffset];
|
||||
for (auto i = 0u; i < commonVertexWeightCount; i++)
|
||||
{
|
||||
joints[i] = static_cast<unsigned char>(commonVertexWeights.weights[i].boneIndex);
|
||||
weights[i] = commonVertexWeights.weights[i].weight;
|
||||
joints[i] = static_cast<unsigned char>(commonVertexWeightData[i].boneIndex);
|
||||
weights[i] = commonVertexWeightData[i].weight;
|
||||
}
|
||||
|
||||
for (auto i = commonVertexWeightCount; i < 4u; i++)
|
||||
@ -571,7 +574,7 @@ namespace
|
||||
|
||||
result += xmodel.m_vertices.size() * sizeof(GltfVertex);
|
||||
|
||||
if (!xmodel.m_vertex_bone_weights.empty())
|
||||
if (!xmodel.m_bone_weight_data.weights.empty())
|
||||
{
|
||||
// Joints and weights
|
||||
result += xmodel.m_vertices.size() * 4u * (sizeof(uint8_t) + sizeof(float));
|
||||
|
@ -101,7 +101,7 @@ set string szUseHintString;
|
||||
set string dropHintString;
|
||||
set string szScript;
|
||||
set reusable locationDamageMultipliers;
|
||||
set count locationDamageMultipliers HITLOC_NUM;
|
||||
set count locationDamageMultipliers HITLOC_COUNT;
|
||||
set string fireRumble;
|
||||
set string meleeImpactRumble;
|
||||
set string turretBarrelSpinRumble;
|
||||
|
@ -139,7 +139,7 @@ set count originalAiVsPlayerAccuracyGraphKnots aiVsPlayerAccuracyGraphKnotCount;
|
||||
set string szUseHintString;
|
||||
set string dropHintString;
|
||||
set string szScript;
|
||||
set count locationDamageMultipliers HITLOC_NUM;
|
||||
set count locationDamageMultipliers HITLOC_COUNT;
|
||||
set reusable locationDamageMultipliers;
|
||||
set string fireRumble;
|
||||
set string meleeImpactRumble;
|
||||
|
@ -13,7 +13,8 @@ set count parentList numBones - numRootBones;
|
||||
set reusable quats;
|
||||
set count quats numBones - numRootBones;
|
||||
set reusable trans;
|
||||
set count trans numBones - numRootBones;
|
||||
// This is actually the count but it looks like a bug? It is used like a vec3, but it takes as much memory as vec4
|
||||
set count trans (numBones - numRootBones) * 4;
|
||||
set reusable partClassification;
|
||||
set count partClassification numBones;
|
||||
set reusable baseMat;
|
||||
|
@ -160,7 +160,7 @@ set count originalAiVsPlayerAccuracyGraphKnots aiVsPlayerAccuracyGraphKnotCount;
|
||||
set string szUseHintString;
|
||||
set string dropHintString;
|
||||
set string szScript;
|
||||
set count locationDamageMultipliers HITLOC_NUM;
|
||||
set count locationDamageMultipliers HITLOC_COUNT;
|
||||
set reusable locationDamageMultipliers;
|
||||
set string fireRumble;
|
||||
set string meleeImpactRumble;
|
||||
|
@ -13,7 +13,8 @@ set count parentList numBones - numRootBones;
|
||||
set reusable quats;
|
||||
set count quats numBones - numRootBones;
|
||||
set reusable trans;
|
||||
set count trans numBones - numRootBones;
|
||||
// This is actually the count but it looks like a bug? It is used like a vec3, but it takes as much memory as vec4
|
||||
set count trans (numBones - numRootBones) * 4;
|
||||
set reusable partClassification;
|
||||
set count partClassification numBones;
|
||||
set reusable baseMat;
|
||||
|
Loading…
x
Reference in New Issue
Block a user