initial commit
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
*.o
|
||||
*.pyc
|
||||
.import
|
||||
@@ -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
|
||||
Vendored
+17
@@ -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"}]
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+11
@@ -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"
|
||||
}
|
||||
}
|
||||
Vendored
+27
@@ -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"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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.
@@ -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
|
||||
@@ -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()
|
||||
@@ -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.
@@ -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")
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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"]
|
||||
@@ -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"]
|
||||
@@ -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 )]
|
||||
@@ -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 )]
|
||||
@@ -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
|
||||
@@ -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 )
|
||||
Binary file not shown.
@@ -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"
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 378 KiB |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 908 KiB |
@@ -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
|
||||
Binary file not shown.
+1407974
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
cd godot
|
||||
../src/godot/bin/godot.x11.opt.tools.64 -e $*
|
||||
Submodule
+1
Submodule src/godot added at db8700e8f8
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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>();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -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"
|
||||
Submodule
+1
Submodule src/modules/character/flecs added at bbf695607e
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user