Improve skinning
This commit is contained in:
parent
9d0e83b3ce
commit
17b7c2d900
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'):
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)));')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
5
blender/arm/proxy.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
|
||||
import bpy
|
||||
|
||||
def make(obj):
|
||||
bpy.ops.object.proxy_make('EXEC_DEFAULT')
|
|
@ -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
|
||||
|
|
|
@ -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_
|
||||
|
|
Loading…
Reference in a new issue