Dual quaternion skinning, mirrors.

This commit is contained in:
Lubos Lenco 2016-08-29 09:56:34 +02:00
parent 8e3c2e0c74
commit f3554b4de8
56 changed files with 1508 additions and 1018 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,3 +0,0 @@
package armory;
typedef Root = iron.Root;

View file

@ -1,53 +1,3 @@
package armory; package armory;
import iron.App; typedef Scene = iron.Scene;
import iron.Root;
import iron.object.Object;
import iron.object.CameraObject;
import iron.data.SceneFormat;
import iron.data.Data;
import armory.trait.internal.PhysicsWorld;
class Scene {
var cam:CameraObject;
public static var physics:PhysicsWorld;
public function new(sceneName:String) {
// Startup scene
var sceneObject = Root.addScene(sceneName);
if (Root.cameras.length == 0) {
trace('No camera found for scene "$sceneName"!');
return;
}
cam = Root.cameras[0];
// Attach world to camera for now
var raw:TSceneFormat = Data.getSceneRaw(sceneName);
cam.world = Data.getWorld(sceneName, raw.world_ref);
// Physics
physics = new PhysicsWorld(raw.gravity);
sceneObject.addTrait(physics);
App.notifyOnRender(render);
// Experimental scene reloading
// App.notifyOnUpdate(function() {
// if (iron.sys.Input.released) {
// // kha.Assets.loadBlob(sceneName + '_arm', function(b:kha.Blob) {
// iron.App.reset();
// iron.data.Data.clearSceneData();
// new iron.App(armory.Root);
// // });
// }
// });
}
function render(g:kha.graphics4.Graphics) {
cam.renderFrame(g, Root.root, Root.lamps);
}
}

View file

