Files
kicking-high/proto2/characters/generate_maps.gd

453 lines
17 KiB
GDScript

extends Control
const TEX_SIZE: int = 512
var maps = {}
var vert_indices = {}
var draw_data_list : = []
var dnatool: DNATool
onready var characters = [load("res://characters/female_2018.escn"), load("res://characters/male_2018.escn")]
func find_mesh_name(base: Node, mesh_name: String) -> MeshInstance:
var queue = [base]
var mi: MeshInstance
while queue.size() > 0:
var item = queue[0]
queue.pop_front()
if item is MeshInstance && item.name == mesh_name:
mi = item
break
for c in item.get_children():
queue.push_back(c)
return mi
func find_same_verts():
for chdata in range(characters.size()):
var ch_scene = characters[chdata].instance()
var bmesh = find_mesh_name(ch_scene, "body")
if !vert_indices.has(chdata):
vert_indices[chdata] = {}
for surface in range(bmesh.mesh.get_surface_count()):
var arrays: Array = bmesh.mesh.surface_get_arrays(surface).duplicate(true)
for index1 in range(arrays[ArrayMesh.ARRAY_VERTEX].size()):
var v1: Vector3 = arrays[ArrayMesh.ARRAY_VERTEX][index1]
var ok = false
for rk in vert_indices[chdata].keys():
if (v1 - rk).length() < 0.001:
ok = true
vert_indices[chdata][rk].push_back(index1)
if !ok:
vert_indices[chdata][v1] = [index1]
static func check_triangle(verts: Array) -> bool:
var uv1 = verts[0].uv
var uv2 = verts[1].uv
var uv3 = verts[2].uv
var v1 = uv1 - uv3
var v2 = uv1 - uv3
if v1.length() * TEX_SIZE < 1.2:
return false
if v2.length() * TEX_SIZE < 1.2:
return false
var sumdata = Vector3()
for k in range(2):
for ks in range(verts[k].shape.size()):
sumdata += verts[k].shape[ks]
if sumdata.length() < 0.001:
return false
return true
static func pad_morphs(morphs: Dictionary, nshapes: Dictionary, min_point: Vector3, max_point: Vector3, min_normal: Vector3, max_normal: Vector3):
for mesh in morphs.keys():
for m in morphs[mesh].keys():
var ns = nshapes[mesh][m]
for t in range(morphs[mesh][m].size()):
for v in range(morphs[mesh][m][t].size()):
# print(morphs[m][t][v])
for s in range(morphs[mesh][m][t][v].shape.size()):
for u in range(3):
var cd : float = max_point[u] - min_point[u]
var ncd : float = max_normal[u] - min_normal[u]
var d = morphs[mesh][m][t][v].shape[s][u]
morphs[mesh][m][t][v].shape[s][u] = (d - min_point[u]) / cd
var ew = morphs[mesh][m][t][v].shape[s][u] * cd + min_point[u]
assert(abs(ew - d) < 0.001)
morphs[mesh][m][t][v].normal[s][u] = (morphs[mesh][m][t][v].normal[s][u] - min_normal[u]) / ncd
static func fill_draw_data(morphs: Dictionary, draw_data: Dictionary, morph_names: Dictionary, nshapes: Dictionary, rects: Dictionary):
var offset : = 0
for mesh in morphs.keys():
for m in morphs[mesh].keys():
if !draw_data.has(m):
draw_data[m] = {}
var ns = nshapes[mesh][m]
for sh in range(ns):
print(morph_names[mesh][m][sh], ": ", m, " ", sh + offset)
draw_data[m][sh + offset] = {"name": morph_names[mesh][m][sh], "triangles": [], "rects": []}
for t in range(morphs[mesh][m].size()):
var tri : = []
var midp = Vector2()
var sp = Vector3()
for v in range(morphs[mesh][m][t].size()):
midp += morphs[mesh][m][t][v].uv
midp /= 3.0
for v in range(morphs[mesh][m][t].size()):
var pt = morphs[mesh][m][t][v].uv - midp
var dpt = pt.normalized() * (3.5 / TEX_SIZE)
tri.push_back({"uv": morphs[mesh][m][t][v].uv + dpt, "shape": morphs[mesh][m][t][v].shape[sh], "normal": morphs[mesh][m][t][v].normal[sh]})
draw_data[m][sh + offset].triangles.push_back(tri)
draw_data[m][sh + offset].rect = rects[mesh][m][sh]
offset = draw_data[m].keys().size()
var common = []
var common_path = "characters/common"
func load_data():
var fd = File.new()
fd.open("characters/common/data.json", File.READ)
var json = fd.get_as_text()
var json_result = JSON.parse(json)
var json_data = json_result.result
fd.close()
for e in json_data.files:
var load_path = "res://" + e
var item = load(load_path)
assert(item)
common.push_back(item)
static func update_rects(arrays: Array, bshapes: Array) -> Dictionary:
var rects = {}
for idx in range(0, arrays[ArrayMesh.ARRAY_INDEX].size(), 3):
for t in range(3):
var index_base = arrays[ArrayMesh.ARRAY_INDEX][idx + t]
var vertex_base = arrays[ArrayMesh.ARRAY_VERTEX][index_base]
var normal_base = arrays[ArrayMesh.ARRAY_NORMAL][index_base]
var uv_base = arrays[ArrayMesh.ARRAY_TEX_UV][index_base]
for bsc in range(bshapes.size()):
if !rects.has(bsc):
rects[bsc] = Rect2(uv_base, Vector2())
var vertex_mod = bshapes[bsc][ArrayMesh.ARRAY_VERTEX][index_base] - vertex_base
if vertex_mod.length() > 0.0001:
rects[bsc] = rects[bsc].expand(uv_base)
return rects
static func update_triangles(arrays: Array, bshapes: Array) -> Array:
var triangles: Array = []
var ntriangles : = 0
var skipped : = 0
for idx in range(0, arrays[ArrayMesh.ARRAY_INDEX].size(), 3):
var verts = []
for t in range(3):
var index_base = arrays[ArrayMesh.ARRAY_INDEX][idx + t]
var vertex_base = arrays[ArrayMesh.ARRAY_VERTEX][index_base]
var normal_base = arrays[ArrayMesh.ARRAY_NORMAL][index_base]
var uv_base = arrays[ArrayMesh.ARRAY_TEX_UV][index_base]
var index_shape = []
var vertex_shape = []
var normal_shape = []
var uv_shape = []
for bsc in range(bshapes.size()):
index_shape.push_back(bshapes[bsc][ArrayMesh.ARRAY_INDEX][idx + t])
var index = index_shape[index_shape.size() - 1]
if index != index_base:
print("index mismatch", bsc, " ", index_base, " ", index)
var vertex_mod = bshapes[bsc][ArrayMesh.ARRAY_VERTEX][index] - vertex_base
var normal_mod = bshapes[bsc][ArrayMesh.ARRAY_NORMAL][index] - normal_base
vertex_shape.push_back(vertex_mod)
normal_shape.push_back(normal_mod)
uv_shape.push_back(bshapes[bsc][ArrayMesh.ARRAY_TEX_UV][index])
if (uv_shape[uv_shape.size() - 1] - uv_base).length() != 0:
print("uv mismatch", bsc, " ", idx)
var vdata = {}
vdata.shape = vertex_shape
vdata.normal = normal_shape
vdata.uv = uv_base
verts.push_back(vdata)
if check_triangle(verts):
triangles.push_back(verts)
ntriangles += 1
else:
skipped += 1
if skipped > 0:
print("ntriangles: ", ntriangles, " skipped: ", skipped)
return triangles
static func get_shape_names(mesh: ArrayMesh) -> PoolStringArray:
var shape_names: Array = []
for r in range(mesh.get_blend_shape_count()):
shape_names.push_back(mesh.get_blend_shape_name(r))
return PoolStringArray(shape_names)
static func process_morph_meshes(mesh: ArrayMesh, morphs: Dictionary, rects: Dictionary, mesh_data: Dictionary, nshapes: Dictionary):
for sc in range(mesh.get_surface_count()):
if !morphs.has(sc):
morphs[sc] = []
mesh_data[sc] = PoolStringArray()
rects[sc] = {}
var bshapes: Array = mesh.surface_get_blend_shape_arrays(sc)
var arrays: Array = mesh.surface_get_arrays(sc)
print("vertices: ", arrays[ArrayMesh.ARRAY_VERTEX].size())
print("indices: ", arrays[ArrayMesh.ARRAY_INDEX].size())
print("surf: ", sc, " shapes: ", bshapes.size())
var shape_names : = get_shape_names(mesh)
rects[sc] = update_rects(arrays, bshapes)
var triangles : = update_triangles(arrays, bshapes)
morphs[sc] += triangles
mesh_data[sc] += shape_names
nshapes[sc] = bshapes.size()
var mesh_queue = []
static func _process_morph_meshes(mesh: ArrayMesh) -> Dictionary:
var ret = {}
for sc in range(mesh.get_surface_count()):
var bshapes: Array = mesh.surface_get_blend_shape_arrays(sc)
var arrays: Array = mesh.surface_get_arrays(sc)
var shape_names : = get_shape_names(mesh)
ret[sc] = {}
ret[sc].rects = update_rects(arrays, bshapes)
var triangles : = update_triangles(arrays, bshapes)
ret[sc].morphs = triangles
ret[sc].mesh_data = shape_names
ret[sc].nshapes = bshapes.size()
return ret
static func _pad_morphs(morphs: Dictionary, nshapes: Dictionary, min_point: Vector3, max_point: Vector3, min_normal: Vector3, max_normal: Vector3):
# mesh - scene id from common array
for mesh in morphs.keys():
for m in morphs[mesh].keys():
var ns = nshapes[mesh][m]
for t in range(morphs[mesh][m].size()):
for v in range(morphs[mesh][m][t].size()):
# print(morphs[m][t][v])
for s in range(morphs[mesh][m][t][v].shape.size()):
for u in range(3):
var cd : float = max_point[u] - min_point[u]
var ncd : float = max_normal[u] - min_normal[u]
var d = morphs[mesh][m][t][v].shape[s][u]
morphs[mesh][m][t][v].shape[s][u] = (d - min_point[u]) / cd
var ew = morphs[mesh][m][t][v].shape[s][u] * cd + min_point[u]
assert(abs(ew - d) < 0.001)
morphs[mesh][m][t][v].normal[s][u] = (morphs[mesh][m][t][v].normal[s][u] - min_normal[u]) / ncd
#func build_queue():
# var scene_data : = {}
# for scene_no in range(common.size()):
# var ch: Node = common[scene_no].instance()
# var helper_data : = {}
# for mesh_name in ["base", "robe_helper", "tights_helper", "skirt_helper"]:
# helper_data[mesh_name] = {}
# var mi: MeshInstance = find_mesh_name(ch, mesh_name)
# var mesh: ArrayMesh = mi.mesh
# var morph_list = get_shape_names(mesh)
# var helper_shapes : = PoolStringArray()
# for e in morph_list:
# if !e in helper_shapes:
# helper_shapes.push_back(e)
# dnatool.find_min_max(mesh)
# helper_data[mesh_name].shape_list = helper_shapes
# helper_data[mesh_name].morph_data = _process_morph_meshes(mesh)
# scene_data[scene_no] = helper_data
func _ready():
dnatool = DNATool.new()
$gen/drawable.connect("drawing_finished", self, "handle_drawing_finished", [$gen])
var morphs = {}
var morphs_helper = {}
var mesh_data = {}
var mesh_data_helper = {}
var nshapes = {}
var rects = {}
var rects_helper = {}
var nshapes_helper = {}
load_data()
# build_queue()
var base_shapes : = PoolStringArray()
var file_shapes = {}
for mesh_no in range(common.size()):
var ch: Node = common[mesh_no].instance()
var mi: MeshInstance = find_mesh_name(ch, "base")
var mesh: ArrayMesh = mi.mesh
var morph_list = get_shape_names(mesh)
base_shapes += morph_list
file_shapes[common[mesh_no].resource_path] = morph_list
for helper in ["robe_helper", "tights_helper", "skirt_helper"]:
var helper_shapes : = PoolStringArray()
for mesh_no in range(common.size()):
var ch: Node = common[mesh_no].instance()
var mi: MeshInstance = find_mesh_name(ch, helper)
var mesh: ArrayMesh = mi.mesh
var morph_list = get_shape_names(mesh)
helper_shapes += morph_list
for e in base_shapes:
assert(e in helper_shapes)
for mesh_no in range(common.size()):
# var skipped : = 0
# var ntriangles : = 0
var ch: Node = common[mesh_no].instance()
# add_child(ch)
var mi: MeshInstance = find_mesh_name(ch, "base")
if !mi:
return
var mesh: ArrayMesh = mi.mesh
if !mesh:
return
dnatool.find_min_max(mesh)
if !morphs.has(mesh_no):
morphs[mesh_no] = {}
mesh_data[mesh_no] = {}
nshapes[mesh_no] = {}
rects[mesh_no] = {}
process_morph_meshes(mesh, morphs[mesh_no], rects[mesh_no], mesh_data[mesh_no], nshapes[mesh_no])
pad_morphs(morphs, nshapes, dnatool.min_point, dnatool.max_point, dnatool.min_normal, dnatool.max_normal)
var draw_data: Dictionary = {}
fill_draw_data(morphs, draw_data, mesh_data, nshapes, rects)
draw_data_list.push_back(draw_data)
for helper_name in ["robe_helper", "tights_helper", "skirt_helper"]:
for mesh_no in range(common.size()):
var ch: Node = common[mesh_no].instance()
# var mi: MeshInstance = find_mesh_name(ch, "base")
var mi_helper: MeshInstance = find_mesh_name(ch, helper_name)
assert(mi_helper != null)
var mesh: ArrayMesh = mi_helper.mesh
# var mesh_skirt: ArrayMesh = mi_skirt.mesh
morphs_helper[mesh_no] = {}
rects_helper[mesh_no] = {}
mesh_data_helper[mesh_no] = {}
nshapes_helper[mesh_no] = {}
process_morph_meshes(mesh, morphs_helper[mesh_no], rects_helper[mesh_no], mesh_data_helper[mesh_no], nshapes_helper[mesh_no])
pad_morphs(morphs_helper, nshapes_helper, dnatool.min_point, dnatool.max_point, dnatool.min_normal, dnatool.max_normal)
var helper_draw_data: Dictionary = {}
fill_draw_data(morphs_helper, helper_draw_data, mesh_data_helper, nshapes, rects_helper)
draw_data_list.push_back(helper_draw_data)
# TODO: combine helpers here
# var draw_data: Dictionary = {}
# fill_draw_data(morphs, draw_data, mesh_data, nshapes, rects)
# draw_data_list.push_back(draw_data)
# var draw_data_helper: Dictionary = {}
# fill_draw_data(morphs_helper, draw_data_helper, mesh_data_helper, nshapes_helper, rects_helper)
# draw_data_list.push_back(draw_data_helper)
print("data count: ", draw_data.keys(), " ", draw_data[0].keys())
$gen/drawable.triangles = draw_data[0][0].triangles
$gen/drawable.min_point = dnatool.min_point
$gen/drawable.max_point = dnatool.max_point
$gen/drawable.normals = false
# print("done ", mesh.get_surface_count(), " ", mesh.get_blend_shape_count(), " ", min_point, " ", max_point, " added: ", ntriangles, " skipped: ", skipped)
var helper : = 0
var surface : = 0
var shape : = 0
var exit_delay : = 3.0
var draw_delay : = 2.0
#func save_viewport(v: Viewport, maplist: Dictionary, shape_name: String, rect: Rect2, normals: bool):
## v.set_clear_mode(Viewport.CLEAR_MODE_ONLY_NEXT_FRAME)
## yield(get_tree(), "idle_frame")
## yield(get_tree(), "idle_frame")
# var viewport: Viewport = v
# var vtex : = viewport.get_texture()
# var tex_img : = vtex.get_data()
# var fn = ""
# if !maplist.has(shape_name):
# maplist[shape_name] = {}
# maplist[shape_name].width = tex_img.get_width()
# maplist[shape_name].height = tex_img.get_height()
# maplist[shape_name].format = tex_img.get_format()
# var byte_data = tex_img.duplicate(true).get_data()
# var image_size = byte_data.size()
# if normals:
# maplist[shape_name].image_normal_data = byte_data.compress(File.COMPRESSION_FASTLZ)
# maplist[shape_name].image_normal_size = image_size
# else:
# maplist[shape_name].image_data = byte_data.compress(File.COMPRESSION_FASTLZ)
# maplist[shape_name].rect = rect.grow(0.003)
# maplist[shape_name].image_size = image_size
var helpers_prefix = ["", "robe_", "tights_", "skirt_"]
func finish_map_gen():
print("generating same vert indices...")
find_same_verts()
var fd = File.new()
fd.open("res://characters/common/config.bin", File.WRITE)
fd.store_var(dnatool.min_point)
fd.store_var(dnatool.max_point)
fd.store_var(dnatool.min_normal)
fd.store_var(dnatool.max_normal)
fd.store_var(maps)
fd.store_var(vert_indices)
fd.close()
save_images()
get_tree().quit()
# get_tree().change_scene("res://map_test.tscn")
func next_surface():
shape = 0
surface += 1
draw_delay = 1.0
$gen_maps/ProgressBar.value = 100.0
$gen/drawable.normals = false
func next_helper():
shape = 0
surface = 0
draw_delay = 1.0
helper += 1
$gen/drawable.normals = false
if helper >= helpers_prefix.size():
finish_map_gen()
func setup_draw():
$gen/drawable.normals = !$gen/drawable.normals
if $gen/drawable.normals:
$gen/drawable.min_point = dnatool.min_normal
$gen/drawable.max_point = dnatool.max_normal
else:
$gen/drawable.min_point = dnatool.min_point
$gen/drawable.max_point = dnatool.max_point
$gen/drawable.triangles = draw_data_list[helper][surface][shape].triangles
$gen/drawable.update()
func save_images():
for k in maps.keys():
for e in ["diffuse", "normal"]:
var fn = "res://characters/common/" + k + "_" + e + "_orig.png"
var data: PoolByteArray
var size: int
if e == "diffuse":
data = maps[k].image_data
size = maps[k].image_size
elif e == "normal":
data = maps[k].image_normal_data
size = maps[k].image_normal_size
var image_data = data.decompress(size, File.COMPRESSION_FASTLZ)
var img = Image.new()
img.create_from_data(maps[k].width, maps[k].height, false, maps[k].format, image_data)
print("saving ", fn)
img.save_png(fn)
var fd = File.new()
var outj = {}
outj.min_point = dnatool.min_point
outj.max_point = dnatool.max_point
outj.min_normal = dnatool.min_normal
outj.max_normal = dnatool.max_normal
outj.maps = maps
outj.vert_indices = vert_indices
fd.open("res://characters/common/debug-data-orig.json", File.WRITE)
fd.store_string(JSON.print(outj, "\t", true))
fd.close()
func handle_drawing_finished(viewport):
print_debug("drawing finished")
func _process(delta):
if surface == draw_data_list[helper].size():
if exit_delay > 0:
exit_delay -= delta
print(exit_delay)
else:
next_helper()
elif shape == draw_data_list[helper][surface].size():
next_surface()
else:
$gen_maps/ProgressBar.value = 100.0 * shape / draw_data_list[helper][surface].size()
print("value ", $gen_maps/ProgressBar.value)
if draw_delay > 0:
draw_delay -= delta
else:
dnatool.save_viewport($gen, maps, helpers_prefix[helper] + draw_data_list[helper][surface][shape].name, draw_data_list[helper][surface][shape].rect, $gen/drawable.normals)
# save_viewport($gen, maps, helpers_prefix[helper] + draw_data_list[helper][surface][shape].name, draw_data_list[helper][surface][shape].rect, $gen/drawable.normals)
if $gen/drawable.normals:
shape += 1
draw_delay = 1.0
print("shape ", shape)
if shape < draw_data_list[helper][surface].size():
setup_draw()