Implement shape keys, vertex modifier support. Fix related bugs.
This commit is contained in:
parent
12940dad1d
commit
0192db3f67
8 changed files with 164 additions and 27 deletions
|
@ -234,6 +234,7 @@ proc initShader*(self: Shader, engine: MyouEngine, material: Material,
|
||||||
fragment_lines.insert(material.scene.get_lighting_code_defines.join("\n"), def_index)
|
fragment_lines.insert(material.scene.get_lighting_code_defines.join("\n"), def_index)
|
||||||
if material.fragment == "":
|
if material.fragment == "":
|
||||||
fragment_lines.add "void main(){}"
|
fragment_lines.add "void main(){}"
|
||||||
|
var modifier_ubos: seq[UBO]
|
||||||
var vs: string
|
var vs: string
|
||||||
if self.material.vertex != "":
|
if self.material.vertex != "":
|
||||||
vs = self.material.vertex
|
vs = self.material.vertex
|
||||||
|
@ -253,12 +254,14 @@ proc initShader*(self: Shader, engine: MyouEngine, material: Material,
|
||||||
var modifiers_uniforms, modifiers_bodies, modifiers_post_bodies: seq[string]
|
var modifiers_uniforms, modifiers_bodies, modifiers_post_bodies: seq[string]
|
||||||
var required_extensions: Table[string, bool]
|
var required_extensions: Table[string, bool]
|
||||||
for m in modifiers:
|
for m in modifiers:
|
||||||
let (uniform_lines, body_lines, post_transform_lines, extensions) = m.get_code(self.material.varyings)
|
let code_lines = m.get_code(self.material.varyings)
|
||||||
modifiers_uniforms &= uniform_lines
|
modifiers_uniforms &= code_lines.uniform_lines
|
||||||
modifiers_bodies &= body_lines
|
modifiers_bodies &= code_lines.body_lines
|
||||||
modifiers_post_bodies &= post_transform_lines
|
modifiers_post_bodies &= code_lines.post_transform_lines
|
||||||
for e in extensions:
|
for e in code_lines.extensions:
|
||||||
required_extensions[e] = true
|
required_extensions[e] = true
|
||||||
|
if m.ubo != nil:
|
||||||
|
modifier_ubos.add m.ubo
|
||||||
var extension_lines: seq[string]
|
var extension_lines: seq[string]
|
||||||
for e in keys(required_extensions):
|
for e in keys(required_extensions):
|
||||||
extension_lines.add(&"#extension {e} : require")
|
extension_lines.add(&"#extension {e} : require")
|
||||||
|
@ -480,7 +483,7 @@ proc initShader*(self: Shader, engine: MyouEngine, material: Material,
|
||||||
|
|
||||||
var ubo_names = newSeqOfCap[string](material.ubos.len)
|
var ubo_names = newSeqOfCap[string](material.ubos.len)
|
||||||
self.ubos.setLen 0
|
self.ubos.setLen 0
|
||||||
for ubo in material.ubos:
|
for ubo in material.ubos & modifier_ubos:
|
||||||
let idx = ubo.get_index prog
|
let idx = ubo.get_index prog
|
||||||
if idx == GL_INVALID_INDEX:
|
if idx == GL_INVALID_INDEX:
|
||||||
if ubo.byte_storage.len != 0:
|
if ubo.byte_storage.len != 0:
|
||||||
|
|
|
@ -261,6 +261,10 @@ proc draw_mesh*(self: RenderManager, mesh: Mesh, mesh2world: Mat4, cam_data: Ren
|
||||||
self.set_flip_normals mesh.flip
|
self.set_flip_normals mesh.flip
|
||||||
let data {.cursor.} = amesh.data
|
let data {.cursor.} = amesh.data
|
||||||
|
|
||||||
|
for vmod in mesh.vertex_modifiers:
|
||||||
|
if vmod.update.nonNil:
|
||||||
|
vmod.update(mesh)
|
||||||
|
|
||||||
# unbindAllTextures()
|
# unbindAllTextures()
|
||||||
|
|
||||||
# Main routine for each submesh
|
# Main routine for each submesh
|
||||||
|
|
|
@ -190,6 +190,7 @@ template is_valid*(block_index: GLuint): bool =
|
||||||
block_index != GL_INVALID_INDEX
|
block_index != GL_INVALID_INDEX
|
||||||
|
|
||||||
proc destroy*(self: UBO) =
|
proc destroy*(self: UBO) =
|
||||||
|
if self != nil:
|
||||||
self.unbind()
|
self.unbind()
|
||||||
self.byte_storage = nil
|
self.byte_storage = nil
|
||||||
self.buffer = 0.GPUBuffer
|
self.buffer = 0.GPUBuffer
|
||||||
|
|
|
@ -348,6 +348,7 @@ proc cstr*(n: FNode): cstring =
|
||||||
template str*(n: FNode): string = $(n.cstr)
|
template str*(n: FNode): string = $(n.cstr)
|
||||||
|
|
||||||
template valid*(n: FNode): bool = n.p != nil
|
template valid*(n: FNode): bool = n.p != nil
|
||||||
|
template nonNil*(n: FNode): bool = n.p != nil
|
||||||
template isNil*(n: FNode): bool = n.p == nil
|
template isNil*(n: FNode): bool = n.p == nil
|
||||||
|
|
||||||
proc strip2*(s: string): string {.inline.} =
|
proc strip2*(s: string): string {.inline.} =
|
||||||
|
|
|
@ -44,6 +44,7 @@ import ../attributes
|
||||||
import ./blend_format
|
import ./blend_format
|
||||||
import ../util
|
import ../util
|
||||||
import ../objects/mesh
|
import ../objects/mesh
|
||||||
|
import ../modifiers/shape_keys
|
||||||
|
|
||||||
template vec2(p: ptr array[16, float32]): Vec2 = cast[ptr Vec2](p)[]
|
template vec2(p: ptr array[16, float32]): Vec2 = cast[ptr Vec2](p)[]
|
||||||
template vec3(p: ptr array[16, float32]): Vec3 = cast[ptr Vec3](p)[]
|
template vec3(p: ptr array[16, float32]): Vec3 = cast[ptr Vec3](p)[]
|
||||||
|
@ -64,6 +65,18 @@ type AttrData = object
|
||||||
count: int8
|
count: int8
|
||||||
buffer: ArrRef[int8]
|
buffer: ArrRef[int8]
|
||||||
|
|
||||||
|
|
||||||
|
proc pack_normal(v: Vec3): float32 =
|
||||||
|
let x = clamp(int32(v.x * 127), -127, 127)
|
||||||
|
let y = clamp(int32((v.y + 1.0) * 127.5), 0, 255)
|
||||||
|
let z = clamp(int32((v.z + 1.0) * 127.5), 0, 255)
|
||||||
|
let i = 0x3f800000'i32 or
|
||||||
|
((x and 0x80) shl 24) or
|
||||||
|
((abs(x)) shl 16) or
|
||||||
|
((y) shl 8) or
|
||||||
|
(z)
|
||||||
|
let f = cast[float32](i)
|
||||||
|
return f - (if f >= 0.0: 1.0 else: -1.0)
|
||||||
proc equals[T](a: ArrRef[T], s:seq[T], offset: int): bool =
|
proc equals[T](a: ArrRef[T], s:seq[T], offset: int): bool =
|
||||||
for i, v in a:
|
for i, v in a:
|
||||||
if a[i] != s[offset+i]: return false
|
if a[i] != s[offset+i]: return false
|
||||||
|
@ -165,14 +178,14 @@ proc interleave_attributes(attributes: someArr[AttrData], indices: someArr[int32
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc calculate_normals(vertices: ArrRef[Vec3], indices, loop_sizes: ArrRef[int32]): seq[Vec3] =
|
proc calculate_normals(vertices: ArrRef[Vec3] | ArrRef[Vec4], indices, loop_sizes: ArrRef[int32]): seq[Vec3] =
|
||||||
result.setLen vertices.len
|
result.setLen vertices.len
|
||||||
var pos = 0
|
var pos = 0
|
||||||
var first, prev = vec3()
|
var first, prev = vec3()
|
||||||
for ls in loop_sizes:
|
for ls in loop_sizes:
|
||||||
var n_sum = vec3()
|
var n_sum = vec3()
|
||||||
for i in 0 ..< ls:
|
for i in 0 ..< ls:
|
||||||
var v = vertices[indices[i+pos]]
|
var v = vertices[indices[i+pos]].xyz
|
||||||
case i:
|
case i:
|
||||||
of 0:
|
of 0:
|
||||||
first = v
|
first = v
|
||||||
|
@ -196,11 +209,13 @@ proc loadMeshImpl*(self: BlendLoader, obn, mesh: FNode, ob: GameObject,
|
||||||
var vertices = newArrRef[Vec3](mesh.totvert.i32[0])
|
var vertices = newArrRef[Vec3](mesh.totvert.i32[0])
|
||||||
var kb = mesh.key.`block`.first
|
var kb = mesh.key.`block`.first
|
||||||
if kb.isNil:
|
if kb.isNil:
|
||||||
|
# TODO: delay this until after position attribute has been read?
|
||||||
if mesh.mvert.valid:
|
if mesh.mvert.valid:
|
||||||
for i,co in mesh.mvert[0 ..< vertices.len].co:
|
for i,co in mesh.mvert[0 ..< vertices.len].co:
|
||||||
vertices[i] = co.f32.vec3
|
vertices[i] = co.f32.vec3
|
||||||
else:
|
else:
|
||||||
# Use basis as source of "original" vertices
|
# Use basis as source of "original" vertices
|
||||||
|
# TODO: delay this until after position attribute has been read?
|
||||||
let data = kb.data.get_array(vertices.len, Vec3)
|
let data = kb.data.get_array(vertices.len, Vec3)
|
||||||
for i in 0 ..< vertices.len:
|
for i in 0 ..< vertices.len:
|
||||||
vertices[i] = data[i]
|
vertices[i] = data[i]
|
||||||
|
@ -303,17 +318,18 @@ proc loadMeshImpl*(self: BlendLoader, obn, mesh: FNode, ob: GameObject,
|
||||||
of ".corner_edge":
|
of ".corner_edge":
|
||||||
discard
|
discard
|
||||||
else:
|
else:
|
||||||
echo "Unknown int32 loop attribute, name ",layer.name.str
|
echo "Unknown int32 loop attribute, name ", repr(layer.name.str)
|
||||||
of 50: # bool
|
of 50: # bool
|
||||||
case layer.name.str.split(".")[1]:
|
|
||||||
of "vs":
|
|
||||||
# UV map of all zeros or all ones
|
|
||||||
# TODO: use static value instead of ignoring
|
|
||||||
discard
|
discard
|
||||||
|
# case layer.name.str.split(".")[1]:
|
||||||
|
# of "vs", "es":
|
||||||
|
# # UV map of all zeros or all ones
|
||||||
|
# # TODO: use static value instead of ignoring
|
||||||
|
# discard
|
||||||
|
# else:
|
||||||
|
# echo "Unknown bool loop attribute, name ", repr(layer.name.str)
|
||||||
else:
|
else:
|
||||||
echo "Unknown bool loop attribute, name ",layer.name.str
|
echo "Unknown loop attribute type ", layer["type"].i32[0],", name ", repr(layer.name.str)
|
||||||
else:
|
|
||||||
echo "Unknown loop attribute type ", layer["type"].i32[0],", name ",layer.name.str
|
|
||||||
# dump data
|
# dump data
|
||||||
for layer in mesh.vdata.arr_len(layers, totlayer):
|
for layer in mesh.vdata.arr_len(layers, totlayer):
|
||||||
# echo layer["type"].i32[0]," v ",layer.name.str
|
# echo layer["type"].i32[0]," v ",layer.name.str
|
||||||
|
@ -377,7 +393,7 @@ proc loadMeshImpl*(self: BlendLoader, obn, mesh: FNode, ob: GameObject,
|
||||||
used_materials[m] = true
|
used_materials[m] = true
|
||||||
materials[i] = m.int16
|
materials[i] = m.int16
|
||||||
else:
|
else:
|
||||||
echo "Unknown int32 loop attribute, name ",layer.name.str
|
echo "Unknown int32 loop attribute, name ", repr(layer.name.str)
|
||||||
of 50: # bool
|
of 50: # bool
|
||||||
case layer.name.str:
|
case layer.name.str:
|
||||||
of ".select_poly":
|
of ".select_poly":
|
||||||
|
@ -416,7 +432,20 @@ proc loadMeshImpl*(self: BlendLoader, obn, mesh: FNode, ob: GameObject,
|
||||||
# for dv in mesh.dvert[0 ..< vertices.len]:
|
# for dv in mesh.dvert[0 ..< vertices.len]:
|
||||||
# dump dv
|
# dump dv
|
||||||
|
|
||||||
# TODO: shape keys
|
# shape keys
|
||||||
|
var shape_keys: OrderedTable[string, float32]
|
||||||
|
var shape_key_arrays: seq[ArrRef[Vec4]]
|
||||||
|
kb = mesh.key.`block`.first
|
||||||
|
if kb.valid:
|
||||||
|
kb = kb.next
|
||||||
|
while kb.valid:
|
||||||
|
let data = kb.data.get_array(vertices.len, Vec3)
|
||||||
|
var out_array = newArrRef[Vec4](vertices.len)
|
||||||
|
for i in 0 ..< vertices.len:
|
||||||
|
out_array[i] = vec4(data[i], 0.0)
|
||||||
|
shape_key_arrays.add out_array
|
||||||
|
shape_keys[kb.name.str] = kb.curval.f32[0]
|
||||||
|
kb = kb.next
|
||||||
|
|
||||||
# calculate normals
|
# calculate normals
|
||||||
let normals = calculate_normals(vertices, indices, loop_sizes)
|
let normals = calculate_normals(vertices, indices, loop_sizes)
|
||||||
|
@ -424,6 +453,24 @@ proc loadMeshImpl*(self: BlendLoader, obn, mesh: FNode, ob: GameObject,
|
||||||
for i,n in normals:
|
for i,n in normals:
|
||||||
normals_byte[i] = [int8(n.x*127), int8(n.y*127), int8(n.z*127), 0]
|
normals_byte[i] = [int8(n.x*127), int8(n.y*127), int8(n.z*127), 0]
|
||||||
|
|
||||||
|
# calculate normals of shape keys
|
||||||
|
for shape in shape_key_arrays:
|
||||||
|
let normals = calculate_normals(shape, indices, loop_sizes)
|
||||||
|
for i,v in shape.mpairs:
|
||||||
|
v.w = pack_normal(normals[i])
|
||||||
|
|
||||||
|
# make relative and add shape keys attributes
|
||||||
|
for j,shape in shape_key_arrays:
|
||||||
|
for i,v in shape.mpairs:
|
||||||
|
v -= vec4(vertices[i], 0.0)
|
||||||
|
|
||||||
|
attributes.add AttrData(
|
||||||
|
name: "shape" & $j, domain: DVertex,
|
||||||
|
dtype: Float, count: 4,
|
||||||
|
buffer: shape.to(int8),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# add empty tangents (to be calculated later)
|
# add empty tangents (to be calculated later)
|
||||||
if required_tangents.len != 0:
|
if required_tangents.len != 0:
|
||||||
var tg_attributes: seq[AttrData]
|
var tg_attributes: seq[AttrData]
|
||||||
|
@ -542,6 +589,9 @@ proc loadMeshImpl*(self: BlendLoader, obn, mesh: FNode, ob: GameObject,
|
||||||
final_ia.add newArrRef(ia_per_material[i])
|
final_ia.add newArrRef(ia_per_material[i])
|
||||||
index_types.add UInt
|
index_types.add UInt
|
||||||
|
|
||||||
|
ob.get_mesh.shape_keys = shape_keys
|
||||||
|
if shape_keys.len != 0:
|
||||||
|
ob.get_mesh.add_modifier(ob.engine.newShapeKeyModifier(shape_keys.len))
|
||||||
ob.get_mesh.generate_tangents = required_tangents.len != 0
|
ob.get_mesh.generate_tangents = required_tangents.len != 0
|
||||||
ob.get_mesh.load_from_va_ia(final_va, final_ia, layout, index_types)
|
ob.get_mesh.load_from_va_ia(final_va, final_ia, layout, index_types)
|
||||||
# ob.get_mesh.debug_print_vertices(0,10)
|
# ob.get_mesh.debug_print_vertices(0,10)
|
||||||
|
|
74
src/modifiers/shape_keys.nim
Normal file
74
src/modifiers/shape_keys.nim
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
|
||||||
|
import ../types
|
||||||
|
import ../graphics/ubo
|
||||||
|
import ../util
|
||||||
|
import std/strformat
|
||||||
|
import std/strutils
|
||||||
|
import std/tables
|
||||||
|
|
||||||
|
proc newShapeKeyModifier*(engine: MyouEngine, count: int): VertexModifier =
|
||||||
|
|
||||||
|
let float_count = count.align4
|
||||||
|
|
||||||
|
result.get_code = proc(v: seq[Varying]): VertexModifierCodeLines =
|
||||||
|
result.uniform_lines = @[
|
||||||
|
"layout(std140) uniform ShapeKeys {",
|
||||||
|
&" vec4 shape_values[{float_count div 4}];",
|
||||||
|
"};",
|
||||||
|
# 256/255 = 1.0039216 = 2.0078433 / 2.0
|
||||||
|
# which helps us remap 0 to -1 and 255 to 1.
|
||||||
|
"""
|
||||||
|
vec3 unpack(float f){
|
||||||
|
float x = f;
|
||||||
|
float y = fract(abs(x)*128.0);
|
||||||
|
float z = fract(y*256.0);
|
||||||
|
y = y * 2.0078433 - 1.0;
|
||||||
|
z = z * 2.0078433 - 1.0;
|
||||||
|
return vec3(x,y,z);
|
||||||
|
}
|
||||||
|
""".dedent,
|
||||||
|
]
|
||||||
|
var body = @[
|
||||||
|
# Equivalent to /= 127.0, and roughly to normalize byte normals
|
||||||
|
"normal *= 0.007874;",
|
||||||
|
"float relf = 0.0;",
|
||||||
|
"vec3 co0 = co.xyz, orig_n = normal;",
|
||||||
|
]
|
||||||
|
for i in 0 ..< count:
|
||||||
|
let i4 = i div 4
|
||||||
|
body &= @[
|
||||||
|
&"co += vec4(shape{i}.xyz, 0.0) * shape_values[{i4}][{i and 3}];",
|
||||||
|
&"float shape{i}use = min(1.0, length(shape{i}.xyz) * 1000.0);",
|
||||||
|
&"relf += abs(shape_values[{i4}][{i and 3}]) * shape{i}use;",
|
||||||
|
]
|
||||||
|
# Interpolating normals instead of re-calculating them is wrong
|
||||||
|
# But it's fast, completely unnoticeable in most cases,
|
||||||
|
# and better than just not changing them (as many engines do)
|
||||||
|
# body &= "normal *= clamp(1.0 - relf, 0.0, 1.0);"
|
||||||
|
body &= "vec3 snormal = normal * 0.001;"
|
||||||
|
body &= "#define DEBUG1 0.001"
|
||||||
|
body &= "#define DEBUG2 0.001"
|
||||||
|
body &= "#define DEBUG3 0.001"
|
||||||
|
for i in 0 ..< count:
|
||||||
|
let i4 = i div 4
|
||||||
|
# I substract 0.001 to avoid using noise near 0 which makes normals
|
||||||
|
# change in places where a shape key is not affecting
|
||||||
|
body &= &"""
|
||||||
|
snormal += unpack(shape{i}.w) * max(0.0, shape_values[{i4}][{i and 3}] * shape{i}use)
|
||||||
|
;"""
|
||||||
|
body &= "normal = mix(normal, snormal, clamp(relf, 0.0, 1.0));"
|
||||||
|
|
||||||
|
result.body_lines = body
|
||||||
|
|
||||||
|
let ubo = engine.renderer.newUBO("ShapeKeys", float32, float_count)
|
||||||
|
result.ubo = ubo
|
||||||
|
|
||||||
|
result.update = proc(m: Mesh) =
|
||||||
|
var data = ubo.storage(float32)
|
||||||
|
var i = 0
|
||||||
|
for k,v in m.shape_keys:
|
||||||
|
data[i] = v
|
||||||
|
i.inc
|
||||||
|
ubo.unbind()
|
||||||
|
ubo.update()
|
||||||
|
|
|
@ -578,9 +578,7 @@ proc destroy*(self: GameObject, recursive: bool = true) =
|
||||||
var child = child
|
var child = child
|
||||||
child.destroy(true)
|
child.destroy(true)
|
||||||
self.engine.objects.del(self.name)
|
self.engine.objects.del(self.name)
|
||||||
self.object_render_ubo.unbind()
|
|
||||||
self.object_render_ubo.destroy()
|
self.object_render_ubo.destroy()
|
||||||
self.object_ubo.unbind()
|
|
||||||
self.object_ubo.destroy()
|
self.object_ubo.destroy()
|
||||||
|
|
||||||
proc convert_bone_child_to_bone_parent*(self: GameObject) =
|
proc convert_bone_child_to_bone_parent*(self: GameObject) =
|
||||||
|
|
|
@ -207,6 +207,7 @@ type
|
||||||
layout*: AttributeList
|
layout*: AttributeList
|
||||||
vertex_modifiers*: seq[VertexModifier]
|
vertex_modifiers*: seq[VertexModifier]
|
||||||
mesh_id*: int8
|
mesh_id*: int8
|
||||||
|
shape_keys*: OrderedTable[string, float32]
|
||||||
bone_index_maps*: seq[Table[int32,int32]]
|
bone_index_maps*: seq[Table[int32,int32]]
|
||||||
sort_sign*: SortSign
|
sort_sign*: SortSign
|
||||||
# related_textures*: seq[unknown]
|
# related_textures*: seq[unknown]
|
||||||
|
@ -502,7 +503,6 @@ type
|
||||||
UBO* = ref object
|
UBO* = ref object
|
||||||
renderer* {.cursor.}: RenderManager
|
renderer* {.cursor.}: RenderManager
|
||||||
name*: string
|
name*: string
|
||||||
size32*: int
|
|
||||||
byte_storage*: ArrRef[byte]
|
byte_storage*: ArrRef[byte]
|
||||||
buffer*: GPUBuffer
|
buffer*: GPUBuffer
|
||||||
binding_point*: int32
|
binding_point*: int32
|
||||||
|
@ -889,9 +889,15 @@ type
|
||||||
rect*: (float32,float32,float32,float32)
|
rect*: (float32,float32,float32,float32)
|
||||||
rect_pix*: (int32,int32,int32,int32)
|
rect_pix*: (int32,int32,int32,int32)
|
||||||
|
|
||||||
|
VertexModifierCodeLines* = object
|
||||||
|
uniform_lines*, body_lines*, post_transform_lines*: seq[string]
|
||||||
|
extensions*: seq[string]
|
||||||
|
|
||||||
VertexModifier* = object of RootObj
|
VertexModifier* = object of RootObj
|
||||||
get_code*: proc(v: seq[Varying]): (seq[string], seq[string], seq[string], seq[string])
|
get_code*: proc(v: seq[Varying]): VertexModifierCodeLines
|
||||||
prepare_mesh*: proc(m: Mesh)
|
prepare_mesh*: proc(m: Mesh)
|
||||||
|
ubo*: UBO
|
||||||
|
update*: proc(m: Mesh)
|
||||||
|
|
||||||
AnimationStrip* = object
|
AnimationStrip* = object
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue