Working on new animations
This commit is contained in:
parent
fe01a5d1b2
commit
52c4ad8ee0
@ -5,11 +5,11 @@ Convert a text-based bone animation file to the BSAF binary format.
|
|||||||
Usage:
|
Usage:
|
||||||
python convert_anim_to_binary.py <input.txt> <output.bin>
|
python convert_anim_to_binary.py <input.txt> <output.bin>
|
||||||
|
|
||||||
Binary format (BSAF v1) -- all values little-endian:
|
Binary format (BSAF v2) -- all values little-endian:
|
||||||
|
|
||||||
HEADER
|
HEADER
|
||||||
4 bytes magic "BSAF"
|
4 bytes magic "BSAF"
|
||||||
uint32 version (1)
|
uint32 version (2)
|
||||||
|
|
||||||
BONES
|
BONES
|
||||||
uint32 numBones
|
uint32 numBones
|
||||||
@ -21,6 +21,11 @@ Binary format (BSAF v1) -- all values little-endian:
|
|||||||
uint32 numChildren
|
uint32 numChildren
|
||||||
numChildren x int32 childIndices
|
numChildren x int32 childIndices
|
||||||
|
|
||||||
|
BONE NAMES (v2+)
|
||||||
|
per bone:
|
||||||
|
uint32 nameLen
|
||||||
|
nameLen bytes UTF-8 name (no terminator)
|
||||||
|
|
||||||
VERTICES
|
VERTICES
|
||||||
uint32 numVertices
|
uint32 numVertices
|
||||||
numVertices x 3 x float positions
|
numVertices x 3 x float positions
|
||||||
@ -248,7 +253,7 @@ def convert(input_path, output_path):
|
|||||||
with open(output_path, 'wb') as out:
|
with open(output_path, 'wb') as out:
|
||||||
# Header
|
# Header
|
||||||
out.write(b'BSAF')
|
out.write(b'BSAF')
|
||||||
out.write(struct.pack('<I', 1))
|
out.write(struct.pack('<I', 2))
|
||||||
|
|
||||||
# Bones
|
# Bones
|
||||||
out.write(struct.pack('<I', num_bones))
|
out.write(struct.pack('<I', num_bones))
|
||||||
@ -262,6 +267,12 @@ def convert(input_path, output_path):
|
|||||||
for c in b['children']:
|
for c in b['children']:
|
||||||
out.write(struct.pack('<i', c))
|
out.write(struct.pack('<i', c))
|
||||||
|
|
||||||
|
# Bone names (v2+)
|
||||||
|
for name in bone_names:
|
||||||
|
name_bytes = name.encode('utf-8')
|
||||||
|
out.write(struct.pack('<I', len(name_bytes)))
|
||||||
|
out.write(name_bytes)
|
||||||
|
|
||||||
# Vertices
|
# Vertices
|
||||||
out.write(struct.pack('<I', num_vertices))
|
out.write(struct.pack('<I', num_vertices))
|
||||||
for v in vertices:
|
for v in vertices:
|
||||||
|
|||||||
BIN
resources/w/default_float001.anim
(Stored with Git LFS)
BIN
resources/w/default_float001.anim
(Stored with Git LFS)
Binary file not shown.
BIN
resources/w/default_float001_cut.anim
(Stored with Git LFS)
BIN
resources/w/default_float001_cut.anim
(Stored with Git LFS)
Binary file not shown.
BIN
resources/w/default_idle002.anim
(Stored with Git LFS)
BIN
resources/w/default_idle002.anim
(Stored with Git LFS)
Binary file not shown.
BIN
resources/w/default_walk001.anim
(Stored with Git LFS)
BIN
resources/w/default_walk001.anim
(Stored with Git LFS)
Binary file not shown.
BIN
resources/w/float_attack003.anim
(Stored with Git LFS)
BIN
resources/w/float_attack003.anim
(Stored with Git LFS)
Binary file not shown.
BIN
resources/w/float_attack003_cut.anim
(Stored with Git LFS)
BIN
resources/w/float_attack003_cut.anim
(Stored with Git LFS)
Binary file not shown.
BIN
resources/w/gg/UniV_Grid_2K_Base_color.png
(Stored with Git LFS)
Normal file
BIN
resources/w/gg/UniV_Grid_2K_Base_color.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/w/gg/gg_action_attack001.anim
(Stored with Git LFS)
BIN
resources/w/gg/gg_action_attack001.anim
(Stored with Git LFS)
Binary file not shown.
BIN
resources/w/gg/gg_action_idle001.anim
(Stored with Git LFS)
BIN
resources/w/gg/gg_action_idle001.anim
(Stored with Git LFS)
Binary file not shown.
BIN
resources/w/gg/gg_action_to_stand001.anim
(Stored with Git LFS)
BIN
resources/w/gg/gg_action_to_stand001.anim
(Stored with Git LFS)
Binary file not shown.
BIN
resources/w/gg/gg_stand_idle001.anim
(Stored with Git LFS)
BIN
resources/w/gg/gg_stand_idle001.anim
(Stored with Git LFS)
Binary file not shown.
BIN
resources/w/gg/gg_stand_to_action002.anim
(Stored with Git LFS)
BIN
resources/w/gg/gg_stand_to_action002.anim
(Stored with Git LFS)
Binary file not shown.
BIN
resources/w/gg/gg_walking001.anim
(Stored with Git LFS)
BIN
resources/w/gg/gg_walking001.anim
(Stored with Git LFS)
Binary file not shown.
64
resources/w/gg/knife001.txt
Normal file
64
resources/w/gg/knife001.txt
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
===Vertices (Split by UV/Normal): 41
|
||||||
|
V 0: Pos(0.02, 0.15, 0.0) Norm(0.577338, 0.577356, -0.577357) UV(0.77503, 0.750033)
|
||||||
|
V 1: Pos(-0.02, -0.15, 0.0) Norm(-0.577338, -0.577356, -0.577357) UV(0.808365, 0.500022)
|
||||||
|
V 2: Pos(-0.02, 0.15, 0.0) Norm(-0.577347, 0.577357, -0.577347) UV(0.808365, 0.750033)
|
||||||
|
V 3: Pos(0.02, -0.15, 0.6) Norm(0.577338, -0.577356, 0.577357) UV(0.866701, 0.496276)
|
||||||
|
V 4: Pos(-0.02, 0.15, 0.6) Norm(-0.577338, 0.577356, 0.577357) UV(0.833366, 0.746287)
|
||||||
|
V 5: Pos(-0.02, -0.15, 0.6) Norm(-0.577347, -0.577357, 0.577347) UV(0.833366, 0.496276)
|
||||||
|
V 6: Pos(0.02, -0.15, 0.0) Norm(0.577347, -0.577357, -0.577347) UV(0.833366, 0.496276)
|
||||||
|
V 7: Pos(-0.02, -0.15, 0.6) Norm(-0.577347, -0.577357, 0.577347) UV(0.866701, 0.0)
|
||||||
|
V 8: Pos(-0.02, -0.15, 0.0) Norm(-0.577338, -0.577356, -0.577357) UV(0.866701, 0.496276)
|
||||||
|
V 9: Pos(0.02, 0.15, 0.0) Norm(0.577338, 0.577356, -0.577357) UV(0.500022, 0.5)
|
||||||
|
V 10: Pos(0.02, -0.15, 0.6) Norm(0.577338, -0.577356, 0.577357) UV(0.750033, 0.0)
|
||||||
|
V 11: Pos(0.02, -0.15, 0.0) Norm(0.577347, -0.577357, -0.577347) UV(0.750033, 0.5)
|
||||||
|
V 12: Pos(-0.02, 0.15, 0.0) Norm(-0.577347, 0.577357, -0.577347) UV(0.77503, 0.500022)
|
||||||
|
V 13: Pos(0.02, 0.15, 0.6) Norm(0.577347, 0.577357, 0.577347) UV(0.808365, 0.0)
|
||||||
|
V 14: Pos(0.02, 0.15, 0.0) Norm(0.577338, 0.577356, -0.577357) UV(0.808365, 0.500022)
|
||||||
|
V 15: Pos(-0.02, -0.15, 0.0) Norm(-0.577338, -0.577356, -0.577357) UV(0.500022, 1.0)
|
||||||
|
V 16: Pos(-0.02, 0.15, 0.6) Norm(-0.577338, 0.577356, 0.577357) UV(0.750033, 0.5)
|
||||||
|
V 17: Pos(-0.02, 0.15, 0.0) Norm(-0.577347, 0.577357, -0.577347) UV(0.750033, 1.0)
|
||||||
|
V 18: Pos(0.015, 0.15, 0.6) Norm(0.583227, 0.576373, -0.572398) UV(0.8667, 0.250011)
|
||||||
|
V 19: Pos(-0.015, -0.15, 0.6) Norm(-0.570792, -0.648582, -0.503526) UV(0.891702, 0.0)
|
||||||
|
V 20: Pos(-0.015, 0.15, 0.6) Norm(-0.583229, 0.576375, -0.572394) UV(0.891702, 0.250011)
|
||||||
|
V 21: Pos(0.0, 0.05, 1.4) Norm(0.0, -0.417006, 0.908904) UV(0.166674, 0.0)
|
||||||
|
V 22: Pos(-0.015, 0.15, 0.6) Norm(-0.583229, 0.576375, -0.572394) UV(0.250011, 0.666784)
|
||||||
|
V 23: Pos(-0.015, -0.15, 0.6) Norm(-0.570792, -0.648582, -0.503526) UV(0.0, 0.666784)
|
||||||
|
V 24: Pos(0.015, 0.15, 0.6) Norm(0.583227, 0.576373, -0.572398) UV(0.250011, 0.666784)
|
||||||
|
V 25: Pos(0.0, 0.05, 1.4) Norm(0.0, -0.417006, 0.908904) UV(0.333348, 0.0)
|
||||||
|
V 26: Pos(0.015, -0.15, 0.6) Norm(0.570794, -0.648585, -0.50352) UV(0.500022, 0.666784)
|
||||||
|
V 27: Pos(-0.015, -0.15, 0.6) Norm(-0.570792, -0.648582, -0.503526) UV(0.77503, 0.68218)
|
||||||
|
V 28: Pos(0.015, -0.15, 0.6) Norm(0.570794, -0.648585, -0.50352) UV(0.750033, 0.681722)
|
||||||
|
V 29: Pos(0.0, 0.05, 1.4) Norm(0.0, -0.417006, 0.908904) UV(0.77503, 0.0)
|
||||||
|
V 30: Pos(-0.015, 0.15, 0.6) Norm(-0.583229, 0.576375, -0.572394) UV(0.808365, 0.666696)
|
||||||
|
V 31: Pos(0.0, 0.15, 1.4) Norm(0.0, 0.536928, 0.843628) UV(0.820865, 0.0)
|
||||||
|
V 32: Pos(0.015, 0.15, 0.6) Norm(0.583227, 0.576373, -0.572398) UV(0.833366, 0.666696)
|
||||||
|
V 33: Pos(0.02, -0.15, 0.0) Norm(0.577347, -0.577357, -0.577347) UV(0.77503, 0.500022)
|
||||||
|
V 34: Pos(0.02, 0.15, 0.6) Norm(0.577347, 0.577357, 0.577347) UV(0.866701, 0.746287)
|
||||||
|
V 35: Pos(0.02, -0.15, 0.6) Norm(0.577338, -0.577356, 0.577357) UV(0.833366, 0.0)
|
||||||
|
V 36: Pos(0.02, 0.15, 0.6) Norm(0.577347, 0.577357, 0.577347) UV(0.500022, 0.0)
|
||||||
|
V 37: Pos(-0.02, 0.15, 0.6) Norm(-0.577338, 0.577356, 0.577357) UV(0.77503, 0.0)
|
||||||
|
V 38: Pos(-0.02, -0.15, 0.6) Norm(-0.577347, -0.577357, 0.577347) UV(0.500022, 0.5)
|
||||||
|
V 39: Pos(0.015, -0.15, 0.6) Norm(0.570794, -0.648585, -0.50352) UV(0.8667, 0.0)
|
||||||
|
V 40: Pos(0.0, 0.15, 1.4) Norm(0.0, 0.536928, 0.843628) UV(0.250011, 0.0)
|
||||||
|
|
||||||
|
===Triangles (Indices): 20
|
||||||
|
Tri: 0 1 2
|
||||||
|
Tri: 3 4 5
|
||||||
|
Tri: 6 7 8
|
||||||
|
Tri: 9 10 11
|
||||||
|
Tri: 12 13 14
|
||||||
|
Tri: 15 16 17
|
||||||
|
Tri: 18 19 20
|
||||||
|
Tri: 21 22 23
|
||||||
|
Tri: 24 25 26
|
||||||
|
Tri: 27 28 29
|
||||||
|
Tri: 30 31 32
|
||||||
|
Tri: 0 33 1
|
||||||
|
Tri: 3 34 4
|
||||||
|
Tri: 6 35 7
|
||||||
|
Tri: 9 36 10
|
||||||
|
Tri: 12 37 13
|
||||||
|
Tri: 15 38 16
|
||||||
|
Tri: 18 39 19
|
||||||
|
Tri: 21 40 22
|
||||||
|
Tri: 24 40 25
|
||||||
64
resources/w/gg/knife002.txt
Normal file
64
resources/w/gg/knife002.txt
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
===Vertices (Split by UV/Normal): 41
|
||||||
|
V 0: Pos(0.02, 0.15, 0.0) Norm(0.577338, 0.577356, -0.577357) UV(0.77503, 0.750033)
|
||||||
|
V 1: Pos(-0.02, -0.15, 0.0) Norm(-0.577338, -0.577356, -0.577357) UV(0.808365, 0.500022)
|
||||||
|
V 2: Pos(-0.02, 0.15, 0.0) Norm(-0.577347, 0.577357, -0.577347) UV(0.808365, 0.750033)
|
||||||
|
V 3: Pos(0.02, -0.15, 0.6) Norm(0.577338, -0.577356, 0.577357) UV(0.866701, 0.496276)
|
||||||
|
V 4: Pos(-0.02, 0.15, 0.6) Norm(-0.577338, 0.577356, 0.577357) UV(0.833366, 0.746287)
|
||||||
|
V 5: Pos(-0.02, -0.15, 0.6) Norm(-0.577347, -0.577357, 0.577347) UV(0.833366, 0.496276)
|
||||||
|
V 6: Pos(0.02, -0.15, 0.0) Norm(0.577347, -0.577357, -0.577347) UV(0.833366, 0.496276)
|
||||||
|
V 7: Pos(-0.02, -0.15, 0.6) Norm(-0.577347, -0.577357, 0.577347) UV(0.866701, 0.0)
|
||||||
|
V 8: Pos(-0.02, -0.15, 0.0) Norm(-0.577338, -0.577356, -0.577357) UV(0.866701, 0.496276)
|
||||||
|
V 9: Pos(0.02, 0.15, 0.0) Norm(0.577338, 0.577356, -0.577357) UV(0.500022, 0.5)
|
||||||
|
V 10: Pos(0.02, -0.15, 0.6) Norm(0.577338, -0.577356, 0.577357) UV(0.750033, 0.0)
|
||||||
|
V 11: Pos(0.02, -0.15, 0.0) Norm(0.577347, -0.577357, -0.577347) UV(0.750033, 0.5)
|
||||||
|
V 12: Pos(-0.02, 0.15, 0.0) Norm(-0.577347, 0.577357, -0.577347) UV(0.77503, 0.500022)
|
||||||
|
V 13: Pos(0.02, 0.15, 0.6) Norm(0.577347, 0.577357, 0.577347) UV(0.808365, 0.0)
|
||||||
|
V 14: Pos(0.02, 0.15, 0.0) Norm(0.577338, 0.577356, -0.577357) UV(0.808365, 0.500022)
|
||||||
|
V 15: Pos(-0.02, -0.15, 0.0) Norm(-0.577338, -0.577356, -0.577357) UV(0.500022, 1.0)
|
||||||
|
V 16: Pos(-0.02, 0.15, 0.6) Norm(-0.577338, 0.577356, 0.577357) UV(0.750033, 0.5)
|
||||||
|
V 17: Pos(-0.02, 0.15, 0.0) Norm(-0.577347, 0.577357, -0.577347) UV(0.750033, 1.0)
|
||||||
|
V 18: Pos(0.015, 0.15, 0.6) Norm(0.583227, 0.576373, -0.572398) UV(0.8667, 0.250011)
|
||||||
|
V 19: Pos(-0.015, -0.15, 0.6) Norm(-0.570792, -0.648582, -0.503526) UV(0.891702, 0.0)
|
||||||
|
V 20: Pos(-0.015, 0.15, 0.6) Norm(-0.583229, 0.576375, -0.572394) UV(0.891702, 0.250011)
|
||||||
|
V 21: Pos(0.0, 0.05, 1.4) Norm(0.0, -0.417006, 0.908904) UV(0.166674, 0.0)
|
||||||
|
V 22: Pos(-0.015, 0.15, 0.6) Norm(-0.583229, 0.576375, -0.572394) UV(0.250011, 0.666784)
|
||||||
|
V 23: Pos(-0.015, -0.15, 0.6) Norm(-0.570792, -0.648582, -0.503526) UV(0.0, 0.666784)
|
||||||
|
V 24: Pos(0.015, 0.15, 0.6) Norm(0.583227, 0.576373, -0.572398) UV(0.250011, 0.666784)
|
||||||
|
V 25: Pos(0.0, 0.05, 1.4) Norm(0.0, -0.417006, 0.908904) UV(0.333348, 0.0)
|
||||||
|
V 26: Pos(0.015, -0.15, 0.6) Norm(0.570794, -0.648585, -0.50352) UV(0.500022, 0.666784)
|
||||||
|
V 27: Pos(-0.015, -0.15, 0.6) Norm(-0.570792, -0.648582, -0.503526) UV(0.77503, 0.68218)
|
||||||
|
V 28: Pos(0.015, -0.15, 0.6) Norm(0.570794, -0.648585, -0.50352) UV(0.750033, 0.681722)
|
||||||
|
V 29: Pos(0.0, 0.05, 1.4) Norm(0.0, -0.417006, 0.908904) UV(0.77503, 0.0)
|
||||||
|
V 30: Pos(-0.015, 0.15, 0.6) Norm(-0.583229, 0.576375, -0.572394) UV(0.808365, 0.666696)
|
||||||
|
V 31: Pos(0.0, 0.15, 1.4) Norm(0.0, 0.536928, 0.843628) UV(0.820865, 0.0)
|
||||||
|
V 32: Pos(0.015, 0.15, 0.6) Norm(0.583227, 0.576373, -0.572398) UV(0.833366, 0.666696)
|
||||||
|
V 33: Pos(0.02, -0.15, 0.0) Norm(0.577347, -0.577357, -0.577347) UV(0.77503, 0.500022)
|
||||||
|
V 34: Pos(0.02, 0.15, 0.6) Norm(0.577347, 0.577357, 0.577347) UV(0.866701, 0.746287)
|
||||||
|
V 35: Pos(0.02, -0.15, 0.6) Norm(0.577338, -0.577356, 0.577357) UV(0.833366, 0.0)
|
||||||
|
V 36: Pos(0.02, 0.15, 0.6) Norm(0.577347, 0.577357, 0.577347) UV(0.500022, 0.0)
|
||||||
|
V 37: Pos(-0.02, 0.15, 0.6) Norm(-0.577338, 0.577356, 0.577357) UV(0.77503, 0.0)
|
||||||
|
V 38: Pos(-0.02, -0.15, 0.6) Norm(-0.577347, -0.577357, 0.577347) UV(0.500022, 0.5)
|
||||||
|
V 39: Pos(0.015, -0.15, 0.6) Norm(0.570794, -0.648585, -0.50352) UV(0.8667, 0.0)
|
||||||
|
V 40: Pos(0.0, 0.15, 1.4) Norm(0.0, 0.536928, 0.843628) UV(0.250011, 0.0)
|
||||||
|
|
||||||
|
===Triangles (Indices): 20
|
||||||
|
Tri: 0 1 2
|
||||||
|
Tri: 3 4 5
|
||||||
|
Tri: 6 7 8
|
||||||
|
Tri: 9 10 11
|
||||||
|
Tri: 12 13 14
|
||||||
|
Tri: 15 16 17
|
||||||
|
Tri: 18 19 20
|
||||||
|
Tri: 21 22 23
|
||||||
|
Tri: 24 25 26
|
||||||
|
Tri: 27 28 29
|
||||||
|
Tri: 30 31 32
|
||||||
|
Tri: 0 33 1
|
||||||
|
Tri: 3 34 4
|
||||||
|
Tri: 6 35 7
|
||||||
|
Tri: 9 36 10
|
||||||
|
Tri: 12 37 13
|
||||||
|
Tri: 15 38 16
|
||||||
|
Tri: 18 39 19
|
||||||
|
Tri: 21 40 22
|
||||||
|
Tri: 24 40 25
|
||||||
BIN
resources/w/gg/new/gg_action_to_stand001.anim
(Stored with Git LFS)
Normal file
BIN
resources/w/gg/new/gg_action_to_stand001.anim
(Stored with Git LFS)
Normal file
Binary file not shown.
111867
resources/w/gg/new/gg_action_to_stand001.txt
Normal file
111867
resources/w/gg/new/gg_action_to_stand001.txt
Normal file
File diff suppressed because it is too large
Load Diff
BIN
resources/w/gg/new/gg_stand_idle001.anim
(Stored with Git LFS)
Normal file
BIN
resources/w/gg/new/gg_stand_idle001.anim
(Stored with Git LFS)
Normal file
Binary file not shown.
88132
resources/w/gg/new/gg_stand_idle001.txt
Normal file
88132
resources/w/gg/new/gg_stand_idle001.txt
Normal file
File diff suppressed because it is too large
Load Diff
BIN
resources/w/gg/new/gg_stand_to_action001.anim
(Stored with Git LFS)
Normal file
BIN
resources/w/gg/new/gg_stand_to_action001.anim
(Stored with Git LFS)
Normal file
Binary file not shown.
111867
resources/w/gg/new/gg_stand_to_action001.txt
Normal file
111867
resources/w/gg/new/gg_stand_to_action001.txt
Normal file
File diff suppressed because it is too large
Load Diff
BIN
resources/w/gg/new/gg_walk001.anim
(Stored with Git LFS)
Normal file
BIN
resources/w/gg/new/gg_walk001.anim
(Stored with Git LFS)
Normal file
Binary file not shown.
104292
resources/w/gg/new/gg_walk001.txt
Normal file
104292
resources/w/gg/new/gg_walk001.txt
Normal file
File diff suppressed because it is too large
Load Diff
BIN
resources/w/white.png
(Stored with Git LFS)
Normal file
BIN
resources/w/white.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -211,6 +211,7 @@ namespace ZL
|
|||||||
|
|
||||||
startBones = bones;
|
startBones = bones;
|
||||||
currentBones = bones;
|
currentBones = bones;
|
||||||
|
this->boneNames = boneNames;
|
||||||
|
|
||||||
std::getline(f, tempLine); //vertice count
|
std::getline(f, tempLine); //vertice count
|
||||||
int numberVertices;
|
int numberVertices;
|
||||||
@ -597,7 +598,7 @@ namespace ZL
|
|||||||
if (std::memcmp(magic, "BSAF", 4) != 0)
|
if (std::memcmp(magic, "BSAF", 4) != 0)
|
||||||
throw std::runtime_error("Invalid binary animation file (bad magic)");
|
throw std::runtime_error("Invalid binary animation file (bad magic)");
|
||||||
uint32_t version = readUint32();
|
uint32_t version = readUint32();
|
||||||
if (version != 1)
|
if (version != 2)
|
||||||
throw std::runtime_error("Unsupported binary animation file version");
|
throw std::runtime_error("Unsupported binary animation file version");
|
||||||
|
|
||||||
// ---- Bones ----
|
// ---- Bones ----
|
||||||
@ -630,6 +631,15 @@ namespace ZL
|
|||||||
startBones = bones;
|
startBones = bones;
|
||||||
currentBones = bones;
|
currentBones = bones;
|
||||||
|
|
||||||
|
// ---- Bone names ----
|
||||||
|
boneNames.resize(numBones);
|
||||||
|
for (uint32_t i = 0; i < numBones; i++)
|
||||||
|
{
|
||||||
|
uint32_t nameLen = readUint32();
|
||||||
|
boneNames[i].resize(nameLen);
|
||||||
|
if (nameLen > 0) readRaw(&boneNames[i][0], nameLen);
|
||||||
|
}
|
||||||
|
|
||||||
// ---- Vertices ----
|
// ---- Vertices ----
|
||||||
uint32_t numVertices = readUint32();
|
uint32_t numVertices = readUint32();
|
||||||
std::vector<Vector3f> vertices(numVertices);
|
std::vector<Vector3f> vertices(numVertices);
|
||||||
@ -728,6 +738,15 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int BoneSystem::findBoneIndex(const std::string& name) const
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < boneNames.size(); i++)
|
||||||
|
{
|
||||||
|
if (boneNames[i] == name) return static_cast<int>(i);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
void GpuBoneData::PrepareGpuSkinningData(const std::vector<std::array<BoneWeight, MAX_BONE_COUNT>>& verticesBoneWeight)
|
void GpuBoneData::PrepareGpuSkinningData(const std::vector<std::array<BoneWeight, MAX_BONE_COUNT>>& verticesBoneWeight)
|
||||||
{
|
{
|
||||||
size_t vertexCount = verticesBoneWeight.size();
|
size_t vertexCount = verticesBoneWeight.size();
|
||||||
|
|||||||
@ -53,6 +53,7 @@ namespace ZL
|
|||||||
|
|
||||||
std::vector<Bone> startBones;
|
std::vector<Bone> startBones;
|
||||||
std::vector<Bone> currentBones;
|
std::vector<Bone> currentBones;
|
||||||
|
std::vector<std::string> boneNames;
|
||||||
|
|
||||||
std::vector<Animation> animations;
|
std::vector<Animation> animations;
|
||||||
int startingFrame = 0;
|
int startingFrame = 0;
|
||||||
@ -62,6 +63,8 @@ namespace ZL
|
|||||||
|
|
||||||
void Interpolate(int frame);
|
void Interpolate(int frame);
|
||||||
|
|
||||||
|
int findBoneIndex(const std::string& name) const;
|
||||||
|
|
||||||
// GPU skinning: compute skinning matrices without modifying the mesh
|
// GPU skinning: compute skinning matrices without modifying the mesh
|
||||||
//void ComputeSkinningMatrices(int frame, std::vector<Matrix4f>& outMatrices) const;
|
//void ComputeSkinningMatrices(int frame, std::vector<Matrix4f>& outMatrices) const;
|
||||||
};
|
};
|
||||||
|
|||||||
870
src/BoneAnimatedModelNew.cpp
Normal file
870
src/BoneAnimatedModelNew.cpp
Normal file
@ -0,0 +1,870 @@
|
|||||||
|
#include "BoneAnimatedModelNew.h"
|
||||||
|
#include <regex>
|
||||||
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace ZL
|
||||||
|
{
|
||||||
|
#ifdef EMSCRIPTEN
|
||||||
|
using std::min;
|
||||||
|
using std::max;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern int getIndexByValue(const std::string& name, const std::vector<std::string>& words);
|
||||||
|
|
||||||
|
static std::string trimRight(const std::string& s)
|
||||||
|
{
|
||||||
|
size_t end = s.size();
|
||||||
|
while (end > 0 && (s[end - 1] == '\r' || s[end - 1] == '\n' || s[end - 1] == ' ' || s[end - 1] == '\t'))
|
||||||
|
end--;
|
||||||
|
return s.substr(0, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoneSystemNew::LoadFromFile(const std::string& fileName, const std::string& ZIPFileName)
|
||||||
|
{
|
||||||
|
std::ifstream filestream;
|
||||||
|
std::istringstream zipStream;
|
||||||
|
|
||||||
|
if (!ZIPFileName.empty())
|
||||||
|
{
|
||||||
|
std::vector<char> fileData = readFileFromZIP(fileName, ZIPFileName);
|
||||||
|
std::string fileContents(fileData.begin(), fileData.end());
|
||||||
|
zipStream.str(fileContents);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
filestream.open(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::istream& f = (!ZIPFileName.empty()) ? static_cast<std::istream&>(zipStream) : static_cast<std::istream&>(filestream);
|
||||||
|
|
||||||
|
static const std::regex pattern_count(R"(\d+)");
|
||||||
|
static const std::regex pattern_float(R"([-]?\d+\.\d+)");
|
||||||
|
static const std::regex pattern_int(R"([-]?\d+)");
|
||||||
|
static const std::regex pattern_boneChildren(R"(\'([^\']+)\')");
|
||||||
|
static const std::regex pattern_bone_weight(R"(\'([^\']+)\'.*?([-]?\d+\.\d+))");
|
||||||
|
|
||||||
|
std::smatch match;
|
||||||
|
std::string tempLine;
|
||||||
|
|
||||||
|
// ---- Armature matrix (4 lines after the header) ----
|
||||||
|
std::getline(f, tempLine); // === Armature Matrix ===
|
||||||
|
armatureMatrix = Matrix4f::Identity();
|
||||||
|
for (int r = 0; r < 4; r++)
|
||||||
|
{
|
||||||
|
std::getline(f, tempLine);
|
||||||
|
std::vector<float> floatValues;
|
||||||
|
auto b = tempLine.cbegin();
|
||||||
|
auto e = tempLine.cend();
|
||||||
|
while (std::regex_search(b, e, match, pattern_float)) {
|
||||||
|
floatValues.push_back(std::stof(match.str()));
|
||||||
|
b = match.suffix().first;
|
||||||
|
}
|
||||||
|
if (floatValues.size() >= 4)
|
||||||
|
{
|
||||||
|
for (int c = 0; c < 4; c++)
|
||||||
|
armatureMatrix.data()[r + c * 4] = floatValues[c];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Bones ----
|
||||||
|
std::getline(f, tempLine); // === Armature Bones: N
|
||||||
|
int numberBones;
|
||||||
|
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||||
|
numberBones = std::stoi(match.str());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw std::runtime_error("Armature bones count not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Bone> bones;
|
||||||
|
std::vector<std::string> boneNames;
|
||||||
|
std::vector<std::string> boneParentNames;
|
||||||
|
std::unordered_map<std::string, std::vector<std::string>> boneChildren;
|
||||||
|
|
||||||
|
bones.resize(numberBones);
|
||||||
|
boneNames.resize(numberBones);
|
||||||
|
boneParentNames.resize(numberBones);
|
||||||
|
|
||||||
|
for (int i = 0; i < numberBones; i++)
|
||||||
|
{
|
||||||
|
std::getline(f, tempLine); // Bone: name
|
||||||
|
std::string boneName = trimRight(tempLine.substr(6));
|
||||||
|
boneNames[i] = boneName;
|
||||||
|
|
||||||
|
std::getline(f, tempLine); // HEAD_LOCAL
|
||||||
|
std::vector<float> floatValues;
|
||||||
|
auto b = tempLine.cbegin();
|
||||||
|
auto e = tempLine.cend();
|
||||||
|
while (std::regex_search(b, e, match, pattern_float)) {
|
||||||
|
floatValues.push_back(std::stof(match.str()));
|
||||||
|
b = match.suffix().first;
|
||||||
|
}
|
||||||
|
bones[i].boneStartWorld = Vector3f{ floatValues[0], floatValues[1], floatValues[2] };
|
||||||
|
|
||||||
|
std::getline(f, tempLine); // TAIL_LOCAL
|
||||||
|
|
||||||
|
std::getline(f, tempLine); // Length
|
||||||
|
if (std::regex_search(tempLine, match, pattern_float)) {
|
||||||
|
bones[i].boneLength = std::stof(match.str());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw std::runtime_error("Bone length not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3x3 matrix
|
||||||
|
for (int r = 0; r < 3; r++)
|
||||||
|
{
|
||||||
|
std::getline(f, tempLine);
|
||||||
|
b = tempLine.cbegin();
|
||||||
|
e = tempLine.cend();
|
||||||
|
floatValues.clear();
|
||||||
|
while (std::regex_search(b, e, match, pattern_float)) {
|
||||||
|
floatValues.push_back(std::stof(match.str()));
|
||||||
|
b = match.suffix().first;
|
||||||
|
}
|
||||||
|
bones[i].boneMatrixWorld.data()[r] = floatValues[0];
|
||||||
|
bones[i].boneMatrixWorld.data()[r + 1 * 3] = floatValues[1];
|
||||||
|
bones[i].boneMatrixWorld.data()[r + 2 * 3] = floatValues[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::getline(f, tempLine); // Parent
|
||||||
|
std::string parentLine = trimRight(tempLine);
|
||||||
|
if (parentLine == " Parent: None")
|
||||||
|
{
|
||||||
|
bones[i].parent = -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
boneParentNames[i] = parentLine.substr(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::getline(f, tempLine); // Children
|
||||||
|
auto bc = tempLine.cbegin();
|
||||||
|
auto ec = tempLine.cend();
|
||||||
|
while (std::regex_search(bc, ec, match, pattern_boneChildren)) {
|
||||||
|
boneChildren[boneName].push_back(match.str(1));
|
||||||
|
bc = match.suffix().first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve parent/child indices
|
||||||
|
for (int i = 0; i < numberBones; i++)
|
||||||
|
{
|
||||||
|
const std::string& boneName = boneNames[i];
|
||||||
|
const std::string& boneParent = boneParentNames[i];
|
||||||
|
if (boneParent == "" || boneParent == "None")
|
||||||
|
bones[i].parent = -1;
|
||||||
|
else
|
||||||
|
bones[i].parent = getIndexByValue(boneParent, boneNames);
|
||||||
|
|
||||||
|
for (size_t j = 0; j < boneChildren[boneName].size(); j++)
|
||||||
|
bones[i].children.push_back(getIndexByValue(boneChildren[boneName][j], boneNames));
|
||||||
|
}
|
||||||
|
|
||||||
|
startBones = bones;
|
||||||
|
currentBones = bones;
|
||||||
|
|
||||||
|
// ---- Multi-mesh header ----
|
||||||
|
std::getline(f, tempLine); // === TOTAL MESHES TO EXPORT: N ===
|
||||||
|
int numberMeshes;
|
||||||
|
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||||
|
numberMeshes = std::stoi(match.str());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw std::runtime_error("Total meshes count not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int meshIdx = 0; meshIdx < numberMeshes; meshIdx++)
|
||||||
|
{
|
||||||
|
// === Mesh Object: Name ===
|
||||||
|
std::getline(f, tempLine);
|
||||||
|
std::string meshHeader = trimRight(tempLine);
|
||||||
|
// Extract mesh name: strip "=== Mesh Object: " prefix and " ===" suffix
|
||||||
|
const std::string prefix = "=== Mesh Object: ";
|
||||||
|
const std::string suffix = " ===";
|
||||||
|
std::string meshName;
|
||||||
|
if (meshHeader.size() >= prefix.size() + suffix.size() &&
|
||||||
|
meshHeader.compare(0, prefix.size(), prefix) == 0 &&
|
||||||
|
meshHeader.compare(meshHeader.size() - suffix.size(), suffix.size(), suffix) == 0)
|
||||||
|
{
|
||||||
|
meshName = meshHeader.substr(prefix.size(),
|
||||||
|
meshHeader.size() - prefix.size() - suffix.size());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Invalid mesh header: " + meshHeader);
|
||||||
|
}
|
||||||
|
|
||||||
|
MeshBoneData meshData;
|
||||||
|
|
||||||
|
// Vertices
|
||||||
|
std::getline(f, tempLine); // ===Vertices: N
|
||||||
|
int numberVertices;
|
||||||
|
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||||
|
numberVertices = std::stoi(match.str());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw std::runtime_error("Vertex count not found for mesh " + meshName);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Vector3f> vertices(numberVertices);
|
||||||
|
for (int i = 0; i < numberVertices; i++)
|
||||||
|
{
|
||||||
|
std::getline(f, tempLine);
|
||||||
|
std::vector<float> floatValues;
|
||||||
|
auto b = tempLine.cbegin();
|
||||||
|
auto e = tempLine.cend();
|
||||||
|
while (std::regex_search(b, e, match, pattern_float)) {
|
||||||
|
floatValues.push_back(std::stof(match.str()));
|
||||||
|
b = match.suffix().first;
|
||||||
|
}
|
||||||
|
vertices[i] = Vector3f{ floatValues[0], floatValues[1], floatValues[2] };
|
||||||
|
}
|
||||||
|
|
||||||
|
// UV coordinates
|
||||||
|
std::getline(f, tempLine); // ===UV Coordinates:
|
||||||
|
std::getline(f, tempLine); // Face count: M
|
||||||
|
int numberFaces;
|
||||||
|
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||||
|
numberFaces = std::stoi(match.str());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw std::runtime_error("Face count not found for mesh " + meshName);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::array<Vector2f, 3>> uvCoords(numberFaces);
|
||||||
|
for (int i = 0; i < numberFaces; i++)
|
||||||
|
{
|
||||||
|
std::getline(f, tempLine); // Face X
|
||||||
|
std::getline(f, tempLine); // UV Count: 3
|
||||||
|
int uvCount;
|
||||||
|
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||||
|
uvCount = std::stoi(match.str());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw std::runtime_error("UV count not found");
|
||||||
|
}
|
||||||
|
if (uvCount != 3)
|
||||||
|
throw std::runtime_error("more than 3 uvs");
|
||||||
|
|
||||||
|
for (int j = 0; j < 3; j++)
|
||||||
|
{
|
||||||
|
std::getline(f, tempLine); // UV <Vector (u, v)>
|
||||||
|
std::vector<float> floatValues;
|
||||||
|
auto b = tempLine.cbegin();
|
||||||
|
auto e = tempLine.cend();
|
||||||
|
while (std::regex_search(b, e, match, pattern_float)) {
|
||||||
|
floatValues.push_back(std::stof(match.str()));
|
||||||
|
b = match.suffix().first;
|
||||||
|
}
|
||||||
|
if (floatValues.size() != 2)
|
||||||
|
throw std::runtime_error("more than 2 uvs---");
|
||||||
|
uvCoords[i][j] = Vector2f{ floatValues[0], floatValues[1] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normals
|
||||||
|
std::getline(f, tempLine); // ===Normals:
|
||||||
|
std::vector<Vector3f> normals(numberVertices);
|
||||||
|
for (int i = 0; i < numberVertices; i++)
|
||||||
|
{
|
||||||
|
std::getline(f, tempLine);
|
||||||
|
std::vector<float> floatValues;
|
||||||
|
auto b = tempLine.cbegin();
|
||||||
|
auto e = tempLine.cend();
|
||||||
|
while (std::regex_search(b, e, match, pattern_float)) {
|
||||||
|
floatValues.push_back(std::stof(match.str()));
|
||||||
|
b = match.suffix().first;
|
||||||
|
}
|
||||||
|
normals[i] = Vector3f{ floatValues[0], floatValues[1], floatValues[2] };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Triangles
|
||||||
|
std::getline(f, tempLine); // ===Triangles: M
|
||||||
|
int numberTriangles;
|
||||||
|
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||||
|
numberTriangles = std::stoi(match.str());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw std::runtime_error("Triangle count not found for mesh " + meshName);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::array<int, 3>> triangles(numberTriangles);
|
||||||
|
for (int i = 0; i < numberTriangles; i++)
|
||||||
|
{
|
||||||
|
std::getline(f, tempLine);
|
||||||
|
std::vector<int> intValues;
|
||||||
|
auto b = tempLine.cbegin();
|
||||||
|
auto e = tempLine.cend();
|
||||||
|
while (std::regex_search(b, e, match, pattern_int)) {
|
||||||
|
intValues.push_back(std::stoi(match.str()));
|
||||||
|
b = match.suffix().first;
|
||||||
|
}
|
||||||
|
triangles[i] = { intValues[0], intValues[1], intValues[2] };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vertex weights
|
||||||
|
std::getline(f, tempLine); // === Vertex Weights (Max 5 bones per vertex) ===
|
||||||
|
std::vector<std::array<BoneWeight, MAX_BONE_COUNT>> localVerticesBoneWeight(numberVertices);
|
||||||
|
for (int i = 0; i < numberVertices; i++)
|
||||||
|
{
|
||||||
|
std::getline(f, tempLine); // Vertex X:
|
||||||
|
std::getline(f, tempLine); // Vertex groups: K
|
||||||
|
int boneCount;
|
||||||
|
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||||
|
boneCount = std::stoi(match.str());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw std::runtime_error("Vertex group count not found");
|
||||||
|
}
|
||||||
|
if (boneCount > MAX_BONE_COUNT)
|
||||||
|
throw std::runtime_error("more than 5 bones");
|
||||||
|
|
||||||
|
float sumWeights = 0;
|
||||||
|
for (int j = 0; j < boneCount; j++)
|
||||||
|
{
|
||||||
|
std::getline(f, tempLine);
|
||||||
|
if (std::regex_search(tempLine, match, pattern_bone_weight)) {
|
||||||
|
std::string word = match.str(1);
|
||||||
|
double weight = std::stod(match.str(2));
|
||||||
|
int boneNumber = getIndexByValue(word, boneNames);
|
||||||
|
localVerticesBoneWeight[i][j].boneIndex = boneNumber;
|
||||||
|
localVerticesBoneWeight[i][j].weight = static_cast<float>(weight);
|
||||||
|
sumWeights += static_cast<float>(weight);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw std::runtime_error("No match found in bone weight line");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int j = 0; j < boneCount; j++)
|
||||||
|
localVerticesBoneWeight[i][j].weight /= sumWeights;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build per-triangle expanded mesh
|
||||||
|
for (int i = 0; i < numberTriangles; i++)
|
||||||
|
{
|
||||||
|
meshData.mesh.PositionData.push_back(vertices[triangles[i][0]]);
|
||||||
|
meshData.mesh.PositionData.push_back(vertices[triangles[i][1]]);
|
||||||
|
meshData.mesh.PositionData.push_back(vertices[triangles[i][2]]);
|
||||||
|
|
||||||
|
meshData.verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][0]]);
|
||||||
|
meshData.verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][1]]);
|
||||||
|
meshData.verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][2]]);
|
||||||
|
|
||||||
|
meshData.mesh.TexCoordData.push_back(uvCoords[i][0]);
|
||||||
|
meshData.mesh.TexCoordData.push_back(uvCoords[i][1]);
|
||||||
|
meshData.mesh.TexCoordData.push_back(uvCoords[i][2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
meshData.startMesh = meshData.mesh;
|
||||||
|
|
||||||
|
meshes[meshName] = std::move(meshData);
|
||||||
|
meshNamesOrdered.push_back(meshName);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Loaded " << numberMeshes << " meshes from " << fileName << std::endl;
|
||||||
|
|
||||||
|
// ---- Animation Keyframes ----
|
||||||
|
std::getline(f, tempLine); // === Animation Keyframes ===
|
||||||
|
std::getline(f, tempLine); // === Bone Transforms per Keyframe ===
|
||||||
|
std::getline(f, tempLine); // Keyframes: N
|
||||||
|
int numberKeyFrames;
|
||||||
|
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||||
|
numberKeyFrames = std::stoi(match.str());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw std::runtime_error("Keyframe count not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
animations.resize(1);
|
||||||
|
animations[0].keyFrames.resize(numberKeyFrames);
|
||||||
|
|
||||||
|
for (int i = 0; i < numberKeyFrames; i++)
|
||||||
|
{
|
||||||
|
std::getline(f, tempLine); // Frame: N
|
||||||
|
int numberFrame;
|
||||||
|
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||||
|
numberFrame = std::stoi(match.str());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw std::runtime_error("Frame number not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
animations[0].keyFrames[i].frame = numberFrame;
|
||||||
|
animations[0].keyFrames[i].bones.resize(numberBones);
|
||||||
|
|
||||||
|
for (int j = 0; j < numberBones; j++)
|
||||||
|
{
|
||||||
|
std::getline(f, tempLine); // Bone: name
|
||||||
|
std::string boneName = trimRight(tempLine.substr(8));
|
||||||
|
int boneNumber = getIndexByValue(boneName, boneNames);
|
||||||
|
animations[0].keyFrames[i].bones[boneNumber] = startBones[boneNumber];
|
||||||
|
|
||||||
|
std::getline(f, tempLine); // Location
|
||||||
|
std::vector<float> floatValues;
|
||||||
|
auto b = tempLine.cbegin();
|
||||||
|
auto e = tempLine.cend();
|
||||||
|
while (std::regex_search(b, e, match, pattern_float)) {
|
||||||
|
floatValues.push_back(std::stof(match.str()));
|
||||||
|
b = match.suffix().first;
|
||||||
|
}
|
||||||
|
animations[0].keyFrames[i].bones[boneNumber].boneStartWorld =
|
||||||
|
Vector3f{ floatValues[0], floatValues[1], floatValues[2] };
|
||||||
|
|
||||||
|
std::getline(f, tempLine); // Rotation
|
||||||
|
std::getline(f, tempLine); // Matrix:
|
||||||
|
|
||||||
|
for (int r = 0; r < 4; r++)
|
||||||
|
{
|
||||||
|
std::getline(f, tempLine);
|
||||||
|
b = tempLine.cbegin();
|
||||||
|
e = tempLine.cend();
|
||||||
|
floatValues.clear();
|
||||||
|
while (std::regex_search(b, e, match, pattern_float)) {
|
||||||
|
floatValues.push_back(std::stof(match.str()));
|
||||||
|
b = match.suffix().first;
|
||||||
|
}
|
||||||
|
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[r] = floatValues[0];
|
||||||
|
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[r + 1 * 4] = floatValues[1];
|
||||||
|
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[r + 2 * 4] = floatValues[2];
|
||||||
|
animations[0].keyFrames[i].bones[boneNumber].boneMatrixWorld.data()[r + 3 * 4] = floatValues[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startBones.size() > MAX_GPU_BONES)
|
||||||
|
{
|
||||||
|
std::cout << "Warning: model has " << startBones.size()
|
||||||
|
<< " bones, exceeding GPU skinning limit of " << MAX_GPU_BONES << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoneSystemNew::LoadFromBinaryFile(const std::string& fileName, const std::string& ZIPFileName)
|
||||||
|
{
|
||||||
|
std::vector<char> fileData;
|
||||||
|
|
||||||
|
if (!ZIPFileName.empty())
|
||||||
|
{
|
||||||
|
fileData = readFileFromZIP(fileName, ZIPFileName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::ifstream f(fileName, std::ios::binary | std::ios::ate);
|
||||||
|
if (!f.is_open()) throw std::runtime_error("Failed to open binary file: " + fileName);
|
||||||
|
std::streamsize fileSize = f.tellg();
|
||||||
|
f.seekg(0);
|
||||||
|
fileData.resize(static_cast<size_t>(fileSize));
|
||||||
|
f.read(fileData.data(), fileSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* ptr = fileData.data();
|
||||||
|
const char* end = ptr + fileData.size();
|
||||||
|
|
||||||
|
auto readRaw = [&](void* dst, size_t n) {
|
||||||
|
if (ptr + n > end) throw std::runtime_error("Unexpected EOF in binary animation file");
|
||||||
|
std::memcpy(dst, ptr, n);
|
||||||
|
ptr += n;
|
||||||
|
};
|
||||||
|
auto readUint32 = [&]() -> uint32_t { uint32_t v; readRaw(&v, 4); return v; };
|
||||||
|
auto readInt32 = [&]() -> int32_t { int32_t v; readRaw(&v, 4); return v; };
|
||||||
|
auto readFloat = [&]() -> float { float v; readRaw(&v, 4); return v; };
|
||||||
|
auto readVec3 = [&]() -> Vector3f { return Vector3f{readFloat(), readFloat(), readFloat()}; };
|
||||||
|
auto readVec2 = [&]() -> Vector2f { return Vector2f{readFloat(), readFloat()}; };
|
||||||
|
auto readString = [&]() -> std::string {
|
||||||
|
uint32_t len = readUint32();
|
||||||
|
std::string s(len, '\0');
|
||||||
|
if (len > 0) readRaw(&s[0], len);
|
||||||
|
return s;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Header
|
||||||
|
char magic[4];
|
||||||
|
readRaw(magic, 4);
|
||||||
|
if (std::memcmp(magic, "BSMF", 4) != 0)
|
||||||
|
throw std::runtime_error("Invalid multi-mesh binary animation file (bad magic)");
|
||||||
|
uint32_t version = readUint32();
|
||||||
|
if (version != 1)
|
||||||
|
throw std::runtime_error("Unsupported multi-mesh binary animation file version");
|
||||||
|
|
||||||
|
// Armature matrix (row-major in file, stored with stride-4 into Matrix4f)
|
||||||
|
{
|
||||||
|
float m[16];
|
||||||
|
for (int k = 0; k < 16; k++) m[k] = readFloat();
|
||||||
|
armatureMatrix = Matrix4f::Identity();
|
||||||
|
for (int r = 0; r < 4; r++)
|
||||||
|
for (int c = 0; c < 4; c++)
|
||||||
|
armatureMatrix.data()[r + c * 4] = m[r * 4 + c];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bones
|
||||||
|
uint32_t numBones = readUint32();
|
||||||
|
std::vector<Bone> bones(numBones);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < numBones; i++)
|
||||||
|
{
|
||||||
|
bones[i].boneStartWorld = readVec3();
|
||||||
|
bones[i].boneLength = readFloat();
|
||||||
|
|
||||||
|
float m[9];
|
||||||
|
for (int j = 0; j < 9; j++) m[j] = readFloat();
|
||||||
|
|
||||||
|
bones[i].boneMatrixWorld = Matrix4f::Zero();
|
||||||
|
for (int r = 0; r < 3; r++)
|
||||||
|
for (int c = 0; c < 3; c++)
|
||||||
|
bones[i].boneMatrixWorld.data()[r + c * 3] = m[r * 3 + c];
|
||||||
|
|
||||||
|
bones[i].parent = readInt32();
|
||||||
|
|
||||||
|
uint32_t numChildren = readUint32();
|
||||||
|
bones[i].children.resize(numChildren);
|
||||||
|
for (uint32_t j = 0; j < numChildren; j++)
|
||||||
|
bones[i].children[j] = readInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
startBones = bones;
|
||||||
|
currentBones = bones;
|
||||||
|
|
||||||
|
// Meshes
|
||||||
|
uint32_t numMeshes = readUint32();
|
||||||
|
|
||||||
|
for (uint32_t meshIdx = 0; meshIdx < numMeshes; meshIdx++)
|
||||||
|
{
|
||||||
|
std::string meshName = readString();
|
||||||
|
MeshBoneData meshData;
|
||||||
|
|
||||||
|
uint32_t numVertices = readUint32();
|
||||||
|
std::vector<Vector3f> vertices(numVertices);
|
||||||
|
for (uint32_t i = 0; i < numVertices; i++)
|
||||||
|
vertices[i] = readVec3();
|
||||||
|
|
||||||
|
uint32_t numFaces = readUint32();
|
||||||
|
std::vector<std::array<Vector2f, 3>> uvCoords(numFaces);
|
||||||
|
for (uint32_t i = 0; i < numFaces; i++)
|
||||||
|
for (int j = 0; j < 3; j++)
|
||||||
|
uvCoords[i][j] = readVec2();
|
||||||
|
|
||||||
|
std::vector<Vector3f> normals(numVertices);
|
||||||
|
for (uint32_t i = 0; i < numVertices; i++)
|
||||||
|
normals[i] = readVec3();
|
||||||
|
|
||||||
|
uint32_t numTriangles = readUint32();
|
||||||
|
std::vector<std::array<int, 3>> triangles(numTriangles);
|
||||||
|
for (uint32_t i = 0; i < numTriangles; i++)
|
||||||
|
triangles[i] = { readInt32(), readInt32(), readInt32() };
|
||||||
|
|
||||||
|
std::vector<std::array<BoneWeight, MAX_BONE_COUNT>> localVerticesBoneWeight(numVertices);
|
||||||
|
for (uint32_t i = 0; i < numVertices; i++)
|
||||||
|
{
|
||||||
|
uint32_t numGroups = readUint32();
|
||||||
|
float sumWeights = 0;
|
||||||
|
for (uint32_t j = 0; j < numGroups; j++)
|
||||||
|
{
|
||||||
|
int boneIdx = readInt32();
|
||||||
|
float weight = readFloat();
|
||||||
|
if (j < MAX_BONE_COUNT)
|
||||||
|
{
|
||||||
|
localVerticesBoneWeight[i][j].boneIndex = boneIdx;
|
||||||
|
localVerticesBoneWeight[i][j].weight = weight;
|
||||||
|
sumWeights += weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint32_t cap = (numGroups < MAX_BONE_COUNT) ? numGroups : MAX_BONE_COUNT;
|
||||||
|
for (uint32_t j = 0; j < cap; j++)
|
||||||
|
localVerticesBoneWeight[i][j].weight /= sumWeights;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < numTriangles; i++)
|
||||||
|
{
|
||||||
|
meshData.mesh.PositionData.push_back(vertices[triangles[i][0]]);
|
||||||
|
meshData.mesh.PositionData.push_back(vertices[triangles[i][1]]);
|
||||||
|
meshData.mesh.PositionData.push_back(vertices[triangles[i][2]]);
|
||||||
|
|
||||||
|
meshData.verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][0]]);
|
||||||
|
meshData.verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][1]]);
|
||||||
|
meshData.verticesBoneWeight.push_back(localVerticesBoneWeight[triangles[i][2]]);
|
||||||
|
|
||||||
|
meshData.mesh.TexCoordData.push_back(uvCoords[i][0]);
|
||||||
|
meshData.mesh.TexCoordData.push_back(uvCoords[i][1]);
|
||||||
|
meshData.mesh.TexCoordData.push_back(uvCoords[i][2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
meshData.startMesh = meshData.mesh;
|
||||||
|
|
||||||
|
meshes[meshName] = std::move(meshData);
|
||||||
|
meshNamesOrdered.push_back(meshName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Animation Keyframes
|
||||||
|
uint32_t numKeyframes = readUint32();
|
||||||
|
animations.resize(1);
|
||||||
|
animations[0].keyFrames.resize(numKeyframes);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < numKeyframes; i++)
|
||||||
|
{
|
||||||
|
animations[0].keyFrames[i].frame = readInt32();
|
||||||
|
animations[0].keyFrames[i].bones.resize(numBones);
|
||||||
|
|
||||||
|
for (uint32_t j = 0; j < numBones; j++)
|
||||||
|
{
|
||||||
|
animations[0].keyFrames[i].bones[j] = startBones[j];
|
||||||
|
animations[0].keyFrames[i].bones[j].boneStartWorld = readVec3();
|
||||||
|
|
||||||
|
float m[16];
|
||||||
|
for (int k = 0; k < 16; k++) m[k] = readFloat();
|
||||||
|
|
||||||
|
for (int r = 0; r < 4; r++)
|
||||||
|
for (int c = 0; c < 4; c++)
|
||||||
|
animations[0].keyFrames[i].bones[j].boneMatrixWorld.data()[r + c * 4] = m[r * 4 + c];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startBones.size() > MAX_GPU_BONES)
|
||||||
|
{
|
||||||
|
std::cout << "Warning: model has " << startBones.size()
|
||||||
|
<< " bones, exceeding GPU skinning limit of " << MAX_GPU_BONES << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoneSystemNew::Interpolate(int frame)
|
||||||
|
{
|
||||||
|
int startingKeyFrame = -1;
|
||||||
|
for (int i = 0; i < static_cast<int>(animations[0].keyFrames.size()) - 1; i++)
|
||||||
|
{
|
||||||
|
int oldFrame = animations[0].keyFrames[i].frame;
|
||||||
|
int nextFrame = animations[0].keyFrames[i + 1].frame;
|
||||||
|
if (frame >= oldFrame && frame < nextFrame)
|
||||||
|
{
|
||||||
|
startingKeyFrame = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startingKeyFrame == -1)
|
||||||
|
{
|
||||||
|
std::cout << "Exception here: frame number is out of range of keyframes. Frame: " << frame << std::endl;
|
||||||
|
throw std::runtime_error("Exception here");
|
||||||
|
}
|
||||||
|
|
||||||
|
int modifiedFrameNumber = frame - animations[0].keyFrames[startingKeyFrame].frame;
|
||||||
|
int diffFrames = animations[0].keyFrames[startingKeyFrame + 1].frame - animations[0].keyFrames[startingKeyFrame].frame;
|
||||||
|
float t = (modifiedFrameNumber + 0.f) / diffFrames;
|
||||||
|
|
||||||
|
std::vector<Bone>& oneFrameBones = animations[0].keyFrames[startingKeyFrame].bones;
|
||||||
|
std::vector<Bone>& nextFrameBones = animations[0].keyFrames[startingKeyFrame + 1].bones;
|
||||||
|
|
||||||
|
std::vector<Matrix4f> skinningMatrixForEachBone(currentBones.size());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < currentBones.size(); i++)
|
||||||
|
{
|
||||||
|
currentBones[i].boneStartWorld(0) = oneFrameBones[i].boneStartWorld(0) + t * (nextFrameBones[i].boneStartWorld(0) - oneFrameBones[i].boneStartWorld(0));
|
||||||
|
currentBones[i].boneStartWorld(1) = oneFrameBones[i].boneStartWorld(1) + t * (nextFrameBones[i].boneStartWorld(1) - oneFrameBones[i].boneStartWorld(1));
|
||||||
|
currentBones[i].boneStartWorld(2) = oneFrameBones[i].boneStartWorld(2) + t * (nextFrameBones[i].boneStartWorld(2) - oneFrameBones[i].boneStartWorld(2));
|
||||||
|
|
||||||
|
Matrix3f oneFrameBonesMatrix = oneFrameBones[i].boneMatrixWorld.block<3, 3>(0, 0);
|
||||||
|
Matrix3f nextFrameBonesMatrix = nextFrameBones[i].boneMatrixWorld.block<3, 3>(0, 0);
|
||||||
|
|
||||||
|
Eigen::Quaternionf q1 = Eigen::Quaternionf(oneFrameBonesMatrix).normalized();
|
||||||
|
Eigen::Quaternionf q2 = Eigen::Quaternionf(nextFrameBonesMatrix).normalized();
|
||||||
|
Eigen::Quaternionf result = q1.slerp(t, q2);
|
||||||
|
|
||||||
|
Matrix3f boneMatrixWorld3 = result.toRotationMatrix();
|
||||||
|
|
||||||
|
currentBones[i].boneMatrixWorld = Eigen::Matrix4f::Identity();
|
||||||
|
currentBones[i].boneMatrixWorld.block<3, 3>(0, 0) = boneMatrixWorld3;
|
||||||
|
currentBones[i].boneMatrixWorld.block<3, 1>(0, 3) = currentBones[i].boneStartWorld;
|
||||||
|
|
||||||
|
Matrix4f startBoneMatrixWorld4 = animations[0].keyFrames[0].bones[i].boneMatrixWorld;
|
||||||
|
skinningMatrixForEachBone[i] = currentBones[i].boneMatrixWorld * startBoneMatrixWorld4.inverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& name : meshNamesOrdered)
|
||||||
|
{
|
||||||
|
auto it = meshes.find(name);
|
||||||
|
if (it == meshes.end()) continue;
|
||||||
|
MeshBoneData& md = it->second;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < md.mesh.PositionData.size(); i++)
|
||||||
|
{
|
||||||
|
Vector4f originalPos = {
|
||||||
|
md.startMesh.PositionData[i](0),
|
||||||
|
md.startMesh.PositionData[i](1),
|
||||||
|
md.startMesh.PositionData[i](2), 1.0f };
|
||||||
|
|
||||||
|
Vector4f finalPos = Vector4f{ 0.f, 0.f, 0.f, 0.f };
|
||||||
|
bool vMoved = false;
|
||||||
|
|
||||||
|
for (int j = 0; j < MAX_BONE_COUNT; j++)
|
||||||
|
{
|
||||||
|
if (md.verticesBoneWeight[i][j].weight != 0)
|
||||||
|
{
|
||||||
|
if (md.verticesBoneWeight[i][j].boneIndex == -1)
|
||||||
|
{
|
||||||
|
std::cout << "Exception here: bone index is -1 but weight is > 0" << std::endl;
|
||||||
|
throw std::runtime_error("Bones loaded incorrectly - bone index is -1 but weight is > 0");
|
||||||
|
}
|
||||||
|
vMoved = true;
|
||||||
|
finalPos = finalPos + (skinningMatrixForEachBone[md.verticesBoneWeight[i][j].boneIndex] * originalPos) * md.verticesBoneWeight[i][j].weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!vMoved) finalPos = originalPos;
|
||||||
|
|
||||||
|
md.mesh.PositionData[i](0) = finalPos(0);
|
||||||
|
md.mesh.PositionData[i](1) = finalPos(1);
|
||||||
|
md.mesh.PositionData[i](2) = finalPos(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshGpuSkinningData::prepareGpuSkinningVBOs(MeshBoneData& meshData)
|
||||||
|
{
|
||||||
|
if (gpuSkinningPrepared) return;
|
||||||
|
|
||||||
|
gpuBoneData.PrepareGpuSkinningData(meshData.verticesBoneWeight);
|
||||||
|
bindPoseMutable.AssignFrom(meshData.startMesh);
|
||||||
|
|
||||||
|
auto& gpu = gpuBoneData;
|
||||||
|
|
||||||
|
boneIndices0VBO = std::make_shared<VBOHolder>();
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, boneIndices0VBO->getBuffer());
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, gpu.boneIndices0.size() * sizeof(Eigen::Vector4f),
|
||||||
|
gpu.boneIndices0.data(), GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
boneIndices1VBO = std::make_shared<VBOHolder>();
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, boneIndices1VBO->getBuffer());
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, gpu.boneIndices1.size() * sizeof(Eigen::Vector2f),
|
||||||
|
gpu.boneIndices1.data(), GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
boneWeights0VBO = std::make_shared<VBOHolder>();
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, boneWeights0VBO->getBuffer());
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, gpu.boneWeights0.size() * sizeof(Eigen::Vector4f),
|
||||||
|
gpu.boneWeights0.data(), GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
boneWeights1VBO = std::make_shared<VBOHolder>();
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, boneWeights1VBO->getBuffer());
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, gpu.boneWeights1.size() * sizeof(Eigen::Vector2f),
|
||||||
|
gpu.boneWeights1.data(), GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
gpuSkinningPrepared = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshGpuSkinningData::RenderVBO(Renderer& renderer)
|
||||||
|
{
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, bindPoseMutable.positionVBO->getBuffer());
|
||||||
|
renderer.VertexAttribPointer3fv("vPosition", 0, NULL);
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
|
||||||
|
if (bindPoseMutable.texCoordVBO) {
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, bindPoseMutable.texCoordVBO->getBuffer());
|
||||||
|
renderer.VertexAttribPointer2fv("vTexCoord", 0, NULL);
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bindPoseMutable.normalVBO) {
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, bindPoseMutable.normalVBO->getBuffer());
|
||||||
|
renderer.VertexAttribPointer3fv("vNormal", 0, NULL);
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
} else {
|
||||||
|
renderer.DisableVertexAttribArray("vNormal");
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, boneIndices0VBO->getBuffer());
|
||||||
|
renderer.VertexAttribPointer4fv("aBoneIndices0", 0, NULL);
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, boneIndices1VBO->getBuffer());
|
||||||
|
renderer.VertexAttribPointer2fv("aBoneIndices1", 0, NULL);
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, boneWeights0VBO->getBuffer());
|
||||||
|
renderer.VertexAttribPointer4fv("aBoneWeights0", 0, NULL);
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, boneWeights1VBO->getBuffer());
|
||||||
|
renderer.VertexAttribPointer2fv("aBoneWeights1", 0, NULL);
|
||||||
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
|
||||||
|
glDrawArrays(GL_TRIANGLES, 0, static_cast<GLsizei>(bindPoseMutable.data.PositionData.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GpuSkinningShaderDataNew::prepareGpuSkinningVBOs(BoneSystemNew& model)
|
||||||
|
{
|
||||||
|
for (const auto& name : model.meshNamesOrdered)
|
||||||
|
{
|
||||||
|
auto it = model.meshes.find(name);
|
||||||
|
if (it == model.meshes.end()) continue;
|
||||||
|
perMesh[name].prepareGpuSkinningVBOs(it->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GpuSkinningShaderDataNew::RenderVBO(Renderer& renderer, const std::vector<std::string>& meshNamesOrdered)
|
||||||
|
{
|
||||||
|
for (const auto& name : meshNamesOrdered)
|
||||||
|
{
|
||||||
|
auto it = perMesh.find(name);
|
||||||
|
if (it == perMesh.end()) continue;
|
||||||
|
it->second.RenderVBO(renderer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GpuSkinningShaderDataNew::ComputeSkinningMatrices(const std::vector<Bone>& startBones, const std::vector<AnimationKeyFrame>& keyFrames, int frame)
|
||||||
|
{
|
||||||
|
int startingKeyFrame = -1;
|
||||||
|
for (size_t i = 0; i < keyFrames.size() - 1; i++)
|
||||||
|
{
|
||||||
|
int oldFrame = keyFrames[i].frame;
|
||||||
|
int nextFrame = keyFrames[i + 1].frame;
|
||||||
|
if (frame >= oldFrame && frame < nextFrame)
|
||||||
|
{
|
||||||
|
startingKeyFrame = static_cast<int>(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
skinningMatrices.resize(startBones.size());
|
||||||
|
|
||||||
|
if (startingKeyFrame == -1)
|
||||||
|
{
|
||||||
|
for (auto& m : skinningMatrices) m = Matrix4f::Identity();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int modifiedFrameNumber = frame - keyFrames[startingKeyFrame].frame;
|
||||||
|
int diffFrames = keyFrames[startingKeyFrame + 1].frame - keyFrames[startingKeyFrame].frame;
|
||||||
|
float t = (modifiedFrameNumber + 0.f) / diffFrames;
|
||||||
|
|
||||||
|
const std::vector<Bone>& oneFrameBones = keyFrames[startingKeyFrame].bones;
|
||||||
|
const std::vector<Bone>& nextFrameBones = keyFrames[startingKeyFrame + 1].bones;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < startBones.size(); i++)
|
||||||
|
{
|
||||||
|
Vector3f interpPos;
|
||||||
|
interpPos(0) = oneFrameBones[i].boneStartWorld(0) + t * (nextFrameBones[i].boneStartWorld(0) - oneFrameBones[i].boneStartWorld(0));
|
||||||
|
interpPos(1) = oneFrameBones[i].boneStartWorld(1) + t * (nextFrameBones[i].boneStartWorld(1) - oneFrameBones[i].boneStartWorld(1));
|
||||||
|
interpPos(2) = oneFrameBones[i].boneStartWorld(2) + t * (nextFrameBones[i].boneStartWorld(2) - oneFrameBones[i].boneStartWorld(2));
|
||||||
|
|
||||||
|
Matrix3f oneFrameBonesMatrix = oneFrameBones[i].boneMatrixWorld.block<3, 3>(0, 0);
|
||||||
|
Matrix3f nextFrameBonesMatrix = nextFrameBones[i].boneMatrixWorld.block<3, 3>(0, 0);
|
||||||
|
|
||||||
|
Eigen::Quaternionf q1 = Eigen::Quaternionf(oneFrameBonesMatrix).normalized();
|
||||||
|
Eigen::Quaternionf q2 = Eigen::Quaternionf(nextFrameBonesMatrix).normalized();
|
||||||
|
Eigen::Quaternionf result = q1.slerp(t, q2);
|
||||||
|
|
||||||
|
Matrix3f boneMatrixWorld3 = result.toRotationMatrix();
|
||||||
|
|
||||||
|
Matrix4f currentBoneMatrixWorld4 = Eigen::Matrix4f::Identity();
|
||||||
|
currentBoneMatrixWorld4.block<3, 3>(0, 0) = boneMatrixWorld3;
|
||||||
|
currentBoneMatrixWorld4.block<3, 1>(0, 3) = interpPos;
|
||||||
|
|
||||||
|
Matrix4f startBoneMatrixWorld4 = keyFrames[0].bones[i].boneMatrixWorld;
|
||||||
|
skinningMatrices[i] = currentBoneMatrixWorld4 * startBoneMatrixWorld4.inverse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
67
src/BoneAnimatedModelNew.h
Normal file
67
src/BoneAnimatedModelNew.h
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "BoneAnimatedModel.h"
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace ZL
|
||||||
|
{
|
||||||
|
struct MeshBoneData
|
||||||
|
{
|
||||||
|
VertexDataStruct mesh;
|
||||||
|
VertexDataStruct startMesh;
|
||||||
|
std::vector<std::array<BoneWeight, MAX_BONE_COUNT>> verticesBoneWeight;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BoneSystemNew
|
||||||
|
{
|
||||||
|
std::unordered_map<std::string, MeshBoneData> meshes;
|
||||||
|
std::vector<std::string> meshNamesOrdered;
|
||||||
|
|
||||||
|
Matrix4f armatureMatrix;
|
||||||
|
|
||||||
|
std::vector<Bone> startBones;
|
||||||
|
std::vector<Bone> currentBones;
|
||||||
|
|
||||||
|
std::vector<Animation> animations;
|
||||||
|
int startingFrame = 0;
|
||||||
|
|
||||||
|
void LoadFromFile(const std::string& fileName, const std::string& ZIPFileName = "");
|
||||||
|
void LoadFromBinaryFile(const std::string& fileName, const std::string& ZIPFileName = "");
|
||||||
|
|
||||||
|
void Interpolate(int frame);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MeshGpuSkinningData
|
||||||
|
{
|
||||||
|
GpuBoneData gpuBoneData;
|
||||||
|
|
||||||
|
VertexRenderStruct bindPoseMutable;
|
||||||
|
std::shared_ptr<VBOHolder> boneIndices0VBO;
|
||||||
|
std::shared_ptr<VBOHolder> boneIndices1VBO;
|
||||||
|
std::shared_ptr<VBOHolder> boneWeights0VBO;
|
||||||
|
std::shared_ptr<VBOHolder> boneWeights1VBO;
|
||||||
|
bool gpuSkinningPrepared = false;
|
||||||
|
|
||||||
|
void prepareGpuSkinningVBOs(MeshBoneData& meshData);
|
||||||
|
void RenderVBO(Renderer& renderer);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GpuSkinningShaderDataNew
|
||||||
|
{
|
||||||
|
std::unordered_map<std::string, MeshGpuSkinningData> perMesh;
|
||||||
|
std::vector<Eigen::Matrix4f> skinningMatrices;
|
||||||
|
|
||||||
|
void prepareGpuSkinningVBOs(BoneSystemNew& model);
|
||||||
|
void RenderVBO(Renderer& renderer, const std::vector<std::string>& meshNamesOrdered);
|
||||||
|
void ComputeSkinningMatrices(const std::vector<Bone>& startBones, const std::vector<AnimationKeyFrame>& keyFrames, int frame);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BoneAnimationDataNew
|
||||||
|
{
|
||||||
|
BoneSystemNew model;
|
||||||
|
float currentFrame = 0.f;
|
||||||
|
int lastFrame = -1;
|
||||||
|
int totalFrames = 1;
|
||||||
|
|
||||||
|
GpuSkinningShaderDataNew gpuSkinningShaderData;
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -7,6 +7,8 @@
|
|||||||
namespace ZL {
|
namespace ZL {
|
||||||
|
|
||||||
const float ATTACK_COOLDOWN_TIME = 3.0f;
|
const float ATTACK_COOLDOWN_TIME = 3.0f;
|
||||||
|
extern float x;
|
||||||
|
extern float y;
|
||||||
|
|
||||||
void Character::loadAnimation(AnimationState state, const std::string& filename, const std::string& zipFile) {
|
void Character::loadAnimation(AnimationState state, const std::string& filename, const std::string& zipFile) {
|
||||||
auto& data = animations[state];
|
auto& data = animations[state];
|
||||||
@ -79,13 +81,68 @@ AnimationState Character::resolveActiveState() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Character::update(int64_t deltaMs) {
|
void Character::update(int64_t deltaMs) {
|
||||||
//std::cout << "update called with deltaMs: " << deltaMs << std::endl;
|
|
||||||
// Move toward walk target on the XZ plane
|
//weaponInitialRotation = Eigen::AngleAxisf(x/180.0, Eigen::Vector3f::UnitZ()).toRotationMatrix();
|
||||||
Eigen::Vector3f activeTarget = walkTarget;
|
//weaponInitialRotation = Eigen::AngleAxisf(-M_PI*0.5, Eigen::Vector3f::UnitZ()).toRotationMatrix();
|
||||||
|
//weaponInitialPosition = Eigen::Vector3f(0, 0.09, 0.016);
|
||||||
|
/*if (deltaMs > 200)
|
||||||
|
{
|
||||||
|
deltaMs = 200;
|
||||||
|
}*/
|
||||||
|
Eigen::Vector3f activeTarget;
|
||||||
|
Eigen::Vector3f lookTarget;
|
||||||
|
|
||||||
|
if (attackTarget == nullptr)
|
||||||
|
{
|
||||||
|
battle_state = 0;
|
||||||
|
attack = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attackTarget != nullptr)
|
||||||
|
{
|
||||||
|
float distToGhost = (attackTarget->position - position).norm();
|
||||||
|
if (distToGhost >= 10.f)
|
||||||
|
{
|
||||||
|
if (isPlayer)
|
||||||
|
{
|
||||||
|
setTarget(attackTarget->position);
|
||||||
|
}
|
||||||
|
battle_state = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (distToGhost < 10.0f && distToGhost >= 1.f) {
|
||||||
|
setTarget(attackTarget->position);
|
||||||
|
battle_state = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
battle_state = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!pathWaypoints.empty() && currentWaypointIndex < pathWaypoints.size()) {
|
if (!pathWaypoints.empty() && currentWaypointIndex < pathWaypoints.size()) {
|
||||||
activeTarget = pathWaypoints[currentWaypointIndex];
|
activeTarget = pathWaypoints[currentWaypointIndex];
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
activeTarget = walkTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (battle_state == 0)
|
||||||
|
{
|
||||||
|
lookTarget = activeTarget;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lookTarget = attackTarget->position;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetFacingAngle = facingAngle;
|
||||||
|
|
||||||
|
if (currentState == AnimationState::WALK || currentState == AnimationState::STAND)
|
||||||
|
{
|
||||||
Eigen::Vector3f toTarget = activeTarget - position;
|
Eigen::Vector3f toTarget = activeTarget - position;
|
||||||
toTarget.y() = 0.f;
|
toTarget.y() = 0.f;
|
||||||
float dist = toTarget.norm();
|
float dist = toTarget.norm();
|
||||||
@ -96,21 +153,15 @@ void Character::update(int64_t deltaMs) {
|
|||||||
if (moveAmount >= dist) {
|
if (moveAmount >= dist) {
|
||||||
position = activeTarget;
|
position = activeTarget;
|
||||||
position.y() = 0.f;
|
position.y() = 0.f;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
position += dir * moveAmount;
|
position += dir * moveAmount;
|
||||||
}
|
}
|
||||||
targetFacingAngle = atan2(dir.x(), -dir.z());
|
targetFacingAngle = atan2(dir.x(), -dir.z());
|
||||||
if (battle_state == 0 && currentState == AnimationState::STAND)
|
|
||||||
{
|
|
||||||
currentState = AnimationState::WALK;
|
currentState = AnimationState::WALK;
|
||||||
}
|
}
|
||||||
} else {
|
else {
|
||||||
if (battle_state == 0 &&
|
|
||||||
currentState == AnimationState::WALK
|
|
||||||
)
|
|
||||||
{
|
|
||||||
currentState = AnimationState::STAND;
|
currentState = AnimationState::STAND;
|
||||||
}
|
|
||||||
|
|
||||||
const bool hasNextWaypoint = !pathWaypoints.empty() && currentWaypointIndex + 1 < pathWaypoints.size();
|
const bool hasNextWaypoint = !pathWaypoints.empty() && currentWaypointIndex + 1 < pathWaypoints.size();
|
||||||
if (hasNextWaypoint) {
|
if (hasNextWaypoint) {
|
||||||
@ -126,66 +177,15 @@ void Character::update(int64_t deltaMs) {
|
|||||||
onArrivedCallback = nullptr;
|
onArrivedCallback = nullptr;
|
||||||
cb();
|
cb();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (battle_state == 1 && attack == 1 && attackTarget != nullptr)
|
targetFacingAngle = facingAngle;
|
||||||
{
|
|
||||||
targetFacingAngle = atan2(attackTarget->position.x() - position.x(), -(attackTarget->position.z() - position.z()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rotate toward target facing angle at constant angular speed
|
|
||||||
float angleDiff = targetFacingAngle - facingAngle;
|
|
||||||
while (angleDiff > static_cast<float>(M_PI)) angleDiff -= 2.f * static_cast<float>(M_PI);
|
|
||||||
while (angleDiff < -static_cast<float>(M_PI)) angleDiff += 2.f * static_cast<float>(M_PI);
|
|
||||||
float rotStep = rotationSpeed * static_cast<float>(deltaMs) / 1000.f;
|
|
||||||
if (std::fabs(angleDiff) <= rotStep) {
|
|
||||||
facingAngle = targetFacingAngle;
|
|
||||||
} else {
|
|
||||||
facingAngle += (angleDiff > 0.f ? rotStep : -rotStep);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (canAttack && attackTarget != nullptr)
|
|
||||||
{
|
|
||||||
float distToGhost = (attackTarget->position - position).norm();
|
|
||||||
if (distToGhost >= 10.f)
|
|
||||||
{
|
|
||||||
if (isPlayer)
|
|
||||||
{
|
|
||||||
setTarget(attackTarget->position);
|
|
||||||
}
|
|
||||||
if (battle_state != 0)
|
|
||||||
{
|
|
||||||
battle_state = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (distToGhost < 10.0f && distToGhost >= 1.f) {
|
|
||||||
setTarget(attackTarget->position);
|
|
||||||
if (battle_state != 0)
|
|
||||||
{
|
|
||||||
battle_state = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
setTarget(position);
|
|
||||||
if (battle_state != 1)
|
|
||||||
{
|
|
||||||
battle_state = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attackTarget == nullptr)
|
|
||||||
{
|
|
||||||
if (battle_state != 0)
|
|
||||||
{
|
|
||||||
battle_state = 0;
|
|
||||||
attack = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (battle_state == 1)
|
if (battle_state == 1)
|
||||||
{
|
{
|
||||||
|
targetFacingAngle = atan2(lookTarget.x() - position.x(), -(lookTarget.z() - position.z()));
|
||||||
|
|
||||||
if (currentState == AnimationState::STAND || currentState == AnimationState::WALK) {
|
if (currentState == AnimationState::STAND || currentState == AnimationState::WALK) {
|
||||||
currentState = AnimationState::STAND_TO_ACTION;
|
currentState = AnimationState::STAND_TO_ACTION;
|
||||||
resetAnim = true;
|
resetAnim = true;
|
||||||
@ -218,6 +218,19 @@ void Character::update(int64_t deltaMs) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rotate toward target facing angle at constant angular speed
|
||||||
|
float angleDiff = targetFacingAngle - facingAngle;
|
||||||
|
while (angleDiff > static_cast<float>(M_PI)) angleDiff -= 2.f * static_cast<float>(M_PI);
|
||||||
|
while (angleDiff < -static_cast<float>(M_PI)) angleDiff += 2.f * static_cast<float>(M_PI);
|
||||||
|
float rotStep = rotationSpeed * static_cast<float>(deltaMs) / 1000.f;
|
||||||
|
if (std::fabs(angleDiff) <= rotStep) {
|
||||||
|
facingAngle = targetFacingAngle;
|
||||||
|
} else {
|
||||||
|
facingAngle += (angleDiff > 0.f ? rotStep : -rotStep);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
auto it = animations.find(currentState);
|
auto it = animations.find(currentState);
|
||||||
if (it == animations.end()) return;
|
if (it == animations.end()) return;
|
||||||
|
|
||||||
@ -229,7 +242,22 @@ void Character::update(int64_t deltaMs) {
|
|||||||
anim.currentFrame = 0;
|
anim.currentFrame = 0;
|
||||||
}
|
}
|
||||||
anim.currentFrame += static_cast<float>(deltaMs) / 24.f;
|
anim.currentFrame += static_cast<float>(deltaMs) / 24.f;
|
||||||
|
|
||||||
//std::cout << "Current animation frame: " << anim.currentFrame << " / " << anim.totalFrames << " -- " << anim.lastFrame << std::endl;
|
//std::cout << "Current animation frame: " << anim.currentFrame << " / " << anim.totalFrames << " -- " << anim.lastFrame << std::endl;
|
||||||
|
|
||||||
|
if (static_cast<int>(anim.currentFrame) >= 20 && currentState == AnimationState::STAND_TO_ACTION)
|
||||||
|
{
|
||||||
|
showWeapon = true;
|
||||||
|
}
|
||||||
|
else if (static_cast<int>(anim.currentFrame) <= 32 && currentState == AnimationState::ACTION_TO_STAND)
|
||||||
|
{
|
||||||
|
showWeapon = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
showWeapon = false;
|
||||||
|
}
|
||||||
|
|
||||||
int frms = anim.model.animations[0].keyFrames[anim.model.animations[0].keyFrames.size() - 1].frame;
|
int frms = anim.model.animations[0].keyFrames[anim.model.animations[0].keyFrames.size() - 1].frame;
|
||||||
if (static_cast<int>(anim.currentFrame) >= frms) {
|
if (static_cast<int>(anim.currentFrame) >= frms) {
|
||||||
anim.currentFrame = anim.model.startingFrame;
|
anim.currentFrame = anim.model.startingFrame;
|
||||||
@ -255,9 +283,8 @@ void Character::update(int64_t deltaMs) {
|
|||||||
|
|
||||||
|
|
||||||
if (static_cast<int>(anim.currentFrame) != anim.lastFrame) {
|
if (static_cast<int>(anim.currentFrame) != anim.lastFrame) {
|
||||||
if (useGpuSkinning) {
|
|
||||||
anim.gpuSkinningShaderData.ComputeSkinningMatrices(anim.model.startBones, anim.model.animations[0].keyFrames, static_cast<int>(anim.currentFrame));
|
anim.gpuSkinningShaderData.ComputeSkinningMatrices(anim.model.startBones, anim.model.animations[0].keyFrames, static_cast<int>(anim.currentFrame));
|
||||||
} else {
|
if (!useGpuSkinning) {
|
||||||
anim.model.Interpolate(static_cast<int>(anim.currentFrame));
|
anim.model.Interpolate(static_cast<int>(anim.currentFrame));
|
||||||
}
|
}
|
||||||
anim.lastFrame = static_cast<int>(anim.currentFrame);
|
anim.lastFrame = static_cast<int>(anim.currentFrame);
|
||||||
@ -294,6 +321,9 @@ void Character::draw(Renderer& renderer) {
|
|||||||
renderer.DrawVertexRenderStruct(modelMutable);
|
renderer.DrawVertexRenderStruct(modelMutable);
|
||||||
|
|
||||||
renderer.PopMatrix();
|
renderer.PopMatrix();
|
||||||
|
|
||||||
|
drawAttachedWeapon(renderer);
|
||||||
|
|
||||||
renderer.PopProjectionMatrix();
|
renderer.PopProjectionMatrix();
|
||||||
renderer.shaderManager.PopShader();
|
renderer.shaderManager.PopShader();
|
||||||
}
|
}
|
||||||
@ -353,6 +383,12 @@ void Character::drawGpuSkinning(Renderer& renderer) {
|
|||||||
anim.gpuSkinningShaderData.RenderVBO(renderer);
|
anim.gpuSkinningShaderData.RenderVBO(renderer);
|
||||||
|
|
||||||
renderer.PopMatrix();
|
renderer.PopMatrix();
|
||||||
|
|
||||||
|
renderer.shaderManager.PushShader(defaultShaderName);
|
||||||
|
renderer.RenderUniform1i(textureUniformName, 0);
|
||||||
|
drawAttachedWeapon(renderer);
|
||||||
|
renderer.shaderManager.PopShader();
|
||||||
|
|
||||||
renderer.PopProjectionMatrix();
|
renderer.PopProjectionMatrix();
|
||||||
renderer.shaderManager.PopShader();
|
renderer.shaderManager.PopShader();
|
||||||
}
|
}
|
||||||
@ -385,6 +421,38 @@ bool Character::prepareGpuSkinning()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Character::drawAttachedWeapon(Renderer& renderer)
|
||||||
|
{
|
||||||
|
if (!showWeapon) return;
|
||||||
|
if (weaponMesh.data.PositionData.size() == 0 || !weaponTexture) return;
|
||||||
|
|
||||||
|
auto it = animations.find(resolveActiveState());
|
||||||
|
if (it == animations.end()) return;
|
||||||
|
|
||||||
|
auto& anim = it->second;
|
||||||
|
if (anim.gpuSkinningShaderData.skinningMatrices.empty()) return;
|
||||||
|
|
||||||
|
int boneIdx = anim.model.findBoneIndex(weaponAttachBoneName);
|
||||||
|
if (boneIdx < 0) return;
|
||||||
|
|
||||||
|
const Eigen::Matrix4f& bindBone = anim.model.animations[0].keyFrames[0].bones[boneIdx].boneMatrixWorld;
|
||||||
|
Eigen::Matrix4f currentBone = anim.gpuSkinningShaderData.skinningMatrices[boneIdx] * bindBone;
|
||||||
|
|
||||||
|
renderer.PushMatrix();
|
||||||
|
renderer.TranslateMatrix({ position.x(), position.y(), position.z() });
|
||||||
|
renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-facingAngle, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||||
|
renderer.ScaleMatrix(modelScale);
|
||||||
|
renderer.RotateMatrix(modelCorrectionRotation.toRotationMatrix());
|
||||||
|
renderer.TranslateMatrix(Eigen::Vector3f(currentBone.block<3, 1>(0, 3)));
|
||||||
|
renderer.RotateMatrix(Eigen::Matrix3f(currentBone.block<3, 3>(0, 0)));
|
||||||
|
renderer.TranslateMatrix(weaponInitialPosition);
|
||||||
|
renderer.RotateMatrix(weaponInitialRotation);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, weaponTexture->getTexID());
|
||||||
|
renderer.DrawVertexRenderStruct(weaponMesh);
|
||||||
|
renderer.PopMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== Shadow depth pass ====================
|
// ==================== Shadow depth pass ====================
|
||||||
|
|
||||||
void Character::drawShadowDepth(Renderer& renderer) {
|
void Character::drawShadowDepth(Renderer& renderer) {
|
||||||
@ -412,8 +480,9 @@ void Character::drawShadowDepthCpu(Renderer& renderer) {
|
|||||||
modelMutable.AssignFrom(anim.model.mesh);
|
modelMutable.AssignFrom(anim.model.mesh);
|
||||||
modelMutable.RefreshVBO();
|
modelMutable.RefreshVBO();
|
||||||
renderer.DrawVertexRenderStruct(modelMutable);
|
renderer.DrawVertexRenderStruct(modelMutable);
|
||||||
|
|
||||||
renderer.PopMatrix();
|
renderer.PopMatrix();
|
||||||
|
|
||||||
|
drawAttachedWeapon(renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Character::drawShadowDepthGpuSkinning(Renderer& renderer) {
|
void Character::drawShadowDepthGpuSkinning(Renderer& renderer) {
|
||||||
@ -453,6 +522,8 @@ void Character::drawShadowDepthGpuSkinning(Renderer& renderer) {
|
|||||||
renderer.PopMatrix();
|
renderer.PopMatrix();
|
||||||
renderer.shaderManager.PopShader();
|
renderer.shaderManager.PopShader();
|
||||||
|
|
||||||
|
drawAttachedWeapon(renderer);
|
||||||
|
|
||||||
CheckGlError(__FILE__, __LINE__);
|
CheckGlError(__FILE__, __LINE__);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -500,6 +571,12 @@ void Character::drawCpuWithShadow(Renderer& renderer, const Eigen::Matrix4f& lig
|
|||||||
renderer.DrawVertexRenderStruct(modelMutable);
|
renderer.DrawVertexRenderStruct(modelMutable);
|
||||||
|
|
||||||
renderer.PopMatrix();
|
renderer.PopMatrix();
|
||||||
|
|
||||||
|
renderer.shaderManager.PushShader(defaultShaderName);
|
||||||
|
renderer.RenderUniform1i(textureUniformName, 0);
|
||||||
|
drawAttachedWeapon(renderer);
|
||||||
|
renderer.shaderManager.PopShader();
|
||||||
|
|
||||||
renderer.PopProjectionMatrix();
|
renderer.PopProjectionMatrix();
|
||||||
renderer.shaderManager.PopShader();
|
renderer.shaderManager.PopShader();
|
||||||
}
|
}
|
||||||
@ -553,9 +630,16 @@ void Character::drawGpuSkinningWithShadow(Renderer& renderer, const Eigen::Matri
|
|||||||
CheckGlError(__FILE__, __LINE__);
|
CheckGlError(__FILE__, __LINE__);
|
||||||
it->second.gpuSkinningShaderData.RenderVBO(renderer);
|
it->second.gpuSkinningShaderData.RenderVBO(renderer);
|
||||||
|
|
||||||
|
renderer.PopMatrix();
|
||||||
|
|
||||||
|
renderer.shaderManager.PushShader(defaultShaderName);
|
||||||
|
renderer.RenderUniform1i(textureUniformName, 0);
|
||||||
|
drawAttachedWeapon(renderer);
|
||||||
|
renderer.shaderManager.PopShader();
|
||||||
|
|
||||||
CheckGlError(__FILE__, __LINE__);
|
CheckGlError(__FILE__, __LINE__);
|
||||||
|
|
||||||
renderer.PopMatrix();
|
|
||||||
renderer.PopProjectionMatrix();
|
renderer.PopProjectionMatrix();
|
||||||
renderer.shaderManager.PopShader();
|
renderer.shaderManager.PopShader();
|
||||||
|
|
||||||
|
|||||||
@ -75,6 +75,13 @@ public:
|
|||||||
|
|
||||||
float interactionRadius = 0.0f;
|
float interactionRadius = 0.0f;
|
||||||
|
|
||||||
|
VertexRenderStruct weaponMesh;
|
||||||
|
std::shared_ptr<Texture> weaponTexture;
|
||||||
|
Eigen::Matrix3f weaponInitialRotation = Eigen::Matrix3f::Identity();
|
||||||
|
Eigen::Vector3f weaponInitialPosition = Eigen::Vector3f::Zero();
|
||||||
|
std::string weaponAttachBoneName = "RightHand";
|
||||||
|
bool showWeapon = true;
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@ -99,6 +106,10 @@ private:
|
|||||||
|
|
||||||
bool prepareGpuSkinning();
|
bool prepareGpuSkinning();
|
||||||
|
|
||||||
|
// Draws the weapon mesh attached to weaponAttachBoneName.
|
||||||
|
// Caller must ensure the right shader is active with matching uniforms.
|
||||||
|
void drawAttachedWeapon(Renderer& renderer);
|
||||||
|
|
||||||
// GPU skinning: draw using shader-based skinning
|
// GPU skinning: draw using shader-based skinning
|
||||||
void drawGpuSkinning(Renderer& renderer);
|
void drawGpuSkinning(Renderer& renderer);
|
||||||
// Shadow: draw into depth map (no texture, no projection push)
|
// Shadow: draw into depth map (no texture, no projection push)
|
||||||
|
|||||||
10
src/Game.cpp
10
src/Game.cpp
@ -503,10 +503,20 @@ namespace ZL
|
|||||||
currentLocation->dialogueSystem.startDialogue("test_cutscene_pan_dialogue");
|
currentLocation->dialogueSystem.startDialogue("test_cutscene_pan_dialogue");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SDLK_o:
|
||||||
|
y = y + 0.002;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDLK_k:
|
||||||
|
y = y - 0.002;
|
||||||
|
break;
|
||||||
|
|
||||||
case SDLK_p:
|
case SDLK_p:
|
||||||
|
x = x + 0.002;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDLK_l:
|
case SDLK_l:
|
||||||
|
x = x - 0.002;
|
||||||
break;
|
break;
|
||||||
case SDLK_RETURN:
|
case SDLK_RETURN:
|
||||||
|
|
||||||
|
|||||||
@ -41,23 +41,50 @@ namespace ZL
|
|||||||
interactiveObjects = GameObjectLoader::loadAndCreateInteractiveObjects("resources/config2/gameobjects.json", renderer, CONST_ZIP_FILE);
|
interactiveObjects = GameObjectLoader::loadAndCreateInteractiveObjects("resources/config2/gameobjects.json", renderer, CONST_ZIP_FILE);
|
||||||
|
|
||||||
|
|
||||||
auto playerTexture = std::make_shared<Texture>(CreateTextureDataFromPng("resources/w/gg/IMG_20260413_182354_992.png", CONST_ZIP_FILE));
|
//auto playerTexture = std::make_shared<Texture>(CreateTextureDataFromPng("resources/w/gg/IMG_20260413_182354_992.png", CONST_ZIP_FILE));
|
||||||
|
auto playerTexture = std::make_shared<Texture>(CreateTextureDataFromPng("resources/w/gg/UniV_Grid_2K_Base_color.png", CONST_ZIP_FILE));
|
||||||
|
|
||||||
player = std::make_unique<Character>();
|
player = std::make_unique<Character>();
|
||||||
|
/*
|
||||||
player->loadBinaryAnimation(AnimationState::STAND, "resources/w/gg/gg_stand_idle001.anim", CONST_ZIP_FILE);
|
player->loadBinaryAnimation(AnimationState::STAND, "resources/w/gg/gg_stand_idle001.anim", CONST_ZIP_FILE);
|
||||||
player->loadBinaryAnimation(AnimationState::WALK, "resources/w/gg/gg_walking001.anim", CONST_ZIP_FILE);
|
player->loadBinaryAnimation(AnimationState::WALK, "resources/w/gg/gg_walking001.anim", CONST_ZIP_FILE);
|
||||||
player->loadBinaryAnimation(AnimationState::STAND_TO_ACTION, "resources/w/gg/gg_stand_to_action002.anim", CONST_ZIP_FILE);
|
player->loadBinaryAnimation(AnimationState::STAND_TO_ACTION, "resources/w/gg/gg_stand_to_action002.anim", CONST_ZIP_FILE);
|
||||||
player->loadBinaryAnimation(AnimationState::ACTION_ATTACK, "resources/w/gg/gg_action_attack001.anim", CONST_ZIP_FILE);
|
player->loadBinaryAnimation(AnimationState::ACTION_ATTACK, "resources/w/gg/gg_action_attack001.anim", CONST_ZIP_FILE);
|
||||||
player->loadBinaryAnimation(AnimationState::ACTION_IDLE, "resources/w/gg/gg_action_idle001.anim", CONST_ZIP_FILE);
|
player->loadBinaryAnimation(AnimationState::ACTION_IDLE, "resources/w/gg/gg_action_idle001.anim", CONST_ZIP_FILE);
|
||||||
player->loadBinaryAnimation(AnimationState::ACTION_TO_STAND, "resources/w/gg/gg_action_to_stand001.anim", CONST_ZIP_FILE);
|
player->loadBinaryAnimation(AnimationState::ACTION_TO_STAND, "resources/w/gg/gg_action_to_stand001.anim", CONST_ZIP_FILE);
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
player->loadBinaryAnimation(AnimationState::STAND, "resources/w/gg/new/gg_stand_idle001.anim", CONST_ZIP_FILE);
|
||||||
|
player->loadBinaryAnimation(AnimationState::WALK, "resources/w/gg/new/gg_walk001.anim", CONST_ZIP_FILE);
|
||||||
|
player->loadBinaryAnimation(AnimationState::STAND_TO_ACTION, "resources/w/gg/new/gg_stand_to_action001.anim", CONST_ZIP_FILE);
|
||||||
|
|
||||||
|
player->loadBinaryAnimation(AnimationState::ACTION_ATTACK, "resources/w/gg/gg_action_attack001.anim", CONST_ZIP_FILE);
|
||||||
|
player->loadBinaryAnimation(AnimationState::ACTION_IDLE, "resources/w/gg/gg_action_idle001.anim", CONST_ZIP_FILE);
|
||||||
|
player->loadBinaryAnimation(AnimationState::ACTION_TO_STAND, "resources/w/gg/new/gg_action_to_stand001.anim", CONST_ZIP_FILE);
|
||||||
|
|
||||||
|
player->weaponTexture = std::make_shared<Texture>(CreateTextureDataFromPng("resources/w/white.png", CONST_ZIP_FILE));
|
||||||
|
//player->weaponMesh.AssignFrom(LoadFromTextFile02("resources/w/gg/knife001.txt", CONST_ZIP_FILE));
|
||||||
|
|
||||||
|
player->weaponMesh.data = LoadFromTextFile02("resources/w/gg/knife002.txt", CONST_ZIP_FILE);
|
||||||
|
player->weaponMesh.data.Scale(0.1f);
|
||||||
|
//player->weaponMesh.data.RotateByMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-M_PI * 0.5, Eigen::Vector3f::UnitY())).toRotationMatrix());
|
||||||
|
player->weaponMesh.RefreshVBO();
|
||||||
|
player->weaponAttachBoneName = "RightHand";
|
||||||
|
player->weaponInitialRotation = Eigen::AngleAxisf(-M_PI * 0.5, Eigen::Vector3f::UnitZ()).toRotationMatrix();
|
||||||
|
player->weaponInitialPosition = Eigen::Vector3f(0, 0.09, 0.016);
|
||||||
|
|
||||||
player->setTexture(playerTexture);
|
player->setTexture(playerTexture);
|
||||||
player->walkSpeed = 3.0f;
|
player->walkSpeed = 3.0f;
|
||||||
player->rotationSpeed = 8.0f;
|
player->rotationSpeed = 8.0f;
|
||||||
|
|
||||||
player->modelScale = 1.f;
|
player->modelScale = 1.f;
|
||||||
player->modelCorrectionRotation = Eigen::Quaternionf(Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitY()));
|
player->modelCorrectionRotation = Eigen::Quaternionf(
|
||||||
|
Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitY())
|
||||||
|
*
|
||||||
|
Eigen::AngleAxisf(-M_PI*0.5, Eigen::Vector3f::UnitX())
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
player->canAttack = true;
|
player->canAttack = true;
|
||||||
player->isPlayer = true;
|
player->isPlayer = true;
|
||||||
@ -295,14 +322,6 @@ namespace ZL
|
|||||||
renderer.DrawVertexRenderStruct(gameObj.mesh);
|
renderer.DrawVertexRenderStruct(gameObj.mesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
glBindTexture(GL_TEXTURE_2D, fireboxTexture->getTexID());
|
|
||||||
renderer.DrawVertexRenderStruct(fireboxMesh);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, inaiTexture->getTexID());
|
|
||||||
renderer.DrawVertexRenderStruct(inaiMesh);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, benchTexture->getTexID());
|
|
||||||
renderer.DrawVertexRenderStruct(benchMesh);
|
|
||||||
*/
|
|
||||||
for (auto& intObj : interactiveObjects) {
|
for (auto& intObj : interactiveObjects) {
|
||||||
if (intObj.isActive) {
|
if (intObj.isActive) {
|
||||||
intObj.draw(renderer);
|
intObj.draw(renderer);
|
||||||
@ -499,7 +518,14 @@ namespace ZL
|
|||||||
player->update(delta);
|
player->update(delta);
|
||||||
dialogueSystem.update(static_cast<int>(delta), player->position);
|
dialogueSystem.update(static_cast<int>(delta), player->position);
|
||||||
}
|
}
|
||||||
for (auto& npc : npcs) npc->update(delta);
|
for (auto& npc : npcs)
|
||||||
|
{
|
||||||
|
if (npc->canAttack)
|
||||||
|
{
|
||||||
|
npc->update(delta);
|
||||||
|
}
|
||||||
|
//npc->update(delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -573,6 +599,7 @@ namespace ZL
|
|||||||
|
|
||||||
targetInteractiveObject = clickedObject;
|
targetInteractiveObject = clickedObject;
|
||||||
player->setTarget(clickedObject->position);
|
player->setTarget(clickedObject->position);
|
||||||
|
player->attackTarget = nullptr;
|
||||||
std::cout << "[CLICK] Player moving to object..." << std::endl;
|
std::cout << "[CLICK] Player moving to object..." << std::endl;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -602,6 +629,10 @@ namespace ZL
|
|||||||
{
|
{
|
||||||
player->attackTarget = clickedNpc;
|
player->attackTarget = clickedNpc;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
player->attackTarget = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -611,10 +642,8 @@ namespace ZL
|
|||||||
Eigen::Vector3f hit = camPos + rayDir * t;
|
Eigen::Vector3f hit = camPos + rayDir * t;
|
||||||
std::cout << "[CLICK] Clicked on ground at: (" << hit.x() << ", " << hit.z() << ")" << std::endl;
|
std::cout << "[CLICK] Clicked on ground at: (" << hit.x() << ", " << hit.z() << ")" << std::endl;
|
||||||
|
|
||||||
if (player->currentState == AnimationState::STAND || player->currentState == AnimationState::WALK)
|
|
||||||
{
|
|
||||||
player->setTarget(Eigen::Vector3f(hit.x(), 0.f, hit.z()));
|
player->setTarget(Eigen::Vector3f(hit.x(), 0.f, hit.z()));
|
||||||
}
|
player->attackTarget = nullptr;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
std::cout << "[CLICK] No valid target found" << std::endl;
|
std::cout << "[CLICK] No valid target found" << std::endl;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user