From ca373d6bfd6ce80fad8914184545482c6640f5df Mon Sep 17 00:00:00 2001 From: Lubos Lenco Date: Sun, 10 Jul 2016 00:51:39 +0200 Subject: [PATCH] Finalizing SDK. --- Assets/{envmap_brdf.png => brdf.png} | Bin Assets/checker.png | Bin 3441 -> 0 bytes Assets/checker_r.png | Bin 3434 -> 0 bytes Assets/{DroidSans.ttf => droid_sans.ttf} | Bin Assets/noise.png | Bin 120 -> 0 bytes Assets/{SMAAArea.png => smaa_area.png} | Bin Assets/{SMAASearch.png => smaa_search.png} | Bin Sources/{cycles => armory}/Root.hx | 18 +- Sources/{cycles => armory}/node/BoolNode.hx | 2 +- Sources/{cycles => armory}/node/FloatNode.hx | 2 +- Sources/{cycles => armory}/node/IntNode.hx | 2 +- Sources/{cycles => armory}/node/Node.hx | 4 +- .../{cycles => armory}/node/ScaleValueNode.hx | 2 +- Sources/{cycles => armory}/node/SineNode.hx | 2 +- Sources/{cycles => armory}/node/StringNode.hx | 2 +- Sources/{cycles => armory}/node/TimeNode.hx | 6 +- .../{cycles => armory}/node/TransformNode.hx | 10 +- Sources/{cycles => armory}/node/VectorNode.hx | 2 +- .../{cycles => armory}/renderpipeline/FFT.hx | 6 +- .../renderpipeline/HosekWilkie.hx | 8 +- .../renderpipeline/HosekWilkieData.hx | 2 +- .../renderpipeline/SMAAData.hx | 2 +- .../{cycles => armory}/trait/ArcBallCamera.hx | 22 +- .../trait/FirstPersonController.hx | 28 +- Sources/{cycles => armory}/trait/FlyCamera.hx | 48 +- .../{cycles => armory}/trait/PhysicsDrag.hx | 12 +- .../{cycles => armory}/trait/VehicleBody.hx | 17 +- .../{cycles => armory}/trait/VehicleWheel.hx | 4 +- .../trait/internal}/Animation.hx | 16 +- .../trait/internal}/MovieTexture.hx | 12 +- .../trait/internal}/NodeExecutor.hx | 10 +- .../trait/internal}/PathTracer.hx | 24 +- .../trait/internal}/PhysicsWorld.hx | 16 +- .../trait/internal}/RigidBody.hx | 20 +- .../trait/internal}/SceneInstance.hx | 8 +- blender/data/data.blend | Bin 1134072 -> 900044 bytes blender/exporter.py | 51 +- blender/lib/compile.py | 80 - blender/lib/make_resources.py | 4 +- blender/lib/make_variants.py | 4 +- blender/lib/server.py | 2 +- blender/nodes_logic.py | 6 +- blender/nodes_material.py | 2 +- blender/nodes_pipeline.py | 2028 ++++++++++------- blender/nodes_world.py | 7 +- blender/project.py | 239 +- blender/props.py | 24 +- blender/start.py | 21 +- blender/traits.py | 99 +- blender/traits_animation.py | 33 +- blender/traits_params.py | 40 +- blender/utils.py | 27 +- blender/write_data.py | 130 +- raw/compositor_pass/compositor_pass.frag.glsl | 10 +- raw/deferred/decals.frag.glsl | 12 - raw/deferred_light/deferred_light.frag.glsl | 58 +- raw/env_map/env_map.frag.glsl | 16 +- raw/env_map/env_map.shader.json | 5 + .../motion_blur_pass.frag.glsl | 9 +- raw/ssao_pass/ssao_pass.frag.glsl | 1 - raw/ssao_pass/ssao_pass_st.frag.glsl | 145 -- raw/ssr_pass/ssr_pass.frag.glsl | 8 +- raw/ssr_pass/ssr_pass_kode.frag.glsl | 263 --- raw/ssr_pass/ssr_pass_kode.vert.glsl | 25 - raw/sss_pass/sss_pass.frag.glsl | 8 +- raw/water_pass/water_pass.frag.glsl | 15 +- 66 files changed, 1956 insertions(+), 1723 deletions(-) rename Assets/{envmap_brdf.png => brdf.png} (100%) delete mode 100644 Assets/checker.png delete mode 100644 Assets/checker_r.png rename Assets/{DroidSans.ttf => droid_sans.ttf} (100%) delete mode 100644 Assets/noise.png rename Assets/{SMAAArea.png => smaa_area.png} (100%) rename Assets/{SMAASearch.png => smaa_search.png} (100%) rename Sources/{cycles => armory}/Root.hx (69%) rename Sources/{cycles => armory}/node/BoolNode.hx (90%) rename Sources/{cycles => armory}/node/FloatNode.hx (90%) rename Sources/{cycles => armory}/node/IntNode.hx (89%) rename Sources/{cycles => armory}/node/Node.hx (87%) rename Sources/{cycles => armory}/node/ScaleValueNode.hx (95%) rename Sources/{cycles => armory}/node/SineNode.hx (94%) rename Sources/{cycles => armory}/node/StringNode.hx (90%) rename Sources/{cycles => armory}/node/TimeNode.hx (94%) rename Sources/{cycles => armory}/node/TransformNode.hx (92%) rename Sources/{cycles => armory}/node/VectorNode.hx (95%) rename Sources/{cycles => armory}/renderpipeline/FFT.hx (80%) rename Sources/{cycles => armory}/renderpipeline/HosekWilkie.hx (97%) rename Sources/{cycles => armory}/renderpipeline/HosekWilkieData.hx (99%) rename Sources/{cycles => armory}/renderpipeline/SMAAData.hx (99%) rename Sources/{cycles => armory}/trait/ArcBallCamera.hx (68%) rename Sources/{cycles => armory}/trait/FirstPersonController.hx (89%) rename Sources/{cycles => armory}/trait/FlyCamera.hx (60%) rename Sources/{cycles => armory}/trait/PhysicsDrag.hx (95%) rename Sources/{cycles => armory}/trait/VehicleBody.hx (96%) rename Sources/{cycles => armory}/trait/VehicleWheel.hx (97%) rename Sources/{cycles/trait => armory/trait/internal}/Animation.hx (78%) rename Sources/{cycles/trait => armory/trait/internal}/MovieTexture.hx (85%) rename Sources/{cycles/trait => armory/trait/internal}/NodeExecutor.hx (69%) rename Sources/{cycles/trait => armory/trait/internal}/PathTracer.hx (92%) rename Sources/{cycles/trait => armory/trait/internal}/PhysicsWorld.hx (94%) rename Sources/{cycles/trait => armory/trait/internal}/RigidBody.hx (96%) rename Sources/{cycles/trait => armory/trait/internal}/SceneInstance.hx (59%) delete mode 100644 blender/lib/compile.py delete mode 100644 raw/ssao_pass/ssao_pass_st.frag.glsl delete mode 100644 raw/ssr_pass/ssr_pass_kode.frag.glsl delete mode 100644 raw/ssr_pass/ssr_pass_kode.vert.glsl diff --git a/Assets/envmap_brdf.png b/Assets/brdf.png similarity index 100% rename from Assets/envmap_brdf.png rename to Assets/brdf.png diff --git a/Assets/checker.png b/Assets/checker.png deleted file mode 100644 index 3c8655059e0eb7e66d4da3e164272abd45c105f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3441 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 Uz$(|1K%*EuUHx3vIVCg!00e|7VgLXD diff --git a/Assets/SMAAArea.png b/Assets/smaa_area.png similarity index 100% rename from Assets/SMAAArea.png rename to Assets/smaa_area.png diff --git a/Assets/SMAASearch.png b/Assets/smaa_search.png similarity index 100% rename from Assets/SMAASearch.png rename to Assets/smaa_search.png diff --git a/Sources/cycles/Root.hx b/Sources/armory/Root.hx similarity index 69% rename from Sources/cycles/Root.hx rename to Sources/armory/Root.hx index 3d25e5d8..33f74e40 100644 --- a/Sources/cycles/Root.hx +++ b/Sources/armory/Root.hx @@ -1,12 +1,12 @@ -package cycles; +package armory; -import lue.App; -import lue.Eg; -import lue.node.RootNode; -import lue.node.CameraNode; -import lue.resource.SceneFormat; -import lue.resource.Resource; -import cycles.trait.PhysicsWorld; +import iron.App; +import iron.Eg; +import iron.node.RootNode; +import iron.node.CameraNode; +import iron.resource.SceneFormat; +import iron.resource.Resource; +import armory.trait.internal.PhysicsWorld; class Root { @@ -28,7 +28,7 @@ class Root { physics = new PhysicsWorld(); Eg.addNodeTrait(sceneNode, physics); - App.requestRender(render); + App.notifyOnRender(render); } function render(g:kha.graphics4.Graphics) { diff --git a/Sources/cycles/node/BoolNode.hx b/Sources/armory/node/BoolNode.hx similarity index 90% rename from Sources/cycles/node/BoolNode.hx rename to Sources/armory/node/BoolNode.hx index 1edbb978..d12bde47 100644 --- a/Sources/cycles/node/BoolNode.hx +++ b/Sources/armory/node/BoolNode.hx @@ -1,4 +1,4 @@ -package cycles.node; +package armory.node; class BoolNode extends Node { diff --git a/Sources/cycles/node/FloatNode.hx b/Sources/armory/node/FloatNode.hx similarity index 90% rename from Sources/cycles/node/FloatNode.hx rename to Sources/armory/node/FloatNode.hx index cf313c80..223eefce 100644 --- a/Sources/cycles/node/FloatNode.hx +++ b/Sources/armory/node/FloatNode.hx @@ -1,4 +1,4 @@ -package cycles.node; +package armory.node; class FloatNode extends Node { diff --git a/Sources/cycles/node/IntNode.hx b/Sources/armory/node/IntNode.hx similarity index 89% rename from Sources/cycles/node/IntNode.hx rename to Sources/armory/node/IntNode.hx index 12028dc6..3e7a8afd 100644 --- a/Sources/cycles/node/IntNode.hx +++ b/Sources/armory/node/IntNode.hx @@ -1,4 +1,4 @@ -package cycles.node; +package armory.node; class IntNode extends Node { diff --git a/Sources/cycles/node/Node.hx b/Sources/armory/node/Node.hx similarity index 87% rename from Sources/cycles/node/Node.hx rename to Sources/armory/node/Node.hx index 75a11414..6d30a877 100644 --- a/Sources/cycles/node/Node.hx +++ b/Sources/armory/node/Node.hx @@ -1,6 +1,6 @@ -package cycles.node; +package armory.node; -import cycles.trait.NodeExecutor; +import armory.trait.internal.NodeExecutor; class Node { diff --git a/Sources/cycles/node/ScaleValueNode.hx b/Sources/armory/node/ScaleValueNode.hx similarity index 95% rename from Sources/cycles/node/ScaleValueNode.hx rename to Sources/armory/node/ScaleValueNode.hx index 80ad78fb..76ec72d9 100644 --- a/Sources/cycles/node/ScaleValueNode.hx +++ b/Sources/armory/node/ScaleValueNode.hx @@ -1,4 +1,4 @@ -package cycles.node; +package armory.node; class ScaleValueNode extends FloatNode { diff --git a/Sources/cycles/node/SineNode.hx b/Sources/armory/node/SineNode.hx similarity index 94% rename from Sources/cycles/node/SineNode.hx rename to Sources/armory/node/SineNode.hx index 4c0fbb98..2922a57a 100644 --- a/Sources/cycles/node/SineNode.hx +++ b/Sources/armory/node/SineNode.hx @@ -1,4 +1,4 @@ -package cycles.node; +package armory.node; class SineNode extends FloatNode { diff --git a/Sources/cycles/node/StringNode.hx b/Sources/armory/node/StringNode.hx similarity index 90% rename from Sources/cycles/node/StringNode.hx rename to Sources/armory/node/StringNode.hx index 5a686e07..c84a5604 100644 --- a/Sources/cycles/node/StringNode.hx +++ b/Sources/armory/node/StringNode.hx @@ -1,4 +1,4 @@ -package cycles.node; +package armory.node; class StringNode extends Node { diff --git a/Sources/cycles/node/TimeNode.hx b/Sources/armory/node/TimeNode.hx similarity index 94% rename from Sources/cycles/node/TimeNode.hx rename to Sources/armory/node/TimeNode.hx index 9d3d5310..1833b626 100644 --- a/Sources/cycles/node/TimeNode.hx +++ b/Sources/armory/node/TimeNode.hx @@ -1,6 +1,6 @@ -package cycles.node; +package armory.node; -import cycles.trait.NodeExecutor; +import armory.trait.NodeExecutor; class TimeNode extends FloatNode { @@ -26,7 +26,7 @@ class TimeNode extends FloatNode { function update() { if (inputs[_enabled].b) { - f += lue.sys.Time.delta * inputs[_scale].f; + f += iron.sys.Time.delta * inputs[_scale].f; // Time out if (inputs[_stopTime].f > 0) { diff --git a/Sources/cycles/node/TransformNode.hx b/Sources/armory/node/TransformNode.hx similarity index 92% rename from Sources/cycles/node/TransformNode.hx rename to Sources/armory/node/TransformNode.hx index 723ce7aa..60b5c64e 100644 --- a/Sources/cycles/node/TransformNode.hx +++ b/Sources/armory/node/TransformNode.hx @@ -1,8 +1,8 @@ -package cycles.node; +package armory.node; -import lue.math.Mat4; -import lue.math.Vec4; -import lue.math.Quat; +import iron.math.Mat4; +import iron.math.Vec4; +import iron.math.Quat; class TransformNode extends Node { @@ -10,7 +10,7 @@ class TransformNode extends Node { public static inline var _rotation = 1; // Vector public static inline var _scale = 2; // Vector - public var transform:lue.node.Transform; + public var transform:iron.node.Transform; var matrix:Mat4; var pos:Vec4; diff --git a/Sources/cycles/node/VectorNode.hx b/Sources/armory/node/VectorNode.hx similarity index 95% rename from Sources/cycles/node/VectorNode.hx rename to Sources/armory/node/VectorNode.hx index 26faf1ef..f2298f51 100644 --- a/Sources/cycles/node/VectorNode.hx +++ b/Sources/armory/node/VectorNode.hx @@ -1,4 +1,4 @@ -package cycles.node; +package armory.node; class VectorNode extends Node { diff --git a/Sources/cycles/renderpipeline/FFT.hx b/Sources/armory/renderpipeline/FFT.hx similarity index 80% rename from Sources/cycles/renderpipeline/FFT.hx rename to Sources/armory/renderpipeline/FFT.hx index 43185fe4..b72c1047 100644 --- a/Sources/cycles/renderpipeline/FFT.hx +++ b/Sources/armory/renderpipeline/FFT.hx @@ -1,6 +1,6 @@ -package cycles.renderpipeline; +package armory.renderpipeline; -import lue.node.RenderPipeline; +import iron.node.RenderPath; class FFT { @@ -19,7 +19,7 @@ class FFT { // } // } - public static function run(pipe:RenderPipeline) { + public static function run(path:RenderPath) { } } diff --git a/Sources/cycles/renderpipeline/HosekWilkie.hx b/Sources/armory/renderpipeline/HosekWilkie.hx similarity index 97% rename from Sources/cycles/renderpipeline/HosekWilkie.hx rename to Sources/armory/renderpipeline/HosekWilkie.hx index a17d2101..d31f2d43 100644 --- a/Sources/cycles/renderpipeline/HosekWilkie.hx +++ b/Sources/armory/renderpipeline/HosekWilkie.hx @@ -1,11 +1,11 @@ // An Analytic Model for Full Spectral Sky-Dome Radiance // Lukas Hosek and Alexander Wilkie // Based on https://github.com/ddiakopoulos/sandbox -package cycles.renderpipeline; +package armory.renderpipeline; import kha.math.FastVector3; -import lue.node.RenderPipeline; -import lue.resource.WorldResource; +import iron.node.RenderPath; +import iron.resource.WorldResource; class HosekWilkieRadianceData { @@ -127,7 +127,7 @@ class HosekWilkie { HosekWilkie.recompute(sunPositionX, turbidity, albedo, 1.15); } - public static function run(pipe:RenderPipeline) { + public static function run(path:RenderPath) { // Set uniforms } } diff --git a/Sources/cycles/renderpipeline/HosekWilkieData.hx b/Sources/armory/renderpipeline/HosekWilkieData.hx similarity index 99% rename from Sources/cycles/renderpipeline/HosekWilkieData.hx rename to Sources/armory/renderpipeline/HosekWilkieData.hx index ea5b8e00..ae42ac72 100644 --- a/Sources/cycles/renderpipeline/HosekWilkieData.hx +++ b/Sources/armory/renderpipeline/HosekWilkieData.hx @@ -97,7 +97,7 @@ This file contains the coefficient data for the RGB colour space version of the model. */ -package cycles.renderpipeline; +package armory.renderpipeline; class HosekWilkieData { diff --git a/Sources/cycles/renderpipeline/SMAAData.hx b/Sources/armory/renderpipeline/SMAAData.hx similarity index 99% rename from Sources/cycles/renderpipeline/SMAAData.hx rename to Sources/armory/renderpipeline/SMAAData.hx index 46e12aa0..3621c2dc 100644 --- a/Sources/cycles/renderpipeline/SMAAData.hx +++ b/Sources/armory/renderpipeline/SMAAData.hx @@ -36,7 +36,7 @@ // #define SEARCHTEX_PITCH SEARCHTEX_WIDTH // #define SEARCHTEX_SIZE (SEARCHTEX_HEIGHT * SEARCHTEX_PITCH) -package cycles.renderpipeline; +package armory.renderpipeline; class SMAAAreaData { diff --git a/Sources/cycles/trait/ArcBallCamera.hx b/Sources/armory/trait/ArcBallCamera.hx similarity index 68% rename from Sources/cycles/trait/ArcBallCamera.hx rename to Sources/armory/trait/ArcBallCamera.hx index 2fec6d1e..11a598dc 100644 --- a/Sources/cycles/trait/ArcBallCamera.hx +++ b/Sources/armory/trait/ArcBallCamera.hx @@ -1,11 +1,11 @@ -package cycles.trait; +package armory.trait; -import lue.Trait; -import lue.sys.Input; -import lue.node.CameraNode; -import lue.node.RootNode; -import lue.math.Vec4; -import lue.math.Quat; +import iron.Trait; +import iron.sys.Input; +import iron.node.CameraNode; +import iron.node.RootNode; +import iron.math.Vec4; +import iron.math.Quat; class ArcBallCamera extends Trait { @@ -19,8 +19,8 @@ class ArcBallCamera extends Trait { origin = new Vec4(); - requestInit(init); - requestUpdate(update); + notifyOnInit(init); + notifyOnUpdate(update); } function init() { @@ -31,13 +31,13 @@ class ArcBallCamera extends Trait { q.inverse(q); var e = q.getEuler(); - pitchRad = lue.math.Math.degToRad(90) - e.x; + pitchRad = iron.math.Math.degToRad(90) - e.x; } function update() { if (Input.touch) { - var dist = lue.math.Math.distance3d(camera.transform.pos, origin); + var dist = iron.math.Math.distance3d(camera.transform.pos, origin); camera.move(camera.look(), dist); camera.rotate(camera.right(), pitchRad); diff --git a/Sources/cycles/trait/FirstPersonController.hx b/Sources/armory/trait/FirstPersonController.hx similarity index 89% rename from Sources/cycles/trait/FirstPersonController.hx rename to Sources/armory/trait/FirstPersonController.hx index 872bdf7b..0e9d60a6 100644 --- a/Sources/cycles/trait/FirstPersonController.hx +++ b/Sources/armory/trait/FirstPersonController.hx @@ -1,13 +1,13 @@ -package cycles.trait; +package armory.trait; -import lue.math.Mat4; -import lue.math.Vec4; -import lue.Trait; -import lue.sys.Input; -import lue.sys.Time; -import lue.node.Transform; -import lue.node.CameraNode; -import cycles.trait.RigidBody; +import iron.math.Mat4; +import iron.math.Vec4; +import iron.Trait; +import iron.sys.Input; +import iron.sys.Time; +import iron.node.Transform; +import iron.node.CameraNode; +import armory.trait.internal.RigidBody; class FirstPersonController extends Trait { @@ -28,15 +28,15 @@ class FirstPersonController extends Trait { public function new() { super(); - requestInit(init); - requestUpdate(update); + notifyOnInit(init); + notifyOnUpdate(update); kha.input.Keyboard.get().notify(onDown, onUp); } function init() { transform = node.transform; body = node.getTrait(RigidBody); - camera = lue.node.RootNode.cameras[0]; + camera = iron.node.RootNode.cameras[0]; } function onDown(key: kha.Key, char: String) { @@ -61,8 +61,8 @@ class FirstPersonController extends Trait { // Unlock // if (locked && - // Input.x > lue.App.w / 2 - 20 && Input.x < lue.App.w / 2 + 20 && - // Input.y > lue.App.h / 2 - 20 && Input.y < lue.App.h / 2 +20) { + // Input.x > iron.App.w / 2 - 20 && Input.x < iron.App.w / 2 + 20 && + // Input.y > iron.App.h / 2 - 20 && Input.y < iron.App.h / 2 +20) { // locked = false; // } diff --git a/Sources/cycles/trait/FlyCamera.hx b/Sources/armory/trait/FlyCamera.hx similarity index 60% rename from Sources/cycles/trait/FlyCamera.hx rename to Sources/armory/trait/FlyCamera.hx index 4e6f646c..3b3d73b0 100644 --- a/Sources/cycles/trait/FlyCamera.hx +++ b/Sources/armory/trait/FlyCamera.hx @@ -1,13 +1,13 @@ -package cycles.trait; +package armory.trait; import kha.Key; -import lue.Trait; -import lue.sys.Input; -import lue.sys.Time; -import lue.node.CameraNode; -import lue.node.RootNode; -import lue.math.Vec4; -import lue.math.Quat; +import iron.Trait; +import iron.sys.Input; +import iron.sys.Time; +import iron.node.CameraNode; +import iron.node.RootNode; +import iron.math.Vec4; +import iron.math.Quat; class FlyCamera extends Trait { @@ -27,9 +27,9 @@ class FlyCamera extends Trait { kha.input.Keyboard.get().notify(onKeyDown, onKeyUp); - requestInit(init); - requestUpdate(update); - requestRemove(removed); + notifyOnInit(init); + notifyOnUpdate(update); + notifyOnRemove(removed); } function removed() { @@ -44,7 +44,7 @@ class FlyCamera extends Trait { q.inverse(q); var e = q.getEuler(); - pitchRad = lue.math.Math.degToRad(90) - e.x; + pitchRad = iron.math.Math.degToRad(90) - e.x; } function update() { @@ -87,18 +87,18 @@ class FlyCamera extends Trait { else if (char == 'q') strafeForward = true; else if (char == 'e') strafeBackward = true; - // if (char == 'r') {lue.node.ModelNode._u1 += 0.01;trace("u1:", lue.node.ModelNode._u1);} - // else if (char == 'f') {lue.node.ModelNode._u1 -= 0.01;trace("u1:", lue.node.ModelNode._u1);} - // else if (char == 't') {lue.node.ModelNode._u2 += 0.01;trace("u2:", lue.node.ModelNode._u2);} - // else if (char == 'g') {lue.node.ModelNode._u2 -= 0.01;trace("u2:", lue.node.ModelNode._u2);} - // else if (char == 'y') {lue.node.ModelNode._u3 += 0.1;trace("u3:", lue.node.ModelNode._u3);} - // else if (char == 'h') {lue.node.ModelNode._u3 -= 0.1;trace("u3:", lue.node.ModelNode._u3);} - // else if (char == 'u') {lue.node.ModelNode._u4 += 0.1;trace("u4:", lue.node.ModelNode._u4);} - // else if (char == 'j') {lue.node.ModelNode._u4 -= 0.1;trace("u4:", lue.node.ModelNode._u4);} - // else if (char == 'i') {lue.node.ModelNode._u5 += 0.1;trace("u5:", lue.node.ModelNode._u5);} - // else if (char == 'k') {lue.node.ModelNode._u5 -= 0.1;trace("u5:", lue.node.ModelNode._u5);} - // else if (char == 'o') {lue.node.ModelNode._u6 += 0.005;trace("u6:", lue.node.ModelNode._u6);} - // else if (char == 'l') {lue.node.ModelNode._u6 -= 0.005;trace("u6:", lue.node.ModelNode._u6);} + // if (char == 'r') {iron.node.ModelNode._u1 += 0.01;trace("u1:", iron.node.ModelNode._u1);} + // else if (char == 'f') {iron.node.ModelNode._u1 -= 0.01;trace("u1:", iron.node.ModelNode._u1);} + // else if (char == 't') {iron.node.ModelNode._u2 += 0.01;trace("u2:", iron.node.ModelNode._u2);} + // else if (char == 'g') {iron.node.ModelNode._u2 -= 0.01;trace("u2:", iron.node.ModelNode._u2);} + // else if (char == 'y') {iron.node.ModelNode._u3 += 0.1;trace("u3:", iron.node.ModelNode._u3);} + // else if (char == 'h') {iron.node.ModelNode._u3 -= 0.1;trace("u3:", iron.node.ModelNode._u3);} + // else if (char == 'u') {iron.node.ModelNode._u4 += 0.1;trace("u4:", iron.node.ModelNode._u4);} + // else if (char == 'j') {iron.node.ModelNode._u4 -= 0.1;trace("u4:", iron.node.ModelNode._u4);} + // else if (char == 'i') {iron.node.ModelNode._u5 += 0.1;trace("u5:", iron.node.ModelNode._u5);} + // else if (char == 'k') {iron.node.ModelNode._u5 -= 0.1;trace("u5:", iron.node.ModelNode._u5);} + // else if (char == 'o') {iron.node.ModelNode._u6 += 0.005;trace("u6:", iron.node.ModelNode._u6);} + // else if (char == 'l') {iron.node.ModelNode._u6 -= 0.005;trace("u6:", iron.node.ModelNode._u6);} } function onKeyUp(key:kha.Key, char:String) { diff --git a/Sources/cycles/trait/PhysicsDrag.hx b/Sources/armory/trait/PhysicsDrag.hx similarity index 95% rename from Sources/cycles/trait/PhysicsDrag.hx rename to Sources/armory/trait/PhysicsDrag.hx index c8764ed6..7f89638a 100644 --- a/Sources/cycles/trait/PhysicsDrag.hx +++ b/Sources/armory/trait/PhysicsDrag.hx @@ -1,7 +1,9 @@ -package cycles.trait; +package armory.trait; -import lue.Trait; -import lue.sys.Input; +import iron.Trait; +import iron.sys.Input; +import armory.trait.internal.RigidBody; +import armory.trait.internal.PhysicsWorld; #if WITH_PHYSICS import haxebullet.Bullet; #end @@ -24,8 +26,8 @@ class PhysicsDrag extends Trait { public function new() { super(); - requestInit(init); - requestUpdate(update); + notifyOnInit(init); + notifyOnUpdate(update); } function init() { diff --git a/Sources/cycles/trait/VehicleBody.hx b/Sources/armory/trait/VehicleBody.hx similarity index 96% rename from Sources/cycles/trait/VehicleBody.hx rename to Sources/armory/trait/VehicleBody.hx index 4ee5e6d1..29880689 100644 --- a/Sources/cycles/trait/VehicleBody.hx +++ b/Sources/armory/trait/VehicleBody.hx @@ -1,10 +1,11 @@ -package cycles.trait; +package armory.trait; -import lue.Eg; -import lue.Trait; -import lue.node.RootNode; -import lue.node.CameraNode; -import lue.node.Transform; +import iron.Eg; +import iron.Trait; +import iron.node.RootNode; +import iron.node.CameraNode; +import iron.node.Transform; +import armory.trait.internal.PhysicsWorld; #if WITH_PHYSICS import haxebullet.Bullet; #end @@ -39,8 +40,8 @@ class VehicleBody extends Trait { wheelNames = [wheelName1, wheelName2, wheelName3, wheelName4]; - requestInit(init); - requestUpdate(update); + notifyOnInit(init); + notifyOnUpdate(update); kha.input.Keyboard.get().notify(onKeyDown, onKeyUp); } diff --git a/Sources/cycles/trait/VehicleWheel.hx b/Sources/armory/trait/VehicleWheel.hx similarity index 97% rename from Sources/cycles/trait/VehicleWheel.hx rename to Sources/armory/trait/VehicleWheel.hx index 310b1391..5ad80710 100644 --- a/Sources/cycles/trait/VehicleWheel.hx +++ b/Sources/armory/trait/VehicleWheel.hx @@ -1,6 +1,6 @@ -package cycles.trait; +package armory.trait; -import lue.Trait; +import iron.Trait; #if WITH_PHYSICS import haxebullet.Bullet; #end diff --git a/Sources/cycles/trait/Animation.hx b/Sources/armory/trait/internal/Animation.hx similarity index 78% rename from Sources/cycles/trait/Animation.hx rename to Sources/armory/trait/internal/Animation.hx index 02f2775c..58ee510d 100644 --- a/Sources/cycles/trait/Animation.hx +++ b/Sources/armory/trait/internal/Animation.hx @@ -1,9 +1,9 @@ -package cycles.trait; +package armory.trait.internal; -import lue.Trait; -import lue.Eg; -import lue.resource.Resource; -import lue.node.ModelNode; +import iron.Trait; +import iron.Eg; +import iron.resource.Resource; +import iron.node.ModelNode; class Animation extends Trait { @@ -22,8 +22,8 @@ class Animation extends Trait { this.starts = starts; this.ends = ends; - requestAdd(add); - requestUpdate(update); + notifyOnAdd(add); + notifyOnUpdate(update); } function add() { @@ -32,7 +32,7 @@ class Animation extends Trait { } function update() { - Eg.setAnimationParams(model, lue.sys.Time.delta); + Eg.setAnimationParams(model, iron.sys.Time.delta); } public function play(trackName:String, loop = true, speed = 1.0, onTrackComplete:Void->Void = null) { diff --git a/Sources/cycles/trait/MovieTexture.hx b/Sources/armory/trait/internal/MovieTexture.hx similarity index 85% rename from Sources/cycles/trait/MovieTexture.hx rename to Sources/armory/trait/internal/MovieTexture.hx index 6621d8d1..138ffca3 100644 --- a/Sources/cycles/trait/MovieTexture.hx +++ b/Sources/armory/trait/internal/MovieTexture.hx @@ -1,10 +1,10 @@ -package cycles.trait; +package armory.trait.internal; import kha.Image; import kha.Video; import kha.Assets; -import lue.Trait; -import lue.node.ModelNode; +import iron.Trait; +import iron.node.ModelNode; class MovieTexture extends Trait { @@ -36,8 +36,8 @@ class MovieTexture extends Trait { if (!created) { created = true; - requestInit(init); - requestRender2D(render); + notifyOnInit(init); + notifyOnRender2D(render); } } @@ -47,7 +47,7 @@ class MovieTexture extends Trait { image = Image.createRenderTarget(getPower2(video.width()), getPower2(video.height())); - var n = cast(node, lue.node.ModelNode); + var n = cast(node, iron.node.ModelNode); n.materials[0].contexts[0].textures[0] = image; // Override diffuse texture } diff --git a/Sources/cycles/trait/NodeExecutor.hx b/Sources/armory/trait/internal/NodeExecutor.hx similarity index 69% rename from Sources/cycles/trait/NodeExecutor.hx rename to Sources/armory/trait/internal/NodeExecutor.hx index 9b359d3e..b633c7d3 100644 --- a/Sources/cycles/trait/NodeExecutor.hx +++ b/Sources/armory/trait/internal/NodeExecutor.hx @@ -1,19 +1,19 @@ -package cycles.trait; +package armory.trait.internal; -import lue.Trait; +import iron.Trait; class NodeExecutor extends Trait { - var baseNode:cycles.node.Node; + var baseNode:armory.node.Node; var nodeUpdates:ArrayVoid> = []; public function new() { super(); - requestUpdate(update); + notifyOnUpdate(update); } - public function start(baseNode:cycles.node.Node) { + public function start(baseNode:armory.node.Node) { this.baseNode = baseNode; baseNode.start(this); } diff --git a/Sources/cycles/trait/PathTracer.hx b/Sources/armory/trait/internal/PathTracer.hx similarity index 92% rename from Sources/cycles/trait/PathTracer.hx rename to Sources/armory/trait/internal/PathTracer.hx index ffad83e1..96573b8a 100644 --- a/Sources/cycles/trait/PathTracer.hx +++ b/Sources/armory/trait/internal/PathTracer.hx @@ -1,13 +1,13 @@ -package cycles.trait; +package armory.trait.internal; -import lue.Eg; -import lue.math.Mat4; -import lue.math.Vec4; -import lue.Trait; -import lue.node.Transform; -import lue.node.RootNode; -import lue.node.ModelNode; -import lue.resource.MaterialResource.MaterialContext; +import iron.Eg; +import iron.math.Mat4; +import iron.math.Vec4; +import iron.Trait; +import iron.node.Transform; +import iron.node.RootNode; +import iron.node.ModelNode; +import iron.resource.MaterialResource.MaterialContext; class PathTracer extends Trait { @@ -23,8 +23,8 @@ class PathTracer extends Trait { public function new() { super(); - requestInit(init); - requestUpdate(update); + notifyOnInit(init); + notifyOnUpdate(update); } function getColorFromNode(node:ModelNode):Array { @@ -149,7 +149,7 @@ class PathTracer extends Trait { // var jitter = Mat4.identity(); // jitter.initTranslate(Math.random() * 2 - 1, Math.random() * 2 - 1, 0); - // jitter.multiplyScalar(1 / lue.App.w); + // jitter.multiplyScalar(1 / iron.App.w); // jitter.multiplyScalar(1 / 400); var mvp = Mat4.identity(); mvp.mult2(camera.V); diff --git a/Sources/cycles/trait/PhysicsWorld.hx b/Sources/armory/trait/internal/PhysicsWorld.hx similarity index 94% rename from Sources/cycles/trait/PhysicsWorld.hx rename to Sources/armory/trait/internal/PhysicsWorld.hx index 6310ede2..dedb67a0 100644 --- a/Sources/cycles/trait/PhysicsWorld.hx +++ b/Sources/armory/trait/internal/PhysicsWorld.hx @@ -1,12 +1,12 @@ -package cycles.trait; +package armory.trait.internal; #if WITH_PHYSICS import haxebullet.Bullet; #end -import lue.Trait; -import lue.sys.Time; -import lue.math.Vec4; -import lue.math.RayCaster; +import iron.Trait; +import iron.sys.Time; +import iron.math.Vec4; +import iron.math.RayCaster; class ContactPair { public var a:Int; @@ -51,7 +51,7 @@ class PhysicsWorld extends Trait { world = BtDiscreteDynamicsWorld.create(dispatcher, broadphase, solver, collisionConfiguration); world.ptr.setGravity(BtVector3.create(0, 0, -9.81).value); - requestUpdate(update); + notifyOnUpdate(update); } public function addRigidBody(body:RigidBody) { @@ -159,12 +159,12 @@ class PhysicsWorld extends Trait { } public function getRayFrom():BtVector3Pointer { - var camera = lue.node.RootNode.cameras[0]; + var camera = iron.node.RootNode.cameras[0]; return BtVector3.create(camera.transform.pos.x, camera.transform.pos.y, camera.transform.pos.z); } public function getRayTo(inputX:Float, inputY:Float):BtVector3Pointer { - var camera = lue.node.RootNode.cameras[0]; + var camera = iron.node.RootNode.cameras[0]; var start = new Vec4(); var end = new Vec4(); RayCaster.getDirection(start, end, inputX, inputY, camera); diff --git a/Sources/cycles/trait/RigidBody.hx b/Sources/armory/trait/internal/RigidBody.hx similarity index 96% rename from Sources/cycles/trait/RigidBody.hx rename to Sources/armory/trait/internal/RigidBody.hx index dc73e639..d93964c9 100644 --- a/Sources/cycles/trait/RigidBody.hx +++ b/Sources/armory/trait/internal/RigidBody.hx @@ -1,14 +1,14 @@ -package cycles.trait; +package armory.trait.internal; #if WITH_PHYSICS import haxebullet.Bullet; #end -import lue.Trait; -import lue.sys.Time; -import lue.math.Vec4; -import lue.node.Transform; -import lue.node.ModelNode; -import cycles.Root; +import iron.Trait; +import iron.sys.Time; +import iron.math.Vec4; +import iron.node.Transform; +import iron.node.ModelNode; +import armory.Root; class RigidBody extends Trait { @@ -51,9 +51,9 @@ class RigidBody extends Trait { this.friction = friction; this.collisionMargin = collisionMargin; - requestInit(init); - requestLateUpdate(lateUpdate); - requestRemove(removeFromWorld); + notifyOnInit(init); + notifyOnLateUpdate(lateUpdate); + notifyOnRemove(removeFromWorld); } inline function withMargin(f:Float) { diff --git a/Sources/cycles/trait/SceneInstance.hx b/Sources/armory/trait/internal/SceneInstance.hx similarity index 59% rename from Sources/cycles/trait/SceneInstance.hx rename to Sources/armory/trait/internal/SceneInstance.hx index 9dc5dc60..0586e264 100644 --- a/Sources/cycles/trait/SceneInstance.hx +++ b/Sources/armory/trait/internal/SceneInstance.hx @@ -1,14 +1,14 @@ -package cycles.trait; +package armory.trait.internal; -import lue.Trait; -import lue.Eg; +import iron.Trait; +import iron.Eg; class SceneInstance extends Trait { public function new(sceneId:String) { super(); - requestInit(function() { + notifyOnInit(function() { Eg.addScene(sceneId, node); }); } diff --git a/blender/data/data.blend b/blender/data/data.blend index 2c974dcd71a675a8116ee3de9b168fbaeccdafac..3585a07027331a72b4f5dbf0d81028d7c61a15d7 100644 GIT binary patch delta 122112 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 IkiGQ(0h4AUKmY&$ literal 1134072 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

vp^x1v_xsJXogb_B*xbhLs%p z@5}nE-}zPk&1=7!dlj3f|Gh8xMNYSV=U1Vd)_$d3owVNxT;ONG2Uh49I9Rf`s-LLx zLoc`corI?rqG$1N^xXf6zNEI^4mbmMNQFIOljON5?p#QfK2_z1#7y8`xS};>-2E%m zaEISpaYy-@b3WWA+7*f&!4;|B;{!hFQnV{*N6@bLhw=D}_9pKqGVZNIj5Px@1MHQ> zje&GU>*)pW*ztwVR`pZ6Li;8e>$rzX?MiG2F9>&iouSnbIDC1L&&IEs@RJuVwxa zn#y($E1_q7lI7v%^;Oz#YgcI_)^6V3SGsxoqr0^$$z-Ab20Rk^S?t)orFKPsZ)CE} zd$_L3kF0aFQfBLZnp!s%~`FpzzJ=6WB$`5&DA6ir7wtZr|aKE)f4YE}z`Gvl=I4;Ld61NXUD#Qcp`uc;B)-C>?w*08Ir!7AuX}=!0z`ubH z>Q8H-+`m-qm#gwaFSq-bgr^qzEam=XRB=8TI0JW-3-<7Cl-JFu_RCfIpf=pGyw)Ssq?AFiKK zzuv92=!7TRdo$(u=J~T)d;`wF-MbgZ9rlO~m2p>H~AabW}4B)YeV5yB@bc9CtOy7owwA2xA&jV!lGo3BNb-)>|B*(wxcC_@e; zncr1ljB#NP*vC7X+Xr?Fzw(CkT$Ab=S5ex}0A9}ex_N4K}E5q4y7p{0QW!$3*_wwel&7GQoB#jUZ5R8yV6|9<1gA9ujfUnzGvjw z3kS~y8tuy0qFos-wJZK{yd16LIW3rIq2k>?sp7V9cVFa)7r*w^zIG_HP4BN&Xg&11 z$e!PCh-lhc-+|Y4Z&z9$+Z@^RH~(PQ?GN;?feZW$_<+mPDDO9^>L;rF(97-kMtEx4 zM=ASRSg~CJ&cGezf<2lR%ebpPJE6)C{oH=`f^aV^e97lo*-Ot}#O282n#K&hs|vVf zX%>Mj0zX^?&>o^+V2#Vrym^DX@1$ybrpgb!`fJ6m-b<&x#eqY=Pq{sW9_do_JJHWX zzq7-~`)BBnMpwwsfcgXc*^3hwSI(QkUMc6~d0*0%`12`)vEwH=nKLx{otV$qRqA&( z9mdPiSuDy^B2&CKELGe-`u7&!C)f3F@2h+1Z!Fo^-n!!fO#XECjyz1a`vZOkT;ON1NgNk&dH()G^)tRk)~$Q#gr}w*V#2ZKdBd>cvlqY_xL0^N?yyJj z+HGTv7U5o9-7jU_{ZFW^VF1p+J$g9D9rozBPR5<@5}5C}+pfwFKXkjlPq?pt?tv1| znqKPf$K}ZV{Y8r^>*mnANXOZHL*cVDi$E5Etr7wBLlB1%m%T^wxQuw+G+ll+prmHu zy!^CxB{d*a2O^fm&B%ZS5>%b3SUyslU- z&)ezkS=vwJ8KqO@M^2e&50{S2HO=eyJU)aT=~CbhoPj&?fjy%4$hf0DW9^}%-U#?1 z$0^N3d$@GuxOUG=z0@AY< ziFLUnr1cT4WnzBeL^0l-DbsYhzc=j#z@4d4rt+Lzy*F*a5TpS>HGyhTt9Pj9)|GLw9zKKdhU069#y+90i1z* zv!CM*dsKWFllDkdeO^PAAN9fQ`$2^J`sY4M`F@aRgBtF@8Mu3n;kd&d(T`-@Ro6GD z@&k9b`}-XC8%jK{^-_O7E=TU~pHaKGVNPJ)`~Y7h(c|fyvo$1SU$aLRfh+<)b_CE5 zK^#V0ZaS97WyI^I4X;SggX!~ETjBZ$ipzzC|Cwsowda0_U$H#|&cHo(9LF8@h`k`= zt~!6E$`4#J(H<_nwdNVm#$IX<<8tKokiGZ@MaiPy%ewZl}hrk)QM^E6m!yX0a%ebRG+uGJg@P5eO)_C6U zrS>o`M{WgFGw1m&R>v-n>2n@A%nGS|8DQ5-&&a6j7cEnMPmUXJpSa(XvjF z$@@ie2JXe|(l_fPR!&(NSvloaw%68GzQ3FrGu02S=gL)2Mpmx6E$O~xzy*E=d{A2d zJO7gIOQ`yZDnImcyWdH8YTBPnIHpq@pjC2;_y(MTJ2+vF)^}yxRo6GD@UQvG8i&rz6e#19R9G9)2>rzDLq_ z1-QV^fDgd;J|^!suj(hN{Lsto_(phY+M_A^nMd{A#G)Poca#hEX!)~@JDbm7w`3cU zEN!aF4}osq?;4|1xe~fb9;@5STZ|{v2>*X%>Mj z0$TwB=eAa2mHrWKe**PM%>7*pP97Z30&Z3(JGD$>QC@5 zvb|OPM3ohx6OKKP@BE76YTyjqQ7>VSj;CbY6rO0Xk8`8Wz~L!DnImcd!Cv4bjx$1DYu8vBVCIAKKl3Q?^i6~{XdKY z3fg4cTZataj(SGa%byu&s9W3^NLRF;UhuMIvJPJ6$8w^e`)EU1HijHKNyhuLQ9l#er>R}G&k2hgl? z_TiE4`)h36?s<0IV`{hMnuz!dT;Sg=^&A(ZZu*;SZ`FQ^DnImcd%lJ6OlNihcV*!~*d_tJbzT#npM^|+zNIQij?{Sd@q#O2OL9+wfX zqtDB6S-(H$q@yTMQ{@Nvndpb4v%f}Iu{{LNz&-eDjyvq(eL==u_5Oh>KXA`Pd#LZP zk^A5dd>myrnu2ual zLyj~Lqqk>{o@nJIQ{@LfnP?Bw*gEthXC3`#9_o`%;O_oH!qgsvfiF;h3_AsxZIojYXE29 zjv5GigopN*`XQ$F&{1y${E*|6W@LOglI^cCw3ph$xE#4Xbc#ooFN;7Hfge8tXb*A! z+8UQp!p@!LxU72LRh1ulxxGG(?tu?4?Y;dqupR>IBRVeP<3p^Q2o95RcYl4FgUvM< z_{RD)?60whv_7Kg0$vVK&xQOP6!uF*n*|P_r!N@T{_>%RBwe4DC_mNTPhW6F`{@fV zVmkbZeSYtFeH!pcb_<@9h8#OdM*KznOk|&q64^eg@l%x_7`eSZ81`uuerSgpWUElb zUqgpRtz| ze>*SdoY@mc=GKSIWmyAf;0~#< zNA!%1+r<0#2=~ImH&Vtu+^V)dxI_586?fq8ohRe2T3=Q9p_kk9Dik{|AJE?vH!@e> z_5I~UJA!s4d?Sy)Xm45qGVbp8>zyqoH2U?}Uv6)yT`9PZmjkr*dVUTH`z4}&fdlB4 z6GFakpPb10(>*`;mnc8g-@m%^Ti=!wa!S8_^6~a>pL~evk=}uJ1$ZR#v+#V`-m3E= zs{AOW+w&@0PS6;ZkG409dSTa_gKVN!;;%L9;LW^Tpq_uE*RL;rOlv>A>PGgg zSl{fkbmqBtu08XqTUcN8Mc-b-*h8+C%Q3A>;0PKWDsy!80qY(a*vCqtTL| zwKj`>jiCOeB7O*(46lycczV!t3Rjdy3ol}J>A9cN*igA}QSIWYz(SQ>0lI5<&Wed; z=d36!ixz53kKAs4)}2HC1^pc0lE}}3d&%}z?Wd^nqZDrUbIvIwEOSZ=;`O2D@kDr~ z^Kl&Ya|}6&+tIm91=Q0-_UYJLwvTE*MU~%dA2;tG!9K0R5A9HcY!!-rjv>cRlF`pW zJV?~fK|ci){sy~R`#FfWh`$wgiMS);a(IvHrT28X9)BmOjx({p%sx{5?fe}t2Wa?C zeh!+QQ)(g5s|s$4-(l&P4r8WS@?`W&5bc zPgQ*K zzWG7l1Cfoa|Jyg?%t5yvR66L^UzKX14}Ar%9^bA1JLuN&?0Rjt-+?aTFK~f>x7@>V zLAru5vc0Dz%n=+tQ&oQC?)Lg|!c!|QEcJ}&rS;=+IkJB*3RcdmJ8ODnJqntoSp>2O zY%>TTE+Y;jE_?6eaT)Qtd0#m$pHefgF{pr&DnCRI)QVXf+)s;sJuJTOC*OB*nxh#Y zipxt+Ehx&xmYx%z zQL}J<{*1bX^N}!3s>%;>xuQSWyv5p|EUBxjMXJ;(wTFc(I(;2gBZl&pFmvr;E>p{p zW5i|HBu~siS#5&ScnW2ksO}M~$`8HVzQ<4VST`LzxmWrqbdMi%nwaB^KFa%snEPxx zRL0%)drXDS+T-r``1h6OID-%Ka)5R|!p}irzeF@FZ~(ofp8+9+4_PP;&bo6YLWderSgpWUEl}3;iBoT#lV2 z;~o>@L85z1h^L_NH`vvBk0~nTBK~?>d7J{j|3lf{?#Ex%XY}!m=uf2h+wvGM2dMXP zeh!+p}FB&}Ul zji0Lgu#?;I_l^fgGAxHi@QRiph8#OdM*KznOk|&eYi0YW#!pp#VB~iE zg?(CtAKIY?*(wzA*N|f;$%wy*2Z`b@;wdQn4R*E0U&LF)U;oo0?ufYDe4T7>_v7#3 zj^H!;zgT0npA>&%Px5krHa*49L6Zsf2pmA~9ksmV{o6;gxYzR<*JSxRy?*bg-F^3t zI>`6_?RQ7szkQkG_zOG|`FGO|vb|N~rz$`2ay$OsJ8BHW@~%jQs26r#2iZjZifgP4 zId+na_>20P$UdYXT?SDg{ryy_Q`uMfR~eZr38FYrj@ z-@%}4Z`Jsz$`3oa9e+2kn#*v!JklZRg`L+yHqq#-YVp^QV<*Xozo?&y?BgG>y>xF< zHGZn{10%QNFYFT(erSgpWUElbUqgFS@_kTvE|Pgbctu#50K(-%U^jp^8PN$^ADM(YqNVMZ7D4~e?;lV)4l9D zgJ=nRYLb0=@3?2uxn=pIFE7*DW->i)DYZ(z=hx?znME`dx;cZjz*D z3}6q~$NN0D5A4>ogKQtwX9QIFflwywv-H$+OY=neyP*cbiWK%SN5hW{D8;pXAH=HmY!N~>K|rqP32tbPYRrYJNnzOM@3*KX}qZVjDRXX zaL+`4a_OmoQcpoI^(W(UWWShsbjj37hoY2OnnfUsz_x@yygw=CJrI|H0pfMET8_)A z&j_gULqE5lG2rdt&r1ED56bwv%5zyE3^_(zhE2j^4$5j1l-(PYZKC>&fGR)qa{Czr zn#a2Pp{skPk3!EFU``WroZfeN-w<=3Ej2Rk{s7-c<;2C6^JY{oTI6&m72?mQ3>?1j znC|iOInMuzInD#6InIukc{wWH7UlW3OoOkdw~xxA)2El+UYsB4t9?{nD$vSb3g?z< z8{dtbe|8JgLq1+=V%NRwdV&Lf23+80{#Q9JU<-~LDD_EI_uEwEhhA=v1qe?q|8WzJ znH>uNXW-u2!EuK@I>yVmtHvi)egpSX=4t)3SnhDkJ_)nf4lwCIgHE^?Ui?(bxceV< zes6CsGbwNe?gg)L++mN{)g$Eku9qsGy*`%REq3s{H%G-eKX7+@45s7$PFut?wwK0W zaXCg?Pi3oas;rz-&*J_#n{Oz5mSz#iBCu5=fPM(#FyeCa>pU(aURPWz$7TIJ?9R44 z)p1nihcT$X?|OUaTbFWs=vk}Q9s+0J?s=2r4tqqelW|vlkGd*9aL-74xcu3O=g?ki z594y=_Apax(tul*W)a9D@WVv_?IGeY;&Ri!cw9!j_TC`JWxYLf_6i0a3{`$0nu&3F zVgJ9U+#W)YbSZEL&cHqP7RMd-s5t#Y>6u@&XAY;WH1MaZuQh!}+QX_>BAz39sXdI# zk=sLSIkV?k1hNQhI|v{yBMu`jV;&#zI{I@tF6-@Cp>v#6`5`$Y?P2@tz1AKAXW))< z!5-c@GVW;4SbONGHv)dhaY{4N9*%iC;wkQ>_Ao9d+a5Zt16jT-0$UpbXb+>}{uzUtsV^-6n4>m#rp0_!8fAMo)Z)=lJ{A>&># zp0AH6Z>U@lI78p2+tCsU6ThROrm;Q(@0mGBS|0)Xfabl=%LSV5`iQqqm|XV#V<#}X z^v!!_hL?R-HhjQ0Wm^6_k+(j1)l@%t35UPOt_M1kJ_CmGC-Sq-D%swu_t{nXVL!Lm zArY3J-M7(%W9HUJ=zE3OfGOx;%qe7319Q2$xkv@zF2rj5DER6kG? zd!}g5fJY)fYyRgO!^|~K^;vaQew51XXWawi3CpV^9il!Y>-|R~&hP)xUYdp+J4vQz z-Gx4h?BjV|wvXzw>Z<%^`?&c&Q`jdc{Ll_H$X21`7y4eOaXEI9OwYQDbP1nz7x5JR z75E$CPq$~?MSh6Cp3ixl0)NvRvb|l8zlExMJz*W_AyWKp`IMIf)cYAf2Tc}#qyOdU zLA`}b%F2&T9)DHpLQlMd?)Q`r`oL3Oci1(Ny2G5uU*M6*zbpPF+gmk$s`3L9x8v`i z|067~j^v4YVdr&_P1OHawfJkuv6E!PU)0Y;_KChF+ebBis`3LPxAz)>eOiPc+Mx#7 zDirb8kYgvwh`)#jiQ+H%ub}WZ*wq?;5pNNH{a=f?BjU36ZQ0)L$6wX{?`NX_J64Ln zu`hTzK%2hg=b*{ruSehjI&;|_Wdkd(NZS8ZsS7>vjvhX`eDv(m<(uYS7uhuT8pr)# z;E~9`JEnJR6aC-OhZ2_8Mk++Tu=6^zJ)(;7*N|f;$%wzGpNZ__KS8#SYW!5?2S#qk zU)U%1757^^)F4}hBK{h3>?9fS7x5rb{6#zkg}=eB*7%Efi})MdH-7tc+jad$Pfa%;%s&8+ME>18Q?|Ei|5cSA<#fCM`}JRUDlZcAQ*yWHyTqvhCm;E~9`Tb`8dtr|a7`B4tH2Ida|C8BLVpZ_LBXQ884MXJ?uE-|Tt) zgH^MyrX60k|0f;oeYMy0sOn8+vHxsl>xj;4Z;L%&V%2Zdz|YEJ$I^BC1MwHQz`rZD z=eU5&zf`uj>R!>R{Lstob=HKZrfqEVl=sp)>$n`*zZWfF`%(nv%@1Vvnue*fG>gDC zg8vL+KMdsdUd9xctE>HMqN4nAw1iZ3ihh?Nr<){c zFJssP_G#LY+Xr@w?jYMob+2euejtc=1Sdqd$hMaDaB>TV~uup6! z;Xk4td3TiUqqxpQR%`ruk9xYF0>9r-)m?8Mvdr4SQ4^CgZNUSF|cW zN}h@SgBQB#jh}Y3ca$Hv3 zD_WHw`nkQ=G;a@o(H?$r#8BQ6X0AQVWrZ;07;zakiHbQWt4&aLuUEE->N8rZ{Lsto zXS`@0t1Xsmn#0PRP4s6e#hfPQI6L!s-w<=3Er-jvx9r59rS#PXDjPV6j-I8|`SW=O z>|x5ZZ}b~;oF9laR)x|WXWp*79G!b|8s6JNlRV=E`-1*`#f4>8>@$O{E$y3UDVH6c zTfU4vXP{|kM_#*V*KTWEm;IJqKZUN_AMi8a0zV6n;JAP*e57n|)n|@X`JtEFdpr`J znl{0NW6yr(dCK`3tGO&|;0)Y5cjLIj9tEclmi9dM)z#KDpzsc;DnCTJ-R2PP)$5Lo z1i$E=_Zt{;c$-63hA+Sdut|8Nh!Mgkntv|a(SE1?{?`4! zompMC$&&I&P?X4%IiKe@bpMfl;1!KwZI)3_nEjHv!qZ_SJwqy>~jI^1AD+e=>NcOu}axKvl;^n zt7>W;E33*6!5Q(Hb=8sBE0cHSEql-HJM>7G67Dp%G2|HbflXio*aR|QtBNYwCh!@j zQ&n#HjN9|$yq)<~Bq;jg22w!89UAwUxSVd1r1^1-4XpNoHqm*qeN_9js{GK)?Xf3~ zFBbIMKjl6#^hlSIef0MniLoZep56((Ex|gQj;D5)`otM{&(VZQ=_=3C8)MJ+#n|&u zY3$jt4=+c>SWctkWSZ>xN~=mwzPzMt#LY7zpA{GM$-CUcHQSY6_lKRzwPlw@KA%6r zR6n?;;V0Sk)7bR{2jU=bfuH&J<+y;Y^=aAOs(zx%553$TdlH^nTk2!asN&cYI0N_A zpK#n^kAl@Q?y7TRs{97-#hRP%bs*fUmmFoPznSY3%w<^vXW(A2KgS*RXl|2nSA9>B zDnD>{`~IJfd->53&&*!B{}-1dxBKz+WOL>P8Ut00SNoDYu8vBV7vIfirNA9n5iuJz~4dxT8IDdOHRD z>FR4upON;kydmQGMK867aXE5(Xf0>iR_dChT@CDcaHVOY!#0cRN&2P&#QC;(>$`6~ky*6LB$-1&gP?SGY zJ#@ldUz;D7V=oEzfjwZKPA|6)?B;n#wvX!CdR2bdCnG+yWJbh)P47HIX~;3`1Dn7G zuu1p`;WNUfP4CJ!!8&87x3=B#8MoKw>poL{S|ljSZy<$Q;0}%ZOk9pxlFizDkq-9h z6#XCAE&AVq(t20bwe_m}Kq(_WGq5_+{LtWDd7j0PW7sF|Gr|V23CaxL@qQuO1U}me07oHlMdM^^u_HiyKG*5qD_ZXX0|YNs`y*i*#1|K%18T$@Wq0)2i}AFSo~@ zG`_gazhBCIV(5`BCHv@W^Tk*bV^8l1ye+|)H18`J_e`wK|7p6)v-HN;^BpnvoFt7s zJBoQZDvskcI$fseuFYTB@!K*_!;HvvV@CAJ+WaePN0qnixp%o1x-9a_=)YuVnaexk|`z1$vq5}sNp^|kr_m1@t0183mg>f^Y>9>H&9 z+*Q}stMVJT7i(^=%_rQe%X@KczQ7r{7nE__VULc@GVZEt>s9%IyW97gbleBBwfSfE z(!Hj*9J$@sm*SsM8(27R&RKz)`9ZNbzvo{7N|t63$RhBgMgZe6#9_qc=5ijF(JuPG zmE*F$cEafd2187hAE0NVA5y*U#FYE{;lHT$Lx3}I_xL&Put)26GVZEtCsg@?dq&#B zC2VbeeJ{0#aXE5(sBe3oU7HUxXK5CJZ3+Rjhls<7%T3dGTt>Vu=#t~I-kv#~oMZ^A z^211OuSMkJ!(LpQFK`C#v12&yut&2tNSdohd$zT$&FAAowl@FQz0@AY<;d+J>Q$Cz z5y&F2?ID2n5OElB8T0sP7d<(0T-MvOt#EC=9+%ghm-6_~X^jEyz!|tBKiH$Gzl^)7 zJ#^F?0YBt8rI{EX7Phms`RDagdl;9KZ4aH+fh=DZfvpVz#AV#Sw#H?YHe<2QEA z_X5q-sj)udJ+VIGr_%a}yc2mj!ZSsAX2~?&^${<9a&_5PpZ49_{N)!8EnoijaJJuG zN#xPpyYHL7{O|1g_3V0r1AYcv;Ag-GY+jFSZ&g20<%eEw-l=2=;e01 zLb2nYSNGn!JG3KcS6VB0{Kfja@L(DDit)T%DQ~D;5IAFTb#5(XMmhbGElX8569_doToz}O)ui$5$ zL0%u>e;q?)+%vJh)j{{ow7&JBOGVgISBE_S2 z>y!1ZTRz&SeEstM%eA?eMJBz{z5ll5h%x2sXR+%E4)_^xfuDI!=eU5ab!XY$s_l^~ zKlF0DA3=C(#i_4vjXtTit^_y(_okn7++mNVJLJ7KRpXN?e+2R*&L(9^eez zW53|I!yd6aW!zQQr>gP;cem$jslHdQE01_C>ZSSGxEy)DR$t$`kXE*yQP&WxtD84R zU)svIpicdFz?7v~1hNSHh!8+O1aTN~xneGl%ZS$%cgb;Cb$zNTKOoLTKcsp|>g!wm zitQnA2JX>HjyvoTy<5gz_5PzOKXA`Tdsxoaw}yMEJ&end+r#YM)39)sW)ax-5I}o~ zIE=XLt>SSR@!ETj9G6wsr>gS9QWKzoQdjJS+>e8lU#`{lT-w`W`7`c^$I zFX_GYt-u+$qw>KX;T9QpQ+v2ot#2jV3zxI?tvB^jdl;7^w}+bEAi@e+nnfUsz_x?{ z+C$vGw#H>((fNBhF00;mRpp0X^!#JjkQ^sDW%vgsM6YC?6kk&_ZoXN}KuNCE4Ak%c$ zA$|Ka!w{b7>^&WRQEhz$a0c!u7wpmckc_+P`UX{g=$8rHY42&|Z|MyhKZ|c+ z#XY}Uh2L9ohoFK-WZYHjt13VABHZ0vKMHrf@eZ#`0KS3JMs2e5YKlJ;c}{JF%c_b#un>pQaR2@bR?zy*HR)WC58Thj{J z-qT8P#TixQhg7%kyAqz7w(J?t(q0-@$K}Y8)YxYaCC$<-0$BvM6$B8M5r+|%J&imr zBVNZIljHI!HS-#S3LvTSL$urbo>N?|u0C%~Y!TX}bXsX>m7Egp^nMLPjvSZSC28Mt z*aP-yUd;7_-6|fJ?c>)sO--&xq71D|CBlZnai>U&cGf0ZP>&6 zq>OucUBiOPMwC8%s>%NHGPTgr9WWr1rC9{B2>e(P zi1#POya(bk%7b_vy=fJ~Q!9H}FC$BsYL*(4{;aSmjX`)S{ZduK;UbDZAuc{xC% zzv1Vg$&{`TIDn2^vuD}MXAWd@{vl?YD4Sy}=r+%oEKil|BQN@0*~pwbm=2dkUOw|u z)4hk(a)qA(k3@dvIZ1x+-n51Fixs-0nkqj^m&5KQ&^uiG{f+v^?@>E@L+QvhzhhV~ ziL{7%k<8CLuQ-DZ~%St*>_6c3hb6N{?6z7%8XBz zf4bM-JhrOr&12`4y%lJVycM{`ar^}yiTpcys%&r7_^HYdoZODTZ$4Yau>5T#b}_FP z_TB{PY%XQBTKqNS*hwyBvQVLubU_Eh7F-k>YRbWxO1q z{>%9}XfnYaSMc|xJG%bm%T;AxzC5q&i*ZXLUyQrNar^}y ziTpeK3)$YP@l%x_V7VQCzdVVsyfjiE>V=)xK{nCwDz*4)$gz`T#9!3UME2>NBilzc zeyZ{VBe&x(?9(dz&<-`oR-uT$h8#OdM*KxQNECk&PeI{tu&XuxBHkkYR$M3Ij)=>_ zO4;5n$6vO{%E7{22DlM_zZ3DdNQ%FmSMhRyhOgo0pvi2OCvX5=Kj+=j*kv!VdsjWr zKZyRX!dvF-HCz8yRoVKt=9R@RJ2?`&Y^LM*3p^6}x4%lZw`%-U7 z+1{%0QV=)xK{nB-;{1al$4-(Fe^EaZ*{7*SwvTH3ROJUo zZpUBP$1D8M4mHSDp@_eR96L!y{Ef$hMDxF*--fsfe}i4E@wZu&5Aip87mrimkDV#o z+x2G*oP}T0-o9NT{!W$RZ_{siIbwH+@+^~Sx;9%C+FbgVPa?j)dd6T?=$x`c)`iQo z(96ERd{SC!)qhpnxn+m!$F3(hsGiMbbq2V=zhliD7qCTZWqYeWBcRF;z1&_CMtEw) z2b6m5>!meeaXGSoHwG5eYwVtZwg@H7(kud51hy3f5SI~$5to~m^0;e0DZs+!a-CF8o`=~x6pvn(~ zGGm`%rLo)4Z?Id10O`&t>|@C3**>rb?9=o+;Xk4t1xt36)>NuKBcRF;WZZtnfcuZf z)IZFeedbbsQs4~S(cgwWI(#zjs?P|h@&orw^d}2fZ1#B`?4|x>T#oD)^o+rzLs7~s z%_5LRU|T{U-k%in9*E1p0P)&iCdXyfX9QIFp`Y8&7*KmyxZ+)3>m9>~@|G}j?O`q} zgdxX>%dkm>n1iy~1Z8h6mu;f@jDRXX^m6+d1DeNr^W?%_>7&py2AI>t9H-}g-Z#YD zXMtbF-SvA+g>K#BFOlXrTbA>3fO_xc=b*4(A{xDqrw1M6+b{Bbzq2FB?(rwePxbdf zzWXDCeD^XPHnUGB(<8kDeg-@e`I+Y!+1{%6h*bGeO1JMZ4f5T`aQr5cC+dX}kswi~ zp6AroSQ&EcBpLUZP(Ksdr|DSPKC1VKRQZ9D+xM7YpBCYVcBnzN3dLC3kYgvwxW|Ne zkmw#i;wdQn4R*EOV?w+|{PjO5;*Pl2)cn|Wdzf2g*W>Sm!yLh9tg*r~20khN##(qe zK${-m=b!~|^}LUjN8kWD|HCgzcO1T;&*JY#v(75}XjW6%Yk$5p^4gy-b{v0!MDB#OU?r=ajR*wq?;5pNNH!z+1tO^2OY)uGY9u9FBaXGev$7RIpyj5~s*5A*h@Z^vxKWvbh z_OLPK_Asp29s+0JUh!v+JM0l&E#r>&Gok$HQdNG)%S3y){;F%+J*~ae9>(R!?O~># zNd|6NnnfUszz-Jzw1=&xa4@5K39 z$OrcD-z(#8Y7ZU7ZSX^mQ<|Ca;d||#NH4XAaXE5(=oF7EUlxHZ0zZBP5SMZP+8UQp z!q)rbxU72LRh1ul(fH8K_bk#q@VO7%lJb39=#efZ+-ZF<)fXZh+D)YmPlX{>8V|9y+ZpKoN4uKK1u5Iz*^Bg&-p5y4fw9Ib0OEr?iX zlJ#j;h1&mqdf6UD{n)y&zF8lU`+xr`%f0Lac2EDK_R>K|oBB&!)7<%QmF0f$e$w?3 zzy*HRv6|z8@_HVS?XBu3s{GK)?Rgl&QwuFM;b`t{L+bytcO?K$6j?hUVymMdhz1WF z?|7_$ARAAPNwB?95aS65GX@$c5pkF0XIXu; zi{b9%I*R4WS1+7XGVW+cxf1IrtedcoO1(k+j`dagQW5t`)S24(rj=1=`UPJ{jTY8X zt&|)|m!(8WNnm^kqFRU9*mw)BmyHoVS+f>!hy|o*w6pFvok&6Z+rx zWq-H+!Tkzw!8}WAgbTE^%fxug&J$UEw99FJV|aQzua|tk5_wfFp95#$9(s{*M?CzO zi@3|KgJkt9iF-Nk(~Pvs;SQXEyY>>{j(Ehb5OJ5S6P48m?oRKUvhkjMpXQ5I+BePl z5%*0`ZER|qMpdSk|3v>?=_24Fu=XK^v z0P-Pt7`*I#g?Jf!otY={vTU8GtUe;;bX^V0haGcoD>)yc9py^k4xE8|h%x6%7lCyh0p!Cl ze|~M|W#ALPPUL0T=dQB)XqVIV5$qZGSG_JN`ME9HQLbdTvpQ4ML!dsw`w872qHZFS z6wk%0M191tavQII-l&iGoYzMjEYwHDSMmLYkNExWKSWt>eZZ4sw*GDis%Q=Tj zlRSqDI0N_S+k`vfp=u)Tvh@wJ`jy1JoO8IeSLB`t0cYT@yi2$v9`P4M+-2(1@CY9!WhIiVG3xY9+yMPO|}0PB75FnGE3 zJ>q5XweKa7m$Um^v0P-Pt7`z;MpLiL3t^8HwW!d@$S$#yx>E}T#AC_|t z7ur#-1n$5YxN9E}?ubY0%OdW`XKP!1L>6~;4%e4e%7-~WVm>T4j_&7O1Y88x9t6P4 z;9>BxpYP*?uhXxHyewPaAghlER3;x*?;I}R4BXK##3OuIjquLuynMJ;)kjc1)X(Ai zu1fhZ=f};57;~<45m?s|Kt7D|=htRl20p37MP8PD?kcN~b~#-i!JdJ~$5-zhF4RMy zJ|grP-5;WELOoK%-TC)K+IdSE)kpNygV4?MEw7JIh5CrhKdB#(k$=%OWU=}PFUJA$ zrkWFib&WH->^+fhFMB8W_Q-dGy*Hhk?7is~w%-#8JPOS--xx98vhR76)yF(@`aO|v zcVk#ynGExBQEXKbk$g|2;m698@t#PG&qDEu9xcX4_C1fX`uXu;=jf>GZN4Ep(TttU zH@0c=8TBppPNqV9GN01CwL%TPWhBc#y>$bIA1hDBdm_OHh29ehK83`5LtMM_#xlRG zJ`H>e{*C@b{e`|VR*ZKg>eKAJsn@7a`;7DNAp-yUzo34^GbDZgHOVs1K|E=;8r^enO zAMJ9wK8@k2pD9^#9cV3Y@p#v7XcT6wE+R_ zw}6Mi%b9P8m%-QSB?E+g@a*%0wXi;oczJfo`B0HOM-Vsz_waXwJK_<)RKy+6AMB1j zS$+7eL_X}8J3pa#9@v;pz^4C)Kc^%c?@AAk$M7TOL$^N7DWqLHE&{(j1dtEG!{FuA z_r%NKYu{xeFU!`K$?5}Xr=Q2OeArQQPs#ZZ?I>3Qci;@%)gK9W#3LFJaYsH|+v?M@ zxX*nmq4ca$KFs+M^I^GhbU*JR;3Ba0AOKzl4}+Jnj}N|9t`K=yw!TbO9}%cbK76%m z`4Bh*ck~PKXuVRzJue@wRrP6<4?jsLHC4)oIX`Yb#F%rXi@>^$0P-Q8Uz>Ru_@w8G zye#|NRaPJEa=Jc^Jp=#l*99d%w?#Y3l?-=QAAx!Z)JLQ?-nl>9DMZ~wXr73BCF&#m zC-=z;6*4?ZyN z5xqV_f4|YM8;&ry?}4Fz3O?}Y;-d8tzyG8Z- zm3cN%pHRRVxQ9G69ube&H6re^^$oK62JQh*H~l~99_G3;sszC2N|g-v1^u@udA%Q5 zCU;%|a0c#LH^Lq9@Lnt8E?eIqs}CHVejY@)k4z|is+co@9w z?Mb{0z7F3g^0I7wgRDLx<@EO!SU&8ixxM6k7JK~Xwint@6t!?!Y zS={I9?>8DyrF@w4Bj&@?PMy`zP~VK<5De*Tt7d= zK2ZOw^%0a0SCpI&?IHl&firN2J;Wn)iHLh%K3uEnBPbv0?>E}1O8GG7N6d%7-<2)` zE&}Tw0^nslzc%wS5Ya9bd0FG3q1!!|Njs6Y3-4oACXG{``J-D^ZqPAMxw!+XlxZBl@{H zT~i-1{KZ>?!>@=1J-2_ zdAa>B;0)ZAK7>2sk-1XDUADeKRv)-K{X8g(`<=^@%I;NK@8|r8>;0KC`J0V8{lk5m z5n}I37lE}E0j&4I!{FuCzQoJm>+n?~FR!ij5m{bd@A;DRp_Y)#hrk)Qr#C0u5s%b7 z5qIo=*&UCv`tVzkd^mYUQW;dGe3^v0P-Pt7`z8n0U4He7Hm|9|C9Ku5C%UBOaN}MBI_j*0%bHEbcq5N-D#u zln--$#C*u>BL-o7xza_zMPS`Q0QnF+3|{v0eSGkBxUa~|vh@wJ`iM|P^5MnrmYfeG zPsrs%;0)Z+FT}%tGLDlGw@*_|GDIRh<21K8Sbn;0`(B6j|lBa_lGz~ z!n;7ky#nu5>m%Nd?;7;)d$}H$uBngM`oa5x zTTj=|hiLmGdBEoP=Zz1F{MJX_AKdz{e=b@d0bDT8!rKup7(bbt#dyok6Ip$<%jtC| z!_%|$FL^lTulnHCklJFobtiBJ?x|YB9q|Y+6mgfWZ;;hT&ntoZkR6wljJuMQ!yPyS zcXbEC9r4J#)>EiMm#uG*)d%iQKM!K#eaOYjlgg+nt@m?&#PxosvWS8B%>?&t8VJ5C zT?E!r1hC!*4}+KEI}$I0ufuPMyqw*?S_{vQ$nx?>rPoJ9BQ+sW(O3 zvHxXvILhk7Z$ZRTYlk$O$! zW!dMhvifM3)8|L9XW#=~TwHQKL_5lr40l!^fqDqkM??qH{UPcm)Hg)jq&ASQA&b>Vgg6e6EB4(z zxb+(s>Tzis>G5cz^b-N}|5fj2lYjpyxJlEZVE4msNp?ScLEiW%tqquGz@yMS^Q{u& zeaQ3~vsy}XVfiUpeHiWTp>qt>^^Ea1wrTPi^(}QxjZNjB$GFKrhULx47#|nnssvsQ zsYyJS=&hSE{8)Kv8;o zFV62dQsCd#A=D2@WgohREXKc4jsxWIyJ~|yCUzbFwjF;*u)8bF9*-ii56 zPmGT&f6D3uA*cBl@oDAr&eI3W@oKxSIJl#rp^T^&UXu z5&GKS#CSW-zk_A@_eWkIe3ZbyaX<9~Qr(ZPA&c>EYJd7X30xW97-+j;)A^L9^qzbEZK{J{BljKIH{1F0X7k>PX=S&VSJ&?&A)vPU|3$BjPY?{HS6G;NllV}4L??%4F1LVEEJ#U(PDgL`BPRO z7&*xGoLo3waTSkI^4L??%4E_Zl6yjg-DJ13_;%esKDDNNmH+2~C6!ev`V!R#a z-$8c1ot(e^{fV!Cj}iE{Ri%DFDu>cFWHJ7YavUIkw_cxMYWs(Zu77!bWk6<3Sm{oW zUmKk6zb<(0$3G;`{rEfE>tEneXnv>1iSd@@Pg#9n;WYnF&s?ix`F%2d2#pJ?SqI-t zYER4UKNxrYJ{{UPtzkNp$F3|Fa#dFDI>x5+W(JrU!ZW*2)&p(#KzSXQV{-*Cgykeh!C+hn? zz!|tl#}Mv_M{KHyyX^T6vij(IC2(KRf7_DZm#4ldhdXcv?#eNQJL2IzL&RP7dk#lT zc)4{f@iO>2(kSwB_WL__7K$7^S$)KyGWl?3$@wrMnGb<8a8HjT+!2q=G!b`v{|Egq zSIX+cUnTNkN6pmxjFwzi!Q!R`+`YMx9e4OPmA zIX|89p~R@hsC1=^fQ!Iy5dq{w@Gy88`}p8%wOQolY(BI1C^rj}tUj=I`aRECUY`4M z$@viNC|3e^;0)Yh2l0r{6mieXhqibd`tV~{R_6ZjW%N+H{Efc(-^Z1G?kcN~cCq_I_PywI5A*ld=}8$rSE^*sz(2p}x{{yU zmTK4hIfi@o{TaL-0`(E0M!G*l-Gu*25%&tzM+~-8A#Bt~e8uY{#tHQinUnec!c>00 zd%7sgt&ccoRnOq;zON)6eqpP+t~ej!sTGmn__P10zdz%vgy(DD2y;B2YP>u+{;g|@ z)<*yr%(L(W!UgucSBdeKohP#TXqVIbFbq$R=gpGmnfj{SJ`8XM?x~4{JK_<$r@L?- zoNRrAtbQeNU(kPxlGpp{N;%wtGjLZ=CEO7YZ%o8pw!T4DAGkZc-e==|LI05nrMXJ$ z{hS|hy?<)stmX-mrc9dAGIchuk|^m{6Iy|cD_sO!1b(XsV7(6>1~11a6EB0WBln5C zoZY`#3+p3@mm5pYhl*rA1kS+SS5LSj9+~?^+-3K#Wc4x5E0GU7=3bdlTC0=~bAH5p z=+;Ld!me}?SoaV>J_HYgm!l2D%i!zqB9WJ6>lvW%&#m*H2g%kq$m#=Ur|ToKyj-)QLdKq z`ToM${C@WjCi(Xr`sc5&iZ$WAy9WpV-0yPz+?=jCKVrbmj|2yt_;k=S;*;cF9rtxQ zzh}Vyj|K-k^;FUN2;hQw7HcA0(Es?IV!UPNiL5@_<@CCf;pu7nOCFBpj0Uu-xCFle zXW;HVlW<2oe0Phu%hor@>Z4~)?|(7ehkW#O$+)XYx&1HT4BR8lggfF9y+_1dw!T4D zAGkaHJSdBM-SVVzMU~e3IX~ih|Ex(Z_04p8gylcce^mCBghu~rGa%wj5GWa@uzsSqkd}iy31Ys|6cM{J|cU6m{^rmZm3c|%=r=XA+L`Zgz@D{ z7XcT6bq4|DL+~(oInMX-!PnXYA}?q2+5g1(5%ivCr@k+n<>ejUD>)x3OXTt)a0c!e zd5DMqArbeyd}wPm0)6d>F8ci>v1%;3DwbMF9EG$Dd!D zc^N&6JuLFF>~mLHeYA@`w{`OT2=)y8#yN{i&WC76xsu_|>LXAOf%=Hlg>-+2b0oZr zMcf^KztP@yPLIfcztMmB`4QuV`iR!^sUMI^8(l*dJ3k`Iaexe5yh~7PF8+R_a^q0y z=P&igf|tf)!ILigF?rHu-{*~w(kx(}0gpoSEc1vMZ`t<~$?BtbPQTyi(pHA$56Sce zG%mzd3A`FolYPH^&X1L+y!RXNaat%o;m5@I$iAOQRv!pC{eC0Fr=U>_PEB}Y{ z?{NbE#xJIRK&qG0HDodVP0gXtLpBW<6ny;HuH)Z_U;A_L;jxQ@{v&=&`j4>Bzrdr= z{Eo-Pc+2vqtUl(J)BO8zUxwumNgp2gF^fZK83`5LtM@L3%&*ahURkK!TRqhG2V{z z?_hhIddB;eQ6C%<_%{=wen3X9plirt{Ojd7K#tA)5Lkb&!TP$hZDd!RpZ4ad=LFw8 z^$)>2Z|$4B^VVLr*T2A{(EM&)BF0;mKV|hXx18qRH$OZ_$8xh|n2!sqSqI-tDogF} zyR!9_;m698!M_-vh2oQbMvRXvf6D3uBd7Tn@yT3C^VSMA_?D62U&D`;Cxd^%2Zi_- zd*5B!_Dfp`k~t%r;8cAkH2Jyy+rKdz?^d^gVz zoPSRc__y_1>IbB9JzYZ<kR1Nd0NeZv zJPOV4(2-)iW%*N9A9KrT{#|$%!?HG+zK+I))vSYWCe>%<_}B1b<;mb*jL$;x(T)=1 zBg>z%`oPF({zZIR`8>2j4ZdY0_}B1b<;mb*@IfK|1)oAU!^YV}{dp_!C^WylW5syO@~5mmdg(O(Htfl;ydvr2At#6radAF+Q^VDXR~RoaSG|C(h@g6>9J;Bf-ChA1hA=|AG$+ z@h|uk67vmlHS;g{7W^B!o%0Uo<;<~Syq)J?+5KOve@_zlH?xrX0U5cKt|5!@ub1Nh z`R6T02ai7LxGv-0Z(e#n_|0KjaQ_b{B=`UDMBDreJPOV4)Nx|GW%*N9A9KrT{{3cS zhULl0Fdr9IvktzQRG*XMU&D`;Cxd@6J`2T1Jzk8DEPu-C10$#T7xBs5M)TGRHTag1 z;9tX!l_!IL!3Tx-7kmnd`G&Zf`4@Z({*B&4{e^z&wF>Y54z+VSPk#N`&wTxRvcSLo zJE!WQ>mst62x%Oloh+@vq^>%9Fvr7@vjW<69-hN0vWj^?{Mo z{EPVb`8>2j4ZdY0_}B1b<;mb*@IfK|1)oAx9_}jt`DB~-&=~Fj|N;Yzti^-F0j}7wis{OI(b=rw9D!5VKY2E zo_CkSzSXQV{-&>gU$M`>6ZL%`;0)YDe`DU{E=zAq_U(mmQ$>$+Q zB;S_Q+k-fzI5ahG9?R+$Lf7nqI zPbd#oDIezii22Zce;j6(D_sQEJp_;s!NcHX?Bj#4GXq6l&gL_Fr8HTPW%Us$r{Cj| z<>k2@CFeu5qg)BxfirN29mFF%Sj0UqAKKcWgFgJ&m6f?a{3xM3TBUrL^CRX%yLh;I zE&?tBzkLM2%Xofm=4JFSwYSL2vd>**_0cYNf9T}-XzUsIT?6Kp{M;7pC|5Gv+4*Rw zhd_OV_i4I6MBPN{!8e5GwvN{a58bESre{BId_UtSULP?*sE>%psUMK)lXMMPtUe<3 z6n!4@nqHd)7xnDA`m~kFKL%F@t_^-Y=Gf%dW9-+b0gpoSEdH<lL!DMU zM>b8VAKNtfjQW!~2V~?~x`r&qzg~_5f~Z}?c-^})cS(EL`m6XPw*pR)RxI!^QNJTJrY*kqWG z3#(ZN-%M(<-_OYTvGQc_FUDt~__WrF@sZ_ES$$ySbbT=5lUYjh)(SQFmXY9J!;h6G zgMYyXh4>eI3W@oKxSIJFd<*`KK1cn9etLT`-p=!{?D`kyQ%(~2*Poz%Kx#?4hAhUv zA&vv&j<4+)eDHb3dtGxr+LXxM!70i;!S7dwlHac!XPbY4N1^$x?I^}umOo|nF}IxN z-znPNI+o*;F+MJ=W*vMpsXlMNKA5eq3_n($4F1LVEEFIA&SHFI`BPRO7&*xGo zLo3waTSkI^4L??%4E_Zl6yjg-DJ13_;%esKDDNNmH}w+n6!c@ei1Buwf9<`L)YyN( z`DmvK{M)KgKOmJC=^C;a|3*0uko}Gs6nyr6#(Q1EzvDMp92|ey;^3Ayosit}rt!A< z7kCt!-w~e}Z(077)yLd&nt#V1%&-h4(=X7tu$p!7&7|5c$G?UjD^CXhVtf{gPi8kU zKC=8Ns}GEv=3m68mCr*f)Zkl2f`1J^R-O$01s@dRU+^g;<{RQ_=HC?WANbe%Dvd|z zhxZWU?L7b5daRmvJ{ryko-FWhyo34yslH6tkj3~n^$L9+a{GVoAMCyp=N-<=zCmKV9p~Snvi$o!fBrT_;NQ$j>IY=xb-IQu#=l;U1LTH* zqk{*`4Rsm+9{Qh^!9&k}Ex6f9rzST$=@i@i3p@(VZ)I;W-m?5DtB<+mH2)sDFT-+T zGR((?)vSYWCKZYAC$s$r!;h6GgMTqT3&p2(h!`JP{*=`RMo#lD;*)uU=B*WK@GT?3 zzlI+xPX_;j4+`-w_!JWJ4RJN|FZdSx8~ud(3;oE3{}!INR^WVyL3S9}I3MC`&cF2n z|N2wZkN8IZ9+Kz(&c;Y>~e{%YJ{y-lM84YO?x(zB2i6 zQpx#Hk$6uGTZaH=;2!=v;f{Eu+eF-DzrQ1^58Nw}4?Ak+B$TC9%7-~WVm@@whd_i~ z=_0W1A%J`c9tJO`-X~rLUxzLdc{!WU>};HHh-LK=sY>L-j=49NoDWrr_g1oe2%Let z`XS+tczDyRgzx_#pRH}@L$Le9j+#Fwl;^6H4|9IRe8|s-!0>XVi-3#3I)ni7A$S

v?nFK6@FT6jK0mY3%~S8_hIJ5+!>a0c$Mhj?T@6>-nYhqhKD(1#zpvNHFF z?O)QtUk@~W97-HPs8{u6rafL;(U~? zFO${JkB^h*Lm)n_d>&e%2H!H0@gR$j;m698QJ)4rC{&*YK83`5LtM@EX(`@6@UQX> zjYsHb?hxaxdXL+dJvSU%-$ErY>>!SDXV0iF69iqqmJHb2=`-rnP#=7nz`yY?s2{$6 z^ZtA#O67y{>eJr7zF%O*5qm1;M`3-!amAXrex9mDaRv+zhdLNwO>G2HB!;y{Z!av#@_W5_BZW=fPcX&cP zV(0V{p2y49m&xj*eU-qyp+PAf_m|~xkMenM#vT3ho-5)mJ6>h=(JrUg^^7|MZiU1?LYwcx8Py$a_m3E%i!zGB_c1& z)-B8G1N}`Q{QL7&zkpp zBTMA+A#eunnO_KZ#6!JY#2xv}-qR3|2m0upU0IoYcw9pHTb1%*&X1T6XU?3|uMl}To6r6y>Yf9hZu)=HJ*?I%+N~ zIUj0&k;{j`8Mvdph=*^kh&%F`o)6cmy5}tJb8ko}|EN+v%=t0p!|5aHuExoh+ZY~vg~tLS$(vNy(iYmx@YzbeCSrOlAqh69py@fJF9y} zJp}3_S{2H#sGCS%AfB64k-EEK<+eV*KH?jGF5Fb1KEl_7`Vn29_h$o>EVJ(J$TK5> zL*EcA+@WWa}Gb_0c}3_rDnK4GqIa!oIi~G=7VJMI_hz z3ZM68++p8;v532DUXayCyBO|H<`u>r6GP9GJl@fcawYN#@(A*Ze+%MYy*c_OQi zb~(*&3{Q_|Ov&>s@`#8N+Eq+q-3*+8yMGh1hj?Tz6LGiqmQtfNvih*p!!yFe_K~7L z^)7b{|sefI2m2JbcbXzA^LI=*Ff=EeKu7lFgYRL%vxat1nos|L+2D8sdTYVEu!+6TeD~&)Amw>6540AadSjS$%Z95_4uj z|D6)P{i-u(3_nJE5EH}zF+raZE8o>(OfY9`J;>0FJ2hu|@;RgZXr42NOfP@Ncsy** zEa-n!BF5L^22w!88JU|iIX|5|wXsQq_~72a93QkMI$w;B?3z|qAMJAb-jm&5?6gmO z6zq3G&G>`VzZmXpPZRf=xc3ZiL%9U^q{<&f+*9k*y=S0#(pd-BH%+T=X+GP|%_q~Z zg#jB642^rwulSzk8N$7%*Gv6~^e36wN|fc^dnWrl6nN&bab0%rxyR$12KN|NtKVCu zmbKoyZKr$BJ$~pF+~bDXE5=)Pp2+H>T~6P7GCVz=14^D}n&iDF za0c$^7vd4RUc_C0madmR*Z4swuRGb=qV?VfOU{RAN4XN~POLMr?u_n0 z>ocrJ{ZSG3)OcEV9^TY6<8bzM(V2EuogNRrQn5^#d}# z9bNmjCt2>g^Mn;s0t>V)x@_I)>w8zg*Y|mSpMLqM_m|G?wC?m>dr!c3ZBqZc^$+G5 z@F+CT{P+Azc&DE1JdxE$Z=7CtGCVz=UrU~6n&fxhG5&x%#v%F_yH~`$PJh=_V||P5 zEp}Obw99FJV`KRJ5o7blZ{?nOOw>&SXW)*0As*iQMBEuK;WKumtUi2J0{4c7U8{n7 z_H0U?N03)idlT;>-$d>gaj(D`HiP#8m}N*Kul$GSm1#m=Y2AtXkr~MQGgy@6=9QKn zs{(Jo-F0W!ymRlc;5)mj`aSqPi4iCF&l?{E+B^UJeej)sjxM^N0bDT8(mN9_fIhrP zjJIrFkkv=KSS_TJ-wj}RdOUlUoLA6}awYOPa0c$7T?u!@BlUoYyKIe>tUmf#3EXeH zssFREFYYoJzm@$La0kx7UGov{h==-+h`a0=G_v~Wx6}JbY`ix#3|g*yRi%BToF6g5 zEzOgr&upAExqf=fgy#C0O^vhb2bTCdPfjEI_D>+?N*4haf!_)O;AQYIc-g-@@iO>2 z{;H}Y=?{66|4;k{{a@ZHy()ca@r9CFD*YNvd61W3r;2zt9>>(b$#Uk$b zj$$$Uh5jz954)AfhYR|5PbfcBDIezih!J+rut9`f=_0W1Apl+m4}+Jzzaw4-Uq>Gi zd0F-h8d-frsuK5y3;O>q0sA6b8o!nO7jOs8z&)}T;f{DHkBPV=pV=)Bg}!Y2E0YgH z3FYT1<-?qxN<~-}VOQTpz(t_i2!NNt!{BA?zFxngr2%li^OJ}!!_N)(B@JNEr*!;h6GLJcY*>`Ws>gUJD$#-`mK5;$|tx$t+8Oi1ai;v;Q%9HW#PVhmYcXxtMAu-<&*Y3Qr z%rE0#@GbZ^7393b>(o?rrSR_LN}Tng-`kW;!bW}CcbtEl1pdwJ%lk2kWH=zoV*G24 z>7k1b2uyx+Q=Rv^=G~oxKRGMl{r3O#THTe)w|wNfI&=F5e{ycXJM)&Jbd;bSMhofs3GTQRuY#_-QOHD{bY z>xFo1*hrQ4-$2S{M+>Os=1k5{zNgM-y&xWlPl~U9Fn4_0i}8^?t3_5H-LAx(X=v~) z_XVqT)=SQh5uee_P%oK$)5F+HBfHL>GQ_DC(f^)A>4b$52k+j4j~ymRFvi3 zd(PPZ^FZy9UH9FnRew7yxayRX_3!h1wd~Q^o!{@hYQPb}RRc~c`rRntf_Vmf5QWgL zV!UPNiL5@_<@CKL!?T?Cdn<3z_(6M0mB>H98Ms3q@z8uC?y~D(S$(vx61bQ3es4+K zG4IW|qksP0Mcie_tE@iS<#b+Q-0|q_>b(~Mc?5aIAENn#y-nrgFNN>+d$*;$B5`ze z7CO?`cQfsL;a0a`{MU+57jjd)kgpZu866`rfMe^O;Bgd)vM3;%|*B@EdT!JPRE~xZsoNPsDg1QgD|P z8f%ua`mpYFO%21-Gh*|kqExA-Cg(?7U-R!=*uCWkJg#&Ra1rg=02FH>T@iF{#@+8Cu@j!giWBL5y<_y)>srV^tl?$b!61)YRfjidQh)3*m5%UL+JEm#+Y4~87QdS@CoPM@R>&Zit@x?V2|LzQ{2Qd5?yo{J&56T=9 z^xeCEjZmW@dv2DjKHBB<9w*z!I(WiHRav93XJ^>c#2#n-R9YKi?=y0Mh0!ej}D9hdBY`f$5y0aEtt;eNn>Nd}u zcW3*Ve_f#O%k@q6`1OiTdz@!}c}M%1({C=i#|d08&mt!hE*Q_@gT#2#JgFUozn3dz z_2JLyXE+Q`PusFQ9M^m=5P4k03GFH-!EeABxMxlxdx%GBxFznk_dYr9w)N4kO5om6 zv#Mm=70GwM0%zbJK80{cJi=RjE{gY0K2fJI;0)Z+FT_LL))IHycf}lc z+xlo7)#n~6V{EoJrL(CPInTcdP;_JOMK3|qfqy^8f~ zI!yeIb#3$(5%>5my!MN|&r@Ozo4?1YFJjBivz?#O&lJ|L(J9mqrGaGYX`(E5{kn98 zn)uy|w|3e3wf?as?e*V3s;{|*B;Wpi{k(ZeBeA}p{`c!2E4qFKE|_P)2je+@=r!Bq zujFK(vCHbCT~6<*GCVyawofXXS7}c*=f}8rXMLMCY3g+J(UmR&E&}To0$A^Zhr!E0 z0etN{T;yfh?|aDVqkT^AsWM(}=(%T7>3&dEzGG$hF?bm|9FBxHjQ{0e4Sn`^0I8*r>s7Z?&%45*xtYPW8V$gN`*aS z9osbdjQW;CrnlfjWk|-$3;Lgvi1Gf5Y}wh6Ch>hGRyS(+>EubMOGG>npHLIU2XPB^ z6YDT!>po@mfm9{p(=qpt2|w?DC)D7WOXY2GePQ}@!AmwTSbPjWojeKgK|By2Z3dq| zm`B~k_~h@27Hq^C{vfLl(@sCLWb>z^CRXyEw(^Ai_)OF%Ja7i?SZ^a9{vIOkvUQ)b z`oO&s>&cF}OA<qZe_SGowSdkEy#lYHL;ybL73*Rl0QUY4!Ix%mBFw39~v4}VTchI8l z`6N>ph_c*0&f1L*P3#v~)Ma~|`@gWHeg6&h=jtOO$xZ+E`@B7H!ejs4^uOO%{JA@D z!8{AM5-u1&;f=(2%kELi>Z4swuSpr6o)Ke{$}Uw}lji&w+{gOXQs0U$y3$3!MPMC6 z0P7#{FnBq0Ht{m}I<>LL%d&fvvib;<(|c%)mm7MXl1%abi);z6hEyf@&f{;=KP5B!rkLUgk9+(uqg_t#ak71^4URgbDr*$B$B8{n z>~W?pqqQOSK3jYLW4ruSlJ|7_ZqeAu&Gq%uZGSc1L;AMA8lU&wc%1(^TiE0DT}=Ie zj9)_6>Ku~g?s59RdnWPq)zL27#qOoqJLR0J=VD|1)0*!S zK0aUbQG-t>B;sTE>Eubcmqt7gAG85+^Y;_uBfD22tB>|oB0deH*H6ZIzUYJ+97{^X z$MDn1Q!YL%pCNxCpN0O&c^}X2lHzw|9X{*Dc5jVmb~x+hEFqu8FQiO#Iz>4m7>Fp4IuH97s`?fQS<}=`e`RKcn zaDg@LIWgX}?pyoLdSQ5aM%<8xWB#fQXc$t7i#Qp7S@XYg>j;iBaF5OsBUO8Pi$#CyjF~4NoRY}}|GjLbt5$+iGu@@|HU+d0#$>M(al_lej_?Ig& z-hnf42M;42-j^(Kw|!O%=PBFz=%>?r=rrChts3sY8Mvcgh(~0FCGIxA9hl=8+xlo< zMQ}g7t!lWZ`TD|)JNlRDu)=-qJL`q5Q5r_xTov5e`W5R{tY0JlNBoX;ZTMvocgJha zY-S2${fh6^whHT4|8>-l*v%xp3q@J(`nC74L~`|ur|NO(nzIU5?s9ee%7!`m`t-WM z-#p9m#s|%im2>8`uk3kQ(e*2E!90syPq<*7sjrIhmaQ3+)knLWen!af^o)2UpzK|x zXM{OF;{D{L$&+VILnmG7BH$vhP9cExK6n_s?7xwC8GIdIDe|)HKB}xfg5-2f6XWHM z6%Pcwy#FFw!mA-Q$(km^PbW{geZGnMItlSWe6%RV2XXVgF2+Z8A5~T#2vsCLhsOdj z-v3Uh!7-Q0JL3An^yh+?*oQUzbn+y`2k}6B{0sQ};o~U!h8Q2&nlV{@blmCvA(}sT zM7-oR3ffVwWVo|EPV8x7k28Kh ztqrmF8M#Kp-SIun-; zRy$qYzSR_ck8^1-u=2~R^Tr3^vDGE>+PCUae2){jV4kIJCtMJP@U>#RW%np$_0cY; z*Q5+jPwhnk<)A99NppS-?qhvxuAf~$sd3V2_2{T8T?AYN))@q_{s9kzm&11wFN3dB z*NMC=yGJRjj{rHnhsJn$!SQDYRNjA)E#cLWs^lJ;;ir=)VGj-QKzuTHQG5`$@Uj8I z`vPV6C}s75P-WsXJ&@x4?}Qp0a~YA0kKw12Cm}wF2jUaHhtD5Aj#A6T_{i>2%IX6q zr)yJa{!GqWALidR!K=IkZvkiEj`cR;p(aJ#W%np$_0jW6tS1*79||aeDy=7Te#Cj< z?r|c*u5=Mt_YeRtgNMP(KmvRnZx?x4c8^k4AMJB`kCXD@5drP~UR8PLwBg6#WyAz~ zQ0ADR@4n~7n8@x?%Ic$CPVaHDeXOawURISg3ftqvo+kD<(J$#{x0>lWVDe%*UFwtL?H zW8lI6+?O{#C^Fama%=l_=S7RwaRQG*^9*r@l>J_;tUlV|@r=-8?cDcj8J?coZU`uR zTo{aGcy!`ljNjPzY7Ia6o;rW87ULB0!FVmy7TLWDS$(vN-Ag<9y;?TbSM7OwAj;=! zK5FpkghYG{Kb<@Y_tJ<5;)6CIZn%b&-K&t*M>{GJpN>U02Yi1fKJA1W97{^X$MDn1 zQ!YM!-IvPSbE%%&*xh|3*FI`U=(C;rv)`+WNc#TE<%1-<)5o&seK32_hdqtkgP+tJ zqzAjPf6%r63u}AOhdpI6;Rip_cG830@auFPUuA6%`mm>Rd+-yQwqv%xsXCIrb4kX7 zB+dPWKI}!fJ^1negY+`5k@S8-Qe9Pd4%J~ z?WH!}Ion_FHY8(Pk<4sM(%fI@!=B3R!B6W%YKM9P$yftP-$~Z(gFfs`wZv3Q1)HlA(=An)?fV*i#;*&%qDu__v_nYnzkwdadn2 zANEvk4}Q>3_xae_<@~RH?rgRkc_NX06(q|dm6unA8&;8 zVxLfZQggYzv~~NS4|~dE96xRscGSLPC;AQ9@%6KAAM{~Q<@VqQc3S(BzTzbr+S=M4 z^kFZ;?ZFT1_;)3JZD*3+-K_0FANDkEPvsJJ!oMT^%pN4wf!6k*4|~ev96xR^bpz=| zcc9;^3+P(&(REJ~KSLk(RBjJ`ytk0PcO1#ctt7+8ku=+bKI}!fJ@|ng@Iop?c0#9; zG>>QK!=A?Nsa)cWcJKnu(D$A|{WaTzKI|!f;rMZTp*@M`LX9M~fh5DTNSgZ#eb`gE zJ@`=$C;MohHlF)C(Yk%mhrI~52S460;RN1~oI%&=VIKJXyg7rv5kG~0td?BV@N@Pqb&x6r=m7S{Hl4|}L9 zhabGB74rn|(Z&0h%_vE;I{d&M`VD)iJ2%^dKJ01Sp2}r(G>r?aJCsA|THBJO zc|1cO_LL|2eB$;p2l4e@h@^G|$><3M)_+_d_EdfiKjC9Vd+Kp?t(;_S5Bjhd;r7r@ zqqRNg!=Caq$B)}XKQpzYAK8v%Y6ok3(1$&h+k+q2LBF+uWXF57 zwLR#=UWD6&A3t#8^CWU0Z{G-Od(ekHjoVXUcT*Z~Sa-B;M%VEuN%MGyKI|z=IDXt- z^b?x*;05JVx^7ji`2hN`r*eDnle&=fkk8di=~`{Iwg-LKi*S4J6Io9ELLSbv({*O5 zb$_7`dm6W=a+%tj`j3364x?-DPuA^&KI|#aaQwKvXd90wzmHQcq3iVKWY0X`pbvX0 zw+BC1zj&`CJ6OMD=30*z=)+!w+k+qPs~ms6e-L?{u0xNJJ@a^hKJ01So{E0+c%&{R zeZ(VvnRWZ14|~e996xRkc~V_M@x*%4_bgp^!jJ32p31M`M~Raj;?nvQUAJCj-CyX# zUWD62JH3|?UeRkvMy?>)dM(LL_?g09_4zn6YD|J++XOUeacdfANswR z`WKo{_WY02wf~RS{e?d4soWm?zz+JYT}yT{5$pCrANC^L9{j*g=uOfGe&N4c+k-yr zY22R5W$Fgf^L|3@QWwy5=AYL6g+A;l%Q$|p$H%d9FSmC&$#jgQIUdl5J(b&oAJ{>E zTkj`3k$0{83w_v&aC`6*`UB~Q=90d4K3)6g(RHWs$Ms=P_Pf2q8&`v&YaX*9kru4Dy zFZ5wg<@VqQ^B4QEuovFW+8*>_FT(A?55_arLzr)|CTn}phdqtkQ@Jc;FVbvn5BjjD zv~&EpJ@9DcpOiNu|DrgA|3%lG_>b$up31M`2X?UEp81sQ_};d*2YuL!aC>NHXc_e@ z`W#6uNwQTV*@^#5?bG-*{J;+QKzWhuq_qO$&ty+|j^oFF4|}PX$PVnqJLtL-eq0~@ zRrxjA2|IY6qQ1=SzH8k+=)+!w+e15H&wC5mfxXN^>-Iq(_B3u!89AD_ep33dP5A1*sBF~Z?UoVp8ct9WaBHSMQ zz#hgS?D-Sc_Mi`Y8n>r%2|M5yjAwNRYkSa#Jw@aAaeL9hq=$V}Wgois??ti`eq0~+ zRDKOVSU1Bi?q^zuSho-QuovO>&`#Jvf6+eQ&eryz4|^K7r*auRm-KL-skG5`{9=;k z{z4!2lovRDu*dhqU>Eygt>;^}5BjjDa(nOtJGuVy_9;GVd(ely2)746;8X2$;!p5t z>`QAtg+AC2WDomnu$TVUx_!`xJ(b%-JAF;m z4)sUUPoGWKzW-R;gFfsQdnzPvmxg^iZZEc;wLR#= zp7IjM5AEE5^sujp{jl(_*8PP(?5W%y{6rogJ&oTNW*(;N*rO!P^8@;@7vc8c2X?Su zlDogmSdVAu!=A?Nsa)du7TTG6zV(oGf1wY1$_kDjw+B0TK7;;h`&!$BKJ2O79{gax zUAvv)g8lZ`o!0S%KI}!fJ^0Dl;m_s$cat4$vUU5Q4|^K7r*etsC<@;P#Cf9WUe@-Y z4|_@n#}Dn~cJN#j`-zd=t?fY{_Ec^Ue(>B4aaJdhzsy2wd(ely2)746*cZaSPJAiZ zNk3t25BjjDaeFH4^8F#ymtcP=eYmwf=)<1!SB@X-@_j4RgP?uk+1B=;4|^)N2S3>7 zjGjmOc;480fwev8!(N2jgCE#I`;-gGPHcv?J?O)p#_g$G7P1$aX>AYsu&2Dt@iW;= zT}1k5U;Gkld(ekHmD__K*g<^NIb_Fou(dtt!(N2jgP%h7ltZoUK_B)sZcpVB_SAW# zpKITIYkSa#J>?aSAMA2Fsn3XyQdn%W(lRAv_!;g`SA7O0|`mm?G%JGA}>xqwXzZJTT zu6=J=^F8!oPv!RDC(7;N{s{IWUt8OQKI}!fJ@~;sB5?G5O#RH9Y~5ez!=A?Nsayg# z-2cW-AUmzkTelDTu&1o#_;Gu={__1@^f&gFb^D+Xdn&gFKd^`X!k&7nwLR#=UWD6& zAMJUX53mc`>gFjANG{jIDY6iZy)TTecB>xd(ekH zmD__Kv=4F4wQroYJ?O(;gxiB3?E6KYrt#r@ie&g1>+u48*weT@75(OR@SNJ0Bs;0o zt?fY{_LSE-e%v0Or=Wd!o)TY9_RQ;D=)<1M?ZHoOKa95%`(dePt=k8E*o$y`@Kea1 zy3E=h^kGlq_EauWM~L-qxF?P0)_q8t_cx#qd&(OeKW;B_E$N}|F?>B;$2YXL2YuL6 zxjpy^Z$tJ`-{jk#u6;d7n%f6`*o$y`@PoQe+<#=YB0K4Ct;aL;VNc`sR4$Pxy?(M2 z{fg{l_O)&w^kGkVlRgJOSZBa)>==&U7uM~AKJ2O79{gbc8P9#N|Li}{db~g%_9EOK z{J;+Q0MCznM_IQI`mm>Qdn%WO>?y}s+k-yrDXTbs+#a5Dhi)hR$UN$&|4wUr(1$&h z+k+q20pDwPksV*XwLR#=UWD6&AK1e_CG5rH*7l$edm6W=atS-&dzJ5Z#vZb^2YuL6 zQXD^S5ATyiodn+Z8BP|Mzg!>oRDKOVSeK$s0_(~6Q0w^teb|d|duS)@VE&@sBJ{m= zf1wY18n>sSo&3HWbt|}^ZcSU;gFfsjZ*ly%J@5c{D;%J|s{^e23w_vAxjp!Som7zY z{V+u9!VVGrj-!wnD;zW;@Dz0LNZ4||b! zs2}hHd#IB|`*2o{*&g&^kC(rD;Kyta?ZcTouy^D*{ky*U7oRlVo;m#cQL%k|+3(AQ z|4QFefPKg#$4#kksBdnrpE6;_)EV`SQ>WKYXlyzQe=bF4o4~$vW%Z-2NAKyaJ9rE? zKTdyP@3^yPNFMs;KV)XzPO0H_k@pDy^cK`l->$sByx*(!pF(Z3YHzu}Tfbkq8l`=n zeJ?J1MkVhbzP}y*JN37am3YnO8?yegHzJR2o;1xa%vs&N1`W3RqPy<%yLr~v+o6Ap zyBFGe?kPJTH2Hul4)S=~hU0go%Cw4igIjCFhYm~p`0~%AeqDWV-Iz0H)OkF}{xBhSAXKZ-Vl%}(4kC-&W_?;8~I{oKM>h?K*mw;#Ku)w%;Z|wB_?P5MyynE;$ zLO-wK^S7I4ME1LiY%C!57z^qLG!`%i%sw0*3xjNamEY5OEOhg1_~hOP9QVkT`+s!z z*5!|d_5=H^X-*$`7$O9Sb8K9gtW${+ea^7#&>SG_Ag+`Rv*m zlV;j~V_3dL`u9V>^lqPV%(SG()1%JYXD9n(f%nsDEFkt63%-wNEMSb7Tjlgvu=`@V z&SRmw=iOcJKWJLb3|qVq8n|X&XutdE!d+ znm_EW^HO;~7z^m9)mT96F&4BmjRlMmvqy)=!r(z>2PIy2=7se=nVv5n*mmc32if6; z^!aNZ3mblXbMm?09~MyCpRG%s6wQu>zc=k4JYhGlzzd^8BS)6lg|a^O*ndw5_LzOC z&I^Ym9^Q9_ZC*e>t;PakkFgN@l*R(ah`GTIkA*>Y?<4wlx{v7T`N#I-4+v%UKETdc zh>h7`jl6I}?d0U>N8hUR-_t$t${x35$HJ!{eqNW_{w0AI#*Q6%lo>N8u1B46d@>#% zsQcY>)GMFq_Y-CQfEUnDtFeICV=ScqO=AIL#OyNO%ebdJroOppR*Sv6O0KJC345zY z_TB^be5#x0XMRt)fp8{R!LBK=z4llbrvTgY5e+@rYo(%rQ z_$)O(vivEl4~(4VU&Ke_^Uw-4_?D62U&D`;Cxd^%2Zi_-d0=y>py@huXU{Hu(3yoPReI_*eOn`T-fu&^4sktIk(GjsxWUXTDqRxp91#@$dX+ z&P~jJrZLfW*v5K0Zm`Y2z@yapEz6&>`iP9v{5$`dmW1cVHH{OE`4{m~`8>2j4ZdY0_}B1b<;mb*@IfK|1)oA< zz9Ft={*Ceefq&CIcBLAzT;36T=Q#fkk==j5`gdc2eK06a>a-?IEEtB+ne&A*@S zyk4+&$oYv(cN!N~vktzQ^z9+XzlI+xPX_;De3lv?S^kvO2S!fwFX9v7^Uw-4_?D62 zU&D`;Cxd^%2Zi_-dxJ{q5gR;aD#qBj+-P}`M9u}b@0ukPxASj;m698!M_-vrN&2=KV|iSk<Et zG7|i2__6Y2@GtnF5dVTtAu-<&S2O>HdH=w_vCWC6pl|M*&Bllu+c zMsG)rZT@6+BomYXNyd|X(~I{0QXw!7T^gW<=@lfl0jpQXk} zmOo|nfsxbvi}m~4SWDDvCq}NN=kj0$DI1Z3wkG(T+*G&iMytn51-(q&{ z{(bBlI|RqRv19PAo0cc;y6H*V{0lrvo!_$jDXR~>oaW!L$1*IRNo2O9abY#<;G4s6Oa+-e;p9r6aR;a}U-sX}Jh5MJ%oFAt#H9oTZDXR~RoaSG|N8|I*3N`qak>Fp$kCi8b zf58WZ_!oQ%iTQ@On)w%e3;qr7#Ca!|cf{T~&%gG*C2jEUk9_^RnZUow_S6r^=nixZ zDfX)Km5<{98R+(8V82hU>azN?z=it;0~hWW-0zd$EpJ)zj_vw0;8E)QmgP@beMH7- z{ta|vSZ=bsm5&RnSqI-t`u3Jv{~CU*JQ@6p@mXqoWcgE89~e2!zle{*=b;s9@GT?3 zzlI+xPX_;j4+`-w_!JWJ4RJN|ZT_7V73-G%xQ-<9`gH>ko2QWLJq}%5*Xz~`mwP-lb!+=d#3vcG`L@bq{-)MJSs9h^1%qb5wt2k+~T)Xb)T?AYNe)|X@ANu&Zz|6~qdMKOEWcAT5mJiu^4^HJn zwzgP0{D+eBA=*){WVo|)7qHI6x-Q9?G-OhrO8_epPjhAt4 ztUG_@_x*i^b*I@>>z>pv$a2@6BOdi6PMUmem#sS=I&hQrQ~vN{($n@)-2+D|oz|TX z{jzcUDL?&3UrSm4V4k7>>-s!nSUxcCi>l2t%qz?@?@$_#n1AM|sa~3AW9wV!{7sv4 zP1olKc$z(wc5Tcv)VFUg%rk8u^}|1yWNdFymOIZpEBcNa`r&0=HqYj5_H}aJW`EV^ z(UWyQ?zM5Jc{cBwFO&1Gd8Oz)11^|nzz3*D7xvDcNsVjSd?c%nb~t^1$ME!c{$BDt z^9_`{zXQ&|9sNQ)BE!AC^Y3(Ma^X|uOIdw*tOV{uhP+TR?rDBTJla#LL|(zXH{%XF z@rC`i$j4oFyvpjMT~4px7qLdG24x8%HKWESU;&* zBD2&neV$qL!wbtkJiy2+_}*$iA+Kcip?;(eA{icGl7FXj1kC*_bp73Bfx3x*8qsBW z<>)zHit=ZqJoFfX%iSd&fXUXiL%jyH=?mQ+=oe@bf zdA?5N{TFvw;Z-X3qS% zPT5DP%A7I$Sj-t71H>eJAfGe2u>%xHX2-mp1kcSlHD~m^>0!@5BHx&KY;t1r;s#&p32;55Vo@xl5BbI06@u`Tt}Cr`CO z-B{&B~2K_G`cK z5nsE<^F+cke)cl73-ZQq{p~k?yPN)Z>mSTB;DUMP3lc8YTKbzi@kCN0A26G<{lh3b&Aw| zwyZwd)05W{I<-D!yu6^lFA?Sa7ugbC4Vjj# zt7iUf>DRLQ@Kl-jj7|7>|2v@u$6We`*nbwt_Nfd%ojeKgK|Bzj_#u4$6EP0iD6 zZ1v>K$m+vTCDxN2bFWJ%-YTspbAH4KA9L`ik;8_fm#%aXa1mIS5Xh}3`Mw8u85n@C zaSb^}|NcSkm?qorftA%qJDlEwX8EwA=Kh4YC*F@)**$2(kHO1`3HG4OF+tzWQnoHh zRv+zgdXJOsV@<5Te>v{$I;pld!J@s@m*+-GrPv4C8Dj$ z&HOc@oyTv+9w+Lfw-oj`(??N1LPwL-juB+|OA$Kxf^qF9Ul1(1#|d08&w!6P&bXG{qmO# ze#99)ZPL`~=%XuL1Y88xEd;Rs0S|+hfdcp%*O0Px4YK-Zhtq3P#>)+JwoG~lY*>|A zBEyft%ZLfq4Ca`i?`A2xrj*r3dz@aAvUSI=FLu}0KKQBJeF9sPVhxHlY5WvgyJ4-0 zRzODfpf%~i66;ZBi|gylQG1mw#+npo1$%`xsdqf}BXuH4^<{Z%v%c=t?rHlaG5_4UPHWOxgKOJo4c@HiniRNTo<)u$To7NgqEV{W2k+|iD5CGt6N2JYZt z#KUZt<2JCPD!8-tE7q%6zot(oe#g2N{e*P9Zq(kZHZAH# z`wQ#W=tSy=5+>O?Rg~rKsd`o=MxHSDmM&YrPQ78@_NiO2y3qrZ6PIXtiFVyqre68OgWWsL3~>$oVRuUN=|l9R8}ADa(Yjd;pu5xolthF(w=J0k8$tL`o`-< zZLd>-k1JgSTm*i52w=Sr9tJN*ClfD&uTyvR5zfkxt^1VK2hvX0B{E)an6pu`mG@s{ zOL#S8TCy(D@YBhYP?v~!AU?i&iVxzZq;~M;=S|tVPg#8+RFU|M?w3?}|2v@u$6We` z+TZtKb)$x#PM(DLARdTMw1LkbK901$TMK9Ww*`2Rl3+r!FVIm4^GWVo|EPV8x7kF)hmS{q{T)9lH6Iz6Kw zJGr^OUgBU~(l=W#{knTC-sgb2(XE6%POXvp0U4T3*Xbsb#vC z;>PyHd!MN9%YBw;-C%y+_#n&{H=ohIxcQ`_&**_ip?PMGz}U%?8tcu!EaO^M-*7d; zZ4ukUYeaR z?$mc=*;s#iy+;xnpRf6-!KV`v@iF{#@+90#BOZtk+JLy>8d7$zLRKH`s6>1^=4y!$ z&ljCggJVgF_!xdVdCJ9ym(K* z!~^k3pGEOO+>92{zhu{;vifLGMdH(QTvFrx?}Qp0a~YG2kKw12Cm}wF2jUYto6n!z zI1(Es`^-&NAFXlvnO%1NJa&cuSabCF3>=0pBx(KX$2!NNt!{B8g0lvmHr0g>{S$(v_>1TGdo@`0RPdKO_ zWx~qk!ybAk3_rxn%o}2YJt%Wb(08+xedZ>skM=nI%#Q72d2bzBl{E_6>j=Fu$-!kA?8Vrp4`Rafft|L zy(VRNdTK8UC~>xfA+&{0>q2)GEWGYEi}!NcI?$VJ4<;A=A= zvU`-W`e={S?^|KKyx{n=17Y5OkuBj>UNYQSErsDH&y$k1FYKWq9*B?k5{eJvW^R$} z9;K{4+EbbMOb=u(#=4<$_Y5pPhMzUZ2k}6BBA4>{lN(1sgrw{qrK~<$%hvzXQFNzI0JXAw-FEY6H<1MQdS@BsKk15!SSJhGO9}J$(*0Od7*bM5F9g39c=Ta z3gEcXMZiViH;F)QJ<0byz{}_i_!`%cvU`-W`e=vKdz_RHj|iwA*Hq=~K*Nv0%ZLf~ zpv*Br-_25Xk5X12?QwdKlkH>ezSm_{S);H$PV8x7k5idXYeVdPqMwkl6Zv`8&6CbL zuBo|kimlVE^<1p$*JcgCa(U9IKd*amKHte)z5;8J#Ce-$&)`aV{B<3S2a; zSF)@2IA{0UEjau7{et>iZ0dgA`eA*qRs5NK?C!zY$L_2D-TDXf47gyP0Uv8Ek4N_1 zma_V27n^77dkIdRrN;2|c$)KYT=Ts^OyWD_6Lr(T8MvcghzG7Av-eQAupwpj;j$9A zH#Cec8F$}c``_JXxMSX%aYz5mQg*z`>Z3hQ=M}~szix6>$+)8(1(JTp=(KM*NIa3xV*e_ zNL$8W$HxT9Z)N6aJQF1zlP)kpg( zf%}k0?kySjwB(tznD=Jf(ZBGu@BPn?clP~3r`A!7J8t^Rt0m(Oe9D!`E65|rE15fq zf01v@c4M1TUa4!WpVTaoS+e>(vzUF9a&6?5ulfDTc0yk9MyVf}+ek+45M{Y}<*Vy% z4y30aljy3vGJ5j=vv(zcZ4_5~b1Ps7Hx8#MM+K!2Vkn{|i6hCuP>wW7!9NrdlgP;6 z*v4@{93|!mxR6qgmYS4O&i;ayJAk+o%TTq%EkE#sTUww%`Bj?IaEq(?XEgKe?2J~n zc4uX~$qJmOmGs`5`QFUDH}`L&vlgx+e&e?Ak5B%8wayzo5&Hba~yrQ9KRfaSM(E`;}l&&#wSy;GSE_aECqW|GM)o))Yiq zn_JtFc^6cdA1b{bBcr%iSN}2+d|_zD$b=p?Mn*A4j0GFOCS5o1HiFlQuGc@=&T5n6 z8=9BuoGGBo51WkU=b+b(*)D(n84c~{wk|v)*5S_T%qCYw;ynLiG!YuTp%nN0Jh-XH zPE+R$*a!B2eR4l%_JQ3}Yd)3iquWQ<ho;U5JvMcQ+W>{;e`7-b3f;|qVS9QkVb*Ah6ZIwDRtGU(1rN^|+G&a^n;ynL$B^l*GhnjW9 z)MM3Dcn$~b0sA2SLEVY};T6Td$66LPHM>sYNWI62*=LtXcH9p9{3UBk?mD{#jMP43 z$)`e(un*cX*Z?+x5ZJ0~k56~CrVe$+?S&g&)funX^H7`gt$jTl=drjz3W#_^%{pW1 zDbOUZx8lE3?Ss^0&c8uA?@~9W)#XRJydKk}{fpln@Lb6;G1B3bQv1*`P4qR<_YB_2 zdJ>NZLee~>mCX)G`pG2PA;XO17!)%T-kw+05CD+4a5XTV3b zGoE$pi7r3V;dS4W;%OL9oPj$e!yb4BjkLCq<4`*A=$PvAL!sB} z<|yvf)we|gbwjjn&eS9OAGQx;vA+)khW4qCz$SzM`UQv!)ONWT6uSOQmmlf!I_{*g z#r(~GU$Q?$I-F9(orp6Lcg7!O@fqS#Rc>93#hu?-+OTML!;&R#VooZP@>fv-B<4I! zRZ_yUh&%tqQ~*ddDdO+9kFzbMuiYj2M&n%~@XTD;@H0Uv>dPkjV@ z1U3!?5bvWMM!TH3kG0Eauhra!n_3oCxjdJd%F^XWddlGs8ylNT_lIB8^M}A0xQ8BK zxWgXECn#}*OF9}_Tn~Wh@*^GP@Q00!HIcx)A@YZ&9@!uA`%#fipZW;+2y8+Kz#pO= zM!TF`#oA@G*D54=f97^loCwq9M|#TR54S7bA8PKM0?xp_@*##h?16lO!k@Wa#shxH zb4$zN5Bt_W7Y>{_ME=m!Bm2W8OB!1J`v=^X3ZKtM;6EDy_(QbAXqPdLkM>&4W!|4v ztDlzstjiBgetP{w;0)X$7xut2DEyg=r9*iU;Dg7h zy87aofdxb44^2I?h5d6X-IfTS&qv@t9|8Eo9G|~d+vQ?8)SY+LF z4G;}&ZZ!9D)c_}ZG2_gRzT5Uv_lQLbttVD)ILIwohD|V5cwV2gXN^{v|HJM_7oKL4R#&P6N~mY$@7Qy zH{QqEDe$X#E61LE(zkKk2#fV;AM*BhPpSRwTEp}4J4SQQ%5<>ySIzfPzdk&=@TDzD z`&&VL=XYDvk&Z6f2eF9-97cX{6MBWb>@jkATl)j zi-jIxAG6MI8^9*H46if1>|{c+O|ZY%?b0Hz>WtTW@>!kv??^w-zd#D5z#D4T8B>o{ zQ{kR`*aP;7^Y{mKC-hC(KDvAAb@_o(dFss7k*-lgQ)h%8VIQ;3a2voT$TMt}{FZDJ z)ET$S54@@~Uhm1Lb*8cL>_|V4#RXD8#2aeX8B{l~MtQAfA7wjE8Ex4YaO zMft0k0El)(Mc?xS-uK)~>U-Axf#m}<^&EQ+KF_GrzUPT+7e!ZQb{Mw4XXoZE(beNt zl5+7v8IH4y+Sly8K9w*X-L&{+=LtE)dQxx6DCPAP3?s9%8}fnUkK#M)hqZK`tXr!c=VtG!`y?9`== zWCxY2ZMPPFSIZZLU->upD|<_RMNN0&e_6gjvu`^)zxuV~rqw>W@~Gb78q<96(Nk(4 z{AL^3Cwga(QStK{HGdC&Hox}4&sxa$_AmGq&n#Co_NRZ!&c8G{Ca8a*y}DP^=OOqy@%axX@{AGCagQFcI?L&4O-8D3+h?u zPYf5>Ir-T&o5b7!#nUiaEI3+Q7oZ_%R&xzKa0c$lR~YWFN8r?ebf%wf`=rZ{oO>NN zQ`{RH!zJS$|Ek_vP2ddNEC0f9hdmMtW!!c5rt9(pcdyrKGTe8F1R95Et){6*_F#>P zhKBiV=`V*r>|2{z6KEeIe`x9{mxXl^ z_W6ATd<2FX0kq3#htV!$93Sm<=DKy#T~2v_HX`=6m%|@+4at1a0@C#a&cGe^hdn~w zGVbta8{6J?7Kgmr6G#k^KQ#6D{t#u(r#=FkIs)*An7>xrW#E&%UT&9l*UjnjBVAta zZKreK{kvRI^86vv;gnL`={h8=hrs%X(0bNC#JUMpZeoGBbBeB^YhBXR(duF;QvS{< zAwX3#=(AWKf%}fXDy@$Q{GH{a{-2Cy-j!+aO=s(nMo)Mzn)&tC!?r%+u`g%VKKA{& zWKKU3erq@DJ>(3k$EHPUADcF3(Df0(1@$cP4#TAw;JWohmmlf!dR~I!N$yc7xt_%j z(wmn6&cHqU4~9GJfqa7Mu5ZxgM>@)Zdt>9lCF7pe#2q*TceDqv2l5H3yC+qbAL%F$ z?tzkVk894u0cYTje8C=g20bF2U(1Q6KU3+(9Xhs(hd2qw51kb05mo={})9{bc)S+cDduhig5zG3^(T&KtKU z`zv0KT>8{xLY3FFJIME26@JcthW-k0K|KRLkd~?XALU-@hSOsB(gpRho_RT+Me#I@ zFG{Xw@k8|5H{cB10VV7axLU?tHxAO}NBX?>D-`$Y>bFbAJ*nweQ14aTk-y5l4wv>X z=$2Pqex%E5ze3xN^zH#m`!Mi&CZxkDg%=ZO5G8ArQ6Q@KN9EtKD_GP?JVVk zfmL_JR^rile&Ej~GLY_{q_2Xz!|vLk7n{v}?(k>sW_dbdA&Y@&ViqJ!Whf^w{_MOTJjUZzghM_hUTKGCxphHrgDRqDNT zP20EAhVfkZiZOKko&2gw{U7;$KKb7Mg?a{DP|tD`87|;b=jnCpi7r3V<@GoW#nUi8 zC^_y-YOap}&cHpsIl~?HP{+S?*Ei_$mlOBu>c5nXdr)(I1aJoK{S^#%*h3xv(mkJ1 zmmj!$9q-ffUS0j0NZ`65iuX-Da=bs_f`|jEKu&_^k1U5Yc;1AIbqg@Ve&Dv$O*OgzF+hyJL4Z8fWRC)a2@{;{w zQgeL-a0c$(+c4Z=kEAm02!FP*t&gDn!^Xx_B7s|m$RC<|WPj+dk3h}xsgJ;>hXDK` z+F`WI0X~k8_PX*-c|Iub&o;vK5v*OFTCzWMTVjAaa0c#>2YYnACF5@Kha1)U2!{Lk zNFX^x{?ODT`$ORGQy&2zflUtqw95%Tf33F5KqU7!xn0(sch%)by1ZT=LFd4qxp3&# zM_@e!)<+;;ST})Z&~mJgaN}6T`iLA~AMrJ5eMF{`k1y=S`@4I~)am+&ci#9s^3SWr zq=)NVqVOq4^@dLw+iMtahoAdL;r->|Q)l<|WkshzdzbNk250rjyd~)A`%O}#QQj~Y# z4BRVsW%6JTl}~qlgDyYP<{^T5VGl09|0eMO$Y(_L$t$am*ab|b{Xxp3W@Ie23>xn z$LsqqSwH3SlKmml;gkY*;0)aRzrt{bJyf~yXB*r42#R}SV^bvX=n(lsQ;+NqRlNOY z9|0eMO$PzA%V>wuF4ysKe6-gpB6)wd5w4G5?eerC^M}A0xC3X{1J9rqf9Pf!XT;tI z&2Zm35?DP%{?OFp`$Lp1pZW-F>IlFeruh7|+AbHvXC%$rdUD^_Q_+N1kRJdIu_guFq#{P+L6iwKjdNGJ@jPsyy&RUNFe|!_X6&$J6(9M(a-I={v|iec*?@IF|=K zfvY(Gb&O_y%xGn|y*%(kUXsg$o)DLlyOr^G-NI=9?e_A(4|!QGFOGEbd{!RJ_>)r^ z%^hMd5B!i9;P+^}w@Izjbrw@7{2mYvg3nmBW;i~BaKjdY(yf`Q? zckmxbANG%_^1u&yINt<%l4uY9#mXVtgUoyES(OKV$ix0{=*e<9NMHR2Ob*UJQRRUj z@{)YNIrJ2hhyCNKJn%zamdlHC3VFegSh|qDz$fFVa`tNX$}zu0ulzbmUBxQ|wq2Y$$lb9vCyy&vPx@;D)}KYPv`$fzn0{E(OA z@}MW)$N1pig3ojLSKG@2KjdY(yf~+bgV3I)l3f0;?d5?V@`6)1eq0{xkbH{q= z?HE7&dDjm1^1u&yL4H3q^Z+;5Irl7+Q}+jZdEkdU+%pb6NLT+47=L9Yqro5A%L6~; zC3*Uw2Xat9y02t%a1Xg!p1}`!SuQWmY2r-Ak9I$FK6~z3XWt%xAM%3yo@wX_?#}ql z^nHarU=Jo&%`f;NFUivfJ;l<8d#zP@;D@{{mluaz zo-XtUyLo;q|6-p$@Izj(n%7@0FMB%ULtI(k!k&Y*_VU0Fd2ucedQ^GYRwgGk%U&M% zAuq}0K@a4i+#r3K4tsgvhrBG87e_jIejyL(t3S(L9{3?IcreFLk%xW~(ib?@ULN=% zFV5vb5Aq9nNMH9tdwJl8yd;+gJ!W~nlkuZGhaRw(2Y$%Qa(Qu3?vKzeq@H7PQdii^ z13%C@S6KT7`Jl(nYiw;lyP&x=L=upg@KFqS{C2cN9i!J5BR#di67Kk4=Cv)UULyMRilh?gpM*c@cmGR@p|`!X)k{A(&68|Yu*f`zf_vKdW+4i`BeE9jXN(gw*HpxhiE;!>X?Q$>!{JelTGbCmi!d?{2|FK&F?6q#z0ws?NJtj-(qC}Wkl8FX<2Z)z=$NOuq=$yDhplJ zn+;eNE}8yJq+*+$dXD?+!_i|-dL&;K4(!OJ4?pX2sVp23Yh4`cX!rao(AB4JvF5G0 zht?X#e!cyxmP$HR8`(XhEFhnDWdXKFSx8K0WdUU*lYVhqYkf&<%i7^^?6js9+Bmpu zg7f@#i7xRou}|{fyj>VA?UPi+1g5ckffg(4Dj%NTY+Ks<&Bu2mHmD%wAVX--u{|Km z4ZZxHhk7r+=UTE4a&7qWlWGduh070I)qDAY*A2Q)68Z1s?Sh(?`p_iF4{WUSZ4}GL zPx{beLu)BlQE~r_HW&2@^(=W5E03su!K*iyaIf5z)ibu=(S1c@{O%jw!g@AFs%P~z zEFYO87!4gMQ>XRpf<#B}d-M0`8LoP^_rnkN?)~sJq#oUzzTq3AEaiihfxQoXpm*;> ze==x211_j%nVAe1l%(X($4d9A9~s3r?x-$5w0gZCh2m)#D|-UZ4AFWcQxA)Kkgp~A zc+K;04dQ+3Bj6)2f)Sv8cmZ+oXot}**GE{pjP^S8(HO}OpU||ZBd(DzU4Ez@Z`2s0 z$bXqnwl(|Zb6Xdl5$iazr2{{5Kxw;NUA?|1#Pct=mJS3B@>7cYR^&HAk3~};&)i9d zR+hm&um|jujWYYdZlO16F=&@+B zu@CG4`_#|k^@o?Ez#so8^%tYDwvKpJcxhu}tldq5b@`#w>-}7`{;ZsQY#Q<{sELm} zcxToNjx%saybXJF@A!g*`>fXX#SI$uLYE&|FGoDNa`HLpz}g{-Crv%Fg`>5z=hYqb z4P?`&J_0@hn-BtMm(dQRT?Pd3i-D8ncx84|%V|}!TU$=U4;@lneyAJEGJx_ zMLLdk|Dx^5SQ=i|T;nr+N^#HMNy*1FF~-?Ho5hA0`>cQVT?zNPN;bwhH`bvw%t`pG z%yCW^yY<8v=a+mgVXQR9*>yC_NA4I#4UxC0-j6n+JM1b(Ia z6lO=vq1COD=Teee^0h+Ev4(aH&k~{h+_N}MJN>=zD_?NGGEVX*f8j*(Q)y!0y+PqGrP@Ml8XFJq2?U0w|1b2|)ERCA*d%x& zuQR;tgy#I8)DO*V;UA?L&!1Hfcl2IOXs99%B zJyuPfGsyHFo#Hd+_+Ss%2k{T;PUyA=Kk z+cOVSUOyxB2>YNNgAHI4#nCrV?Ss^0$BmQLlZ~X9*vq~r?O)t`Nv`CW82C7))IM}f6MaqeJu{1# zFF~I)UMts&{&B4D8EtPk8|%W|tlp&jRS@99H;KOI=e+MZUg~=$7O;F|8yKx$C{w3> z&u`xRcKY@!CWk}2RFZz*(65i)YTEPZ1s7*~4CAJn_O$g}IcE8-re)Ii&I}CN_XIAe zXTS&LC(u~2m9>=~N%hRjz9+@gFm5clo`p2W)_^l`2i~wp|NqKkn&f;was|8l1rECW zNT1j18z}B8uiqjZcz%f1H<)^4|HIbHF7|qR6JYbHkARQB2u1+?0>lMsyNsk)){K$* z1tZBHdKq`p*rGlCZpr=-_&BAAI}v9h?yQWn_zdxAZv11?7)JdR7I)_N+n$TTXXlUeQWO2LF*ZCK|PC~ z!EjOK;dvzaGcWsh6i>r=m9(394P4Jsj}MF!q^K0_8*m2h{mo1s?4k1MwokhJNRQWR zRw(W(C+}2p`whCA$me1hU07(6?ry8O^r4u9CU;)m(Lt3%`uO+B(dB!l?KqECGUd;~Tz1mF+R z4x?S}ZfET>+G`aS-FrTD`H>#4_m$IrN?*mTCHq69!zl&sz!|s)IvDP-hbkBT%+=s9 z5e9x$uG8o8_`_5>@YWFdLsO6J51r!R|K3NyM_}VY0PQl`VYJH_$47gu<~Z-qHo|*C znLqsfkoiO44BUY;?15)c_%q@UT`h$LKjgWkt+va>@;h?oZEMKM^`kvIYkmz^*>KX7UhWf~< zXHmn;^$`?L!}wFl^(^-oD?i0>D)u?rH{cB1@q5?<&!D>dKy~?%4zK5fDDEpKSC$;_ zhcxGdDtWzEaYz1CI+Dt}mwtt|9r2ScFS)#{xRm+~zXCr3zmmFy)gSm9RbILFRXOdn z3BU3Q_bZ!8ekIET>0e8$Z+hdO8@<}=xzH4 z@_xn096DW1U%Kgy=xyu959(Kd3+fs0QRU%zB-Jx7{R+j?iC;-*p6LOcfjjVpJ@5=l z#v?Y$eN_~9>Q@5mhiE**)FY2)@LSRhattIO_o76O^FtSRBF;qInZ2CFXNX5t{8Rhz@eF#7Jv+wU)rdyEzol^wzKA=quWEBC?(Dvl z~n~y{sK|KRL_FRUcdykSXKhov(d=ACaSRLpMd^|+)kEutFe-_btG|yl32Rr-J zM_^Mz0PQl`VYJJ@0PQuNL3PjY(&a}wygmbrw#$u|ZrhtUcjC~THEHS*?J{hFm_fA( z@~%?dJ$AbMNRQWhkZIiU;t#*H#2f?fjYK+}Qi?mBV?zvzn6&b07P}!<4J?$;yDiJP z`7YKDiM{@x@v+bfDJIRVVEG7L$!PLNGIbi0uAX(*nsqqViqrBdPD_ zfxC$}FQHhH~QaYz0VkIJ}@r1I|NSO;x8 zuA2VilFK{raZ2G=;78zB`fp_IFZ_)vw;bo@yV)lo#yURbeq{^EujH;_`RM-{qw$~1 z)Tv*&_mzrBV%;vo=2s3l{?h0H$KOHP!e`RP&mJmRyK%rvUC{$x`sJX01-PJ|#ed9j zQSFT9kyOvT^eYrkazj+f^(>)zb|G*E?)^VuxWgX68`Rwi=-{S%X!OqAha|82G8Fff zlXpyaO&OXqz=a;xm!VNQVl3DIHi`d~w-H6^gzkC7y8N(-*XQOln}pMKJpZEC2aVoP ziaR|wU+A&ZB+ku;Jz$^yZe}0YO-+^VdL>al7nJU1WqfPE1E zpzf%ubjQ4O`H`M-)S12&H>bM~8=5*J^a%Tyeul@OunF=ETj3cLb;jwsGl?G$8r17^ z^I4sFI9}B7R_Ah>SO;^b=aWQ-b z`<&W`j%lK=iN0sxSIn27PpZnP+?CCx??V)?}@uWx0L#xshe0nf=NcZ z?~tj}zUOh59G-rE^$o->!*j0UBe_3BzW2q~Nq=eE-rR4SEMr2fcs-m-wap4)LkbQ=mzTPhk((r|x#97j~1Slkt^U(`j)x0B}LN{76q(?6cyf zbdcx205vG)G@)r9p{GETWFOcA_DTJc*B`SS$qCb4=c&t&)ObB-$m$Qh+ZQi7+FJaT z(3~>_&cGe{i#i-E{ep4zI_csXuJIbo1W8 zhM`%vDfEbT88*Qflxh>?U8TBXWV-xFkJsxq={VK}_y4ry7zOEYN-6Gij1yy;7~@Rd z&tgN2eX4hZ1`lClob;?VopH|mS#L~$o8>i@JDPHQea$3kj5GUdmXFLmjE3%$snape z+|5@9bxpt- zxFcV%hk6&T?pcPq{79eIeK3Z5BpnD0Q6J3IBYZv0+md+0qSmvI@$Y1Xp?&Hjun8f6 zcpveY+Aaf%?6`3gt)p`r>zW1{)4jZW=*|v6e2;h^c%vVHXVA*iS-d}YVS6mr;(kp7 z1)8L}GuQ+6K^kB;JcH`aP3rO^9p$i3U&S5i zN^}2WdFzkRQ`|lwlGzYX&vKF+nxcf6K88f_Tig#E#b{s}qg_WcI+_DU%LSDOe#lF4 zdC&ttiL-VP$KWhFRUY^uFURF2IK^G)*w2Q$>ao98l?Q&v3-SIj^dxyc`)9NK#gAh& zHOD@`;D-XV*dx>IKd~#Tj8Z16Y*@e#lGk zvv8pY`Gq{>7iSHt^1u&yDJ~Csa3?y>{>Gj4xJy`-2Y$%Qad`<&kuJ!?S-iLtT9pTW z$io^P=z(1!59I}SJF4=)4|!Nq0zHWLO}RWS#@ZZJ9{3@TH;)GNsPf<+u?7h8j-6*1 zDbgUP_!^f>dHWpSZr{nYolYLY>L=ub9y^bpE;+xgsV&yr)DoND+-4j6+dJhb?f29_()l9f5B)m<XU_fV zq0uXs(|%eo{Koq8d*68I%CJ=?y=%Pn>?+R(L|@(TSnuquckVHaH`AdVLPh(D*0fmP zQ+t!8GzRkdLy}pV-%&z9vyNHw z@15Qx^IdPIJ2zVe(2np2agPAN_w|wC;ao>a?C+km%@r zZ~h)V!&T4Dz3hSBb1%D!)T5`8>;EmoMV2O>boZ3$ z@*^c)&tFqK4P*OsV9Ozzzc%%-xCi<2=dXc;PkjV7Jp>T{pdCiL9DkOz%V@7vWOV0U zbor4Uug^@N?efaWHR(E@f4Q}UR((ow&!5d>>al1Vo0l)m+rS>MPi`%<5A3FGC5jV~iTZ%XO%< z{`6H`k%oK=YT_dg-l5$B&cGeJIt_vguPF0Uv=)2m!RqXot}*0|KM$0ba=fdjrqfe(xEWM)XO)XM#B=J zN3_eZ3C5sQn;`Eh)!lQU%a8PUy(f*1W8FHt^m~Mn4yTmjPUn>{rin4mY(I+)G4`p- zEz4a3E)KU7W1Kj5YpgWJ+5IBRN8lw!6E7>2&mF7zEB3tNyji`yi}xmD&%-m9&~^Fp z-mSK;CF63jO+6s)Mxdbo!0u)cf zSTGa$7A=za#QHA24O?4a{j8Mtz#TXPcksd$Xq2{79eId(tTGeQUF8Ab&92 zgWKxij(V@+4*iMW$+(ZC^6uq%j>Rb1eud&m&X+FPuOv173UCJQ z$-gk%VUMl@?w4@ay$e8>ALYU8z6`~^Z*6W(>e!*_%LqNpgVEL$bpbYjO+v5nHiFlQ zy7<_wtgWx^x#qh3u!+}q2~e9YSVP@0?_3L zHsz=@D<^+D-JckmbDV@8VIR}a@E8;}L7ri&+zYZzP-k2Wx8y~8Njq5MfQ_~9N&6RT*M7&+x3K0KXy{XFA3COq zz9#yf{p*-7L7!CB)IW}`kB+uCoQ-wiZg$^O{wfG?v3ygEX@1W8p5vvyXIGZxBlmYk z15Z_J%({n&+Mbt73e7t}N0W6xz6BdMNw+4rP)l0458`5w5QWi|Vrz!|thFYJM5P;y>V zM=as~Flt?Xq{Hj=4HWmr#)_W6PD8Z5!PF!BAGTh0v47qZ4DC}NflUYj_(Q}6YP(zv zijm|Gy`1x)vBm78YAkWZ!2YlpK7)OZxD#&(LTza*tW$ z&IwZ7nR=JyBeKSlBJp(@WTpMrPN%1s{7cFu6!1XMyIj0MpfjjVqJ@5>wI~T6Y zk92rFmrikCIeB{;^YhohxZ`{T{JK<%@{W41;$BS7#_CsS+wr~lB_)^lV)zX9Is6Lz z2>eRMU~3}aZ&W$um`hi6l=3Y6%BS3~1SG$b{gC;Q%$JPTZ&2tU=Uu32xM|O})i+EX zKELwW^y=tm)6XG(vwmtJG1N#-E!zrch+gQRR^sw;^ zstrB|Hh@j)Kjm#ikvgF}N1@9P47{F8XEv!xhj{+wksoN)rxbTOmoD_!X%chkum|ju z`Ha~Ic2iTNI~T6YkMxwM&YY3%UN$uIf9o$! z{eB^yf4h<_BjN7JtTUz_tER%abl3y-LHvWdqozW4E?k!%=_yB@q5A^6rVq{f6QM`g z$MiEi28B(KXV?nQpr|t&+gv)UGh|N?kHrO2$hkMvtTU#b0!`9fI{F5xeTt=KB*nyD zj%m{V#e!)=*Y`wU6MfHkfb~7mCspwuxp!7gl=_~@(JUWb;~342m#Nb+%`HQ_L>64R zMP#_{th(*z7e>$MxR>;o$ociN3-7GDZP!bpXMA|qpnXr^f_hdtmf-?B$Bl3AX6+4( zqSib(dtoygqx(661nGB4zJ@*iu=mRlhc8HhA8ed^~iB2zX=1`^r?@4kH98`0OC%>1!}uo42qHC4{P#w z1ZKyIJ7DIvE<7XFF_+wW5^FD({$iig*y5d6SCs4zi^&=6bBa6NvxPVlaVPi?kK!4$ zehS;OmA|Xz)TND$v356GwhRluiyd2rA=WtIto_ZUxD)9GO>D*DPEexbd*W1i;D@|8?^i$%&f!BkaZV!66I11ZAM%o19`qnxunYEzVC}Rj5B!jq z`^~hhCiD z9fv#gLdWxQ1Aez2EI45 zf5~GQ4b5XT_gzN+)9=tj{t#CLDp|gY*=D5Nn>P9?_1-kYs5>Nc_-+mF9=`cQ-*kM3 zUU0#9e#VP^&d)pLne^YAKM8+j?Pbx&KS~n+9SmQy#dhhpPI!Kem18z3?N#$=akqY| z|NfS#UA?!&E{htS>(lFB3KxwZifN+$Xe{{>`Lw$?4Yo&FsNb2D1(Xq0b{S$!RYgh9 zVr&yJ=BNQ<%AFVJH7E2g}D)Y#~*j~-8AlHu8(b$Z>!kwb2qK*r;`(%+fz4NGjq zQha*d56SnV2OZl){x|g)lVX`1bG`+~V&0}$i74%JS_kuYRq}d;dY0Ucl}FS+Rhp;w z6RN@;JQw!m2ELy#v@6ROsMC6uN}e3PWz@wz!&T1~-LYS9W)3|w;<=euzLX8CP%XM+ zZ}R;MmU1Kig4%llDN4wHsr78+jG06|9W$X=UinhUf@68>8R`}4S^s{lJi@QZ)*|QQ z?aIbXSYO!Ha7kXIZeO~vp2_zUc2%)_M`WnH_;EL8FK-sJni>ltuCJeGPfOZ+}Fxq8g2JJPTL3QsJ z)a6Gyyxt$q{9!sByk}_6R1kVZy9}FP3`(^L@~%?dF*03#q{r*^Ds&uczd&F}ViY=; zfH6&saaMkd#fBLBL_R_5Vr-1_J4+iD&2Ct-#O)|2<*%XwAlM-lb9Ok(TfUz#wLi;8 zD8%z~m`t6Haeg?hI=Z5_rRUct&4>)geuTxlRz)MvE+cbt_or{&%phryUyFC$n|y!n zpmTP>1@$a=0K-L=CZ0ypT!NST5h$L9an($b(}DW}35~w=0wNkX1NZI&8Sb!$NC$hT z+dk>?BRyX4Kc=|%t<9`~eBlYiC)RgZFJt|zl=tu}z!|s)4q~{&9;z(1nzzdJb`D*B zq{r(%7{$H1x~nG;8lpa!sYkYOTgUvyrj~~0`E8`_KX8C2!>wlkfLak}A%J!n?J(Nq`a@W| zjP_bZChyNQ<}!5okshz-=x9G><>cBS8UI>9TrF@0?wM~e++h#o6BPc;)n-N_1pJWa zmX^mKwxt8nA@YZ&9@!td#lz?G5%3ZC&qo0M5bZG9WsKvay;gIY_h(KIHL?iN<%g(p z_`|*xKO8cD2%LdC@&$X~85I7E_(NCG2>2n-EiH#X?5nsn9XM);{GqAG_lIuFfY0Y6 zu(2Tkf0*R+*J`_5EWaaX-qzc5bLbrStnK4Nwg(LBA+SE8ZYJv=V%m&Bs;@!ymUu)=j@`~DFSs!uRqq{}AIyNJ7`gJ|)Um9PqKH|1Vr;_i-4Z1!8xS*Z^ zAD|px^|WN~kyFpuUIVYrr=oZo#_E~K_h3;;NOOGza0c$s3wv~b*tUbUo|#pH1dwhn zJ=f(&`n--iDeir1Z&(wUJw$P*sYkXjnVDbAn=d*`Z+*`XV{yNB3)uP8N5Ds5WFmmL zv!BNWYP*b7cRjdces3F|N0LAE@|aY6GyQ44Dl%N2KD}~b4YeZ z#k$-tdE7Z(iaUc5mXGczqk&m6bsBe$UvXaKfa@E3Di1q&SmMt0Q?5?mRJDSXi=%ty zRE;f&JJ(OSEL}6R&QfkG&3^5tkye2RA|P25rMRoo$8r6Z}ld+Aqb+i_b>!qPri zdpRl!?w@HpL-%{YkHD|sTtfI8RYPXF7|(Dsjsbc3@eC@!J1=oRyA!=c74bv|Lbm;D@{v zmj^vKa~R_^I1?Jl?Q&v!+byVKpyf7dDvH|$^$>- z;d}+?LAo&Cf$>7D2T-<+!{Ar-)~eZ`4bid8o<* zKjd-q8_=W5L;c6uijaqURB&$!?ooj}+^>RX&|~M#4$m<<8d?^{$eRXy?3fm-ecb_e zFWTJvZD{#;jRbCv#TPKV@w+>Fr@XmcPwoWfKfAwVG`Hn`lWAI#6=MF8KJQl`Vc-u^ zJ2N?eKj@E(;n zZ{K+a{+uEdYMi!xu`)(wt$z54$fV!z7tM}th;Db~9b`-|818;_oAB(mMLkws$HY1s znyXl8w0^Gpe%nd2qlYcutJdh86y5By8=_9-QG1UiOvtCWM`b$$WdXKFSxBA4%RH|G zxeNC!Us-UPAD=}1JV0wdXIa?x-JXIr(Icq?NJtT3s_k|8413k zv|Sm$VG-Hr3Ger{jqUMj2QKAm8=TeKN}jXmCDz1{Kz`f0yDo znDmCjSKs%q*TRO;IdeG9=lb5Ur>wc^nejFE{p*Cteg8U5Du4DlkUyn$e*tOufQK6Q zrsu=4UZFh&W{9m zxhROnp_tRqHtzg2#hu3eLXVxM&Ka-|%4aeAbS;qWbKKGnEd%ND114%8+HdySKHacfj7(ip8fW>i63zGLj49V#p-weLfPI;Bc%N`c>C+sxMgP9v8!jl{k3o%FBf*T&Wdnu zd%gBo=#e$4N5sYHUXmcMd#nq@OSQ z^gM9!yxs>c-m=#)USBgc_ER4JwwZD80~a4hzTcL7Z~sF33tUjYlT8d4aCOIJd(UcE zh_75xU4EgohS++Pk^9hg-iWB_a1;eoJPl)2xXa8xE!Ez?P``y9R=?RIyH0)&u84nO z57-Cg7j_GrF54#`{kscc@|o4;2V!3LPpExXUVm7)lGk6kRfJYSE5F=9>bHq+27U+o z2tB4IY8(D1>;rqiK9CN(CC;$1kE@d#nB=^DH29Iea@eQ)`pGp(-maRcL7_rJwVk`V zsbR2>&|_+{wGY~P)jmj0w%Nu$_|gS+*ZVG1G&=V)Tn0|f1F9(fZ?4{{{dg6q6y(dl} z@k{3~Bd;&mhmYlo_~pb2$C2*`k6(Zb>UW@>;er%~+GKmr?P!m+oYoP?_fDxUztG3V z@4Sj%D4s^=Cz0%#$p65tt92evq-pSXsNX^ltKY@q7uW;#>0ZLp2fHQDl(bNWEiuO%gElLYsEO^z_fBlzio{6wl5|`|(m`J$-kjL}9rFG5gN~O07u0j$ z!{m|jb&hQB;~Scnx?2&zB{CPPqIdBowNKxQo6~V#Zza?KiU*Y9PUrK5 z9-*mkZAF27(2l6~K~jR}+t>$Rx}e_K$J_ZN+7?}U;DmJU0@khy)Ldk802bIs=&{!X z|A_Xwn16)714X@pT~+@Ge+d7WxRm=5?l-%?FWb9&iaH*{BGO|ToDIq_t}3T&CX&=Y z-b0>*f0XyXhc0CK$Xvu|{UtIr1@SjQo^O1fUCw?FI{KCGN7}bpnKq0a$e0ZEjrRT# z{9e=1uY4J~@SjJLahOwk+qc_|GW$-u{L57r3B)10QfDmdW_Lt&mtUj(c$;*d*trObazC-;MdW5FFwMX;*e$`Ca2ljw{pci(_ zcFOkA9naR~NBX>;ub}p6y!6E05U;laHE5J+?iRiI3Zci;WX>g7`;llzRQn(`^%u$Z z(e1D6@*`cN_<9Sk`jNCPI_w8u>&@_X(nJjkJ)*Q;(Eh&AGms{+?hWmBF+T~v2a5Uy z`&#`ZZB*eO){rqdbL94T*X}Ei4$WKs3>xlx;DfU(xUb@Uw9LhFnd+}peQ@pKrX@?7 zT3eifZgR0kI_|W`N`3GOqEh%^c`tB%7x$q*U^H=sOr52coK{kOQRI?s-XlJE;58iv zN2AJRzKW%5@9NRfy{qe@#)2R9Ub4-Z+y{$a?_E8Cd>AxJnH97xx;c8Gr5zf${vaJrDf|@tlIf>@%-eJBzj8m2alh&7s-GfD z2wiMJBV2svn!Am8)K7JiOq11U{H(Nz(wP;RD_K4=S2G&AR;JjCSPZu$#{u-3yJtib zv(6dRPv!SuH8(BPTUl9{s>5n0)gJbARjsjl_vjnB`T~D+*z&Dv4_m%_Z6PQ71oaHK zpq|xV#c%;%{}r;mb=MB+@*`bn2fdoRr+6AQ)1o0W{{z>vgy!0Bp@+4DywKAA2ziM) zE!YG0LHUK<>V7EO$3@j+D*3;<{1E2#S)J5AEB|Y%rJN6JpVTDxYx5Tn(ZCtFBVVvb z>PkD@-49}qY}~(EGVY^$ zd3iYd*2yG}z?gH8j5`;;%s3*S!NTtc`vmq;f4A+V`O(WK{3L30?iRiCjz(VI#F+E8 zlNLoUzw8=IIg}-saRkQ&^|rp7;euZ$u9fYr+h5h?N4mW3?@~MsN*MN~67` zbACb(izCq2rBsR|czR%;EHA%idH%6%AGdS=z*UzYa=ec7sC`yWJ}8>u`DdsJZR)#( zrhSAS5$6%xOlNvToCkZrK1c`ZQT=tYeRRi+b@`FLQ3hKB=+&4rv(G-!dfu)MW}nn{ z?&A$Q<}CCS(heRU*W(h@XZ-JW-?jLKf!jHx!zne# zC3yURc%trh?uU7t5xQQ+J++q_PaNHFPOSZeSkq~7Cuc|GxvCtqnc1Y{5;u`2aVE&- z^0>r}EFbkZ@%-G(XyR5z2a6{H90$X_aX{pK%9h$lEMsAs?j;wpb8+dJGGYgvRZT~J+qp_1(>@M_O9#nV`QVKl_c zN3m84`g0oXVLqNP^+=iu$0hjhU?1quaJzNgAlpaR-{|rqeO~)3YM+(YC!!f%Zza^A z)Iw7l<3{;8Qd3VcO|rkr^54TwXjhP=+|Ok@&1#P=X^FXCyspcSbkVra%d-P$o3uQ# zB3jSe)ne!g{qEjTe?{w&&|{}b#C@;_>;tW^Tl^P_eHs=vwa`uWE?;E)Rf}J!Wp~_q z)y`4-^sT)#n!SzrJv-EdZujqCAEC!ilUQQ_zgl!$1mhZY253AqB#zkNc*azhob_?Dn+edd?Lzf@+@p@c@+NZJc{GK4Mzjm#n&`rjj zwszm|ruGqfm?l2|#Z?IZ2z$UjNCWKFeY8He3G6Y`6pC%$+0&WtB`{D62O z`55=Zd|V)rlyT4OrjCmo8$0`mSnJ|gN4t}W78fj1Mrl0pL-L7OSGa`~Pe?x|55ap_ zzA7K(afQQp;)3>h=~s`teb~lD;;SDH@3j7rNOiT5{@%B?Bdbf zC!uE`O~OvF3+xnskl6|LOWv*6seNgzN~2QD)8q#><*`$1x{l|6AoP$1HxzafdIr)Y z>;${OPPvDeonXJpdu2Nv*|M;8vBrp$EaKpkg8a2jvb@>H0EZ*~Ke3-_2eHE#6?h#f_2SQJz!TCFC zCptbM^w?<<_JKWMA83T#;;Ur)%x-OJ>1c9ss0J6T%a8OKJRbMdJ}dgtUA+Hkhnmps z{vGTi^w?=)_Mz(#(H}PZ$7von@P2dugR*_RT8Aj^Uval!kM@t{b%85}|k+#?9&6lgNMh`p5b9 z($vH1W3lme*aP-K`GwtrkI44X9WU4Ahkd*rZ>RR@Tk%l3i`QSfR!rz7{z!8jqR_)M z(eZY>b%?MB?1MDGZrzW{_R;8KT8 z-m7*39)ZVYJL!&>>+&OAUXQm^pYhq8Q@FpNTFTeX<>Lt+KOmk6tYz&h;*7)-GVWg8 z&7gHL13`tx6YOpV>}!}L#}iNUd}J66{Z6KX#S@ul+3!KyDxaHq(PR5YhHD+-3sbj@ zzA%-}-)|lnf5ST@9rPv6ntNgDw&eT4=kL2XE~safzhU`g^2j=$r(}EU?r+rPN4mWB zR}@d9von%=2KgVjp5=Dbn;#Z>gufzrD_DmJd%!+PUVdS>%xc*_`FPFkL31Ply8N(@ z*Xt{&eHt6T8|mWtAFLe=RqEMC=rJ`>+wecpKZZSEALJi)3-#F8N8@gVynQtIk-qZS zXWvLAZ&yv!pirR+&H5wsn3`$Rx}e_mm)QDHukQAvzGBRkzlkKd zA2U&Nk;j2pFn%HQ*lVJ{3lTx^r^VJECV4ypYSt@W&PMaZPo}2tn z%l3Y5XLY@RyA{6fxCD};M~yO;k;hTS=X|NqR#NtFO=30TYGmFAjOm1EfuNf<-@+#3GHs*p?(WJLet9YAE9k9d5N>-VGr1c zeNXmJ^YR?e$oA3gFYEFneO~vsseP)eZ;WK0WA#>`28}WeY4}IFo@(;Up|6aC_ zuAkH8NBX?>lhi(aYgdO;yxt1bpi!o&9o+Zxsh<>jifNMlB-#_zPDoYYIoVFSeomJk z>GIl7(l+Vli3dmOczan4J)z&-JNQYVhiNM2C(&*f^ONvN!KGi)K9XzS3cuVZ6o=~u2)z-D*wW0@>Q7*)}IV<96)z@_321O zSNX@oi;c>zy6o2Is>_}sZF_a(s(9ge_^JuNjINr{JE)%oE~wwY2c@_E1=-%ZcR%Rz zBV8B|_iDWh#nUjpAIbfhm5agZcS55b&d;x#dL&IV)Vo(;57-C!hutzS+Steav315s zwohZ@cOqT9-b$ze6bsF1+DGV-G!@tf?TBh0Bqj8cjeYQ?3+knPyxfmZ+oHOIe-o+X z?WBa7i+mPifqxWw>^0Hf(S9V_>tg;9{tguN3U*cfV}j?;^p9EYN4VcizAW3@`}1^* zAv5sj)IWBTpNuj-<^FM7$v@V=!SYf6H%2pW%XCox$Z-g9dTMyP=x=XaK4?EutUuG8 z5ts8Hc;&@t;FV8En_dyl-g9w5KQeIA%hA9|9}VgsfeY$)_DzNhv~~SawzqEls>_db z!9RL6PDJrEjLDIVng3#u2}?mrV?2-gN1=z+Z(eBmc&nI?ggsy%lwa5__b1sty61!H z^20t}pQlUh)3^3dq`u)buzhk{y6;CWAfkaYaEDgdBmQSQ+}#HzBOCXNO2$2;aSj5- z9qqD;JMi!SiyiLx);aYOcQ4P=rEPomz?qhIec~u^`5(C62H(`XpG4>xB#z+e zfqhVZ>$%;!-;nL2+b-(z!#-Ze5!60?Yg20=-@bPxzRhi;8AtGT0=Ppm>PO%$JKQ(E zID+C{U42Op@;?~vDNWonyj@mthyKLh>~P1o&Z(E>-OD(Fw(XZ6d1lG+oAaDA4l|D6 z{;g;n!Q+Air>F<8S3w-X ziQf43n8@t^+m-lmdJnAqx%{)kbNIbQJacqX!Gl-$wVAd1-|$`H`|b?i_;%rVM0oc| z?f&1JH+USuaY4P!d?4eK$;$R-7Mg#E+bjq?+)7}4Ui&GE=Z%fuw!~ut#}T1z_52iY z2JR@AD5s%!Y;m9Jc4>OFX-Qji!@`&z>Xh5t^NT6&jg7lULT0@j825zcxpG2}>=8F? zShj)uEi>b5o*vi-xWR6C2Aw}YmgpcyOwXT>PlktX*ib{-z)?nOoM@Nlwk|v))^U7O zd&klS_b2xvKLgtbzj8_`?sWYz{0;n1_m|xNaKBXfPq|)rJYVd#dga$uvH9YEbN{o0 z)Q?T(SUy6ZF`D^Yri0;C&v5`f@tPOX%cp!u+AH*9sShfAMOs_VinVuC;iS zbor4kuiHV2r%^LKTFJ|Y+&T%Zc-PgbcMq!2BWbEp=Zj$v*a!MyxA=M+`?&8;c{pFp z+Ci`8^QnDS{#SK0$?L6z8bI-Y(t1JXD})}Qsc&sXfql@9sP;ip`ros$559Cky{tc8 z&KJ|RDBOK&G{oCUftrg<4!{EY2tD?i=p`*xZbC%{Ejm_@3Am2aRbJuC4Bw7q2X}f0oajN8K}H-Q0imzI@nkdyUR5NID+q z5jkV;*1fQ<_vJ^P9n?Pp2h{JtC>f97N3y;1nV5g5s;=Z`^St!-l38qjNcTt5`t8;J zJBsBCi*`?E4dlOkaVTwng&tPFi=9^jd%!;3qgnc3x9*Sa>@&667PJyRJZ|B_=A}#M zfvun=FxmX19QIi``KWY)=ik0nv`L7=kI?=KJ$9PJd_L>}`vk`_`@n92oSl7KcLNMB z&y(@eKhpZMa`Mu2fal*1HHW6K{W;e0v#xeWAZi!Fr>~ol{?ovL) zJ(;EacHrN5yKjoxr?29wbT`kxgxbIfar7AJ_x-L4Oc-OB!F#&p#qTtp9Vc>-xj&qhEXI_@LMETi=S$({ZyuT;BeV z&||d^MfqIPXBG7a_JDn02iOhIpd)FXgxcpnah`;ZD`CAY#+B4{ptwg4>;G{74%QJ` z$CdK?8u&U=$V>2N=rPZ)!@f0s?jhFgs`9`Oc_}Ur>BKlz{W3PrgLox#5qnnUfgkd6 zTwVf{rz^XH&#UnL2K86j%L6~;h4}fI(33ie@d3x+eD;iczsBr zhx@)#TrSdwa)b2YoKRIB_#qGTywC%Aum|Mfo-b7%_#rRF=Z&EU$~jbfgkd6TwVf{%fULM#1%{q)=d`R$N3-+>oK6m ztY>`PQ|MYI59^;)dEke<1Yh4_rW5O^czMD4DXiB}<$)jaQd}PNnD}wICVqT6_bD;lb@?gLcifgkd6Twa1x$V2-FdD-jj^9z2+3-NoKp$GFZS={@I z`TiU2<$)ja5?mhiz#fo`@fXY&srdyzwJ)%1ZM@-CHQ_j=)w7a$T!Y~#Q9{ZJn%zaipzr@-<+!{Ar^qMbUF^5T zzCBeQ_#qGHok9=FotZu!U*X(XRUY^uFTu}Gg&x!o$V2_Wd5o$&@Izki7Pj64dQhGr z7yYe7n|*l(KjfvJW&0qY2mLd|MY%oLI7rcWoby9o<(E6;`{B?7Ii_45UxmKLgD$T8!Xotd0s@fG+XuktV^4|-I2b-OZo#o{aQLtgGUZV#SL$U{8TwI`ET zEFJdEkd0JR`rMAd_cTe-{woQ@p%moCW0-a#T8U)&bNQUfx@RdR8uA(E@tta)G5CQdg?)q`QQoYi$0q5!qj7}7lwg-#9?wxN)V|_kNiVv zppiWnQxKjrbI~Fr?)rRxA~!#@N_7qL%CCjX@O)4oQjXWC(RLBBwU172^qutEnoh2q z{Tl7R{5$mNDYf2BYW-aAJxzqF8=oO9JfZpD;j_*e<;1VhGD0s+4aLvkCp`==en^*! z89oQK`<{O0zkCFI1bhTaMWBNIaK@hGpRr>Lc{U>-eVhC>HZ~G}v38o(Hwgbx$Y=dz zlxTeO<1R!_ey{2t9aKOg7p&O0BQX2uVlLKN?@qSPp*q8SyJkbECOTnN8x7Z-}y5b;JKRpY|fH!OSVfcYC9 zrL?_a!=u3|eq1O_YxSqr_u?0b3xQj)xbWp$+swG|M?Fi6$Avr&G~>c^d0bdz+>pkF zOWO@HUU;xbl(Tn^#tY-TeE4x;VcimOzFk?de&&xC@-~mu2`i2=M&dWLK&#(5E9CyK zeFS_2HgyDsV7yR-n~vi`(Z*TrR9ajJziby53Tf81mVPRp)woa{FU%d}$AvPEsy_mj z>{o~jfm^Y-u;lSV9tWCn;UzpS?7bK;3FW77t(8Fq?Z@tg%+WX<3j6LA$`yNMbSBA^1V4;2)}F> z7kV*Xn5p*TLQzejL#zjw_W7Q4m9J!D|uYV<3+1(`kuvw^nA0X zwk3wE*G>9mR$HynGDzcvmL}f|Yqu zzJ;CW4@7Js)(wl#tlztP4;`Sz(qa9c=^t+V{Pl&@CjQGuz(-)yL4e*6-oMD_+`7eP^8uBpXjIJS&a+TdF1$^eq1Qy$o#k*H;7*#E(C7H z;=+>Wk$D_w#)Y@=xRA$-LNj|u;==Y=!xEjq&{15N4;3ME8U`4<(UN~MX1jH`}9xv>aVma`rG#`1g4;{yaI^%`#%W7Om*A$7Iia3xZ zQ9P@0;ad&&JUR8^wi$o=VA)ffJ^b^>Lx=fsp|G`GOb9I5uMigkw_bJL>t=Ah_x6~zM+0q>a(M`Fu}`*9~Yt=SgHCR&(sw3 zz4$99e*XHxQj5O-J0AfbflUX2A=r0<3+b4@r{~?|gkQFc3x#h->{3Y8xKLeR z*nhYm7YfT${Otb3FAx_3w_Jqmu-q@XK~_p@>y!yGmb$)GjXEpPyg2BRRh?Io*#7 z8Fs)=^-;i-{R(j*aMO+pc^qiQg}>u*A&(bD+Sof57akam#oFj(vYSyuM{!}6mydFe z7pi%bV+Q}elR|vNC!0!KEpqI?^AYe7@DV5lfxN3LYg}l#U)b?@A@y@KHYAk(VN5DQ z-WUBtTnN8x7Z>JporedpOChz33$5o`Cu;n-P?Rv!Hn0(=;unYuftz+*$m2jWE_{*4 zg*;vqn%O%N7dAI^bi`T?jI_3Nv^Om2a2y;uiVIV`eE4x;VclYNncBr#ZeTI%XZ|@g zg&4{B4E{ZE^{J15kAROrDF~E5E}S5eF{E)J{BpVC!n&{@7YfrB^Ht&(hzo(6c3jBg zKr=3UmB)oVUKEq7;m^a8Lkf#y4 z{ri!Dicft6d;~Tn1Xx^1??ASDd9l85oDl3ZF2oq2v?qo5i=tzN^gE0f!Y`LAF073D zaiK6Re5p#sFAx_3H|@BP$AM;CnB{RHj~9hz_AWf<$d*PjVi+S+$$9E{xX{`C$o0H@ zlryeT^N3iAQGXt}5Fhc0KaVW1@Zb3e_z3t22n5PMUTCf^bi5zAhQyKh%g-ZYKDng0 z5PrE_abdRBj|)XPQsY59i(eov1a8`KA&&#ixbS@*7xH*fXlC!ibB=B}C)Vyf8We&Y z%_9eAvGU=^g|sZtmzApT@r>WmXZBZ4{QU94ykY!*d<1+1d;}~Als_&M$J7{3#|!go z3)%WYOOi_*FN9w%S6rAn!jB7uRfHdhKNoQT`xW9s;HDiH@;K0p3%}rTA&(bD+Sof1 z7dD+1C+iE1L)5QIeRdQV=6LxiXIxX5aH}2s@xnrU#3%lEp}@j_=Of@F;3FUq5a-m4 zwG8ax`+??>#eG@$wLKjrCXyY;g(AkV$}Vjl8GhL=E)>$JAEmECs_r{c&oAsc(vJ%n zcEHaXli!>BRxB}e<39l&FAB}_7AQ8KO00I&i_F*4(Ls*90 z>;qvLcC!z=;V-_w-*e9UzPGAc){-87_~cgid*8FY=RM1Fp8cHnlcuQ=A5_Qb z{3khnYki@CV*Rrof%OQiN1!nR`g-w`J*Vb36vCA4$mQifMxb0Jt=vD_IW?<%qWBM; zQ&Z^jS$ttl4@9H%clv&kFVy*k&2wtre09wi#@LndJB!}eB?@0y#_e%@;fFrIF!O;G zU-(?+3p2l1zd1tig~Ow?douZ>#6J>W`1K4QPm-@W-T3Alto4Pb$EQwN>kI3#Scldl zupWW+2-G9+@%4ot{`<()+rF>*!a|qN;tQ)5oxu-A-a9{Yk}o`|FT8Mf%@?kYW0NN- z;krcO3(L4YjxYSs`ohcyR(#=a%6wtw7wb1i2)=MRS`4S7k;@PX#_q#Us&6lK6t#&g#k;2 zzOQ^?q03K-FZ`9))_h?-^M#h4yw@cPUs%TN6Z3_c53KmYFJ!(j^NaPHBXs5dYs>!5 z;QO}kHA52;mPmdy}!BU z3+t&38otVxlrfD`g)c1Q_Bg)qL)(!vA6W5)Kau&u%rDk$i_n$(o5OK$_b2amcE-c) zVp$&lhS>bN>5?d|~hxANMFSu%MaIlVGT$%9WV5}<^!Ts(HE9+`^0=<<^wCf@J{9nGrw53Ekf{x z`-9H+?_BGn3?IMNe9b#~AlCkcs}Ql)7p{)SI<_8x^$4s-AV%Oh-Lik-=`s1ka(-c$ z-~1lE>kt0YFZq15A0tvOg89re6-%k`g*A^6>G8XIE)8X_HZSoXI;W=4<+J$0y0+8& zV1CE1x*z%3H`jdO>NuXo8`dQXUs%TNaeU#2-jAI5z=|(?nEArYFV=64kodyMWU>3P zEXYxXg{X#{bMh4>ZW-rRhL0!77nT>+_vRd| @$<#cGx7oHxIb;^1K)+4YUffPL3 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
j<3e?v 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 W6ys7ch=Ah3p0Y&?b5r@pzyAS2`wbod diff --git a/blender/exporter.py b/blender/exporter.py index 1775049f..97b6de3b 100644 --- a/blender/exporter.py +++ b/blender/exporter.py @@ -1463,9 +1463,9 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper): t1data[i * 2] = vtx.uvs[1].x t1data[i * 2 + 1] = vtx.uvs[1].y if num_colors > 0: - cdata[i * 3] = vtx.cols[0] - cdata[i * 3 + 1] = vtx.cols[1] - cdata[i * 3 + 2] = vtx.cols[2] + cdata[i * 3] = vtx.col[0] + cdata[i * 3 + 1] = vtx.col[1] + cdata[i * 3 + 2] = vtx.col[2] # Output om.vertex_arrays = [] pa = Object() @@ -2102,29 +2102,33 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper): x = Object() if t.type_prop == 'Nodes' and t.nodes_name_prop != '': x.type = 'Script' - x.class_name = t.nodes_name_prop.replace('.', '_') + x.class_name = bpy.data.worlds[0].CGProjectPackage + '.node.' + t.nodes_name_prop.replace('.', '_') elif t.type_prop == 'Scene Instance': x.type = 'Script' - x.class_name = 'SceneInstance' + x.class_name = 'armory.trait.internal.SceneInstance' x.parameters = [t.scene_prop.replace('.', '_')] elif t.type_prop == 'Animation': x.type = 'Script' - x.class_name = 'Animation' + x.class_name = 'armory.trait.internal.Animation' names = [] starts = [] ends = [] - for at in node.my_animationtraitlist: + for at in t.my_animationtraitlist: if at.enabled_prop: names.append(at.name) starts.append(at.start_prop) ends.append(at.end_prop) x.parameters = [t.start_track_name_prop, names, starts, ends] else: # Script - x.type = t.type_prop - x.class_name = t.class_name_prop - if len(node.my_paramstraitlist) > 0: + x.type = 'Script' + if t.type_prop == 'Bundled Script': + trait_prefix = 'armory.trait.' + else: + trait_prefix = bpy.data.worlds[0].CGProjectPackage + '.' + x.class_name = trait_prefix + t.class_name_prop + if len(t.my_paramstraitlist) > 0: x.parameters = [] - for pt in node.my_paramstraitlist: # Append parameters + for pt in t.my_paramstraitlist: # Append parameters x.parameters.append(ast.literal_eval(pt.name)) o.traits.append(x) @@ -2153,7 +2157,7 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper): body_mass = rb.mass x = Object() x.type = 'Script' - x.class_name = 'RigidBody' + x.class_name = 'armory.trait.internal.RigidBody' x.parameters = [body_mass, shape, rb.friction] if rb.use_margin: x.parameters.append(rb.collision_margin) @@ -2292,17 +2296,21 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper): o.shader = material.custom_shader_name def cb_export_world(self, world, o): - o.brdf = 'envmap_brdf' + o.brdf = 'brdf' o.probes = [] # Main probe - world_generate_radiance = bpy.data.worlds[0].generate_radiance + world_generate_radiance = False + 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 = '_EnvTex' in defs or '_EnvSky' in defs envtex = bpy.data.cameras[0].world_envtex_name.rsplit('.', 1)[0] num_mips = bpy.data.cameras[0].world_envtex_num_mips 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) + po = self.make_probe('world', envtex, num_mips, strength, 1.0, [0, 0, 0], [0, 0, 0], world_generate_radiance, generate_irradiance) o.probes.append(po) - if '_EnvSky' in bpy.data.worlds[0].world_defs: + if '_EnvSky' in defs: # Sky data for probe po.sun_direction = list(bpy.data.cameras[0].world_envtex_sun_direction) po.turbidity = bpy.data.cameras[0].world_envtex_turbidity @@ -2323,16 +2331,19 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper): cam.probe_num_mips = write_probes.write_probes(cam.probe_texture, disable_hdr, cam.probe_num_mips, generate_radiance=generate_radiance) 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) + 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) o.probes.append(po) - def make_probe(self, id, envtex, mipmaps, strength, blending, volume, volume_center, generate_radiance): + def make_probe(self, id, envtex, mipmaps, strength, blending, volume, volume_center, generate_radiance, generate_irradiance): po = Object() po.id = id if generate_radiance: po.radiance = envtex + '_radiance' - po.radiance_mipmaps = mipmaps - po.irradiance = envtex + '_irradiance' + po.radiance_mipmaps = mipmaps + if generate_irradiance: + po.irradiance = envtex + '_irradiance' + else: + po.irradiance = '' # No irradiance data, fallback to default at runtime po.strength = strength po.blending = blending po.volume = volume diff --git a/blender/lib/compile.py b/blender/lib/compile.py deleted file mode 100644 index 29bd4884..00000000 --- a/blender/lib/compile.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/python - -# Usage: 'python compile.py' -# Output ../compiled/ - -import make_resources -import make_variants -import os - -os.chdir('./forward') -make_resources.make('forward.shader.json') -make_variants.make('forward.shader.json') - -os.chdir('../deferred') -make_resources.make('deferred.shader.json') -make_variants.make('deferred.shader.json') - -os.chdir('../deferred_light') -make_resources.make('deferred_light.shader.json') -make_variants.make('deferred_light.shader.json') - -os.chdir('../env_map') -make_resources.make('env_map.shader.json') -make_variants.make('env_map.shader.json') - -os.chdir('../fxaa_pass') -make_resources.make('fxaa_pass.shader.json') -make_variants.make('fxaa_pass.shader.json') - -os.chdir('../ssao_pass') -make_resources.make('ssao_pass.shader.json') -make_variants.make('ssao_pass.shader.json') - -os.chdir('../ssdo_pass') -make_resources.make('ssdo_pass.shader.json') -make_variants.make('ssdo_pass.shader.json') - -os.chdir('../blur_pass') -make_resources.make('blur_pass.shader.json') -make_variants.make('blur_pass.shader.json') - -os.chdir('../motion_blur_pass') -make_resources.make('motion_blur_pass.shader.json') -make_variants.make('motion_blur_pass.shader.json') - -os.chdir('../compositor_pass') -make_resources.make('compositor_pass.shader.json') -make_variants.make('compositor_pass.shader.json') - -os.chdir('../bloom_pass') -make_resources.make('bloom_pass.shader.json') -make_variants.make('bloom_pass.shader.json') - -os.chdir('../ssr_pass') -make_resources.make('ssr_pass.shader.json') -make_variants.make('ssr_pass.shader.json') - -os.chdir('../combine_pass') -make_resources.make('combine_pass.shader.json') -make_variants.make('combine_pass.shader.json') - -os.chdir('../sss_pass') -make_resources.make('sss_pass.shader.json') -make_variants.make('sss_pass.shader.json') - -os.chdir('../water_pass') -make_resources.make('water_pass.shader.json') -make_variants.make('water_pass.shader.json') - -os.chdir('../godrays_pass') -make_resources.make('godrays_pass.shader.json') -make_variants.make('godrays_pass.shader.json') - -# os.chdir('../pt_trace_pass') -# make_resources.make('pt_trace_pass.shader.json') -# make_variants.make('pt_trace_pass.shader.json') - -# os.chdir('../pt_final_pass') -# make_resources.make('pt_final_pass.shader.json') -# make_variants.make('pt_final_pass.shader.json') diff --git a/blender/lib/make_resources.py b/blender/lib/make_resources.py index 30e24716..dab52e46 100644 --- a/blender/lib/make_resources.py +++ b/blender/lib/make_resources.py @@ -227,11 +227,11 @@ def saveResource(path, base_name, subset, res, minimize): r.shader_resources = [res.shader_resources[-1]] f.write(r.to_JSON(minimize)) -def make(json_name, minimize=False, defs=None): +def make(json_name, fp, minimize=False, defs=None): base_name = json_name.split('.', 1)[0] # Make out dir - path = '../../../../compiled/ShaderResources/' + base_name + path = fp + '/compiled/ShaderResources/' + base_name if not os.path.exists(path): os.makedirs(path) diff --git a/blender/lib/make_variants.py b/blender/lib/make_variants.py index 763016fa..d1c99189 100644 --- a/blender/lib/make_variants.py +++ b/blender/lib/make_variants.py @@ -22,7 +22,7 @@ def writeFile(path, name, defs, lines): f.write('#define ' + d + '\n') defs_written = True -def make(json_name, defs=None): +def make(json_name, fp, defs=None): vert_shaders = [] frag_shaders = [] shader_names = [] @@ -36,7 +36,7 @@ def make(json_name, defs=None): base_name = json_name.split('.', 1)[0] # Make out dir - path = '../../../../compiled/Shaders/' + base_name + path = fp + '/compiled/Shaders/' + base_name if not os.path.exists(path): os.makedirs(path) diff --git a/blender/lib/server.py b/blender/lib/server.py index 53cc5b53..fc7121d1 100755 --- a/blender/lib/server.py +++ b/blender/lib/server.py @@ -10,7 +10,7 @@ import os def run_server(): Handler = http.server.SimpleHTTPRequestHandler - httpd = socketserver.TCPServer(("", 8080), Handler) + httpd = socketserver.TCPServer(("", 8040), Handler) httpd.serve_forever() run_server() diff --git a/blender/nodes_logic.py b/blender/nodes_logic.py index 24efc18c..b0d746c0 100755 --- a/blender/nodes_logic.py +++ b/blender/nodes_logic.py @@ -232,7 +232,7 @@ def buildNodeTrees(): # Export node scripts for node_group in bpy.data.node_groups: - if node_group.bl_idname == 'CGTreeType': # Build only cycles game trees + if node_group.bl_idname == 'CGTreeType': # Build only game trees node_group.use_fake_user = True # Keep fake references for now buildNodeTree(node_group) @@ -245,8 +245,8 @@ def buildNodeTree(node_group): with open(path + node_group_name + '.hx', 'w') as f: f.write('package ' + bpy.data.worlds[0].CGProjectPackage + '.node;\n\n') - f.write('import cycles.node.*;\n\n') - f.write('class ' + node_group_name + ' extends cycles.trait.NodeExecutor {\n\n') + f.write('import armory.node.*;\n\n') + f.write('class ' + node_group_name + ' extends armory.trait.internal.NodeExecutor {\n\n') f.write('\tpublic function new() { super(); requestAdd(add); }\n\n') f.write('\tfunction add() {\n') # Make sure root node exists diff --git a/blender/nodes_material.py b/blender/nodes_material.py index c4ab2ed3..fe4a316a 100644 --- a/blender/nodes_material.py +++ b/blender/nodes_material.py @@ -114,7 +114,7 @@ def make_texture(self, id, image_node, material): if image.source == 'MOVIE': # Just append movie texture trait for now movie_trait = Object() movie_trait.type = 'Script' - movie_trait.class_name = 'MovieTexture' + movie_trait.class_name = 'armory.trait.internal.MovieTexture' movie_trait.parameters = [tex.name] for o in self.materialToGameObjectDict[material]: o.traits.append(movie_trait) diff --git a/blender/nodes_pipeline.py b/blender/nodes_pipeline.py index b81d967d..c333f9e4 100755 --- a/blender/nodes_pipeline.py +++ b/blender/nodes_pipeline.py @@ -9,448 +9,678 @@ import subprocess import nodes_compositor class CGPipelineTree(NodeTree): - '''Pipeline nodes''' - bl_idname = 'CGPipelineTreeType' - bl_label = 'CG Pipeline Node Tree' - bl_icon = 'GAME' + '''Pipeline nodes''' + bl_idname = 'CGPipelineTreeType' + bl_label = 'CG Pipeline Node Tree' + bl_icon = 'GAME' class CGPipelineTreeNode: - @classmethod - def poll(cls, ntree): - return ntree.bl_idname == 'CGPipelineTreeType' - + @classmethod + def poll(cls, ntree): + return ntree.bl_idname == 'CGPipelineTreeType' + +# Prebuilt +class QuadPassNode(Node, CGPipelineTreeNode): + '''A custom node''' + bl_idname = 'QuadPassNodeType' + bl_label = 'Quad Pass' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketShader', "Stage") + self.inputs.new('NodeSocketShader', "Target") + self.inputs.new('NodeSocketString', "Shader Context") + self.inputs.new('NodeSocketShader', "Bind 1") + self.inputs.new('NodeSocketString', "Constant") + self.inputs.new('NodeSocketShader', "Bind 2") + self.inputs.new('NodeSocketString', "Constant") + self.inputs.new('NodeSocketShader', "Bind 3") + self.inputs.new('NodeSocketString', "Constant") + + self.outputs.new('NodeSocketShader', "Stage") + + def copy(self, node): + print("Copying from node ", node) + + def free(self): + print("Removing node ", self, ", Goodbye!") + +class SSAOPassNode(Node, CGPipelineTreeNode): + '''A custom node''' + bl_idname = 'SSAOPassNodeType' + bl_label = 'SSAO' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketShader', "Stage") + self.inputs.new('NodeSocketShader', "Target") + self.inputs.new('NodeSocketShader', "Target Temp") + self.inputs.new('NodeSocketShader', "GBufferD") + self.inputs.new('NodeSocketShader', "GBuffer0") + + self.outputs.new('NodeSocketShader', "Stage") + + def copy(self, node): + print("Copying from node ", node) + + def free(self): + print("Removing node ", self, ", Goodbye!") + +class SSRPassNode(Node, CGPipelineTreeNode): + '''A custom node''' + bl_idname = 'SSRPassNodeType' + bl_label = 'SSR' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketShader', "Stage") + self.inputs.new('NodeSocketShader', "Target") + self.inputs.new('NodeSocketShader', "A") + self.inputs.new('NodeSocketShader', "B") + self.inputs.new('NodeSocketShader', "Color") + self.inputs.new('NodeSocketShader', "GBuffer") + self.inputs.new('NodeSocketShader', "GBuffer1") + + self.outputs.new('NodeSocketShader', "Stage") + + def copy(self, node): + print("Copying from node ", node) + + def free(self): + print("Removing node ", self, ", Goodbye!") + +class BloomPassNode(Node, CGPipelineTreeNode): + '''A custom node''' + bl_idname = 'BloomPassNodeType' + bl_label = 'Bloom' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketShader', "Stage") + self.inputs.new('NodeSocketShader', "Target") + self.inputs.new('NodeSocketShader', "A") + self.inputs.new('NodeSocketShader', "B") + self.inputs.new('NodeSocketShader', "Color") + + self.outputs.new('NodeSocketShader', "Stage") + + def copy(self, node): + print("Copying from node ", node) + + def free(self): + print("Removing node ", self, ", Goodbye!") + +class MotionBlurPassNode(Node, CGPipelineTreeNode): + '''A custom node''' + bl_idname = 'MotionBlurPassNodeType' + bl_label = 'Motion Blur' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketShader', "Stage") + self.inputs.new('NodeSocketShader', "Target") + self.inputs.new('NodeSocketShader', "Color") + self.inputs.new('NodeSocketShader', "GBufferD") + self.inputs.new('NodeSocketShader', "GBuffer0") + + self.outputs.new('NodeSocketShader', "Stage") + + def copy(self, node): + print("Copying from node ", node) + + def free(self): + print("Removing node ", self, ", Goodbye!") + +class FXAAPassNode(Node, CGPipelineTreeNode): + '''A custom node''' + bl_idname = 'FXAAPassNodeType' + bl_label = 'FXAA' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketShader', "Stage") + self.inputs.new('NodeSocketShader', "Target") + self.inputs.new('NodeSocketShader', "Color") + + self.outputs.new('NodeSocketShader', "Stage") + + def copy(self, node): + print("Copying from node ", node) + + def free(self): + print("Removing node ", self, ", Goodbye!") + +class SMAAPassNode(Node, CGPipelineTreeNode): + '''A custom node''' + bl_idname = 'SMAAPassNodeType' + bl_label = 'SMAA' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketShader', "Stage") + self.inputs.new('NodeSocketShader', "Target") + self.inputs.new('NodeSocketShader', "Edges Target") + self.inputs.new('NodeSocketShader', "Blend Target") + self.inputs.new('NodeSocketShader', "Color") + + self.outputs.new('NodeSocketShader', "Stage") + + def copy(self, node): + print("Copying from node ", node) + + def free(self): + print("Removing node ", self, ", Goodbye!") + +class SSSPassNode(Node, CGPipelineTreeNode): + '''A custom node''' + bl_idname = 'SSSPassNodeType' + bl_label = 'SSS' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketShader', "Stage") + self.inputs.new('NodeSocketShader', "Target In") + self.inputs.new('NodeSocketShader', "Target Out") + self.inputs.new('NodeSocketShader', "Color") + self.inputs.new('NodeSocketShader', "GBufferD") + self.inputs.new('NodeSocketShader', "GBuffer0") + + self.outputs.new('NodeSocketShader', "Stage") + + def copy(self, node): + print("Copying from node ", node) + + def free(self): + print("Removing node ", self, ", Goodbye!") + +class WaterPassNode(Node, CGPipelineTreeNode): + '''A custom node''' + bl_idname = 'WaterPassNodeType' + bl_label = 'Water' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketShader', "Stage") + self.inputs.new('NodeSocketShader', "Target") + self.inputs.new('NodeSocketShader', "Color") + self.inputs.new('NodeSocketShader', "GBufferD") + self.inputs.new('NodeSocketShader', "GBuffer0") + + self.outputs.new('NodeSocketShader', "Stage") + + def copy(self, node): + print("Copying from node ", node) + + def free(self): + print("Removing node ", self, ", Goodbye!") + +class DeferredLightPassNode(Node, CGPipelineTreeNode): + '''A custom node''' + bl_idname = 'DeferredLightPassNodeType' + bl_label = 'Deferred Light' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketShader', "Stage") + self.inputs.new('NodeSocketShader', "Target") + self.inputs.new('NodeSocketShader', "GBuffer") + self.inputs.new('NodeSocketShader', "SSAO Map") + self.inputs.new('NodeSocketShader', "Shadow Map") + + self.outputs.new('NodeSocketShader', "Stage") + + def copy(self, node): + print("Copying from node ", node) + + def free(self): + print("Removing node ", self, ", Goodbye!") + +class TranslucentResolvePassNode(Node, CGPipelineTreeNode): + '''A custom node''' + bl_idname = 'TranslucentResolvePassNodeType' + bl_label = 'Translucent Resolve' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketShader', "Stage") + self.inputs.new('NodeSocketShader', "Target") + self.inputs.new('NodeSocketShader', "Translucent GBuffer") + + self.outputs.new('NodeSocketShader', "Stage") + + def copy(self, node): + print("Copying from node ", node) + + def free(self): + print("Removing node ", self, ", Goodbye!") + +# Pipeline class DrawGeometryNode(Node, CGPipelineTreeNode): - '''A custom node''' - bl_idname = 'DrawGeometryNodeType' - bl_label = 'Draw Geometry' - bl_icon = 'SOUND' - - def init(self, context): - self.inputs.new('NodeSocketShader', "Stage") - self.inputs.new('NodeSocketString', "Context") + '''A custom node''' + bl_idname = 'DrawGeometryNodeType' + bl_label = 'Draw Geometry' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketShader', "Stage") + self.inputs.new('NodeSocketString', "Context") - self.outputs.new('NodeSocketShader', "Stage") + self.outputs.new('NodeSocketShader', "Stage") - def copy(self, node): - print("Copying from node ", node) + def copy(self, node): + print("Copying from node ", node) - def free(self): - print("Removing node ", self, ", Goodbye!") - + def free(self): + print("Removing node ", self, ", Goodbye!") + class DrawDecalsNode(Node, CGPipelineTreeNode): - '''A custom node''' - bl_idname = 'DrawDecalsNodeType' - bl_label = 'Draw Decals' - bl_icon = 'SOUND' - - def init(self, context): - self.inputs.new('NodeSocketShader', "Stage") - self.inputs.new('NodeSocketString', "Context") + '''A custom node''' + bl_idname = 'DrawDecalsNodeType' + bl_label = 'Draw Decals' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketShader', "Stage") + self.inputs.new('NodeSocketString', "Context") - self.outputs.new('NodeSocketShader', "Stage") + self.outputs.new('NodeSocketShader', "Stage") - def copy(self, node): - print("Copying from node ", node) + def copy(self, node): + print("Copying from node ", node) - def free(self): - print("Removing node ", self, ", Goodbye!") - + def free(self): + print("Removing node ", self, ", Goodbye!") + class ClearTargetNode(Node, CGPipelineTreeNode): - '''A custom node''' - bl_idname = 'ClearTargetNodeType' - bl_label = 'Clear Target' - bl_icon = 'SOUND' - - def init(self, context): - self.inputs.new('NodeSocketShader', "Stage") - self.inputs.new('NodeSocketBool', "Color") - self.inputs.new('NodeSocketColor', "Value") - self.inputs.new('NodeSocketBool', "Depth") - self.inputs.new('NodeSocketFloat', "Value") - self.inputs[4].default_value = 1.0 - self.inputs.new('NodeSocketBool', "Stencil") - self.inputs.new('NodeSocketInt', "Value") + '''A custom node''' + bl_idname = 'ClearTargetNodeType' + bl_label = 'Clear Target' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketShader', "Stage") + self.inputs.new('NodeSocketBool', "Color") + self.inputs.new('NodeSocketColor', "Value") + self.inputs.new('NodeSocketBool', "Depth") + self.inputs.new('NodeSocketFloat', "Value") + self.inputs[4].default_value = 1.0 + self.inputs.new('NodeSocketBool', "Stencil") + self.inputs.new('NodeSocketInt', "Value") - self.outputs.new('NodeSocketShader', "Stage") + self.outputs.new('NodeSocketShader', "Stage") - def copy(self, node): - print("Copying from node ", node) + def copy(self, node): + print("Copying from node ", node) - def free(self): - print("Removing node ", self, ", Goodbye!") + def free(self): + print("Removing node ", self, ", Goodbye!") class BeginNode(Node, CGPipelineTreeNode): - '''A custom node''' - bl_idname = 'BeginNodeType' - bl_label = 'Begin' - bl_icon = 'SOUND' - - def init(self, context): - self.outputs.new('NodeSocketShader', "Stage") + '''A custom node''' + bl_idname = 'BeginNodeType' + bl_label = 'Begin' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketString', "ID") + self.inputs.new('NodeSocketString', "Geometry") + self.inputs.new('NodeSocketString', "Shadows") + self.inputs.new('NodeSocketString', "Translucent") + self.outputs.new('NodeSocketShader', "Stage") - def copy(self, node): - print("Copying from node ", node) + def copy(self, node): + print("Copying from node ", node) - def free(self): - print("Removing node ", self, ", Goodbye!") - + def free(self): + print("Removing node ", self, ", Goodbye!") + class SetTargetNode(Node, CGPipelineTreeNode): - '''A custom node''' - bl_idname = 'SetTargetNodeType' - bl_label = 'Set Target' - bl_icon = 'SOUND' - - def init(self, context): - self.inputs.new('NodeSocketShader', "Stage") - self.inputs.new('NodeSocketShader', "Target") + '''A custom node''' + bl_idname = 'SetTargetNodeType' + bl_label = 'Set Target' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketShader', "Stage") + self.inputs.new('NodeSocketShader', "Target") - self.outputs.new('NodeSocketShader', "Stage") + self.outputs.new('NodeSocketShader', "Stage") - def copy(self, node): - print("Copying from node ", node) + def copy(self, node): + print("Copying from node ", node) - def free(self): - print("Removing node ", self, ", Goodbye!") - + def free(self): + print("Removing node ", self, ", Goodbye!") + class TargetNode(Node, CGPipelineTreeNode): - '''A custom node''' - bl_idname = 'TargetNodeType' - bl_label = 'Target' - bl_icon = 'SOUND' - - def init(self, context): - self.inputs.new('NodeSocketString', "ID") - self.inputs.new('NodeSocketInt', "Width") - self.inputs.new('NodeSocketInt', "Height") - self.inputs.new('NodeSocketShader', "Depth Buffer") - self.inputs.new('NodeSocketString', "Format") - self.inputs.new('NodeSocketBool', "Ping Pong") + '''A custom node''' + bl_idname = 'TargetNodeType' + bl_label = 'Target' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketString', "ID") + self.inputs.new('NodeSocketInt', "Width") + self.inputs.new('NodeSocketInt', "Height") + self.inputs.new('NodeSocketShader', "Depth Buffer") + self.inputs.new('NodeSocketString', "Format") + self.inputs.new('NodeSocketBool', "Ping Pong") - self.outputs.new('NodeSocketShader', "Target") + self.outputs.new('NodeSocketShader', "Target") - def copy(self, node): - print("Copying from node ", node) + def copy(self, node): + print("Copying from node ", node) - def free(self): - print("Removing node ", self, ", Goodbye!") + def free(self): + print("Removing node ", self, ", Goodbye!") class TargetArrayNode(Node, CGPipelineTreeNode): - '''A custom node''' - bl_idname = 'TargetArrayNodeType' - bl_label = 'Target Array' - bl_icon = 'SOUND' - - def init(self, context): - self.inputs.new('NodeSocketShader', "Target") - self.inputs.new('NodeSocketInt', "Instances") + '''A custom node''' + bl_idname = 'TargetArrayNodeType' + bl_label = 'Target Array' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketShader', "Target") + self.inputs.new('NodeSocketInt', "Instances") - self.outputs.new('NodeSocketShader', "Targets") + self.outputs.new('NodeSocketShader', "Targets") - def copy(self, node): - print("Copying from node ", node) + def copy(self, node): + print("Copying from node ", node) - def free(self): - print("Removing node ", self, ", Goodbye!") + def free(self): + print("Removing node ", self, ", Goodbye!") -class DebthBufferNode(Node, CGPipelineTreeNode): - '''A custom node''' - bl_idname = 'DepthBufferNodeType' - bl_label = 'Depth Buffer' - bl_icon = 'SOUND' - - def init(self, context): - self.inputs.new('NodeSocketString', "ID") - self.inputs.new('NodeSocketBool', "Stencil") - - self.outputs.new('NodeSocketShader', "Target") +class DepthBufferNode(Node, CGPipelineTreeNode): + '''A custom node''' + bl_idname = 'DepthBufferNodeType' + bl_label = 'Depth Buffer' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketString', "ID") + self.inputs.new('NodeSocketBool', "Stencil") + + self.outputs.new('NodeSocketShader', "Target") - def copy(self, node): - print("Copying from node ", node) + def copy(self, node): + print("Copying from node ", node) - def free(self): - print("Removing node ", self, ", Goodbye!") + def free(self): + print("Removing node ", self, ", Goodbye!") class GBufferNode(Node, CGPipelineTreeNode): - '''A custom node''' - bl_idname = 'GBufferNodeType' - bl_label = 'GBuffer' - bl_icon = 'SOUND' - - def init(self, context): - self.inputs.new('NodeSocketShader', "Target 0") - self.inputs.new('NodeSocketShader', "Target 1") - self.inputs.new('NodeSocketShader', "Target 2") - self.inputs.new('NodeSocketShader', "Target 3") - self.inputs.new('NodeSocketShader', "Target 4") + '''A custom node''' + bl_idname = 'GBufferNodeType' + bl_label = 'GBuffer' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketShader', "Target 0") + self.inputs.new('NodeSocketShader', "Target 1") + self.inputs.new('NodeSocketShader', "Target 2") + self.inputs.new('NodeSocketShader', "Target 3") + self.inputs.new('NodeSocketShader', "Target 4") - self.outputs.new('NodeSocketShader', "Targets") + self.outputs.new('NodeSocketShader', "Targets") - def copy(self, node): - print("Copying from node ", node) + def copy(self, node): + print("Copying from node ", node) - def free(self): - print("Removing node ", self, ", Goodbye!") - + def free(self): + print("Removing node ", self, ", Goodbye!") + class FramebufferNode(Node, CGPipelineTreeNode): - '''A custom node''' - bl_idname = 'FramebufferNodeType' - bl_label = 'Framebuffer' - bl_icon = 'SOUND' - - def init(self, context): - self.outputs.new('NodeSocketShader', "Target") + '''A custom node''' + bl_idname = 'FramebufferNodeType' + bl_label = 'Framebuffer' + bl_icon = 'SOUND' + + def init(self, context): + self.outputs.new('NodeSocketShader', "Target") - def copy(self, node): - print("Copying from node ", node) + def copy(self, node): + print("Copying from node ", node) - def free(self): - print("Removing node ", self, ", Goodbye!") + def free(self): + print("Removing node ", self, ", Goodbye!") class BindTargetNode(Node, CGPipelineTreeNode): - '''A custom node''' - bl_idname = 'BindTargetNodeType' - bl_label = 'Bind Target' - bl_icon = 'SOUND' - - def init(self, context): - self.inputs.new('NodeSocketShader', "Stage") - self.inputs.new('NodeSocketShader', "Target") - self.inputs.new('NodeSocketString', "Constant") + '''A custom node''' + bl_idname = 'BindTargetNodeType' + bl_label = 'Bind Target' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketShader', "Stage") + self.inputs.new('NodeSocketShader', "Target") + self.inputs.new('NodeSocketString', "Constant") - self.outputs.new('NodeSocketShader', "Stage") + self.outputs.new('NodeSocketShader', "Stage") - def copy(self, node): - print("Copying from node ", node) + def copy(self, node): + print("Copying from node ", node) - def free(self): - print("Removing node ", self, ", Goodbye!") + def free(self): + print("Removing node ", self, ", Goodbye!") class DrawMaterialQuadNode(Node, CGPipelineTreeNode): - '''A custom node''' - bl_idname = 'DrawMaterialQuadNodeType' - bl_label = 'Draw Material Quad' - bl_icon = 'SOUND' - - def init(self, context): - self.inputs.new('NodeSocketShader', "Stage") - self.inputs.new('NodeSocketString', "Material Context") + '''A custom node''' + bl_idname = 'DrawMaterialQuadNodeType' + bl_label = 'Draw Material Quad' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketShader', "Stage") + self.inputs.new('NodeSocketString', "Material Context") - self.outputs.new('NodeSocketShader', "Stage") + self.outputs.new('NodeSocketShader', "Stage") - def copy(self, node): - print("Copying from node ", node) + def copy(self, node): + print("Copying from node ", node) - def free(self): - print("Removing node ", self, ", Goodbye!") - + def free(self): + print("Removing node ", self, ", Goodbye!") + class DrawQuadNode(Node, CGPipelineTreeNode): - '''A custom node''' - bl_idname = 'DrawQuadNodeType' - bl_label = 'Draw Quad' - bl_icon = 'SOUND' - - def init(self, context): - self.inputs.new('NodeSocketShader', "Stage") - self.inputs.new('NodeSocketString', "Shader Context") + '''A custom node''' + bl_idname = 'DrawQuadNodeType' + bl_label = 'Draw Quad' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketShader', "Stage") + self.inputs.new('NodeSocketString', "Shader Context") - self.outputs.new('NodeSocketShader', "Stage") + self.outputs.new('NodeSocketShader', "Stage") - def copy(self, node): - print("Copying from node ", node) + def copy(self, node): + print("Copying from node ", node) - def free(self): - print("Removing node ", self, ", Goodbye!") + def free(self): + print("Removing node ", self, ", Goodbye!") class CallFunctionNode(Node, CGPipelineTreeNode): - '''A custom node''' - bl_idname = 'CallFunctionNodeType' - bl_label = 'Call Function' - bl_icon = 'SOUND' - - def init(self, context): - self.inputs.new('NodeSocketShader', "Stage") - self.inputs.new('NodeSocketString', "Function") + '''A custom node''' + bl_idname = 'CallFunctionNodeType' + bl_label = 'Call Function' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketShader', "Stage") + self.inputs.new('NodeSocketString', "Function") - self.outputs.new('NodeSocketShader', "Stage") + self.outputs.new('NodeSocketShader', "Stage") - def copy(self, node): - print("Copying from node ", node) + def copy(self, node): + print("Copying from node ", node) - def free(self): - print("Removing node ", self, ", Goodbye!") - + def free(self): + print("Removing node ", self, ", Goodbye!") + class BranchFunctionNode(Node, CGPipelineTreeNode): - '''A custom node''' - bl_idname = 'BranchFunctionNodeType' - bl_label = 'Branch Function' - bl_icon = 'SOUND' - - def init(self, context): - self.inputs.new('NodeSocketShader', "Stage") - self.inputs.new('NodeSocketString', "Function") + '''A custom node''' + bl_idname = 'BranchFunctionNodeType' + bl_label = 'Branch Function' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketShader', "Stage") + self.inputs.new('NodeSocketString', "Function") - self.outputs.new('NodeSocketShader', "True") - self.outputs.new('NodeSocketShader', "False") + self.outputs.new('NodeSocketShader', "True") + self.outputs.new('NodeSocketShader', "False") - def copy(self, node): - print("Copying from node ", node) + def copy(self, node): + print("Copying from node ", node) - def free(self): - print("Removing node ", self, ", Goodbye!") - + def free(self): + print("Removing node ", self, ", Goodbye!") + class MergeStagesNode(Node, CGPipelineTreeNode): - '''A custom node''' - bl_idname = 'MergeStagesNodeType' - bl_label = 'Merge Stages' - bl_icon = 'SOUND' - - def init(self, context): - self.inputs.new('NodeSocketShader', "Stage") - self.inputs.new('NodeSocketShader', "Stage") + '''A custom node''' + bl_idname = 'MergeStagesNodeType' + bl_label = 'Merge Stages' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketShader', "Stage") + self.inputs.new('NodeSocketShader', "Stage") - self.outputs.new('NodeSocketShader', "Stage") + self.outputs.new('NodeSocketShader', "Stage") - def copy(self, node): - print("Copying from node ", node) + def copy(self, node): + print("Copying from node ", node) - def free(self): - print("Removing node ", self, ", Goodbye!") + def free(self): + print("Removing node ", self, ", Goodbye!") class LoopStagesNode(Node, CGPipelineTreeNode): - '''A custom node''' - bl_idname = 'LoopStagesNodeType' - bl_label = 'Loop Stages' - bl_icon = 'SOUND' - - def init(self, context): - self.inputs.new('NodeSocketShader', "Stage") - self.inputs.new('NodeSocketInt', "From") - self.inputs.new('NodeSocketInt', "To") + '''A custom node''' + bl_idname = 'LoopStagesNodeType' + bl_label = 'Loop Stages' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketShader', "Stage") + self.inputs.new('NodeSocketInt', "From") + self.inputs.new('NodeSocketInt', "To") - self.outputs.new('NodeSocketShader', "Complete") - self.outputs.new('NodeSocketShader', "Loop") + self.outputs.new('NodeSocketShader', "Complete") + self.outputs.new('NodeSocketShader', "Loop") - def copy(self, node): - print("Copying from node ", node) + def copy(self, node): + print("Copying from node ", node) - def free(self): - print("Removing node ", self, ", Goodbye!") - + def free(self): + print("Removing node ", self, ", Goodbye!") + class LoopLightsNode(Node, CGPipelineTreeNode): - '''A custom node''' - bl_idname = 'LoopLightsNodeType' - bl_label = 'Loop Lights' - bl_icon = 'SOUND' - - def init(self, context): - self.inputs.new('NodeSocketShader', "Stage") + '''A custom node''' + bl_idname = 'LoopLightsNodeType' + bl_label = 'Loop Lights' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketShader', "Stage") - self.outputs.new('NodeSocketShader', "Complete") - self.outputs.new('NodeSocketShader', "Loop") + self.outputs.new('NodeSocketShader', "Complete") + self.outputs.new('NodeSocketShader', "Loop") - def copy(self, node): - print("Copying from node ", node) + def copy(self, node): + print("Copying from node ", node) - def free(self): - print("Removing node ", self, ", Goodbye!") + def free(self): + print("Removing node ", self, ", Goodbye!") class DrawWorldNode(Node, CGPipelineTreeNode): - '''A custom node''' - bl_idname = 'DrawWorldNodeType' - bl_label = 'Draw World' - bl_icon = 'SOUND' - - def init(self, context): - self.inputs.new('NodeSocketShader', "Stage") - self.inputs.new('NodeSocketShader', "Depth") + '''A custom node''' + bl_idname = 'DrawWorldNodeType' + bl_label = 'Draw World' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketShader', "Stage") + self.inputs.new('NodeSocketShader', "Depth") - self.outputs.new('NodeSocketShader', "Stage") + self.outputs.new('NodeSocketShader', "Stage") - def copy(self, node): - print("Copying from node ", node) + def copy(self, node): + print("Copying from node ", node) - def free(self): - print("Removing node ", self, ", Goodbye!") + def free(self): + print("Removing node ", self, ", Goodbye!") class DrawCompositorNode(Node, CGPipelineTreeNode): - '''A custom node''' - bl_idname = 'DrawCompositorNodeType' - bl_label = 'Draw Compositor' - bl_icon = 'SOUND' - - def init(self, context): - self.inputs.new('NodeSocketShader', "Stage") - self.inputs.new('NodeSocketShader', "Target") - self.inputs.new('NodeSocketShader', "Color") - self.inputs.new('NodeSocketShader', "GBuffer") + '''A custom node''' + bl_idname = 'DrawCompositorNodeType' + bl_label = 'Draw Compositor' + bl_icon = 'SOUND' + + def init(self, context): + self.inputs.new('NodeSocketShader', "Stage") + self.inputs.new('NodeSocketShader', "Target") + self.inputs.new('NodeSocketShader', "Color") + self.inputs.new('NodeSocketShader', "GBuffer") - self.outputs.new('NodeSocketShader', "Stage") + self.outputs.new('NodeSocketShader', "Stage") - def copy(self, node): - print("Copying from node ", node) + def copy(self, node): + print("Copying from node ", node) - def free(self): - print("Removing node ", self, ", Goodbye!") + def free(self): + print("Removing node ", self, ", Goodbye!") -# Pass nodes -class QuadPassNode(Node, CGPipelineTreeNode): - '''A custom node''' - bl_idname = 'QuadPassNodeType' - bl_label = 'Quad Pass' - bl_icon = 'SOUND' - - def init(self, context): - self.inputs.new('NodeSocketShader', "Stage") - self.inputs.new('NodeSocketShader', "Target") - self.inputs.new('NodeSocketString', "Shader Context") - self.inputs.new('NodeSocketShader', "Bind 1") - self.inputs.new('NodeSocketString', "Constant") - self.inputs.new('NodeSocketShader', "Bind 2") - self.inputs.new('NodeSocketString', "Constant") - self.inputs.new('NodeSocketShader', "Bind 3") - self.inputs.new('NodeSocketString', "Constant") - - self.outputs.new('NodeSocketShader', "Stage") - - def copy(self, node): - print("Copying from node ", node) - - def free(self): - print("Removing node ", self, ", Goodbye!") # Constant nodes class ScreenNode(Node, CGPipelineTreeNode): - '''A custom node''' - bl_idname = 'ScreenNodeType' - bl_label = 'Screen' - bl_icon = 'SOUND' - - def init(self, context): - self.outputs.new('NodeSocketInt', "Width") - self.outputs.new('NodeSocketInt', "Height") - self.inputs.new('NodeSocketFloat', "Scale") - self.inputs[0].default_value = 1.0 + '''A custom node''' + bl_idname = 'ScreenNodeType' + bl_label = 'Screen' + bl_icon = 'SOUND' + + def init(self, context): + self.outputs.new('NodeSocketInt', "Width") + self.outputs.new('NodeSocketInt', "Height") + self.inputs.new('NodeSocketFloat', "Scale") + self.inputs[0].default_value = 1.0 - def copy(self, node): - print("Copying from node ", node) + def copy(self, node): + print("Copying from node ", node) - def free(self): - print("Removing node ", self, ", Goodbye!") - + def free(self): + print("Removing node ", self, ", Goodbye!") + +class BackgroundColorNode(Node, CGPipelineTreeNode): + '''A custom node''' + bl_idname = 'BackgroundColorNodeType' + bl_label = 'Background Color' + bl_icon = 'SOUND' + + def init(self, context): + self.outputs.new('NodeSocketInt', "Color") + + def copy(self, node): + print("Copying from node ", node) + + def free(self): + print("Removing node ", self, ", Goodbye!") + class LightCount(Node, CGPipelineTreeNode): - '''A custom node''' - bl_idname = 'LightCountNodeType' - bl_label = 'Light Count' - bl_icon = 'SOUND' - - def init(self, context): - self.outputs.new('NodeSocketInt', "Count") + '''A custom node''' + bl_idname = 'LightCountNodeType' + bl_label = 'Light Count' + bl_icon = 'SOUND' + + def init(self, context): + self.outputs.new('NodeSocketInt', "Count") - def copy(self, node): - print("Copying from node ", node) + def copy(self, node): + print("Copying from node ", node) - def free(self): - print("Removing node ", self, ", Goodbye!") + def free(self): + print("Removing node ", self, ", Goodbye!") ### Node Categories ### # Node categories are a python system for automatically @@ -461,65 +691,75 @@ import nodeitems_utils from nodeitems_utils import NodeCategory, NodeItem class MyPipelineNodeCategory(NodeCategory): - @classmethod - def poll(cls, context): - return context.space_data.tree_type == 'CGPipelineTreeType' + @classmethod + def poll(cls, context): + return context.space_data.tree_type == 'CGPipelineTreeType' class MyTargetNodeCategory(NodeCategory): - @classmethod - def poll(cls, context): - return context.space_data.tree_type == 'CGPipelineTreeType' + @classmethod + def poll(cls, context): + return context.space_data.tree_type == 'CGPipelineTreeType' class MyPassNodeCategory(NodeCategory): - @classmethod - def poll(cls, context): - return context.space_data.tree_type == 'CGPipelineTreeType' - + @classmethod + def poll(cls, context): + return context.space_data.tree_type == 'CGPipelineTreeType' + class MyConstantNodeCategory(NodeCategory): - @classmethod - def poll(cls, context): - return context.space_data.tree_type == 'CGPipelineTreeType' + @classmethod + def poll(cls, context): + return context.space_data.tree_type == 'CGPipelineTreeType' class MyLogicNodeCategory(NodeCategory): - @classmethod - def poll(cls, context): - return context.space_data.tree_type == 'CGPipelineTreeType' + @classmethod + def poll(cls, context): + return context.space_data.tree_type == 'CGPipelineTreeType' node_categories = [ - MyPipelineNodeCategory("PIPELINENODES", "Pipeline", items=[ - NodeItem("BeginNodeType"), - NodeItem("DrawGeometryNodeType"), - NodeItem("DrawDecalsNodeType"), - NodeItem("ClearTargetNodeType"), - NodeItem("SetTargetNodeType"), - NodeItem("BindTargetNodeType"), - NodeItem("DrawMaterialQuadNodeType"), - NodeItem("DrawQuadNodeType"), - NodeItem("DrawWorldNodeType"), - NodeItem("DrawCompositorNodeType"), - ]), - MyTargetNodeCategory("TARGETNODES", "Target", items=[ - NodeItem("TargetNodeType"), - NodeItem("TargetArrayNodeType"), - NodeItem("DepthBufferNodeType"), - NodeItem("GBufferNodeType"), - NodeItem("FramebufferNodeType"), - ]), - MyPassNodeCategory("PASSNODES", "Pass", items=[ - NodeItem("QuadPassNodeType"), - # Prebuilt passes here - ]), - MyConstantNodeCategory("CONSTANTNODES", "Constant", items=[ - NodeItem("ScreenNodeType"), - NodeItem("LightCountNodeType"), - ]), - MyLogicNodeCategory("LOGICNODES", "Logic", items=[ - NodeItem("CallFunctionNodeType"), - NodeItem("BranchFunctionNodeType"), - NodeItem("MergeStagesNodeType"), - NodeItem("LoopStagesNodeType"), - NodeItem("LoopLightsNodeType"), - ]), + MyPipelineNodeCategory("PIPELINENODES", "Pipeline", items=[ + NodeItem("BeginNodeType"), + NodeItem("DrawGeometryNodeType"), + NodeItem("DrawDecalsNodeType"), + NodeItem("ClearTargetNodeType"), + NodeItem("SetTargetNodeType"), + NodeItem("BindTargetNodeType"), + NodeItem("DrawMaterialQuadNodeType"), + NodeItem("DrawQuadNodeType"), + NodeItem("DrawWorldNodeType"), + NodeItem("DrawCompositorNodeType"), + ]), + MyTargetNodeCategory("TARGETNODES", "Target", items=[ + NodeItem("TargetNodeType"), + NodeItem("TargetArrayNodeType"), + NodeItem("DepthBufferNodeType"), + NodeItem("GBufferNodeType"), + NodeItem("FramebufferNodeType"), + ]), + MyPassNodeCategory("PREBUILTNODES", "Prebuilt", items=[ + NodeItem("QuadPassNodeType"), + NodeItem("SSAOPassNodeType"), + NodeItem("SSRPassNodeType"), + NodeItem("BloomPassNodeType"), + NodeItem("MotionBlurPassNodeType"), + NodeItem("FXAAPassNodeType"), + NodeItem("SMAAPassNodeType"), + NodeItem("SSSPassNodeType"), + NodeItem("WaterPassNodeType"), + NodeItem("DeferredLightPassNodeType"), + NodeItem("TranslucentResolvePassNodeType"), + ]), + MyConstantNodeCategory("CONSTANTNODES", "Constant", items=[ + NodeItem("ScreenNodeType"), + NodeItem("BackgroundColorNodeType"), + NodeItem("LightCountNodeType"), + ]), + MyLogicNodeCategory("LOGICNODES", "Logic", items=[ + NodeItem("CallFunctionNodeType"), + NodeItem("BranchFunctionNodeType"), + NodeItem("MergeStagesNodeType"), + NodeItem("LoopStagesNodeType"), + NodeItem("LoopLightsNodeType"), + ]), ] @@ -527,472 +767,628 @@ node_categories = [ def reload_blend_data(): - if bpy.data.node_groups.get('forward_pipeline') == None: - load_library() - pass + if bpy.data.node_groups.get('forward_pipeline') == None: + load_library() + pass def load_library(): - haxelib_path = "haxelib" - if platform.system() == 'Darwin': - haxelib_path = "/usr/local/bin/haxelib" + user_preferences = bpy.context.user_preferences + addon_prefs = user_preferences.addons['armory'].preferences + sdk_path = addon_prefs.sdk_path + data_path = sdk_path + '/armory/blender/data/data.blend' - output = subprocess.check_output([haxelib_path + " path cyclesgame"], shell=True) - output = str(output).split("\\n")[0].split("'")[1] - data_path = output[:-8] + "blender/data/data.blend" # Remove 'Sources/' from haxelib path - - with bpy.data.libraries.load(data_path, link=False) as (data_from, data_to): - data_to.node_groups = ['forward_pipeline', 'forward_pipeline_noshadow', 'deferred_pipeline', 'pathtrace_pipeline', 'PBR'] - - # TODO: cannot use for loop - # TODO: import pbr group separately, no need for fake user - bpy.data.node_groups['forward_pipeline'].use_fake_user = True - bpy.data.node_groups['forward_pipeline_noshadow'].use_fake_user = True - bpy.data.node_groups['deferred_pipeline'].use_fake_user = True - bpy.data.node_groups['pathtrace_pipeline'].use_fake_user = True - bpy.data.node_groups['PBR'].use_fake_user = True + with bpy.data.libraries.load(data_path, link=False) as (data_from, data_to): + data_to.node_groups = ['forward_pipeline', 'forward_pipeline_low', 'deferred_pipeline', 'deferred_pipeline_low', 'pathtrace_pipeline', 'PBR'] + + # TODO: cannot use for loop + # TODO: import pbr group separately, no need for fake user + bpy.data.node_groups['forward_pipeline'].use_fake_user = True + bpy.data.node_groups['forward_pipeline_low'].use_fake_user = True + bpy.data.node_groups['deferred_pipeline'].use_fake_user = True + bpy.data.node_groups['deferred_pipeline_low'].use_fake_user = True + bpy.data.node_groups['pathtrace_pipeline'].use_fake_user = True + bpy.data.node_groups['PBR'].use_fake_user = True def register(): - bpy.utils.register_module(__name__) - try: - nodeitems_utils.register_node_categories("CG_PIPELINE_NODES", node_categories) - reload_blend_data() - except: - pass + bpy.utils.register_module(__name__) + try: + nodeitems_utils.register_node_categories("CG_PIPELINE_NODES", node_categories) + reload_blend_data() + except: + pass def unregister(): - nodeitems_utils.unregister_node_categories("CG_PIPELINE_NODES") - bpy.utils.unregister_module(__name__) + nodeitems_utils.unregister_node_categories("CG_PIPELINE_NODES") + bpy.utils.unregister_module(__name__) # Generating pipeline resources class Object: - def to_JSON(self): - if bpy.data.worlds[0].CGMinimize == True: - return json.dumps(self, default=lambda o: o.__dict__, separators=(',',':')) - else: - return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4) + def to_JSON(self): + if bpy.data.worlds[0].CGMinimize == True: + return json.dumps(self, default=lambda o: o.__dict__, separators=(',',':')) + else: + return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4) -def buildNodeTrees(shader_references, asset_references): - s = bpy.data.filepath.split(os.path.sep) - s.pop() - fp = os.path.sep.join(s) - os.chdir(fp) +def buildNodeTrees(shader_references, asset_references, assets_path): + s = bpy.data.filepath.split(os.path.sep) + s.pop() + fp = os.path.sep.join(s) + os.chdir(fp) - # Make sure Assets dir exists - if not os.path.exists('Assets/generated/pipelines'): - os.makedirs('Assets/generated/pipelines') - - # Export selected pipeline - # node_group.bl_idname == 'CGPipelineTreeType' - node_group = bpy.data.node_groups[bpy.data.cameras[0].pipeline_path] - buildNodeTree(node_group, shader_references, asset_references) + # Make sure Assets dir exists + if not os.path.exists('Assets/generated/pipelines'): + os.makedirs('Assets/generated/pipelines') + + buildNodeTrees.assets_path = assets_path + buildNodeTrees.linked_assets = [] + # Always include + buildNodeTrees.linked_assets.append(buildNodeTrees.assets_path + 'brdf.png') + + # Export selected pipeline + # node_group.bl_idname == 'CGPipelineTreeType' + node_group = bpy.data.node_groups[bpy.data.cameras[0].pipeline_path] + buildNodeTree(node_group, shader_references, asset_references) + + return buildNodeTrees.linked_assets def buildNodeTree(node_group, shader_references, asset_references): - output = Object() - res = Object() - output.pipeline_resources = [res] - - path = 'Assets/generated/pipelines/' - node_group_name = node_group.name.replace('.', '_') - - rn = getRootNode(node_group) - if rn == None: - return - - res.id = node_group_name - res.render_targets, res.depth_buffers = get_render_targets(rn, node_group) - res.stages = [] - - # Used to merge bind target nodes into one stage - last_bind_target = None - - buildNode(res.stages, rn, node_group, last_bind_target, shader_references, asset_references) + output = Object() + res = Object() + output.pipeline_resources = [res] + + path = 'Assets/generated/pipelines/' + node_group_name = node_group.name.replace('.', '_') + + rn = getRootNode(node_group) + if rn == None: + return + + res.id = node_group_name + res.render_targets, res.depth_buffers = get_render_targets(rn, node_group) + res.stages = [] + + buildNode(res.stages, rn, node_group, shader_references, asset_references) - with open(path + node_group_name + '.json', 'w') as f: - f.write(output.to_JSON()) + with open(path + node_group_name + '.json', 'w') as f: + f.write(output.to_JSON()) def make_set_target(stage, node_group, node, currentNode=None, target_index=1): - if currentNode == None: - currentNode = node - - stage.command = 'set_target' - currentNode = findNodeByLink(node_group, currentNode, currentNode.inputs[target_index]) - - if currentNode.bl_idname == 'TargetNodeType': - targetId = currentNode.inputs[0].default_value - stage.params.append(targetId) - - elif currentNode.bl_idname == 'GBufferNodeType': - # Set all linked targets - for i in range(0, 5): - if currentNode.inputs[i].is_linked: - make_set_target(stage, node_group, node, currentNode, target_index=i) - - elif currentNode.bl_idname == 'NodeReroute': - make_set_target(stage, node_group, node, currentNode, target_index=0) - - else: # Framebuffer - targetId = '' - stage.params.append(targetId) + if currentNode == None: + currentNode = node + + stage.command = 'set_target' + currentNode = findNodeByLink(node_group, currentNode, currentNode.inputs[target_index]) + + if currentNode.bl_idname == 'TargetNodeType': + targetId = currentNode.inputs[0].default_value + stage.params.append(targetId) + # Store current target size + buildNode.last_set_target_w = currentNode.inputs[1].default_value + buildNode.last_set_target_h = currentNode.inputs[2].default_value + + elif currentNode.bl_idname == 'GBufferNodeType': + # Set all linked targets + for i in range(0, 5): + if currentNode.inputs[i].is_linked: + make_set_target(stage, node_group, node, currentNode, target_index=i) + + elif currentNode.bl_idname == 'NodeReroute': + make_set_target(stage, node_group, node, currentNode, target_index=0) + + else: # Framebuffer + targetId = '' + stage.params.append(targetId) -def make_clear_target(stage, node_group, node): - stage.command = 'clear_target' - if node.inputs[1].default_value == True: - stage.params.append('color') - val = node.inputs[2].default_value - hex = '#%02x%02x%02x%02x' % (int(val[3] * 255), int(val[0] * 255), int(val[1] * 255), int(val[2] * 255)) - stage.params.append(str(hex)) - if node.inputs[3].default_value == True: - stage.params.append('depth') - val = node.inputs[4].default_value - stage.params.append(str(val)) - if node.inputs[5].default_value == True: - stage.params.append('stencil') - val = node.inputs[6].default_value - stage.params.append(str(val)) +def to_hex(val): + return '#%02x%02x%02x%02x' % (int(val[3] * 255), int(val[0] * 255), int(val[1] * 255), int(val[2] * 255)) + +def make_clear_target(stage, color_val=None, depth_val=None, stencil_val=None): + stage.command = 'clear_target' + if color_val != None: + stage.params.append('color') + stage.params.append(str(to_hex(color_val))) + if depth_val != None: + stage.params.append('depth') + stage.params.append(str(depth_val)) + if stencil_val != None: + stage.params.append('stencil') + stage.params.append(str(stencil_val)) def make_draw_geometry(stage, node_group, node): - stage.command = 'draw_geometry' - stage.params.append(node.inputs[1].default_value) # Context - + stage.command = 'draw_geometry' + context = node.inputs[1].default_value + # Store shadowmap size + if context == bpy.data.cameras[0].shadows_context: + bpy.data.worlds[0].shadowmap_size = 1024 + stage.params.append(context) + def make_draw_decals(stage, node_group, node, shader_references, asset_references): - stage.command = 'draw_decals' - context = node.inputs[1].default_value - stage.params.append(context) # Context - bpy.data.cameras[0].last_decal_context = context + stage.command = 'draw_decals' + context = node.inputs[1].default_value + stage.params.append(context) # Context + bpy.data.cameras[0].last_decal_context = context -def make_bind_target(stage, node_group, node, currentNode=None, target_index=1, constant_index=2, constant_name=None): - if currentNode == None: - currentNode = node - - stage.command = 'bind_target' - - link = findLink(node_group, currentNode, currentNode.inputs[target_index]) - currentNode = link.from_node - - if constant_name == None: - constant_name = node.inputs[constant_index].default_value - - if currentNode.bl_idname == 'NodeReroute': - make_bind_target(stage, node_group, node, currentNode, target_index=0, constant_index=constant_index, constant_name=constant_name) - - elif currentNode.bl_idname == 'GBufferNodeType': - for i in range(0, 5): - if currentNode.inputs[i].is_linked: - targetNode = findNodeByLink(node_group, currentNode, currentNode.inputs[i]) - targetId = targetNode.inputs[0].default_value - # if i == 0 and targetNode.inputs[3].default_value == True: # Depth - if targetNode.inputs[3].is_linked: # Depth - db_node = findNodeByLink(node_group, targetNode, targetNode.inputs[3]) - db_id = db_node.inputs[0].default_value - stage.params.append('_' + db_id) - stage.params.append(constant_name + 'D') - stage.params.append(targetId) # Color buffer - stage.params.append(constant_name + str(i)) - - elif currentNode.bl_idname == 'TargetNodeType': - targetId = currentNode.inputs[0].default_value - stage.params.append(targetId) - stage.params.append(constant_name) - - elif currentNode.bl_idname == 'DepthBufferNodeType': - targetId = '_' + currentNode.inputs[0].default_value - stage.params.append(targetId) - stage.params.append(constant_name) +def make_bind_target(stage, node_group, node, constant_name, currentNode=None, target_index=1): + if currentNode == None: + currentNode = node + + stage.command = 'bind_target' + + link = findLink(node_group, currentNode, currentNode.inputs[target_index]) + currentNode = link.from_node + + if currentNode.bl_idname == 'NodeReroute': + make_bind_target(stage, node_group, node, constant_name, currentNode=currentNode, target_index=0) + + elif currentNode.bl_idname == 'GBufferNodeType': + for i in range(0, 5): + if currentNode.inputs[i].is_linked: + targetNode = findNodeByLink(node_group, currentNode, currentNode.inputs[i]) + targetId = targetNode.inputs[0].default_value + # if i == 0 and targetNode.inputs[3].default_value == True: # Depth + if targetNode.inputs[3].is_linked: # Depth + db_node = findNodeByLink(node_group, targetNode, targetNode.inputs[3]) + db_id = db_node.inputs[0].default_value + stage.params.append('_' + db_id) + stage.params.append(constant_name + 'D') + stage.params.append(targetId) # Color buffer + stage.params.append(constant_name + str(i)) + + elif currentNode.bl_idname == 'TargetNodeType': + targetId = currentNode.inputs[0].default_value + stage.params.append(targetId) + stage.params.append(constant_name) + + elif currentNode.bl_idname == 'DepthBufferNodeType': + targetId = '_' + currentNode.inputs[0].default_value + stage.params.append(targetId) + stage.params.append(constant_name) def make_draw_material_quad(stage, node_group, node, shader_references, asset_references, context_index=1): - stage.command = 'draw_material_quad' - material_context = node.inputs[context_index].default_value - stage.params.append(material_context) - # Include resource and shaders - shader_context = node.inputs[context_index].default_value - scon = shader_context.split('/') - dir_name = scon[2] - # No world defs for material passes - res_name = scon[2] - asset_references.append('compiled/ShaderResources/' + dir_name + '/' + res_name + '.json') - shader_references.append('compiled/Shaders/' + dir_name + '/' + res_name) + stage.command = 'draw_material_quad' + material_context = node.inputs[context_index].default_value + stage.params.append(material_context) + # Include resource and shaders + shader_context = node.inputs[context_index].default_value + scon = shader_context.split('/') + dir_name = scon[2] + # No world defs for material passes + res_name = scon[2] + asset_references.append('compiled/ShaderResources/' + dir_name + '/' + res_name + '.json') + shader_references.append('compiled/Shaders/' + dir_name + '/' + res_name) -def make_draw_quad(stage, node_group, node, shader_references, asset_references, context_index=1): - stage.command = 'draw_shader_quad' - # Append world defs to get proper context - world_defs = bpy.data.worlds[0].world_defs - shader_context = node.inputs[context_index].default_value - scon = shader_context.split('/') - stage.params.append(scon[0] + world_defs + '/' + scon[1] + world_defs + '/' + scon[2]) - # Include resource and shaders - dir_name = scon[0] - # Append world defs - res_name = scon[1] + world_defs - asset_references.append('compiled/ShaderResources/' + dir_name + '/' + res_name + '.json') - shader_references.append('compiled/Shaders/' + dir_name + '/' + res_name) +def make_draw_quad(stage, node_group, node, shader_references, asset_references, context_index=1, shader_context=None): + stage.command = 'draw_shader_quad' + # Append world defs to get proper context + world_defs = bpy.data.worlds[0].world_defs + if shader_context == None: + shader_context = node.inputs[context_index].default_value + scon = shader_context.split('/') + stage.params.append(scon[0] + world_defs + '/' + scon[1] + world_defs + '/' + scon[2]) + # Include resource and shaders + dir_name = scon[0] + # Append world defs + res_name = scon[1] + world_defs + asset_references.append('compiled/ShaderResources/' + dir_name + '/' + res_name + '.json') + shader_references.append('compiled/Shaders/' + dir_name + '/' + res_name) def make_draw_world(stage, node_group, node, shader_references, asset_references): - stage.command = 'draw_material_quad' - wname = bpy.data.worlds[0].name - stage.params.append(wname + '_material/' + wname + '_material/env_map') # Only one world for now + stage.command = 'draw_material_quad' + wname = bpy.data.worlds[0].name + stage.params.append(wname + '_material/' + wname + '_material/env_map') # Only one world for now def make_draw_compositor(stage, node_group, node, shader_references, asset_references): - scon = 'compositor_pass' - world_defs = bpy.data.worlds[0].world_defs - compositor_defs = nodes_compositor.parse_defs(bpy.data.scenes[0].node_tree) # Thrown in scene 0 for now - defs = world_defs + compositor_defs - res_name = scon + defs - - stage.command = 'draw_shader_quad' - stage.params.append(res_name + '/' + res_name + '/' + scon) - # Include resource and shaders - asset_references.append('compiled/ShaderResources/' + scon + '/' + res_name + '.json') - shader_references.append('compiled/Shaders/' + scon + '/' + res_name) + scon = 'compositor_pass' + world_defs = bpy.data.worlds[0].world_defs + compositor_defs = nodes_compositor.parse_defs(bpy.data.scenes[0].node_tree) # Thrown in scene 0 for now + defs = world_defs + compositor_defs + res_name = scon + defs + + stage.command = 'draw_shader_quad' + stage.params.append(res_name + '/' + res_name + '/' + scon) + # Include resource and shaders + asset_references.append('compiled/ShaderResources/' + scon + '/' + res_name + '.json') + shader_references.append('compiled/Shaders/' + scon + '/' + res_name) + # Link assets + buildNodeTrees.linked_assets.append(buildNodeTrees.assets_path + 'noise256.png') def make_call_function(stage, node_group, node): - stage.command = 'call_function' - stage.params.append(node.inputs[1].default_value) + stage.command = 'call_function' + stage.params.append(node.inputs[1].default_value) def make_branch_function(stage, node_group, node): - make_call_function(stage, node_group, node) - -def process_call_function(stage, stages, node, node_group, last_bind_target, shader_references, asset_references): - # Step till merge node - stage.returns_true = [] - if node.outputs[0].is_linked: - stageNode = findNodeByLinkFrom(node_group, node, node.outputs[0]) - buildNode(stage.returns_true, stageNode, node_group, last_bind_target, shader_references, asset_references) - - stage.returns_false = [] - if node.outputs[1].is_linked: - stageNode = findNodeByLinkFrom(node_group, node, node.outputs[1]) - margeNode = buildNode(stage.returns_false, stageNode, node_group, last_bind_target, shader_references, asset_references) - - # Continue using top level stages after merge node - afterMergeNode = findNodeByLinkFrom(node_group, margeNode, margeNode.outputs[0]) - buildNode(stages, afterMergeNode, node_group, last_bind_target, shader_references, asset_references) + make_call_function(stage, node_group, node) + +def process_call_function(stage, stages, node, node_group, shader_references, asset_references): + # Step till merge node + stage.returns_true = [] + if node.outputs[0].is_linked: + stageNode = findNodeByLinkFrom(node_group, node, node.outputs[0]) + buildNode(stage.returns_true, stageNode, node_group, shader_references, asset_references) + + stage.returns_false = [] + if node.outputs[1].is_linked: + stageNode = findNodeByLinkFrom(node_group, node, node.outputs[1]) + margeNode = buildNode(stage.returns_false, stageNode, node_group, shader_references, asset_references) + + # Continue using top level stages after merge node + afterMergeNode = findNodeByLinkFrom(node_group, margeNode, margeNode.outputs[0]) + buildNode(stages, afterMergeNode, node_group, shader_references, asset_references) + +def make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[3, 5, 7], bind_target_constants=None, shader_context=None): + # Set target + if target_index != None and node.inputs[target_index].is_linked: + stage = Object() + stage.params = [] + make_set_target(stage, node_group, node, target_index=target_index) + stages.append(stage) + # Bind targets + stage = Object() + stage.params = [] + buildNode.last_bind_target = stage + bind_target_used = False + for i in range(0, len(bind_target_indices)): + index = bind_target_indices[i] + if node.inputs[index].is_linked: + bind_target_used = True + if bind_target_constants == None: + constant_name = node.inputs[index + 1].default_value + else: + constant_name = bind_target_constants[i] + make_bind_target(stage, node_group, node, constant_name, target_index=index) + if bind_target_used: + stages.append(stage) + stage = Object() + stage.params = [] + # Draw quad + make_draw_quad(stage, node_group, node, shader_references, asset_references, context_index=2, shader_context=shader_context) + stages.append(stage) + +def make_ssao_pass(stages, node_group, node, shader_references, asset_references): + make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[3, 4], bind_target_constants=['gbufferD', 'gbuffer0'], shader_context='ssao_pass/ssao_pass/ssao_pass') + make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[2, 4], bind_target_constants=['tex', 'gbuffer0'], shader_context='blur_edge_pass/blur_edge_pass/blur_edge_pass_x') + make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[2, 4], bind_target_constants=['tex', 'gbuffer0'], shader_context='blur_edge_pass/blur_edge_pass/blur_edge_pass_y') + buildNodeTrees.linked_assets.append(buildNodeTrees.assets_path + 'noise8.png') + +def make_ssr_pass(stages, node_group, node, shader_references, asset_references): + make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=2, bind_target_indices=[4, 5], bind_target_constants=['tex', 'gbuffer'], shader_context='ssr_pass/ssr_pass/ssr_pass') + make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=3, bind_target_indices=[2, 6], bind_target_constants=['tex', 'gbuffer1'], shader_context='blur_adaptive_pass/blur_adaptive_pass/blur_adaptive_pass_x') + make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=2, bind_target_indices=[3, 6], bind_target_constants=['tex', 'gbuffer1'], shader_context='blur_adaptive_pass/blur_adaptive_pass/blur_adaptive_pass_y2') + make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[4, 2], bind_target_constants=['tex', 'tex2'], shader_context='combine_pass/combine_pass/combine_pass') + +def make_bloom_pass(stages, node_group, node, shader_references, asset_references): + make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=2, bind_target_indices=[4], bind_target_constants=['tex'], shader_context='bloom_pass/bloom_pass/bloom_pass') + make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=3, bind_target_indices=[2], bind_target_constants=['tex'], shader_context='blur_gaus_pass/blur_gaus_pass/blur_gaus_pass_x') + make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=2, bind_target_indices=[3], bind_target_constants=['tex'], shader_context='blur_gaus_pass/blur_gaus_pass/blur_gaus_pass_y') + make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[4, 2], bind_target_constants=['tex', 'tex2'], shader_context='combine_pass/combine_pass/combine_pass') + +def make_motion_blur_pass(stages, node_group, node, shader_references, asset_references): + make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[2, 3, 4], bind_target_constants=['tex', 'gbufferD', 'gbuffer0'], shader_context='motion_blur_pass/motion_blur_pass/motion_blur_pass') + +def make_fxaa_pass(stages, node_group, node, shader_references, asset_references): + make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[2], bind_target_constants=['tex'], shader_context='fxaa_pass/fxaa_pass/fxaa_pass') + +def make_smaa_pass(stages, node_group, node, shader_references, asset_references): + stage = Object() + stage.params = [] + make_set_target(stage, node_group, node, target_index=2) + stages.append(stage) + + stage = Object() + stage.params = [] + make_clear_target(stage, color_val=[0.0, 0.0, 0.0, 0.0]) + stages.append(stage) + + make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=None, bind_target_indices=[4], bind_target_constants=['colorTex'], shader_context='smaa_edge_detect/smaa_edge_detect/smaa_edge_detect') + + stage = Object() + stage.params = [] + make_set_target(stage, node_group, node, target_index=3) + stages.append(stage) + + stage = Object() + stage.params = [] + make_clear_target(stage, color_val=[0.0, 0.0, 0.0, 0.0]) + stages.append(stage) + + make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=None, bind_target_indices=[2], bind_target_constants=['edgesTex'], shader_context='smaa_blend_weight/smaa_blend_weight/smaa_blend_weight') + make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[4, 3], bind_target_constants=['colorTex', 'blendTex'], shader_context='smaa_neighborhood_blend/smaa_neighborhood_blend/smaa_neighborhood_blend') + buildNodeTrees.linked_assets.append(buildNodeTrees.assets_path + 'smaa_area.png') + buildNodeTrees.linked_assets.append(buildNodeTrees.assets_path + 'smaa_search.png') + +def make_sss_pass(stages, node_group, node, shader_references, asset_references): + make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[3, 4, 5], bind_target_constants=['tex', 'gbufferD', 'gbuffer0'], shader_context='sss_pass/sss_pass/sss_pass_x') + make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=2, bind_target_indices=[3, 4, 5], bind_target_constants=['tex', 'gbufferD', 'gbuffer0'], shader_context='sss_pass/sss_pass/sss_pass_y') + +def make_water_pass(stages, node_group, node, shader_references, asset_references): + make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[2, 3, 4], bind_target_constants=['tex', 'gbufferD', 'gbuffer0'], shader_context='water_pass/water_pass/water_pass') + +def make_deferred_light_pass(stages, node_group, node, shader_references, asset_references): + make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[2, 3, 4], bind_target_constants=['gbuffer', 'ssaotex', 'shadowMap'], shader_context='deferred_light/deferred_light/deferred_light') + +def make_translucent_resolve_pass(stages, node_group, node, shader_references, asset_references): + make_quad_pass(stages, node_group, node, shader_references, asset_references, target_index=1, bind_target_indices=[2], bind_target_constants=['gbuffer'], shader_context='translucent_resolve/translucent_resolve/translucent_resolve') # Returns merge node -def buildNode(stages, node, node_group, last_bind_target, shader_references, asset_references): - stage = Object() - stage.params = [] - - append_stage = True - - if node.bl_idname == 'MergeStagesNodeType': - return node - - elif node.bl_idname == 'SetTargetNodeType': - last_bind_target = None - make_set_target(stage, node_group, node) +def buildNode(stages, node, node_group, shader_references, asset_references): + stage = Object() + stage.params = [] + + append_stage = True + + if node.bl_idname == 'MergeStagesNodeType': + return node + + elif node.bl_idname == 'SetTargetNodeType': + buildNode.last_bind_target = None + make_set_target(stage, node_group, node) + + elif node.bl_idname == 'ClearTargetNodeType': + color_val = None + depth_val = None + stencil_val = None + if node.inputs[1].default_value == True: + if node.inputs[2].is_linked: # Assume background color node is linked + color_val = bpy.data.cameras[0].world_envtex_color + else: + color_val = node.inputs[2].default_value + if node.inputs[3].default_value == True: + depth_val = node.inputs[4].default_value + if node.inputs[5].default_value == True: + stencil_val = node.inputs[6].default_value + make_clear_target(stage, color_val=color_val, depth_val=depth_val, stencil_val=stencil_val) + + elif node.bl_idname == 'DrawGeometryNodeType': + make_draw_geometry(stage, node_group, node) + + elif node.bl_idname == 'DrawDecalsNodeType': + make_draw_decals(stage, node_group, node, shader_references, asset_references) + + elif node.bl_idname == 'BindTargetNodeType': + if buildNode.last_bind_target is not None: + stage = buildNode.last_bind_target + append_stage = False + buildNode.last_bind_target = stage + constant_name = node.inputs[2].default_value + make_bind_target(stage, node_group, node, constant_name) + + elif node.bl_idname == 'DrawMaterialQuadNodeType': + make_draw_material_quad(stage, node_group, node, shader_references, asset_references) + + elif node.bl_idname == 'DrawQuadNodeType': + make_draw_quad(stage, node_group, node, shader_references, asset_references) + + elif node.bl_idname == 'DrawWorldNodeType': + # Bind depth + if node.inputs[1].is_linked: + stage = Object() + stage.params = [] + buildNode.last_bind_target = stage + if node.inputs[1].is_linked: + make_bind_target(stage, node_group, node, target_index=1, constant_name='gbufferD') + stages.append(stage) + # Draw quad + stage = Object() + stage.params = [] + make_draw_world(stage, node_group, node, shader_references, asset_references) + + elif node.bl_idname == 'DrawCompositorNodeType': + # Set target + if node.inputs[1].is_linked: + make_set_target(stage, node_group, node) + stages.append(stage) + # Bind targets + if node.inputs[2].is_linked or node.inputs[3].is_linked: + stage = Object() + stage.params = [] + buildNode.last_bind_target = stage + if node.inputs[2].is_linked: + make_bind_target(stage, node_group, node, target_index=2, constant_name='tex') + if node.inputs[3].is_linked: + make_bind_target(stage, node_group, node, target_index=3, constant_name='gbuffer') + stages.append(stage) + # Draw quad + stage = Object() + stage.params = [] + make_draw_compositor(stage, node_group, node, shader_references, asset_references) + + elif node.bl_idname == 'BranchFunctionNodeType': + make_branch_function(stage, node_group, node) + stages.append(stage) + process_call_function(stage, stages, node, node_group, shader_references, asset_references) + return + + elif node.bl_idname == 'LoopStagesNodeType': + # Just repeats the commands + append_stage = False + if node.outputs[1].is_linked: + count = node.inputs[2].default_value + for i in range(0, count): + loopNode = findNodeByLinkFrom(node_group, node, node.outputs[1]) + buildNode(stages, loopNode, node_group, shader_references, asset_references) + + elif node.bl_idname == 'LoopLightsNodeType': + append_stage = False + stage.command = 'loop_lights' + stages.append(stage) + stage.returns_true = [] + if node.outputs[1].is_linked: + loopNode = findNodeByLinkFrom(node_group, node, node.outputs[1]) + buildNode(stage.returns_true, loopNode, node_group, shader_references, asset_references) + + elif node.bl_idname == 'CallFunctionNodeType': + make_call_function(stage, node_group, node) + + elif node.bl_idname == 'QuadPassNodeType': + make_quad_pass(stages, node_group, node, shader_references, asset_references) + append_stage = False + + elif node.bl_idname == 'SSAOPassNodeType': + make_ssao_pass(stages, node_group, node, shader_references, asset_references) + append_stage = False + elif node.bl_idname == 'SSRPassNodeType': + make_ssr_pass(stages, node_group, node, shader_references, asset_references) + append_stage = False + elif node.bl_idname == 'BloomPassNodeType': + make_bloom_pass(stages, node_group, node, shader_references, asset_references) + append_stage = False + elif node.bl_idname == 'MotionBlurPassNodeType': + make_motion_blur_pass(stages, node_group, node, shader_references, asset_references) + append_stage = False + elif node.bl_idname == 'FXAAPassNodeType': + make_fxaa_pass(stages, node_group, node, shader_references, asset_references) + append_stage = False + elif node.bl_idname == 'SMAAPassNodeType': + make_smaa_pass(stages, node_group, node, shader_references, asset_references) + append_stage = False + elif node.bl_idname == 'SSSPassNodeType': + make_sss_pass(stages, node_group, node, shader_references, asset_references) + append_stage = False + elif node.bl_idname == 'WaterPassNodeType': + make_water_pass(stages, node_group, node, shader_references, asset_references) + append_stage = False + elif node.bl_idname == 'DeferredLightPassNodeType': + make_deferred_light_pass(stages, node_group, node, shader_references, asset_references) + append_stage = False + elif node.bl_idname == 'TranslucentResolvePassNodeType': + make_translucent_resolve_pass(stages, node_group, node, shader_references, asset_references) + append_stage = False + + if append_stage: + stages.append(stage) + + # Build next stage + if node.outputs[0].is_linked: + stageNode = findNodeByLinkFrom(node_group, node, node.outputs[0]) + buildNode(stages, stageNode, node_group, shader_references, asset_references) +# Used to merge bind target nodes into one stage +buildNode.last_bind_target = None +# Used to determine shadowmap size +buildNode.last_set_target_w = 0 +buildNode.last_set_target_h = 0 + - elif node.bl_idname == 'ClearTargetNodeType': - make_clear_target(stage, node_group, node) - - elif node.bl_idname == 'DrawGeometryNodeType': - make_draw_geometry(stage, node_group, node) - - elif node.bl_idname == 'DrawDecalsNodeType': - make_draw_decals(stage, node_group, node, shader_references, asset_references) - - elif node.bl_idname == 'BindTargetNodeType': - if last_bind_target is not None: - stage = last_bind_target - append_stage = False - last_bind_target = stage - make_bind_target(stage, node_group, node) - - elif node.bl_idname == 'DrawMaterialQuadNodeType': - make_draw_material_quad(stage, node_group, node, shader_references, asset_references) - - elif node.bl_idname == 'DrawQuadNodeType': - make_draw_quad(stage, node_group, node, shader_references, asset_references) - - elif node.bl_idname == 'DrawWorldNodeType': - # Bind depth - if node.inputs[1].is_linked: - stage = Object() - stage.params = [] - last_bind_target = stage - if node.inputs[1].is_linked: - make_bind_target(stage, node_group, node, target_index=1, constant_name='gbufferD') - stages.append(stage) - # Draw quad - stage = Object() - stage.params = [] - make_draw_world(stage, node_group, node, shader_references, asset_references) - - elif node.bl_idname == 'DrawCompositorNodeType': - # Set target - if node.inputs[1].is_linked: - make_set_target(stage, node_group, node) - stages.append(stage) - # Bind targets - if node.inputs[2].is_linked or node.inputs[3].is_linked: - stage = Object() - stage.params = [] - last_bind_target = stage - if node.inputs[2].is_linked: - make_bind_target(stage, node_group, node, target_index=2, constant_name='tex') - if node.inputs[3].is_linked: - make_bind_target(stage, node_group, node, target_index=3, constant_name='gbuffer') - stages.append(stage) - # Draw quad - stage = Object() - stage.params = [] - make_draw_compositor(stage, node_group, node, shader_references, asset_references) - - elif node.bl_idname == 'BranchFunctionNodeType': - make_branch_function(stage, node_group, node) - stages.append(stage) - process_call_function(stage, stages, node, node_group, last_bind_target, shader_references, asset_references) - return - - elif node.bl_idname == 'LoopStagesNodeType': - # Just repeats the commands - append_stage = False - if node.outputs[1].is_linked: - count = node.inputs[2].default_value - for i in range(0, count): - loopNode = findNodeByLinkFrom(node_group, node, node.outputs[1]) - buildNode(stages, loopNode, node_group, last_bind_target, shader_references, asset_references) - - elif node.bl_idname == 'LoopLightsNodeType': - append_stage = False - stage.command = 'loop_lights' - stages.append(stage) - stage.returns_true = [] - if node.outputs[1].is_linked: - loopNode = findNodeByLinkFrom(node_group, node, node.outputs[1]) - buildNode(stage.returns_true, loopNode, node_group, last_bind_target, shader_references, asset_references) - - elif node.bl_idname == 'CallFunctionNodeType': - make_call_function(stage, node_group, node) - - elif node.bl_idname == 'QuadPassNodeType': - # Set target - if node.inputs[1].is_linked: - make_set_target(stage, node_group, node) - stages.append(stage) - # Bind targets - stage = Object() - stage.params = [] - last_bind_target = stage - bind_target_used = False - for i in [3, 5, 7]: - if node.inputs[i].is_linked: - bind_target_used = True - make_bind_target(stage, node_group, node, target_index=i, constant_index=(i + 1)) - if bind_target_used: - stages.append(stage) - stage = Object() - stage.params = [] - # Draw quad - make_draw_quad(stage, node_group, node, shader_references, asset_references, context_index=2) - - if append_stage: - stages.append(stage) - - # Build next stage - if node.outputs[0].is_linked: - stageNode = findNodeByLinkFrom(node_group, node, node.outputs[0]) - buildNode(stages, stageNode, node_group, last_bind_target, shader_references, asset_references) - def findNodeByLink(node_group, to_node, inp): - for link in node_group.links: - if link.to_node == to_node and link.to_socket == inp: - return link.from_node + for link in node_group.links: + if link.to_node == to_node and link.to_socket == inp: + return link.from_node def findLink(node_group, to_node, inp): - for link in node_group.links: - if link.to_node == to_node and link.to_socket == inp: - return link - + for link in node_group.links: + if link.to_node == to_node and link.to_socket == inp: + return link + def findNodeByLinkFrom(node_group, from_node, outp): - for link in node_group.links: - if link.from_node == from_node and link.from_socket == outp: - return link.to_node - + for link in node_group.links: + if link.from_node == from_node and link.from_socket == outp: + return link.to_node + def getRootNode(node_group): - # Find first node linked to begin node - for n in node_group.nodes: - if n.bl_idname == 'BeginNodeType': - return findNodeByLinkFrom(node_group, n, n.outputs[0]) + # Find first node linked to begin node + for n in node_group.nodes: + if n.bl_idname == 'BeginNodeType': + # Store contexts + bpy.data.cameras[0].pipeline_id = n.inputs[0].default_value + bpy.data.cameras[0].geometry_context = n.inputs[1].default_value + bpy.data.cameras[0].shadows_context = n.inputs[2].default_value + bpy.data.cameras[0].translucent_context = n.inputs[3].default_value + return findNodeByLinkFrom(node_group, n, n.outputs[0]) def get_render_targets(root_node, node_group): - render_targets = [] - depth_buffers = [] - traverse_for_rt(root_node, node_group, render_targets, depth_buffers) - return render_targets, depth_buffers - + render_targets = [] + depth_buffers = [] + traverse_for_rt(root_node, node_group, render_targets, depth_buffers) + return render_targets, depth_buffers + def traverse_for_rt(node, node_group, render_targets, depth_buffers): - # Collect render targets - if node.bl_idname == 'SetTargetNodeType' or node.bl_idname == 'QuadPassNodeType': - if node.inputs[1].is_linked: - tnode = findNodeByLink(node_group, node, node.inputs[1]) - parse_render_target(tnode, node_group, render_targets, depth_buffers) - - # Traverse loops - if node.bl_idname == 'LoopStagesNodeType' or node.bl_idname == 'LoopLightsNodeType': - if node.outputs[1].is_linked: - loop_node = findNodeByLinkFrom(node_group, node, node.outputs[1]) - traverse_for_rt(loop_node, node_group, render_targets, depth_buffers) - - # Next stage - if node.outputs[0].is_linked: - stagenode = findNodeByLinkFrom(node_group, node, node.outputs[0]) - traverse_for_rt(stagenode, node_group, render_targets, depth_buffers) - + # Collect render targets + if node.bl_idname == 'SetTargetNodeType' or node.bl_idname == 'QuadPassNodeType': + if node.inputs[1].is_linked: + tnode = findNodeByLink(node_group, node, node.inputs[1]) + parse_render_target(tnode, node_group, render_targets, depth_buffers) + + # Traverse loops + elif node.bl_idname == 'LoopStagesNodeType' or node.bl_idname == 'LoopLightsNodeType': + if node.outputs[1].is_linked: + loop_node = findNodeByLinkFrom(node_group, node, node.outputs[1]) + traverse_for_rt(loop_node, node_group, render_targets, depth_buffers) + + # Prebuilt + elif node.bl_idname == 'MotionBlurPassNodeType' or node.bl_idname == 'FXAAPassNodeType' or node.bl_idname == 'WaterPassNodeType' or node.bl_idname == 'DeferredLightPassNodeType' or node.bl_idname == 'TranslucentResolvePassNodeType': + if node.inputs[1].is_linked: + tnode = findNodeByLink(node_group, node, node.inputs[1]) + parse_render_target(tnode, node_group, render_targets, depth_buffers) + elif node.bl_idname == 'SSRPassNodeType' or node.bl_idname == 'BloomPassNodeType' or node.bl_idname == 'SMAAPassNodeType': + for i in range(1, 4): + if node.inputs[i].is_linked: + tnode = findNodeByLink(node_group, node, node.inputs[i]) + parse_render_target(tnode, node_group, render_targets, depth_buffers) + elif node.bl_idname == 'SSAOPassNodeType' or node.bl_idname == 'SSSPassNodeType': + for i in range(1, 3): + if node.inputs[i].is_linked: + tnode = findNodeByLink(node_group, node, node.inputs[i]) + parse_render_target(tnode, node_group, render_targets, depth_buffers) + + # Next stage + if node.outputs[0].is_linked: + stagenode = findNodeByLinkFrom(node_group, node, node.outputs[0]) + traverse_for_rt(stagenode, node_group, render_targets, depth_buffers) + def parse_render_target(node, node_group, render_targets, depth_buffers): - if node.bl_idname == 'NodeReroute': - tnode = findNodeByLink(node_group, node, node.inputs[0]) - parse_render_target(tnode, node_group, render_targets, depth_buffers) - - elif node.bl_idname == 'TargetNodeType': - # Target already exists - id = node.inputs[0].default_value - for t in render_targets: - if t.id == id: - return - - depth_buffer_id = None - if node.inputs[3].is_linked: - # Find depth buffer - depth_node = findNodeByLink(node_group, node, node.inputs[3]) - depth_buffer_id = depth_node.inputs[0].default_value - # Append depth buffer - found = False - for db in depth_buffers: - if db.id == depth_buffer_id: - found = True - break - if found == False: - db = Object() - db.id = depth_buffer_id - db.stencil_buffer = depth_node.inputs[1].default_value - depth_buffers.append(db) - # Get scale - scale = 1.0 - if node.inputs[1].is_linked: - size_node = findNodeByLink(node_group, node, node.inputs[1]) - scale = size_node.inputs[0].default_value - - # Append target - target = make_render_target(node, scale, depth_buffer_id=depth_buffer_id) - render_targets.append(target) - - elif node.bl_idname == 'GBufferNodeType': - for i in range(0, 5): - if node.inputs[i].is_linked: - n = findNodeByLink(node_group, node, node.inputs[i]) - parse_render_target(n, node_group, render_targets, depth_buffers) + if node.bl_idname == 'NodeReroute': + tnode = findNodeByLink(node_group, node, node.inputs[0]) + parse_render_target(tnode, node_group, render_targets, depth_buffers) + + elif node.bl_idname == 'TargetNodeType': + # Target already exists + id = node.inputs[0].default_value + for t in render_targets: + if t.id == id: + return + + depth_buffer_id = None + if node.inputs[3].is_linked: + # Find depth buffer + depth_node = findNodeByLink(node_group, node, node.inputs[3]) + depth_buffer_id = depth_node.inputs[0].default_value + # Append depth buffer + found = False + for db in depth_buffers: + if db.id == depth_buffer_id: + found = True + break + if found == False: + db = Object() + db.id = depth_buffer_id + db.stencil_buffer = depth_node.inputs[1].default_value + depth_buffers.append(db) + # Get scale + scale = 1.0 + if node.inputs[1].is_linked: + size_node = findNodeByLink(node_group, node, node.inputs[1]) + scale = size_node.inputs[0].default_value + + # Append target + target = make_render_target(node, scale, depth_buffer_id=depth_buffer_id) + render_targets.append(target) + + elif node.bl_idname == 'GBufferNodeType': + for i in range(0, 5): + if node.inputs[i].is_linked: + n = findNodeByLink(node_group, node, node.inputs[i]) + parse_render_target(n, node_group, render_targets, depth_buffers) def make_render_target(n, scale, depth_buffer_id=None): - target = Object() - target.id = n.inputs[0].default_value - target.width = n.inputs[1].default_value - target.height = n.inputs[2].default_value - target.format = n.inputs[4].default_value - target.ping_pong = n.inputs[5].default_value - if scale != 1.0: - target.scale = scale - if depth_buffer_id != None: - target.depth_buffer = depth_buffer_id - return target - \ No newline at end of file + target = Object() + target.id = n.inputs[0].default_value + target.width = n.inputs[1].default_value + target.height = n.inputs[2].default_value + target.format = n.inputs[4].default_value + target.ping_pong = n.inputs[5].default_value + if scale != 1.0: + target.scale = scale + if depth_buffer_id != None: + target.depth_buffer = depth_buffer_id + return target + \ No newline at end of file diff --git a/blender/nodes_world.py b/blender/nodes_world.py index ad90cc26..77f71274 100755 --- a/blender/nodes_world.py +++ b/blender/nodes_world.py @@ -67,6 +67,11 @@ def buildNodeTree(world_name, node_group, shader_references, asset_references): if output_node != None: parse_world_output(node_group, output_node, context) + # Clear to color if no texture or sky is provided + world_defs = bpy.data.worlds[0].world_defs + if '_EnvSky' not in world_defs and '_EnvTex' not in world_defs: + world_defs += '_EnvCol' + # Enable probes for cam in bpy.data.cameras: if cam.is_probe: @@ -75,7 +80,6 @@ def buildNodeTree(world_name, node_group, shader_references, asset_references): # Add resources to khafie dir_name = 'env_map' # Append world defs - world_defs = bpy.data.worlds[0].world_defs res_name = 'env_map' + world_defs # Reference correct shader context res.shader = res_name + '/' + res_name @@ -94,6 +98,7 @@ def parse_world_output(node_group, node, context): def parse_surface(node_group, node, context): # Extract environment strength if node.type == 'BACKGROUND': + bpy.data.cameras[0].world_envtex_color = node.inputs[0].default_value bpy.data.cameras[0].world_envtex_strength = node.inputs[1].default_value # Strength diff --git a/blender/project.py b/blender/project.py index 942606df..dfa5db99 100755 --- a/blender/project.py +++ b/blender/project.py @@ -6,8 +6,7 @@ import platform import json from bpy.props import * import subprocess -from subprocess import call -import atexit +import threading import webbrowser import write_data import nodes_logic @@ -20,18 +19,48 @@ import lib.make_variants import utils def init_armory_props(): - if not 'CGVersion' in bpy.data.worlds[0]: - wrd = bpy.data.worlds[0] + # First run + wrd = bpy.data.worlds[0] + if wrd.CGVersion == '': + wrd.use_fake_user = True # Store data in worlds[0], add fake user to keep it alive + wrd.CGVersion = '16.7' + wrd.CGProjectTarget = 'HTML5' + # Take blend file name + wrd.CGProjectName = bpy.path.basename(bpy.context.blend_data.filepath).rsplit('.')[0] + wrd.CGProjectPackage = 'game' + wrd.CGProjectWidth = 800 + wrd.CGProjectHeight = 600 wrd.CGProjectScene = bpy.data.scenes[0].name + wrd.CGProjectSamplesPerPixel = 1 + wrd.CGPhysics = 'Bullet' + wrd.CGKhafileConfig = '' + wrd.CGMinimize = True + wrd.CGCacheShaders = True + wrd.CGPlayViewportCamera = False + wrd.CGPlayConsole = False + wrd.CGPlayDeveloperTools = False # Switch to Cycles if bpy.data.scenes[0].render.engine == 'BLENDER_RENDER': for scene in bpy.data.scenes: scene.render.engine = 'CYCLES' + # Use nodes + for w in bpy.data.worlds: + w.use_nodes = True + for s in bpy.data.scenes: + s.use_nodes = True + for l in bpy.data.lamps: + l.use_nodes = True + for m in bpy.data.materials: + m.use_nodes = True + utils.fetch_script_names() # Play button in 3D View panel def draw_play_item(self, context): layout = self.layout - layout.operator("arm.play") + if play_project.playproc == None: + layout.operator("arm.play_in_frame") + else: + layout.operator("arm.stop") # Menu in render region class ArmoryProjectPanel(bpy.types.Panel): @@ -74,6 +103,20 @@ class ArmoryBuildPanel(bpy.types.Panel): layout.prop(wrd, 'CGCacheShaders') layout.label('Armory v' + wrd.CGVersion) +class ArmoryPlayPanel(bpy.types.Panel): + bl_label = "Armory Play" + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "render" + + def draw(self, context): + layout = self.layout + wrd = bpy.data.worlds[0] + layout.operator("arm.play") + layout.prop(wrd, 'CGPlayViewportCamera') + layout.prop(wrd, 'CGPlayConsole') + layout.prop(wrd, 'CGPlayDeveloperTools') + def get_export_scene_override(scene): # None for now override = { @@ -87,8 +130,9 @@ def get_export_scene_override(scene): def compile_shader(raw_path, shader_name, defs): os.chdir(raw_path + './' + shader_name) - lib.make_resources.make(shader_name + '.shader.json', minimize=bpy.data.worlds[0].CGMinimize, defs=defs) - lib.make_variants.make(shader_name + '.shader.json', defs) + fp = os.path.relpath(utils.get_fp()) + lib.make_resources.make(shader_name + '.shader.json', fp, minimize=bpy.data.worlds[0].CGMinimize, defs=defs) + lib.make_variants.make(shader_name + '.shader.json', fp, defs) def def_strings_to_array(strdefs): defs = strdefs.split('_') @@ -96,7 +140,10 @@ def def_strings_to_array(strdefs): defs = ['_' + d for d in defs] # Restore _ return defs -def export_game_data(fp, raw_path): +def export_game_data(fp, sdk_path): + raw_path = sdk_path + 'armory/raw/' + assets_path = sdk_path + 'armory/Assets/' + shader_references = [] asset_references = [] @@ -104,7 +151,7 @@ def export_game_data(fp, raw_path): # TODO: cache nodes_logic.buildNodeTrees() nodes_world.buildNodeTrees(shader_references, asset_references) # TODO: Have to build nodes everytime to collect env map resources - nodes_pipeline.buildNodeTrees(shader_references, asset_references) + linked_assets = nodes_pipeline.buildNodeTrees(shader_references, asset_references, assets_path) # TODO: Set armatures to center of world so skin transform is zero armatures = [] @@ -135,12 +182,6 @@ def export_game_data(fp, raw_path): a.armature.location.y = a.y a.armature.location.z = a.z - # Write khafile.js - write_data.write_khafilejs(shader_references, asset_references) - - # Write Main.hx - write_data.write_main() - # Clean compiled variants if cache is disabled if bpy.data.worlds[0].CGCacheShaders == False: if os.path.isdir("compiled"): @@ -161,6 +202,46 @@ def export_game_data(fp, raw_path): else: defs = [] compile_shader(raw_path, shader_name, defs) + # Add linked assets from shader resources + asset_references += linked_assets + # Reset path + os.chdir(fp) + + # Write compiled.glsl + clip_start = bpy.data.cameras[0].clip_start # Same clip values for all cameras for now + clip_end = bpy.data.cameras[0].clip_end + shadowmap_size = bpy.data.worlds[0].shadowmap_size + write_data.write_compiledglsl(clip_start, clip_end, shadowmap_size) + + # Write khafile.js + write_data.write_khafilejs(shader_references, asset_references) + + # Write Main.hx + write_data.write_main() + +def compile_project(self, target_index=None): + user_preferences = bpy.context.user_preferences + addon_prefs = user_preferences.addons['armory'].preferences + sdk_path = addon_prefs.sdk_path + + # Set build command + if target_index == None: + target_index = bpy.data.worlds[0]['CGProjectTarget'] + targets = ['html5', 'windows', 'osx', 'linux', 'ios', 'android-native'] + + # Copy ammo.js if necessary + if target_index == 0 and bpy.data.worlds[0].CGPhysics == 'Bullet': + ammojs_path = sdk_path + '/haxebullet/js/ammo/ammo.js' + if not os.path.isfile('build/html5/ammo.js'): + shutil.copy(ammojs_path, 'build/html5') + if not os.path.isfile('build/debug-html5/ammo.js'): + shutil.copy(ammojs_path, 'build/debug-html5') + + node_path = sdk_path + '/nodejs/node-osx' + khamake_path = sdk_path + '/KodeStudio/KodeStudio.app/Contents/Resources/app/extensions/kha/Kha/make' + cmd = [node_path, khamake_path, targets[target_index]] + self.report({'INFO'}, "Building, see console...") + return subprocess.Popen(cmd) def build_project(self): # Save blend @@ -170,7 +251,6 @@ def build_project(self): user_preferences = bpy.context.user_preferences addon_prefs = user_preferences.addons['armory'].preferences sdk_path = addon_prefs.sdk_path - scripts_path = sdk_path + '/armory/blender/' raw_path = sdk_path + '/armory/raw/' # Set dir @@ -186,82 +266,145 @@ def build_project(self): os.makedirs('Sources') if not os.path.exists('Assets'): os.makedirs('Assets') - + if not os.path.isdir('build/html5'): + os.makedirs('build/html5') + if not os.path.isdir('build/debug-html5'): + os.makedirs('build/debug-html5') + # Compile path tracer shaders if len(bpy.data.cameras) > 0 and bpy.data.cameras[0].pipeline_path == 'pathtrace_pipeline': path_tracer.compile(raw_path + 'pt_trace_pass/pt_trace_pass.frag.glsl') # Export data - export_game_data(fp, raw_path) - - # Set build command - target_index = bpy.data.worlds[0]['CGProjectTarget'] - targets = ['html5', 'windows', 'osx', 'linux', 'ios', 'android-native'] + export_game_data(fp, sdk_path) - # Copy ammo.js if necessary - # if target_index == '0': - # if not os.path.isfile('build/html5/ammo.js'): - # ammojs_path = utils.get_fp() + '/../haxebullet/js/ammo/ammo.js' - # shutil.copy(ammojs_path, 'build/html5') +def stop_project(self): + if play_project.playproc != None: + play_project.playproc.terminate() + play_project.playproc = None - node_path = sdk_path + '/nodejs/node-osx' - khamake_path = sdk_path + '/KodeStudio.app/Contents/Resources/app/extensions/kha/Kha/make' - os.system(node_path + ' ' + khamake_path + ' ' + targets[target_index] + ' &') - - self.report({'INFO'}, "Building, see console...") +def watch_play(): + if play_project.playproc == None: + return + if play_project.playproc.poll() == None: + threading.Timer(0.5, watch_play).start() + else: + play_project.playproc = None -def play_project(self): - pass +def watch_compile(): + if play_project.compileproc.poll() == None: + threading.Timer(0.1, watch_compile).start() + else: + play_project.compileproc = None + on_compiled() + +def play_project(self, in_frame): + # Build data + build_project(self) + + if in_frame == False: + # Windowed player + wrd = bpy.data.worlds[0] + x = 0 + y = 0 + w = wrd.CGProjectWidth + h = wrd.CGProjectHeight + winoff = 0 + else: + # Player dimensions + psize = bpy.context.user_preferences.system.pixel_size + x = bpy.context.window.x + (bpy.context.area.x - 5) / psize + y = bpy.context.window.height + 45 - (bpy.context.area.y + bpy.context.area.height) / psize + w = (bpy.context.area.width + 5) / psize + h = (bpy.context.area.height) / psize - 25 + winoff = bpy.context.window.y + bpy.context.window.height + winoff += 22 # Header + + write_data.write_electronjs(x, y, w, h, winoff, in_frame) + write_data.write_indexhtml(w, h, in_frame) + + # Compile + play_project.compileproc = compile_project(self, target_index=0) + watch_compile() + +def on_compiled(): + user_preferences = bpy.context.user_preferences + addon_prefs = user_preferences.addons['armory'].preferences + sdk_path = addon_prefs.sdk_path + electron_path = sdk_path + 'KodeStudio/KodeStudio.app/Contents/MacOS/Electron' + electron_app_path = './build/electron.js' + + play_project.playproc = subprocess.Popen([electron_path, electron_app_path]) + watch_play() +play_project.playproc = None +play_project.compileproc = None def clean_project(self): os.chdir(utils.get_fp()) # Remove build data - if os.path.isdir("build"): + if os.path.isdir('build'): shutil.rmtree('build') # Remove generated data - if os.path.isdir("Assets/generated"): + if os.path.isdir('Assets/generated'): shutil.rmtree('Assets/generated') # Remove generated shader variants - if os.path.isdir("compiled"): + if os.path.isdir('compiled'): shutil.rmtree('compiled') # Remove compiled nodes - nodes_path = "Sources/" + bpy.data.worlds[0].CGProjectPackage.replace(".", "/") + "/node/" + nodes_path = 'Sources/' + bpy.data.worlds[0].CGProjectPackage.replace('.', '/') + '/node/' if os.path.isdir(nodes_path): shutil.rmtree(nodes_path) - self.report({'INFO'}, "Done") + self.report({'INFO'}, 'Done') class ArmoryPlayButton(bpy.types.Operator): - bl_idname = "arm.play" - bl_label = "Play" + bl_idname = 'arm.play' + bl_label = 'Play' def execute(self, context): - play_project(self) + play_project(self, False) + return{'FINISHED'} + +class ArmoryPlayInFrameButton(bpy.types.Operator): + bl_idname = 'arm.play_in_frame' + bl_label = 'Play' + + def execute(self, context): + play_project(self, True) + return{'FINISHED'} + +class ArmoryStopButton(bpy.types.Operator): + bl_idname = 'arm.stop' + bl_label = 'Stop' + + def execute(self, context): + stop_project(self) return{'FINISHED'} class ArmoryBuildButton(bpy.types.Operator): - bl_idname = "arm.build" - bl_label = "Build" + bl_idname = 'arm.build' + bl_label = 'Build' def execute(self, context): build_project(self) + compile_project(self) return{'FINISHED'} class ArmoryFolderButton(bpy.types.Operator): - bl_idname = "arm.folder" - bl_label = "Project Folder" + bl_idname = 'arm.folder' + bl_label = 'Project Folder' def execute(self, context): webbrowser.open('file://' + utils.get_fp()) return{'FINISHED'} class ArmoryCleanButton(bpy.types.Operator): - bl_idname = "arm.clean" - bl_label = "Clean Project" + bl_idname = 'arm.clean' + bl_label = 'Clean Project' def execute(self, context): clean_project(self) diff --git a/blender/props.py b/blender/props.py index bbc4e2aa..72d2be0d 100755 --- a/blender/props.py +++ b/blender/props.py @@ -13,7 +13,7 @@ def cb_scene_update(context): def initProperties(): # For project - bpy.types.World.CGVersion = StringProperty(name = "CGVersion", default="16.7") + bpy.types.World.CGVersion = StringProperty(name = "CGVersion", default="") bpy.types.World.CGProjectTarget = EnumProperty( items = [('HTML5', 'HTML5', 'HTML5'), ('Windows', 'Windows', 'Windows'), @@ -33,8 +33,11 @@ def initProperties(): ('Bullet', 'Bullet', 'Bullet')], name = "Physics", default='Bullet') bpy.types.World.CGKhafileConfig = StringProperty(name = "Config") - bpy.types.World.CGMinimize = BoolProperty(name="Minimize", default=True) + bpy.types.World.CGMinimize = BoolProperty(name="Minimize Data", default=True) bpy.types.World.CGCacheShaders = BoolProperty(name="Cache Shaders", default=True) + bpy.types.World.CGPlayViewportCamera = BoolProperty(name="Viewport Camera", default=False) + bpy.types.World.CGPlayConsole = BoolProperty(name="Console", default=False) + bpy.types.World.CGPlayDeveloperTools = BoolProperty(name="Developer Tools", default=False) # For object bpy.types.Object.geometry_cached = bpy.props.BoolProperty(name="Geometry Cached", default=False) # TODO: move to mesh type @@ -63,6 +66,7 @@ def initProperties(): # TODO: move to world 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_color = bpy.props.FloatVectorProperty(name="Environment Color", size=4, default=[0,0,0,1]) bpy.types.Camera.world_envtex_strength = bpy.props.FloatProperty(name="Environment Strength", default=1.0) bpy.types.Camera.world_envtex_sun_direction = bpy.props.FloatVectorProperty(name="Sun Direction", size=3, default=[0,0,0]) bpy.types.Camera.world_envtex_turbidity = bpy.props.FloatProperty(name="Turbidity", default=1.0) @@ -70,6 +74,9 @@ def initProperties(): 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.generate_radiance = bpy.props.BoolProperty(name="Generate Radiance", default=True) + bpy.types.World.shadowmap_size = bpy.props.IntProperty(name="Shadowmap Size", default=0) + bpy.types.World.scripts_list = bpy.props.CollectionProperty(type=bpy.types.PropertyGroup) + bpy.types.World.bundled_scripts_list = bpy.props.CollectionProperty(type=bpy.types.PropertyGroup) # For material bpy.types.Material.receive_shadow = bpy.props.BoolProperty(name="Receive Shadow", default=True) bpy.types.Material.custom_shader = bpy.props.BoolProperty(name="Custom Shader", default=False) @@ -82,7 +89,7 @@ def initProperties(): # Menu in object region class ObjectPropsPanel(bpy.types.Panel): - bl_label = "Cycles Props" + bl_label = "Armory Props" bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "object" @@ -101,7 +108,7 @@ class ObjectPropsPanel(bpy.types.Panel): # Menu in data region class DataPropsPanel(bpy.types.Panel): - bl_label = "Cycles Props" + bl_label = "Armory Props" bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "data" @@ -117,14 +124,9 @@ class DataPropsPanel(bpy.types.Panel): layout.prop_search(obj.data, "probe_volume", bpy.data, "objects") layout.prop(obj.data, 'probe_strength') layout.prop(obj.data, 'probe_blending') - layout.prop(obj.data, 'frustum_culling') layout.prop(obj.data, 'sort_front_to_back') layout.prop_search(obj.data, "pipeline_path", bpy.data, "node_groups") - layout.prop(obj.data, 'pipeline_id') - layout.prop(obj.data, 'geometry_context') - layout.prop(obj.data, 'shadows_context') - layout.prop(obj.data, 'translucent_context') layout.operator("cg.reset_pipelines") elif obj.type == 'MESH': layout.prop(obj.data, 'static_usage') @@ -159,7 +161,7 @@ class OBJECT_OT_INVALIDATECACHEButton(bpy.types.Operator): # Menu in materials region class MatsPropsPanel(bpy.types.Panel): - bl_label = "Cycles Props" + bl_label = "Armory Props" bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "material" @@ -177,7 +179,7 @@ class MatsPropsPanel(bpy.types.Panel): # Menu in world region class WorldPropsPanel(bpy.types.Panel): - bl_label = "Cycles Props" + bl_label = "Armory Props" bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "world" diff --git a/blender/start.py b/blender/start.py index 49a0dc96..f5b27c29 100755 --- a/blender/start.py +++ b/blender/start.py @@ -8,7 +8,14 @@ import traits_params import traits import props +import bpy +import utils +import subprocess +import atexit +import os + def register(): + props.register() project.register() nodes_logic.register() nodes_pipeline.register() @@ -17,7 +24,16 @@ def register(): traits_animation.register() traits_params.register() traits.register() - props.register() + + # Start server + user_preferences = bpy.context.user_preferences + addon_prefs = user_preferences.addons['armory'].preferences + scripts_path = addon_prefs.sdk_path + '/armory/blender/' + os.chdir(utils.get_fp()) + blender_path = bpy.app.binary_path + blend_path = bpy.data.filepath + register.p = subprocess.Popen([blender_path, blend_path, '-b', '-P', scripts_path + 'lib/server.py', '&']) + atexit.register(register.p.terminate) def unregister(): project.unregister() @@ -29,3 +45,6 @@ def unregister(): traits_params.unregister() traits.unregister() props.unregister() + + # Stop server + register.p.terminate() diff --git a/blender/traits.py b/blender/traits.py index 0ced83ed..27cf7563 100755 --- a/blender/traits.py +++ b/blender/traits.py @@ -2,17 +2,20 @@ import shutil import bpy import os import json -from traits_animation import ListAnimationTraitItem -from traits_params import ListParamsTraitItem +from traits_animation import * +from traits_params import * from bpy.types import Menu, Panel, UIList from bpy.props import * +import utils +import write_data +import subprocess class ListTraitItem(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="Untitled") + default="") enabled_prop = bpy.props.BoolProperty( name="", @@ -21,6 +24,7 @@ class ListTraitItem(bpy.types.PropertyGroup): type_prop = bpy.props.EnumProperty( items = [('Script', 'Script', 'Script'), + ('Bundled Script', 'Bundled Script', 'Bundled Script'), ('Nodes', 'Nodes', 'Nodes'), ('Scene Instance', 'Scene Instance', 'Scene Instance'), ('Animation', 'Animation', 'Animation') @@ -40,7 +44,7 @@ class ListTraitItem(bpy.types.PropertyGroup): class_name_prop = bpy.props.StringProperty( name="Class", description="A name for this item", - default="Untitled") + default="") nodes_name_prop = bpy.props.StringProperty( name="Nodes", @@ -52,6 +56,12 @@ class ListTraitItem(bpy.types.PropertyGroup): description="A name for this item", default="") + my_paramstraitlist = bpy.props.CollectionProperty(type = ListParamsTraitItem) + 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): 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... @@ -152,10 +162,51 @@ class LIST_OT_TraitMoveItem(bpy.types.Operator): return{'CANCELLED'} return{'FINISHED'} +class ArmoryEditScriptButton(bpy.types.Operator): + bl_idname = 'arm.edit_script' + bl_label = 'Edit Script' + + def execute(self, context): + user_preferences = bpy.context.user_preferences + addon_prefs = user_preferences.addons['armory'].preferences + sdk_path = addon_prefs.sdk_path + kode_path = sdk_path + '/KodeStudio/KodeStudio.app/Contents/MacOS/Electron' + project_path = utils.get_fp() + item = context.object.my_traitlist[context.object.traitlist_index] + hx_path = project_path + '/Sources/' + bpy.data.worlds[0].CGProjectPackage + '/' + item.class_name_prop + '.hx' + subprocess.call([kode_path + ' -cmd ' + utils.get_fp() + ' ' + hx_path + ' &'], shell=True) + return{'FINISHED'} + +class ArmoryNewScriptDialog(bpy.types.Operator): + bl_idname = "arm.new_script" + bl_label = "New Script" + + class_name = StringProperty(name="Name") + + def execute(self, context): + self.class_name = self.class_name.replace(' ', '') + write_data.write_traithx(self.class_name) + utils.fetch_script_names() + obj = context.object + item = obj.my_traitlist[obj.traitlist_index] + item.class_name_prop = self.class_name + return {'FINISHED'} + + def invoke(self, context, event): + self.class_name = 'MyTrait' + return context.window_manager.invoke_props_dialog(self) + +class ArmoryRefreshScriptsListButton(bpy.types.Operator): + bl_idname = 'arm.refresh_scripts_list' + bl_label = 'Refresh Scripts List' + + def execute(self, context): + utils.fetch_script_names() + return{'FINISHED'} # Menu in tools region class ToolsTraitsPanel(bpy.types.Panel): - bl_label = "Cycles Traits" + bl_label = "Armory Traits" bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "object" @@ -188,31 +239,43 @@ class ToolsTraitsPanel(bpy.types.Panel): row.prop(item, "type_prop") # Script - if item.type_prop =='Script': + if item.type_prop == 'Script' or item.type_prop == 'Bundled Script': item.name = item.class_name_prop row = layout.row() - row.prop(item, "class_name_prop") + # row.prop(item, "class_name_prop") + if item.type_prop == 'Script': + row.prop_search(item, "class_name_prop", bpy.data.worlds[0], "scripts_list", "Class") + else: + row.prop_search(item, "class_name_prop", bpy.data.worlds[0], "bundled_scripts_list", "Class") # Params layout.label("Parameters") paramsrow = layout.row() paramsrows = 2 - if len(obj.my_animationtraitlist) > 1: + if len(item.my_paramstraitlist) > 1: paramsrows = 4 row = layout.row() - row.template_list("MY_UL_ParamsTraitList", "The_List", obj, "my_paramstraitlist", obj, "paramstraitlist_index", rows=paramsrows) + row.template_list("MY_UL_ParamsTraitList", "The_List", item, "my_paramstraitlist", item, "paramstraitlist_index", rows=paramsrows) col = row.column(align=True) col.operator("my_paramstraitlist.new_item", icon='ZOOMIN', text="") col.operator("my_paramstraitlist.delete_item", icon='ZOOMOUT', text="") - if len(obj.my_paramstraitlist) > 1: + if len(item.my_paramstraitlist) > 1: col.separator() col.operator("my_paramstraitlist.move_item", icon='TRIA_UP', text="").direction = 'UP' col.operator("my_paramstraitlist.move_item", icon='TRIA_DOWN', text="").direction = 'DOWN' - if obj.paramstraitlist_index >= 0 and len(obj.my_paramstraitlist) > 0: - item = obj.my_paramstraitlist[obj.paramstraitlist_index] + if item.paramstraitlist_index >= 0 and len(item.my_paramstraitlist) > 0: + item = item.my_paramstraitlist[item.paramstraitlist_index] + + if item.type_prop == 'Script': + row = layout.row() + if item.class_name_prop == '': + row.enabled = False + row.operator("arm.edit_script") + layout.operator("arm.new_script") + layout.operator("arm.refresh_scripts_list") # Nodes elif item.type_prop =='Nodes': @@ -231,28 +294,28 @@ class ToolsTraitsPanel(bpy.types.Panel): elif item.type_prop == 'Animation': item.name = item.type_prop row = layout.row() - row.prop_search(item, "start_track_name_prop", obj, "my_animationtraitlist", "Start Track") + 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(obj.my_animationtraitlist) > 1: + if len(item.my_animationtraitlist) > 1: animrows = 4 row = layout.row() - row.template_list("MY_UL_AnimationTraitList", "The_List", obj, "my_animationtraitlist", obj, "animationtraitlist_index", rows=animrows) + 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(obj.my_animationtraitlist) > 1: + 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 obj.animationtraitlist_index >= 0 and len(obj.my_animationtraitlist) > 0: - item = obj.my_animationtraitlist[obj.animationtraitlist_index] + if item.animationtraitlist_index >= 0 and len(item.my_animationtraitlist) > 0: + item = item.my_animationtraitlist[item.animationtraitlist_index] row = layout.row() row.prop(item, "start_prop") diff --git a/blender/traits_animation.py b/blender/traits_animation.py index 28594aeb..1376d923 100755 --- a/blender/traits_animation.py +++ b/blender/traits_animation.py @@ -44,18 +44,15 @@ class MY_UL_AnimationTraitList(bpy.types.UIList): layout.alignment = 'CENTER' layout.label("", icon = custom_icon) -def initObjectProperties(): - bpy.types.Object.my_animationtraitlist = bpy.props.CollectionProperty(type = ListAnimationTraitItem) - bpy.types.Object.animationtraitlist_index = bpy.props.IntProperty(name = "Index for my_list", default = 0) - class LIST_OT_AnimationTraitNewItem(bpy.types.Operator): # Add a new item to the list bl_idname = "my_animationtraitlist.new_item" bl_label = "Add a new item" def execute(self, context): - bpy.context.object.my_animationtraitlist.add() - bpy.context.object.animationtraitlist_index += 1 + trait = context.object.my_traitlist[context.object.traitlist_index] + trait.my_animationtraitlist.add() + trait.animationtraitlist_index += 1 return{'FINISHED'} @@ -67,18 +64,20 @@ class LIST_OT_AnimationTraitDeleteItem(bpy.types.Operator): @classmethod def poll(self, context): """ Enable if there's something in the list """ - return len(bpy.context.object.my_animationtraitlist) > 0 + trait = context.object.my_traitlist[context.object.traitlist_index] + return len(trait.my_animationtraitlist) > 0 def execute(self, context): - list = bpy.context.object.my_animationtraitlist - index = bpy.context.object.animationtraitlist_index + trait = context.object.my_traitlist[context.object.traitlist_index] + list = trait.my_animationtraitlist + index = trait.animationtraitlist_index list.remove(index) if index > 0: index = index - 1 - bpy.context.object.animationtraitlist_index = index + trait.animationtraitlist_index = index return{'FINISHED'} @@ -94,13 +93,15 @@ class LIST_OT_AnimationTraitMoveItem(bpy.types.Operator): @classmethod def poll(self, context): """ Enable if there's something in the list. """ - return len(bpy.context.object.my_animationtraitlist) > 0 + trait = context.object.my_traitlist[context.object.traitlist_index] + return len(trait.my_animationtraitlist) > 0 def move_index(self): # Move index of an item render queue while clamping it - index = bpy.context.object.animationtraitlist_index - list_length = len(bpy.context.object.my_animationtraitlist) - 1 + trait = context.object.my_traitlist[context.object.traitlist_index] + index = trait.animationtraitlist_index + list_length = len(trait.my_animationtraitlist) - 1 new_index = 0 if self.direction == 'UP': @@ -113,8 +114,9 @@ class LIST_OT_AnimationTraitMoveItem(bpy.types.Operator): def execute(self, context): - list = bpy.context.object.my_animationtraitlist - index = bpy.context.object.animationtraitlist_index + trait = context.object.my_traitlist[context.object.traitlist_index] + list = trait.my_animationtraitlist + index = trait.animationtraitlist_index if self.direction == 'DOWN': neighbor = index + 1 @@ -131,7 +133,6 @@ class LIST_OT_AnimationTraitMoveItem(bpy.types.Operator): def register(): bpy.utils.register_module(__name__) - initObjectProperties() def unregister(): bpy.utils.unregister_module(__name__) diff --git a/blender/traits_params.py b/blender/traits_params.py index 2a870e12..dafa4145 100755 --- a/blender/traits_params.py +++ b/blender/traits_params.py @@ -11,13 +11,6 @@ class ListParamsTraitItem(bpy.types.PropertyGroup): name="Name", description="A name for this item", default="Untitled") - - # enabled_prop = bpy.props.BoolProperty( - # name="", - # description="A name for this item", - # default=True) - - class MY_UL_ParamsTraitList(bpy.types.UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): @@ -34,18 +27,15 @@ class MY_UL_ParamsTraitList(bpy.types.UIList): layout.alignment = 'CENTER' layout.label("", icon = custom_icon) -def initObjectProperties(): - bpy.types.Object.my_paramstraitlist = bpy.props.CollectionProperty(type = ListParamsTraitItem) - bpy.types.Object.paramstraitlist_index = bpy.props.IntProperty(name = "Index for my_list", default = 0) - class LIST_OT_ParamsTraitNewItem(bpy.types.Operator): # Add a new item to the list bl_idname = "my_paramstraitlist.new_item" bl_label = "Add a new item" def execute(self, context): - bpy.context.object.my_paramstraitlist.add() - bpy.context.object.paramstraitlist_index += 1 + trait = context.object.my_traitlist[context.object.traitlist_index] + trait.my_paramstraitlist.add() + trait.paramstraitlist_index += 1 return{'FINISHED'} @@ -57,18 +47,20 @@ class LIST_OT_ParamsTraitDeleteItem(bpy.types.Operator): @classmethod def poll(self, context): """ Enable if there's something in the list """ - return len(bpy.context.object.my_paramstraitlist) > 0 + trait = context.object.my_traitlist[context.object.traitlist_index] + return len(trait.my_paramstraitlist) > 0 def execute(self, context): - list = bpy.context.object.my_paramstraitlist - index = bpy.context.object.paramstraitlist_index + trait = context.object.my_traitlist[context.object.traitlist_index] + list = trait.my_paramstraitlist + index = trait.paramstraitlist_index list.remove(index) if index > 0: index = index - 1 - bpy.context.object.paramstraitlist_index = index + trait.paramstraitlist_index = index return{'FINISHED'} @@ -84,13 +76,15 @@ class LIST_OT_ParamsTraitMoveItem(bpy.types.Operator): @classmethod def poll(self, context): """ Enable if there's something in the list. """ - return len(bpy.context.object.my_paramstraitlist) > 0 + trait = context.object.my_traitlist[context.object.traitlist_index] + return len(trait.my_paramstraitlist) > 0 def move_index(self): # Move index of an item render queue while clamping it - index = bpy.context.object.paramstraitlist_index - list_length = len(bpy.context.object.my_paramstraitlist) - 1 + trait = context.object.my_traitlist[context.object.traitlist_index] + index = trait.paramstraitlist_index + list_length = len(trait.my_paramstraitlist) - 1 new_index = 0 if self.direction == 'UP': @@ -103,8 +97,9 @@ class LIST_OT_ParamsTraitMoveItem(bpy.types.Operator): def execute(self, context): - list = bpy.context.object.my_paramstraitlist - index = bpy.context.object.paramstraitlist_index + trait = context.object.my_traitlist[context.object.traitlist_index] + list = trait.my_paramstraitlist + index = trait.paramstraitlist_index if self.direction == 'DOWN': neighbor = index + 1 @@ -121,7 +116,6 @@ class LIST_OT_ParamsTraitMoveItem(bpy.types.Operator): def register(): bpy.utils.register_module(__name__) - initObjectProperties() def unregister(): bpy.utils.unregister_module(__name__) diff --git a/blender/utils.py b/blender/utils.py index 9732a146..548c41c3 100755 --- a/blender/utils.py +++ b/blender/utils.py @@ -1,6 +1,7 @@ import bpy import json import os +import glob class Object: def to_JSON(self): @@ -14,13 +15,19 @@ def get_fp(): s.pop() return os.path.sep.join(s) - -# Start server -# s = bpy.data.filepath.split(os.path.sep) -# s.pop() -# fp = os.path.sep.join(s) -# os.chdir(fp) -# blender_path = bpy.app.binary_path -# blend_path = bpy.data.filepath -# p = subprocess.Popen([blender_path, blend_path, '-b', '-P', scripts_path + 'lib/server.py', '&']) -# atexit.register(p.terminate) +def fetch_script_names(): + user_preferences = bpy.context.user_preferences + addon_prefs = user_preferences.addons['armory'].preferences + sdk_path = addon_prefs.sdk_path + wrd = bpy.data.worlds[0] + wrd.bundled_scripts_list.clear() + os.chdir(sdk_path + '/armory/Sources/armory/trait') + for file in glob.glob('*.hx'): + wrd.bundled_scripts_list.add().name = file.rsplit('.')[0] + wrd.scripts_list.clear() + sources_path = get_fp() + '/Sources/' + wrd.CGProjectPackage + if os.path.isdir(sources_path): + os.chdir(sources_path) + for file in glob.glob('*.hx'): + wrd.scripts_list.add().name = file.rsplit('.')[0] + os.chdir(get_fp()) diff --git a/blender/write_data.py b/blender/write_data.py index b65d84d5..07132ff3 100644 --- a/blender/write_data.py +++ b/blender/write_data.py @@ -23,7 +23,7 @@ project.addAssets('Assets/**'); """) f.write('project.addLibrary("../' + bpy.path.relpath(sdk_path + '/armory')[2:] + '");\n') - f.write('project.addLibrary("../' + bpy.path.relpath(sdk_path + '/lue')[2:] + '");\n') + f.write('project.addLibrary("../' + bpy.path.relpath(sdk_path + '/iron')[2:] + '");\n') f.write('project.addLibrary("../' + bpy.path.relpath(sdk_path + '/zui')[2:] + '");\n') if bpy.data.worlds[0]['CGPhysics'] != 0: @@ -47,23 +47,23 @@ project.addAssets('Assets/**'); # Write Main.hx def write_main(): + wrd = bpy.data.worlds[0] #if not os.path.isfile('Sources/Main.hx'): with open('Sources/Main.hx', 'w') as f: f.write( """// Auto-generated package ; class Main { - public static inline var projectName = '""" + bpy.data.worlds[0]['CGProjectName'] + """'; - public static inline var projectPackage = '""" + bpy.data.worlds[0]['CGProjectPackage'] + """'; - static inline var projectWidth = """ + str(bpy.data.worlds[0]['CGProjectWidth']) + """; - static inline var projectHeight = """ + str(bpy.data.worlds[0]['CGProjectHeight']) + """; - static inline var projectSamplesPerPixel = """ + str(bpy.data.worlds[0]['CGProjectSamplesPerPixel']) + """; - public static inline var projectScene = '""" + str(bpy.data.worlds[0]['CGProjectScene']) + """'; + public static inline var projectName = '""" + wrd['CGProjectName'] + """'; + public static inline var projectPackage = '""" + wrd['CGProjectPackage'] + """'; + static inline var projectWidth = """ + str(wrd['CGProjectWidth']) + """; + static inline var projectHeight = """ + str(wrd['CGProjectHeight']) + """; + static inline var projectSamplesPerPixel = """ + str(wrd['CGProjectSamplesPerPixel']) + """; + public static inline var projectScene = '""" + str(wrd['CGProjectScene']) + """'; public static function main() { - lue.sys.CompileTime.importPackage('lue.trait'); - lue.sys.CompileTime.importPackage('cycles.trait'); - lue.sys.CompileTime.importPackage('cycles.renderpipeline'); - lue.sys.CompileTime.importPackage('""" + bpy.data.worlds[0]['CGProjectPackage'] + """'); + iron.sys.CompileTime.importPackage('armory.trait'); + iron.sys.CompileTime.importPackage('armory.renderpipeline'); + iron.sys.CompileTime.importPackage('""" + wrd['CGProjectPackage'] + """'); #if (js && WITH_PHYSICS) untyped __js__(" function loadScript(url, callback) { @@ -83,8 +83,114 @@ class Main { } static function start() { kha.System.init({title: projectName, width: projectWidth, height: projectHeight, samplesPerPixel: projectSamplesPerPixel}, function() { - new lue.App(cycles.Root); + new iron.App(armory.Root); }); } } """) + +# Write electron.js +def write_electronjs(x, y, w, h, winoff, in_frame): + wrd = bpy.data.worlds[0] + dev_tools = wrd.CGPlayDeveloperTools + with open('build/electron.js', 'w') as f: + f.write( +"""// Auto-generated +'use strict'; +const electron = require('electron'); +const app = electron.app; +const BrowserWindow = electron.BrowserWindow; +let mainWindow; + +function createWindow () { """) + + if in_frame: + f.write( +""" + var point = electron.screen.getCursorScreenPoint(); + var targetDisplay = electron.screen.getDisplayNearestPoint(point); + var offY = targetDisplay.workAreaSize.height - """ + str(int(winoff)) + """; + var targetX = targetDisplay.bounds.x + """ + str(int(x)) + """; + var targetY = targetDisplay.bounds.y + """ + str(int(y)) + """ + offY; + mainWindow = new BrowserWindow({x: targetX, y: targetY, width: """ + str(int(w)) + """, height: """ + str(int(h)) + """, frame: false, autoHideMenuBar: true, useContentSize: true, movable: false, resizable: false, transparent: true, enableLargerThanScreen: true}); + mainWindow.setSkipTaskbar(true); + mainWindow.setAlwaysOnTop(true); + app.dock.setBadge(''); +""") + else: + f.write( +""" + mainWindow = new BrowserWindow({width: """ + str(int(w)) + """, height: """ + str(int(h)) + """, autoHideMenuBar: true, useContentSize: true}); +""") + f.write( +""" + //mainWindow.loadURL('file://' + __dirname + '/build/html5/index.html'); + mainWindow.loadURL('http://localhost:8040/build/html5/index.html'); + mainWindow.on('closed', function() { mainWindow = null; });""") + + if dev_tools: + f.write(""" + mainWindow.toggleDevTools();""") + + f.write(""" +} +app.on('ready', createWindow); +app.on('window-all-closed', function () { app.quit(); }); +app.on('activate', function () { if (mainWindow === null) { createWindow(); } }); +""") + +# Write index.html +def write_indexhtml(w, h, in_frame): + with open('build/html5/index.html', 'w') as f: + f.write( +""" + + + + ArmoryGame + + + + + + + +""") + +def write_compiledglsl(clip_start, clip_end, shadowmap_size): + with open('compiled/Shaders/compiled.glsl', 'w') as f: + f.write( +"""const float PI = 3.1415926535; +const float PI2 = PI * 2.0; +const vec2 cameraPlane = vec2(""" + str(int(clip_start * 100) / 100) + """, """ + str(int(clip_end * 100) / 100) + """); +const vec2 shadowmapSize = vec2(""" + str(shadowmap_size) + """, """ + str(shadowmap_size) + """); +""") + +def write_traithx(class_name): + wrd = bpy.data.worlds[0] + with open('Sources/' + wrd.CGProjectPackage + '/' + class_name + '.hx', 'w') as f: + f.write( +"""package """ + wrd.CGProjectPackage + """; + +class """ + class_name + """ extends iron.Trait { + public function new() { + super(); + + // notifyOnInit(function() { + // }); + + // notifyOnUpdate(function() { + // }); + + // notifyOnRemove(function() { + // }); + } +} +""") diff --git a/raw/compositor_pass/compositor_pass.frag.glsl b/raw/compositor_pass/compositor_pass.frag.glsl index 83e6d401..7d0db2e0 100644 --- a/raw/compositor_pass/compositor_pass.frag.glsl +++ b/raw/compositor_pass/compositor_pass.frag.glsl @@ -21,6 +21,8 @@ uniform vec3 light; uniform mat4 VP; #endif +// uniform vec2 cameraPlane; + // #ifdef _Grain // uniform float time; // #endif @@ -189,9 +191,9 @@ vec4 sampleBox(float size) { return color; } -float linearize(float depth, float znear, float zfar) { - return -zfar * znear / (depth * (zfar - znear) - zfar); -} +// float linearize(float depth) { + // return -cameraPlane.y * cameraPlane.x / (depth * (cameraPlane.y - cameraPlane.x) - cameraPlane.y); +// } // Based on lense flare implementation by musk // https://www.shadertoy.com/view/4sX3Rs @@ -302,7 +304,7 @@ void main() { // float lightDistance = distance(eye, light); // vec2 lss = lndc.xy * 0.5 + 0.5; - // float lssdepth = linearize(texture(gbuffer0, lss).a, 0.1, 1000.0); + // float lssdepth = linearize(texture(gbuffer0, lss).a); // if (lssdepth >= lightDistance) { // vec2 lensuv = (texCoord - 0.5) * 2.0; diff --git a/raw/deferred/decals.frag.glsl b/raw/deferred/decals.frag.glsl index dd2cd259..a330ca4e 100644 --- a/raw/deferred/decals.frag.glsl +++ b/raw/deferred/decals.frag.glsl @@ -47,18 +47,6 @@ mat3 cotangentFrame(vec3 nor, vec3 pos, vec2 uv) { return mat3(T * invmax, B * invmax, nor); } -// vec3 getPos(float depth) { -// vec3 vray = normalize(viewRay); -// const float znear = 0.1; -// const float zfar = 1000.0; -// const float projectionA = zfar / (zfar - znear); -// const float projectionB = (-zfar * znear) / (zfar - znear); -// float linearDepth = projectionB / (depth * 0.5 + 0.5 - projectionA); -// float viewZDist = dot(eyeLook, vray); -// vec3 wposition = eye + vray * (linearDepth / viewZDist); -// return wposition; -// } - vec2 octahedronWrap(vec2 v) { return (1.0 - abs(v.yx)) * (vec2(v.x >= 0.0 ? 1.0 : -1.0, v.y >= 0.0 ? 1.0 : -1.0)); } diff --git a/raw/deferred_light/deferred_light.frag.glsl b/raw/deferred_light/deferred_light.frag.glsl index eb799950..6b9db41d 100644 --- a/raw/deferred_light/deferred_light.frag.glsl +++ b/raw/deferred_light/deferred_light.frag.glsl @@ -4,6 +4,8 @@ precision mediump float; #endif +#include "../compiled.glsl" + uniform sampler2D gbufferD; uniform sampler2D gbuffer0; uniform sampler2D gbuffer1; @@ -25,12 +27,6 @@ uniform float envmapStrength; uniform sampler2D ssaotex; uniform sampler2D shadowMap; -const float znear = 0.1; -const float zfar = 1000.0; -const vec2 shadowMapSize = vec2(2048, 2048); -const float PI = 3.1415926535; -const float TwoPI = (2.0 * PI); - // #ifdef _LTC // uniform sampler2D sltcMat; // uniform sampler2D sltcMag; @@ -101,7 +97,7 @@ vec3 SSSSTransmittance(float translucency, float sssWidth, vec3 worldPosition, v vec2 envMapEquirect(vec3 normal) { float phi = acos(normal.z); float theta = atan(-normal.y, normal.x) + PI; - return vec2(theta / TwoPI, phi / PI); + return vec2(theta / PI2, phi / PI); } #ifdef _Rad @@ -172,27 +168,27 @@ float PCF(vec2 uv, float compare) { float result = 0.0; // for (int x = -1; x <= 1; x++){ // for(int y = -1; y <= 1; y++){ - // vec2 off = vec2(x, y) / shadowMapSize; - // result += texture2DShadowLerp(shadowMapSize, uv + off, compare); + // vec2 off = vec2(x, y) / shadowmapSize; + // result += texture2DShadowLerp(shadowmapSize, uv + off, compare); - vec2 off = vec2(-1, -1) / shadowMapSize; - result += texture2DShadowLerp(shadowMapSize, uv + off, compare); - off = vec2(-1, 0) / shadowMapSize; - result += texture2DShadowLerp(shadowMapSize, uv + off, compare); - off = vec2(-1, 1) / shadowMapSize; - result += texture2DShadowLerp(shadowMapSize, uv + off, compare); - off = vec2(0, -1) / shadowMapSize; - result += texture2DShadowLerp(shadowMapSize, uv + off, compare); - off = vec2(0, 0) / shadowMapSize; - result += texture2DShadowLerp(shadowMapSize, uv + off, compare); - off = vec2(0, 1) / shadowMapSize; - result += texture2DShadowLerp(shadowMapSize, uv + off, compare); - off = vec2(1, -1) / shadowMapSize; - result += texture2DShadowLerp(shadowMapSize, uv + off, compare); - off = vec2(1, 0) / shadowMapSize; - result += texture2DShadowLerp(shadowMapSize, uv + off, compare); - off = vec2(1, 1) / shadowMapSize; - result += texture2DShadowLerp(shadowMapSize, uv + off, compare); + vec2 off = vec2(-1, -1) / shadowmapSize; + result += texture2DShadowLerp(shadowmapSize, uv + off, compare); + off = vec2(-1, 0) / shadowmapSize; + result += texture2DShadowLerp(shadowmapSize, uv + off, compare); + off = vec2(-1, 1) / shadowmapSize; + result += texture2DShadowLerp(shadowmapSize, uv + off, compare); + off = vec2(0, -1) / shadowmapSize; + result += texture2DShadowLerp(shadowmapSize, uv + off, compare); + off = vec2(0, 0) / shadowmapSize; + result += texture2DShadowLerp(shadowmapSize, uv + off, compare); + off = vec2(0, 1) / shadowmapSize; + result += texture2DShadowLerp(shadowmapSize, uv + off, compare); + off = vec2(1, -1) / shadowmapSize; + result += texture2DShadowLerp(shadowmapSize, uv + off, compare); + off = vec2(1, 0) / shadowmapSize; + result += texture2DShadowLerp(shadowmapSize, uv + off, compare); + off = vec2(1, 1) / shadowmapSize; + result += texture2DShadowLerp(shadowmapSize, uv + off, compare); // } // } return result / 9.0; @@ -232,10 +228,10 @@ float PCF(vec2 uv, float compare) { } void initPoissonSamples(const in vec2 randomSeed) { - const float ANGLE_STEP = TwoPI * float(NUM_RINGS) / float(NUM_SAMPLES); + const float ANGLE_STEP = PI2 * float(NUM_RINGS) / float(NUM_SAMPLES); const float INV_NUM_SAMPLES = 1.0 / float(NUM_SAMPLES); - float angle = rand(randomSeed) * TwoPI; + float angle = rand(randomSeed) * PI2; float radius = INV_NUM_SAMPLES; float radiusStep = radius; @@ -453,8 +449,8 @@ vec3 getPos(float depth) { // return pos.xyz; vec3 vray = normalize(viewRay); - const float projectionA = zfar / (zfar - znear); - const float projectionB = (-zfar * znear) / (zfar - znear); + const float projectionA = cameraPlane.y / (cameraPlane.y - cameraPlane.x); + const float projectionB = (-cameraPlane.y * cameraPlane.x) / (cameraPlane.y - cameraPlane.x); // float linearDepth = projectionB / (depth - projectionA); float linearDepth = projectionB / (depth * 0.5 + 0.5 - projectionA); float viewZDist = dot(eyeLook, vray); diff --git a/raw/env_map/env_map.frag.glsl b/raw/env_map/env_map.frag.glsl index 416e424e..4f82f827 100644 --- a/raw/env_map/env_map.frag.glsl +++ b/raw/env_map/env_map.frag.glsl @@ -7,6 +7,9 @@ precision mediump float; const float PI = 3.1415926; const float TwoPI = (2.0 * PI); +#ifdef _EnvCol + uniform vec3 backgroundCol; +#endif #ifdef _EnvSky uniform vec3 A; uniform vec3 B; @@ -48,11 +51,17 @@ void main() { if (texture(gbufferD, texCoord).r/* * 2.0 - 1.0*/ != 1.0) { discard; } - + +#ifdef _EnvCol + gl_FragColor = vec4(backgroundCol, 1.0); + return; +#else vec3 n = normalize(normal); - +#endif + #ifdef _EnvTex gl_FragColor = texture(envmap, envMapEquirect(n)) * envmapStrength; + return; #endif #ifdef _EnvSky @@ -65,7 +74,8 @@ void main() { float gamma_val = acos(cos_gamma); vec3 R = Z * hosekWilkie(cos_theta, gamma_val, cos_gamma) * envmapStrength; - // R = pow(R, vec3(2.2)); + R = pow(R, vec3(2.2)); gl_FragColor = vec4(R, 1.0); + return; #endif } diff --git a/raw/env_map/env_map.shader.json b/raw/env_map/env_map.shader.json index 15cfd1e8..e3a624a7 100755 --- a/raw/env_map/env_map.shader.json +++ b/raw/env_map/env_map.shader.json @@ -25,6 +25,11 @@ "id": "invP", "link": "_inverseProjectionMatrix" }, + { + "id": "backgroundCol", + "link": "_backgroundCol", + "ifdef": ["_EnvCol"] + }, { "id": "A", "link": "_hosekA", diff --git a/raw/motion_blur_pass/motion_blur_pass.frag.glsl b/raw/motion_blur_pass/motion_blur_pass.frag.glsl index 75e7b6cb..83793ec9 100644 --- a/raw/motion_blur_pass/motion_blur_pass.frag.glsl +++ b/raw/motion_blur_pass/motion_blur_pass.frag.glsl @@ -6,9 +6,10 @@ precision mediump float; #endif +#include "../compiled.glsl" + uniform sampler2D gbufferD; uniform sampler2D gbuffer0; -uniform sampler2D gbuffer1; uniform sampler2D tex; uniform mat4 prevVP; @@ -23,10 +24,8 @@ const int samples = 8; vec3 getPos(float depth, vec2 coord) { vec3 vray = normalize(viewRay); - const float znear = 0.1; - const float zfar = 1000.0; - const float projectionA = zfar / (zfar - znear); - const float projectionB = (-zfar * znear) / (zfar - znear); + const float projectionA = cameraPlane.y / (cameraPlane.y - cameraPlane.x); + const float projectionB = (-cameraPlane.y * cameraPlane.x) / (cameraPlane.y - cameraPlane.x); float linearDepth = projectionB / (depth * 0.5 + 0.5 - projectionA); float viewZDist = dot(eyeLook, vray); vec3 wposition = eye + vray * (linearDepth / viewZDist); diff --git a/raw/ssao_pass/ssao_pass.frag.glsl b/raw/ssao_pass/ssao_pass.frag.glsl index c825bded..aea9fa65 100644 --- a/raw/ssao_pass/ssao_pass.frag.glsl +++ b/raw/ssao_pass/ssao_pass.frag.glsl @@ -19,7 +19,6 @@ precision mediump float; uniform sampler2D gbufferD; uniform sampler2D gbuffer0; -uniform sampler2D gbuffer1; uniform sampler2D snoise; uniform mat4 invVP; diff --git a/raw/ssao_pass/ssao_pass_st.frag.glsl b/raw/ssao_pass/ssao_pass_st.frag.glsl deleted file mode 100644 index 4efe6c65..00000000 --- a/raw/ssao_pass/ssao_pass_st.frag.glsl +++ /dev/null @@ -1,145 +0,0 @@ -// Based on SSAO by Reinder Nijhoff 2016 @reindernijhoff -// https://www.shadertoy.com/view/ls3GWS - -#version 450 - -#ifdef GL_ES -precision mediump float; -#endif - -#define SAMPLES 8 -#define INTENSITY 3.5 -#define SCALE 3.5 -#define BIAS 0.75 -#define SAMPLE_RAD 0.1 -#define MAX_DISTANCE 0.34 - -#define MOD3 vec3(.1031,.11369,.13787) - -uniform mat4 invP; - -uniform sampler2D gbuffer0; -uniform sampler2D gbuffer1; - -in vec2 texCoord; - -float hash12(vec2 p) -{ - vec3 p3 = fract(vec3(p.xyx) * MOD3); - p3 += dot(p3, p3.yzx + 19.19); - return fract((p3.x + p3.y) * p3.z); -} - -// vec3 getPosition(vec2 uv) { -// float fl = texture(iChannel0, vec2(0.)).x; -// float d = texture(iChannel0, uv).w; - -// vec2 p = uv*2.-1.; -// mat3 ca = mat3(1.,0.,0.,0.,1.,0.,0.,0.,-1./1.5); -// vec3 rd = normalize( ca * vec3(p,fl) ); - -// vec3 pos = rd * d; -// return pos; -// } -vec3 getViewPos(vec2 texCoord, float depth) { - float x = texCoord.s * 2.0 - 1.0; - float y = texCoord.t * 2.0 - 1.0; - float z = depth * 2.0 - 1.0; - vec4 posProj = vec4(x, y, z, 1.0); - vec4 posView = invP * posProj; - posView /= posView.w; - return posView.xyz; -} - -float doAmbientOcclusion(vec2 tcoord,vec2 uv, vec3 p, vec3 cnorm) -{ - // vec3 diff = getPosition(tcoord + uv) - p; - float depth = texture(gbuffer0, tcoord + uv).a; - vec3 diff = getViewPos(tcoord + uv, depth) - p; - float l = length(diff); - vec3 v = diff/l; - float d = l*SCALE; - float ao = max(0.0,dot(cnorm,v)-BIAS)*(1.0/(1.0+d)); - ao *= smoothstep(MAX_DISTANCE,MAX_DISTANCE * 0.5, l); - return ao; -} - -float spiralAO(vec2 uv, vec3 p, vec3 n, float rad) -{ - float goldenAngle = 2.4; - float ao = 0.; - float inv = 1. / float(SAMPLES); - float radius = 0.; - - float rotatePhase = hash12( uv*100. ) * 6.28; - float rStep = inv * rad; - vec2 spiralUV; - - // for (int i = 0; i < SAMPLES; i++) { - spiralUV.x = sin(rotatePhase); - spiralUV.y = cos(rotatePhase); - radius += rStep; - ao += doAmbientOcclusion(uv, spiralUV * radius, p, n); - rotatePhase += goldenAngle; - - spiralUV.x = sin(rotatePhase); - spiralUV.y = cos(rotatePhase); - radius += rStep; - ao += doAmbientOcclusion(uv, spiralUV * radius, p, n); - rotatePhase += goldenAngle; - - spiralUV.x = sin(rotatePhase); - spiralUV.y = cos(rotatePhase); - radius += rStep; - ao += doAmbientOcclusion(uv, spiralUV * radius, p, n); - rotatePhase += goldenAngle; - - spiralUV.x = sin(rotatePhase); - spiralUV.y = cos(rotatePhase); - radius += rStep; - ao += doAmbientOcclusion(uv, spiralUV * radius, p, n); - rotatePhase += goldenAngle; - - spiralUV.x = sin(rotatePhase); - spiralUV.y = cos(rotatePhase); - radius += rStep; - ao += doAmbientOcclusion(uv, spiralUV * radius, p, n); - rotatePhase += goldenAngle; - - spiralUV.x = sin(rotatePhase); - spiralUV.y = cos(rotatePhase); - radius += rStep; - ao += doAmbientOcclusion(uv, spiralUV * radius, p, n); - rotatePhase += goldenAngle; - - spiralUV.x = sin(rotatePhase); - spiralUV.y = cos(rotatePhase); - radius += rStep; - ao += doAmbientOcclusion(uv, spiralUV * radius, p, n); - rotatePhase += goldenAngle; - - spiralUV.x = sin(rotatePhase); - spiralUV.y = cos(rotatePhase); - radius += rStep; - ao += doAmbientOcclusion(uv, spiralUV * radius, p, n); - rotatePhase += goldenAngle; - //} - ao *= inv; - return ao; -} - -void main() { - vec4 g0 = texture(gbuffer0, texCoord); - float depth = g0.a; - vec3 n = g0.rgb * 2.0 - 1.0; - vec3 p = getViewPos(texCoord, depth); - - float ao = 0.; - float rad = SAMPLE_RAD/p.z; - - ao = spiralAO(texCoord, p, n, rad); - - ao = 1. - ao * INTENSITY; - - gl_FragColor = vec4(ao,ao,ao,1.); -} diff --git a/raw/ssr_pass/ssr_pass.frag.glsl b/raw/ssr_pass/ssr_pass.frag.glsl index 89e7f61e..a3f94150 100644 --- a/raw/ssr_pass/ssr_pass.frag.glsl +++ b/raw/ssr_pass/ssr_pass.frag.glsl @@ -4,6 +4,8 @@ precision mediump float; #endif +#include "../compiled.glsl" + uniform sampler2D tex; uniform sampler2D gbufferD; uniform sampler2D gbuffer0; // Normal @@ -37,10 +39,8 @@ vec4 getProjectedCoord(vec3 hitCoord) { } vec3 getPos(float depth) { - const float znear = 0.1; - const float zfar = 1000.0; - const float projectionA = zfar / (zfar - znear); - const float projectionB = (-zfar * znear) / (zfar - znear); + const float projectionA = cameraPlane.y / (cameraPlane.y - cameraPlane.x); + const float projectionB = (-cameraPlane.y * cameraPlane.x) / (cameraPlane.y - cameraPlane.x); float linearDepth = projectionB / (projectionA - depth); return viewRay * linearDepth; } diff --git a/raw/ssr_pass/ssr_pass_kode.frag.glsl b/raw/ssr_pass/ssr_pass_kode.frag.glsl deleted file mode 100644 index f72f38e2..00000000 --- a/raw/ssr_pass/ssr_pass_kode.frag.glsl +++ /dev/null @@ -1,263 +0,0 @@ -// SSR based on implementation by Ben Hopkins -// Work in progress -#version 450 - -#ifdef GL_ES -precision mediump float; -#endif - -uniform sampler2D tex; -uniform sampler2D gbuffer0; // Normal, depth -uniform sampler2D gbuffer1; // Color, roughness -uniform mat4 P; -uniform mat4 invP; -uniform mat4 V; -uniform vec3 eye; - -in vec2 texCoord; -in vec3 cameraRay; - -vec3 ScreenSpaceToViewSpace(vec3 cameraRay, float depth) { - return (cameraRay * depth); -} - -// void swapIfBigger(inout float aa, inout float bb) { -// if( aa > bb) { -// float tmp = aa; -// aa = bb; -// bb = tmp; -// } -// } - -// Projection params: x is 1.0 (or –1.0 if currently rendering with a flipped projection matrix), y is the camera’s near plane, z is the camera’s far plane and w is 1/FarPlane. - -// Linear01Depth: Z buffer to linear 0..1 depth (0 at eye, 1 at far plane) inline float Linear01Depth( float z ) { } // Z buffer to linear depth inline float -float Linear01Depth(float z) { - // return 1.0 / (_ZBufferParams.x * z + _ZBufferParams.y); - return z; - // x = 1 - 100/0.1 = -999 - // y = 100/0.1 = 1000 - // 1 / (-999 * 200 + 1000) -} - -// double zc0, zc1; -// // OpenGL would be this: -// // zc0 = (1.0 - m_FarClip / m_NearClip) / 2.0; -// // zc1 = (1.0 + m_FarClip / m_NearClip) / 2.0; -// // D3D is this: -// zc0 = 1.0 - m_FarClip / m_NearClip; -// zc1 = m_FarClip / m_NearClip; -// // now set _ZBufferParams with (zc0, zc1, zc0/m_FarClip, zc1/m_FarClip); - - // // Values used to linearize the Z buffer (http://www.humus.name/temp/Linearize%20depth.txt) - // // x = 1-far/near - // // y = far/near - // // z = x/far - // // w = y/far - // uniform float4 _ZBufferParams; - -bool rayIntersectsDepthBF(float zA, float zB, vec2 uv) { - // vec4 uv4 = vec4(uv, 0.0, 0.0); - vec2 uv4 = uv; - float cameraZ = Linear01Depth(texture(gbuffer0, uv4).a) * - 100.0;//_ProjectionParams.z; - float backZ = texture(_BackFaceDepthTex, uv4).r * - 100;//_ProjectionParams.z; - - return zB <= cameraZ && zA >= backZ - _PixelZSize; -} - -float distanceSquared( vec2 a, vec2 b) { a -= b; return dot(a, a); } - - -// Trace a ray in screenspace from rayOrigin (in camera space) pointing in rayDirection (in camera space) -// using jitter to offset the ray based on (jitter * _PixelStride). -// -// Returns true if the ray hits a pixel in the depth buffer -// and outputs the hitPixel (in UV space), the hitPoint (in camera space) and the number -// of iterations it took to get there. -bool traceScreenSpaceRay( vec3 rayOrigin, - vec3 rayDirection, - float jitter, - out vec2 hitPixel, - out vec3 hitPoint, - out float iterationCount, bool debugHalf) { - // Clip to the near plane - float rayLength = ((rayOrigin.z + rayDirection.z * _MaxRayDistance) > -_ProjectionParams.y) ? - (-_ProjectionParams.y - rayOrigin.z) / rayDirection.z : _MaxRayDistance; - vec3 rayEnd = rayOrigin + rayDirection * rayLength; - - // Project into homogeneous clip space - vec4 H0 = P * vec4( rayOrigin, 1.0); - vec4 H1 = P * vec4( rayEnd, 1.0); - - float k0 = 1.0 / H0.w, k1 = 1.0 / H1.w; - - // The interpolated homogeneous version of the camera-space points - vec3 Q0 = rayOrigin * k0, Q1 = rayEnd * k1; - - // Screen-space endpoints - vec2 P0 = H0.xy * k0, P1 = H1.xy * k1; - - // If the line is degenerate, make it cover at least one pixel - // to avoid handling zero-pixel extent as a special case later - P1 += (distanceSquared(P0, P1) < 0.0001) ? 0.01 : 0.0; - - vec2 delta = P1 - P0; - - // Permute so that the primary iteration is in x to collapse - // all quadrant-specific DDA cases later - bool permute = false; - if (abs(delta.x) < abs(delta.y)) { - // This is a more-vertical line - permute = true; delta = delta.yx; P0 = P0.yx; P1 = P1.yx; - } - - float stepDir = sign(delta.x); - float invdx = stepDir / delta.x; - - // Track the derivatives of Q and k - vec3 dQ = (Q1 - Q0) * invdx; - float dk = (k1 - k0) * invdx; - vec2 dP = vec2(stepDir, delta.y * invdx); - - // Calculate pixel stride based on distance of ray origin from camera. - // Since perspective means distant objects will be smaller in screen space - // we can use this to have higher quality reflections for far away objects - // while still using a large pixel stride for near objects (and increase performance) - // this also helps mitigate artifacts on distant reflections when we use a large - // pixel stride. - float strideScaler = 1.0 - min( 1.0, -rayOrigin.z / _PixelStrideZCuttoff); - float pixelStride = 1.0 + strideScaler * _PixelStride; - - // Scale derivatives by the desired pixel stride and then - // offset the starting values by the jitter fraction - dP *= pixelStride; dQ *= pixelStride; dk *= pixelStride; - P0 += dP * jitter; Q0 += dQ * jitter; k0 += dk * jitter; - - float i, zA = 0.0, zB = 0.0; - - // Track ray step and derivatives in a float4 to parallelize - float4 pqk = float4( P0, Q0.z, k0); - float4 dPQK = float4( dP, dQ.z, dk); - bool intersect = false; - - for( i=0; i<_Iterations && intersect == false; i++) { - pqk += dPQK; - - zA = zB; - zB = (dPQK.z * 0.5 + pqk.z) / (dPQK.w * 0.5 + pqk.w); - swapIfBigger( zB, zA); - - hitPixel = permute ? pqk.yx : pqk.xy; - hitPixel *= _OneDividedByRenderBufferSize; - - intersect = rayIntersectsDepthBF( zA, zB, hitPixel); - } - - // Binary search refinement - if( pixelStride > 1.0 && intersect) { - pqk -= dPQK; - dPQK /= pixelStride; - - float originalStride = pixelStride * 0.5; - float stride = originalStride; - - zA = pqk.z / pqk.w; - zB = zA; - - for( float j=0; j<_BinarySearchIterations; j++) - { - pqk += dPQK * stride; - - zA = zB; - zB = (dPQK.z * -0.5 + pqk.z) / (dPQK.w * -0.5 + pqk.w); - swapIfBigger( zB, zA); - - hitPixel = permute ? pqk.yx : pqk.xy; - hitPixel *= _OneDividedByRenderBufferSize; - - originalStride *= 0.5; - stride = rayIntersectsDepthBF( zA, zB, hitPixel) ? -originalStride : originalStride; - } - } - - - Q0.xy += dQ.xy * i; - Q0.z = pqk.z; - hitPoint = Q0 / pqk.w; - iterationCount = i; - - return intersect; -} - -float calculateAlphaForIntersection( bool intersect, - float iterationCount, - float specularStrength, - vec2 hitPixel, - vec3 hitPoint, - vec3 vsRayOrigin, - vec3 vsRayDirection) { - float alpha = min( 1.0, specularStrength * 1.0); - - // Fade ray hits that approach the maximum iterations - alpha *= 1.0 - (iterationCount / _Iterations); - - // Fade ray hits that approach the screen edge - float screenFade = _ScreenEdgeFadeStart; - vec2 hitPixelNDC = (hitPixel * 2.0 - 1.0); - float maxDimension = min( 1.0, max( abs( hitPixelNDC.x), abs( hitPixelNDC.y))); - alpha *= 1.0 - (max( 0.0, maxDimension - screenFade) / (1.0 - screenFade)); - - // Fade ray hits base on how much they face the camera - float eyeFadeStart = _EyeFadeStart; - float eyeFadeEnd = _EyeFadeEnd; - swapIfBigger( eyeFadeStart, eyeFadeEnd); - - float eyeDirection = clamp( vsRayDirection.z, eyeFadeStart, eyeFadeEnd); - alpha *= 1.0 - ((eyeDirection - eyeFadeStart) / (eyeFadeEnd - eyeFadeStart)); - - // Fade ray hits based on distance from ray origin - alpha *= 1.0 - clamp( distance( vsRayOrigin, hitPoint) / _MaxRayDistance, 0.0, 1.0); - - alpha *= intersect; - - return alpha; -} - - - -void main() { - - vec4 specRoughPixel = texture(_CameraGBufferTexture1, texCoord); - vec3 specularStrength = specRoughPixel.a; - - float decodedDepth = Linear01Depth(texture(_CameraDepthTexture, texCoord).r); - - vec3 vsRayOrigin = ScreenSpaceToViewSpace(cameraRay, decodedDepth); - - vec3 decodedNormal = (texture( _CameraGBufferTexture2, texCoord)).rgb * 2.0 - 1.0; - decodedNormal = mul( (mat3)_NormalMatrix, decodedNormal); - - vec3 vsRayDirection = normalize( reflect( normalize( vsRayOrigin), normalize(decodedNormal))); - - vec2 hitPixel; - vec3 hitPoint; - float iterationCount; - - vec2 uv2 = texCoord * _RenderBufferSize; - float c = (uv2.x + uv2.y) * 0.25; - float jitter = fmod( c, 1.0); - - bool intersect = traceScreenSpaceRay( vsRayOrigin, vsRayDirection, jitter, hitPixel, hitPoint, iterationCount, texCoord.x > 0.5); - float alpha = calculateAlphaForIntersection( intersect, iterationCount, specularStrength, hitPixel, hitPoint, vsRayOrigin, vsRayDirection); - hitPixel = lerp( texCoord, hitPixel, intersect); - - // Comment out the line below to get faked specular, - // in no way physically correct but will tint based - // on spec. Physically correct handling of spec is coming... - specRoughPixel = vec4( 1.0, 1.0, 1.0, 1.0); - - return vec4( (texture( _MainTex, hitPixel)).rgb * specRoughPixel.rgb, alpha); - - // vec4 texColor = texture(tex, texCoord); - // gl_FragColor = texColor * (1.0 - intensity) + reflCol * intensity; -} diff --git a/raw/ssr_pass/ssr_pass_kode.vert.glsl b/raw/ssr_pass/ssr_pass_kode.vert.glsl deleted file mode 100644 index 4768a333..00000000 --- a/raw/ssr_pass/ssr_pass_kode.vert.glsl +++ /dev/null @@ -1,25 +0,0 @@ -#version 450 - -#ifdef GL_ES -precision highp float; -#endif - -in vec2 pos; - -out vec2 texCoord; -out vec3 cameraRay; - -uniform mat4 invP; - -const vec2 madd = vec2(0.5, 0.5); - -void main() { - // Scale vertex attribute to [0-1] range - texCoord = pos.xy * madd + madd; - - vec4 ray = vec4(texcoord * 2.0 - 1.0, 1.0, 1.0); - ray = invP * ray; - cameraRay = ray.xyz / ray.w; - - gl_Position = vec4(pos.xy, 0.0, 1.0); -} diff --git a/raw/sss_pass/sss_pass.frag.glsl b/raw/sss_pass/sss_pass.frag.glsl index 0091b734..294a0e18 100644 --- a/raw/sss_pass/sss_pass.frag.glsl +++ b/raw/sss_pass/sss_pass.frag.glsl @@ -41,6 +41,8 @@ precision mediump float; #endif +#include "../compiled.glsl" + uniform sampler2D gbufferD; uniform sampler2D gbuffer0; uniform sampler2D tex; @@ -78,10 +80,8 @@ vec4 SSSSBlur(float sssWidth) { // vec4 g0 = texture(gbuffer0, texCoord); // float depth = 1.0 - g0.a; float depth = texture(gbufferD, texCoord).r * 2.0 - 1.0; - const float znear = 0.1; - const float zfar = 1000.0; - const float projectionA = zfar / (zfar - znear); - const float projectionB = (-zfar * znear) / (zfar - znear); + const float projectionA = cameraPlane.y / (cameraPlane.y - cameraPlane.x); + const float projectionB = (-cameraPlane.y * cameraPlane.x) / (cameraPlane.y - cameraPlane.x); float depthM = projectionB / (depth * 0.5 + 0.5 - projectionA); // Calculate the sssWidth scale (1.0 for a unit plane sitting on the projection window) diff --git a/raw/water_pass/water_pass.frag.glsl b/raw/water_pass/water_pass.frag.glsl index 259e112a..876ef392 100644 --- a/raw/water_pass/water_pass.frag.glsl +++ b/raw/water_pass/water_pass.frag.glsl @@ -9,8 +9,7 @@ precision mediump float; #endif -const float PI = 3.1415926535; -const float TwoPI = (2.0 * PI); +#include "../compiled.glsl" uniform sampler2D gbufferD; uniform sampler2D gbuffer0; @@ -57,7 +56,7 @@ const mat2 octave_m = mat2(1.6,1.2,-1.2,1.6); vec2 envMapEquirect(vec3 normal) { float phi = acos(normal.z); float theta = atan(-normal.y, normal.x) + PI; - return vec2(theta / TwoPI, phi / PI); + return vec2(theta / PI2, phi / PI); } float hash( vec2 p ) { @@ -264,10 +263,8 @@ vec3 getSeaColor(vec3 p, vec3 n, vec3 l, vec3 eye, vec3 dist) { vec3 getPos(float depth) { vec3 vray = normalize(viewRay); - const float znear = 0.1; - const float zfar = 1000.0; - const float projectionA = zfar / (zfar - znear); - const float projectionB = (-zfar * znear) / (zfar - znear); + const float projectionA = cameraPlane.y / (cameraPlane.y - cameraPlane.x); + const float projectionB = (-cameraPlane.y * cameraPlane.x) / (cameraPlane.y - cameraPlane.x); float linearDepth = projectionB / (depth * 0.5 + 0.5 - projectionA); float viewZDist = dot(eyeLook, vray); vec3 wposition = eye + vray * (linearDepth / viewZDist); @@ -288,7 +285,7 @@ vec2 octahedronWrap(vec2 v) { // } vec3 caustic(vec2 uv) { - vec2 p = mod(uv*TwoPI, TwoPI)-250.0; + vec2 p = mod(uv*PI2, PI2)-250.0; float loctime = time * .5+23.0; vec2 i = vec2(p); @@ -323,7 +320,7 @@ vec3 caustic(vec2 uv) { } float causticX(float x, float power, float gtime) { - float p = mod(x*TwoPI, TwoPI)-250.0; + float p = mod(x*PI2, PI2)-250.0; float time = gtime * .5+23.0; float i = p;