initial commit

This commit is contained in:
2023-09-13 03:04:01 +03:00
commit 22482cc9b2
58 changed files with 2051448 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
*.o
*.pyc
.import

8
.gitmodules vendored Normal file
View File

@@ -0,0 +1,8 @@
[submodule "src/godot"]
path = src/godot
url = https://github.com/godotengine/godot
branch = 3.x
[submodule "src/modules/character/flecs"]
path = src/modules/character/flecs
url = https://github.com/SanderMertens/flecs
branch = master

17
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,17 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Run Gym",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceRoot}/src/godot/bin/godot.x11.opt.tools.64",
"args": ["entries/main.tscn"],
"cwd": "${workspaceRoot}/godot",
"setupCommands": [{"text": "source ${workspaceRoot}/debug.py"}]
}
]
}

11
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,11 @@
{
"godot_tools.editor_path": "/home/slapin/godot-projects/office/src/godot/bin/godot.x11.opt.tools.64",
"files.associations": {
"*.ipp": "cpp",
"memory": "cpp",
"future": "cpp",
"istream": "cpp",
"functional": "cpp",
"numbers": "cpp"
}
}

27
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,27 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "build",
"type": "shell",
"command": "make",
"args": [
"godot-editor-main"
],
"group": {
"kind": "build",
"isDefault": true
},
"dependsOn":[
]
},
{
"label": "run-gym",
"type": "shell",
"command": "../src/godot/bin/godot.x11.opt.tools.64",
"args": ["locations/gym.tscn"]
}
]
}

31
Makefile Normal file
View File

