initial commit
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
*.o
|
||||
*.pyc
|
||||
.import
|
||||
8
.gitmodules
vendored
Normal file
8
.gitmodules
vendored
Normal 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
17
.vscode/launch.json
vendored
Normal 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
11
.vscode/settings.json
vendored
Normal 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
27
.vscode/tasks.json
vendored
Normal 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
31
Makefile
Normal 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
12
TODO
Normal 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
|
||||
|
||||
BIN
assets/blender/female-coat2.blend
Normal file
BIN
assets/blender/female-coat2.blend
Normal file
Binary file not shown.
61
assets/blender/scripts/drivers.py
Normal file
61
assets/blender/scripts/drivers.py
Normal 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
|
||||
371
assets/blender/scripts/export_clothes.py
Normal file
371
assets/blender/scripts/export_clothes.py
Normal 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()
|
||||
265
assets/blender/scripts/export_models.py
Normal file
265
assets/blender/scripts/export_models.py
Normal 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()
|
||||
BIN
assets/blender/scripts/tmp-female.blend
Normal file
BIN
assets/blender/scripts/tmp-female.blend
Normal file
Binary file not shown.
BIN
assets/blender/scripts/tmp-male.blend
Normal file
BIN
assets/blender/scripts/tmp-male.blend
Normal file
Binary file not shown.
BIN
assets/blender/vroid1-female.blend
Normal file
BIN
assets/blender/vroid1-female.blend
Normal file
Binary file not shown.
BIN
assets/blender/vroid1-man-animate.blend
Normal file
BIN
assets/blender/vroid1-man-animate.blend
Normal file
Binary file not shown.
5
debug.py
Normal file
5
debug.py
Normal 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
BIN
godot/DefaultSkin.material
Normal file
Binary file not shown.
BIN
godot/F00_000_00_Body_00_SKIN.material
Normal file
BIN
godot/F00_000_00_Body_00_SKIN.material
Normal file
Binary file not shown.
BIN
godot/M00_000_00_Body_00_SKIN_001.material
Normal file
BIN
godot/M00_000_00_Body_00_SKIN_001.material
Normal file
Binary file not shown.
16
godot/characters/female-base.tscn
Normal file
16
godot/characters/female-base.tscn
Normal 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"]
|
||||
17
godot/characters/male-base.tscn
Normal file
17
godot/characters/male-base.tscn
Normal 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"]
|
||||
5
godot/characters/vroid1-female.tscn
Normal file
5
godot/characters/vroid1-female.tscn
Normal 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 )]
|
||||
5
godot/characters/vroid1-man-animate.tscn
Normal file
5
godot/characters/vroid1-man-animate.tscn
Normal 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
35
godot/entries/main.gd
Normal 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
25
godot/entries/main.tscn
Normal 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
BIN
godot/privates.material
Normal file
Binary file not shown.
13
godot/project.godot
Normal file
13
godot/project.godot
Normal 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
BIN
godot/textures/Image_7.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 378 KiB |
35
godot/textures/Image_7.png.import
Normal file
35
godot/textures/Image_7.png.import
Normal 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
|
||||
BIN
godot/textures/M00_000_00_Body_00.png
Normal file
BIN
godot/textures/M00_000_00_Body_00.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 MiB |
35
godot/textures/M00_000_00_Body_00.png.import
Normal file
35
godot/textures/M00_000_00_Body_00.png.import
Normal 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
|
||||
BIN
godot/textures/M00_000_00_Body_00_nml.png
Normal file
BIN
godot/textures/M00_000_00_Body_00_nml.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.7 MiB |
35
godot/textures/M00_000_00_Body_00_nml.png.import
Normal file
35
godot/textures/M00_000_00_Body_00_nml.png.import
Normal 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
|
||||
BIN
godot/textures/Shader_NoneBlack_01.png
Normal file
BIN
godot/textures/Shader_NoneBlack_01.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 81 B |
35
godot/textures/Shader_NoneBlack_01.png.import
Normal file
35
godot/textures/Shader_NoneBlack_01.png.import
Normal 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
BIN
godot/textures/privates.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 908 KiB |
35
godot/textures/privates.png.import
Normal file
35
godot/textures/privates.png.import
Normal 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
BIN
godot/vroid1-female.bin
Normal file
Binary file not shown.
1407974
godot/vroid1-female.gltf
Normal file
1407974
godot/vroid1-female.gltf
Normal file
File diff suppressed because it is too large
Load Diff
1065
godot/vroid1-female.gltf.import
Normal file
1065
godot/vroid1-female.gltf.import
Normal file
File diff suppressed because it is too large
Load Diff
BIN
godot/vroid1-man-animate.bin
Normal file
BIN
godot/vroid1-man-animate.bin
Normal file
Binary file not shown.
637629
godot/vroid1-man-animate.gltf
Normal file
637629
godot/vroid1-man-animate.gltf
Normal file
File diff suppressed because it is too large
Load Diff
1065
godot/vroid1-man-animate.gltf.import
Normal file
1065
godot/vroid1-man-animate.gltf.import
Normal file
File diff suppressed because it is too large
Load Diff
4
run-editor.sh
Executable file
4
run-editor.sh
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
cd godot
|
||||
../src/godot/bin/godot.x11.opt.tools.64 -e $*
|
||||
1
src/godot
Submodule
1
src/godot
Submodule
Submodule src/godot added at db8700e8f8
17
src/modules/character/SCsub
Normal file
17
src/modules/character/SCsub
Normal 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
|
||||
|
||||
635
src/modules/character/character.cpp
Normal file
635
src/modules/character/character.cpp
Normal 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());
|
||||
}
|
||||
150
src/modules/character/character.h
Normal file
150
src/modules/character/character.h
Normal 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
|
||||
212
src/modules/character/character_skeleton.cpp
Normal file
212
src/modules/character/character_skeleton.cpp
Normal 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
|
||||
}
|
||||
154
src/modules/character/character_slot.cpp
Normal file
154
src/modules/character/character_slot.cpp
Normal 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>();
|
||||
}
|
||||
});
|
||||
}
|
||||
14
src/modules/character/config.py
Normal file
14
src/modules/character/config.py
Normal 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"
|
||||
1
src/modules/character/flecs
Submodule
1
src/modules/character/flecs
Submodule
Submodule src/modules/character/flecs added at bbf695607e
44
src/modules/character/register_types.cpp
Normal file
44
src/modules/character/register_types.cpp
Normal 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();
|
||||
}
|
||||
7
src/modules/character/register_types.h
Normal file
7
src/modules/character/register_types.h
Normal 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
|
||||
11
src/modules/character/skeleton-prep.h
Normal file
11
src/modules/character/skeleton-prep.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
1262
src/patches/0002-Revert-PopupMenu-rework-and-enhancements.patch
Normal file
1262
src/patches/0002-Revert-PopupMenu-rework-and-enhancements.patch
Normal file
File diff suppressed because it is too large
Load Diff
37
src/patches/0003-Asserts-work-in-debug-builds.patch
Normal file
37
src/patches/0003-Asserts-work-in-debug-builds.patch
Normal 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
|
||||
|
||||
Reference in New Issue
Block a user