Improve skinning

This commit is contained in:
Lubos Lenco 2017-10-10 09:57:23 +02:00
parent 9d0e83b3ce
commit 17b7c2d900
14 changed files with 107 additions and 58 deletions

View file

@ -29,8 +29,3 @@ mat4 getSkinningMat(const ivec4 bone, const vec4 weight) {
mat3 getSkinningMatVec(const mat4 skinningMat) {
return mat3(skinningMat[0].xyz, skinningMat[1].xyz, skinningMat[2].xyz);
}
// mat4 skinningMat = getSkinningMat();
// mat3 skinningMatVec = getSkinningMatVec(skinningMat);
// sPos = sPos * skinningMat;
// vec3 _normal = normalize(mat3(N) * (nor * skinningMatVec));

View file

@ -2,6 +2,7 @@ package armory.trait;
import iron.math.Vec4;
import iron.system.Input;
import iron.object.Object;
import armory.trait.physics.PhysicsWorld;
import armory.trait.internal.CameraController;
@ -13,13 +14,24 @@ class ThirdPersonController extends CameraController {
static inline var rotationSpeed = 1.0;
public function new() {
var animObject:String;
var idleAction:String;
var runAction:String;
var state = 0; // Idle, run
var arm:Object;
public function new(animObject:String, idle = "idle", run = "run") {
super();
this.animObject = animObject;
this.idleAction = idle;
this.runAction = run;
iron.Scene.active.notifyOnInit(init);
}
function init() {
arm = object.getChild(animObject);
PhysicsWorld.active.notifyOnPreUpdate(preUpdate);
notifyOnUpdate(update);
notifyOnRemove(removed);
@ -48,10 +60,7 @@ class ThirdPersonController extends CameraController {
function update() {
if (!body.ready) return;
if (jump) {
body.applyImpulse(new Vec4(0, 0, 20));
jump = false;
}
if (jump) body.applyImpulse(new Vec4(0, 0, 20));
// Move
dir.set(0, 0, 0);
@ -64,15 +73,22 @@ class ThirdPersonController extends CameraController {
var btvec = body.getLinearVelocity();
body.setLinearVelocity(0.0, 0.0, btvec.z() - 1.0);
var arm = object.getChild("Ballie");
arm.animation.paused = true;
if (moveForward || moveBackward || moveLeft || moveRight) {
if (moveForward || moveBackward || moveLeft || moveRight) {
if (state != 1) {
arm.animation.play(runAction, null, 0.2);
state = 1;
}
arm.animation.paused = false;
dir.mult(-4 * 0.7);
body.activate();
body.setLinearVelocity(dir.x, dir.y, btvec.z() - 1.0);
}
else {
if (state != 0) {
arm.animation.play(idleAction, null, 0.2);
state = 0;
}
}
// Keep vertical
body.setAngularFactor(0, 0, 0);

View file

@ -37,7 +37,7 @@ class CameraController extends Trait {
moveRight = keyboard.down("d");
moveBackward = keyboard.down("s");
moveLeft = keyboard.down("a");
jump = keyboard.down("space");
jump = keyboard.started("space");
});
}
#end

View file

@ -1404,7 +1404,8 @@ class ArmoryExporter:
# Export actions
export_actions = [action]
if adata.nla_tracks != None:
# hasattr - armature modifier may reference non-parent armature object to deform with
if hasattr(adata, 'nla_tracks') and adata.nla_tracks != None:
for track in adata.nla_tracks:
if track.strips == None:
continue
@ -1418,15 +1419,17 @@ class ArmoryExporter:
armatureid = arm.utils.safestr(arm.utils.asset_name(bdata))
ext = '.zip' if self.is_compress(bdata) else ''
o['action_refs'] = []
for a in export_actions:
o['action_refs'].append('action_' + armatureid + '_' + a.name + ext)
for action in export_actions:
aname = arm.utils.safestr(arm.utils.asset_name(action))
o['action_refs'].append('action_' + armatureid + '_' + aname + ext)
orig_action = bobject.animation_data.action
for action in export_actions:
aname = arm.utils.safestr(arm.utils.asset_name(action))
bobject.animation_data.action = action
fp = self.get_meshes_file_path('action_' + armatureid + '_' + action.name, compressed=self.is_compress(bdata))
fp = self.get_meshes_file_path('action_' + armatureid + '_' + aname, compressed=self.is_compress(bdata))
assets.add(fp)
if bdata.arm_data_cached == False or not os.path.exists(fp):
if bdata.arm_cached == False or not os.path.exists(fp):
bones = []
for bone in bdata.bones:
if not bone.parent:
@ -1435,11 +1438,11 @@ class ArmoryExporter:
bones.append(boneo)
# Save action separately
action_obj = {}
action_obj['name'] = action.name
action_obj['name'] = aname
action_obj['objects'] = bones
arm.utils.write_arm(fp, action_obj)
bobject.animation_data.action = orig_action
bdata.arm_data_cached = True
bdata.arm_cached = True
if parento == None:
self.output['objects'].append(o)
@ -1477,7 +1480,7 @@ class ArmoryExporter:
bone_array = armature.data.bones
bone_count = len(bone_array)
max_bones = bpy.data.worlds['Arm'].arm_gpu_skin_max_bones
max_bones = bpy.data.worlds['Arm'].arm_skin_max_bones
if bone_count > max_bones:
log.warn(bobject.name + ' - ' + str(bone_count) + ' bones found, exceeds maximum of ' + str(max_bones) + ' bones defined - raise the value in Camera Data - Armory Render Props - Max Bones')
@ -2491,14 +2494,14 @@ class ArmoryExporter:
assets.add_shader(arm.utils.build_dir() + '/compiled/Shaders/grease_pencil/grease_pencil_shadows.frag.glsl')
assets.add_shader(arm.utils.build_dir() + '/compiled/Shaders/grease_pencil/grease_pencil_shadows.vert.glsl')
if gpRef.arm_data_cached == True and os.path.exists(fp):
if gpRef.arm_cached == True and os.path.exists(fp):
return
gpo = self.post_export_grease_pencil(gpRef)
gp_obj = {}
gp_obj['grease_pencil_datas'] = [gpo]
arm.utils.write_arm(fp, gp_obj)
gpRef.arm_data_cached = True
gpRef.arm_cached = True
def is_compress(self, obj):
return ArmoryExporter.compress_enabled and obj.arm_compress
@ -2601,12 +2604,12 @@ class ArmoryExporter:
# Auto-bones
wrd = bpy.data.worlds['Arm']
if wrd.arm_gpu_skin_max_bones_auto:
if wrd.arm_skin_max_bones_auto:
max_bones = 8
for armature in bpy.data.armatures:
if max_bones < len(armature.bones):
max_bones = len(armature.bones)
wrd.arm_gpu_skin_max_bones = max_bones
wrd.arm_skin_max_bones = max_bones
self.output['objects'] = []
for bo in scene_objects:

View file

@ -205,10 +205,7 @@ def on_scene_update_post(context):
space.node_tree.is_cached = False
def recache(edit_obj):
if edit_obj.type == 'MESH':
edit_obj.data.arm_cached = False
elif edit_obj.type == 'ARMATURE':
edit_obj.data.arm_data_cached = False
edit_obj.data.arm_cached = False
def op_changed(op, obj):
# Recache mesh data

View file

@ -25,6 +25,7 @@ def make(context_id):
con['blend_operation'] = 'add'
con['depth_write'] = False
con_mesh = mat_state.data.add_context(con)
mat_state.con_mesh = con_mesh
rpdat = arm.utils.get_rp()
rid = rpdat.rp_renderer
@ -246,7 +247,7 @@ def write_norpos(con_mesh, vert, declare=False, write_nor=True):
make_skin.skin_pos(vert)
if write_nor:
if is_bone:
vert.write(prep + 'wnormal = normalize(N * (nor + 2.0 * cross(skinA.xyz, cross(skinA.xyz, nor) + skinA.w * nor)));')
make_skin.skin_nor(vert, prep)
else:
vert.write(prep + 'wnormal = normalize(N * nor);')
if con_mesh.is_elem('off'):

View file

@ -123,8 +123,15 @@ def make(context_id, rpasses):
# TODO: interleaved buffer has to match vertex structure of mesh context
if not bpy.data.worlds['Arm'].arm_deinterleaved_buffers:
con_shadowmap.add_elem('nor', 3)
if mat_state.material.export_uvs:
con_shadowmap.add_elem('tex', 2)
if mat_state.con_mesh != None:
if mat_state.con_mesh.is_elem('tex'):
con_shadowmap.add_elem('tex', 2)
if mat_state.con_mesh.is_elem('tex1'):
con_shadowmap.add_elem('tex1', 2)
if mat_state.con_mesh.is_elem('col'):
con_shadowmap.add_elem('col', 3)
if mat_state.con_mesh.is_elem('tang'):
con_shadowmap.add_elem('tang', 4)
# TODO: pass vbuf with proper struct
if gapi.startswith('direct3d') and bpy.data.worlds['Arm'].arm_deinterleaved_buffers == False:

View file

@ -1,10 +1,28 @@
import bpy
def skin_pos(vert):
vert.add_include('../../Shaders/compiled.glsl')
vert.add_include('../../Shaders/std/skinning.glsl')
vert.add_uniform('vec4 skinBones[skinMaxBones * 2]', link='_skinBones', included=True)
vert.write('vec4 skinA;')
vert.write('vec4 skinB;')
vert.write('getSkinningDualQuat(ivec4(bone), weight, skinA, skinB);')
vert.write('spos.xyz += 2.0 * cross(skinA.xyz, cross(skinA.xyz, spos.xyz) + skinA.w * spos.xyz); // Rotate')
vert.write('spos.xyz += 2.0 * (skinA.w * skinB.xyz - skinB.w * skinA.xyz + cross(skinA.xyz, skinB.xyz)); // Translate')
if bpy.data.worlds['Arm'].arm_skin == 'GPU (Matrix)':
vert.add_include('../../Shaders/std/skinning_mat.glsl')
vert.add_uniform('vec4 skinBones[skinMaxBones * 3]', link='_skinBones', included=True)
vert.write('mat4 skinningMat = getSkinningMat(ivec4(bone), weight);')
vert.write('spos *= skinningMat;')
else: # Dual Quat
vert.add_include('../../Shaders/std/skinning.glsl')
vert.add_uniform('vec4 skinBones[skinMaxBones * 2]', link='_skinBones', included=True)
vert.write('vec4 skinA;')
vert.write('vec4 skinB;')
vert.write('getSkinningDualQuat(ivec4(bone), weight, skinA, skinB);')
vert.write('spos.xyz += 2.0 * cross(skinA.xyz, cross(skinA.xyz, spos.xyz) + skinA.w * spos.xyz); // Rotate')
vert.write('spos.xyz += 2.0 * (skinA.w * skinB.xyz - skinB.w * skinA.xyz + cross(skinA.xyz, skinB.xyz)); // Translate')
def skin_nor(vert, prep):
if bpy.data.worlds['Arm'].arm_skin == 'GPU (Matrix)':
vert.write('mat3 skinningMatVec = getSkinningMatVec(skinningMat);')
vert.write(prep + 'wnormal = normalize(N * (nor * skinningMatVec));')
else: # Dual Quat
vert.write(prep + 'wnormal = normalize(N * (nor + 2.0 * cross(skinA.xyz, cross(skinA.xyz, nor) + skinA.w * nor)));')

View file

@ -6,3 +6,4 @@ bind_constants = None # Merged with mat_context bind constants
bind_textures = None # Merged with mat_context bind textures
batch = False
texture_grad = False # Sample textures using textureGrad()
con_mesh = None # Mesh context

View file

@ -150,10 +150,10 @@ def init_properties():
bpy.types.MetaBall.arm_compress = bpy.props.BoolProperty(name="Compress", description="Pack data into zip file", default=False)
bpy.types.MetaBall.arm_dynamic_usage = bpy.props.BoolProperty(name="Dynamic Data Usage", description="Metaball data can change at runtime", default=False)
# For grease pencil
bpy.types.GreasePencil.arm_data_cached = bpy.props.BoolProperty(name="GP Cached", description="No need to reexport grease pencil data", default=False)
bpy.types.GreasePencil.arm_cached = bpy.props.BoolProperty(name="GP Cached", description="No need to reexport grease pencil data", default=False)
bpy.types.GreasePencil.arm_compress = bpy.props.BoolProperty(name="Compress", description="Pack data into zip file", default=True)
# For armature
bpy.types.Armature.arm_data_cached = bpy.props.BoolProperty(name="Armature Cached", description="No need to reexport armature data", default=False)
bpy.types.Armature.arm_cached = bpy.props.BoolProperty(name="Armature Cached", description="No need to reexport armature data", default=False)
bpy.types.Armature.arm_compress = bpy.props.BoolProperty(name="Compress", description="Pack data into zip file", default=False)
# For camera
bpy.types.Camera.arm_frustum_culling = bpy.props.BoolProperty(name="Frustum Culling", description="Perform frustum culling for this camera", default=True)
@ -259,9 +259,13 @@ def init_properties():
bpy.types.World.arm_fisheye = bpy.props.BoolProperty(name="Fish Eye", default=False, update=assets.invalidate_shader_cache)
bpy.types.World.arm_vignette = bpy.props.BoolProperty(name="Vignette", default=False, update=assets.invalidate_shader_cache)
# Skin
bpy.types.World.arm_gpu_skin = bpy.props.BoolProperty(name="GPU Skinning", description="Calculate skinning on GPU", default=True, update=assets.invalidate_shader_cache)
bpy.types.World.arm_gpu_skin_max_bones_auto = bpy.props.BoolProperty(name="Auto Bones", description="Calculate amount of maximum bones based on armatures", default=True, update=assets.invalidate_compiled_data)
bpy.types.World.arm_gpu_skin_max_bones = bpy.props.IntProperty(name="Max Bones", default=50, min=1, max=3000, update=assets.invalidate_shader_cache)
bpy.types.World.arm_skin = EnumProperty(
items=[('GPU (Dual-Quat)', 'GPU (Dual-Quat)', 'GPU (Dual-Quat)'),
('GPU (Matrix)', 'GPU (Matrix)', 'GPU (Matrix)'),
('CPU', 'CPU', 'CPU')],
name='Skinning', description='Skinning method', default='GPU (Dual-Quat)', update=assets.invalidate_shader_cache)
bpy.types.World.arm_skin_max_bones_auto = bpy.props.BoolProperty(name="Auto Bones", description="Calculate amount of maximum bones based on armatures", default=True, update=assets.invalidate_compiled_data)
bpy.types.World.arm_skin_max_bones = bpy.props.IntProperty(name="Max Bones", default=50, min=1, max=3000, update=assets.invalidate_shader_cache)
# Material override flags
bpy.types.World.arm_culling = bpy.props.BoolProperty(name="Culling", default=True)
bpy.types.World.arm_two_sided_area_lamp = bpy.props.BoolProperty(name="Two-Sided Area Lamps", description="Emit light from both faces of area lamp", default=False, update=assets.invalidate_shader_cache)

View file

@ -214,7 +214,7 @@ class InvalidateGPCacheButton(bpy.types.Operator):
def execute(self, context):
if context.scene.grease_pencil != None:
context.scene.grease_pencil.arm_data_cached = False
context.scene.grease_pencil.arm_cached = False
return{'FINISHED'}
class MaterialPropsPanel(bpy.types.Panel):
@ -983,11 +983,11 @@ class ArmRenderPropsPanel(bpy.types.Panel):
layout.prop(wrd, 'arm_tonemap')
layout.prop(wrd, 'arm_culling')
layout.prop(wrd, 'arm_two_sided_area_lamp')
layout.prop(wrd, 'arm_gpu_skin')
if wrd.arm_gpu_skin:
layout.prop(wrd, 'arm_gpu_skin_max_bones_auto')
if not wrd.arm_gpu_skin_max_bones_auto:
layout.prop(wrd, 'arm_gpu_skin_max_bones')
layout.prop(wrd, 'arm_skin')
if wrd.arm_skin.startswith('GPU'):
layout.prop(wrd, 'arm_skin_max_bones_auto')
if not wrd.arm_skin_max_bones_auto:
layout.prop(wrd, 'arm_skin_max_bones')
layout.label('PCSS')
layout.prop(wrd, 'arm_pcss_rings')

5
blender/arm/proxy.py Normal file
View file

@ -0,0 +1,5 @@
import bpy
def make(obj):
bpy.ops.object.proxy_make('EXEC_DEFAULT')

View file

@ -381,7 +381,7 @@ def is_bone_animation_enabled(bobject):
return False
def export_bone_data(bobject):
return bobject.find_armature() and is_bone_animation_enabled(bobject) and bpy.data.worlds['Arm'].arm_gpu_skin == True
return bobject.find_armature() and is_bone_animation_enabled(bobject) and bpy.data.worlds['Arm'].arm_skin.startswith('GPU')
def register():
global krom_found

View file

@ -193,8 +193,10 @@ project.addSources('Sources');
if wrd.arm_stream_scene:
assets.add_khafile_def('arm_stream')
if wrd.arm_gpu_skin == False:
assets.add_khafile_def('arm_cpu_skin')
if wrd.arm_skin == 'CPU':
assets.add_khafile_def('arm_skin_cpu')
elif wrd.arm_skin == 'GPU (Matrix)':
assets.add_khafile_def('arm_skin_mat')
for d in assets.khafile_defs:
f.write("project.addDefine('" + d + "');\n")
@ -246,7 +248,7 @@ class Main {
f.write("""
public static function main() {
iron.object.BoneAnimation.skinMaxBones = """ + str(wrd.arm_gpu_skin_max_bones) + """;
iron.object.BoneAnimation.skinMaxBones = """ + str(wrd.arm_skin_max_bones) + """;
state = 1;
#if (js && arm_bullet) state++; loadLib("ammo.js"); #end
#if (js && arm_navigation) state++; loadLib("recast.js"); #end
@ -439,9 +441,9 @@ const float voxelgiRange = """ + str(round(wrd.arm_voxelgi_range * 100) / 100) +
""")
# Skinning
if wrd.arm_gpu_skin:
if wrd.arm_skin.startswith('GPU'):
f.write(
"""const int skinMaxBones = """ + str(wrd.arm_gpu_skin_max_bones) + """;
"""const int skinMaxBones = """ + str(wrd.arm_skin_max_bones) + """;
""")
f.write("""#endif // _COMPILED_GLSL_