@@ -0,0 +1,31 @@
BLENDER = ../../blender-3.3.1-linux-x64/blender
SERVER = src/godot/bin/godot_server.x11.opt.tools.64
.PHONY: all godot-editor-main export export-models export-clothes export-clean export-linux-demo export-windows-demo export-binaries patch
all: godot-editor-main godot-main
godot-main: patch
cd src/godot; \
scons platform=x11 target=release tools=no custom_modules=../modules -j16; \
scons platform=x11 target=release_debug tools=no custom_modules=../modules -j16;
cd src/godot; \
scons platform=x11 target=release tools=no custom_modules=../modules -j16; \
scons platform=server target=release_debug tools=yes custom_modules=../modules -j16
godot-editor-main: patch
cd src/godot; \
scons platform=x11 target=release_debug tools=yes custom_modules=../modules -j16;
patch: ./src/godot/scene/animation/skeleton_ik.cpp
cd ./src/godot && git reset --hard HEAD && for p in ../patches/*.patch; do git apply $$p; done
sed -e 's/ERR_FAIL_COND_V(-1 == p_task->root_bone, false);//g' -i ./src/godot/scene/animation/skeleton_ik.cpp
export: export-models export-clothes
rm -Rf godot/.import
export-clean:
rm -f assets/blender/scripts/tmp-*.blend
export-models: export-clean
$(BLENDER) -b -Y -P assets/blender/scripts/export_models.py
export-clothes: export-clean
$(BLENDER) -b -Y -P assets/blender/scripts/export_clothes.py

12
TODO Normal file
View File

@@ -0,0 +1,12 @@
1. Flexible clothing/customization system
1.1 Clothing slot system without node tree.
1.2 Character display without node tree. (?)
1.3 Character body morph system with bones AND shapes
and pipeline for blend shapes; layered clothing optimizations.
1.4 Separate physical skirt rig(s) with automatic updates
(needed for long dresses)
1.5 3 rig system for character animation with physics.
2. Port LOD system from 4.x
3. Port AnimationTree expressions from 4.x
4. Port animation retarget system from 4.x

Binary file not shown.

View File

@@ -0,0 +1,61 @@
import bpy
from math import radians, pi
def clamp_angle_deg(angle, min_angle_deg, max_angle_deg):
min_angle = radians(min_angle_deg)
max_angle = radians(max_angle_deg)
if angle < min_angle:
angle = min_angle
if angle > max_angle:
angle = max_angle
return angle
def angle_to_linear(angle, divider):
if angle < 0.0:
return angle / divider
else:
return 0.0
def angle_to_linear_x(bone, angle):
skel = bpy.data.objects["skeleton"]
left_base = "ctrl_base_upperleg.L.001"
right_base = "ctrl_base_upperleg.R.001"
base = ""
if base == "":
for e in ["_R", ".R"]:
if bone.name.endswith(e):
base = right_base
break
if base == "":
for e in ["_L", ".L"]:
if bone.name.endswith(e):
base = left_base
break
if base == "":
for e in ["_R.", ".R."]:
if bone.name.find(e) >= 0:
base = right_base
break
if base == "":
for e in ["_L.", ".L."]:
if bone.name.find(e) >= 0:
base = left_base
break
mul = skel.pose.bones[base]["to_linear_x_base"]
offset = skel.pose.bones[base]["angle_offset"]
# print("bone: ", bone.name, "base: ", base, "mul: ", mul)
# print("angle: ", angle, " angle_offset: ", offset, " angle_sum: ", angle + offset)
print("offset: ", mul * (angle + offset), "bone: ", base, "angle: ", angle)
return (angle + offset) * mul
def extra_linear(angle, offset):
ret = 0.0
offt = offset * angle * 2.0 / -radians(-90)
if angle * 2.0 < -radians(65):
if angle * 2.0 > -radians(65):
ret += offset
else:
ret += offt
return ret
bpy.app.driver_namespace["clamp_angle_deg"] = clamp_angle_deg
bpy.app.driver_namespace["angle_to_linear"] = angle_to_linear
bpy.app.driver_namespace["extra_linear"] = extra_linear
bpy.app.driver_namespace["angle_to_linear_x"] = angle_to_linear_x

View File

@@ -0,0 +1,371 @@
#!/usr/bin/env python
import os, time
import bpy
from math import pi
import glob
from mathutils import Vector, Matrix
from math import radians, pi
import json
class ExportMappingFemale:
char_blend_path = "assets/blender/vroid1-female.blend"
cloth_blend_paths = ["assets/blender/female-coat2.blend"]
gltf_path = "godot/clothes/female-coat.gltf"
inner_path = "Object"
objs = ["skeleton", "body", "privates", "ref-bodyskirt-noimp", "ref-bodydress-noimp", "ref-bodycap-noimp", "ref-bodyshoes-noimp", "ref-dress-noimp", "ref-topskirt-noimp", "ref-skirt4-noimp", "ref-skirt3-noimp"]
objs_remove = []
armature_name = "skeleton"
outfile = "tmp-female-cloth.blend"
default_action = 'default'
sex = "female"
def __init__(self):
self.files = []
for fobj in self.objs:
self.files.append({"name": fobj})
class ExportMappingMale:
char_blend_path = "assets/blender/vroid1-man-animate.blend"
cloth_blend_paths = ["assets/blender/male-coat2.blend"]
gltf_path = "godot/clothes/male-coat.gltf"
inner_path = "Object"
objs = ["pxy", "body", "ref-topbottom-noimp", "ref-bodytopbottom-noimp"]
objs_remove = []
armature_name = "pxy"
outfile = "tmp-male-cloth.blend"
default_action = 'default'
sex = "male"
def __init__(self):
self.files = []
for fobj in self.objs:
self.files.append({"name": fobj})
basepath = os.getcwd()
def check_bone(bname):
ok = True
baddie = ["ctrl_", "mch_", "MCH_"]
for bd in baddie:
if bname.startswith(bd):
ok = False
break
return ok
def clamp_angle_deg(angle, min_angle_deg, max_angle_deg):
min_angle = radians(min_angle_deg)
max_angle = radians(max_angle_deg)
if angle < min_angle:
angle = min_angle
if angle > max_angle:
angle = max_angle
return angle
def angle_to_linear(angle, divider):
if angle < 0.0:
return angle / divider
else:
return 0.0
def angle_to_linear_x(bone, angle):
skel = bpy.data.objects["skeleton_orig"]
left_base = "ctrl_base_upperleg.L.001"
right_base = "ctrl_base_upperleg.R.001"
base = ""
if base == "":
for e in ["_R", ".R"]:
if bone.name.endswith(e):
base = right_base
break
if base == "":
for e in ["_L", ".L"]:
if bone.name.endswith(e):
base = left_base
break
if base == "":
for e in ["_R.", ".R."]:
if bone.name.find(e) >= 0:
base = right_base
break
if base == "":
for e in ["_L.", ".L."]:
if bone.name.find(e) >= 0:
base = left_base
break
mul = skel.pose.bones[base]["to_linear_x_base"]
offset = skel.pose.bones[base]["angle_offset"]
# print("bone: ", bone.name, "base: ", base, "mul: ", mul)
# print("angle: ", angle, " angle_offset: ", offset, " angle_sum: ", angle + offset)
print("offset: ", mul * (angle + offset), "bone: ", base, "angle: ", angle)
return (angle + offset) * mul
def extra_linear(angle, offset):
ret = 0.0
offt = offset * angle * 2.0 / -radians(-90)
if angle * 2.0 < -radians(65):
if angle * 2.0 > -radians(65):
ret += offset
else:
ret += offt
return ret
def prepare_armature(mapping):
print("Preparing...")
bpy.ops.object.armature_add(enter_editmode=False)
new_armature = bpy.context.object
orig_armature = bpy.data.objects[mapping.armature_name]
armature_name = orig_armature.name
orig_armature.name = orig_armature.name + "_orig"
new_armature.name = armature_name
queue = []
if new_armature.animation_data is None:
new_armature.animation_data_create()
bpy.context.view_layer.objects.active = new_armature
bpy.ops.object.mode_set(mode='EDIT')
for b in new_armature.data.edit_bones:
new_armature.data.edit_bones.remove(b)
bpy.context.view_layer.objects.active = orig_armature
bpy.ops.object.mode_set(mode='EDIT')
for b in orig_armature.data.edit_bones:
print(b.name)
if b.parent is None:
queue.append(b.name)
print("Copying bones...")
while len(queue) > 0:
item = queue.pop(0)
print(item)
itemb = orig_armature.data.edit_bones[item]
if not itemb.use_deform and not check_bone(item):
continue
for cb in orig_armature.data.edit_bones:
if cb.parent == itemb:
queue.append(cb.name)
nb = new_armature.data.edit_bones.new(item)
nb.name = item
nb.head = itemb.head
nb.tail = itemb.tail
nb.matrix = itemb.matrix
nb.use_deform = itemb.use_deform
if itemb.parent is not None:
ok = True
pname = itemb.parent.name
while not check_bone(pname):
bparent = itemb.parent.parent
if bparent is None:
ok = False
break
pname = bparent.name
if ok:
nb.parent = new_armature.data.edit_bones[itemb.parent.name]
else:
nb.parent = None
else:
nb.parent = None
nb.use_connect = itemb.use_connect
# drivers_data = new_armature.animation_data.drivers
print("Creating constraints...")
bpy.context.view_layer.objects.active = new_armature
bpy.ops.object.mode_set(mode='OBJECT')
bpy.context.view_layer.objects.active = orig_armature
bpy.ops.object.mode_set(mode='OBJECT')
for b in new_armature.pose.bones:
print(b.name)
c = b.constraints.new(type='COPY_TRANSFORMS')
c.target = orig_armature
c.subtarget = b.name
for obj in bpy.data.objects:
if obj.parent == orig_armature:
obj.parent = new_armature
for mod in obj.modifiers:
if mod.type == 'ARMATURE':
mod.object = new_armature
print("Baking actions...")
bpy.context.view_layer.objects.active = new_armature
bpy.ops.object.mode_set(mode='POSE')
for track in orig_armature.animation_data.nla_tracks:
print(track.name)
for s in track.strips:
action = s.action
print(action.name)
orig_armature.animation_data.action = action
new_armature.animation_data.action = None
bpy.context.view_layer.objects.active = new_armature
firstFrame = int(s.action_frame_start)
lastFrame = int(s.action_frame_end)
bpy.ops.nla.bake(frame_start=firstFrame, frame_end=lastFrame, step=5, only_selected=False, visual_keying=True, clear_constraints=False, clean_curves=True, use_current_action=False, bake_types={'POSE'})
aname = orig_armature.animation_data.action.name
orig_armature.animation_data.action.name = "bake_" + aname
new_armature.animation_data.action.name = aname
track = new_armature.animation_data.nla_tracks.new()
track.name = aname
track.strips.new(track.name, int(new_armature.animation_data.action.frame_range[0]), new_armature.animation_data.action)
track.mute = True
track.lock = True
print("Removing constraints...")
for b in new_armature.pose.bones:
for c in b.constraints:
b.constraints.remove(c)
new_armature.animation_data.action = bpy.data.actions[mapping.default_action]
bpy.context.view_layer.objects.active = new_armature
bpy.ops.object.mode_set(mode='OBJECT')
obj_data = {}
for mapping in [ExportMappingFemale(), ExportMappingMale()]:
print("Initializing...")
bpy.ops.wm.read_homefile(use_empty=True)
print("Preparing driver setup...")
bpy.app.driver_namespace["clamp_angle_deg"] = clamp_angle_deg
bpy.app.driver_namespace["angle_to_linear"] = angle_to_linear
bpy.app.driver_namespace["extra_linear"] = extra_linear
bpy.app.driver_namespace["angle_to_linear_x"] = angle_to_linear_x
print("Driver setup done...")
bpy.ops.wm.append(
filepath=os.path.join(mapping.char_blend_path, mapping.inner_path),
directory=os.path.join(mapping.char_blend_path, mapping.inner_path),
files=mapping.files)
for obj in bpy.data.objects:
if obj.name.startswith("bone-"):
bpy.data.objects.remove(obj)
print("Append character done...")
prepare_armature(mapping)
print("Armature done...")
print("Removing original armature and actions...")
orig_arm = bpy.data.objects[mapping.armature_name + '_orig']
bpy.data.objects.remove(orig_arm)
for act in bpy.data.actions:
if act.name.startswith("bake_"):
act.name = act.name + "-noimp"
for act in bpy.data.actions:
if act.name.startswith("bake_"):
bpy.data.actions.remove(act)
print("Removing original armature and actions done...")
for filepath in mapping.cloth_blend_paths:
with bpy.data.libraries.load(filepath) as (data_from, data_to):
data_to.objects = [ob for ob in data_from.objects if not ob.endswith('noimp')]
for obj in data_to.objects:
if obj.type == 'MESH':
bpy.context.scene.collection.objects.link(obj)
obj.parent = bpy.data.objects[mapping.armature_name]
arm_mod = obj.modifiers.new('armature', 'ARMATURE')
arm_mod.object = bpy.data.objects[mapping.armature_name]
for vg in obj.vertex_groups:
obj.vertex_groups.remove(vg)
bpy.ops.object.select_all(action='DESELECT')
bpy.data.objects[obj.name].select_set(True)
src_name = "body"
params = ""
otype = "nearest"
distance = 0.1
if "src_obj" in obj:
obname = obj["src_obj"]
if obname.find(";") >= 0:
src_name, params = obname.split(";");
vpar, vdist = params.split("=")
otype = vpar
distance = float(vdist)
else:
src_name = obname
if src_name not in bpy.data.objects:
src_name = "ref-" + src_name + "-noimp"
keywords = []
keywords.append(mapping.sex)
if "slot" in obj:
keywords.append(obj["slot"])
if "keywords" in obj:
kw = obj["keywords"].split(",")
for xtk in kw:
keywords.append(xtk.strip())
if "state" in obj:
keywords.append(obj["state"].strip())
else:
if obj.name.endswith("-damaged"):
keywords.append("damaged")
elif obj.name.endswith("-revealing"):
keywords.append("revealing")
else:
keywords.append("normal")
if "speciality" in obj:
keywords.append(obj["speciality"].strip())
else:
keywords.append("common")
keywords.append(obj.name.replace("-damaged", "").replace("-revealing", ""))
obj_data[obj.name] = {}
obj_data[obj.name]["keywords"] = keywords
sobj = src_name
vert_mapping = "NEAREST"
use_distance = True
bpy.data.objects[src_name].select_set(True)
bpy.context.view_layer.objects.active = bpy.data.objects[obj.name]
bpy.ops.paint.weight_paint_toggle()
if otype == "nearest":
vert_mapping = "NEAREST"
elif otype == "poly":
vert_mapping = "POLYINTERP_NEAREST"
if distance <= 0.0001:
use_distance = False
bpy.ops.object.data_transfer(use_reverse_transfer=True,
data_type='VGROUP_WEIGHTS', use_create=True,
vert_mapping = vert_mapping,
layers_select_src='NAME',
max_distance = distance,
use_max_distance = use_distance,
layers_select_dst='ALL')
bpy.ops.paint.weight_paint_toggle()
print("Append clothes done...")
for obj in bpy.data.objects:
if obj.name.endswith("noimp"):
bpy.data.objects.remove(obj)
elif obj.name == "body":
obj.name = "body-noimp"
else:
bpy.context.view_layer.objects.active = obj
for modifier in obj.modifiers:
if modifier.type != 'ARMATURE':
bpy.ops.object.modifier_apply(modifier = modifier.name)
# print("Removing original armature and actions...")
# orig_arm = bpy.data.objects[mapping.armature_name + '_orig']
# bpy.data.objects.remove(orig_arm)
# for act in bpy.data.actions:
# if act.name.startswith("bake_"):
# bpy.data.actions.remove(act)
# for obj in bpy.data.objects:
# if obj.type == 'MESH':
# if not obj.name in mapping.objs and obj.parent is None:
# if not obj.name.endswith("-noimp"):
# obj.name = obj.name + "-noimp"
for obj in bpy.data.objects:
if obj.name in mapping.objs_remove:
bpy.data.objects.remove(obj)
bpy.ops.wm.save_as_mainfile(filepath=(basepath + "/assets/blender/scripts/" + mapping.outfile))
bpy.ops.export_scene.gltf(filepath=mapping.gltf_path,
use_selection=False,
check_existing=False,
export_format='GLTF_SEPARATE',
export_texture_dir='textures', export_texcoords=True,
export_normals=True,
export_tangents=False,
export_materials='EXPORT',
export_colors=True,
use_mesh_edges=False,
use_mesh_vertices=False,
export_cameras=False,
use_visible=False,
use_renderable=False,
export_yup=True,
export_animations=False,
export_force_sampling=True,
export_def_bones=False,
export_current_frame=False,
export_morph=True,
export_morph_normal=True,
export_lights=False)
with open(basepath + "/godot/clothes/clothes.json", "w") as write_file:
json.dump(obj_data, write_file, indent=8)
bpy.ops.wm.read_homefile(use_empty=True)
time.sleep(2)
bpy.ops.wm.quit_blender()

View File

@@ -0,0 +1,265 @@
#!/usr/bin/env python
import os, time
import bpy
from math import pi
import glob
from mathutils import Vector, Matrix
from math import radians, pi
class ExportMappingFemale:
blend_path = "assets/blender/vroid1-female.blend"
gltf_path = "godot/vroid1-female.gltf"
inner_path = "Object"
objs = ["skeleton", "body", "privates", "dress-noimp", "skirt-noimp", "top-ref-noimp"]
armature_name = "skeleton"
outfile = "tmp-female.blend"
default_action = 'default'
def __init__(self):
self.files = []
for fobj in self.objs:
self.files.append({"name": fobj})
class ExportMappingMale:
blend_path = "assets/blender/vroid1-man-animate.blend"
gltf_path = "godot/vroid1-man-animate.gltf"
inner_path = "Object"
objs = ["pxy", "body", "penis", "bottom-ref-noimp", "top-ref-noimp"]
armature_name = "pxy"
outfile = "tmp-male.blend"
default_action = 'default'
def __init__(self):
self.files = []
for fobj in self.objs:
self.files.append({"name": fobj})
basepath = os.getcwd()
def check_bone(bname):
ok = True
baddie = ["ctrl_", "mch_", "MCH_"]
for bd in baddie:
if bname.startswith(bd):
ok = False
break
return ok
def prepare_armature(mapping):
print("Preparing...")
bpy.ops.object.armature_add(enter_editmode=False)
new_armature = bpy.context.object
orig_armature = bpy.data.objects[mapping.armature_name]
armature_name = orig_armature.name
orig_armature.name = orig_armature.name + "_orig"
new_armature.name = armature_name
queue = []
if new_armature.animation_data is None:
new_armature.animation_data_create()
bpy.context.view_layer.objects.active = new_armature
bpy.ops.object.mode_set(mode='EDIT')
for b in new_armature.data.edit_bones:
new_armature.data.edit_bones.remove(b)
bpy.context.view_layer.objects.active = orig_armature
bpy.ops.object.mode_set(mode='EDIT')
for b in orig_armature.data.edit_bones:
print(b.name)
if b.parent is None:
queue.append(b.name)
print("Copying bones...")
while len(queue) > 0:
item = queue.pop(0)
print(item)
itemb = orig_armature.data.edit_bones[item]
if not itemb.use_deform and not check_bone(item):
continue
for cb in orig_armature.data.edit_bones:
if cb.parent == itemb:
queue.append(cb.name)
nb = new_armature.data.edit_bones.new(item)
nb.name = item
nb.head = itemb.head
nb.tail = itemb.tail
nb.matrix = itemb.matrix
nb.use_deform = itemb.use_deform
if itemb.parent is not None:
ok = True
pname = itemb.parent.name
while not check_bone(pname):
bparent = itemb.parent.parent
if bparent is None:
ok = False
break
pname = bparent.name
if ok:
nb.parent = new_armature.data.edit_bones[itemb.parent.name]
else:
nb.parent = None
else:
nb.parent = None
nb.use_connect = itemb.use_connect
# drivers_data = new_armature.animation_data.drivers
print("Creating constraints...")
bpy.context.view_layer.objects.active = new_armature
bpy.ops.object.mode_set(mode='OBJECT')
bpy.context.view_layer.objects.active = orig_armature
bpy.ops.object.mode_set(mode='OBJECT')
for b in new_armature.pose.bones:
print(b.name)
c = b.constraints.new(type='COPY_TRANSFORMS')
c.target = orig_armature
c.subtarget = b.name
for obj in bpy.data.objects:
if obj.parent == orig_armature:
obj.parent = new_armature
for mod in obj.modifiers:
if mod.type == 'ARMATURE':
mod.object = new_armature
print("Baking actions...")
bpy.context.view_layer.objects.active = new_armature
bpy.ops.object.mode_set(mode='POSE')
for track in orig_armature.animation_data.nla_tracks:
print(track.name)
for s in track.strips:
action = s.action
print(action.name)
orig_armature.animation_data.action = action
new_armature.animation_data.action = None
bpy.context.view_layer.objects.active = new_armature
firstFrame = int(s.action_frame_start)
lastFrame = int(s.action_frame_end)
bpy.ops.nla.bake(frame_start=firstFrame, frame_end=lastFrame, step=5, only_selected=False, visual_keying=True, clear_constraints=False, clean_curves=True, use_current_action=False, bake_types={'POSE'})
aname = orig_armature.animation_data.action.name
orig_armature.animation_data.action.name = "bake_" + aname
new_armature.animation_data.action.name = aname
track = new_armature.animation_data.nla_tracks.new()
track.name = aname
track.strips.new(track.name, int(new_armature.animation_data.action.frame_range[0]), new_armature.animation_data.action)
track.mute = True
track.lock = True
print("Removing constraints...")
for b in new_armature.pose.bones:
for c in b.constraints:
b.constraints.remove(c)
new_armature.animation_data.action = bpy.data.actions[mapping.default_action]
bpy.context.view_layer.objects.active = new_armature
bpy.ops.object.mode_set(mode='OBJECT')
# track = new_armature.animation_data.nla_tracks.new()
# track.name = action.name
def clamp_angle_deg(angle, min_angle_deg, max_angle_deg):
min_angle = radians(min_angle_deg)
max_angle = radians(max_angle_deg)
if angle < min_angle:
angle = min_angle
if angle > max_angle:
angle = max_angle
return angle
def angle_to_linear(angle, divider):
if angle < 0.0:
return angle / divider
else:
return 0.0
def angle_to_linear_x(bone, angle):
skel = bpy.data.objects["skeleton_orig"]
left_base = "ctrl_base_upperleg.L.001"
right_base = "ctrl_base_upperleg.R.001"
base = ""
if base == "":
for e in ["_R", ".R"]:
if bone.name.endswith(e):
base = right_base
break
if base == "":
for e in ["_L", ".L"]:
if bone.name.endswith(e):
base = left_base
break
if base == "":
for e in ["_R.", ".R."]:
if bone.name.find(e) >= 0:
base = right_base
break
if base == "":
for e in ["_L.", ".L."]:
if bone.name.find(e) >= 0:
base = left_base
break
mul = skel.pose.bones[base]["to_linear_x_base"]
offset = skel.pose.bones[base]["angle_offset"]
# print("bone: ", bone.name, "base: ", base, "mul: ", mul)
# print("angle: ", angle, " angle_offset: ", offset, " angle_sum: ", angle + offset)
print("offset: ", mul * (angle + offset), "bone: ", base, "angle: ", angle)
return (angle + offset) * mul
def extra_linear(angle, offset):
ret = 0.0
offt = offset * angle * 2.0 / -radians(-90)
if angle * 2.0 < -radians(65):
if angle * 2.0 > -radians(65):
ret += offset
else:
ret += offt
return ret
for mapping in [ExportMappingFemale(), ExportMappingMale()]:
print("Initializing...")
bpy.ops.wm.read_homefile(use_empty=True)
print("Preparing driver setup...")
bpy.app.driver_namespace["clamp_angle_deg"] = clamp_angle_deg
bpy.app.driver_namespace["angle_to_linear"] = angle_to_linear
bpy.app.driver_namespace["extra_linear"] = extra_linear
bpy.app.driver_namespace["angle_to_linear_x"] = angle_to_linear_x
print("Driver setup done...")
bpy.ops.wm.append(
filepath=os.path.join(mapping.blend_path, mapping.inner_path),
directory=os.path.join(mapping.blend_path, mapping.inner_path),
files=mapping.files)
print("Append done...")
prepare_armature(mapping)
print("Armature done...")
print("Removing original armature and actions...")
orig_arm = bpy.data.objects[mapping.armature_name + '_orig']
bpy.data.objects.remove(orig_arm)
for act in bpy.data.actions:
if act.name.startswith("bake_"):
act.name = act.name + "-noimp"
for act in bpy.data.actions:
if act.name.startswith("bake_"):
bpy.data.actions.remove(act)
for obj in bpy.data.objects:
if obj.type == 'MESH':
if not obj.name in mapping.objs and obj.parent is None:
if not obj.name.endswith("-noimp"):
obj.name = obj.name + "-noimp"
bpy.ops.wm.save_as_mainfile(filepath=(basepath + "/assets/blender/scripts/" + mapping.outfile))
bpy.ops.export_scene.gltf(filepath=mapping.gltf_path,
use_selection=False,
check_existing=False,
export_format='GLTF_SEPARATE',
export_texture_dir='textures', export_texcoords=True,
export_normals=True,
export_tangents=True,
export_materials='EXPORT',
export_colors=True,
use_mesh_edges=False,
use_mesh_vertices=False,
export_cameras=False,
use_visible=False,
use_renderable=False,
export_yup=True,
export_animations=True,
export_force_sampling=True,
export_def_bones=False,
export_current_frame=False,
export_morph=True,
export_morph_normal=True,
export_lights=False)
bpy.ops.wm.read_homefile(use_empty=True)
time.sleep(2)
bpy.ops.wm.quit_blender()

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

5
debug.py Normal file
View File

@@ -0,0 +1,5 @@
def release_mouse (event):
gdb.write("GDB/godot: Releasing mouse\n")
gdb.execute("call OS::get_singleton()->set_mouse_mode(0)")
gdb.events.stop.connect(release_mouse)
gdb.write("GDB/godot: installed release mouse for godot\n")

BIN
godot/DefaultSkin.material Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,16 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://characters/vroid1-female.tscn" type="PackedScene" id=1]
[sub_resource type="CapsuleShape" id=1]
radius = 0.3
[node name="female_base" type="KinematicBody"]
[node name="vroid1-female" parent="." instance=ExtResource( 1 )]
[node name="CollisionShape" type="CollisionShape" parent="."]
transform = Transform( 1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0.804643, 0 )
shape = SubResource( 1 )
[editable path="vroid1-female"]

View File

@@ -0,0 +1,17 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://characters/vroid1-man-animate.tscn" type="PackedScene" id=1]
[sub_resource type="CapsuleShape" id=1]
radius = 0.3
height = 1.3
[node name="man_base" type="KinematicBody"]
[node name="vroid1-man-animate" parent="." instance=ExtResource( 1 )]
[node name="CollisionShape" type="CollisionShape" parent="."]
transform = Transform( 1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0.956587, 0 )
shape = SubResource( 1 )
[editable path="vroid1-man-animate"]

View File

@@ -0,0 +1,5 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://vroid1-female.gltf" type="PackedScene" id=1]
[node name="vroid1-female" instance=ExtResource( 1 )]

View File

@@ -0,0 +1,5 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://vroid1-man-animate.gltf" type="PackedScene" id=1]
[node name="vroid1-man-animate" instance=ExtResource( 1 )]

35
godot/entries/main.gd Normal file
View File

@@ -0,0 +1,35 @@
extends Spatial
# Declare member variables here. Examples:
# var a = 2
# var b = "text"
# Called when the node enters the scene tree for the first time.
func _ready():
pass
# Called every frame. 'delta' is the elapsed time since the previous frame.
var delay = 0.5
var existing = false
func _process(delta):
delay -= delta
if delay < 0:
if !existing:
create_ch()
delay = 10.0
existing = true
else:
destroy_ch()
delay = 3.0
existing = false
var char_id = -1
func create_ch():
char_id = Character.create_character(0)
func destroy_ch():
print(char_id)
Character.destroy_character(char_id)
char_id = -1

25
godot/entries/main.tscn Normal file
View File

@@ -0,0 +1,25 @@
[gd_scene load_steps=5 format=2]
[ext_resource path="res://entries/main.gd" type="Script" id=1]
[sub_resource type="ProceduralSky" id=2]
[sub_resource type="Environment" id=3]
background_mode = 2
background_sky = SubResource( 2 )
[sub_resource type="PlaneMesh" id=1]
size = Vector2( 200, 200 )
[node name="main" type="Spatial"]
script = ExtResource( 1 )
[node name="Camera" type="Camera" parent="."]
transform = Transform( 1, 0, 0, 0, 0.969497, 0.245104, 0, -0.245104, 0.969497, 0, 1.36901, 1.55894 )
environment = SubResource( 3 )
[node name="MeshInstance" type="MeshInstance" parent="."]
mesh = SubResource( 1 )
[node name="DirectionalLight" type="DirectionalLight" parent="."]
transform = Transform( 1, 0, 0, 0, 0.528512, 0.848926, 0, -0.848926, 0.528512, 0, 20, 0 )

BIN
godot/privates.material Normal file

Binary file not shown.

13
godot/project.godot Normal file
View File

@@ -0,0 +1,13 @@
; Engine configuration file.
; It's best edited using the editor UI and not directly,
; since the parameters that go here are not all obvious.
;
; Format:
; [section] ; section goes between []
; param=value ; assign values to parameters
config_version=4
[application]
run/main_scene="res://entries/main.tscn"

BIN
godot/textures/Image_7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 KiB

View File

@@ -0,0 +1,35 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/Image_7.png-78aa76622c61ce819cd6d964f1ce4104.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://textures/Image_7.png"
dest_files=[ "res://.import/Image_7.png-78aa76622c61ce819cd6d964f1ce4104.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
process/normal_map_invert_y=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

View File

@@ -0,0 +1,35 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/M00_000_00_Body_00.png-07fc708a5b80b00f22fa0e4f98651126.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://textures/M00_000_00_Body_00.png"
dest_files=[ "res://.import/M00_000_00_Body_00.png-07fc708a5b80b00f22fa0e4f98651126.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
process/normal_map_invert_y=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

View File

@@ -0,0 +1,35 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/M00_000_00_Body_00_nml.png-1e813741171d150aca1407d3ca65376e.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://textures/M00_000_00_Body_00_nml.png"
dest_files=[ "res://.import/M00_000_00_Body_00_nml.png-1e813741171d150aca1407d3ca65376e.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
process/normal_map_invert_y=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 B

View File

@@ -0,0 +1,35 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/Shader_NoneBlack_01.png-93f60437ffd726066f2849d1f0c63f50.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://textures/Shader_NoneBlack_01.png"
dest_files=[ "res://.import/Shader_NoneBlack_01.png-93f60437ffd726066f2849d1f0c63f50.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
process/normal_map_invert_y=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

BIN
godot/textures/privates.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 908 KiB

View File

@@ -0,0 +1,35 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/privates.png-fc3f06967a1f97b226df565e4d89874b.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://textures/privates.png"
dest_files=[ "res://.import/privates.png-fc3f06967a1f97b226df565e4d89874b.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
process/normal_map_invert_y=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

BIN
godot/vroid1-female.bin Normal file

Binary file not shown.

1407974
godot/vroid1-female.gltf Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

637629
godot/vroid1-man-animate.gltf Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

4
run-editor.sh Executable file
View File

@@ -0,0 +1,4 @@
#!/bin/sh
cd godot
../src/godot/bin/godot.x11.opt.tools.64 -e $*

1
src/godot Submodule

Submodule src/godot added at db8700e8f8

View File

@@ -0,0 +1,17 @@
#!/usr/bin/env python
Import("env")
Import("env_modules")
env_character = env_modules.Clone()
# Godot source files
module_obj = []
env_character.add_source_files(module_obj, "*.cpp")
env_character.add_source_files(module_obj, "flecs/*.c")
env_character.Prepend(CPPPATH=["flecs"])
env_character.Append(CPPFLAGS=["-UNDEBUG", "-g", "-O0"])
env.modules_sources += module_obj

View File

@@ -0,0 +1,635 @@
#include <cassert>
#include <flecs.h>
#include <core/rid.h>
#include <scene/3d/collision_shape.h>
#include <core/engine.h>
#include <core/variant_parser.h>
#include <scene/3d/mesh_instance.h>
#include <scene/resources/shape.h>
#include <scene/resources/packed_scene.h>
#include <servers/physics_server.h>
#include <servers/visual_server.h>
#include <scene/resources/animation.h>
#include <scene/resources/skin.h>
#include <scene/animation/animation_player.h>
#include <scene/main/viewport.h>
#include "character.h"
#include "skeleton-prep.h"
namespace ECS {
Skeleton::Skeleton()
{
bone_count = 0;
skeleton = RID();
dirty = true;
process_order_dirty = true;
}
Skeleton::~Skeleton()
{
}
}
ECS::RigidBody::RigidBody(RID rid, enum type type)
{
this->body = rid;
this->type = type;
// body = ps->body_create(PhysicsServer::BODY_MODE_KINEMATIC, true);
// print_line("created body");
}
ECS::RigidBody::~RigidBody()
{
/*
if (initialized && body != RID()) {
PhysicsServer *ps = PhysicsServer::get_singleton();
ps->free(body);
body = RID();
}
*/
// print_line("destroyed body");
}
ECS::CShape::CShape(RID body, const Transform &offset, Ref<Shape> shape)
{
PhysicsServer *ps = PhysicsServer::get_singleton();
this->offset = offset;
this->shape = shape;
ps->body_add_shape(body, shape->get_rid(), offset, false);
}
ECS::CShape::~CShape()
{
shape = Ref<Shape>(nullptr);
}
ECS::CharacterSlot::CharacterSlot(Ref<ArrayMesh> mesh, Ref<Skin> skin)
{
this->instance = RID();
this->name = "";
this->mesh = mesh;
this->skin = skin;
#if 0
if (!skin.is_null())
this->skin_internal = skin;
#endif
this->bound = false;
}
ECS::BoneComponent::BoneComponent(int index, int parent_index, const Transform &rest)
{
this->index = index;
this->parent_index = parent_index;
this->rest = rest;
}
int Character::create_character(int id)
{
int i;
if (id != 0 && id != 1)
return -1;
if (!initialized) {
print_line("not initialized");
return -1;
}
print_line("creating the character");
flecs::entity root = ecs.entity();
VisualServer *vs = VS::get_singleton();
SceneTree *st = SceneTree::get_singleton();
PhysicsServer *ps = PhysicsServer::get_singleton();
RID body = ps->body_create(PhysicsServer::BODY_MODE_KINEMATIC, true);
root.emplace<ECS::MetaData>();
root.emplace<Transform>();
root.emplace<ECS::RigidBody>(body, ECS::RigidBody::TYPE_KINEMATIC);
root.emplace<ECS::Skeleton>();
root.emplace<ECS::AnimationTree>();
root.add<ECS::Dirty>();
ECS::MetaData *md = root.get_mut<ECS::MetaData>();
md->type_index = id;
root.modified<ECS::MetaData>();
print_line("body created for root");
for (i = 0; i < scene_data[id].shapes.size(); i++) {
ecs.entity()
.child_of(root)
.emplace<ECS::CShape>(body, scene_data[id].shapes[i].offset, scene_data[id].shapes[i].shape);
}
for (i = 0; i < scene_data[id].bones.size(); i++) {
int index = i, parent_index = scene_data[id].bones[i].parent;
const Transform &rest = scene_data[id].bones[i].rest;
ecs.entity(scene_data[id].bones[i].name.ascii().get_data())
.child_of(root)
.emplace<ECS::BoneComponent>(index, parent_index, rest)
.set<ECS::BonePose>({index, Transform()});
}
for (i = 0; i < scene_data[id].meshes.size(); i++) {
// flecs::log::trace("creating slot -");
flecs::entity entity = ecs.entity(scene_data[id].meshes[i].slot_name.ascii().get_data())
.child_of(root)
.emplace<ECS::CharacterSlot>(scene_data[id].meshes[i].mesh,
scene_data[id].meshes[i].skin);
#if 0
flecs::log::trace("created slot - ");
if (entity.get<ECS::CharacterSlot>()->instance.is_valid())
print_line("instance is valid 1");
else
print_line("instance is invalid 1");
#endif
}
// print_line("character created");
int xid = entities.size();
// print_line(itos(xid) + ":" + itos(root.id()));
entities[xid] = root.id();
return xid;
}
void Character::destroy_character(int xid)
{
if (!entities.has(xid)) {
print_error("No such entity");
return;
}
flecs::entity_t id = entities[xid];
// print_line("destroy: " + itos(xid) + ":" + itos(id));
VisualServer *vs = VS::get_singleton();
PhysicsServer *ps = PhysicsServer::get_singleton();
flecs::entity entity = ecs.entity(id);
if (!entity.is_alive()) {
// print_error("Entity already destroyed");
return;
}
print_line("destroying character");
ECS::RigidBody *pb = entity.get_mut<ECS::RigidBody>();
if (pb->body.is_valid()) {
ps->body_clear_shapes(pb->body);
ps->body_set_space(pb->body, RID());
ps->free(pb->body);
pb->body = RID();
entity.modified<ECS::RigidBody>();
}
#if 0
entity.children([&](flecs::entity child) {
if (child.has<ECS::CharacterSlot>()) {
ECS::CharacterSlot *cs = child.get_mut<ECS::CharacterSlot>();
print_line("entity is CharacterSlot");
if (cs->instance.is_valid())
print_line("instance is valid 2");
else
print_line("instance is invalid 2");
} else if (child.has<ECS::CShape>()) {
print_line("entity is CShape");
}
});
#endif
entity.destruct();
print_line("character destroyed");
}
Character *Character::singleton = nullptr;
Character *Character::get_singleton()
{
if (!singleton)
singleton = memnew(Character);
return singleton;
}
void Character::cleanup()
{
if (singleton)
memdelete(singleton);
singleton = nullptr;
}
void Character::set_scene(int id, const Ref<PackedScene> &scene)
{
if (id < 0 || id > 1)
return;
scene_data[id].scene = scene;
List<Node *> queue;
Node *node = scene_data[id].scene->instance();
queue.push_back(node);
while (!queue.empty()) {
int i;
Node *tmp = queue.front()->get();
queue.pop_front();
MeshInstance *mi = Object::cast_to<MeshInstance>(tmp);
if (mi) {
Transform xform = mi->get_transform();
Node *tmp2 = mi->get_parent();
/* calculating transform */
while (tmp2) {
Spatial *mi2 = Object::cast_to<Spatial>(tmp2);
if (mi2)
xform = mi2->get_transform() * xform;
tmp2 = tmp2->get_parent();
}
struct mesh_data md;
md.slot_name = mi->get_name();
md.mesh = mi->get_mesh();
md.offset = xform;
scene_data[id].meshes.push_back(md);
#if 0
Ref<Skin> skin = mi->get_skin();
bool found = false;
for (Set<SkinReference *>::Element *e =
scene_data[id].skin_bindings.front(); e; e = e->next()) {
if (e->get()->skin == skin) {
found = true;
break;
}
}
if (!found) {
if (skin.is_null()) {
#error GENERATE SKIN
}
}
#endif
}
CollisionShape *cshape = Object::cast_to<CollisionShape>(tmp);
if (cshape) {
Transform xform = cshape->get_transform();
Node *tmp2 = cshape->get_parent();
/* calculating transform */
while (tmp2) {
Spatial *mi2 = Object::cast_to<Spatial>(tmp2);
if (mi2)
xform = mi2->get_transform() * xform;
tmp2 = tmp2->get_parent();
}
struct shape_data sd;
sd.shape = cshape->get_shape();
sd.offset = xform;
scene_data[id].shapes.push_back(sd);
}
AnimationPlayer *anim = Object::cast_to<AnimationPlayer>(tmp);
if (anim) {
List<StringName> anim_list;
anim->get_animation_list(&anim_list);
List<StringName>::Element *e = anim_list.front();
while (e) {
StringName anim_name = e->get();
Ref<Animation> animation = anim->get_animation(anim_name);
scene_data[id].animations.push_back(animation);
e = e->next();
}
}
Skeleton *skel = Object::cast_to<Skeleton>(tmp);
if (skel) {
int bone_count = skel->get_bone_count();
scene_data[id].bones.resize(bone_count);
for (i = 0; i < bone_count; i++) {
scene_data[id].bones.write[i].name = skel->get_bone_name(i);
scene_data[id].bones.write[i].index = i;
scene_data[id].bones.write[i].parent = skel->get_bone_parent(i);
scene_data[id].bones.write[i].rest = skel->get_bone_rest(i);
// print_line("added bone: " + scene_data[id].bones.write[i].name + " " + itos(i));
}
}
for (i = 0; i < tmp->get_child_count(); i++)
queue.push_back(tmp->get_child(i));
}
memdelete(node);
print_line("meshes loaded: " + itos(id) + " " + itos(scene_data[id].meshes.size()));
print_line("shapes loaded: " + itos(id) + " " + itos(scene_data[id].shapes.size()));
print_line("animations loaded: " + itos(id) + " " + itos(scene_data[id].animations.size()));
}
Ref<PackedScene> Character::get_scene(int id) const
{
if (id < 0 || id > 1)
return Ref<PackedScene>(nullptr);
return scene_data[id].scene;
}
Character::Character()
{
call_deferred("__initialize");
singleton = this;
}
void Character::_bind_methods()
{
ClassDB::bind_method(D_METHOD("__initialize"), &Character::initialize);
ClassDB::bind_method(D_METHOD("create_character", "id"), &Character::create_character);
ClassDB::bind_method(D_METHOD("destroy_character", "id"), &Character::destroy_character);
ClassDB::bind_method(D_METHOD("_progress"), &Character::progress);
}
void Character::init_bone()
{
ecs.component<ECS::BoneComponent>()
.on_add([](flecs::entity e, ECS::BoneComponent &s)
{
flecs::log::trace("added bonecomponent %s", e.name().c_str());
flecs::entity parent = e.parent();
parent.add<ECS::Dirty>();
if (parent.has<ECS::Bound>()) {
parent.remove<ECS::SkeletonOrderPrepared>();
parent.remove<ECS::SkeletonTransformReady>();
parent.remove<ECS::Bound>();
parent.remove<ECS::Skinned>();
}
})
.on_set([](flecs::entity e, ECS::BoneComponent &s)
{
flecs::entity parent = e.parent();
ECS::Skeleton *skel = parent.get_mut<ECS::Skeleton>();
if (s.index >= skel->bone_count) {
skel->bone_count = s.index + 1;
skel->bones.resize(skel->bone_count);
}
skel->bones.write[s.index].enabled = true;
skel->bones.write[s.index].rest = s.rest;
skel->bones.write[s.index].pose = Transform();
skel->bones.write[s.index].parent = s.parent_index;
skel->bones.write[s.index].disable_rest = false;
skel->bones.write[s.index].custom_pose_enable = false;
skel->dirty = true;
String out;
VariantWriter::write_to_string(s.rest, out);
flecs::log::trace("bonecomponent set %s (%d) [%s]", e.name().c_str(), skel->bone_count, out.ascii().get_data());
parent.remove<ECS::SkeletonOrderPrepared>();
parent.add<ECS::Dirty>();
});
ecs.component<ECS::BonePose>()
.on_add([](flecs::entity e, ECS::BonePose &s)
{
// flecs::log::trace("added bonepose %s", e.name().c_str());
})
.on_set([](flecs::entity e, ECS::BonePose &s)
{
const Transform &xform = s.pose;
String out;
VariantWriter::write_to_string(xform, out);
flecs::log::trace("bone pose set %s: %d %s",
e.name().c_str(), s.index, out.ascii().get_data());
e.add<ECS::Dirty>();
});
ecs.system<ECS::BonePose, ECS::Dirty>()
.with<ECS::Skeleton>().parent()
.with<ECS::Bound>().parent()
.kind(flecs::OnUpdate)
.each([this](flecs::entity e, ECS::BonePose &s, const ECS::Dirty &d)
{
flecs::entity parent = e.parent();
/* Don't update unbound skeleton */
if (!parent.has<ECS::Bound>()) {
flecs::log::err("the skeleton is not bound");
return;
}
ECS::Skeleton *skel = parent.get_mut<ECS::Skeleton>();
skel->bones.write[s.index].pose = s.pose;
skel->dirty = true;
String out;
VariantWriter::write_to_string(s.pose, out);
flecs::log::trace("set bonepose %s %d %s", e.name().c_str(), s.index, out.ascii().get_data());
parent.modified<ECS::Skeleton>();
parent.add<ECS::Dirty>();
parent.remove<ECS::SkeletonTransformReady>();
e.remove<ECS::Dirty>();
});
}
void Character::initialize()
{
Error err;
flecs::log::set_level(0);
SceneTree *st = SceneTree::get_singleton();
ecs.import<skeleton_prep>();
init_skeleton();
ecs.component<ECS::MetaData>()
.on_add([](flecs::entity e, ECS::MetaData &s)
{
flecs::log::trace("added metadata %s", e.name().c_str());
});
init_bone();
init_slot();
ecs.component<ECS::AnimationTree>()
.on_add([](flecs::entity e, ECS::AnimationTree &s)
{
flecs::log::trace("animationtree slot %s", e.name().c_str());
});
ecs.component<ECS::RigidBody>()
.on_add([](flecs::entity e, ECS::RigidBody &s)
{
flecs::log::trace("added rigidbody %s", e.name().c_str());
});
const String path = "res://characters/";
Ref<PackedScene> scene1 = ResourceLoader::load(path + "male-base.tscn", "PackedScene", true, &err);
if (err == OK && scene1.is_valid())
set_scene(0, scene1);
Ref<PackedScene> scene2 = ResourceLoader::load(path + "female-base.tscn", "PackedScene", true, &err);
if (err == OK && scene2.is_valid())
set_scene(1, scene2);
initialized = true;
#if 0
ecs.system<ECS::CharacterSlot>("BindSkin")
.without<ECS::Bound>()
.kind(flecs::OnUpdate)
.each([this](flecs::entity e, ECS::CharacterSlot &slot)
{
flecs::entity parent = e.parent();
ECS::Skeleton *skel = parent.get_mut<ECS::Skeleton>();
const struct ECS::Skeleton::bone_data *bonesptr = skel->bones.ptr();
const int *order = skel->process_order.ptr();
if (skel->skin.is_null() {
}
}
#endif
#if 0
#endif
#if 0
ecs.system<ECS::CharacterSlot>("BindSkeleton")
.without<ECS::Bound>()
.kind(flecs::PreUpdate)
.each([this](flecs::entity e, ECS::CharacterSlot &slot)
{
flecs::entity parent = e.parent();
if (!parent.get<ECS::SkeletonTransformReady>())
return;
if (parent.get<ECS::Bound>())
return;
flecs::log::trace("unbound skeleton at %s", e.name().c_str());
int i;
ECS::Skeleton *skel = parent.get_mut<ECS::Skeleton>();
if (!skel->skin.is_null())
return;
const struct ECS::Skeleton::bone_data *bonesptr = skel->bones.ptr();
const int *order = skel->process_order.ptr();
VisualServer *vs = VS::get_singleton();
int len = skel->bones.size();
if (slot.skin.is_null() || slot.skin_internal.is_null()) {
flecs::log::err("skin is null for %s", e.name().c_str());
return;
}
#if 0
if (!skel->skin.is_null()) {
slot.skin = skel->skin;
vs->instance_attach_skeleton(slot.instance, skel->skeleton);
parent.add<ECS::Bound>();
e.add<ECS::Bound>();
return;
}
#endif
assert(skel->skeleton.is_valid());
uint32_t bind_count = 0U;
bind_count = skel->skin->get_bind_count();
skel->skin_bone_indices.resize(bind_count);
skel->skin_bone_indices_ptrs = skel->skin_bone_indices.ptrw();
for (i = 0; i < bind_count; i++) {
StringName bind_name = skel->skin->get_bind_name(i);
if (bind_name != StringName()) {
int j;
bool found = false;
for (j = 0; j < len; j++) {
if (bonesptr[j].name == bind_name) {
skel->skin_bone_indices_ptrs[i] = j;
found = true;
break;
}
}
if (!found) {
skel->skin_bone_indices_ptrs[i] = 0;
flecs::log::err("no bind name %s in skeleton", String(bind_name).ascii().get_data());
}
} else if (skel->skin->get_bind_bone(i) >= 0) {
uint32_t bind_index = skel->skin->get_bind_bone(i);
if (bind_index >= len) {
skel->skin_bone_indices_ptrs[i] = 0;
flecs::log::err("bind %d bad index %d", i, bind_index);
} else
skel->skin_bone_indices_ptrs[i] = bind_index;
} else {
flecs::log::err("no bind name for %d", i);
skel->skin_bone_indices_ptrs[i] = 0;
}
}
assert(bind_count > 0);
vs->skeleton_allocate(skel->skeleton, bind_count);
flecs::log::trace("skeltton is prepared and bound for %s", e.name().c_str());
parent.modified<ECS::Skeleton>();
parent.add<ECS::Bound>();
assert(false);
});
ecs.system<ECS::Skeleton>("Check2")
.kind(flecs::PostUpdate)
.each([this](flecs::entity e, ECS::Skeleton &skel)
{
if (e.has<ECS::Dirty>())
flecs::log::trace("is dirty");
if (e.has<ECS::Bound>())
flecs::log::trace("is bound");
if (e.has<ECS::SkeletonTransformReady>())
flecs::log::trace("transform ready");
flecs::log::trace("end of frame systems");
});
#endif
#if 0
ecs.system<ECS::CharacterSlot>("PrepareSkins2")
.kind(flecs::OnUpdate)
.each([this](flecs::entity e, ECS::CharacterSlot &slot)
{
int i;
flecs::entity parent = e.parent();
ECS::Skeleton *skel = parent.get_mut<ECS::Skeleton>();
if (slot.bound && !skel->dirty)
return;
const struct ECS::Skeleton::bone_data *bonesptr = skel->bones.ptr();
const int *order = skel->process_order.ptr();
VisualServer *vs = VS::get_singleton();
uint32_t bind_count = 0U;
if (!slot.bound) {
int len = skel->bones.size();
if (slot.skin.is_null())
flecs::log::trace("skin is null for %s", e.name().c_str());
if (slot.skin_internal.is_null() && skel->skin.is_null()) {
flecs::log::trace("creating skin for %s", e.name().c_str());
slot.skin_internal.instance();
slot.skin_internal->set_bind_count(skel->bones.size());
for (i = 0; i < len; i++) {
const struct ECS::Skeleton::bone_data &b = bonesptr[order[i]];
if (b.parent >= 0)
slot.skin_internal->set_bind_pose(order[i], slot.skin_internal->get_bind_pose(b.parent) * b.rest);
else
slot.skin_internal->set_bind_pose(order[i], b.rest);
}
for (i = 0; i < len; i++) {
slot.skin_internal->set_bind_bone(i, i);
slot.skin_internal->set_bind_pose(i, slot.skin_internal->get_bind_pose(i).affine_inverse());
}
flecs::log::trace("created skin for %s: %d", e.name().c_str(), len);
}
assert(skel->skeleton.is_valid());
skel->skin = slot.skin_internal;
bind_count = skel->skin->get_bind_count();
assert(bind_count > 0);
skel->skin_bone_indices.resize(bind_count);
skel->skin_bone_indices_ptrs = skel->skin_bone_indices.ptrw();
for (i = 0; i < bind_count; i++) {
StringName bind_name = skel->skin->get_bind_name(i);
if (bind_name != StringName()) {
int j;
bool found = false;
for (j = 0; j < len; j++) {
if (bonesptr[j].name == bind_name) {
skel->skin_bone_indices_ptrs[i] = j;
found = true;
break;
}
}
if (!found) {
skel->skin_bone_indices_ptrs[i] = 0;
flecs::log::err("no bind name %s in skeleton", String(bind_name).ascii().get_data());
}
} else if (skel->skin->get_bind_bone(i) >= 0) {
uint32_t bind_index = skel->skin->get_bind_bone(i);
if (bind_index >= len) {
skel->skin_bone_indices_ptrs[i] = 0;
flecs::log::err("bind %d bad index %d", i, bind_index);
} else
skel->skin_bone_indices_ptrs[i] = bind_index;
} else {
flecs::log::err("no bind name for %d", i);
skel->skin_bone_indices_ptrs[i] = 0;
}
}
vs->skeleton_allocate(skel->skeleton, bind_count);
vs->instance_attach_skeleton(slot.instance, skel->skeleton);
slot.bound = true;
flecs::log::trace("skeltton is prepared and bound for %s", e.name().c_str());
e.modified<ECS::Skeleton>();
}
bind_count = skel->skin->get_bind_count();
for (i = 0; i < (int)bind_count; i++) {
uint32_t bone_index = skel->skin_bone_indices_ptrs[i];
vs->skeleton_bone_set_transform(skel->skeleton,
i, bonesptr[bone_index].pose_global *skel->skin->get_bind_pose(i));
}
skel->dirty = false;
});
#endif
ecs.system<ECS::Skeleton>("UpdateBone")
.with<ECS::Bound>()
.kind(flecs::OnUpdate)
.each([](flecs::entity e, ECS::Skeleton &sk)
{
flecs::entity root = e.lookup("Root");
if (!root.is_valid() || !root.is_alive())
return;
if (!root.has<ECS::BonePose>())
return;
float delta = SceneTree::get_singleton()->get_physics_process_time();
struct ECS::BonePose *bp = root.get_mut<ECS::BonePose>();
// bp->pose.basis = bp->pose.basis.rotated(Vector3(0, 1, 0), 0.016f);
bp->pose.origin += Vector3(0.1f * delta, 0.0f, 0.0f);
root.modified<ECS::BonePose>();
});
if (!Engine::get_singleton()->is_editor_hint()) {
err = st->connect("physics_frame", this, "_progress");
assert(err == OK);
}
print_line("initialized");
}
void Character::progress()
{
ecs.progress(SceneTree::get_singleton()->get_physics_process_time());
}

View File

@@ -0,0 +1,150 @@
#ifndef CHARACTER_H
#define CHARACTER_H
#include <core/object.h>
#include <flecs.h>
class PackedScene;
class Shape;
class ArrayMesh;
class Animation;
class Skin;
class SkinReference;
namespace ECS {
struct Skeleton {
bool dirty;
RID skeleton;
Ref<Skin> skin;
int bone_count;
Vector<int> process_order;
bool process_order_dirty;
struct bone_data {
String name;
bool enabled;
int parent;
bool disable_rest;
bool custom_pose_enable;
Transform
custom_pose,
rest,
pose,
pose_global,
pose_global_no_override;
int sort_index;
};
Vector<uint32_t> skin_bone_indices;
uint32_t *skin_bone_indices_ptrs;
Vector<struct bone_data> bones;
Skeleton();
~Skeleton();
};
struct Bound {};
struct Skinned {};
struct Dirty {};
struct SkeletonOrderPrepared {};
struct SkeletonTransformReady {};
struct MetaData {
int type_index;
};
struct BoneComponent {
flecs::entity_t skeleton;
int index, parent_index;
Transform rest, pose;
BoneComponent(int index, int parent_index, const Transform &rest);
};
struct BonePose {
int index;
Transform pose;
};
struct AnimationTree {
void *root;
AnimationTree(): root(nullptr)
{
}
~AnimationTree()
{
}
};
struct CharacterSlot {
String name;
RID instance;
RID skeleton;
Ref<ArrayMesh> mesh;
Ref<Skin> skin, skin_internal;
bool bound;
CharacterSlot(Ref<ArrayMesh> mesh, Ref<Skin> skin);
#if 0
CharacterSlot(const CharacterSlot &obj);
CharacterSlot(CharacterSlot &&obj);
CharacterSlot &operator=(const CharacterSlot &obj);
CharacterSlot &operator=(CharacterSlot &&obj);
~CharacterSlot();
#endif
};
struct RigidBody {
enum type {
TYPE_NONE = 0,
TYPE_STATIC = 1,
TYPE_KINEMATIC,
TYPE_RIGID
};
bool initialized;
RID body;
Vector3 velocity;
enum type type;
RigidBody(RID rid, enum type type);
~RigidBody();
};
struct CShape {
Transform offset;
Ref<Shape> shape;
CShape(RID body, const Transform &offset, Ref<Shape> shape);
~CShape();
};
}
class Character: public Object {
GDCLASS(Character, Object)
public:
int create_character(int type_id);
void destroy_character(int id);
static Character *singleton;
static Character *get_singleton();
static void cleanup();
void set_scene(int id, const Ref<PackedScene> &scene);
Ref<PackedScene> get_scene(int id) const;
Character();
protected:
flecs::world ecs;
struct shape_data {
Ref<Shape> shape;
Transform offset;
};
struct mesh_data {
Ref<Skin> skin;
String slot_name;
Ref<ArrayMesh> mesh;
Transform offset;
};
struct bone_data {
String name;
int index, parent;
Transform rest;
};
struct scene_data {
Ref<PackedScene> scene;
Vector<struct shape_data> shapes;
Vector<struct mesh_data> meshes;
Vector<Ref<Animation> > animations;
Vector<struct bone_data> bones;
Set<SkinReference *> skin_bindings;
} scene_data[2];
static void _bind_methods();
void init_skeleton();
void init_bone();
void init_slot();
void initialize();
HashMap<int, flecs::entity_t> entities;
bool initialized;
void progress();
};
#endif

View File

@@ -0,0 +1,212 @@
#include <cassert>
#include <flecs.h>
#include <servers/visual_server.h>
#include <scene/main/scene_tree.h>
#include <core/reference.h>
#include <scene/main/viewport.h>
#include <scene/resources/skin.h>
#include <core/variant_parser.h>
#include "character.h"
void Character::init_skeleton()
{
VisualServer *vs = VS::get_singleton();
ecs.component<ECS::Skeleton>()
.on_add([](flecs::entity e, ECS::Skeleton &s)
{
// flecs::log::trace("added skeleton %s", e.name().c_str());
if (e.has<ECS::SkeletonOrderPrepared>())
e.remove<ECS::SkeletonOrderPrepared>();
})
.on_set([](flecs::entity e, ECS::Skeleton &s)
{
// flecs::log::trace("on set: set skeleton %s", e.name().c_str());
// s.process_order_dirty = true;
e.add<ECS::Dirty>();
});
ecs.system<ECS::Skeleton>("CreateSkeleton")
.without<ECS::Bound>()
.kind(flecs::PreUpdate)
.each([vs](flecs::entity e, ECS::Skeleton &skel)
{
if (!skel.skeleton.is_valid()) {
skel.skeleton = vs->skeleton_create();
flecs::log::trace("created skeleton");
}
});
ecs.system<ECS::Skeleton>("PrepareSkeletonsOrder")
.with<ECS::Dirty>()
.without<ECS::SkeletonOrderPrepared>()
.kind(flecs::PreUpdate)
.each([](flecs::entity e, ECS::Skeleton &sk)
{
if (sk.process_order_dirty) {
flecs::log::trace("skeletons: %d dirty: sorting bones", sk.bone_count);
int i;
struct ECS::Skeleton::bone_data *bonesptr = sk.bones.ptrw();
int len = sk.bones.size();
sk.process_order.resize(len);
int *order = sk.process_order.ptrw();
for (i = 0; i < len; i++) {
if (bonesptr[i].parent >= len) {
flecs::log::err("Bone %d has bad parent %d", i, bonesptr[i].parent);
bonesptr[i].parent = -1;
}
order[i] = i;
bonesptr[i].sort_index = i;
}
int pass_count = 0;
while (pass_count < len * len) {
// fucking bubblesort
bool swapped = false;
for (i = 0; i < len; i++) {
int parent_idx = bonesptr[order[i]].parent;
if (parent_idx < 0)
continue;
int parent_order = bonesptr[parent_idx].sort_index;
if (parent_order > i) {
bonesptr[order[i]].sort_index = parent_order;
bonesptr[parent_idx].sort_index = i;
SWAP(order[i], order[parent_order]);
swapped = true;
}
}
if (!swapped)
break;
pass_count++;
}
if (pass_count >= len * len)
flecs::log::err("Skeleton parenthood graph is cyclic");
sk.process_order_dirty = false;
}
e.add<ECS::SkeletonOrderPrepared>();
});
ecs.system<ECS::Skeleton>("PrepSkeletonTransform")
.with<ECS::Dirty>()
.with<ECS::SkeletonOrderPrepared>()
.without<ECS::SkeletonTransformReady>()
.kind(flecs::OnUpdate)
.each([](flecs::entity e, ECS::Skeleton &sk)
{
int i;
VisualServer *vs = VS::get_singleton();
// flecs::log::trace("skeletons: %d dirty: update try", sk.bone_count);
if (sk.process_order_dirty)
return;
flecs::log::trace("skeletons: %d dirty: update", sk.bone_count);
struct ECS::Skeleton::bone_data *bonesptr = sk.bones.ptrw();
int len = sk.bones.size();
const int *order = sk.process_order.ptr();
for (i = 0; i < len; i++) {
struct ECS::Skeleton::bone_data &b = bonesptr[order[i]];
if (b.disable_rest) {
if (b.enabled) {
Transform pose = b.pose;
if (b.custom_pose_enable)
pose = b.custom_pose * pose;
if (b.parent >= 0) {
b.pose_global = bonesptr[b.parent].pose_global * pose;
b.pose_global_no_override = bonesptr[b.parent].pose_global_no_override * pose;
} else {
b.pose_global = pose;
b.pose_global_no_override = pose;
}
} else {
if (b.parent >= 0) {
b.pose_global = bonesptr[b.parent].pose_global;
b.pose_global_no_override = bonesptr[b.parent].pose_global_no_override;
} else {
b.pose_global = Transform();
b.pose_global_no_override = Transform();
}
}
} else {
if (b.enabled) {
Transform pose = b.pose;
if (b.custom_pose_enable)
pose = b.custom_pose * pose;
if (b.parent >= 0) {
b.pose_global = bonesptr[b.parent].pose_global * (b.rest * pose);
b.pose_global_no_override = bonesptr[b.parent].pose_global_no_override * (b.rest * pose);
} else {
b.pose_global = b.rest * pose;
b.pose_global_no_override = b.rest * pose;
}
} else {
if (b.parent >= 0) {
b.pose_global = bonesptr[b.parent].pose_global * b.rest;
b.pose_global_no_override = bonesptr[b.parent].pose_global_no_override * b.rest;
} else {
b.pose_global = b.rest;
b.pose_global_no_override = b.rest;
}
}
}
}
flecs::log::trace("transform should be ready");
e.add<ECS::SkeletonTransformReady>();
assert(sk.skin_bone_indices_ptrs);
uint32_t bind_count = sk.skin->get_bind_count();
for (i = 0; i < (int)bind_count; i++) {
uint32_t bone_index = sk.skin_bone_indices_ptrs[i];
Transform xform = bonesptr[bone_index].pose_global * sk.skin->get_bind_pose(i);
if (i == 0) {
String out;
VariantWriter::write_to_string(xform, out);
flecs::log::trace("dirty bone: set skeleton %d %s %s",
i, bonesptr[bone_index].name.ascii().get_data(),
out.ascii().get_data());
}
vs->skeleton_bone_set_transform(sk.skeleton,
i, bonesptr[bone_index].pose_global * sk.skin->get_bind_pose(i));
}
sk.dirty = false;
e.remove<ECS::Dirty>();
});
ecs.system<ECS::Skeleton>("c1")
.with<ECS::Bound>()
.kind(flecs::OnUpdate)
.each([this](flecs::entity e, ECS::Skeleton &skel)
{
flecs::log::trace("is bound");
});
ecs.system<ECS::Skeleton>("c2")
.with<ECS::Dirty>()
.kind(flecs::OnUpdate)
.each([this](flecs::entity e, ECS::Skeleton &skel)
{
flecs::log::trace("is dirty");
});
ecs.system<ECS::Skeleton>("c3")
.with<ECS::SkeletonTransformReady>()
.kind(flecs::OnUpdate)
.each([this](flecs::entity e, ECS::Skeleton &skel)
{
flecs::log::trace("transform ready");
});
ecs.system<ECS::Skeleton>("c4")
.with<ECS::SkeletonOrderPrepared>()
.kind(flecs::OnUpdate)
.each([this](flecs::entity e, ECS::Skeleton &skel)
{
flecs::log::trace("order prepared");
});
#if 0
ecs.system<ECS::Skeleton>("Check_")
.kind(flecs::PreUpdate)
.each([this](flecs::entity e, ECS::Skeleton &skel)
{
if (e.has<ECS::Dirty>())
flecs::log::trace("is dirty");
if (e.has<ECS::Bound>())
flecs::log::trace("is bound");
if (e.has<ECS::SkeletonOrderPrepared>())
flecs::log::trace("order is prepared");
if (e.has<ECS::SkeletonTransformReady>())
flecs::log::trace("transform ready");
flecs::log::trace("start of frame systems");
});
#endif
}

View File

@@ -0,0 +1,154 @@
#include <cassert>
#include <flecs.h>
#include <servers/visual_server.h>
#include <scene/main/scene_tree.h>
#include <core/reference.h>
#include <scene/main/viewport.h>
#include <scene/resources/skin.h>
#include "character.h"
void Character::init_slot()
{
SceneTree *st = SceneTree::get_singleton();
VisualServer *vs = VS::get_singleton();
ecs.component<ECS::CharacterSlot>()
.on_add([](flecs::entity e, ECS::CharacterSlot &s)
{
flecs::log::trace("added slot %s", e.name().c_str());
})
.on_remove([vs](flecs::entity e, ECS::CharacterSlot &s)
{
if (s.instance.is_valid()) {
vs->instance_set_scenario(s.instance, RID());
vs->instance_attach_skeleton(s.instance, RID());
vs->instance_set_base(s.instance, RID());
vs->free(s.instance);
s.instance = RID();
// print_line("mesh instance slot removed " + s.name);
}
// flecs::log::trace("removed slot %s", e.name().c_str());
})
.on_set([st](flecs::entity e, ECS::CharacterSlot &s)
{
flecs::log::trace("set slot %s", e.name().c_str());
if (s.mesh.is_null() || !s.mesh.is_valid())
flecs::log::trace("invalid mesh %s", e.name().c_str());
});
ecs.system<ECS::CharacterSlot>("UpdateSlot")
.without<ECS::Bound>()
.with<ECS::Skeleton>().parent()
.kind(flecs::PreUpdate)
.each([vs, st](flecs::entity e, ECS::CharacterSlot &slot)
{
assert(e.parent().has<ECS::Skeleton>());
if (!slot.instance.is_valid()) {
slot.name = e.name().c_str();
RID scenario = st->get_root()->get_world()->get_scenario();
slot.instance = vs->instance_create();
vs->instance_set_scenario(slot.instance, scenario);
vs->instance_set_base(slot.instance, slot.mesh->get_rid());
flecs::log::trace("created instance for %s", e.name().c_str());
}
});
ecs.system<ECS::CharacterSlot>("AttachToSkeleton")
.without<ECS::Bound>()
.with<ECS::Skeleton>().parent()
.with<ECS::Skinned>()
.kind(flecs::PreUpdate)
.each([vs, st](flecs::entity e, ECS::CharacterSlot &slot)
{
flecs::entity parent = e.parent();
assert(parent.has<ECS::Skeleton>());
assert(!e.has<ECS::Bound>());
assert(slot.instance.is_valid());
const ECS::Skeleton *skel = parent.get<ECS::Skeleton>();
assert(skel->skeleton.is_valid());
flecs::log::trace("binding slot for %s", e.name().c_str());
if (skel->skeleton.is_valid())
slot.skeleton = skel->skeleton;
if (slot.skin.is_null() && !skel->skin.is_null())
slot.skin_internal = skel->skin;
if (skel->skeleton.is_valid() && !skel->skin.is_null()) {
uint32_t bind_count = skel->skin->get_bind_count();
vs->skeleton_allocate(skel->skeleton, bind_count);
vs->instance_attach_skeleton(slot.instance, skel->skeleton);
slot.bound = true;
e.add<ECS::Bound>();
parent.add<ECS::Bound>();
flecs::log::trace("bound slot for %s", e.name().c_str());
}
});
ecs.system<ECS::CharacterSlot>("BindSlotsToSkeleton")
.with<ECS::Skeleton>().parent()
.without<ECS::Bound>()
.without<ECS::Skinned>()
.kind(flecs::PreUpdate)
.each([vs](flecs::entity e, ECS::CharacterSlot &slot)
{
flecs::entity parent = e.parent();
int i;
assert(!e.has<ECS::Bound>());
assert(!e.has<ECS::Skinned>());
ECS::Skeleton *skel = parent.get_mut<ECS::Skeleton>();
if (skel->skin.is_null() && slot.skin_internal.is_null()) {
const struct ECS::Skeleton::bone_data *bonesptr = skel->bones.ptr();
const int *order = skel->process_order.ptr();
flecs::log::trace("binding slot for %s", e.name().c_str());
slot.skin_internal.instance();
slot.skin_internal->set_bind_count(skel->bones.size());
int len = skel->bones.size();
flecs::log::trace("creating skin for %s", e.name().c_str());
for (i = 0; i < len; i++) {
const struct ECS::Skeleton::bone_data &b = bonesptr[order[i]];
if (b.parent >= 0)
slot.skin_internal->set_bind_pose(order[i], slot.skin_internal->get_bind_pose(b.parent) * b.rest);
else
slot.skin_internal->set_bind_pose(order[i], b.rest);
}
for (i = 0; i < len; i++) {
slot.skin_internal->set_bind_bone(i, i);
slot.skin_internal->set_bind_pose(i, slot.skin_internal->get_bind_pose(i).affine_inverse());
}
skel->skin = slot.skin_internal;
flecs::log::trace("created skin for %s: %d", e.name().c_str(), len);
assert(skel->skeleton.is_valid());
skel->skin = slot.skin_internal;
uint32_t bind_count = skel->skin->get_bind_count();
assert(bind_count > 0);
skel->skin_bone_indices.resize(bind_count);
skel->skin_bone_indices_ptrs = skel->skin_bone_indices.ptrw();
for (i = 0; i < bind_count; i++) {
StringName bind_name = skel->skin->get_bind_name(i);
if (bind_name != StringName()) {
int j;
bool found = false;
for (j = 0; j < len; j++) {
if (bonesptr[j].name == bind_name) {
skel->skin_bone_indices_ptrs[i] = j;
found = true;
break;
}
}
if (!found) {
skel->skin_bone_indices_ptrs[i] = 0;
flecs::log::err("no bind name %s in skeleton", String(bind_name).ascii().get_data());
}
} else if (skel->skin->get_bind_bone(i) >= 0) {
uint32_t bind_index = skel->skin->get_bind_bone(i);
if (bind_index >= len) {
skel->skin_bone_indices_ptrs[i] = 0;
flecs::log::err("bind %d bad index %d", i, bind_index);
} else
skel->skin_bone_indices_ptrs[i] = bind_index;
} else {
flecs::log::err("no bind name for %d", i);
skel->skin_bone_indices_ptrs[i] = 0;
}
}
e.add<ECS::Skinned>();
} else if (!skel->skin.is_null() && slot.skin_internal.is_null()) {
slot.skin_internal = skel->skin;
e.add<ECS::Skinned>();
}
});
}

View File

@@ -0,0 +1,14 @@
def can_build(env, platform):
return True
def configure(env):
pass
def get_doc_classes():
return []
def get_doc_path():
return "doc_classes"

View File

@@ -0,0 +1,44 @@
#include <core/engine.h>
#include "character.h"
#include "register_types.h"
void register_character_types()
{
#if 0
ClassDB::register_class<AnimScene>();
ClassDB::register_virtual_class<CharAnimStepCommon>();
ClassDB::register_class<CharAnimStepBase>();
ClassDB::register_class<CharAnimData>();
ClassDB::register_class<AnimatedScene>();
ClassDB::register_class<SmartObjectStep>();
ClassDB::register_class<SmartObjectSteps>();
ClassDB::register_class<GoapGoal>();
ClassDB::register_class<GoapAction>();
ClassDB::register_class<SmartObjectAction>();
ClassDB::register_class<GoapActionPlanner>();
ClassDB::register_class<GoapActionData>();
ClassDB::register_class<GoapRunner>();
ClassDB::register_class<NPCClimb>();
ClassDB::register_class<ContextSteering>();
ClassDB::register_class<SceneComps>();
ClassDB::register_class<NPCMotion>();
ClassDB::register_class<NPCBackground>();
ClassDB::register_class<NPC_Data>();
Engine::get_singleton()->add_singleton(Engine::Singleton("SceneComps", SceneComps::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("Profile", Profile::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("NPC_Data", NPC_Data::get_singleton()));
#endif
ClassDB::register_class<Character>();
Character::get_singleton();
Engine::get_singleton()->add_singleton(Engine::Singleton("Character", Character::get_singleton()));
}
void unregister_character_types()
{
#if 0
SceneComps::cleanup();
Profile::cleanup();
NPC_Data::cleanup();
#endif
Character::cleanup();
}

View File

@@ -0,0 +1,7 @@
#ifndef ANIMATION_REGISTER_TYPES_H
#define ANIMATION_REGISTER_TYPES_H
void register_character_types();
void unregister_character_types();
#endif // ANIMATION_REGISTER_TYPES_H

View File

@@ -0,0 +1,11 @@
#ifndef SKELETON_PREP_H
#define SKELETON_PREP_H
#include <flecs.h>
struct skeleton_prep {
skeleton_prep(flecs::world &world)
{
world.module<skeleton_prep>();
}
};
#endif

View File

@@ -0,0 +1,64 @@
From 29a376ed6ad549cf80aa1fd215aa5d34e9b9c29f Mon Sep 17 00:00:00 2001
From: Sergey Lapin <slapinid@gmail.com>
Date: Fri, 13 Jan 2023 19:09:56 +0300
Subject: [PATCH] Shut up and fix some errors breaking engine
---
drivers/gles2/rasterizer_storage_gles2.cpp | 2 +-
drivers/gles3/rasterizer_storage_gles3.cpp | 2 +-
scene/animation/skeleton_ik.cpp | 6 +++++-
3 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp
index 028b399a59..807b95fe57 100644
--- a/drivers/gles2/rasterizer_storage_gles2.cpp
+++ b/drivers/gles2/rasterizer_storage_gles2.cpp
@@ -2833,7 +2833,7 @@ AABB RasterizerStorageGLES2::mesh_get_aabb(RID p_mesh, RID p_skeleton) const {
const bool *skused = mesh->surfaces[i]->skeleton_bone_used.ptr();
int sbs = sk->size;
- ERR_CONTINUE(bs > sbs);
+// ERR_CONTINUE(bs > sbs);
const float *texture = sk->bone_data.ptr();
bool first = true;
diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp
index c865c77bf8..bacc78d02d 100644
--- a/drivers/gles3/rasterizer_storage_gles3.cpp
+++ b/drivers/gles3/rasterizer_storage_gles3.cpp
@@ -4153,7 +4153,7 @@ AABB RasterizerStorageGLES3::mesh_get_aabb(RID p_mesh, RID p_skeleton) const {
const bool *skused = mesh->surfaces[i]->skeleton_bone_used.ptr();
int sbs = sk->size;
- ERR_CONTINUE(bs > sbs);
+// ERR_CONTINUE(bs > sbs);
const float *texture = sk->skel_texture.ptr();
bool first = true;
diff --git a/scene/animation/skeleton_ik.cpp b/scene/animation/skeleton_ik.cpp
index 954ec66a84..a4ad34fa2d 100644
--- a/scene/animation/skeleton_ik.cpp
+++ b/scene/animation/skeleton_ik.cpp
@@ -55,7 +55,7 @@ FabrikInverseKinematic::ChainItem *FabrikInverseKinematic::ChainItem::add_child(
/// Build a chain that starts from the root to tip
bool FabrikInverseKinematic::build_chain(Task *p_task, bool p_force_simple_chain) {
- ERR_FAIL_COND_V(-1 == p_task->root_bone, false);
+
Chain &chain(p_task->chain);
@@ -566,6 +566,10 @@ void SkeletonIK::reload_chain() {
if (!skeleton) {
return;
}
+ if (skeleton->find_bone(root_bone) < 0)
+ return;
+ if (skeleton->find_bone(tip_bone) < 0)
+ return;
task = FabrikInverseKinematic::create_simple_task(skeleton, skeleton->find_bone(root_bone), skeleton->find_bone(tip_bone), _get_target_transform());
if (task) {
--
2.34.1

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,37 @@
From 0c7a32d332d352544d797f3909604f49c6ba40b8 Mon Sep 17 00:00:00 2001
From: Sergey Lapin <slapinid@gmail.com>
Date: Thu, 6 Apr 2023 19:35:47 +0300
Subject: [PATCH] Asserts work in debug builds
---
modules/gdscript/gdscript_function.cpp | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp
index d58164ffa9..9bd955452d 100644
--- a/modules/gdscript/gdscript_function.cpp
+++ b/modules/gdscript/gdscript_function.cpp
@@ -28,6 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
+#include <cstdio>
+#include <cstdlib>
+
#include "gdscript_function.h"
#include "core/os/os.h"
@@ -1550,6 +1553,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
_err_print_error(err_func.utf8().get_data(), err_file.utf8().get_data(), err_line, err_text.utf8().get_data(), ERR_HANDLER_SCRIPT);
}
+#ifndef TOOLS_ENABLED
+ fprintf(stderr, "ASSERT failed: %ls\n", err_text.c_str());
+ abort();
+#endif
#endif
OPCODE_OUT;
}
--
2.34.1