@ -17,7 +17,7 @@ class PickerNode extends Node {
} }
function init() { function init() {
target = iron.Root.root.getChild(property0); target = iron.Scene.active.getChild(property0);
} }
public static function create(_property0:String):PickerNode { public static function create(_property0:String):PickerNode {

View file

@ -128,7 +128,7 @@ class HosekWilkie {
// Extract direction from lamp // Extract direction from lamp
// var mat = iron.data.Data.getMaterial("World_material", "World_material").data; // var mat = iron.data.Data.getMaterial("World_material", "World_material").data;
// var lamp = iron.Root.lamps[0]; // var lamp = iron.Scene.active.lamps[0];
// var ltr = lamp.transform; // var ltr = lamp.transform;
// var lf = ltr.matrix.look2(); // var lf = ltr.matrix.look2();
// lamp.data.data.strength = 3.3 - Math.abs(ltr.absy()) / 45; // lamp.data.data.strength = 3.3 - Math.abs(ltr.absy()) / 45;

View file

@ -1,7 +1,6 @@
package armory.trait; package armory.trait;
import iron.Trait; import iron.Trait;
import iron.Root;
import iron.sys.Input; import iron.sys.Input;
import iron.object.CameraObject; import iron.object.CameraObject;
import iron.math.Vec4; import iron.math.Vec4;
@ -24,7 +23,7 @@ class ArcBallCamera extends Trait {
} }
function init() { function init() {
camera = Root.cameras[0]; camera = cast(object, CameraObject);
var r = camera.transform.rot; var r = camera.transform.rot;
var q = new Quat(r.x, r.y, r.z, r.w); var q = new Quat(r.x, r.y, r.z, r.w);

View file

@ -36,7 +36,7 @@ class FirstPersonController extends Trait {
function init() { function init() {
transform = object.transform; transform = object.transform;
body = object.getTrait(RigidBody); body = object.getTrait(RigidBody);
camera = iron.Root.cameras[0]; camera = cast(object, CameraObject);
} }
function onDown(key: kha.Key, char: String) { function onDown(key: kha.Key, char: String) {

View file

@ -0,0 +1,23 @@
package armory.trait;
import iron.Trait;
import iron.object.MeshObject;
class MirrorTexture extends Trait {
var cameraName:String;
public function new(cameraName:String) {
super();
this.cameraName = cameraName;
notifyOnInit(init);
}
function init() {
var image = iron.Scene.active.getCamera(cameraName).data.mirror;
var o = cast(object, iron.object.MeshObject);
o.materials[0].contexts[0].textures[0] = image; // Override diffuse texture
}
}

View file

@ -31,7 +31,7 @@ class PhysicsDrag extends Trait {
} }
function init() { function init() {
physics = armory.Scene.physics; physics = armory.trait.internal.PhysicsWorld.active;
} }
function update() { function update() {

View file

@ -1,7 +1,6 @@
package armory.trait; package armory.trait;
import iron.Trait; import iron.Trait;
import iron.Root;
class SceneInstance extends Trait { class SceneInstance extends Trait {
@ -9,7 +8,7 @@ class SceneInstance extends Trait {
super(); super();
notifyOnInit(function() { notifyOnInit(function() {
Root.addScene(sceneName, object); iron.Scene.active.addScene(sceneName, object);
}); });
} }
} }

View file

@ -67,12 +67,12 @@ class VehicleBody extends Trait {
} }
function init() { function init() {
physics = armory.Scene.physics; physics = armory.trait.internal.PhysicsWorld.active;
transform = object.transform; transform = object.transform;
camera = iron.Root.cameras[0]; camera = iron.Scene.active.camera;
for (n in wheelNames) { for (n in wheelNames) {
wheels.push(iron.Root.root.getChild(n)); wheels.push(iron.Scene.active.root.getChild(n));
} }
var rightIndex = 0; var rightIndex = 0;

View file

@ -2,7 +2,6 @@ package armory.trait;
import kha.Key; import kha.Key;
import iron.Trait; import iron.Trait;
import iron.Root;
import iron.sys.Input; import iron.sys.Input;
import iron.sys.Time; import iron.sys.Time;
import iron.object.CameraObject; import iron.object.CameraObject;
@ -39,7 +38,7 @@ class WalkNavigation extends Trait {
} }
function init() { function init() {
camera = Root.cameras[0]; camera = cast(object, CameraObject);
} }
function update() { function update() {

View file

@ -11,8 +11,9 @@ class Animation extends Trait {
var speeds:Array<Float>; var speeds:Array<Float>;
var loops:Array<Bool>; var loops:Array<Bool>;
var reflects:Array<Bool>; var reflects:Array<Bool>;
static var maxBones:Int;
public function new(startTrack:String, names:Array<String>, starts:Array<Int>, ends:Array<Int>, speeds:Array<Float>, loops:Array<Bool>, reflects:Array<Bool>) { public function new(startTrack:String, names:Array<String>, starts:Array<Int>, ends:Array<Int>, speeds:Array<Float>, loops:Array<Bool>, reflects:Array<Bool>, _maxBones:Int) {
super(); super();
this.startTrack = startTrack; this.startTrack = startTrack;
@ -22,13 +23,14 @@ class Animation extends Trait {
this.speeds = speeds; this.speeds = speeds;
this.loops = loops; this.loops = loops;
this.reflects = reflects; this.reflects = reflects;
maxBones = _maxBones;
notifyOnAdd(add); notifyOnAdd(add);
notifyOnUpdate(update); notifyOnUpdate(update);
} }
function add() { function add() {
object.setupAnimation(startTrack, names, starts, ends, speeds, loops, reflects); object.setupAnimation(startTrack, names, starts, ends, speeds, loops, reflects, maxBones);
} }
function update() { function update() {

View file

@ -77,7 +77,7 @@ class Console extends Trait {
} }
ui.separator(); ui.separator();
if (ui.node(Id.node(), "Inspector", 0, false)) { if (ui.node(Id.node(), "Inspector", 0, false)) {
for (o in iron.Root.meshes) { for (o in iron.Scene.active.meshes) {
ui.text(o.name + " (" + Std.int(o.transform.absx() * 100) / 100 + ", " + Std.int(o.transform.absy() * 100) / 100 + ", " + Std.int(o.transform.absz() * 100) / 100 + ")"); ui.text(o.name + " (" + Std.int(o.transform.absx() * 100) / 100 + ", " + Std.int(o.transform.absy() * 100) / 100 + ", " + Std.int(o.transform.absz() * 100) / 100 + ")");
} }
} }

View file

@ -47,7 +47,7 @@ class JSScriptAPI {
class JSScriptAPI { class JSScriptAPI {
public static var App = iron.App; public static var App = iron.App;
public static var Root = iron.Root; public static var Scene = iron.Scene;
public static var Time = iron.sys.Time; public static var Time = iron.sys.Time;
public static var Object = iron.object.Object; public static var Object = iron.object.Object;
public static var Data = iron.data.Data; public static var Data = iron.data.Data;

View file

@ -3,7 +3,6 @@ package armory.trait.internal;
import iron.math.Mat4; import iron.math.Mat4;
import iron.math.Vec4; import iron.math.Vec4;
import iron.Trait; import iron.Trait;
import iron.Root;
import iron.object.Transform; import iron.object.Transform;
import iron.object.MeshObject; import iron.object.MeshObject;
import iron.data.Data; import iron.data.Data;
@ -83,7 +82,7 @@ class PathTracer extends Trait {
transformMap = new Map(); transformMap = new Map();
var sphereNum = 0; var sphereNum = 0;
var cubeNum = 0; var cubeNum = 0;
for (n in Root.meshes) { for (n in iron.Scene.active.meshes) {
if (n.name.split(".")[0] == "Sphere") { if (n.name.split(".")[0] == "Sphere") {
context.raw.bind_constants.push( context.raw.bind_constants.push(
{ {
@ -144,7 +143,7 @@ class PathTracer extends Trait {
} }
function update() { function update() {
var camera = Root.cameras[0]; var camera = iron.Scene.active.camera;
var eye = camera.transform.loc; var eye = camera.transform.loc;
// var jitter = Mat4.identity(); // var jitter = Mat4.identity();

View file

@ -19,6 +19,8 @@ class ContactPair {
class PhysicsWorld extends Trait { class PhysicsWorld extends Trait {
public static var active:PhysicsWorld;
#if (!WITH_PHYSICS) #if (!WITH_PHYSICS)
public function new(gravity:Array<Float> = null) { super(); } public function new(gravity:Array<Float> = null) { super(); }
#else #else
@ -39,6 +41,8 @@ class PhysicsWorld extends Trait {
public function new(gravity:Array<Float> = null) { public function new(gravity:Array<Float> = null) {
super(); super();
active = this;
rbMap = new Map(); rbMap = new Map();
//var min = BtVector3.create(-100, -100, -100); //var min = BtVector3.create(-100, -100, -100);
@ -172,12 +176,12 @@ class PhysicsWorld extends Trait {
} }
public function getRayFrom():BtVector3Pointer { public function getRayFrom():BtVector3Pointer {
var camera = iron.Root.cameras[0]; var camera = iron.Scene.active.camera;
return BtVector3.create(camera.transform.loc.x, camera.transform.loc.y, camera.transform.loc.z); return BtVector3.create(camera.transform.loc.x, camera.transform.loc.y, camera.transform.loc.z);
} }
public function getRayTo(inputX:Float, inputY:Float):BtVector3Pointer { public function getRayTo(inputX:Float, inputY:Float):BtVector3Pointer {
var camera = iron.Root.cameras[0]; var camera = iron.Scene.active.camera;
var start = new Vec4(); var start = new Vec4();
var end = new Vec4(); var end = new Vec4();
RayCaster.getDirection(start, end, inputX, inputY, camera); RayCaster.getDirection(start, end, inputX, inputY, camera);

View file

@ -52,7 +52,7 @@ class RigidBody extends Trait {
public function init() { public function init() {
transform = object.transform; transform = object.transform;
physics = armory.Scene.physics; physics = armory.trait.internal.PhysicsWorld.active;
if (bodyCreated) return; if (bodyCreated) return;
bodyCreated = true; bodyCreated = true;

Binary file not shown.

View file

@ -561,7 +561,7 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
return unifiedVertexArray return unifiedVertexArray
def ExportBone(self, armature, bone, scene, o): def ExportBone(self, armature, bone, scene, o, action):
bobjectRef = self.bobjectArray.get(bone) bobjectRef = self.bobjectArray.get(bone)
if (bobjectRef): if (bobjectRef):
@ -572,12 +572,12 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
#if (name != ""): #if (name != ""):
# o.name = name # o.name = name
self.ExportBoneTransform(armature, bone, scene, o) self.ExportBoneTransform(armature, bone, scene, o, action)
o['objects'] = [] # TODO o['objects'] = [] # TODO
for subbobject in bone.children: for subbobject in bone.children:
so = {} so = {}
self.ExportBone(armature, subbobject, scene, so) self.ExportBone(armature, subbobject, scene, so, action)
o['objects'].append(so) o['objects'].append(so)
# Export any ordinary objects that are parented to this bone # Export any ordinary objects that are parented to this bone
@ -634,15 +634,28 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
scene.frame_set(currentFrame, currentSubframe) scene.frame_set(currentFrame, currentSubframe)
def ExportBoneSampledAnimation(self, poseBone, scene, o): def get_action_framerange(self, action):
# TODO: experimental
begin_frame = int(action.frame_range[0])
end_frame = int(action.frame_range[1])
if self.beginFrame > begin_frame: # Cap frames to timeline bounds
begin_frame = self.beginFrame
if self.endFrame > end_frame:
end_frame = self.endFrame
return begin_frame, end_frame
def ExportBoneSampledAnimation(self, poseBone, scene, o, action):
# This function exports bone animation as full 4x4 matrices for each frame. # This function exports bone animation as full 4x4 matrices for each frame.
currentFrame = scene.frame_current currentFrame = scene.frame_current
currentSubframe = scene.frame_subframe currentSubframe = scene.frame_subframe
# Frame range
begin_frame, end_frame = self.get_action_framerange(action)
animationFlag = False animationFlag = False
m1 = poseBone.matrix.copy() m1 = poseBone.matrix.copy()
for i in range(self.beginFrame, self.endFrame): for i in range(begin_frame, end_frame):
scene.frame_set(i) scene.frame_set(i)
m2 = poseBone.matrix m2 = poseBone.matrix
if (ArmoryExporter.MatricesDifferent(m1, m2)): if (ArmoryExporter.MatricesDifferent(m1, m2)):
@ -656,28 +669,28 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
tracko['time'] = {} tracko['time'] = {}
tracko['time']['values'] = [] tracko['time']['values'] = []
for i in range(self.beginFrame, self.endFrame): for i in range(begin_frame, end_frame):
tracko['time']['values'].append(((i - self.beginFrame) * self.frameTime)) tracko['time']['values'].append(((i - begin_frame) * self.frameTime))
tracko['time']['values'].append((self.endFrame * self.frameTime)) tracko['time']['values'].append((end_frame * self.frameTime))
tracko['value'] = {} tracko['value'] = {}
tracko['value']['values'] = [] tracko['value']['values'] = []
parent = poseBone.parent parent = poseBone.parent
if (parent): if (parent):
for i in range(self.beginFrame, self.endFrame): for i in range(begin_frame, end_frame):
scene.frame_set(i) scene.frame_set(i)
tracko['value']['values'].append(self.WriteMatrix(parent.matrix.inverted() * poseBone.matrix)) tracko['value']['values'].append(self.WriteMatrix(parent.matrix.inverted() * poseBone.matrix))
scene.frame_set(self.endFrame) scene.frame_set(end_frame)
tracko['value']['values'].append(self.WriteMatrix(parent.matrix.inverted() * poseBone.matrix)) tracko['value']['values'].append(self.WriteMatrix(parent.matrix.inverted() * poseBone.matrix))
else: else:
for i in range(self.beginFrame, self.endFrame): for i in range(begin_frame, end_frame):
scene.frame_set(i) scene.frame_set(i)
tracko['value']['values'].append(self.WriteMatrix(poseBone.matrix)) tracko['value']['values'].append(self.WriteMatrix(poseBone.matrix))
scene.frame_set(self.endFrame) scene.frame_set(end_frame)
tracko['value']['values'].append(self.WriteMatrix(poseBone.matrix)) tracko['value']['values'].append(self.WriteMatrix(poseBone.matrix))
o['animation']['tracks'] = [tracko] o['animation']['tracks'] = [tracko]
@ -1325,7 +1338,7 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
# If an object is used as a bone, then we force its type to be a bone. # If an object is used as a bone, then we force its type to be a bone.
boneRef[1]["objectType"] = kNodeTypeBone boneRef[1]["objectType"] = kNodeTypeBone
def ExportBoneTransform(self, armature, bone, scene, o): def ExportBoneTransform(self, armature, bone, scene, o, action):
curveArray = self.CollectBoneAnimation(armature, bone.name) curveArray = self.CollectBoneAnimation(armature, bone.name)
animation = ((len(curveArray) != 0) or (ArmoryExporter.sampleAnimationFlag)) animation = ((len(curveArray) != 0) or (ArmoryExporter.sampleAnimationFlag))
@ -1341,7 +1354,6 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
if (parentPoseBone): if (parentPoseBone):
transform = parentPoseBone.matrix.inverted() * transform transform = parentPoseBone.matrix.inverted() * transform
o['transform'] = {} o['transform'] = {}
#if (animation): #if (animation):
@ -1350,7 +1362,7 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
o['transform']['values'] = self.WriteMatrix(transform) o['transform']['values'] = self.WriteMatrix(transform)
if ((animation) and (poseBone)): if ((animation) and (poseBone)):
self.ExportBoneSampledAnimation(poseBone, scene, o) self.ExportBoneSampledAnimation(poseBone, scene, o, action)
def ExportMaterialRef(self, material, index, o): def ExportMaterialRef(self, material, index, o):
if material == None: if material == None:
@ -1475,7 +1487,7 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
self.ExportObjectTransform(bobject, scene, o) self.ExportObjectTransform(bobject, scene, o)
# Viewport Camera - overwrite active camera matrix with viewport matrix # Viewport Camera - overwrite active camera matrix with viewport matrix
if type == kNodeTypeCamera and bpy.data.worlds[0].ArmPlayViewportCamera: if type == kNodeTypeCamera and bpy.data.worlds[0].ArmPlayViewportCamera and bobject.name == self.scene.camera.name:
viewport_matrix = self.get_viewport_view_matrix() viewport_matrix = self.get_viewport_view_matrix()
if viewport_matrix != None: if viewport_matrix != None:
o['transform']['values'] = self.WriteMatrix(viewport_matrix.inverted()) o['transform']['values'] = self.WriteMatrix(viewport_matrix.inverted())
@ -1483,29 +1495,41 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
o['local_transform_only'] = True o['local_transform_only'] = True
if (bobject.type == "ARMATURE"): if (bobject.type == "ARMATURE"):
skeleton = bobject.data # Armature data armdata = bobject.data # Armature data
if (skeleton): if (armdata):
armatureid = utils.safe_filename(skeleton.name) # Reference start action
o['bones_ref'] = 'bones_' + armatureid if bobject.edit_actions_prop:
action = bpy.data.actions[bobject.start_action_name_prop]
else: # Use default
action = bobject.animation_data.action
armatureid = utils.safe_filename(armdata.name)
o['bones_ref'] = 'bones_' + armatureid + '_' + action.name
# TODO: use option_mesh_per_file # Write bones
fp = self.get_meshes_file_path(o['bones_ref']) if armdata.edit_actions:
assets.add(fp) export_actions = []
for t in my_actiontraitlist:
export_actions.append(t.name)
else: # Use default
export_actions = [action]
if bobject.data.armature_cached == False or not os.path.exists(fp): for action in export_actions:
bones = [] armdata.animation_data.action = action
for bone in skeleton.bones: fp = self.get_meshes_file_path('bones_' + armatureid + '_' + action.name)
if (not bone.parent): assets.add(fp)
boneo = {} if armdata.armature_cached == False or not os.path.exists(fp):
self.ExportBone(bobject, bone, scene, boneo) bones = []
#o.objects.append(boneo) for bone in armdata.bones:
bones.append(boneo) if (not bone.parent):
boneo = {}
# Save bones separately self.ExportBone(bobject, bone, scene, boneo, action)
bones_obj = {} #o.objects.append(boneo)
bones_obj['objects'] = bones bones.append(boneo)
utils.write_arm(fp, bones_obj) # Save bones separately
bobject.data.armature_cached = True bones_obj = {}
bones_obj['objects'] = bones
utils.write_arm(fp, bones_obj)
armdata.armature_cached = True
if (parento == None): if (parento == None):
self.output['objects'].append(o) self.output['objects'].append(o)
@ -1544,9 +1568,6 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
boneArray = armature.data.bones boneArray = armature.data.bones
boneCount = len(boneArray) boneCount = len(boneArray)
#self.IndentWrite(B"ref\t\t\t// ")
#self.WriteInt(boneCount)
for i in range(boneCount): for i in range(boneCount):
boneRef = self.FindNode(boneArray[i].name) boneRef = self.FindNode(boneArray[i].name)
if (boneRef): if (boneRef):
@ -1554,16 +1575,12 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
else: else:
oskel['bone_ref_array'].append("null") oskel['bone_ref_array'].append("null")
# Write the bind pose transform array. # Write the bind pose transform array
oskel['transforms'] = [] oskel['transforms'] = []
#self.IndentWrite(B"float[16]\t// ")
#self.WriteInt(boneCount)
for i in range(boneCount): for i in range(boneCount):
oskel['transforms'].append(self.WriteMatrix(armature.matrix_world * boneArray[i].matrix_local)) oskel['transforms'].append(self.WriteMatrix(armature.matrix_world * boneArray[i].matrix_local))
# Export the per-vertex bone influence data. # Export the per-vertex bone influence data
groupRemap = [] groupRemap = []
for group in bobject.vertex_groups: for group in bobject.vertex_groups:
@ -2149,8 +2166,23 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
o['type'] = 'perspective' o['type'] = 'perspective'
else: else:
o['type'] = 'orthographic' o['type'] = 'orthographic'
if objref.is_mirror:
o['is_mirror'] = True
o['mirror_resolution_x'] = int(objref.mirror_resolution_x)
o['mirror_resolution_y'] = int(objref.mirror_resolution_y)
o['frustum_culling'] = objref.frustum_culling
o['pipeline'] = objref.pipeline_path + '/' + objref.pipeline_path # Same file name and id
self.cb_export_camera(objref, o) if 'Background' in bpy.data.worlds[0].node_tree.nodes: # TODO: parse node tree
background_node = bpy.data.worlds[0].node_tree.nodes['Background']
col = background_node.inputs[0].default_value
strength = background_node.inputs[1].default_value
o['clear_color'] = [col[0] * strength, col[1] * strength, col[2] * strength, col[3]]
else:
o['clear_color'] = [0.0, 0.0, 0.0, 1.0]
self.output['camera_datas'].append(o) self.output['camera_datas'].append(o)
def ExportSpeaker(self, objectRef): def ExportSpeaker(self, objectRef):
@ -2286,14 +2318,14 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
self.output = {} self.output = {}
scene = context.scene self.scene = context.scene
originalFrame = scene.frame_current originalFrame = self.scene.frame_current
originalSubframe = scene.frame_subframe originalSubframe = self.scene.frame_subframe
self.restoreFrame = False self.restoreFrame = False
self.beginFrame = scene.frame_start self.beginFrame = self.scene.frame_start
self.endFrame = scene.frame_end self.endFrame = self.scene.frame_end
self.frameTime = 1.0 / (scene.render.fps_base * scene.render.fps) self.frameTime = 1.0 / (self.scene.render.fps_base * self.scene.render.fps)
self.bobjectArray = {} self.bobjectArray = {}
self.meshArray = {} self.meshArray = {}
@ -2322,18 +2354,21 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
self.cb_preprocess() self.cb_preprocess()
for bobject in scene.objects: for bobject in self.scene.objects:
if (not bobject.parent): if (not bobject.parent):
self.ProcessBObject(bobject) self.ProcessBObject(bobject)
self.ProcessSkinnedMeshes() self.ProcessSkinnedMeshes()
self.output['name'] = self.scene.name
self.output['objects'] = [] self.output['objects'] = []
for object in scene.objects: for object in self.scene.objects:
if (not object.parent): if (not object.parent):
self.ExportObject(object, scene) self.ExportObject(object, self.scene)
if not ArmoryExporter.option_mesh_only: if not ArmoryExporter.option_mesh_only:
self.output['camera_ref'] = self.scene.camera.name
self.output['material_datas'] = [] self.output['material_datas'] = []
self.ExportMaterials() self.ExportMaterials()
@ -2342,16 +2377,16 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
self.output['world_datas'] = [] self.output['world_datas'] = []
self.ExportWorlds() self.ExportWorlds()
self.output['world_ref'] = scene.world.name self.output['world_ref'] = self.scene.world.name
self.output['gravity'] = [scene.gravity[0], scene.gravity[1], scene.gravity[2]] self.output['gravity'] = [self.scene.gravity[0], self.scene.gravity[1], self.scene.gravity[2]]
self.ExportObjects(scene) self.ExportObjects(self.scene)
self.cb_postprocess() self.cb_postprocess()
if (self.restoreFrame): if (self.restoreFrame):
scene.frame_set(originalFrame, originalSubframe) self.scene.frame_set(originalFrame, originalSubframe)
# Write .arm # Write .arm
utils.write_arm(self.filepath, self.output) utils.write_arm(self.filepath, self.output)
@ -2471,7 +2506,6 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
break break
def cb_export_object(self, bobject, o, type): def cb_export_object(self, bobject, o, type):
#return
# Export traits # Export traits
o['traits'] = [] o['traits'] = []
for t in bobject.my_traitlist: for t in bobject.my_traitlist:
@ -2481,24 +2515,6 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
if t.type_prop == 'Logic Nodes' and t.nodes_name_prop != '': if t.type_prop == 'Logic Nodes' and t.nodes_name_prop != '':
x['type'] = 'Script' x['type'] = 'Script'
x['class_name'] = bpy.data.worlds[0].ArmProjectPackage + '.node.' + utils.safe_filename(t.nodes_name_prop) x['class_name'] = bpy.data.worlds[0].ArmProjectPackage + '.node.' + utils.safe_filename(t.nodes_name_prop)
elif t.type_prop == 'Animation':
x['type'] = 'Script'
x['class_name'] = 'armory.trait.internal.Animation'
names = []
starts = []
ends = []
speeds = []
loops = []
reflects = []
for at in t.my_animationtraitlist:
if at.enabled_prop:
names.append(at.name)
starts.append(at.start_prop)
ends.append(at.end_prop)
speeds.append(at.speed_prop)
loops.append(at.loop_prop)
reflects.append(at.reflect_prop)
x['parameters'] = [t.start_track_name_prop, names, starts, ends, speeds, loops, reflects]
elif t.type_prop == 'JS Script' or t.type_prop == 'Python Script': elif t.type_prop == 'JS Script' or t.type_prop == 'Python Script':
x['type'] = 'Script' x['type'] = 'Script'
x['class_name'] = 'armory.trait.internal.JSScript' x['class_name'] = 'armory.trait.internal.JSScript'
@ -2551,7 +2567,37 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
x['parameters'] = [] x['parameters'] = []
for pt in t.my_paramstraitlist: # Append parameters for pt in t.my_paramstraitlist: # Append parameters
x['parameters'].append(ast.literal_eval(pt.name)) x['parameters'].append(ast.literal_eval(pt.name))
o['traits'].append(x)
# Animation trait
if self.is_bone_animation_enabled(bobject) or self.is_object_animation_enabled(bobject):
x = {}
x['type'] = 'Script'
x['class_name'] = 'armory.trait.internal.Animation'
if len(bobject.my_cliptraitlist) > 0:
# Edit clips enabled
names = []
starts = []
ends = []
speeds = []
loops = []
reflects = []
for at in bobject.my_cliptraitlist:
if at.enabled_prop:
names.append(at.name)
starts.append(at.start_prop)
ends.append(at.end_prop)
speeds.append(at.speed_prop)
loops.append(at.loop_prop)
reflects.append(at.reflect_prop)
x['parameters'] = [bobject.start_track_name_prop, names, starts, ends, speeds, loops, reflects, bpy.data.worlds[0].generate_gpu_skin_max_bones]
else:
# Export default clip, taking full action
if self.is_bone_animation_enabled(bobject):
begin_frame, end_frame = self.get_action_framerange(bobject.parent.animation_data.action)
else:
begin_frame, end_frame = self.get_action_framerange(bobject.animation_data.action)
x['parameters'] = ['default', ['default'], [begin_frame], [end_frame], [1.0], [True], [False], bpy.data.worlds[0].generate_gpu_skin_max_bones]
o['traits'].append(x) o['traits'].append(x)
# Rigid body trait # Rigid body trait
@ -2592,8 +2638,8 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
console_trait['class_name'] = 'armory.trait.internal.Console' console_trait['class_name'] = 'armory.trait.internal.Console'
console_trait['parameters'] = [] console_trait['parameters'] = []
o['traits'].append(console_trait) o['traits'].append(console_trait)
# Viewport camera enabled, attach navigation if enabled # Viewport camera enabled, attach navigation to active camera if enabled
if bpy.data.worlds[0].ArmPlayViewportCamera and bpy.data.worlds[0].ArmPlayViewportNavigation == 'Walk': if bobject.name == self.scene.camera.name and bpy.data.worlds[0].ArmPlayViewportCamera and bpy.data.worlds[0].ArmPlayViewportNavigation == 'Walk':
navigation_trait = {} navigation_trait = {}
navigation_trait['type'] = 'Script' navigation_trait['type'] = 'Script'
navigation_trait['class_name'] = 'armory.trait.WalkNavigation' navigation_trait['class_name'] = 'armory.trait.WalkNavigation'
@ -2613,23 +2659,20 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
self.materialToObjectDict[mat] = [bobject] self.materialToObjectDict[mat] = [bobject]
self.materialToGameObjectDict[mat] = [o] self.materialToGameObjectDict[mat] = [o]
def cb_export_camera(self, object, o): def is_object_animation_enabled(self, bobject):
#return # Checks if animation is present and enabled
o['frustum_culling'] = object.frustum_culling if bobject.object_animation_enabled == False or bobject.type == 'ARMATURE' or bobject.type == 'BONE':
o['pipeline'] = object.pipeline_path + '/' + object.pipeline_path # Same file name and id return False
if bobject.animation_data and bobject.animation_data.action:
if 'Background' in bpy.data.worlds[0].node_tree.nodes: # TODO: parse node tree return True
background_node = bpy.data.worlds[0].node_tree.nodes['Background'] return False
col = background_node.inputs[0].default_value
strength = background_node.inputs[1].default_value
o['clear_color'] = [col[0] * strength, col[1] * strength, col[2] * strength, col[3]]
else:
o['clear_color'] = [0.0, 0.0, 0.0, 1.0]
def find_anim_trait(self, ob): def is_bone_animation_enabled(self, bobject):
# Checks if animation trait is attached # Checks if animation is present and enabled for parented armature
for t in ob.my_traitlist: if bobject.parent and bobject.parent.type == 'ARMATURE':
if t.type_prop == 'Animation' and t.enabled_prop == True: if bobject.parent.bone_animation_enabled == False:
return False
if bobject.parent.animation_data and bobject.parent.animation_data.action:
return True return True
return False return False
@ -2733,7 +2776,7 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
if ob.instanced_children or len(ob.particle_systems) > 0: if ob.instanced_children or len(ob.particle_systems) > 0:
defs.append('_Instancing') defs.append('_Instancing')
# GPU Skinning # GPU Skinning
if ob.find_armature() and self.find_anim_trait(ob) and bpy.data.worlds[0].generate_gpu_skin == True: if ob.find_armature() and self.is_bone_animation_enabled(ob) and bpy.data.worlds[0].generate_gpu_skin == True:
defs.append('_Skinning') defs.append('_Skinning')
# Billboarding # Billboarding
if len(ob.constraints) > 0 and ob.constraints[0].target != None and \ if len(ob.constraints) > 0 and ob.constraints[0].target != None and \
@ -2762,13 +2805,20 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
# Main probe # Main probe
world_generate_radiance = False world_generate_radiance = False
defs = bpy.data.worlds[0].world_defs defs = bpy.data.worlds[0].world_defs
if '_EnvTex' in defs: # Radiance only for texture
world_generate_radiance = bpy.data.worlds[0].generate_radiance
generate_irradiance = True #'_EnvTex' in defs or '_EnvSky' in defs or '_EnvCon' in defs generate_irradiance = True #'_EnvTex' in defs or '_EnvSky' in defs or '_EnvCon' in defs
envtex = bpy.data.cameras[0].world_envtex_name.rsplit('.', 1)[0] irrtex = bpy.data.cameras[0].world_envtex_name.rsplit('.', 1)[0]
radtex = irrtex
# Radiance
if '_EnvTex' in defs:
world_generate_radiance = bpy.data.worlds[0].generate_radiance
elif '_EnvSky' in defs and bpy.data.worlds[0].generate_radiance_sky:
world_generate_radiance = bpy.data.worlds[0].generate_radiance
radtex = 'hosek'
num_mips = bpy.data.cameras[0].world_envtex_num_mips num_mips = bpy.data.cameras[0].world_envtex_num_mips
strength = bpy.data.cameras[0].world_envtex_strength strength = bpy.data.cameras[0].world_envtex_strength
po = self.make_probe('world', envtex, num_mips, strength, 1.0, [0, 0, 0], [0, 0, 0], world_generate_radiance, generate_irradiance) po = self.make_probe('world', irrtex, radtex, num_mips, strength, 1.0, [0, 0, 0], [0, 0, 0], world_generate_radiance, generate_irradiance)
o['probes'].append(po) o['probes'].append(po)
if '_EnvSky' in defs: if '_EnvSky' in defs:
@ -2793,17 +2843,17 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
texture_path = '//' + cam.probe_texture texture_path = '//' + cam.probe_texture
cam.probe_num_mips = write_probes.write_probes(texture_path, disable_hdr, cam.probe_num_mips, generate_radiance=generate_radiance) cam.probe_num_mips = write_probes.write_probes(texture_path, disable_hdr, cam.probe_num_mips, generate_radiance=generate_radiance)
base_name = cam.probe_texture.rsplit('.', 1)[0] base_name = cam.probe_texture.rsplit('.', 1)[0]
po = self.make_probe(cam.name, base_name, cam.probe_num_mips, cam.probe_strength, cam.probe_blending, volume, volume_center, generate_radiance, generate_irradiance) po = self.make_probe(cam.name, base_name, base_name, cam.probe_num_mips, cam.probe_strength, cam.probe_blending, volume, volume_center, generate_radiance, generate_irradiance)
o['probes'].append(po) o['probes'].append(po)
def make_probe(self, id, envtex, mipmaps, strength, blending, volume, volume_center, generate_radiance, generate_irradiance): def make_probe(self, id, irrtex, radtex, mipmaps, strength, blending, volume, volume_center, generate_radiance, generate_irradiance):
po = {} po = {}
po['name'] = id po['name'] = id
if generate_radiance: if generate_radiance:
po['radiance'] = envtex + '_radiance' po['radiance'] = radtex + '_radiance'
po['radiance_mipmaps'] = mipmaps po['radiance_mipmaps'] = mipmaps
if generate_irradiance: if generate_irradiance:
po['irradiance'] = envtex + '_irradiance' po['irradiance'] = irrtex + '_irradiance'
else: else:
po['irradiance'] = '' # No irradiance data, fallback to default at runtime po['irradiance'] = '' # No irradiance data, fallback to default at runtime
po['strength'] = strength po['strength'] = strength

View file

@ -796,13 +796,19 @@ def buildNodeTrees(shader_references, asset_references, assets_path):
# Always include # Always include
buildNodeTrees.linked_assets.append(buildNodeTrees.assets_path + 'brdf.png') buildNodeTrees.linked_assets.append(buildNodeTrees.assets_path + 'brdf.png')
# Export selected pipeline # Export pipeline for each camera
node_group = bpy.data.node_groups[bpy.data.cameras[0].pipeline_path] parsed_paths = []
buildNodeTree(node_group, shader_references, asset_references) for cam in bpy.data.cameras:
# if cam.game_export
if cam.pipeline_path not in parsed_paths:
node_group = bpy.data.node_groups[cam.pipeline_path]
buildNodeTree(cam, node_group, shader_references, asset_references)
parsed_paths.append(cam.pipeline_path)
return buildNodeTrees.linked_assets return buildNodeTrees.linked_assets
def buildNodeTree(node_group, shader_references, asset_references): def buildNodeTree(cam, node_group, shader_references, asset_references):
buildNodeTree.cam = cam
output = {} output = {}
dat = {} dat = {}
output['pipeline_datas'] = [dat] output['pipeline_datas'] = [dat]
@ -817,8 +823,8 @@ def buildNodeTree(node_group, shader_references, asset_references):
dat['name'] = node_group_name dat['name'] = node_group_name
# Store main context names # Store main context names
dat['mesh_context'] = bpy.data.cameras[0].mesh_context dat['mesh_context'] = buildNodeTree.cam.mesh_context
dat['shadows_context'] = bpy.data.cameras[0].shadows_context dat['shadows_context'] = buildNodeTree.cam.shadows_context
dat['render_targets'], dat['depth_buffers'] = preprocess_pipeline(rn, node_group) dat['render_targets'], dat['depth_buffers'] = preprocess_pipeline(rn, node_group)
dat['stages'] = [] dat['stages'] = []
@ -878,7 +884,7 @@ def make_draw_meshes(stage, node_group, node):
# Context # Context
context = node.inputs[1].default_value context = node.inputs[1].default_value
# Store shadowmap size # Store shadowmap size
if context == bpy.data.cameras[0].shadows_context: if context == buildNodeTree.cam.shadows_context:
bpy.data.worlds[0].shadowmap_size = buildNode.last_set_target_w bpy.data.worlds[0].shadowmap_size = buildNode.last_set_target_w
stage['params'].append(context) stage['params'].append(context)
# Order # Order
@ -889,7 +895,7 @@ def make_draw_decals(stage, node_group, node, shader_references, asset_reference
stage['command'] = 'draw_decals' stage['command'] = 'draw_decals'
context = node.inputs[1].default_value context = node.inputs[1].default_value
stage['params'].append(context) # Context stage['params'].append(context) # Context
bpy.data.cameras[0].last_decal_context = context buildNodeTree.cam.last_decal_context = context
def make_bind_target(stage, node_group, node, constant_name, currentNode=None, target_index=1): def make_bind_target(stage, node_group, node, constant_name, currentNode=None, target_index=1):
if currentNode == None: if currentNode == None:
@ -985,7 +991,7 @@ def make_draw_compositor(stage, node_group, node, shader_references, asset_refer
if wrd.generate_fog: if wrd.generate_fog:
compositor_defs += '_CompoFog' compositor_defs += '_CompoFog'
compo_pos = True compo_pos = True
if bpy.data.cameras[0].cycles.aperture_size > 0.0: if buildNodeTree.cam.cycles.aperture_size > 0.0:
compositor_defs += '_CompoDOF' compositor_defs += '_CompoDOF'
compo_depth = True compo_depth = True
if compo_pos: if compo_pos:
@ -1186,7 +1192,7 @@ def buildNode(stages, node, node_group, shader_references, asset_references):
stencil_val = None stencil_val = None
if node.inputs[1].default_value == True: if node.inputs[1].default_value == True:
if node.inputs[2].is_linked: # Assume background color node is linked if node.inputs[2].is_linked: # Assume background color node is linked
color_val = bpy.data.cameras[0].world_envtex_color color_val = buildNodeTree.cam.world_envtex_color
else: else:
color_val = node.inputs[2].default_value color_val = node.inputs[2].default_value
if node.inputs[3].default_value == True: if node.inputs[3].default_value == True:
@ -1237,7 +1243,7 @@ def buildNode(stages, node, node_group, shader_references, asset_references):
make_set_target(stage, node_group, node) make_set_target(stage, node_group, node)
stages.append(stage) stages.append(stage)
# Bind targets # Bind targets
if node.inputs[2].is_linked or node.inputs[3].is_linked: if node.inputs[2].is_linked or node.inputs[3].is_linked or node.inputs[4].is_linked:
stage = {} stage = {}
stage['params'] = [] stage['params'] = []
buildNode.last_bind_target = stage buildNode.last_bind_target = stage
@ -1390,14 +1396,14 @@ def get_root_node(node_group):
for n in node_group.nodes: for n in node_group.nodes:
if n.bl_idname == 'BeginNodeType': if n.bl_idname == 'BeginNodeType':
# Store contexts # Store contexts
bpy.data.cameras[0].pipeline_id = n.inputs[0].default_value buildNodeTree.cam.pipeline_id = n.inputs[0].default_value
mesh_contexts = n.inputs[1].default_value.split(',') mesh_contexts = n.inputs[1].default_value.split(',')
bpy.data.cameras[0].mesh_context = mesh_contexts[0] buildNodeTree.cam.mesh_context = mesh_contexts[0]
if len(mesh_contexts) > 1: if len(mesh_contexts) > 1:
bpy.data.cameras[0].mesh_context_empty = mesh_contexts[1] buildNodeTree.cam.mesh_context_empty = mesh_contexts[1]
bpy.data.cameras[0].shadows_context = n.inputs[2].default_value buildNodeTree.cam.shadows_context = n.inputs[2].default_value
bpy.data.cameras[0].translucent_context = n.inputs[3].default_value buildNodeTree.cam.translucent_context = n.inputs[3].default_value
bpy.data.cameras[0].overlay_context = n.inputs[4].default_value buildNodeTree.cam.overlay_context = n.inputs[4].default_value
if n.inputs[5].default_value == False: # No HDR space lighting, append def if n.inputs[5].default_value == False: # No HDR space lighting, append def
bpy.data.worlds[0].world_defs += '_LDR' bpy.data.worlds[0].world_defs += '_LDR'
rn = findNodeByLinkFrom(node_group, n, n.outputs[0]) rn = findNodeByLinkFrom(node_group, n, n.outputs[0])
@ -1408,16 +1414,16 @@ def preprocess_pipeline(root_node, node_group):
render_targets = [] render_targets = []
depth_buffers = [] depth_buffers = []
preprocess_pipeline.velocity_def_added = False preprocess_pipeline.velocity_def_added = False
bpy.data.cameras[0].pipeline_passes = '' buildNodeTree.cam.pipeline_passes = ''
traverse_pipeline(root_node, node_group, render_targets, depth_buffers) traverse_pipeline(root_node, node_group, render_targets, depth_buffers)
return render_targets, depth_buffers return render_targets, depth_buffers
def traverse_pipeline(node, node_group, render_targets, depth_buffers): def traverse_pipeline(node, node_group, render_targets, depth_buffers):
# Gather linked draw geometry contexts # Gather linked draw geometry contexts
if node.bl_idname == 'DrawMeshesNodeType': if node.bl_idname == 'DrawMeshesNodeType':
if bpy.data.cameras[0].pipeline_passes != '': if buildNodeTree.cam.pipeline_passes != '':
bpy.data.cameras[0].pipeline_passes += '_' # Separator buildNodeTree.cam.pipeline_passes += '_' # Separator
bpy.data.cameras[0].pipeline_passes += node.inputs[1].default_value buildNodeTree.cam.pipeline_passes += node.inputs[1].default_value
# Gather defs from linked nodes # Gather defs from linked nodes
if node.bl_idname == 'TAAPassNodeType' or node.bl_idname == 'MotionBlurVelocityPassNodeType' or node.bl_idname == 'SSAOReprojectPassNodeType': if node.bl_idname == 'TAAPassNodeType' or node.bl_idname == 'MotionBlurVelocityPassNodeType' or node.bl_idname == 'SSAOReprojectPassNodeType':

View file

@ -77,6 +77,10 @@ def buildNodeTree(world_name, node_group):
if wrd.generate_shadows == False: if wrd.generate_shadows == False:
wrd.world_defs += '_NoShadows' wrd.world_defs += '_NoShadows'
# Percentage closer soft shadows
if wrd.generate_pcss:
wrd.world_defs += '_PCSS'
# Enable probes # Enable probes
for cam in bpy.data.cameras: for cam in bpy.data.cameras:
if cam.is_probe: if cam.is_probe:
@ -166,7 +170,7 @@ def parse_color(node_group, node, context, envmap_strength_const):
# Append LDR define # Append LDR define
if disable_hdr: if disable_hdr:
bpy.data.worlds[0].world_defs += '_EnvLDR' bpy.data.worlds[0].world_defs += '_EnvLDR'
# Append radiance degine # Append radiance define
if generate_radiance: if generate_radiance:
bpy.data.worlds[0].world_defs += '_Rad' bpy.data.worlds[0].world_defs += '_Rad'
@ -191,5 +195,18 @@ def parse_color(node_group, node, context, envmap_strength_const):
write_probes.write_sky_irradiance(base_name) write_probes.write_sky_irradiance(base_name)
# Radiance
if bpy.data.worlds[0].generate_radiance_sky and bpy.data.worlds[0].generate_radiance:
bpy.data.worlds[0].world_defs += '_Rad'
user_preferences = bpy.context.user_preferences
addon_prefs = user_preferences.addons['armory'].preferences
sdk_path = addon_prefs.sdk_path
assets.add(sdk_path + 'armory/Assets/hosek/hosek_radiance.hdr')
for i in range(0, 8):
assets.add(sdk_path + 'armory/Assets/hosek/hosek_radiance_' + str(i) + '.hdr')
bpy.data.cameras[0].world_envtex_num_mips = 8
# Adjust strength to match Cycles # Adjust strength to match Cycles
envmap_strength_const['float'] *= 0.25 envmap_strength_const['float'] *= 0.25

View file

@ -5,6 +5,8 @@ import json
import nodes_renderpath import nodes_renderpath
from bpy.types import Menu, Panel, UIList from bpy.types import Menu, Panel, UIList
from bpy.props import * from bpy.props import *
from traits_clip import *
from traits_action import *
import utils import utils
def on_scene_update(context): def on_scene_update(context):
@ -15,10 +17,6 @@ def on_scene_update(context):
elif edit_obj.type == 'ARMATURE': elif edit_obj.type == 'ARMATURE':
edit_obj.data.armature_cached = False edit_obj.data.armature_cached = False
for text in bpy.data.texts:
if text.is_updated:
print('ASDASDASDASDASD')
def invalidate_shader_cache(self, context): def invalidate_shader_cache(self, context):
# compiled.glsl changed, recompile all shaders next time # compiled.glsl changed, recompile all shaders next time
fp = utils.get_fp() fp = utils.get_fp()
@ -91,6 +89,16 @@ def initProperties():
bpy.types.Object.override_material_name = bpy.props.StringProperty(name="Name", default="") bpy.types.Object.override_material_name = bpy.props.StringProperty(name="Name", default="")
bpy.types.Object.game_export = bpy.props.BoolProperty(name="Export", default=True) bpy.types.Object.game_export = bpy.props.BoolProperty(name="Export", default=True)
bpy.types.Object.spawn = bpy.props.BoolProperty(name="Spawn", description="Auto-add this object when creating scene", default=True) bpy.types.Object.spawn = bpy.props.BoolProperty(name="Spawn", description="Auto-add this object when creating scene", default=True)
# - Clips
bpy.types.Object.bone_animation_enabled = bpy.props.BoolProperty(name="Bone Animation", default=True)
bpy.types.Object.object_animation_enabled = bpy.props.BoolProperty(name="Object Animation", default=True)
bpy.types.Object.edit_tracks_prop = bpy.props.BoolProperty(name="Edit Clips", description="A name for this item", default=False)
bpy.types.Object.start_track_name_prop = bpy.props.StringProperty(name="Start Track", description="A name for this item", default="")
bpy.types.Object.my_cliptraitlist = bpy.props.CollectionProperty(type=ListClipTraitItem)
bpy.types.Object.cliptraitlist_index = bpy.props.IntProperty(name="Index for my_list", default=0)
# - Actions
bpy.types.Object.edit_actions_prop = bpy.props.BoolProperty(name="Edit Actions", description="A name for this item", default=False)
bpy.types.Object.start_action_name_prop = bpy.props.StringProperty(name="Start Action", description="A name for this item", default="")
# For mesh # For mesh
bpy.types.Mesh.mesh_cached = bpy.props.BoolProperty(name="Mesh Cached", default=False) bpy.types.Mesh.mesh_cached = bpy.props.BoolProperty(name="Mesh Cached", default=False)
bpy.types.Mesh.mesh_cached_verts = bpy.props.IntProperty(name="Last Verts", default=0) bpy.types.Mesh.mesh_cached_verts = bpy.props.IntProperty(name="Last Verts", default=0)
@ -99,6 +107,10 @@ def initProperties():
bpy.types.Curve.static_usage = bpy.props.BoolProperty(name="Static Usage", default=True) bpy.types.Curve.static_usage = bpy.props.BoolProperty(name="Static Usage", default=True)
# For armature # For armature
bpy.types.Armature.armature_cached = bpy.props.BoolProperty(name="Armature Cached", default=False) bpy.types.Armature.armature_cached = bpy.props.BoolProperty(name="Armature Cached", default=False)
# Actions
bpy.types.Armature.edit_actions = bpy.props.BoolProperty(name="Edit Actions", default=False)
bpy.types.Armature.my_actiontraitlist = bpy.props.CollectionProperty(type=ListActionTraitItem)
bpy.types.Armature.actiontraitlist_index = bpy.props.IntProperty(name="Index for my_list", default=0)
# For camera # For camera
bpy.types.Camera.frustum_culling = bpy.props.BoolProperty(name="Frustum Culling", default=True) bpy.types.Camera.frustum_culling = bpy.props.BoolProperty(name="Frustum Culling", default=True)
bpy.types.Camera.pipeline_path = bpy.props.StringProperty(name="Render Path", default="deferred_path") bpy.types.Camera.pipeline_path = bpy.props.StringProperty(name="Render Path", default="deferred_path")
@ -117,6 +129,9 @@ def initProperties():
bpy.types.Camera.probe_volume = bpy.props.StringProperty(name="Volume", default="") bpy.types.Camera.probe_volume = bpy.props.StringProperty(name="Volume", default="")
bpy.types.Camera.probe_strength = bpy.props.FloatProperty(name="Strength", default=1.0) bpy.types.Camera.probe_strength = bpy.props.FloatProperty(name="Strength", default=1.0)
bpy.types.Camera.probe_blending = bpy.props.FloatProperty(name="Blending", default=0.0) bpy.types.Camera.probe_blending = bpy.props.FloatProperty(name="Blending", default=0.0)
bpy.types.Camera.is_mirror = bpy.props.BoolProperty(name="Mirror", default=False)
bpy.types.Camera.mirror_resolution_x = bpy.props.FloatProperty(name="X", default=512.0)
bpy.types.Camera.mirror_resolution_y = bpy.props.FloatProperty(name="Y", default=256.0)
# TODO: move to world # TODO: move to world
bpy.types.Camera.world_envtex_name = bpy.props.StringProperty(name="Environment Texture", default='') bpy.types.Camera.world_envtex_name = bpy.props.StringProperty(name="Environment Texture", default='')
bpy.types.Camera.world_envtex_num_mips = bpy.props.IntProperty(name="Number of mips", default=0) bpy.types.Camera.world_envtex_num_mips = bpy.props.IntProperty(name="Number of mips", default=0)
@ -127,7 +142,8 @@ def initProperties():
bpy.types.Camera.world_envtex_ground_albedo = bpy.props.FloatProperty(name="Ground Albedo", default=0.0) bpy.types.Camera.world_envtex_ground_albedo = bpy.props.FloatProperty(name="Ground Albedo", default=0.0)
bpy.types.Camera.last_decal_context = bpy.props.StringProperty(name="Decal Context", default='') bpy.types.Camera.last_decal_context = bpy.props.StringProperty(name="Decal Context", default='')
bpy.types.World.world_defs = bpy.props.StringProperty(name="World Shader Defs", default='') bpy.types.World.world_defs = bpy.props.StringProperty(name="World Shader Defs", default='')
bpy.types.World.generate_radiance = bpy.props.BoolProperty(name="Radiance Probes", default=True, update=invalidate_shader_cache) bpy.types.World.generate_radiance = bpy.props.BoolProperty(name="Probe Radiance", default=True, update=invalidate_shader_cache)
bpy.types.World.generate_radiance_sky = bpy.props.BoolProperty(name="Sky Radiance", default=False, update=invalidate_shader_cache)
bpy.types.World.generate_clouds = bpy.props.BoolProperty(name="Clouds", default=False, update=invalidate_shader_cache) bpy.types.World.generate_clouds = bpy.props.BoolProperty(name="Clouds", default=False, update=invalidate_shader_cache)
bpy.types.World.generate_clouds_density = bpy.props.FloatProperty(name="Density", default=0.5, min=0.0, max=10.0, update=invalidate_shader_cache) bpy.types.World.generate_clouds_density = bpy.props.FloatProperty(name="Density", default=0.5, min=0.0, max=10.0, update=invalidate_shader_cache)
bpy.types.World.generate_clouds_size = bpy.props.FloatProperty(name="Size", default=1.0, min=0.0, max=10.0, update=invalidate_shader_cache) bpy.types.World.generate_clouds_size = bpy.props.FloatProperty(name="Size", default=1.0, min=0.0, max=10.0, update=invalidate_shader_cache)
@ -167,6 +183,8 @@ def initProperties():
bpy.types.World.generate_ssr_falloff_exp = bpy.props.FloatProperty(name="Falloff Exp", default=5.0, update=invalidate_shader_cache) bpy.types.World.generate_ssr_falloff_exp = bpy.props.FloatProperty(name="Falloff Exp", default=5.0, update=invalidate_shader_cache)
bpy.types.World.generate_ssr_jitter = bpy.props.FloatProperty(name="Jitter", default=0.6, update=invalidate_shader_cache) bpy.types.World.generate_ssr_jitter = bpy.props.FloatProperty(name="Jitter", default=0.6, update=invalidate_shader_cache)
bpy.types.World.generate_ssr_texture_scale = bpy.props.FloatProperty(name="Texture Scale", default=0.5, min=0.0, max=1.0, update=invalidate_shader_cache) bpy.types.World.generate_ssr_texture_scale = bpy.props.FloatProperty(name="Texture Scale", default=0.5, min=0.0, max=1.0, update=invalidate_shader_cache)
bpy.types.World.generate_pcss = bpy.props.BoolProperty(name="PCSS", description="Percentage Closer Soft Shadows", default=False, update=invalidate_shader_cache)
# Compositor
bpy.types.World.generate_letterbox = bpy.props.BoolProperty(name="Letterbox", default=False, update=invalidate_shader_cache) bpy.types.World.generate_letterbox = bpy.props.BoolProperty(name="Letterbox", default=False, update=invalidate_shader_cache)
bpy.types.World.generate_letterbox_size = bpy.props.FloatProperty(name="Size", default=0.1, update=invalidate_shader_cache) bpy.types.World.generate_letterbox_size = bpy.props.FloatProperty(name="Size", default=0.1, update=invalidate_shader_cache)
bpy.types.World.generate_grain = bpy.props.BoolProperty(name="Film Grain", default=False, update=invalidate_shader_cache) bpy.types.World.generate_grain = bpy.props.BoolProperty(name="Film Grain", default=False, update=invalidate_shader_cache)
@ -175,6 +193,7 @@ def initProperties():
bpy.types.World.generate_fog_color = bpy.props.FloatVectorProperty(name="Color", size=3, subtype='COLOR', default=[0.5, 0.6, 0.7], update=invalidate_shader_cache) bpy.types.World.generate_fog_color = bpy.props.FloatVectorProperty(name="Color", size=3, subtype='COLOR', default=[0.5, 0.6, 0.7], update=invalidate_shader_cache)
bpy.types.World.generate_fog_amounta = bpy.props.FloatProperty(name="Amount A", default=0.25, update=invalidate_shader_cache) bpy.types.World.generate_fog_amounta = bpy.props.FloatProperty(name="Amount A", default=0.25, update=invalidate_shader_cache)
bpy.types.World.generate_fog_amountb = bpy.props.FloatProperty(name="Amount B", default=0.5, update=invalidate_shader_cache) bpy.types.World.generate_fog_amountb = bpy.props.FloatProperty(name="Amount B", default=0.5, update=invalidate_shader_cache)
# Skin
bpy.types.World.generate_gpu_skin = bpy.props.BoolProperty(name="GPU Skinning", default=True, update=invalidate_shader_cache) bpy.types.World.generate_gpu_skin = bpy.props.BoolProperty(name="GPU Skinning", default=True, update=invalidate_shader_cache)
bpy.types.World.generate_gpu_skin_max_bones = bpy.props.IntProperty(name="Max Bones", default=50, min=1, max=84, update=invalidate_shader_cache) bpy.types.World.generate_gpu_skin_max_bones = bpy.props.IntProperty(name="Max Bones", default=50, min=1, max=84, update=invalidate_shader_cache)
# For material # For material
@ -246,6 +265,47 @@ class ObjectPropsPanel(bpy.types.Panel):
if obj.override_material: if obj.override_material:
layout.prop(obj, 'override_material_name') layout.prop(obj, 'override_material_name')
if obj.type == 'ARMATURE':
layout.prop(obj, 'bone_animation_enabled')
if obj.bone_animation_enabled:
layout.prop(obj, 'edit_actions_prop')
if obj.edit_actions_prop:
layout.prop_search(obj, "start_action_name_prop", obj.data, "my_actiontraitlist", "Start Action")
else:
layout.prop(obj, 'object_animation_enabled')
if (obj.type == 'ARMATURE' and obj.bone_animation_enabled) or (obj.type != 'ARMATURE' and obj.object_animation_enabled):
layout.prop(obj, 'edit_tracks_prop')
if obj.edit_tracks_prop:
layout.prop_search(obj, "start_track_name_prop", obj, "my_cliptraitlist", "Start Clip")
# Tracks list
layout.label("Clips")
animrow = layout.row()
animrows = 2
if len(obj.my_cliptraitlist) > 1:
animrows = 4
row = layout.row()
row.template_list("MY_UL_ClipTraitList", "The_List", obj, "my_cliptraitlist", obj, "cliptraitlist_index", rows=animrows)
col = row.column(align=True)
col.operator("my_cliptraitlist.new_item", icon='ZOOMIN', text="")
col.operator("my_cliptraitlist.delete_item", icon='ZOOMOUT', text="")
if len(obj.my_cliptraitlist) > 1:
col.separator()
col.operator("my_cliptraitlist.move_item", icon='TRIA_UP', text="").direction = 'UP'
col.operator("my_cliptraitlist.move_item", icon='TRIA_DOWN', text="").direction = 'DOWN'
if obj.cliptraitlist_index >= 0 and len(obj.my_cliptraitlist) > 0:
animitem = obj.my_cliptraitlist[obj.cliptraitlist_index]
row = layout.row()
row.prop(animitem, "start_prop")
row.prop(animitem, "end_prop")
layout.prop(animitem, "speed_prop")
layout.prop(animitem, "loop_prop")
layout.prop(animitem, "reflect_prop")
# Menu in modifiers region # Menu in modifiers region
class ModifiersPropsPanel(bpy.types.Panel): class ModifiersPropsPanel(bpy.types.Panel):
bl_label = "Armory Props" bl_label = "Armory Props"
@ -281,6 +341,11 @@ class DataPropsPanel(bpy.types.Panel):
layout.prop_search(obj.data, "probe_volume", bpy.data, "objects") layout.prop_search(obj.data, "probe_volume", bpy.data, "objects")
layout.prop(obj.data, 'probe_strength') layout.prop(obj.data, 'probe_strength')
layout.prop(obj.data, 'probe_blending') layout.prop(obj.data, 'probe_blending')
layout.prop(obj.data, 'is_mirror')
if obj.data.is_mirror == True:
layout.label('Resolution')
layout.prop(obj.data, 'mirror_resolution_x')
layout.prop(obj.data, 'mirror_resolution_y')
layout.prop(obj.data, 'frustum_culling') layout.prop(obj.data, 'frustum_culling')
layout.prop_search(obj.data, "pipeline_path", bpy.data, "node_groups") layout.prop_search(obj.data, "pipeline_path", bpy.data, "node_groups")
layout.operator("arm.reimport_paths_menu") layout.operator("arm.reimport_paths_menu")
@ -292,6 +357,33 @@ class DataPropsPanel(bpy.types.Panel):
layout.prop(obj.data, 'lamp_clip_end') layout.prop(obj.data, 'lamp_clip_end')
layout.prop(obj.data, 'lamp_fov') layout.prop(obj.data, 'lamp_fov')
layout.prop(obj.data, 'lamp_shadows_bias') layout.prop(obj.data, 'lamp_shadows_bias')
elif obj.type == 'ARMATURE':
layout.prop(obj.data, 'edit_actions')
if obj.data.edit_actions:
# Actions list
layout.label("Actions")
animrow = layout.row()
animrows = 2
if len(obj.data.my_actiontraitlist) > 1:
animrows = 4
row = layout.row()
row.template_list("MY_UL_ActionTraitList", "The_List", obj.data, "my_actiontraitlist", obj.data, "actiontraitlist_index", rows=animrows)
col = row.column(align=True)
col.operator("my_actiontraitlist.new_item", icon='ZOOMIN', text="")
col.operator("my_actiontraitlist.delete_item", icon='ZOOMOUT', text="")
if len(obj.data.my_actiontraitlist) > 1:
col.separator()
col.operator("my_actiontraitlist.move_item", icon='TRIA_UP', text="").direction = 'UP'
col.operator("my_actiontraitlist.move_item", icon='TRIA_DOWN', text="").direction = 'DOWN'
if obj.data.actiontraitlist_index >= 0 and len(obj.data.my_actiontraitlist) > 0:
item = obj.data.my_actiontraitlist[obj.data.actiontraitlist_index]
item.name = item.action_name_prop
row = layout.row()
row.prop_search(item, "action_name_prop", bpy.data, "actions", "Action")
class ScenePropsPanel(bpy.types.Panel): class ScenePropsPanel(bpy.types.Panel):
bl_label = "Armory Props" bl_label = "Armory Props"
@ -379,6 +471,8 @@ class WorldPropsPanel(bpy.types.Panel):
wrd = bpy.context.world wrd = bpy.context.world
layout.prop(wrd, 'generate_shadows') layout.prop(wrd, 'generate_shadows')
layout.prop(wrd, 'generate_radiance') layout.prop(wrd, 'generate_radiance')
if wrd.generate_radiance:
layout.prop(wrd, 'generate_radiance_sky')
layout.prop(wrd, 'generate_clouds') layout.prop(wrd, 'generate_clouds')
if wrd.generate_clouds: if wrd.generate_clouds:
layout.prop(wrd, 'generate_clouds_density') layout.prop(wrd, 'generate_clouds_density')
@ -409,7 +503,8 @@ class WorldPropsPanel(bpy.types.Panel):
layout.prop(wrd, 'generate_ssr_falloff_exp') layout.prop(wrd, 'generate_ssr_falloff_exp')
layout.prop(wrd, 'generate_ssr_jitter') layout.prop(wrd, 'generate_ssr_jitter')
layout.prop(wrd, 'generate_ssr_texture_scale') layout.prop(wrd, 'generate_ssr_texture_scale')
layout.prop(wrd, 'generate_pcss')
layout.label('Compositor') layout.label('Compositor')
layout.prop(wrd, 'generate_letterbox') layout.prop(wrd, 'generate_letterbox')
if wrd.generate_letterbox: if wrd.generate_letterbox:

View file

@ -3,20 +3,22 @@ import nodes_logic
import nodes_renderpath import nodes_renderpath
import nodes_world import nodes_world
import exporter import exporter
import traits_animation import traits_action
import traits_clip
import traits_params import traits_params
import traits import traits
import props import props
import lib.drop_to_ground import lib.drop_to_ground
def register(): def register():
traits_action.register()
traits_clip.register()
props.register() props.register()
project.register() project.register()
nodes_logic.register() nodes_logic.register()
nodes_renderpath.register() nodes_renderpath.register()
nodes_world.register() nodes_world.register()
exporter.register() exporter.register()
traits_animation.register()
traits_params.register() traits_params.register()
traits.register() traits.register()
lib.drop_to_ground.register() lib.drop_to_ground.register()
@ -27,8 +29,9 @@ def unregister():
nodes_renderpath.unregister() nodes_renderpath.unregister()
nodes_world.unregister() nodes_world.unregister()
exporter.unregister() exporter.unregister()
traits_animation.unregister()
traits_params.unregister() traits_params.unregister()
traits.unregister() traits.unregister()
props.unregister() props.unregister()
traits_action.unregister()
traits_clip.unregister()
lib.drop_to_ground.unregister() lib.drop_to_ground.unregister()

View file

@ -2,7 +2,6 @@ import shutil
import bpy import bpy
import os import os
import json import json
from traits_animation import *
from traits_params import * from traits_params import *
from bpy.types import Menu, Panel, UIList from bpy.types import Menu, Panel, UIList
from bpy.props import * from bpy.props import *
@ -27,15 +26,14 @@ class ListTraitItem(bpy.types.PropertyGroup):
('Python Script', 'Python Script', 'Python Script'), ('Python Script', 'Python Script', 'Python Script'),
('JS Script', 'JS Script', 'JS Script'), ('JS Script', 'JS Script', 'JS Script'),
('Bundled Script', 'Bundled Script', 'Bundled Script'), ('Bundled Script', 'Bundled Script', 'Bundled Script'),
('Logic Nodes', 'Logic Nodes', 'Logic Nodes'), ('Logic Nodes', 'Logic Nodes', 'Logic Nodes')
('Animation', 'Animation', 'Animation')
], ],
name = "Type") name = "Type")
data_prop = bpy.props.StringProperty( # data_prop = bpy.props.StringProperty(
name="Data", # name="Data",
description="A name for this item", # description="A name for this item",
default="") # default="")
class_name_prop = bpy.props.StringProperty( class_name_prop = bpy.props.StringProperty(
name="Class", name="Class",
@ -52,17 +50,9 @@ class ListTraitItem(bpy.types.PropertyGroup):
description="A name for this item", description="A name for this item",
default="") default="")
start_track_name_prop = bpy.props.StringProperty(
name="Start Track",
description="A name for this item",
default="")
my_paramstraitlist = bpy.props.CollectionProperty(type=ListParamsTraitItem) my_paramstraitlist = bpy.props.CollectionProperty(type=ListParamsTraitItem)
paramstraitlist_index = bpy.props.IntProperty(name="Index for my_list", default=0) paramstraitlist_index = bpy.props.IntProperty(name="Index for my_list", default=0)
my_animationtraitlist = bpy.props.CollectionProperty(type=ListAnimationTraitItem)
animationtraitlist_index = bpy.props.IntProperty(name="Index for my_list", default=0)
class MY_UL_TraitList(bpy.types.UIList): class MY_UL_TraitList(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
# We could write some code to decide which icon to use here... # We could write some code to decide which icon to use here...
@ -71,7 +61,7 @@ class MY_UL_TraitList(bpy.types.UIList):
# Make sure your code supports all 3 layout types # Make sure your code supports all 3 layout types
if self.layout_type in {'DEFAULT', 'COMPACT'}: if self.layout_type in {'DEFAULT', 'COMPACT'}:
layout.prop(item, "enabled_prop") layout.prop(item, "enabled_prop")
layout.label(item.name, icon = custom_icon) layout.label(item.name, icon=custom_icon)
elif self.layout_type in {'GRID'}: elif self.layout_type in {'GRID'}:
layout.alignment = 'CENTER' layout.alignment = 'CENTER'
@ -239,7 +229,7 @@ class ToolsTraitsPanel(bpy.types.Panel):
col.operator("my_traitlist.move_item", icon='TRIA_DOWN', text="").direction = 'DOWN' col.operator("my_traitlist.move_item", icon='TRIA_DOWN', text="").direction = 'DOWN'
if obj.traitlist_index >= 0 and len(obj.my_traitlist) > 0: if obj.traitlist_index >= 0 and len(obj.my_traitlist) > 0:
item = obj.my_traitlist[obj.traitlist_index] item = obj.my_traitlist[obj.traitlist_index]
# Default props # Default props
row = layout.row() row = layout.row()
row.prop(item, "type_prop") row.prop(item, "type_prop")
@ -299,40 +289,6 @@ class ToolsTraitsPanel(bpy.types.Panel):
row = layout.row() row = layout.row()
row.prop_search(item, "nodes_name_prop", bpy.data, "node_groups", "Tree") row.prop_search(item, "nodes_name_prop", bpy.data, "node_groups", "Tree")
# Animation
elif item.type_prop == 'Animation':
item.name = item.type_prop
row = layout.row()
row.prop_search(item, "start_track_name_prop", item, "my_animationtraitlist", "Start Track")
# Tracks list
layout.label("Tracks")
animrow = layout.row()
animrows = 2
if len(item.my_animationtraitlist) > 1:
animrows = 4
row = layout.row()
row.template_list("MY_UL_AnimationTraitList", "The_List", item, "my_animationtraitlist", item, "animationtraitlist_index", rows=animrows)
col = row.column(align=True)
col.operator("my_animationtraitlist.new_item", icon='ZOOMIN', text="")
col.operator("my_animationtraitlist.delete_item", icon='ZOOMOUT', text="")
if len(item.my_animationtraitlist) > 1:
col.separator()
col.operator("my_animationtraitlist.move_item", icon='TRIA_UP', text="").direction = 'UP'
col.operator("my_animationtraitlist.move_item", icon='TRIA_DOWN', text="").direction = 'DOWN'
if item.animationtraitlist_index >= 0 and len(item.my_animationtraitlist) > 0:
animitem = item.my_animationtraitlist[item.animationtraitlist_index]
row = layout.row()
row.prop(animitem, "start_prop")
row.prop(animitem, "end_prop")
layout.prop(animitem, "speed_prop")
layout.prop(animitem, "loop_prop")
layout.prop(animitem, "reflect_prop")
# Registration # Registration
def register(): def register():
bpy.utils.register_module(__name__) bpy.utils.register_module(__name__)

131
blender/traits_action.py Executable file
View file

@ -0,0 +1,131 @@
import shutil
import bpy
import os
import json
from bpy.types import Menu, Panel, UIList
from bpy.props import *
class ListActionTraitItem(bpy.types.PropertyGroup):
# Group of properties representing an item in the list
name = bpy.props.StringProperty(
name="Name",
description="A name for this item",
default="")
enabled_prop = bpy.props.BoolProperty(
name="",
description="A name for this item",
default=True)
action_name_prop = bpy.props.StringProperty(
name="Action",
description="A name for this item",
default="")
class MY_UL_ActionTraitList(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
# We could write some code to decide which icon to use here...
custom_icon = 'OBJECT_DATAMODE'
# Make sure your code supports all 3 layout types
if self.layout_type in {'DEFAULT', 'COMPACT'}:
layout.prop(item, "enabled_prop")
layout.label(item.name, icon=custom_icon)
# layout.prop(item, "name", text="", emboss=False, icon=custom_icon)
elif self.layout_type in {'GRID'}:
layout.alignment = 'CENTER'
layout.label("", icon = custom_icon)
class LIST_OT_ActionTraitNewItem(bpy.types.Operator):
# Add a new item to the list
bl_idname = "my_actiontraitlist.new_item"
bl_label = "Add a new item"
def execute(self, context):
trait = context.object.data
trait.my_actiontraitlist.add()
trait.actiontraitlist_index = len(trait.my_actiontraitlist) - 1
return{'FINISHED'}
class LIST_OT_ActionTraitDeleteItem(bpy.types.Operator):
# Delete the selected item from the list
bl_idname = "my_actiontraitlist.delete_item"
bl_label = "Deletes an item"
@classmethod
def poll(self, context):
""" Enable if there's something in the list """
trait = context.object.data
return len(trait.my_actiontraitlist) > 0
def execute(self, context):
trait = context.object.data
list = trait.my_actiontraitlist
index = trait.actiontraitlist_index
list.remove(index)
if index > 0:
index = index - 1
trait.actiontraitlist_index = index
return{'FINISHED'}
class LIST_OT_ActionTraitMoveItem(bpy.types.Operator):
# Move an item in the list
bl_idname = "my_actiontraitlist.move_item"
bl_label = "Move an item in the list"
direction = bpy.props.EnumProperty(
items=(
('UP', 'Up', ""),
('DOWN', 'Down', ""),))
@classmethod
def poll(self, context):
""" Enable if there's something in the list. """
trait = context.object.data
return len(trait.my_actiontraitlist) > 0
def move_index(self):
# Move index of an item render queue while clamping it
trait = context.object.data
index = trait.actiontraitlist_index
list_length = len(trait.my_actiontraitlist) - 1
new_index = 0
if self.direction == 'UP':
new_index = index - 1
elif self.direction == 'DOWN':
new_index = index + 1
new_index = max(0, min(new_index, list_length))
index = new_index
def execute(self, context):
trait = context.object.data
list = trait.my_actiontraitlist
index = trait.actiontraitlist_index
if self.direction == 'DOWN':
neighbor = index + 1
#queue.move(index,neighbor)
self.move_index()
elif self.direction == 'UP':
neighbor = index - 1
#queue.move(neighbor, index)
self.move_index()
else:
return{'CANCELLED'}
return{'FINISHED'}
def register():
bpy.utils.register_module(__name__)
def unregister():
bpy.utils.unregister_module(__name__)

View file

@ -5,7 +5,7 @@ import json
from bpy.types import Menu, Panel, UIList from bpy.types import Menu, Panel, UIList
from bpy.props import * from bpy.props import *
class ListAnimationTraitItem(bpy.types.PropertyGroup): class ListClipTraitItem(bpy.types.PropertyGroup):
# Group of properties representing an item in the list # Group of properties representing an item in the list
name = bpy.props.StringProperty( name = bpy.props.StringProperty(
name="Name", name="Name",
@ -43,7 +43,7 @@ class ListAnimationTraitItem(bpy.types.PropertyGroup):
default=False) default=False)
class MY_UL_AnimationTraitList(bpy.types.UIList): class MY_UL_ClipTraitList(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
# We could write some code to decide which icon to use here... # We could write some code to decide which icon to use here...
custom_icon = 'OBJECT_DATAMODE' custom_icon = 'OBJECT_DATAMODE'
@ -58,46 +58,46 @@ class MY_UL_AnimationTraitList(bpy.types.UIList):
layout.alignment = 'CENTER' layout.alignment = 'CENTER'
layout.label("", icon = custom_icon) layout.label("", icon = custom_icon)
class LIST_OT_AnimationTraitNewItem(bpy.types.Operator): class LIST_OT_ClipTraitNewItem(bpy.types.Operator):
# Add a new item to the list # Add a new item to the list
bl_idname = "my_animationtraitlist.new_item" bl_idname = "my_cliptraitlist.new_item"
bl_label = "Add a new item" bl_label = "Add a new item"
def execute(self, context): def execute(self, context):
trait = context.object.my_traitlist[context.object.traitlist_index] trait = context.object
trait.my_animationtraitlist.add() trait.my_cliptraitlist.add()
trait.animationtraitlist_index = len(trait.my_animationtraitlist) - 1 trait.cliptraitlist_index = len(trait.my_cliptraitlist) - 1
return{'FINISHED'} return{'FINISHED'}
class LIST_OT_AnimationTraitDeleteItem(bpy.types.Operator): class LIST_OT_ClipTraitDeleteItem(bpy.types.Operator):
# Delete the selected item from the list # Delete the selected item from the list
bl_idname = "my_animationtraitlist.delete_item" bl_idname = "my_cliptraitlist.delete_item"
bl_label = "Deletes an item" bl_label = "Deletes an item"
@classmethod @classmethod
def poll(self, context): def poll(self, context):
""" Enable if there's something in the list """ """ Enable if there's something in the list """
trait = context.object.my_traitlist[context.object.traitlist_index] trait = context.object
return len(trait.my_animationtraitlist) > 0 return len(trait.my_cliptraitlist) > 0
def execute(self, context): def execute(self, context):
trait = context.object.my_traitlist[context.object.traitlist_index] trait = context.object
list = trait.my_animationtraitlist list = trait.my_cliptraitlist
index = trait.animationtraitlist_index index = trait.cliptraitlist_index
list.remove(index) list.remove(index)
if index > 0: if index > 0:
index = index - 1 index = index - 1
trait.animationtraitlist_index = index trait.cliptraitlist_index = index
return{'FINISHED'} return{'FINISHED'}
class LIST_OT_AnimationTraitMoveItem(bpy.types.Operator): class LIST_OT_ClipTraitMoveItem(bpy.types.Operator):
# Move an item in the list # Move an item in the list
bl_idname = "my_animationtraitlist.move_item" bl_idname = "my_cliptraitlist.move_item"
bl_label = "Move an item in the list" bl_label = "Move an item in the list"
direction = bpy.props.EnumProperty( direction = bpy.props.EnumProperty(
items=( items=(
@ -107,15 +107,15 @@ class LIST_OT_AnimationTraitMoveItem(bpy.types.Operator):
@classmethod @classmethod
def poll(self, context): def poll(self, context):
""" Enable if there's something in the list. """ """ Enable if there's something in the list. """
trait = context.object.my_traitlist[context.object.traitlist_index] trait = context.object
return len(trait.my_animationtraitlist) > 0 return len(trait.my_cliptraitlist) > 0
def move_index(self): def move_index(self):
# Move index of an item render queue while clamping it # Move index of an item render queue while clamping it
trait = context.object.my_traitlist[context.object.traitlist_index] trait = context.object
index = trait.animationtraitlist_index index = trait.cliptraitlist_index
list_length = len(trait.my_animationtraitlist) - 1 list_length = len(trait.my_cliptraitlist) - 1
new_index = 0 new_index = 0
if self.direction == 'UP': if self.direction == 'UP':
@ -128,9 +128,9 @@ class LIST_OT_AnimationTraitMoveItem(bpy.types.Operator):
def execute(self, context): def execute(self, context):
trait = context.object.my_traitlist[context.object.traitlist_index] trait = context.object
list = trait.my_animationtraitlist list = trait.my_cliptraitlist
index = trait.animationtraitlist_index index = trait.cliptraitlist_index
if self.direction == 'DOWN': if self.direction == 'DOWN':
neighbor = index + 1 neighbor = index + 1

View file

@ -123,7 +123,12 @@ class Main {
f.write(""" f.write("""
kha.System.init({title: projectName, width: projectWidth, height: projectHeight, samplesPerPixel: projectSamplesPerPixel}, function() { kha.System.init({title: projectName, width: projectWidth, height: projectHeight, samplesPerPixel: projectSamplesPerPixel}, function() {
iron.App.init(function() { iron.App.init(function() {
new armory.Scene(projectScene); var raw = iron.data.Data.getSceneRaw(projectScene);
var scene = iron.Scene.create(raw);
scene.addTrait(new armory.trait.internal.PhysicsWorld(raw.gravity));
iron.App.notifyOnRender(function(g:kha.graphics4.Graphics) {
iron.Scene.active.renderFrame(g);
});
}); });
}); });
} }

View file

@ -8,240 +8,244 @@ import utils
import assets import assets
def add_irr_assets(output_file_irr): def add_irr_assets(output_file_irr):
assets.add(output_file_irr + '.arm') assets.add(output_file_irr + '.arm')
def add_rad_assets(output_file_rad, rad_format, num_mips): def add_rad_assets(output_file_rad, rad_format, num_mips):
assets.add(output_file_rad + '.' + rad_format) assets.add(output_file_rad + '.' + rad_format)
for i in range(0, num_mips): for i in range(0, num_mips):
assets.add(output_file_rad + '_' + str(i) + '.' + rad_format) assets.add(output_file_rad + '_' + str(i) + '.' + rad_format)
# Generate probes from environment map # Generate probes from environment map
def write_probes(image_filepath, disable_hdr, cached_num_mips, generate_radiance=True): def write_probes(image_filepath, disable_hdr, cached_num_mips, generate_radiance=True):
if not os.path.exists('build/compiled/Assets/envmaps'): if not os.path.exists('build/compiled/Assets/envmaps'):
os.makedirs('build/compiled/Assets/envmaps') os.makedirs('build/compiled/Assets/envmaps')
base_name = image_filepath.rsplit('/', 1)[1].rsplit('.', 1)[0] # Extract file name without extension base_name = image_filepath.rsplit('/', 1)[1].rsplit('.', 1)[0] # Extract file name without extension
# Assets to be generated # Assets to be generated
output_file_irr = 'build/compiled/Assets/envmaps/' + base_name + '_irradiance' output_file_irr = 'build/compiled/Assets/envmaps/' + base_name + '_irradiance'
if generate_radiance: if generate_radiance:
output_file_rad = 'build/compiled/Assets/envmaps/' + base_name + '_radiance' output_file_rad = 'build/compiled/Assets/envmaps/' + base_name + '_radiance'
rad_format = 'jpg' if disable_hdr else 'hdr' rad_format = 'jpg' if disable_hdr else 'hdr'
# Assume irradiance has to exist # Assume irradiance has to exist
if os.path.exists('build/compiled/Assets/envmaps/' + base_name + '_irradiance.arm'): if os.path.exists('build/compiled/Assets/envmaps/' + base_name + '_irradiance.arm'):
# Cached assets # Cached assets
add_irr_assets(output_file_irr) add_irr_assets(output_file_irr)
if generate_radiance: if generate_radiance:
add_rad_assets(output_file_rad, rad_format, cached_num_mips) add_rad_assets(output_file_rad, rad_format, cached_num_mips)
return cached_num_mips return cached_num_mips
# Get paths # Get paths
user_preferences = bpy.context.user_preferences user_preferences = bpy.context.user_preferences
addon_prefs = user_preferences.addons['armory'].preferences addon_prefs = user_preferences.addons['armory'].preferences
sdk_path = addon_prefs.sdk_path sdk_path = addon_prefs.sdk_path
if utils.get_os() == 'win': if utils.get_os() == 'win':
cmft_path = sdk_path + '/armory/tools/cmft/cmft.exe' cmft_path = sdk_path + '/armory/tools/cmft/cmft.exe'
kraffiti_path = sdk_path + '/kode_studio/KodeStudio-win32/resources/app/extensions/kha/Kha/Kore/Tools/kraffiti/kraffiti.exe' kraffiti_path = sdk_path + '/kode_studio/KodeStudio-win32/resources/app/extensions/kha/Kha/Kore/Tools/kraffiti/kraffiti.exe'
elif utils.get_os() == 'mac': elif utils.get_os() == 'mac':
cmft_path = sdk_path + '/armory/tools/cmft/cmft-osx' cmft_path = sdk_path + '/armory/tools/cmft/cmft-osx'
kraffiti_path = sdk_path + '/kode_studio/"Kode Studio.app"/Contents/Resources/app/extensions/kha/Kha/Kore/Tools/kraffiti/kraffiti-osx' kraffiti_path = sdk_path + '/kode_studio/"Kode Studio.app"/Contents/Resources/app/extensions/kha/Kha/Kore/Tools/kraffiti/kraffiti-osx'
else: else:
cmft_path = sdk_path + '/armory/tools/cmft/cmft-linux64' cmft_path = sdk_path + '/armory/tools/cmft/cmft-linux64'
kraffiti_path = sdk_path + '/kode_studio/KodeStudio-linux64/resources/app/extensions/kha/Kha/Kore/Tools/kraffiti/kraffiti-linux64' kraffiti_path = sdk_path + '/kode_studio/KodeStudio-linux64/resources/app/extensions/kha/Kha/Kore/Tools/kraffiti/kraffiti-linux64'
generated_files = [] generated_files = []
output_gama_numerator = '1.0' if disable_hdr else '2.2' output_gama_numerator = '1.0' if disable_hdr else '2.2'
input_file = utils.get_fp() + image_filepath #'Assets/' + image_name input_file = utils.get_fp() + image_filepath #'Assets/' + image_name
# Get input size # Get input size
output = subprocess.check_output([ \ output = subprocess.check_output([ \
kraffiti_path + \ kraffiti_path + \
' from=' + input_file + \ ' from=' + input_file + \
' donothing'], shell=True) ' donothing'], shell=True)
# #%ix%i # #%ix%i
image_w = str(output).split("'")[1] image_w = str(output).split("'")[1]
image_w = image_w[1:] image_w = image_w[1:]
image_w = image_w.split('x')[0] image_w = image_w.split('x')[0]
image_w = int(image_w) image_w = int(image_w)
image_h = image_w / 2 image_h = image_w / 2
# 4096 = 256 face - 6 mips - 1024 latlong # 4096 = 256 face - 6 mips - 1024 latlong
# 2048 = 128 face - 5 mips - 512 latlong # 2048 = 128 face - 5 mips - 512 latlong
# 1024 = 64 face - 4 mips # 1024 = 64 face - 4 mips
# 512 = 32 face - 3 mips # 512 = 32 face - 3 mips
# 256 = 16 face - 2 mips # 256 = 16 face - 2 mips
# 128 = 8 face - 1 mip # 128 = 8 face - 1 mip
mip_count = 1 mip_count = 1
num = 128 num = 128
while num < image_w: while num < image_w:
num *= 2 num *= 2
mip_count += 1 mip_count += 1
face_size = image_w / 16 face_size = image_w / 16
src_face_size = str(face_size) src_face_size = str(face_size)
dst_face_size = str(face_size) dst_face_size = str(face_size)
# Generate irradiance # Generate irradiance
gama_options = '' gama_options = ''
if disable_hdr: if disable_hdr:
gama_options = \ gama_options = \
' --inputGammaNumerator 2.2' + \ ' --inputGammaNumerator 2.2' + \
' --inputGammaDenominator 1.0' + \ ' --inputGammaDenominator 1.0' + \
' --outputGammaNumerator 1.0' + \ ' --outputGammaNumerator 1.0' + \
' --outputGammaDenominator ' + output_gama_numerator ' --outputGammaDenominator ' + output_gama_numerator
# Irradiance spherical harmonics # Irradiance spherical harmonics
subprocess.call([ \ subprocess.call([ \
cmft_path + \ cmft_path + \
' --input ' + input_file + \ ' --input ' + input_file + \
' --filter shcoeffs' + \ ' --filter shcoeffs' + \
#gama_options + \ #gama_options + \
' --outputNum 1' + \ ' --outputNum 1' + \
' --output0 ' + output_file_irr], shell=True) ' --output0 ' + output_file_irr], shell=True)
sh_to_json(output_file_irr) sh_to_json(output_file_irr)
# Non cached assets # Non cached assets
add_irr_assets(output_file_irr) add_irr_assets(output_file_irr)
# Mip-mapped radiance image # Mip-mapped radiance image
if generate_radiance == False: if generate_radiance == False:
return cached_num_mips return cached_num_mips
output = subprocess.check_output([ \ output = subprocess.check_output([ \
kraffiti_path + \ kraffiti_path + \
' from=' + input_file + \ ' from=' + input_file + \
' to=' + output_file_rad + '.' + rad_format + \ ' to=' + output_file_rad + '.' + rad_format + \
' format=' + rad_format + \ ' format=' + rad_format + \
' scale=0.5'], shell=True) ' scale=0.5'], shell=True)
subprocess.call([ \ subprocess.call([ \
cmft_path + \ cmft_path + \
' --input ' + input_file + \ ' --input ' + input_file + \
' --filter radiance' + \ ' --filter radiance' + \
' --dstFaceSize ' + dst_face_size + \ ' --dstFaceSize ' + dst_face_size + \
' --srcFaceSize ' + src_face_size + \ ' --srcFaceSize ' + src_face_size + \
' --excludeBase false' + \ ' --excludeBase false' + \
' --mipCount ' + str(mip_count) + \ ' --mipCount ' + str(mip_count) + \
' --glossScale 7' + \ ' --glossScale 7' + \
' --glossBias 3' + \ ' --glossBias 3' + \
' --lightingModel blinnbrdf' + \ ' --lightingModel blinnbrdf' + \
' --edgeFixup none' + \ ' --edgeFixup none' + \
' --numCpuProcessingThreads 4' + \ ' --numCpuProcessingThreads 4' + \
' --useOpenCL true' + \ ' --useOpenCL true' + \
' --clVendor anyGpuVendor' + \ ' --clVendor anyGpuVendor' + \
' --deviceType gpu' + \ ' --deviceType gpu' + \
' --deviceIndex 0' + \ ' --deviceIndex 0' + \
' --generateMipChain false' + \ ' --generateMipChain false' + \
' --inputGammaNumerator 2.2' + \ ' --inputGammaNumerator 2.2' + \
' --inputGammaDenominator 1.0' + \ ' --inputGammaDenominator 1.0' + \
' --outputGammaNumerator 1.0' + \ ' --outputGammaNumerator 1.0' + \
' --outputGammaDenominator ' + output_gama_numerator + \ ' --outputGammaDenominator ' + output_gama_numerator + \
' --outputNum 1' + \ ' --outputNum 1' + \
' --output0 ' + output_file_rad + \ ' --output0 ' + output_file_rad + \
' --output0params hdr,rgbe,latlong'], shell=True) ' --output0params hdr,rgbe,latlong'], shell=True)
# Remove size extensions in file name # Remove size extensions in file name
mip_w = int(face_size * 4) mip_w = int(face_size * 4)
mip_h = int(face_size * 2) mip_h = int(face_size * 2)
mip_base = output_file_rad + '_' mip_base = output_file_rad + '_'
mip_num = 0 mip_num = 0
while mip_w >= 32: while mip_w >= 32:
mip_name = mip_base + str(mip_num) mip_name = mip_base + str(mip_num)
os.rename( os.rename(
mip_name + '_' + str(mip_w) + 'x' + str(mip_h) + '.hdr', mip_name + '_' + str(mip_w) + 'x' + str(mip_h) + '.hdr',
mip_name + '.hdr') mip_name + '.hdr')
mip_w = int(mip_w / 2) mip_w = int(mip_w / 2)
mip_h = int(mip_h / 2) mip_h = int(mip_h / 2)
mip_num += 1 mip_num += 1
# Append mips # Append mips
for i in range(0, mip_count): for i in range(0, mip_count):
generated_files.append(output_file_rad + '_' + str(i)) generated_files.append(output_file_rad + '_' + str(i))
# Convert to jpgs # Convert to jpgs
if disable_hdr is True: if disable_hdr is True:
for f in generated_files: for f in generated_files:
subprocess.call([ \ subprocess.call([ \
kraffiti_path + \ kraffiti_path + \
' from=' + f + '.hdr' + \ ' from=' + f + '.hdr' + \
' to=' + f + '.jpg' + \ ' to=' + f + '.jpg' + \
' format=jpg'], shell=True) ' format=jpg'], shell=True)
os.remove(f + '.hdr') os.remove(f + '.hdr')
# Scale from (32x16 to 1x1> # Scale from (32x16 to 1x1>
for i in range (0, 5): for i in range (0, 5):
last = generated_files[-1] last = generated_files[-1]
out = output_file_rad + '_' + str(mip_count + i) out = output_file_rad + '_' + str(mip_count + i)
subprocess.call([ \ subprocess.call([ \
kraffiti_path + \ kraffiti_path + \
' from=' + last + '.' + rad_format + \ ' from=' + last + '.' + rad_format + \
' to=' + out + '.' + rad_format + \ ' to=' + out + '.' + rad_format + \
' scale=0.5' + \ ' scale=0.5' + \
' format=' + rad_format], shell=True) ' format=' + rad_format], shell=True)
generated_files.append(out) generated_files.append(out)
mip_count += 5 mip_count += 5
# Non cached assets # Non cached assets
add_rad_assets(output_file_rad, rad_format, mip_count) add_rad_assets(output_file_rad, rad_format, mip_count)
return mip_count return mip_count
# Parse sh coefs produced by cmft into json array # Parse sh coefs produced by cmft into json array
def sh_to_json(sh_file): def sh_to_json(sh_file):
sh_lines = open(sh_file + '.c').read().splitlines() sh_lines = open(sh_file + '.c').read().splitlines()
band0_line = sh_lines[5] band0_line = sh_lines[5]
band1_line = sh_lines[6] band1_line = sh_lines[6]
band2_line = sh_lines[7] band2_line = sh_lines[7]
irradiance_floats = [] irradiance_floats = []
parse_band_floats(irradiance_floats, band0_line) parse_band_floats(irradiance_floats, band0_line)
parse_band_floats(irradiance_floats, band1_line) parse_band_floats(irradiance_floats, band1_line)
parse_band_floats(irradiance_floats, band2_line) parse_band_floats(irradiance_floats, band2_line)
sh_json = {} sh_json = {}
sh_json['irradiance'] = irradiance_floats sh_json['irradiance'] = irradiance_floats
utils.write_arm(sh_file + '.arm', sh_json) utils.write_arm(sh_file + '.arm', sh_json)
# Clean up .c # Clean up .c
os.remove(sh_file + '.c') os.remove(sh_file + '.c')
def parse_band_floats(irradiance_floats, band_line): def parse_band_floats(irradiance_floats, band_line):
string_floats = re.findall(r'[-+]?\d*\.\d+|\d+', band_line) string_floats = re.findall(r'[-+]?\d*\.\d+|\d+', band_line)
string_floats = string_floats[1:] # Remove 'Band 0/1/2' number string_floats = string_floats[1:] # Remove 'Band 0/1/2' number
for s in string_floats: for s in string_floats:
irradiance_floats.append(float(s)) irradiance_floats.append(float(s))
def write_sky_irradiance(base_name): def write_sky_irradiance(base_name):
# Predefined fake spherical harmonics for now # Predefined fake spherical harmonics for now
irradiance_floats = [1.0281457342829743,1.1617608778901902,1.3886220898440544,-0.13044863139637752,-0.2794659158733846,-0.5736106907295643,0.04065421813873111,0.0434367391348577,0.03567450494792305,0.10964557605577738,0.1129839085793664,0.11261660812141877,-0.08271974283263238,-0.08068091195339556,-0.06432614970480094,-0.12517787967665814,-0.11638582546310804,-0.09743696224655113,0.20068697715947176,0.2158788783296805,0.2109374396869599,0.19636637427150455,0.19445523113118082,0.17825330699680575,0.31440860839538637,0.33041120060402407,0.30867788630062676] irradiance_floats = [1.0281457342829743,1.1617608778901902,1.3886220898440544,-0.13044863139637752,-0.2794659158733846,-0.5736106907295643,0.04065421813873111,0.0434367391348577,0.03567450494792305,0.10964557605577738,0.1129839085793664,0.11261660812141877,-0.08271974283263238,-0.08068091195339556,-0.06432614970480094,-0.12517787967665814,-0.11638582546310804,-0.09743696224655113,0.20068697715947176,0.2158788783296805,0.2109374396869599,0.19636637427150455,0.19445523113118082,0.17825330699680575,0.31440860839538637,0.33041120060402407,0.30867788630062676]
# Hosek
if not os.path.exists('build/compiled/Assets/envmaps'): # irradiance_floats = [1.5519331988822218,2.3352207154503266,2.997277451988076,0.2673894962434794,0.4305630474135794,0.11331825259716752,-0.04453633521758638,-0.038753175134160295,-0.021302768541875794,0.00055858020486499,0.000371654770334503,0.000126606145406403,-0.000135708721978705,-0.000787399554583089,-0.001550090690860059,0.021947399048903773,0.05453650591711572,0.08783641266630278,0.17053593578630663,0.14734127083304463,0.07775404698816404,-2.6924363189795e-05,-7.9350169701934e-05,-7.559914435231e-05,0.27035455385870993,0.23122918445556914,0.12158817295211832]
os.makedirs('build/compiled/Assets/envmaps') # for i in range(0, len(irradiance_floats)):
# irradiance_floats[i] /= 2;
output_file = 'build/compiled/Assets/envmaps/' + base_name + '_irradiance'
sh_json = {}
sh_json['irradiance'] = irradiance_floats
utils.write_arm(output_file + '.arm', sh_json)
assets.add(output_file + '.arm') if not os.path.exists('build/compiled/Assets/envmaps'):
os.makedirs('build/compiled/Assets/envmaps')
output_file = 'build/compiled/Assets/envmaps/' + base_name + '_irradiance'
sh_json = {}
sh_json['irradiance'] = irradiance_floats
utils.write_arm(output_file + '.arm', sh_json)
assets.add(output_file + '.arm')
def write_color_irradiance(base_name, col): def write_color_irradiance(base_name, col):
# Constant color # Constant color
irradiance_floats = [col[0], col[1], col[2]] irradiance_floats = [col[0], col[1], col[2]]
for i in range(0, 24): for i in range(0, 24):
irradiance_floats.append(0.0) irradiance_floats.append(0.0)
if not os.path.exists('build/compiled/Assets/envmaps'): if not os.path.exists('build/compiled/Assets/envmaps'):
os.makedirs('build/compiled/Assets/envmaps') os.makedirs('build/compiled/Assets/envmaps')
output_file = 'build/compiled/Assets/envmaps/' + base_name + '_irradiance' output_file = 'build/compiled/Assets/envmaps/' + base_name + '_irradiance'
sh_json = {} sh_json = {}
sh_json['irradiance'] = irradiance_floats sh_json['irradiance'] = irradiance_floats
utils.write_arm(output_file + '.arm', sh_json) utils.write_arm(output_file + '.arm', sh_json)
assets.add(output_file + '.arm') assets.add(output_file + '.arm')

View file

@ -22,7 +22,7 @@ vec2 unpackFloat(float f) {
void main() { void main() {
vec2 tc = texCoord * ssrTextureScale; vec2 tc = texCoord * ssrTextureScale;
float roughness = unpackFloat(texture(gbuffer0, texCoord).b).x; float roughness = unpackFloat(texture(gbuffer0, texCoord).b).y;
if (roughness == 0.0) { if (roughness == 0.0) {
outColor = texture(tex, tc); outColor = texture(tex, tc);
// outColor = vec4(0.0, 0.0, 0.0, 1.0); // outColor = vec4(0.0, 0.0, 0.0, 1.0);

View file

@ -60,17 +60,19 @@ vec3 getPos(float depth) {
// const vec3 compoFogColor = vec3(0.5, 0.6, 0.7); // const vec3 compoFogColor = vec3(0.5, 0.6, 0.7);
// const float compoFogAmountA = 1.0; // b = 0.01 // const float compoFogAmountA = 1.0; // b = 0.01
// const float compoFogAmountB = 1.0; // c = 0.1 // const float compoFogAmountB = 1.0; // c = 0.1
vec3 applyFog(vec3 rgb, // original color of the pixel // vec3 applyFog(vec3 rgb, // original color of the pixel
float distance, // camera to point distance // float distance, // camera to point distance
vec3 rayOri, // camera position // vec3 rayOri, // camera position
vec3 rayDir) { // camera to point vector // vec3 rayDir) { // camera to point vector
float fogAmount = compoFogAmountB * exp(-rayOri.y * compoFogAmountA) * (1.0 - exp(-distance * rayDir.y * compoFogAmountA)) / rayDir.y; // float fogAmount = compoFogAmountB * exp(-rayOri.y * compoFogAmountA) * (1.0 - exp(-distance * rayDir.y * compoFogAmountA)) / rayDir.y;
return mix(rgb, compoFogColor, fogAmount); // return mix(rgb, compoFogColor, fogAmount);
}
// vec3 applyFog(vec3 rgb, float distance) {
// float fogAmount = 1.0 - exp(-distance * compoFogAmountA);
// return mix(rgb, compoFogColor, fogAmount);
// } // }
vec3 applyFog(vec3 rgb, float distance) {
// float fogAmount = 1.0 - exp(-distance * compoFogAmountA);
float fogAmount = 1.0 - exp(-distance * 0.0055);
return mix(rgb, vec3(0.4, 0.7, 0.2), fogAmount);
// return mix(rgb, compoFogColor, fogAmount);
}
#endif #endif
float vignette() { float vignette() {
@ -78,10 +80,11 @@ float vignette() {
// dist = smoothstep(vignout + (fstop / vignfade), vignin + (fstop / vignfade), dist); // dist = smoothstep(vignout + (fstop / vignfade), vignin + (fstop / vignfade), dist);
// return clamp(dist, 0.0, 1.0); // return clamp(dist, 0.0, 1.0);
// vignetting from iq // vignetting from iq
return 0.4 + 0.6 * pow(16.0 * texCoord.x * texCoord.y * (1.0 - texCoord.x) * (1.0 - texCoord.y), 0.2); // return 0.4 + 0.6 * pow(16.0 * texCoord.x * texCoord.y * (1.0 - texCoord.x) * (1.0 - texCoord.y), 0.2);
return 0.3 + 0.7 * pow(16.0 * texCoord.x * texCoord.y * (1.0 - texCoord.x) * (1.0 - texCoord.y), 0.2);
} }
#ifdef _CompoDOF // #ifdef _CompoDOF
vec3 sampleBox(float size) { vec3 sampleBox(float size) {
vec3 color = vec3(texture(tex, vec2(texCoord.x - size, texCoord.y - size)).rgb) * 0.075; vec3 color = vec3(texture(tex, vec2(texCoord.x - size, texCoord.y - size)).rgb) * 0.075;
color += texture(tex, vec2(texCoord.x, texCoord.y - size)).rgb * 0.1; color += texture(tex, vec2(texCoord.x, texCoord.y - size)).rgb * 0.1;
@ -94,7 +97,7 @@ vec3 sampleBox(float size) {
color += texture(tex, vec2(texCoord.x + size, texCoord.y + size)).rgb * 0.075; color += texture(tex, vec2(texCoord.x + size, texCoord.y + size)).rgb * 0.075;
return color; return color;
} }
#endif // #endif
float linearize(float depth) { float linearize(float depth) {
return -cameraPlane.y * cameraPlane.x / (depth * (cameraPlane.y - cameraPlane.x) - cameraPlane.y); return -cameraPlane.y * cameraPlane.x / (depth * (cameraPlane.y - cameraPlane.x) - cameraPlane.y);
@ -232,19 +235,25 @@ void main() {
#endif #endif
#ifdef _CompoDOF #ifdef _CompoDOF
float linDepth = linearize(depth); if (depth < 1.0) {
float blur_amount = abs(linDepth - compoDOFDistance) / cameraPlane.y; float linDepth = linearize(depth);
blur_amount = clamp(blur_amount, 0.0, 1.0); float blur_amount = abs(linDepth - compoDOFDistance) / cameraPlane.y;
float blurSize = compoDOFSize * 10000.0 * blur_amount; // float blur_amount = abs(linDepth - 4.0);
vec3 blurredColor = 0.75 * sampleBox(blurSize * 0.5) + 0.25 * sampleBox(blurSize * 1.0); float blurSize = compoDOFSize * blur_amount;
col.rgb *= (1.0 - blur_amount) + blurredColor * blur_amount; // float blurSize = 0.0005 * blur_amount;
col.rgb = 0.75 * sampleBox(blurSize * 0.5) + 0.25 * sampleBox(blurSize * 1.0);
}
#endif #endif
#ifdef _CompoFog #ifdef _CompoFog
vec3 pos = getPos(depth); // if (depth < 1.0) {
float dist = distance(pos, eye); // vec3 pos = getPos(depth);
vec3 eyedir = eyeLook;// normalize(eye + pos); // float dist = distance(pos, eye);
col.rgb = applyFog(col.rgb, dist, eye, eyedir); float dist = linearize(depth);
// vec3 eyedir = eyeLook;// normalize(eye + pos);
// col.rgb = applyFog(col.rgb, dist, eye, eyedir);
col.rgb = applyFog(col.rgb, dist);
// }
#endif #endif
#ifdef _CompoGlare #ifdef _CompoGlare

View file

@ -48,7 +48,7 @@ void main() {
// vec3 p = getPos(depth); // vec3 p = getPos(depth);
// vec3 baseColor = g1.rgb; // vec3 baseColor = g1.rgb;
// vec2 roughmet = unpackFloat(g1.a); // vec2 metrough = unpackFloat(g1.a);
// float roughness = roughmet.x; // float metalness = metrough.x;
// float metalness = roughmet.y; // float roughness = metrough.y;
} }

View file

@ -31,54 +31,73 @@ in vec3 nor;
uniform mat4 LWVP; uniform mat4 LWVP;
#ifdef _Skinning #ifdef _Skinning
uniform float skinBones[skinMaxBones * 12]; uniform float skinBones[skinMaxBones * 8];
#endif #endif
// out vec4 position; // out vec4 position;
#ifdef _Skinning #ifdef _Skinning
mat4 getBoneMat(const int boneIndex) { void getSkinningDualQuat(vec4 weights, out vec4 A, inout vec4 B) {
vec4 v0 = vec4(skinBones[boneIndex * 12 + 0], // Retrieve the real and dual part of the dual-quaternions
skinBones[boneIndex * 12 + 1], mat4 matA, matB;
skinBones[boneIndex * 12 + 2], matA[0][0] = skinBones[int(bone.x) * 8 + 0];
skinBones[boneIndex * 12 + 3]); matA[0][1] = skinBones[int(bone.x) * 8 + 1];
vec4 v1 = vec4(skinBones[boneIndex * 12 + 4], matA[0][2] = skinBones[int(bone.x) * 8 + 2];
skinBones[boneIndex * 12 + 5], matA[0][3] = skinBones[int(bone.x) * 8 + 3];
skinBones[boneIndex * 12 + 6], matB[0][0] = skinBones[int(bone.x) * 8 + 4];
skinBones[boneIndex * 12 + 7]); matB[0][1] = skinBones[int(bone.x) * 8 + 5];
vec4 v2 = vec4(skinBones[boneIndex * 12 + 8], matB[0][2] = skinBones[int(bone.x) * 8 + 6];
skinBones[boneIndex * 12 + 9], matB[0][3] = skinBones[int(bone.x) * 8 + 7];
skinBones[boneIndex * 12 + 10], matA[1][0] = skinBones[int(bone.y) * 8 + 0];
skinBones[boneIndex * 12 + 11]); matA[1][1] = skinBones[int(bone.y) * 8 + 1];
return mat4(v0.x, v0.y, v0.z, v0.w, matA[1][2] = skinBones[int(bone.y) * 8 + 2];
v1.x, v1.y, v1.z, v1.w, matA[1][3] = skinBones[int(bone.y) * 8 + 3];
v2.x, v2.y, v2.z, v2.w, matB[1][0] = skinBones[int(bone.y) * 8 + 4];
0, 0, 0, 1); matB[1][1] = skinBones[int(bone.y) * 8 + 5];
} matB[1][2] = skinBones[int(bone.y) * 8 + 6];
matB[1][3] = skinBones[int(bone.y) * 8 + 7];
mat4 getSkinningMat() { matA[2][0] = skinBones[int(bone.z) * 8 + 0];
return weight.x * getBoneMat(int(bone.x)) + matA[2][1] = skinBones[int(bone.z) * 8 + 1];
weight.y * getBoneMat(int(bone.y)) + matA[2][2] = skinBones[int(bone.z) * 8 + 2];
weight.z * getBoneMat(int(bone.z)) + matA[2][3] = skinBones[int(bone.z) * 8 + 3];
weight.w * getBoneMat(int(bone.w)); matB[2][0] = skinBones[int(bone.z) * 8 + 4];
} matB[2][1] = skinBones[int(bone.z) * 8 + 5];
matB[2][2] = skinBones[int(bone.z) * 8 + 6];
mat3 getSkinningMatVec(const mat4 skinningMat) { matB[2][3] = skinBones[int(bone.z) * 8 + 7];
return mat3(skinningMat[0].xyz, skinningMat[1].xyz, skinningMat[2].xyz); matA[3][0] = skinBones[int(bone.w) * 8 + 0];
matA[3][1] = skinBones[int(bone.w) * 8 + 1];
matA[3][2] = skinBones[int(bone.w) * 8 + 2];
matA[3][3] = skinBones[int(bone.w) * 8 + 3];
matB[3][0] = skinBones[int(bone.w) * 8 + 4];
matB[3][1] = skinBones[int(bone.w) * 8 + 5];
matB[3][2] = skinBones[int(bone.w) * 8 + 6];
matB[3][3] = skinBones[int(bone.w) * 8 + 7];
// Handles antipodality by sticking joints in the same neighbourhood
// weights.xyz *= sign(matA[3] * mat3x4(matA)).xyz;
weights.xyz *= sign(matA[3] * matA).xyz;
// Apply weights
A = matA * weights; // Real part
B = matB * weights; // Dual part
// Normalize
float invNormA = 1.0 / length(A);
A *= invNormA;
B *= invNormA;
} }
#endif #endif
void main() { void main() {
#ifdef _Instancing #ifdef _Instancing
vec4 sPos = (vec4(pos + off, 1.0)); vec4 sPos = (vec4(pos + off, 1.0));
#else #else
vec4 sPos = (vec4(pos, 1.0)); vec4 sPos = (vec4(pos, 1.0));
#endif #endif
#ifdef _Skinning #ifdef _Skinning
mat4 skinningMat = getSkinningMat(); vec4 skinA;
mat3 skinningMatVec = getSkinningMatVec(skinningMat); vec4 skinB;
sPos = sPos * skinningMat; getSkinningDualQuat(weight, skinA, skinB);
sPos.xyz += 2.0 * cross(skinA.xyz, cross(skinA.xyz, sPos.xyz) + skinA.w * sPos.xyz); // Rotate
sPos.xyz += 2.0 * (skinA.w * skinB.xyz - skinB.w * skinA.xyz + cross(skinA.xyz, skinB.xyz)); // Translate
#endif #endif
gl_Position = LWVP * sPos; gl_Position = LWVP * sPos;

View file

@ -77,8 +77,8 @@ in vec4 matColor;
#endif #endif
float packFloat(float f1, float f2) { float packFloat(float f1, float f2) {
int index = int(f1 * 1000); float index = floor(f1 * 1000.0); // Temporary
float alpha = f2 == 0.0 ? f2 : (f2 - 0.0001); float alpha = clamp(f2, 0.0, 1.0 - 0.001);
return index + alpha; return index + alpha;
} }
@ -179,7 +179,7 @@ float parallaxShadow(vec3 L, vec2 initialTexCoord, float initialHeight) {
// ... // ...
// Shadowing factor should be 1 if there were no points under the surface // Shadowing factor should be 1 if there were no points under the surface
if(numSamplesUnderSurface < 1) shadowMultiplier = 1; if (numSamplesUnderSurface < 1) shadowMultiplier = 1;
else shadowMultiplier = 1.0 - shadowMultiplier; else shadowMultiplier = 1.0 - shadowMultiplier;
} }
return shadowMultiplier; return shadowMultiplier;
@ -259,9 +259,9 @@ void main() {
} }
if (dist > 0) mask_probe = 0; if (dist > 0) mask_probe = 0;
} }
outColor[0] = vec4(n.xy, packFloat(roughness, metalness), mask_probe); outColor[0] = vec4(n.xy, packFloat(metalness, roughness), mask_probe);
#else #else
outColor[0] = vec4(n.xy, packFloat(roughness, metalness), mask); outColor[0] = vec4(n.xy, packFloat(metalness, roughness), mask);
#endif #endif
outColor[1] = vec4(baseColor.rgb, occ); outColor[1] = vec4(baseColor.rgb, occ);

View file

@ -45,7 +45,8 @@ uniform vec4 albedo_color;
uniform mat4 W; uniform mat4 W;
#endif #endif
#ifdef _Skinning #ifdef _Skinning
uniform float skinBones[skinMaxBones * 12]; // Default to 50 // uniform float skinBones[skinMaxBones * 12]; // Defaults to 50
uniform float skinBones[skinMaxBones * 8]; // Dual quat
#endif #endif
#ifdef _Probes #ifdef _Probes
uniform mat4 W; // TODO: Conflicts with _HeightTex uniform mat4 W; // TODO: Conflicts with _HeightTex
@ -78,60 +79,79 @@ out vec4 matColor;
#ifdef _Skinning #ifdef _Skinning
// Geometric Skinning with Approximate Dual Quaternion Blending, Kavan // Geometric Skinning with Approximate Dual Quaternion Blending, Kavan
// Based on https://github.com/tcoppex/aer-engine/blob/master/demos/aura/data/shaders/Skinning.glsl // Based on https://github.com/tcoppex/aer-engine/blob/master/demos/aura/data/shaders/Skinning.glsl
// void getSkinningDualQuat(vec4 weights, inout vec3 v, inout vec3 n) { void getSkinningDualQuat(vec4 weights, out vec4 A, inout vec4 B) {
// // Retrieve the real and dual part of the dual-quaternions // Retrieve the real and dual part of the dual-quaternions
// mat4 matA, matB; mat4 matA, matB;
// vec4 indices = vec4(2.0) * bone; matA[0][0] = skinBones[int(bone.x) * 8 + 0];
// matA[0] = skinBones[int(indices.x) + 0]; matA[0][1] = skinBones[int(bone.x) * 8 + 1];
// matB[0] = skinBones[int(indices.x) + 1]; matA[0][2] = skinBones[int(bone.x) * 8 + 2];
// matA[1] = skinBones[int(indices.y) + 0]; matA[0][3] = skinBones[int(bone.x) * 8 + 3];
// matB[1] = skinBones[int(indices.y) + 1]; matB[0][0] = skinBones[int(bone.x) * 8 + 4];
// matA[2] = skinBones[int(indices.z) + 0]; matB[0][1] = skinBones[int(bone.x) * 8 + 5];
// matB[2] = skinBones[int(indices.z) + 1]; matB[0][2] = skinBones[int(bone.x) * 8 + 6];
// matA[3] = skinBones[int(indices.w) + 0]; matB[0][3] = skinBones[int(bone.x) * 8 + 7];
// matB[3] = skinBones[int(indices.w) + 1]; matA[1][0] = skinBones[int(bone.y) * 8 + 0];
// // Handles antipodality by sticking joints in the same neighbourhood matA[1][1] = skinBones[int(bone.y) * 8 + 1];
// weights.xyz *= sign(matA[3] * mat3x4(matA)); matA[1][2] = skinBones[int(bone.y) * 8 + 2];
// // Apply weights matA[1][3] = skinBones[int(bone.y) * 8 + 3];
// vec4 A = matA * weights; // Real part matB[1][0] = skinBones[int(bone.y) * 8 + 4];
// vec4 B = matB * weights; // Dual part matB[1][1] = skinBones[int(bone.y) * 8 + 5];
// // Normalize matB[1][2] = skinBones[int(bone.y) * 8 + 6];
// float invNormA = 1.0 / length(A); matB[1][3] = skinBones[int(bone.y) * 8 + 7];
// A *= invNormA; matA[2][0] = skinBones[int(bone.z) * 8 + 0];
// B *= invNormA; matA[2][1] = skinBones[int(bone.z) * 8 + 1];
// // Position matA[2][2] = skinBones[int(bone.z) * 8 + 2];
// v += 2.0 * cross(A.xyz, cross(A.xyz, v) + A.w * v); // Rotate matA[2][3] = skinBones[int(bone.z) * 8 + 3];
// v += 2.0 * (A.w * B.xyz - B.w * A.xyz + cross(A.xyz, B.xyz)); // Translate matB[2][0] = skinBones[int(bone.z) * 8 + 4];
// // Normal matB[2][1] = skinBones[int(bone.z) * 8 + 5];
// n += 2.0 * cross(A.xyz, cross(A.xyz, n) + A.w * n); matB[2][2] = skinBones[int(bone.z) * 8 + 6];
matB[2][3] = skinBones[int(bone.z) * 8 + 7];
matA[3][0] = skinBones[int(bone.w) * 8 + 0];
matA[3][1] = skinBones[int(bone.w) * 8 + 1];
matA[3][2] = skinBones[int(bone.w) * 8 + 2];
matA[3][3] = skinBones[int(bone.w) * 8 + 3];
matB[3][0] = skinBones[int(bone.w) * 8 + 4];
matB[3][1] = skinBones[int(bone.w) * 8 + 5];
matB[3][2] = skinBones[int(bone.w) * 8 + 6];
matB[3][3] = skinBones[int(bone.w) * 8 + 7];
// Handles antipodality by sticking joints in the same neighbourhood
// weights.xyz *= sign(matA[3] * mat3x4(matA)).xyz;
weights.xyz *= sign(matA[3] * matA).xyz;
// Apply weights
A = matA * weights; // Real part
B = matB * weights; // Dual part
// Normalize
float invNormA = 1.0 / length(A);
A *= invNormA;
B *= invNormA;
}
// mat4 getBoneMat(const int boneIndex) {
// vec4 v0 = vec4(skinBones[boneIndex * 12 + 0],
// skinBones[boneIndex * 12 + 1],
// skinBones[boneIndex * 12 + 2],
// skinBones[boneIndex * 12 + 3]);
// vec4 v1 = vec4(skinBones[boneIndex * 12 + 4],
// skinBones[boneIndex * 12 + 5],
// skinBones[boneIndex * 12 + 6],
// skinBones[boneIndex * 12 + 7]);
// vec4 v2 = vec4(skinBones[boneIndex * 12 + 8],
// skinBones[boneIndex * 12 + 9],
// skinBones[boneIndex * 12 + 10],
// skinBones[boneIndex * 12 + 11]);
// return mat4(v0.x, v0.y, v0.z, v0.w,
// v1.x, v1.y, v1.z, v1.w,
// v2.x, v2.y, v2.z, v2.w,
// 0, 0, 0, 1);
// }
// mat4 getSkinningMat() {
// return weight.x * getBoneMat(int(bone.x)) +
// weight.y * getBoneMat(int(bone.y)) +
// weight.z * getBoneMat(int(bone.z)) +
// weight.w * getBoneMat(int(bone.w));
// }
// mat3 getSkinningMatVec(const mat4 skinningMat) {
// return mat3(skinningMat[0].xyz, skinningMat[1].xyz, skinningMat[2].xyz);
// } // }
mat4 getBoneMat(const int boneIndex) {
vec4 v0 = vec4(skinBones[boneIndex * 12 + 0],
skinBones[boneIndex * 12 + 1],
skinBones[boneIndex * 12 + 2],
skinBones[boneIndex * 12 + 3]);
vec4 v1 = vec4(skinBones[boneIndex * 12 + 4],
skinBones[boneIndex * 12 + 5],
skinBones[boneIndex * 12 + 6],
skinBones[boneIndex * 12 + 7]);
vec4 v2 = vec4(skinBones[boneIndex * 12 + 8],
skinBones[boneIndex * 12 + 9],
skinBones[boneIndex * 12 + 10],
skinBones[boneIndex * 12 + 11]);
return mat4(v0.x, v0.y, v0.z, v0.w,
v1.x, v1.y, v1.z, v1.w,
v2.x, v2.y, v2.z, v2.w,
0, 0, 0, 1);
}
mat4 getSkinningMat() {
return weight.x * getBoneMat(int(bone.x)) +
weight.y * getBoneMat(int(bone.y)) +
weight.z * getBoneMat(int(bone.z)) +
weight.w * getBoneMat(int(bone.w));
}
mat3 getSkinningMatVec(const mat4 skinningMat) {
return mat3(skinningMat[0].xyz, skinningMat[1].xyz, skinningMat[2].xyz);
}
#endif #endif
void main() { void main() {
@ -142,9 +162,19 @@ void main() {
vec4 sPos = (vec4(pos, 1.0)); vec4 sPos = (vec4(pos, 1.0));
#endif #endif
#ifdef _Skinning #ifdef _Skinning
mat4 skinningMat = getSkinningMat(); // mat4 skinningMat = getSkinningMat();
mat3 skinningMatVec = getSkinningMatVec(skinningMat); // mat3 skinningMatVec = getSkinningMatVec(skinningMat);
sPos = sPos * skinningMat; // sPos = sPos * skinningMat;
// vec3 _normal = normalize(mat3(N) * (nor * skinningMatVec));
vec4 skinA;
vec4 skinB;
getSkinningDualQuat(weight, skinA, skinB);
sPos.xyz += 2.0 * cross(skinA.xyz, cross(skinA.xyz, sPos.xyz) + skinA.w * sPos.xyz); // Rotate
sPos.xyz += 2.0 * (skinA.w * skinB.xyz - skinB.w * skinA.xyz + cross(skinA.xyz, skinB.xyz)); // Translate
vec3 _normal = normalize(mat3(N) * (nor + 2.0 * cross(skinA.xyz, cross(skinA.xyz, nor) + skinA.w * nor)));
#else
vec3 _normal = normalize(mat3(N) * nor);
#endif #endif
#ifdef _Probes #ifdef _Probes
@ -173,12 +203,6 @@ void main() {
texCoord = tex; texCoord = tex;
#endif #endif
#ifdef _Skinning
vec3 _normal = normalize(mat3(N) * (nor * skinningMatVec));
#else
vec3 _normal = normalize(mat3(N) * nor);
#endif
matColor = albedo_color; matColor = albedo_color;
#ifdef _VCols #ifdef _VCols

View file

@ -54,34 +54,51 @@ out vec3 eyeDir;
#endif #endif
#ifdef _Skinning #ifdef _Skinning
mat4 getBoneMat(const int boneIndex) { void getSkinningDualQuat(vec4 weights, out vec4 A, inout vec4 B) {
vec4 v0 = vec4(skinBones[boneIndex * 12 + 0], // Retrieve the real and dual part of the dual-quaternions
skinBones[boneIndex * 12 + 1], mat4 matA, matB;
skinBones[boneIndex * 12 + 2], matA[0][0] = skinBones[int(bone.x) * 8 + 0];
skinBones[boneIndex * 12 + 3]); matA[0][1] = skinBones[int(bone.x) * 8 + 1];
vec4 v1 = vec4(skinBones[boneIndex * 12 + 4], matA[0][2] = skinBones[int(bone.x) * 8 + 2];
skinBones[boneIndex * 12 + 5], matA[0][3] = skinBones[int(bone.x) * 8 + 3];
skinBones[boneIndex * 12 + 6], matB[0][0] = skinBones[int(bone.x) * 8 + 4];
skinBones[boneIndex * 12 + 7]); matB[0][1] = skinBones[int(bone.x) * 8 + 5];
vec4 v2 = vec4(skinBones[boneIndex * 12 + 8], matB[0][2] = skinBones[int(bone.x) * 8 + 6];
skinBones[boneIndex * 12 + 9], matB[0][3] = skinBones[int(bone.x) * 8 + 7];
skinBones[boneIndex * 12 + 10], matA[1][0] = skinBones[int(bone.y) * 8 + 0];
skinBones[boneIndex * 12 + 11]); matA[1][1] = skinBones[int(bone.y) * 8 + 1];
return mat4(v0.x, v0.y, v0.z, v0.w, matA[1][2] = skinBones[int(bone.y) * 8 + 2];
v1.x, v1.y, v1.z, v1.w, matA[1][3] = skinBones[int(bone.y) * 8 + 3];
v2.x, v2.y, v2.z, v2.w, matB[1][0] = skinBones[int(bone.y) * 8 + 4];
0, 0, 0, 1); matB[1][1] = skinBones[int(bone.y) * 8 + 5];
} matB[1][2] = skinBones[int(bone.y) * 8 + 6];
matB[1][3] = skinBones[int(bone.y) * 8 + 7];
mat4 getSkinningMat() { matA[2][0] = skinBones[int(bone.z) * 8 + 0];
return weight.x * getBoneMat(int(bone.x)) + matA[2][1] = skinBones[int(bone.z) * 8 + 1];
weight.y * getBoneMat(int(bone.y)) + matA[2][2] = skinBones[int(bone.z) * 8 + 2];
weight.z * getBoneMat(int(bone.z)) + matA[2][3] = skinBones[int(bone.z) * 8 + 3];
weight.w * getBoneMat(int(bone.w)); matB[2][0] = skinBones[int(bone.z) * 8 + 4];
} matB[2][1] = skinBones[int(bone.z) * 8 + 5];
matB[2][2] = skinBones[int(bone.z) * 8 + 6];
mat3 getSkinningMatVec(const mat4 skinningMat) { matB[2][3] = skinBones[int(bone.z) * 8 + 7];
return mat3(skinningMat[0].xyz, skinningMat[1].xyz, skinningMat[2].xyz); matA[3][0] = skinBones[int(bone.w) * 8 + 0];
matA[3][1] = skinBones[int(bone.w) * 8 + 1];
matA[3][2] = skinBones[int(bone.w) * 8 + 2];
matA[3][3] = skinBones[int(bone.w) * 8 + 3];
matB[3][0] = skinBones[int(bone.w) * 8 + 4];
matB[3][1] = skinBones[int(bone.w) * 8 + 5];
matB[3][2] = skinBones[int(bone.w) * 8 + 6];
matB[3][3] = skinBones[int(bone.w) * 8 + 7];
// Handles antipodality by sticking joints in the same neighbourhood
// weights.xyz *= sign(matA[3] * mat3x4(matA)).xyz;
weights.xyz *= sign(matA[3] * matA).xyz;
// Apply weights
A = matA * weights; // Real part
B = matB * weights; // Dual part
// Normalize
float invNormA = 1.0 / length(A);
A *= invNormA;
B *= invNormA;
} }
#endif #endif
@ -92,11 +109,18 @@ void main() {
#else #else
vec4 sPos = (vec4(pos, 1.0)); vec4 sPos = (vec4(pos, 1.0));
#endif #endif
#ifdef _Skinning #ifdef _Skinning
mat4 skinningMat = getSkinningMat(); vec4 skinA;
mat3 skinningMatVec = getSkinningMatVec(skinningMat); vec4 skinB;
sPos = sPos * skinningMat; getSkinningDualQuat(weight, skinA, skinB);
sPos.xyz += 2.0 * cross(skinA.xyz, cross(skinA.xyz, sPos.xyz) + skinA.w * sPos.xyz); // Rotate
sPos.xyz += 2.0 * (skinA.w * skinB.xyz - skinB.w * skinA.xyz + cross(skinA.xyz, skinB.xyz)); // Translate
vec3 _normal = normalize(mat3(N) * (nor + 2.0 * cross(skinA.xyz, cross(skinA.xyz, nor) + skinA.w * nor)));
#else
vec3 _normal = normalize(mat3(N) * nor);
#endif #endif
lPos = LWVP * sPos; lPos = LWVP * sPos;
mat4 WV = V * W; mat4 WV = V * W;
@ -117,12 +141,6 @@ void main() {
texCoord = tex; texCoord = tex;
#endif #endif
#ifdef _Skinning
vec3 _normal = normalize(mat3(N) * (nor * skinningMatVec));
#else
vec3 _normal = normalize(mat3(N) * nor);
#endif
matColor = albedo_color; matColor = albedo_color;
#ifdef _VCols #ifdef _VCols

View file

@ -38,34 +38,51 @@ uniform mat4 LWVP;
// out vec4 position; // out vec4 position;
#ifdef _Skinning #ifdef _Skinning
mat4 getBoneMat(const int boneIndex) { void getSkinningDualQuat(vec4 weights, out vec4 A, inout vec4 B) {
vec4 v0 = vec4(skinBones[boneIndex * 12 + 0], // Retrieve the real and dual part of the dual-quaternions
skinBones[boneIndex * 12 + 1], mat4 matA, matB;
skinBones[boneIndex * 12 + 2], matA[0][0] = skinBones[int(bone.x) * 8 + 0];
skinBones[boneIndex * 12 + 3]); matA[0][1] = skinBones[int(bone.x) * 8 + 1];
vec4 v1 = vec4(skinBones[boneIndex * 12 + 4], matA[0][2] = skinBones[int(bone.x) * 8 + 2];
skinBones[boneIndex * 12 + 5], matA[0][3] = skinBones[int(bone.x) * 8 + 3];
skinBones[boneIndex * 12 + 6], matB[0][0] = skinBones[int(bone.x) * 8 + 4];
skinBones[boneIndex * 12 + 7]); matB[0][1] = skinBones[int(bone.x) * 8 + 5];
vec4 v2 = vec4(skinBones[boneIndex * 12 + 8], matB[0][2] = skinBones[int(bone.x) * 8 + 6];
skinBones[boneIndex * 12 + 9], matB[0][3] = skinBones[int(bone.x) * 8 + 7];
skinBones[boneIndex * 12 + 10], matA[1][0] = skinBones[int(bone.y) * 8 + 0];
skinBones[boneIndex * 12 + 11]); matA[1][1] = skinBones[int(bone.y) * 8 + 1];
return mat4(v0.x, v0.y, v0.z, v0.w, matA[1][2] = skinBones[int(bone.y) * 8 + 2];
v1.x, v1.y, v1.z, v1.w, matA[1][3] = skinBones[int(bone.y) * 8 + 3];
v2.x, v2.y, v2.z, v2.w, matB[1][0] = skinBones[int(bone.y) * 8 + 4];
0, 0, 0, 1); matB[1][1] = skinBones[int(bone.y) * 8 + 5];
} matB[1][2] = skinBones[int(bone.y) * 8 + 6];
matB[1][3] = skinBones[int(bone.y) * 8 + 7];
mat4 getSkinningMat() { matA[2][0] = skinBones[int(bone.z) * 8 + 0];
return weight.x * getBoneMat(int(bone.x)) + matA[2][1] = skinBones[int(bone.z) * 8 + 1];
weight.y * getBoneMat(int(bone.y)) + matA[2][2] = skinBones[int(bone.z) * 8 + 2];
weight.z * getBoneMat(int(bone.z)) + matA[2][3] = skinBones[int(bone.z) * 8 + 3];
weight.w * getBoneMat(int(bone.w)); matB[2][0] = skinBones[int(bone.z) * 8 + 4];
} matB[2][1] = skinBones[int(bone.z) * 8 + 5];
matB[2][2] = skinBones[int(bone.z) * 8 + 6];
mat3 getSkinningMatVec(const mat4 skinningMat) { matB[2][3] = skinBones[int(bone.z) * 8 + 7];
return mat3(skinningMat[0].xyz, skinningMat[1].xyz, skinningMat[2].xyz); matA[3][0] = skinBones[int(bone.w) * 8 + 0];
matA[3][1] = skinBones[int(bone.w) * 8 + 1];
matA[3][2] = skinBones[int(bone.w) * 8 + 2];
matA[3][3] = skinBones[int(bone.w) * 8 + 3];
matB[3][0] = skinBones[int(bone.w) * 8 + 4];
matB[3][1] = skinBones[int(bone.w) * 8 + 5];
matB[3][2] = skinBones[int(bone.w) * 8 + 6];
matB[3][3] = skinBones[int(bone.w) * 8 + 7];
// Handles antipodality by sticking joints in the same neighbourhood
// weights.xyz *= sign(matA[3] * mat3x4(matA)).xyz;
weights.xyz *= sign(matA[3] * matA).xyz;
// Apply weights
A = matA * weights; // Real part
B = matB * weights; // Dual part
// Normalize
float invNormA = 1.0 / length(A);
A *= invNormA;
B *= invNormA;
} }
#endif #endif
@ -77,9 +94,11 @@ void main() {
#endif #endif
#ifdef _Skinning #ifdef _Skinning
mat4 skinningMat = getSkinningMat(); vec4 skinA;
mat3 skinningMatVec = getSkinningMatVec(skinningMat); vec4 skinB;
sPos = sPos * skinningMat; getSkinningDualQuat(weight, skinA, skinB);
sPos.xyz += 2.0 * cross(skinA.xyz, cross(skinA.xyz, sPos.xyz) + skinA.w * sPos.xyz); // Rotate
sPos.xyz += 2.0 * (skinA.w * skinB.xyz - skinB.w * skinA.xyz + cross(skinA.xyz, skinB.xyz)); // Translate
#endif #endif
gl_Position = LWVP * sPos; gl_Position = LWVP * sPos;

View file

@ -53,34 +53,51 @@ out vec4 matColor;
#endif #endif
#ifdef _Skinning #ifdef _Skinning
mat4 getBoneMat(const int boneIndex) { void getSkinningDualQuat(vec4 weights, out vec4 A, inout vec4 B) {
vec4 v0 = vec4(skinBones[boneIndex * 12 + 0], // Retrieve the real and dual part of the dual-quaternions
skinBones[boneIndex * 12 + 1], mat4 matA, matB;
skinBones[boneIndex * 12 + 2], matA[0][0] = skinBones[int(bone.x) * 8 + 0];
skinBones[boneIndex * 12 + 3]); matA[0][1] = skinBones[int(bone.x) * 8 + 1];
vec4 v1 = vec4(skinBones[boneIndex * 12 + 4], matA[0][2] = skinBones[int(bone.x) * 8 + 2];
skinBones[boneIndex * 12 + 5], matA[0][3] = skinBones[int(bone.x) * 8 + 3];
skinBones[boneIndex * 12 + 6], matB[0][0] = skinBones[int(bone.x) * 8 + 4];
skinBones[boneIndex * 12 + 7]); matB[0][1] = skinBones[int(bone.x) * 8 + 5];
vec4 v2 = vec4(skinBones[boneIndex * 12 + 8], matB[0][2] = skinBones[int(bone.x) * 8 + 6];
skinBones[boneIndex * 12 + 9], matB[0][3] = skinBones[int(bone.x) * 8 + 7];
skinBones[boneIndex * 12 + 10], matA[1][0] = skinBones[int(bone.y) * 8 + 0];
skinBones[boneIndex * 12 + 11]); matA[1][1] = skinBones[int(bone.y) * 8 + 1];
return mat4(v0.x, v0.y, v0.z, v0.w, matA[1][2] = skinBones[int(bone.y) * 8 + 2];
v1.x, v1.y, v1.z, v1.w, matA[1][3] = skinBones[int(bone.y) * 8 + 3];
v2.x, v2.y, v2.z, v2.w, matB[1][0] = skinBones[int(bone.y) * 8 + 4];
0, 0, 0, 1); matB[1][1] = skinBones[int(bone.y) * 8 + 5];
} matB[1][2] = skinBones[int(bone.y) * 8 + 6];
matB[1][3] = skinBones[int(bone.y) * 8 + 7];
mat4 getSkinningMat() { matA[2][0] = skinBones[int(bone.z) * 8 + 0];
return weight.x * getBoneMat(int(bone.x)) + matA[2][1] = skinBones[int(bone.z) * 8 + 1];
weight.y * getBoneMat(int(bone.y)) + matA[2][2] = skinBones[int(bone.z) * 8 + 2];
weight.z * getBoneMat(int(bone.z)) + matA[2][3] = skinBones[int(bone.z) * 8 + 3];
weight.w * getBoneMat(int(bone.w)); matB[2][0] = skinBones[int(bone.z) * 8 + 4];
} matB[2][1] = skinBones[int(bone.z) * 8 + 5];
matB[2][2] = skinBones[int(bone.z) * 8 + 6];
mat3 getSkinningMatVec(const mat4 skinningMat) { matB[2][3] = skinBones[int(bone.z) * 8 + 7];
return mat3(skinningMat[0].xyz, skinningMat[1].xyz, skinningMat[2].xyz); matA[3][0] = skinBones[int(bone.w) * 8 + 0];
matA[3][1] = skinBones[int(bone.w) * 8 + 1];
matA[3][2] = skinBones[int(bone.w) * 8 + 2];
matA[3][3] = skinBones[int(bone.w) * 8 + 3];
matB[3][0] = skinBones[int(bone.w) * 8 + 4];
matB[3][1] = skinBones[int(bone.w) * 8 + 5];
matB[3][2] = skinBones[int(bone.w) * 8 + 6];
matB[3][3] = skinBones[int(bone.w) * 8 + 7];
// Handles antipodality by sticking joints in the same neighbourhood
// weights.xyz *= sign(matA[3] * mat3x4(matA)).xyz;
weights.xyz *= sign(matA[3] * matA).xyz;
// Apply weights
A = matA * weights; // Real part
B = matB * weights; // Dual part
// Normalize
float invNormA = 1.0 / length(A);
A *= invNormA;
B *= invNormA;
} }
#endif #endif
@ -91,11 +108,18 @@ void main() {
#else #else
vec4 sPos = (vec4(pos, 1.0)); vec4 sPos = (vec4(pos, 1.0));
#endif #endif
#ifdef _Skinning #ifdef _Skinning
mat4 skinningMat = getSkinningMat(); vec4 skinA;
mat3 skinningMatVec = getSkinningMatVec(skinningMat); vec4 skinB;
sPos = sPos * skinningMat; getSkinningDualQuat(weight, skinA, skinB);
sPos.xyz += 2.0 * cross(skinA.xyz, cross(skinA.xyz, sPos.xyz) + skinA.w * sPos.xyz); // Rotate
sPos.xyz += 2.0 * (skinA.w * skinB.xyz - skinB.w * skinA.xyz + cross(skinA.xyz, skinB.xyz)); // Translate
vec3 _normal = normalize(mat3(N) * (nor + 2.0 * cross(skinA.xyz, cross(skinA.xyz, nor) + skinA.w * nor)));
#else
vec3 _normal = normalize(mat3(N) * nor);
#endif #endif
lPos = LWVP * sPos; lPos = LWVP * sPos;
#ifdef _Billboard #ifdef _Billboard
@ -115,12 +139,6 @@ void main() {
texCoord = tex; texCoord = tex;
#endif #endif
#ifdef _Skinning
vec3 _normal = normalize(mat3(N) * (nor * skinningMatVec));
#else
vec3 _normal = normalize(mat3(N) * nor);
#endif
matColor = albedo_color; matColor = albedo_color;
#ifdef _VCols #ifdef _VCols

View file

@ -133,14 +133,14 @@ vec3 shIrradiance(vec3 nor, float scale) {
} }
void main() { void main() {
vec4 g0 = texture(gbuffer0, texCoord); // Normal.xy, roughness/metallic, mask vec4 g0 = texture(gbuffer0, texCoord); // Normal.xy, metallic/roughness, mask
vec3 n; vec3 n;
n.z = 1.0 - abs(g0.x) - abs(g0.y); n.z = 1.0 - abs(g0.x) - abs(g0.y);
n.xy = n.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy); n.xy = n.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy);
n = normalize(n); n = normalize(n);
vec2 roughmet = unpackFloat(g0.b); vec2 metrough = unpackFloat(g0.b);
#ifdef _Rad #ifdef _Rad
float depth = texture(gbufferD, texCoord).r * 2.0 - 1.0; float depth = texture(gbufferD, texCoord).r * 2.0 - 1.0;
@ -155,7 +155,7 @@ void main() {
float probeFract = fract(probeFactor); float probeFract = fract(probeFactor);
vec3 indirect; vec3 indirect;
#ifdef _Rad #ifdef _Rad
float lod = getMipLevelFromRoughness(roughmet.x); float lod = getMipLevelFromRoughness(metrough.y);
vec3 reflectionWorld = reflect(-v, n); vec3 reflectionWorld = reflect(-v, n);
vec2 envCoordRefl = envMapEquirect(reflectionWorld); vec2 envCoordRefl = envMapEquirect(reflectionWorld);
vec3 prefilteredColor = textureLod(senvmapRadiance, envCoordRefl, lod).rgb; vec3 prefilteredColor = textureLod(senvmapRadiance, envCoordRefl, lod).rgb;
@ -178,7 +178,7 @@ void main() {
vec3 indirect = shIrradiance(n, 2.2) / PI; vec3 indirect = shIrradiance(n, 2.2) / PI;
#ifdef _Rad #ifdef _Rad
vec3 reflectionWorld = reflect(-v, n); vec3 reflectionWorld = reflect(-v, n);
float lod = getMipLevelFromRoughness(roughmet.x); float lod = getMipLevelFromRoughness(metrough.y);
vec3 prefilteredColor = textureLod(senvmapRadiance, envMapEquirect(reflectionWorld), lod).rgb; vec3 prefilteredColor = textureLod(senvmapRadiance, envMapEquirect(reflectionWorld), lod).rgb;
#endif #endif
#endif #endif
@ -191,16 +191,16 @@ void main() {
#endif #endif
vec4 g1 = texture(gbuffer1, texCoord); // Basecolor.rgb, occlusion vec4 g1 = texture(gbuffer1, texCoord); // Basecolor.rgb, occlusion
vec3 albedo = surfaceAlbedo(g1.rgb, roughmet.y); // g1.rgb - basecolor vec3 albedo = surfaceAlbedo(g1.rgb, metrough.x); // g1.rgb - basecolor
indirect *= albedo; indirect *= albedo;
#ifdef _Rad #ifdef _Rad
// Indirect specular // Indirect specular
float dotNV = max(dot(n, v), 0.0); float dotNV = max(dot(n, v), 0.0);
vec3 f0 = surfaceF0(g1.rgb, roughmet.y); vec3 f0 = surfaceF0(g1.rgb, metrough.x);
vec2 envBRDF = texture(senvmapBrdf, vec2(roughmet.x, 1.0 - dotNV)).xy; vec2 envBRDF = texture(senvmapBrdf, vec2(metrough.y, 1.0 - dotNV)).xy;
indirect += prefilteredColor * (f0 * envBRDF.x + envBRDF.y);; indirect += prefilteredColor * (f0 * envBRDF.x + envBRDF.y);;
#endif #endif

View file

@ -131,7 +131,24 @@ vec3 specularBRDF(vec3 f0, float roughness, float nl, float nh, float nv, float
float a = roughness * roughness; float a = roughness * roughness;
return d_ggx(nh, a) * clamp(v_smithschlick(nl, nv, a), 0.0, 1.0) * f_schlick(f0, vh) / 4.0; return d_ggx(nh, a) * clamp(v_smithschlick(nl, nv, a), 0.0, 1.0) * f_schlick(f0, vh) / 4.0;
} }
// vec3 burleyDiffuseBRDF(vec3 albedo, float roughness, float nv, float nl, float vh) {
// float FD90 = 0.5 + 2.0 * vh * vh * roughness;
// float FdV = 1.0 + (FD90 - 1.0) * pow(1.0 - nv, 5.0);
// float FdL = 1.0 + (FD90 - 1.0) * pow(1.0 - nl, 5.0);
// return albedo * ((1.0 / 3.1415926535) * FdV * FdL);
// }
// vec3 orenNayarDiffuseBRDF(vec3 albedo, float roughness, float nv, float nl, float vh) {
// float a = roughness * roughness;
// float s = a;// / (1.29 + 0.5 * a);
// float s2 = s * s;
// float vl = 2.0 * vh * vh - 1.0; // double angle identity
// float Cosri = vl - nv * nl;
// float C1 = 1.0 - 0.5 * s2 / (s2 + 0.33);
// float test = 1.0;
// if (Cosri >= 0.0) test = (1.0 / (max(nl, nv)));
// float C2 = 0.45 * s2 / (s2 + 0.09) * Cosri * test;
// return albedo / PI * (C1 + C2) * (1.0 + roughness * 0.5);
// }
vec3 diffuseBRDF(vec3 albedo, float nl) { vec3 diffuseBRDF(vec3 albedo, float nl) {
// lambert // lambert
return albedo * nl; // // albedo * max(0.0, nl); return albedo * nl; // // albedo * max(0.0, nl);
@ -684,7 +701,7 @@ void main() {
texCoord += vec2(0.5 / screenSize); // Half pixel offset texCoord += vec2(0.5 / screenSize); // Half pixel offset
float depth = texture(gbufferD, texCoord).r * 2.0 - 1.0; float depth = texture(gbufferD, texCoord).r * 2.0 - 1.0;
vec4 g0 = texture(gbuffer0, texCoord); // Normal.xy, roughness/metallic, mask vec4 g0 = texture(gbuffer0, texCoord); // Normal.xy, metallic/roughness, mask
vec4 g1 = texture(gbuffer1, texCoord); // Basecolor.rgb, occlusion vec4 g1 = texture(gbuffer1, texCoord); // Basecolor.rgb, occlusion
vec3 n; vec3 n;
@ -694,13 +711,13 @@ void main() {
vec3 p = getPos(depth, texCoord); vec3 p = getPos(depth, texCoord);
// vec3 p = getPos(depth); // vec3 p = getPos(depth);
vec2 roughmet = unpackFloat(g0.b); vec2 metrough = unpackFloat(g0.b);
vec3 v = normalize(eye - p.xyz); vec3 v = normalize(eye - p.xyz);
float dotNV = max(dot(n, v), 0.0); float dotNV = max(dot(n, v), 0.0);
vec3 albedo = surfaceAlbedo(g1.rgb, roughmet.y); // g1.rgb - basecolor vec3 albedo = surfaceAlbedo(g1.rgb, metrough.x); // g1.rgb - basecolor
vec3 f0 = surfaceF0(g1.rgb, roughmet.y); vec3 f0 = surfaceF0(g1.rgb, metrough.x);
// Per-light // Per-light
vec3 l; vec3 l;
@ -727,8 +744,10 @@ void main() {
#endif #endif
// Direct // Direct
vec3 direct = diffuseBRDF(albedo, dotNL) + specularBRDF(f0, roughmet.x, dotNL, dotNH, dotNV, dotVH); vec3 direct = diffuseBRDF(albedo, dotNL) + specularBRDF(f0, metrough.y, dotNL, dotNH, dotNV, dotVH);
// vec3 direct = orenNayarDiffuseBRDF(albedo, metrough.y, dotNV, dotNL, dotVH) + specularBRDF(f0, metrough.y, dotNL, dotNH, dotNV, dotVH);
// vec3 direct = burleyDiffuseBRDF(albedo, metrough.y, dotNV, dotNL, dotVH) + specularBRDF(f0, metrough.y, dotNL, dotNH, dotNV, dotVH);
if (lightType == 2) { // Spot if (lightType == 2) { // Spot
float spotEffect = dot(lightDir, l); float spotEffect = dot(lightDir, l);
if (spotEffect < spotlightCutoff) { if (spotEffect < spotlightCutoff) {
@ -738,10 +757,10 @@ void main() {
} }
// Aniso spec // Aniso spec
// float shinyParallel = roughmet.x; // float shinyParallel = metrough.y;
// float shinyPerpendicular = 0.08; // float shinyPerpendicular = 0.08;
// vec3 fiberDirection = vec3(0.0, 1.0, 8.0); // vec3 fiberDirection = vec3(0.0, 1.0, 8.0);
// vec3 direct = diffuseBRDF(albedo, roughmet.x, dotNV, dotNL, dotVH, dotLV) + wardSpecular(n, h, dotNL, dotNV, dotNH, fiberDirection, shinyParallel, shinyPerpendicular); // vec3 direct = diffuseBRDF(albedo, metrough.y, dotNV, dotNL, dotVH, dotLV) + wardSpecular(n, h, dotNL, dotNV, dotNH, fiberDirection, shinyParallel, shinyPerpendicular);
direct = direct * lightColor * lightStrength; direct = direct * lightColor * lightStrength;
@ -763,7 +782,7 @@ void main() {
// float probeFract = fract(probeFactor); // float probeFract = fract(probeFactor);
// vec3 indirect; // vec3 indirect;
// #ifdef _Rad // #ifdef _Rad
// float lod = getMipLevelFromRoughness(roughmet.x); // float lod = getMipLevelFromRoughness(metrough.y);
// vec3 reflectionWorld = reflect(-v, n); // vec3 reflectionWorld = reflect(-v, n);
// vec2 envCoordRefl = envMapEquirect(reflectionWorld); // vec2 envCoordRefl = envMapEquirect(reflectionWorld);
// vec3 prefilteredColor = textureLod(senvmapRadiance, envCoordRefl, lod).rgb; // vec3 prefilteredColor = textureLod(senvmapRadiance, envCoordRefl, lod).rgb;
@ -786,7 +805,7 @@ void main() {
// vec3 indirect = shIrradiance(n, 2.2) / PI; // vec3 indirect = shIrradiance(n, 2.2) / PI;
// #ifdef _Rad // #ifdef _Rad
// vec3 reflectionWorld = reflect(-v, n); // vec3 reflectionWorld = reflect(-v, n);
// float lod = getMipLevelFromRoughness(roughmet.x); // float lod = getMipLevelFromRoughness(metrough.y);
// vec3 prefilteredColor = textureLod(senvmapRadiance, envMapEquirect(reflectionWorld), lod).rgb; // vec3 prefilteredColor = textureLod(senvmapRadiance, envMapEquirect(reflectionWorld), lod).rgb;
// #endif // #endif
// #endif // #endif
@ -801,7 +820,7 @@ void main() {
// #ifdef _Rad // #ifdef _Rad
// // Indirect specular // // Indirect specular
// vec2 envBRDF = texture(senvmapBrdf, vec2(roughmet.x, 1.0 - dotNV)).xy; // vec2 envBRDF = texture(senvmapBrdf, vec2(metrough.y, 1.0 - dotNV)).xy;
// indirect += prefilteredColor * (f0 * envBRDF.x + envBRDF.y);; // indirect += prefilteredColor * (f0 * envBRDF.x + envBRDF.y);;
// #endif // #endif
// indirect = indirect * envmapStrength;// * lightColor * lightStrength; // indirect = indirect * envmapStrength;// * lightColor * lightStrength;
@ -832,7 +851,7 @@ void main() {
// vec3 p3 = lightPos + ex - ey; // vec3 p3 = lightPos + ex - ey;
// vec3 p4 = lightPos - ex - ey; // vec3 p4 = lightPos - ex - ey;
// float theta = acos(dotNV); // float theta = acos(dotNV);
// vec2 tuv = vec2(roughmet.x, theta/(0.5*PI)); // vec2 tuv = vec2(metrough.y, theta/(0.5*PI));
// tuv = tuv*LUT_SCALE + LUT_BIAS; // tuv = tuv*LUT_SCALE + LUT_BIAS;
// vec4 t = texture(sltcMat, tuv); // vec4 t = texture(sltcMat, tuv);

View file

@ -6,10 +6,6 @@ precision highp float;
#include "../compiled.glsl" #include "../compiled.glsl"
#ifdef _NorTex
#define _Tex
#endif
in vec3 pos; in vec3 pos;
in vec3 nor; in vec3 nor;
#ifdef _BaseTex #ifdef _BaseTex
@ -37,7 +33,7 @@ uniform mat4 LWVP;
uniform vec4 albedo_color; uniform vec4 albedo_color;
uniform vec3 eye; uniform vec3 eye;
#ifdef _Skinning #ifdef _Skinning
uniform float skinBones[skinMaxBones * 12]; uniform float skinBones[skinMaxBones * 8];
#endif #endif
#ifdef _VR #ifdef _VR
uniform mat4 U; // Undistortion uniform mat4 U; // Undistortion
@ -58,34 +54,51 @@ out vec3 eyeDir;
#endif #endif
#ifdef _Skinning #ifdef _Skinning
mat4 getBoneMat(const int boneIndex) { void getSkinningDualQuat(vec4 weights, out vec4 A, inout vec4 B) {
vec4 v0 = vec4(skinBones[boneIndex * 12 + 0], // Retrieve the real and dual part of the dual-quaternions
skinBones[boneIndex * 12 + 1], mat4 matA, matB;
skinBones[boneIndex * 12 + 2], matA[0][0] = skinBones[int(bone.x) * 8 + 0];
skinBones[boneIndex * 12 + 3]); matA[0][1] = skinBones[int(bone.x) * 8 + 1];
vec4 v1 = vec4(skinBones[boneIndex * 12 + 4], matA[0][2] = skinBones[int(bone.x) * 8 + 2];
skinBones[boneIndex * 12 + 5], matA[0][3] = skinBones[int(bone.x) * 8 + 3];
skinBones[boneIndex * 12 + 6], matB[0][0] = skinBones[int(bone.x) * 8 + 4];
skinBones[boneIndex * 12 + 7]); matB[0][1] = skinBones[int(bone.x) * 8 + 5];
vec4 v2 = vec4(skinBones[boneIndex * 12 + 8], matB[0][2] = skinBones[int(bone.x) * 8 + 6];
skinBones[boneIndex * 12 + 9], matB[0][3] = skinBones[int(bone.x) * 8 + 7];
skinBones[boneIndex * 12 + 10], matA[1][0] = skinBones[int(bone.y) * 8 + 0];
skinBones[boneIndex * 12 + 11]); matA[1][1] = skinBones[int(bone.y) * 8 + 1];
return mat4(v0.x, v0.y, v0.z, v0.w, matA[1][2] = skinBones[int(bone.y) * 8 + 2];
v1.x, v1.y, v1.z, v1.w, matA[1][3] = skinBones[int(bone.y) * 8 + 3];
v2.x, v2.y, v2.z, v2.w, matB[1][0] = skinBones[int(bone.y) * 8 + 4];
0, 0, 0, 1); matB[1][1] = skinBones[int(bone.y) * 8 + 5];
} matB[1][2] = skinBones[int(bone.y) * 8 + 6];
matB[1][3] = skinBones[int(bone.y) * 8 + 7];
mat4 getSkinningMat() { matA[2][0] = skinBones[int(bone.z) * 8 + 0];
return weight.x * getBoneMat(int(bone.x)) + matA[2][1] = skinBones[int(bone.z) * 8 + 1];
weight.y * getBoneMat(int(bone.y)) + matA[2][2] = skinBones[int(bone.z) * 8 + 2];
weight.z * getBoneMat(int(bone.z)) + matA[2][3] = skinBones[int(bone.z) * 8 + 3];
weight.w * getBoneMat(int(bone.w)); matB[2][0] = skinBones[int(bone.z) * 8 + 4];
} matB[2][1] = skinBones[int(bone.z) * 8 + 5];
matB[2][2] = skinBones[int(bone.z) * 8 + 6];
mat3 getSkinningMatVec(const mat4 skinningMat) { matB[2][3] = skinBones[int(bone.z) * 8 + 7];
return mat3(skinningMat[0].xyz, skinningMat[1].xyz, skinningMat[2].xyz); matA[3][0] = skinBones[int(bone.w) * 8 + 0];
matA[3][1] = skinBones[int(bone.w) * 8 + 1];
matA[3][2] = skinBones[int(bone.w) * 8 + 2];
matA[3][3] = skinBones[int(bone.w) * 8 + 3];
matB[3][0] = skinBones[int(bone.w) * 8 + 4];
matB[3][1] = skinBones[int(bone.w) * 8 + 5];
matB[3][2] = skinBones[int(bone.w) * 8 + 6];
matB[3][3] = skinBones[int(bone.w) * 8 + 7];
// Handles antipodality by sticking joints in the same neighbourhood
// weights.xyz *= sign(matA[3] * mat3x4(matA)).xyz;
weights.xyz *= sign(matA[3] * matA).xyz;
// Apply weights
A = matA * weights; // Real part
B = matB * weights; // Dual part
// Normalize
float invNormA = 1.0 / length(A);
A *= invNormA;
B *= invNormA;
} }
#endif #endif
@ -103,15 +116,15 @@ float distortionFactor(float rSquared) {
} }
// Convert point from world space to undistorted camera space // Convert point from world space to undistorted camera space
vec4 undistort(mat4 WV, vec4 pos) { vec4 undistort(mat4 WV, vec4 pos) {
// Go to camera space // Go to camera space
pos = WV * pos; pos = WV * pos;
const float nearClip = 0.1; const float nearClip = 0.1;
if (pos.z <= -nearClip) { // Reminder: Forward is -Z if (pos.z <= -nearClip) { // Reminder: Forward is -Z
// Undistort the point's coordinates in XY // Undistort the point's coordinates in XY
float r2 = clamp(dot(pos.xy, pos.xy) / (pos.z * pos.z), 0.0, maxRadSq); float r2 = clamp(dot(pos.xy, pos.xy) / (pos.z * pos.z), 0.0, maxRadSq);
pos.xy *= distortionFactor(r2); pos.xy *= distortionFactor(r2);
} }
return pos; return pos;
} }
#endif #endif
@ -122,11 +135,18 @@ void main() {
#else #else
vec4 sPos = (vec4(pos, 1.0)); vec4 sPos = (vec4(pos, 1.0));
#endif #endif
#ifdef _Skinning #ifdef _Skinning
mat4 skinningMat = getSkinningMat(); vec4 skinA;
mat3 skinningMatVec = getSkinningMatVec(skinningMat); vec4 skinB;
sPos = sPos * skinningMat; getSkinningDualQuat(weight, skinA, skinB);
sPos.xyz += 2.0 * cross(skinA.xyz, cross(skinA.xyz, sPos.xyz) + skinA.w * sPos.xyz); // Rotate
sPos.xyz += 2.0 * (skinA.w * skinB.xyz - skinB.w * skinA.xyz + cross(skinA.xyz, skinB.xyz)); // Translate
vec3 _normal = normalize(mat3(N) * (nor + 2.0 * cross(skinA.xyz, cross(skinA.xyz, nor) + skinA.w * nor)));
#else
vec3 _normal = normalize(mat3(N) * nor);
#endif #endif
lPos = LWVP * sPos; lPos = LWVP * sPos;
mat4 WV = V * W; mat4 WV = V * W;
@ -151,12 +171,6 @@ void main() {
texCoord = tex; texCoord = tex;
#endif #endif
#ifdef _Skinning
vec3 _normal = normalize(mat3(N) * (nor * skinningMatVec));
#else
vec3 _normal = normalize(mat3(N) * nor);
#endif
matColor = albedo_color; matColor = albedo_color;
#ifdef _VCols #ifdef _VCols

View file

@ -54,34 +54,51 @@ out vec3 eyeDir;
#endif #endif
#ifdef _Skinning #ifdef _Skinning
mat4 getBoneMat(const int boneIndex) { void getSkinningDualQuat(vec4 weights, out vec4 A, inout vec4 B) {
vec4 v0 = vec4(skinBones[boneIndex * 12 + 0], // Retrieve the real and dual part of the dual-quaternions
skinBones[boneIndex * 12 + 1], mat4 matA, matB;
skinBones[boneIndex * 12 + 2], matA[0][0] = skinBones[int(bone.x) * 8 + 0];
skinBones[boneIndex * 12 + 3]); matA[0][1] = skinBones[int(bone.x) * 8 + 1];
vec4 v1 = vec4(skinBones[boneIndex * 12 + 4], matA[0][2] = skinBones[int(bone.x) * 8 + 2];
skinBones[boneIndex * 12 + 5], matA[0][3] = skinBones[int(bone.x) * 8 + 3];
skinBones[boneIndex * 12 + 6], matB[0][0] = skinBones[int(bone.x) * 8 + 4];
skinBones[boneIndex * 12 + 7]); matB[0][1] = skinBones[int(bone.x) * 8 + 5];
vec4 v2 = vec4(skinBones[boneIndex * 12 + 8], matB[0][2] = skinBones[int(bone.x) * 8 + 6];
skinBones[boneIndex * 12 + 9], matB[0][3] = skinBones[int(bone.x) * 8 + 7];
skinBones[boneIndex * 12 + 10], matA[1][0] = skinBones[int(bone.y) * 8 + 0];
skinBones[boneIndex * 12 + 11]); matA[1][1] = skinBones[int(bone.y) * 8 + 1];
return mat4(v0.x, v0.y, v0.z, v0.w, matA[1][2] = skinBones[int(bone.y) * 8 + 2];
v1.x, v1.y, v1.z, v1.w, matA[1][3] = skinBones[int(bone.y) * 8 + 3];
v2.x, v2.y, v2.z, v2.w, matB[1][0] = skinBones[int(bone.y) * 8 + 4];
0, 0, 0, 1); matB[1][1] = skinBones[int(bone.y) * 8 + 5];
} matB[1][2] = skinBones[int(bone.y) * 8 + 6];
matB[1][3] = skinBones[int(bone.y) * 8 + 7];
mat4 getSkinningMat() { matA[2][0] = skinBones[int(bone.z) * 8 + 0];
return weight.x * getBoneMat(int(bone.x)) + matA[2][1] = skinBones[int(bone.z) * 8 + 1];
weight.y * getBoneMat(int(bone.y)) + matA[2][2] = skinBones[int(bone.z) * 8 + 2];
weight.z * getBoneMat(int(bone.z)) + matA[2][3] = skinBones[int(bone.z) * 8 + 3];
weight.w * getBoneMat(int(bone.w)); matB[2][0] = skinBones[int(bone.z) * 8 + 4];
} matB[2][1] = skinBones[int(bone.z) * 8 + 5];
matB[2][2] = skinBones[int(bone.z) * 8 + 6];
mat3 getSkinningMatVec(const mat4 skinningMat) { matB[2][3] = skinBones[int(bone.z) * 8 + 7];
return mat3(skinningMat[0].xyz, skinningMat[1].xyz, skinningMat[2].xyz); matA[3][0] = skinBones[int(bone.w) * 8 + 0];
matA[3][1] = skinBones[int(bone.w) * 8 + 1];
matA[3][2] = skinBones[int(bone.w) * 8 + 2];
matA[3][3] = skinBones[int(bone.w) * 8 + 3];
matB[3][0] = skinBones[int(bone.w) * 8 + 4];
matB[3][1] = skinBones[int(bone.w) * 8 + 5];
matB[3][2] = skinBones[int(bone.w) * 8 + 6];
matB[3][3] = skinBones[int(bone.w) * 8 + 7];
// Handles antipodality by sticking joints in the same neighbourhood
// weights.xyz *= sign(matA[3] * mat3x4(matA)).xyz;
weights.xyz *= sign(matA[3] * matA).xyz;
// Apply weights
A = matA * weights; // Real part
B = matB * weights; // Dual part
// Normalize
float invNormA = 1.0 / length(A);
A *= invNormA;
B *= invNormA;
} }
#endif #endif
@ -92,11 +109,18 @@ void main() {
#else #else
vec4 sPos = (vec4(pos, 1.0)); vec4 sPos = (vec4(pos, 1.0));
#endif #endif
#ifdef _Skinning #ifdef _Skinning
mat4 skinningMat = getSkinningMat(); vec4 skinA;
mat3 skinningMatVec = getSkinningMatVec(skinningMat); vec4 skinB;
sPos = sPos * skinningMat; getSkinningDualQuat(weight, skinA, skinB);
sPos.xyz += 2.0 * cross(skinA.xyz, cross(skinA.xyz, sPos.xyz) + skinA.w * sPos.xyz); // Rotate
sPos.xyz += 2.0 * (skinA.w * skinB.xyz - skinB.w * skinA.xyz + cross(skinA.xyz, skinB.xyz)); // Translate
vec3 _normal = normalize(mat3(N) * (nor + 2.0 * cross(skinA.xyz, cross(skinA.xyz, nor) + skinA.w * nor)));
#else
vec3 _normal = normalize(mat3(N) * nor);
#endif #endif
lPos = LWVP * sPos; lPos = LWVP * sPos;
mat4 WV = V * W; mat4 WV = V * W;
@ -117,12 +141,6 @@ void main() {
texCoord = tex; texCoord = tex;
#endif #endif
#ifdef _Skinning
vec3 _normal = normalize(mat3(N) * (nor * skinningMatVec));
#else
vec3 _normal = normalize(mat3(N) * nor);
#endif
matColor = albedo_color; matColor = albedo_color;
#ifdef _VCols #ifdef _VCols

View file

@ -38,34 +38,51 @@ uniform mat4 LWVP;
// out vec4 position; // out vec4 position;
#ifdef _Skinning #ifdef _Skinning
mat4 getBoneMat(const int boneIndex) { void getSkinningDualQuat(vec4 weights, out vec4 A, inout vec4 B) {
vec4 v0 = vec4(skinBones[boneIndex * 12 + 0], // Retrieve the real and dual part of the dual-quaternions
skinBones[boneIndex * 12 + 1], mat4 matA, matB;
skinBones[boneIndex * 12 + 2], matA[0][0] = skinBones[int(bone.x) * 8 + 0];
skinBones[boneIndex * 12 + 3]); matA[0][1] = skinBones[int(bone.x) * 8 + 1];
vec4 v1 = vec4(skinBones[boneIndex * 12 + 4], matA[0][2] = skinBones[int(bone.x) * 8 + 2];
skinBones[boneIndex * 12 + 5], matA[0][3] = skinBones[int(bone.x) * 8 + 3];
skinBones[boneIndex * 12 + 6], matB[0][0] = skinBones[int(bone.x) * 8 + 4];
skinBones[boneIndex * 12 + 7]); matB[0][1] = skinBones[int(bone.x) * 8 + 5];
vec4 v2 = vec4(skinBones[boneIndex * 12 + 8], matB[0][2] = skinBones[int(bone.x) * 8 + 6];
skinBones[boneIndex * 12 + 9], matB[0][3] = skinBones[int(bone.x) * 8 + 7];
skinBones[boneIndex * 12 + 10], matA[1][0] = skinBones[int(bone.y) * 8 + 0];
skinBones[boneIndex * 12 + 11]); matA[1][1] = skinBones[int(bone.y) * 8 + 1];
return mat4(v0.x, v0.y, v0.z, v0.w, matA[1][2] = skinBones[int(bone.y) * 8 + 2];
v1.x, v1.y, v1.z, v1.w, matA[1][3] = skinBones[int(bone.y) * 8 + 3];
v2.x, v2.y, v2.z, v2.w, matB[1][0] = skinBones[int(bone.y) * 8 + 4];
0, 0, 0, 1); matB[1][1] = skinBones[int(bone.y) * 8 + 5];
} matB[1][2] = skinBones[int(bone.y) * 8 + 6];
matB[1][3] = skinBones[int(bone.y) * 8 + 7];
mat4 getSkinningMat() { matA[2][0] = skinBones[int(bone.z) * 8 + 0];
return weight.x * getBoneMat(int(bone.x)) + matA[2][1] = skinBones[int(bone.z) * 8 + 1];
weight.y * getBoneMat(int(bone.y)) + matA[2][2] = skinBones[int(bone.z) * 8 + 2];
weight.z * getBoneMat(int(bone.z)) + matA[2][3] = skinBones[int(bone.z) * 8 + 3];
weight.w * getBoneMat(int(bone.w)); matB[2][0] = skinBones[int(bone.z) * 8 + 4];
} matB[2][1] = skinBones[int(bone.z) * 8 + 5];
matB[2][2] = skinBones[int(bone.z) * 8 + 6];
mat3 getSkinningMatVec(const mat4 skinningMat) { matB[2][3] = skinBones[int(bone.z) * 8 + 7];
return mat3(skinningMat[0].xyz, skinningMat[1].xyz, skinningMat[2].xyz); matA[3][0] = skinBones[int(bone.w) * 8 + 0];
matA[3][1] = skinBones[int(bone.w) * 8 + 1];
matA[3][2] = skinBones[int(bone.w) * 8 + 2];
matA[3][3] = skinBones[int(bone.w) * 8 + 3];
matB[3][0] = skinBones[int(bone.w) * 8 + 4];
matB[3][1] = skinBones[int(bone.w) * 8 + 5];
matB[3][2] = skinBones[int(bone.w) * 8 + 6];
matB[3][3] = skinBones[int(bone.w) * 8 + 7];
// Handles antipodality by sticking joints in the same neighbourhood
// weights.xyz *= sign(matA[3] * mat3x4(matA)).xyz;
weights.xyz *= sign(matA[3] * matA).xyz;
// Apply weights
A = matA * weights; // Real part
B = matB * weights; // Dual part
// Normalize
float invNormA = 1.0 / length(A);
A *= invNormA;
B *= invNormA;
} }
#endif #endif
@ -77,9 +94,11 @@ void main() {
#endif #endif
#ifdef _Skinning #ifdef _Skinning
mat4 skinningMat = getSkinningMat(); vec4 skinA;
mat3 skinningMatVec = getSkinningMatVec(skinningMat); vec4 skinB;
sPos = sPos * skinningMat; getSkinningDualQuat(weight, skinA, skinB);
sPos.xyz += 2.0 * cross(skinA.xyz, cross(skinA.xyz, sPos.xyz) + skinA.w * sPos.xyz); // Rotate
sPos.xyz += 2.0 * (skinA.w * skinB.xyz - skinB.w * skinA.xyz + cross(skinA.xyz, skinB.xyz)); // Translate
#endif #endif
gl_Position = LWVP * sPos; gl_Position = LWVP * sPos;

View file

@ -27,38 +27,55 @@ in vec3 nor;
uniform mat4 WVP; uniform mat4 WVP;
#ifdef _Skinning #ifdef _Skinning
uniform float skinBones[skinMaxBones * 12]; uniform float skinBones[skinMaxBones * 8];
#endif #endif
#ifdef _Skinning #ifdef _Skinning
mat4 getBoneMat(const int boneIndex) { void getSkinningDualQuat(vec4 weights, out vec4 A, inout vec4 B) {
vec4 v0 = vec4(skinBones[boneIndex * 12 + 0], // Retrieve the real and dual part of the dual-quaternions
skinBones[boneIndex * 12 + 1], mat4 matA, matB;
skinBones[boneIndex * 12 + 2], matA[0][0] = skinBones[int(bone.x) * 8 + 0];
skinBones[boneIndex * 12 + 3]); matA[0][1] = skinBones[int(bone.x) * 8 + 1];
vec4 v1 = vec4(skinBones[boneIndex * 12 + 4], matA[0][2] = skinBones[int(bone.x) * 8 + 2];
skinBones[boneIndex * 12 + 5], matA[0][3] = skinBones[int(bone.x) * 8 + 3];
skinBones[boneIndex * 12 + 6], matB[0][0] = skinBones[int(bone.x) * 8 + 4];
skinBones[boneIndex * 12 + 7]); matB[0][1] = skinBones[int(bone.x) * 8 + 5];
vec4 v2 = vec4(skinBones[boneIndex * 12 + 8], matB[0][2] = skinBones[int(bone.x) * 8 + 6];
skinBones[boneIndex * 12 + 9], matB[0][3] = skinBones[int(bone.x) * 8 + 7];
skinBones[boneIndex * 12 + 10], matA[1][0] = skinBones[int(bone.y) * 8 + 0];
skinBones[boneIndex * 12 + 11]); matA[1][1] = skinBones[int(bone.y) * 8 + 1];
return mat4(v0.x, v0.y, v0.z, v0.w, matA[1][2] = skinBones[int(bone.y) * 8 + 2];
v1.x, v1.y, v1.z, v1.w, matA[1][3] = skinBones[int(bone.y) * 8 + 3];
v2.x, v2.y, v2.z, v2.w, matB[1][0] = skinBones[int(bone.y) * 8 + 4];
0, 0, 0, 1); matB[1][1] = skinBones[int(bone.y) * 8 + 5];
} matB[1][2] = skinBones[int(bone.y) * 8 + 6];
matB[1][3] = skinBones[int(bone.y) * 8 + 7];
mat4 getSkinningMat() { matA[2][0] = skinBones[int(bone.z) * 8 + 0];
return weight.x * getBoneMat(int(bone.x)) + matA[2][1] = skinBones[int(bone.z) * 8 + 1];
weight.y * getBoneMat(int(bone.y)) + matA[2][2] = skinBones[int(bone.z) * 8 + 2];
weight.z * getBoneMat(int(bone.z)) + matA[2][3] = skinBones[int(bone.z) * 8 + 3];
weight.w * getBoneMat(int(bone.w)); matB[2][0] = skinBones[int(bone.z) * 8 + 4];
} matB[2][1] = skinBones[int(bone.z) * 8 + 5];
matB[2][2] = skinBones[int(bone.z) * 8 + 6];
mat3 getSkinningMatVec(const mat4 skinningMat) { matB[2][3] = skinBones[int(bone.z) * 8 + 7];
return mat3(skinningMat[0].xyz, skinningMat[1].xyz, skinningMat[2].xyz); matA[3][0] = skinBones[int(bone.w) * 8 + 0];
matA[3][1] = skinBones[int(bone.w) * 8 + 1];
matA[3][2] = skinBones[int(bone.w) * 8 + 2];
matA[3][3] = skinBones[int(bone.w) * 8 + 3];
matB[3][0] = skinBones[int(bone.w) * 8 + 4];
matB[3][1] = skinBones[int(bone.w) * 8 + 5];
matB[3][2] = skinBones[int(bone.w) * 8 + 6];
matB[3][3] = skinBones[int(bone.w) * 8 + 7];
// Handles antipodality by sticking joints in the same neighbourhood
// weights.xyz *= sign(matA[3] * mat3x4(matA)).xyz;
weights.xyz *= sign(matA[3] * matA).xyz;
// Apply weights
A = matA * weights; // Real part
B = matB * weights; // Dual part
// Normalize
float invNormA = 1.0 / length(A);
A *= invNormA;
B *= invNormA;
} }
#endif #endif
@ -70,9 +87,11 @@ void main() {
#endif #endif
#ifdef _Skinning #ifdef _Skinning
mat4 skinningMat = getSkinningMat(); vec4 skinA;
mat3 skinningMatVec = getSkinningMatVec(skinningMat); vec4 skinB;
sPos = sPos * skinningMat; getSkinningDualQuat(weight, skinA, skinB);
sPos.xyz += 2.0 * cross(skinA.xyz, cross(skinA.xyz, sPos.xyz) + skinA.w * sPos.xyz); // Rotate
sPos.xyz += 2.0 * (skinA.w * skinB.xyz - skinB.w * skinA.xyz + cross(skinA.xyz, skinB.xyz)); // Translate
#endif #endif
gl_Position = WVP * sPos; gl_Position = WVP * sPos;

View file

@ -34,7 +34,7 @@ uniform mat4 LWVP;
uniform vec4 albedo_color; uniform vec4 albedo_color;
uniform vec3 eye; uniform vec3 eye;
#ifdef _Skinning #ifdef _Skinning
uniform float skinBones[skinMaxBones * 12]; uniform float skinBones[skinMaxBones * 8];
#endif #endif
out vec3 position; out vec3 position;
@ -51,34 +51,51 @@ out vec3 eyeDir;
#endif #endif
#ifdef _Skinning #ifdef _Skinning
mat4 getBoneMat(const int boneIndex) { void getSkinningDualQuat(vec4 weights, out vec4 A, inout vec4 B) {
vec4 v0 = vec4(skinBones[boneIndex * 12 + 0], // Retrieve the real and dual part of the dual-quaternions
skinBones[boneIndex * 12 + 1], mat4 matA, matB;
skinBones[boneIndex * 12 + 2], matA[0][0] = skinBones[int(bone.x) * 8 + 0];
skinBones[boneIndex * 12 + 3]); matA[0][1] = skinBones[int(bone.x) * 8 + 1];
vec4 v1 = vec4(skinBones[boneIndex * 12 + 4], matA[0][2] = skinBones[int(bone.x) * 8 + 2];
skinBones[boneIndex * 12 + 5], matA[0][3] = skinBones[int(bone.x) * 8 + 3];
skinBones[boneIndex * 12 + 6], matB[0][0] = skinBones[int(bone.x) * 8 + 4];
skinBones[boneIndex * 12 + 7]); matB[0][1] = skinBones[int(bone.x) * 8 + 5];
vec4 v2 = vec4(skinBones[boneIndex * 12 + 8], matB[0][2] = skinBones[int(bone.x) * 8 + 6];
skinBones[boneIndex * 12 + 9], matB[0][3] = skinBones[int(bone.x) * 8 + 7];
skinBones[boneIndex * 12 + 10], matA[1][0] = skinBones[int(bone.y) * 8 + 0];
skinBones[boneIndex * 12 + 11]); matA[1][1] = skinBones[int(bone.y) * 8 + 1];
return mat4(v0.x, v0.y, v0.z, v0.w, matA[1][2] = skinBones[int(bone.y) * 8 + 2];
v1.x, v1.y, v1.z, v1.w, matA[1][3] = skinBones[int(bone.y) * 8 + 3];
v2.x, v2.y, v2.z, v2.w, matB[1][0] = skinBones[int(bone.y) * 8 + 4];
0, 0, 0, 1); matB[1][1] = skinBones[int(bone.y) * 8 + 5];
} matB[1][2] = skinBones[int(bone.y) * 8 + 6];
matB[1][3] = skinBones[int(bone.y) * 8 + 7];
mat4 getSkinningMat() { matA[2][0] = skinBones[int(bone.z) * 8 + 0];
return weight.x * getBoneMat(int(bone.x)) + matA[2][1] = skinBones[int(bone.z) * 8 + 1];
weight.y * getBoneMat(int(bone.y)) + matA[2][2] = skinBones[int(bone.z) * 8 + 2];
weight.z * getBoneMat(int(bone.z)) + matA[2][3] = skinBones[int(bone.z) * 8 + 3];
weight.w * getBoneMat(int(bone.w)); matB[2][0] = skinBones[int(bone.z) * 8 + 4];
} matB[2][1] = skinBones[int(bone.z) * 8 + 5];
matB[2][2] = skinBones[int(bone.z) * 8 + 6];
mat3 getSkinningMatVec(const mat4 skinningMat) { matB[2][3] = skinBones[int(bone.z) * 8 + 7];
return mat3(skinningMat[0].xyz, skinningMat[1].xyz, skinningMat[2].xyz); matA[3][0] = skinBones[int(bone.w) * 8 + 0];
matA[3][1] = skinBones[int(bone.w) * 8 + 1];
matA[3][2] = skinBones[int(bone.w) * 8 + 2];
matA[3][3] = skinBones[int(bone.w) * 8 + 3];
matB[3][0] = skinBones[int(bone.w) * 8 + 4];
matB[3][1] = skinBones[int(bone.w) * 8 + 5];
matB[3][2] = skinBones[int(bone.w) * 8 + 6];
matB[3][3] = skinBones[int(bone.w) * 8 + 7];
// Handles antipodality by sticking joints in the same neighbourhood
// weights.xyz *= sign(matA[3] * mat3x4(matA)).xyz;
weights.xyz *= sign(matA[3] * matA).xyz;
// Apply weights
A = matA * weights; // Real part
B = matB * weights; // Dual part
// Normalize
float invNormA = 1.0 / length(A);
A *= invNormA;
B *= invNormA;
} }
#endif #endif
@ -89,11 +106,18 @@ void main() {
#else #else
vec4 sPos = (vec4(pos, 1.0)); vec4 sPos = (vec4(pos, 1.0));
#endif #endif
#ifdef _Skinning #ifdef _Skinning
mat4 skinningMat = getSkinningMat(); vec4 skinA;
mat3 skinningMatVec = getSkinningMatVec(skinningMat); vec4 skinB;
sPos = sPos * skinningMat; getSkinningDualQuat(weight, skinA, skinB);
sPos.xyz += 2.0 * cross(skinA.xyz, cross(skinA.xyz, sPos.xyz) + skinA.w * sPos.xyz); // Rotate
sPos.xyz += 2.0 * (skinA.w * skinB.xyz - skinB.w * skinA.xyz + cross(skinA.xyz, skinB.xyz)); // Translate
vec3 _normal = normalize(mat3(N) * (nor + 2.0 * cross(skinA.xyz, cross(skinA.xyz, nor) + skinA.w * nor)));
#else
vec3 _normal = normalize(mat3(N) * nor);
#endif #endif
lPos = LWVP * sPos; lPos = LWVP * sPos;
mat4 WV = V * W; mat4 WV = V * W;
@ -115,12 +139,6 @@ void main() {
texCoord = tex; texCoord = tex;
#endif #endif
#ifdef _Skinning
vec3 _normal = normalize(mat3(N) * (nor * skinningMatVec));
#else
vec3 _normal = normalize(mat3(N) * nor);
#endif
matColor = albedo_color; matColor = albedo_color;
#ifdef _VCols #ifdef _VCols

View file

@ -37,7 +37,7 @@ uniform mat4 LWVP;
uniform vec4 albedo_color; uniform vec4 albedo_color;
uniform vec3 eye; uniform vec3 eye;
#ifdef _Skinning #ifdef _Skinning
uniform float skinBones[skinMaxBones * 12]; uniform float skinBones[skinMaxBones * 8];
#endif #endif
out vec3 position; out vec3 position;
@ -54,34 +54,51 @@ out vec3 eyeDir;
#endif #endif
#ifdef _Skinning #ifdef _Skinning
mat4 getBoneMat(const int boneIndex) { void getSkinningDualQuat(vec4 weights, out vec4 A, inout vec4 B) {
vec4 v0 = vec4(skinBones[boneIndex * 12 + 0], // Retrieve the real and dual part of the dual-quaternions
skinBones[boneIndex * 12 + 1], mat4 matA, matB;
skinBones[boneIndex * 12 + 2], matA[0][0] = skinBones[int(bone.x) * 8 + 0];
skinBones[boneIndex * 12 + 3]); matA[0][1] = skinBones[int(bone.x) * 8 + 1];
vec4 v1 = vec4(skinBones[boneIndex * 12 + 4], matA[0][2] = skinBones[int(bone.x) * 8 + 2];
skinBones[boneIndex * 12 + 5], matA[0][3] = skinBones[int(bone.x) * 8 + 3];
skinBones[boneIndex * 12 + 6], matB[0][0] = skinBones[int(bone.x) * 8 + 4];
skinBones[boneIndex * 12 + 7]); matB[0][1] = skinBones[int(bone.x) * 8 + 5];
vec4 v2 = vec4(skinBones[boneIndex * 12 + 8], matB[0][2] = skinBones[int(bone.x) * 8 + 6];
skinBones[boneIndex * 12 + 9], matB[0][3] = skinBones[int(bone.x) * 8 + 7];
skinBones[boneIndex * 12 + 10], matA[1][0] = skinBones[int(bone.y) * 8 + 0];
skinBones[boneIndex * 12 + 11]); matA[1][1] = skinBones[int(bone.y) * 8 + 1];
return mat4(v0.x, v0.y, v0.z, v0.w, matA[1][2] = skinBones[int(bone.y) * 8 + 2];
v1.x, v1.y, v1.z, v1.w, matA[1][3] = skinBones[int(bone.y) * 8 + 3];
v2.x, v2.y, v2.z, v2.w, matB[1][0] = skinBones[int(bone.y) * 8 + 4];
0, 0, 0, 1); matB[1][1] = skinBones[int(bone.y) * 8 + 5];
} matB[1][2] = skinBones[int(bone.y) * 8 + 6];
matB[1][3] = skinBones[int(bone.y) * 8 + 7];
mat4 getSkinningMat() { matA[2][0] = skinBones[int(bone.z) * 8 + 0];
return weight.x * getBoneMat(int(bone.x)) + matA[2][1] = skinBones[int(bone.z) * 8 + 1];
weight.y * getBoneMat(int(bone.y)) + matA[2][2] = skinBones[int(bone.z) * 8 + 2];
weight.z * getBoneMat(int(bone.z)) + matA[2][3] = skinBones[int(bone.z) * 8 + 3];
weight.w * getBoneMat(int(bone.w)); matB[2][0] = skinBones[int(bone.z) * 8 + 4];
} matB[2][1] = skinBones[int(bone.z) * 8 + 5];
matB[2][2] = skinBones[int(bone.z) * 8 + 6];
mat3 getSkinningMatVec(const mat4 skinningMat) { matB[2][3] = skinBones[int(bone.z) * 8 + 7];
return mat3(skinningMat[0].xyz, skinningMat[1].xyz, skinningMat[2].xyz); matA[3][0] = skinBones[int(bone.w) * 8 + 0];
matA[3][1] = skinBones[int(bone.w) * 8 + 1];
matA[3][2] = skinBones[int(bone.w) * 8 + 2];
matA[3][3] = skinBones[int(bone.w) * 8 + 3];
matB[3][0] = skinBones[int(bone.w) * 8 + 4];
matB[3][1] = skinBones[int(bone.w) * 8 + 5];
matB[3][2] = skinBones[int(bone.w) * 8 + 6];
matB[3][3] = skinBones[int(bone.w) * 8 + 7];
// Handles antipodality by sticking joints in the same neighbourhood
// weights.xyz *= sign(matA[3] * mat3x4(matA)).xyz;
weights.xyz *= sign(matA[3] * matA).xyz;
// Apply weights
A = matA * weights; // Real part
B = matB * weights; // Dual part
// Normalize
float invNormA = 1.0 / length(A);
A *= invNormA;
B *= invNormA;
} }
#endif #endif
@ -92,11 +109,18 @@ void main() {
#else #else
vec4 sPos = (vec4(pos, 1.0)); vec4 sPos = (vec4(pos, 1.0));
#endif #endif
#ifdef _Skinning #ifdef _Skinning
mat4 skinningMat = getSkinningMat(); vec4 skinA;
mat3 skinningMatVec = getSkinningMatVec(skinningMat); vec4 skinB;
sPos = sPos * skinningMat; getSkinningDualQuat(weight, skinA, skinB);
sPos.xyz += 2.0 * cross(skinA.xyz, cross(skinA.xyz, sPos.xyz) + skinA.w * sPos.xyz); // Rotate
sPos.xyz += 2.0 * (skinA.w * skinB.xyz - skinB.w * skinA.xyz + cross(skinA.xyz, skinB.xyz)); // Translate
vec3 _normal = normalize(mat3(N) * (nor + 2.0 * cross(skinA.xyz, cross(skinA.xyz, nor) + skinA.w * nor)));
#else
vec3 _normal = normalize(mat3(N) * nor);
#endif #endif
lPos = LWVP * sPos; lPos = LWVP * sPos;
mat4 WV = V * W; mat4 WV = V * W;
@ -117,12 +141,6 @@ void main() {
texCoord = tex; texCoord = tex;
#endif #endif
#ifdef _Skinning
vec3 _normal = normalize(mat3(N) * (nor * skinningMatVec));
#else
vec3 _normal = normalize(mat3(N) * nor);
#endif
matColor = albedo_color; matColor = albedo_color;
#ifdef _VCols #ifdef _VCols

View file

@ -28,38 +28,55 @@ in vec3 nor;
uniform mat4 LWVP; uniform mat4 LWVP;
#ifdef _Skinning #ifdef _Skinning
uniform float skinBones[skinMaxBones * 12]; uniform float skinBones[skinMaxBones * 8];
#endif #endif
#ifdef _Skinning #ifdef _Skinning
mat4 getBoneMat(const int boneIndex) { void getSkinningDualQuat(vec4 weights, out vec4 A, inout vec4 B) {
vec4 v0 = vec4(skinBones[boneIndex * 12 + 0], // Retrieve the real and dual part of the dual-quaternions
skinBones[boneIndex * 12 + 1], mat4 matA, matB;
skinBones[boneIndex * 12 + 2], matA[0][0] = skinBones[int(bone.x) * 8 + 0];
skinBones[boneIndex * 12 + 3]); matA[0][1] = skinBones[int(bone.x) * 8 + 1];
vec4 v1 = vec4(skinBones[boneIndex * 12 + 4], matA[0][2] = skinBones[int(bone.x) * 8 + 2];
skinBones[boneIndex * 12 + 5], matA[0][3] = skinBones[int(bone.x) * 8 + 3];
skinBones[boneIndex * 12 + 6], matB[0][0] = skinBones[int(bone.x) * 8 + 4];
skinBones[boneIndex * 12 + 7]); matB[0][1] = skinBones[int(bone.x) * 8 + 5];
vec4 v2 = vec4(skinBones[boneIndex * 12 + 8], matB[0][2] = skinBones[int(bone.x) * 8 + 6];
skinBones[boneIndex * 12 + 9], matB[0][3] = skinBones[int(bone.x) * 8 + 7];
skinBones[boneIndex * 12 + 10], matA[1][0] = skinBones[int(bone.y) * 8 + 0];
skinBones[boneIndex * 12 + 11]); matA[1][1] = skinBones[int(bone.y) * 8 + 1];
return mat4(v0.x, v0.y, v0.z, v0.w, matA[1][2] = skinBones[int(bone.y) * 8 + 2];
v1.x, v1.y, v1.z, v1.w, matA[1][3] = skinBones[int(bone.y) * 8 + 3];
v2.x, v2.y, v2.z, v2.w, matB[1][0] = skinBones[int(bone.y) * 8 + 4];
0, 0, 0, 1); matB[1][1] = skinBones[int(bone.y) * 8 + 5];
} matB[1][2] = skinBones[int(bone.y) * 8 + 6];
matB[1][3] = skinBones[int(bone.y) * 8 + 7];
mat4 getSkinningMat() { matA[2][0] = skinBones[int(bone.z) * 8 + 0];
return weight.x * getBoneMat(int(bone.x)) + matA[2][1] = skinBones[int(bone.z) * 8 + 1];
weight.y * getBoneMat(int(bone.y)) + matA[2][2] = skinBones[int(bone.z) * 8 + 2];
weight.z * getBoneMat(int(bone.z)) + matA[2][3] = skinBones[int(bone.z) * 8 + 3];
weight.w * getBoneMat(int(bone.w)); matB[2][0] = skinBones[int(bone.z) * 8 + 4];
} matB[2][1] = skinBones[int(bone.z) * 8 + 5];
matB[2][2] = skinBones[int(bone.z) * 8 + 6];
mat3 getSkinningMatVec(const mat4 skinningMat) { matB[2][3] = skinBones[int(bone.z) * 8 + 7];
return mat3(skinningMat[0].xyz, skinningMat[1].xyz, skinningMat[2].xyz); matA[3][0] = skinBones[int(bone.w) * 8 + 0];
matA[3][1] = skinBones[int(bone.w) * 8 + 1];
matA[3][2] = skinBones[int(bone.w) * 8 + 2];
matA[3][3] = skinBones[int(bone.w) * 8 + 3];
matB[3][0] = skinBones[int(bone.w) * 8 + 4];
matB[3][1] = skinBones[int(bone.w) * 8 + 5];
matB[3][2] = skinBones[int(bone.w) * 8 + 6];
matB[3][3] = skinBones[int(bone.w) * 8 + 7];
// Handles antipodality by sticking joints in the same neighbourhood
// weights.xyz *= sign(matA[3] * mat3x4(matA)).xyz;
weights.xyz *= sign(matA[3] * matA).xyz;
// Apply weights
A = matA * weights; // Real part
B = matB * weights; // Dual part
// Normalize
float invNormA = 1.0 / length(A);
A *= invNormA;
B *= invNormA;
} }
#endif #endif
@ -71,9 +88,11 @@ void main() {
#endif #endif
#ifdef _Skinning #ifdef _Skinning
mat4 skinningMat = getSkinningMat(); vec4 skinA;
mat3 skinningMatVec = getSkinningMatVec(skinningMat); vec4 skinB;
sPos = sPos * skinningMat; getSkinningDualQuat(weight, skinA, skinB);
sPos.xyz += 2.0 * cross(skinA.xyz, cross(skinA.xyz, sPos.xyz) + skinA.w * sPos.xyz); // Rotate
sPos.xyz += 2.0 * (skinA.w * skinB.xyz - skinB.w * skinA.xyz + cross(skinA.xyz, skinB.xyz)); // Translate
#endif #endif
gl_Position = LWVP * sPos; gl_Position = LWVP * sPos;

View file

@ -187,7 +187,7 @@ vec2 unpackFloat(float f) {
void main() { void main() {
vec4 g0 = texture(gbuffer0, texCoord); vec4 g0 = texture(gbuffer0, texCoord);
float roughness = unpackFloat(g0.b).x; float roughness = unpackFloat(g0.b).y;
if (roughness == 1.0) { if (roughness == 1.0) {
outColor = vec4(0.0); outColor = vec4(0.0);