Compare commits
2 Commits
8d95fcc97e
...
7fa48cab21
| Author | SHA1 | Date | |
|---|---|---|---|
| 7fa48cab21 | |||
| 8d1345a532 |
1
.gitattributes
vendored
@@ -1 +1,2 @@
|
||||
*.blend filter=lfs diff=lfs merge=lfs -text
|
||||
*.kra filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
4
.gitignore
vendored
@@ -1,4 +1,8 @@
|
||||
*~
|
||||
build/*
|
||||
build-vscode/*
|
||||
characters/*
|
||||
resources/buildings/*
|
||||
assets/blender/scripts/*.blend
|
||||
__pycache__/
|
||||
*.blend[1-9]
|
||||
|
||||
BIN
assets/blender/buildings/Atlas_00001.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
assets/blender/buildings/Atlas_00002.png
Normal file
|
After Width: | Height: | Size: 341 B |
BIN
assets/blender/buildings/Atlas_00003.png
Normal file
|
After Width: | Height: | Size: 341 B |
BIN
assets/blender/buildings/Atlas_00004.png
Normal file
|
After Width: | Height: | Size: 341 B |
BIN
assets/blender/buildings/Atlas_00005.png
Normal file
|
After Width: | Height: | Size: 341 B |
BIN
assets/blender/buildings/Atlas_00006.png
Normal file
|
After Width: | Height: | Size: 276 B |
BIN
assets/blender/buildings/Atlas_00007.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/blender/buildings/Atlas_00008.png
Normal file
|
After Width: | Height: | Size: 345 B |
BIN
assets/blender/buildings/Atlas_00009.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
assets/blender/buildings/Atlas_00010.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
assets/blender/buildings/Atlas_12696.png
Normal file
|
After Width: | Height: | Size: 346 B |
BIN
assets/blender/buildings/Atlas_32944.png
Normal file
|
After Width: | Height: | Size: 431 B |
BIN
assets/blender/buildings/Atlas_36953.png
Normal file
|
After Width: | Height: | Size: 437 B |
BIN
assets/blender/buildings/Atlas_52716.png
Normal file
|
After Width: | Height: | Size: 517 B |
BIN
assets/blender/buildings/Atlas_73934.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
assets/blender/buildings/atlas-gym-roughness.png
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
assets/blender/buildings/atlas-gym-specular.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
assets/blender/buildings/bus-stop.blend
LFS
Normal file
BIN
assets/blender/buildings/business-bar1.blend
LFS
Normal file
BIN
assets/blender/buildings/business-cafe1.blend
LFS
Normal file
BIN
assets/blender/buildings/business-office1.blend
LFS
Normal file
BIN
assets/blender/buildings/business-store1.blend
LFS
Normal file
BIN
assets/blender/buildings/dirt-road-corner.blend
LFS
Normal file
BIN
assets/blender/buildings/dirt-road-x.blend
LFS
Normal file
BIN
assets/blender/buildings/dirt-road.blend
LFS
Normal file
BIN
assets/blender/buildings/gym-exterior.blend
LFS
Normal file
BIN
assets/blender/buildings/home-exterior.blend
LFS
Normal file
BIN
assets/blender/buildings/joint-atlas-1-roughness.kra
LFS
Normal file
BIN
assets/blender/buildings/joint-atlas-1-roughness.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
assets/blender/buildings/joint-atlas-1-specular.kra
LFS
Normal file
BIN
assets/blender/buildings/joint-atlas-1-specular.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
assets/blender/buildings/joint-atlas-1.kra
LFS
Normal file
BIN
assets/blender/buildings/joint-atlas-1.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
assets/blender/buildings/logistics-center-exterior.blend
LFS
Normal file
BIN
assets/blender/buildings/lot-large1.blend
LFS
Normal file
BIN
assets/blender/buildings/lot-large2.blend
LFS
Normal file
BIN
assets/blender/buildings/lot-small-m0.blend
LFS
Normal file
BIN
assets/blender/buildings/lot-small0.blend
LFS
Normal file
BIN
assets/blender/buildings/lot-small1.blend
LFS
Normal file
BIN
assets/blender/buildings/lot-small2.blend
LFS
Normal file
BIN
assets/blender/buildings/material.blend
LFS
Normal file
BIN
assets/blender/buildings/office-exterior.blend
LFS
Normal file
BIN
assets/blender/buildings/power-in-box.blend
LFS
Normal file
BIN
assets/blender/buildings/power-pole-wire.blend
LFS
Normal file
BIN
assets/blender/buildings/power-pole.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-garage-door.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-garage-enterance.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-garage-roof1.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-garage.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house-stair.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house1.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house10.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house11.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house12-bottom-balcony.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house12-bottom-floor.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house12-enterance-stair.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house12-enterance.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house12-foundation.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house12-garage.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house12-orig.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house12-pipes.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house12-roof.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house12-second-floor.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house12-second-stair.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house12-simple-first-floor.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house12-simple-roof.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house12-simple-second-floor.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house12-simple.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house12-stair-bottom.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house12-top-balcony.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house12.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house13.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house2.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house3.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house4.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house5.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house6.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house6a.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house7.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house8.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-house9.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-hut1-lod.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-hut1.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-parking.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-wall-corner.blend
LFS
Normal file
BIN
assets/blender/buildings/residental-wall1.blend
LFS
Normal file
BIN
assets/blender/buildings/sideroad-short1.blend
LFS
Normal file
BIN
assets/blender/buildings/t1.blend
LFS
Normal file
BIN
assets/blender/buildings/terrain-lot1.bin
Normal file
BIN
assets/blender/buildings/terrain-lot1.blend
LFS
Normal file
153
assets/blender/buildings/terrain-lot1.gltf
Normal file
@@ -0,0 +1,153 @@
|
||||
{
|
||||
"asset":{
|
||||
"generator":"Khronos glTF Blender I/O v3.6.28",
|
||||
"version":"2.0"
|
||||
},
|
||||
"extensionsUsed":[
|
||||
"KHR_materials_specular",
|
||||
"KHR_materials_ior"
|
||||
],
|
||||
"scene":0,
|
||||
"scenes":[
|
||||
{
|
||||
"name":"Scene",
|
||||
"nodes":[
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes":[
|
||||
{
|
||||
"mesh":0,
|
||||
"name":"terrain-parking-lot-col"
|
||||
}
|
||||
],
|
||||
"materials":[
|
||||
{
|
||||
"alphaCutoff":0.5,
|
||||
"alphaMode":"MASK",
|
||||
"extensions":{
|
||||
"KHR_materials_specular":{
|
||||
"specularColorFactor":[
|
||||
0.474271529955476,
|
||||
0.474271529955476,
|
||||
0.474271529955476
|
||||
]
|
||||
},
|
||||
"KHR_materials_ior":{
|
||||
"ior":1.4500000476837158
|
||||
}
|
||||
},
|
||||
"name":"material_atlas_36953_1",
|
||||
"pbrMetallicRoughness":{
|
||||
"baseColorTexture":{
|
||||
"index":0
|
||||
},
|
||||
"metallicFactor":0
|
||||
}
|
||||
}
|
||||
],
|
||||
"meshes":[
|
||||
{
|
||||
"name":"Plane",
|
||||
"primitives":[
|
||||
{
|
||||
"attributes":{
|
||||
"POSITION":0,
|
||||
"NORMAL":1,
|
||||
"TEXCOORD_0":2
|
||||
},
|
||||
"indices":3,
|
||||
"material":0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"textures":[
|
||||
{
|
||||
"sampler":0,
|
||||
"source":0
|
||||
}
|
||||
],
|
||||
"images":[
|
||||
{
|
||||
"mimeType":"image/png",
|
||||
"name":"Atlas_36953",
|
||||
"uri":"Atlas_36953.png"
|
||||
}
|
||||
],
|
||||
"accessors":[
|
||||
{
|
||||
"bufferView":0,
|
||||
"componentType":5126,
|
||||
"count":250,
|
||||
"max":[
|
||||
25.032987594604492,
|
||||
0.3288002610206604,
|
||||
49.057586669921875
|
||||
],
|
||||
"min":[
|
||||
-25.032987594604492,
|
||||
-4.005487442016602,
|
||||
-37.008201599121094
|
||||
],
|
||||
"type":"VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView":1,
|
||||
"componentType":5126,
|
||||
"count":250,
|
||||
"type":"VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView":2,
|
||||
"componentType":5126,
|
||||
"count":250,
|
||||
"type":"VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView":3,
|
||||
"componentType":5123,
|
||||
"count":474,
|
||||
"type":"SCALAR"
|
||||
}
|
||||
],
|
||||
"bufferViews":[
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":3000,
|
||||
"byteOffset":0,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":3000,
|
||||
"byteOffset":3000,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":2000,
|
||||
"byteOffset":6000,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":948,
|
||||
"byteOffset":8000,
|
||||
"target":34963
|
||||
}
|
||||
],
|
||||
"samplers":[
|
||||
{
|
||||
"magFilter":9729,
|
||||
"minFilter":9987
|
||||
}
|
||||
],
|
||||
"buffers":[
|
||||
{
|
||||
"byteLength":8948,
|
||||
"uri":"terrain-lot1.bin"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
assets/blender/buildings/terrain-parking-lot.blend
LFS
Normal file
BIN
assets/blender/buildings/townhall-base.blend
LFS
Normal file
BIN
assets/blender/buildings/townhall-exterior.blend
LFS
Normal file
BIN
assets/blender/buildings/zebra.blend
LFS
Normal file
72
assets/blender/scripts/export_buildings.py
Normal file
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os, sys, time
|
||||
import bpy
|
||||
from math import pi
|
||||
import glob
|
||||
import shutil
|
||||
from mathutils import Vector, Matrix
|
||||
from math import radians, pi
|
||||
|
||||
argv = sys.argv
|
||||
argv = argv[argv.index("--") + 1:]
|
||||
|
||||
sys.path.insert(0, os.getcwd() + "/assets/blender/scripts")
|
||||
|
||||
gltf_file = argv[0]
|
||||
print("Exporting to " + gltf_file)
|
||||
basepath = os.getcwd()
|
||||
# bpy.ops.export_scene.gltf(filepath="", check_existing=True,
|
||||
# export_import_convert_lighting_mode='SPEC', gltf_export_id="",
|
||||
# export_format='GLB', ui_tab='GENERAL', export_copyright="", export_image_format='AUTO',
|
||||
# export_texture_dir="", export_jpeg_quality=75, export_keep_originals=False,
|
||||
# export_texcoords=True, export_normals=True, export_draco_mesh_compression_enable=False,
|
||||
# export_draco_mesh_compression_level=6, export_draco_position_quantization=14,
|
||||
# export_draco_normal_quantization=10, export_draco_texcoord_quantization=12,
|
||||
# export_draco_color_quantization=10, export_draco_generic_quantization=12, export_tangents=False,
|
||||
# export_materials='EXPORT', export_original_specular=False, export_colors=True,
|
||||
# export_attributes=False, use_mesh_edges=False, use_mesh_vertices=False, export_cameras=False,
|
||||
# use_selection=False, use_visible=False, use_renderable=False,
|
||||
# use_active_collection_with_nested=True, use_active_collection=False, use_active_scene=False,
|
||||
# export_extras=False, export_yup=True, export_apply=False, export_animations=True,
|
||||
# export_frame_range=False, export_frame_step=1, export_force_sampling=True, export_animation_mode='ACTIONS',
|
||||
# export_nla_strips_merged_animation_name="Animation", export_def_bones=False,
|
||||
# export_hierarchy_flatten_bones=False, export_optimize_animation_size=True,
|
||||
# export_optimize_animation_keep_anim_armature=True, export_optimize_animation_keep_anim_object=False,
|
||||
# export_negative_frame='SLIDE', export_anim_slide_to_zero=False, export_bake_animation=False,
|
||||
# export_anim_single_armature=True, export_reset_pose_bones=True, export_current_frame=False,
|
||||
# export_rest_position_armature=True, export_anim_scene_split_object=True, export_skins=True,
|
||||
# export_all_influences=False, export_morph=True, export_morph_normal=True,
|
||||
# export_morph_tangent=False, export_morph_animation=True, export_morph_reset_sk_data=True,
|
||||
# export_lights=False, export_nla_strips=True, will_save_settings=False, filter_glob="*.glb")
|
||||
|
||||
bpy.ops.export_scene.gltf(filepath=gltf_file,
|
||||
use_selection=False,
|
||||
check_existing=False,
|
||||
export_format='GLB',
|
||||
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_apply=True,
|
||||
export_animations=True,
|
||||
export_force_sampling=True,
|
||||
export_def_bones=False,
|
||||
export_current_frame=False,
|
||||
export_morph=True,
|
||||
export_morph_animation=False,
|
||||
export_morph_normal=True,
|
||||
export_morph_tangent=True,
|
||||
export_lights=False,
|
||||
export_skins=True)
|
||||
|
||||
bpy.ops.wm.read_homefile(use_empty=True)
|
||||
time.sleep(2)
|
||||
bpy.ops.wm.quit_blender()
|
||||
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/" + "vroid-normal-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()
|
||||
121
assets/blender/scripts/export_for_modelling.py
Normal file
@@ -0,0 +1,121 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os, sys, time
|
||||
import bpy
|
||||
from math import pi
|
||||
import glob
|
||||
import shutil
|
||||
from mathutils import Vector, Matrix
|
||||
from math import radians, pi
|
||||
sys.path.insert(0, os.getcwd() + "/assets/blender/scripts")
|
||||
from vrm import rename
|
||||
from mixamo import mixamo_rig
|
||||
from mixamo.lib.armature import *
|
||||
#from mixamo.mixamoroot import import_armature
|
||||
#from mixamo.import_mixamo_root_motion import import_mixamo_root_motion
|
||||
|
||||
from settings import VRMDataFemale, VRMDataMale, basepath
|
||||
|
||||
imports = [VRMDataFemale(), VRMDataMale()]
|
||||
|
||||
|
||||
class mkrig:
|
||||
ik_arms = True
|
||||
ik_legs = True
|
||||
def __init__(self):
|
||||
mixamo_rig._make_rig(self)
|
||||
mixamo_rig.remove_temp_objects()
|
||||
mixamo_rig.clean_scene()
|
||||
def get_anim(filepath, root_bone_name="Root", hip_bone_name="mixamorig:Hips", remove_prefix=False, name_prefix="mixamorig:", insert_root=False, delete_armatures=False):
|
||||
# current_context = bpy.context.area.ui_type
|
||||
old_objs = set(bpy.context.scene.objects)
|
||||
try:
|
||||
import_armature(filepath, root_bone_name, hip_bone_name, remove_prefix, name_prefix, insert_root, delete_armatures)
|
||||
imported_objects = set(bpy.context.scene.objects) - old_objs
|
||||
if delete_armatures and num_files > 1:
|
||||
deleteArmature(imported_objects)
|
||||
except Exception as e:
|
||||
log.error("[Mixamo Root] ERROR get_all_anims raised %s when processing %s" % (str(e), filepath))
|
||||
# bpy.context.area.ui_type = current_context
|
||||
bpy.context.scene.frame_start = 0
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
def create_root_bone(anim_obj):
|
||||
bpy.context.view_layer.objects.active = anim_obj
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
root_bone = anim_obj.data.edit_bones.new("Root")
|
||||
root_bone.tail.y = 0.5
|
||||
anim_obj.data.edit_bones["mixamorig:Hips"].parent = anim_obj.data.edit_bones["Root"]
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
def hips_to_root(anim_obj):
|
||||
for fc in anim_obj.animation_data.action.fcurves:
|
||||
if "mixamorig:Hips" in fc.data_path:
|
||||
if "location" in fc.data_path:
|
||||
print(fc, fc.data_path, fc.array_index)
|
||||
if fc.array_index in [0, 2]:
|
||||
fc.data_path = fc.data_path.replace("mixamorig:Hips", "Root")
|
||||
# if fc.array_index == 2:
|
||||
# for kp in fc.keyframe_points:
|
||||
# kp.co.z = min(0, abs(kp.co.z))
|
||||
# hip_curves = [fc for fc in anim_obj.animation_data.action.fcurves if hip_bone_name in fc.data_path and fc.data_path.startswith('location')]
|
||||
# print(hip_curves)
|
||||
def hips_to_root(anim_obj):
|
||||
for fc in anim_obj.animation_data.action.fcurves:
|
||||
if "mixamorig:Hips" in fc.data_path:
|
||||
if "location" in fc.data_path:
|
||||
print(fc, fc.data_path, fc.array_index)
|
||||
if fc.array_index in [0, 2]:
|
||||
fc.data_path = fc.data_path.replace("mixamorig:Hips", "Root")
|
||||
def hips_to_root2(anim_obj):
|
||||
for fc in anim_obj.animation_data.action.fcurves:
|
||||
if "Ctrl_Hips" in fc.data_path:
|
||||
if "location" in fc.data_path:
|
||||
print(fc, fc.data_path, fc.array_index)
|
||||
if fc.array_index in [0, 2]:
|
||||
data_path = fc.data_path[:]
|
||||
# fc.data_path = data_path.replace("Ctrl_Hips", "Ctrl_Master")
|
||||
fcr = anim_obj.animation_data.action.fcurves.new(data_path = data_path.replace("Ctrl_Hips", "Root"), index = fc.array_index)
|
||||
keys = [0] * (2 * len(fc.keyframe_points))
|
||||
fcr.keyframe_points.add(len(fc.keyframe_points))
|
||||
fc.keyframe_points.foreach_get("co", keys)
|
||||
fcr.keyframe_points.foreach_set("co", keys)
|
||||
print("ROOT")
|
||||
# fc.data_path = data_path.replace("Ctrl_Hips", "Root")
|
||||
# for fcr in anim_obj.animation_data.action.fcurves:
|
||||
# if "Root" in fcr.data_path:
|
||||
# fcr.array_index = fc.array_index
|
||||
# print("ROOT")
|
||||
|
||||
for imp in imports:
|
||||
bpy.ops.wm.read_homefile(use_empty=True)
|
||||
for o in bpy.data.objects:
|
||||
bpy.data.objects.remove(o)
|
||||
print(basepath + "/assets/vroid/" + imp.path)
|
||||
result = bpy.ops.import_scene.vrm(filepath=(basepath + "/assets/vroid/" + imp.path))
|
||||
if result != {"FINISHED"}:
|
||||
raise Exception(f"Failed to import vrm: {result}")
|
||||
armature_count = 0
|
||||
for obj in bpy.data.objects:
|
||||
if (obj.type == "ARMATURE"):
|
||||
armature_count += 1
|
||||
if armature_count > 1:
|
||||
raise Exception("Bad scene data")
|
||||
main_armature = None
|
||||
for obj in bpy.data.objects:
|
||||
if (obj.type == "ARMATURE"):
|
||||
main_armature = obj
|
||||
break
|
||||
if main_armature == None:
|
||||
raise Exception("Bad scene data")
|
||||
print("Renaming...")
|
||||
main_armature.select_set(True)
|
||||
bpy.context.view_layer.objects.active = main_armature
|
||||
rename.rename_bones(main_armature)
|
||||
main_armature.select_set(False)
|
||||
main_armature.name = imp.armature_name
|
||||
main_armature["VRM_IMPORTED_NAME"] = "female"
|
||||
if main_armature.animation_data is None:
|
||||
main_armature.animation_data_create()
|
||||
|
||||
main_armature.select_set(False)
|
||||
bpy.context.view_layer.objects.active = None
|
||||
bpy.ops.wm.save_as_mainfile(filepath=(basepath + "/assets/blender/" + imp.editfile))
|
||||