Compare commits

...

337 commits

Author SHA1 Message Date
Lubos Lenco 9220e6b57e
Merge pull request #2381 from Skehmatics/fix/abc_deprication
Correct depricated (and now removed) import
2021-11-08 10:14:09 +01:00
Lubos Lenco 15ec53830f
Merge pull request #2379 from MoritzBrueckner/fix-cpu-count
Fix cpu_count() for incomplete PATH var on Windows and improve error handling
2021-11-08 10:13:36 +01:00
Lubos Lenco 946610c793
Merge pull request #2382 from Skehmatics/fix/morph_targets_linux
Fix morph target exporting on Unix-based systems
2021-11-08 09:07:13 +01:00
Moritz Brückner 1430e22a90 Make utils.cpu_count() even more failsafe on Windows 2021-11-07 16:50:20 +01:00
Derek Schmidt 07f4bea206 Fix morph target exporting on Unix-based systems
Also fixes some extra space issues since my editor auto-fixed them
2021-11-06 17:18:36 -07:00
Derek Schmidt 8581a75f9c Correct depricated (and now removed) import 2021-11-06 14:52:05 -07:00
Moritz Brückner 6bb2513001 Fix cpu_count() for incomplete PATH var on Windows and improve error handling 2021-11-06 22:36:04 +01:00
Lubos Lenco f581e91ba9 Move in morph target texture links 2021-11-06 12:34:03 +01:00
Lubos Lenco 263d5853a5
Merge pull request #2375 from QuantumCoderQC/shape-keys
Implementing Shape keys
2021-11-06 12:31:23 +01:00
QuantumCoderQC 1cec287891 Conditional compilation for shape key node 2021-11-02 15:59:40 +01:00
QuantumCoderQC 83a60e7462 Implement shape key conditions 2021-11-02 15:59:15 +01:00
QuantumCoderQC 603d23fbfd Add shape key export option 2021-11-02 15:58:38 +01:00
QuantumCoderQC 03090cafa2 remove shape key textures on clean 2021-11-02 15:56:46 +01:00
QuantumCoderQC a1951bb66e clean up export 2021-11-02 15:56:24 +01:00
QuantumCoderQC 3c0f580f69 fix morph weights lookup 2021-11-02 13:42:09 +01:00
QuantumCoderQC 8116729822 fix pixel count for images, fix max shape keys 2021-11-02 13:42:09 +01:00
QuantumCoderQC 6c0d4bdead fix error when all shape keys muted 2021-11-02 13:42:09 +01:00
QuantumCoderQC 8f733222a9 fix shape keys and multiple uv 2021-11-02 13:42:09 +01:00
QuantumCoderQC 4b65d8d68c do not export muted shape keys 2021-11-02 13:42:09 +01:00
QuantumCoderQC 13a2f4538a move shape key textures to dedicated directory 2021-11-02 13:42:09 +01:00
QuantumCoderQC b7cafbb4e3 add node to set shape key value 2021-11-02 13:42:08 +01:00
QuantumCoderQC d4f9982768 documentation 1 2021-11-02 13:42:08 +01:00
QuantumCoderQC 8e69bea14e appy position scale 2021-11-02 13:42:08 +01:00
QuantumCoderQC 669898fae1 fix normals with shape key and skin 2021-11-02 13:42:08 +01:00
QuantumCoderQC 8df6fc9176 fix shape key with skinning 2021-11-02 13:42:08 +01:00
QuantumCoderQC edeaf488b9 remove debug prints 2021-11-02 13:42:08 +01:00
QuantumCoderQC cb2cf0c9b4 keep both vert colors and shape keys 2021-11-02 13:42:07 +01:00
QuantumCoderQC 4522055fac use vec4 uniform array instead of float array 2021-11-02 13:42:07 +01:00
QuantumCoderQC 1d0eaf9371 debug prints 2021-11-02 13:42:07 +01:00
QuantumCoderQC e562d2515e export with optimized texture size 2021-11-02 13:42:07 +01:00
QuantumCoderQC 8e7d8a4f56 additional uniforms 2021-11-02 13:42:06 +01:00
QuantumCoderQC 6e153569dc make attrib skin fix 2021-11-02 13:42:06 +01:00
QuantumCoderQC d01c55fcd4 mordify to export shape keys 2021-11-02 13:42:06 +01:00
QuantumCoderQC 3814cd9da7 methods to get if vertex colors and if shapekey present 2021-11-02 13:42:06 +01:00
QuantumCoderQC 0636eb85c6 add morph attribute to shaders 2021-11-02 13:42:06 +01:00
QuantumCoderQC 2f77c5b060 modify normals calculation in skinning for morphing 2021-11-02 13:42:06 +01:00
QuantumCoderQC 26a0fb8c5a add morph target to make_shader.py 2021-11-02 13:42:05 +01:00
QuantumCoderQC 8c034655be implement morph_target.glsl 2021-11-02 13:42:05 +01:00
QuantumCoderQC 6f9b51a57a move make_morph_target 2021-11-02 13:42:05 +01:00
QuantumCoderQC 34243ed74c implement make_morph_target 2021-11-02 13:42:05 +01:00
QuantumCoderQC fb004f5dcc make attributes for shadow maps and depth 2021-11-02 13:42:05 +01:00
QuantumCoderQC 867100d151 make shader attribute for morph targets 2021-11-02 13:42:05 +01:00
QuantumCoderQC 09433f2a19 init morph target glsl 2021-11-02 13:42:05 +01:00
QuantumCoderQC 10d41e5eca init make morph target 2021-11-02 13:42:05 +01:00
QuantumCoderQC b7941d2fa8 export shape keys as texture 2021-11-02 13:42:04 +01:00
Lubos Lenco 93563dbba5 Bump version 2021-11-02 10:29:01 +01:00
Lubos Lenco 9094154440
Merge pull request #2367 from MoritzBrueckner/fix-library-textures
Fix exporting textures from linked libraries
2021-11-01 21:51:20 +01:00
Lubos Lenco 6ad9941d43
Merge pull request #2374 from tong/fix-uniformsmanager-register
Fix switch expression
2021-11-01 20:53:23 +01:00
Lubos Lenco 326d3bbf0d
Merge pull request #2373 from QuantumCoderQC/pick-rb-imp
add normal output for pick rb node
2021-11-01 20:52:23 +01:00
Lubos Lenco b2566a8249
Merge pull request #2372 from QuantumCoderQC/decal-fix
Decal fix
2021-11-01 20:51:37 +01:00
tong 18c5636d03 Fix switch expression 2021-11-01 02:44:56 +01:00
QuantumCoderQC 07cfa40727 add normal output for pick rb node 2021-10-31 23:06:11 +01:00
QuantumCoderQC e06ca4b9e7 get material from decal object too 2021-10-31 23:03:25 +01:00
QuantumCoderQC ec1f25cfec add support for decal object uniforms 2021-10-31 23:02:45 +01:00
QuantumCoderQC 1dc733ca88 dont cast decal objects to mesh objects 2021-10-31 20:58:27 +01:00
Lubos Lenco f22542a0b4
Merge pull request #2369 from t3du/master
add Between and Not Equal to Compare Logic
2021-10-28 11:40:37 +02:00
t3du c97c738870
add Between and Not Equal to Compare Logic 2021-10-27 10:35:58 -03:00
t3du 130e470290
add Between and Not Equal to Compare Logic 2021-10-27 10:32:58 -03:00
Lubos Lenco fabb4c0c3c
Merge pull request #2368 from t3du/master
add Not Equal to Gate Node
2021-10-26 16:50:04 +02:00
t3du ca03897811
Update LN_gate.py 2021-10-26 10:08:46 -03:00
t3du 6e255bcb05
add Not Equal to Gate node logic 2021-10-26 10:05:11 -03:00
t3du f6dfc43dde
add Not Equal to Gate Node 2021-10-26 10:04:31 -03:00
Moritz Brückner 36e42ad736 Fix exporting textures from linked libraries 2021-10-25 19:43:13 +02:00
Lubos Lenco 3df2916f5b
Merge pull request #2366 from MoritzBrueckner/fix-collection-export
Fix exporting collections with parent object in another collection
2021-10-25 10:00:41 +02:00
Moritz Brückner 59aca7ea41 Fix exporting collections with parent object in another collection 2021-10-23 17:12:22 +02:00
Lubos Lenco 297f7cdfca
Merge pull request #2363 from t3du/master
add get object uid nodes / agg args to call haxe static function / add Between to gate node
2021-10-22 09:18:51 +02:00
t3du 176ab43fd0
fix return with correct screen cords
solution from:

https://forums.armory3d.org/t/worldtoscreencoord-draw-debug-lines-with-2d-api/3467/9
2021-10-20 16:51:11 -03:00
t3du 6b1333902c
change version and add NodeReplacement 2021-10-20 11:15:06 -03:00
t3du 89ff1c29fc
change node version and add NodeReplacement 2021-10-20 11:12:01 -03:00
t3du 086c05eba2
change node version and add NodeReplacement 2021-10-20 11:11:44 -03:00
t3du 3e6e1e4bd1
change info 2021-10-20 09:49:00 -03:00
t3du 977b2db376
change info 2021-10-20 09:48:17 -03:00
t3du ebb3076431
add between logic condition 2021-10-19 21:19:16 -03:00
t3du 826dbd786d
add property0 between 2021-10-19 21:18:02 -03:00
t3du 4fe8f683dc
add args to the call of haxe static function 2021-10-19 19:11:10 -03:00
t3du b9b5c65141
add args to the haxe static function 2021-10-19 19:07:49 -03:00
t3du ec3054cdeb
add uid nodes 2021-10-18 17:55:44 -03:00
t3du d3b006c873
add nodes for uid 2021-10-18 17:53:09 -03:00
Lubos Lenco 9c931aff56
Merge pull request #2362 from tong/fix-doc-generation
Fix dox when using special characters
2021-10-15 16:02:15 +02:00
tong 622b8a275f Fix dox when using special characters 2021-10-14 17:35:23 +02:00
Lubos Lenco ea9b01e733
Merge pull request #2361 from SaxonGao-SZ/master
Use project name for web title
2021-10-14 12:35:03 +02:00
SaxonGao-SZ 34897c9747
feature: Use project name for web title
feature: Use project name for web title
2021-10-13 20:52:36 +08:00
Lubos Lenco c1f98dc668
Merge pull request #2359 from t3du/master
add logic nodes: set/get canvas font size and set progress bar color
2021-10-12 16:20:55 +02:00
t3du 67d511568f
add new nodes 2021-10-11 17:41:03 -03:00
t3du b8319fe28b
add new nodes 2021-10-11 17:38:18 -03:00
t3du d5e0208fd6
add getCanvasFontSize and fix setCanvasFontSize 2021-10-11 17:35:56 -03:00
Lubos Lenco 0fadefab69
Merge pull request #2357 from QuantumCoderQC/fix-random-color
random color node fix
2021-10-08 10:27:07 +02:00
QuantumCoderQC 50a1994867 random color node fix 2021-10-07 19:18:43 +02:00
Lubos Lenco b9a220fbcb
Merge pull request #2356 from MoritzBrueckner/fix-world-geometry
Fix Geometry and Texture Coordinate nodes for world shaders
2021-10-06 10:17:43 +02:00
Moritz Brückner 5e1105f51d Fix some Texture Coordinate node outputs for world shaders 2021-10-05 19:03:08 +02:00
Moritz Brückner b060953a7e Further fixes for the geometry node on world shaders 2021-10-05 18:41:45 +02:00
Moritz Brückner a4d09936d6 Fix world normals and position (adjust to Blender) 2021-10-05 18:40:39 +02:00
Lubos Lenco 786e68e475
Merge pull request #2355 from MoritzBrueckner/fix-deprecated-nodes
Fix deprecated nodes and improve deprecation handling
2021-10-05 11:02:00 +02:00
Lubos Lenco c7cba9d792
Merge pull request #2353 from MoritzBrueckner/debug-renderpath
Allow to set default renderpath for debugging
2021-10-05 11:01:00 +02:00
Lubos Lenco d9ddb5f4a3
Merge pull request #2352 from MoritzBrueckner/fix-cmft-cpu-count
Fix CPU count parameter for cmft
2021-10-05 09:39:18 +02:00
Moritz Brückner 8e8e8f586d Assert correct category for deprecated nodes 2021-10-05 00:03:48 +02:00
Moritz Brückner e8fa3b1b9a Whitespace cleanup 2021-10-04 23:57:15 +02:00
Moritz Brückner 7289b95a48 Don't show empty "Deprecated" node category menu 2021-10-04 23:56:35 +02:00
Moritz Brückner 7f5d382131 Allow to set default renderpath for debugging 2021-10-03 22:40:29 +02:00
Lubos Lenco ea02600d14 Bump version 2021-10-03 20:15:36 +02:00
Lubos Lenco a2c33b2f6a
Merge pull request #2351 from MoritzBrueckner/debug-console-trace
Debug console: make log output more readable
2021-09-28 13:00:51 +02:00
Moritz Brückner c40fd26c20 Fix CPU count parameter for cmft 2021-09-27 23:13:30 +02:00
Moritz Brückner 308ab2d77b Add some tooltips to the debug console 2021-09-27 16:02:29 +02:00
Moritz Brückner d979e4fde9 Debug console: make spacing between log lines smaller 2021-09-27 15:53:08 +02:00
Moritz Brückner 1c237dd6c3 Make printing with PosInfos configurable in the debug console UI 2021-09-27 15:46:35 +02:00
Moritz Brückner 152959699a Debug console: optionally omit PosInfos when printing 2021-09-27 15:43:03 +02:00
Moritz Brückner 259c375fda Whitespace cleanup 2021-09-27 15:41:53 +02:00
Lubos Lenco b579c8a15d
Merge pull request #2350 from MoritzBrueckner/fix-nodes-reload
Fix add-on reloading when there are new unimported node files
2021-09-27 11:03:02 +02:00
Lubos Lenco 73a352b76e
Merge pull request #2349 from MoritzBrueckner/global-canvas-scale-nodes
Add nodes to get/set global canvas scale
2021-09-27 11:02:16 +02:00
Moritz Brückner 957c5371c8 Fix add-on reloading when there are new unimported node files 2021-09-26 22:34:09 +02:00
Moritz Brückner 962f8a79a5 Add nodes to get/set global canvas scale 2021-09-26 22:31:56 +02:00
Lubos Lenco 8604e670c3
Merge pull request #2342 from tong/log-message-improvements
Improve log messages
2021-09-26 09:51:06 +02:00
tong d1d266a0f0 Improve log messages 2021-09-23 11:30:46 +02:00
Lubos Lenco 62d399e01a
Merge pull request #2338 from tong/no-haxe-times-param
Remove haxe --times param per default
2021-09-22 19:21:52 +02:00
tong 128efcc0ef Use haxe_times prefs field 2021-09-21 14:56:12 +02:00
tong 3920c475be No haxe times param 2021-09-21 14:43:47 +02:00
Lubos Lenco e3a55db813
Merge pull request #2339 from tong/fix-local-sdk-path
Fix invalid fp when using local sdk
2021-09-21 11:42:22 +02:00
Lubos Lenco 597c291da7
Merge pull request #2336 from QuantumCoderQC/tweenNodes
Introducing Tween nodes
2021-09-19 10:25:45 +02:00
Lubos Lenco 16976cdc5b
Merge pull request #2335 from MoritzBrueckner/fix-trait-fakeuser
Fix fake user export for traits
2021-09-18 11:05:19 +02:00
tong d1fe4d6686 Fix invalid fp when using local sdk 2021-09-17 13:21:36 +02:00
QuantumCoderQC ca966588ff add documentation 2021-09-13 23:10:31 +02:00
QuantumCoderQC 0ce0c5c115 change socket order 2021-09-13 22:48:32 +02:00
QuantumCoderQC 05465bcea1 minor clean-up 2021-09-13 22:30:39 +02:00
Moritz Brückner 77815d95ed Fix fake user export for traits 2021-09-13 21:29:21 +02:00
Lubos Lenco 156f1f433a
Merge pull request #2334 from tong/fix-throw-assert-error
Fix assert throw error
2021-09-13 10:03:11 +02:00
Lubos Lenco 5b4d24f067
Merge pull request #2332 from MoritzBrueckner/fix-realtime-postprocess
Fix realtime postprocess & cleanup arm.lib.make_datas
2021-09-12 08:05:29 +02:00
tong c24389813f Fix assert throw error 2021-09-12 00:03:57 +02:00
Lubos Lenco c398c1181e
Merge pull request #2331 from tong/fix-background-mode-open-build-dir
Do not open build directory in background mode
2021-09-11 13:49:20 +02:00
Lubos Lenco 7d9d5ac50f
Merge pull request #2329 from MoritzBrueckner/fix-ArmNodeAddInputOutputButton
Fix ArmNodeAddInputOutputButton
2021-09-10 09:12:59 +02:00
QuantumCoderQC eb5ef99309 create tween vector node 2021-09-10 01:08:31 +02:00
QuantumCoderQC 62294c8bb4 create tween rotation node 2021-09-10 01:08:12 +02:00
QuantumCoderQC ed6aa96fb0 create tween float node 2021-09-10 01:07:52 +02:00
Lubos Lenco f3fcb2846e
Merge pull request #2326 from onelsonic/patch-2
Update props_ui.py
2021-09-08 08:51:07 +02:00
Lubos Lenco afeecd4df1
Merge pull request #2325 from onelsonic/patch-1
Update props.py
2021-09-08 08:49:39 +02:00
Moritz Brückner de72bbf3a4 Cleanup arm.lib.make_datas 2021-09-07 23:40:52 +02:00
Moritz Brückner ecc4317919 Fix realtime postprocess: use compositor defines also for non-compositor pass shaders 2021-09-07 22:19:12 +02:00
tong 667e73c867 Do not open build directory in background mode 2021-09-07 10:15:11 +02:00
Moritz Brückner 56d84b08f4 Cleanup 2021-09-06 18:23:21 +02:00
Moritz Brückner adb2468245 Fix ArmNodeAddInputOutputButton 2021-09-06 18:22:38 +02:00
onelsonic abc52627a3
Update props_ui.py
change the UI to reflect common order to setup Android SDK values
https://developer.android.com/guide/topics/manifest/uses-sdk-element.html
android:minSdkVersion="integer"
then
android:targetSdkVersion="integer"
then
android:maxSdkVersion="integer"
2021-09-06 15:33:16 +02:00
onelsonic 68ef00b668
Update props.py
proposed UI descriptions changes to reflect current Android SDK
https://developer.android.com/guide/topics/manifest/uses-sdk-element.html
and updating default SDK values to Android Oreo
2021-09-06 15:26:13 +02:00
Lubos Lenco 55cfead084 Bump version 2021-09-06 09:22:25 +02:00
Lubos Lenco fe6d1fa372
Merge pull request #2320 from QuantumCoderQC/AddTraitFix
Add trait fix
2021-09-06 09:02:22 +02:00
Lubos Lenco 974181e75e
Merge pull request #2319 from MoritzBrueckner/assert-string-expr
Allow to use more complex string expressions as assert() messages
2021-09-05 11:55:28 +02:00
Lubos Lenco a4ab1dcc98
Merge pull request #2318 from QuantumCoderQC/navMeshImprove
Small improvement to Navigation nodes
2021-09-04 15:15:44 +02:00
QuantumCoderQC ccbc946bf1 Add warning if trait is already applied on the object 2021-09-03 23:42:43 +02:00
QuantumCoderQC fddef0983c Modify Add Trait Node To Accept String Trait 2021-09-03 23:37:13 +02:00
Moritz Brückner e715bf0108 Allow to use more complex string expressions as assert() messages 2021-09-03 22:21:57 +02:00
QuantumCoderQC 214b84fcf7 Modify NavAgent Trait 2021-09-03 21:00:58 +02:00
QuantumCoderQC 32b745f829 Modify Navigation Go To Location Node 2021-09-03 21:00:33 +02:00
QuantumCoderQC 8caf859db9 Create and implement Get Nav Agent Data Node 2021-09-03 20:59:49 +02:00
Lubos Lenco d6692efbdc
Merge pull request #2316 from onelsonic/patch-1
correcting Haxe Warnings : Std.is is deprecated. Use Std.isOfType ins…
2021-09-03 12:37:23 +02:00
onelsonic c77eea93fa
correcting Haxe Warnings : Std.is is deprecated. Use Std.isOfType instead.
correcting Warning : Std.is is deprecated. Use Std.isOfType instead.
2021-09-03 08:36:13 +02:00
Lubos Lenco e0cbf1b332
Merge pull request #2315 from MoritzBrueckner/fix-ppcomp-warnings
Fix "Uniform PPCompXX not found" warnings
2021-09-02 20:05:11 +02:00
Moritz Brückner b7024a5854 Whitespace cleanup 2021-09-02 17:18:59 +02:00
Moritz Brückner 44d8325001 Fix "Uniform PPCompXX not found" warnings 2021-09-02 17:14:18 +02:00
Lubos Lenco 052c7b00c5
Merge pull request #2309 from MoritzBrueckner/assert
Add assert() macro
2021-09-01 09:16:46 +02:00
Lubos Lenco 675618e529
Merge pull request #2308 from tong/html5-server-settings
HTML5 webserver log, port prefs
2021-08-30 09:10:26 +02:00
Moritz Brückner a6b67e4e00 Improve assert() docstring 2021-08-27 14:34:59 +02:00
Moritz Brückner 4b75a896d6 Auto-import assert() macro 2021-08-27 01:32:38 +02:00
Moritz Brückner ecddc0af92 Add assert() macro 2021-08-27 01:26:00 +02:00
tong e05d556552 Webserver log, port prefs 2021-08-26 10:01:35 +02:00
Lubos Lenco 85edde6d24
Merge pull request #2305 from niacdoial/newrotation
Added a rotation node-socket
2021-08-26 09:53:25 +02:00
niacdoial 703c618c7d
Misc. patches before merge [watch for amends]
- fixed type/import bugs in RotationNode.hx and SeparateRotationNode.hx
2021-08-21 16:07:34 +02:00
Lubos Lenco df715db381
Merge pull request #2307 from MoritzBrueckner/krom-es6
Switch to ES6 on krom target and fix 'armory' define
2021-08-20 10:35:19 +02:00
Lubos Lenco a8a44df249
Merge pull request #2306 from MoritzBrueckner/various-node-fixes
Various node fixes
2021-08-20 10:34:22 +02:00
Moritz Brückner 1eb087125f Fix 'armory' define 2021-08-19 22:27:09 +02:00
Moritz Brückner 2347348504 Switch to ES6 on krom target
See https://github.com/armory3d/armorcore/issues/32
2021-08-19 22:26:30 +02:00
Moritz Brückner 3e22cb3375 Update ArmOpenNodeWikiEntry to new wiki page structure 2021-08-19 22:02:01 +02:00
Moritz Brückner 4e526a6dd3 Various smaller node fixes 2021-08-19 21:59:52 +02:00
Moritz Brückner 2630c159d8 Align new custom sockets to Blender UI 2021-08-19 21:58:43 +02:00
niacdoial a67452802d
React to commit 14d33ee0530e5db640e0fafb8604662333c1b066 in iron. 2021-08-19 11:31:12 +02:00
niacdoial f892fdfd8a
Merge months of changes into 'newrotation' branch
(and homogeneised the contents of said branch in the process)
(plus a couple bugfixes, because what else)
2021-08-18 17:04:00 +02:00
Lubos Lenco 29785172f4
Merge pull request #2304 from tong/just-run
Shift click play just run
2021-08-18 11:03:21 +02:00
niacdoial 1d0a6d7955
Did the haxe part of the ongoing update.
Also fixed a ton of bugs in the python part.
NOTE: this requires a yet-to-be-done commit to Iron to work.
2021-08-17 19:29:35 +02:00
tong 7b6d3536e3 Shift click play just run 2021-08-17 17:47:04 +02:00
Lubos Lenco f017d68ed7
Merge pull request #2303 from MoritzBrueckner/fix-nishita-LUT
Fix nishita artifacts and improve LUT value range
2021-08-17 09:30:22 +02:00
Moritz Brückner 1de97e1898 Minor cleanup 2021-08-15 00:30:41 +02:00
Moritz Brückner ca96174b6b Fix nishita sky artifacts on some GPUs
It could happen that values returned by dot() were slightly larger than 1 or less than -1 due to precision errors, but acos() is undefined outside of [-1, 1]. This would lead to NaN values on some GPUs, causing visible artifacts.
2021-08-14 23:45:33 +02:00
niacdoial bd67667a6e Added node replacement routines for previous commits
(and fixed a couple bugs along the way)
2021-08-12 20:58:35 +02:00
Lubos Lenco 2910fcf1a8
Merge pull request #2302 from MoritzBrueckner/fix-addon-reload
Fix accidental module reloading caused by name conflicts of global vars
2021-08-11 18:25:44 +02:00
Moritz Brückner c56a0c3a72 Fix accidental module reloading caused by name conflicts of global vars 2021-08-11 14:32:21 +02:00
Moritz Brückner d610cc6a2f Fix value range in Nishita LUT 2021-08-09 16:51:26 +02:00
Lubos Lenco 34f6e0fc7c Kha update 2021-08-08 13:02:19 +02:00
Lubos Lenco 45e3c1c138
Merge pull request #2298 from MoritzBrueckner/reload-addon
Implement basic addon reloading
2021-08-07 19:06:44 +02:00
Lubos Lenco c49bece460
Merge pull request #2299 from MoritzBrueckner/fix-armsdk-ci
Fix armsdk CI
2021-08-07 19:03:57 +02:00
Lubos Lenco 2ef4f19eb7
Merge pull request #2300 from N8n5h/volumetric-light
fix volumetric lights not working when shadow map atlas is enabled
2021-08-07 16:54:08 +02:00
N8n5h 5509096158 fix volumetric lights not working with shadow map atlas enabled
Added missing code so the shader refers to the correct variable when
using shadow map atlasing.
2021-08-06 18:34:39 -03:00
Moritz Brückner 2d3e15064a Fix armsdk CI
Updated Logic.hx with changes made in make_logic.py. Is this file even used somewhere?
2021-08-06 22:33:20 +02:00
Lubos Lenco 386d2a3bf4 Bump version 2021-08-06 11:10:28 +02:00
Lubos Lenco 7607cc8ccd
Merge pull request #2296 from tong/physics-drag-limit-props
PhysicsDrag trait limit props
2021-08-05 22:13:58 +02:00
Lubos Lenco 52a594c910
Merge pull request #2297 from MoritzBrueckner/ui-font-fix
Canvas: fix using non-default fonts
2021-08-05 22:13:30 +02:00
Moritz Brückner 4c4bebcf4f Canvas: fix using non-default fonts 2021-08-04 23:35:54 +02:00
Moritz Brückner ea8c13686c Implement basic addon reloading 2021-08-04 22:56:11 +02:00
tong 4c34c2a5ce PhysicsDrag trait limit props 2021-08-04 22:09:54 +02:00
Lubos Lenco b664e3a010
Merge pull request #2287 from MoritzBrueckner/fix-thread-polling
Continue thread polling after an exception in the done callback
2021-08-02 08:23:37 +02:00
Lubos Lenco 60d0f443f8
Merge pull request #2286 from MoritzBrueckner/fix-getfp-drive-root
Windows: fix arm.utils.get_fp() if the project path is a drive root
2021-08-01 16:50:22 +02:00
Lubos Lenco aeffd76b75
Merge pull request #2285 from tong/arcball-improvements
ArcBall axis trait prop
2021-07-31 20:07:16 +02:00
Lubos Lenco 1532a332ff
Merge pull request #2284 from QuantumCoderQC/TerrainTextureFix
Fix terrain export for Krom
2021-07-30 08:25:01 +02:00
Moritz Brückner 4fc1f38b3b Continue thread polling after an exception in the done callback 2021-07-27 00:21:27 +02:00
Moritz Brückner 5f2dce140e Windows: fix arm.utils.get_fp() if the project path is a drive root 2021-07-26 23:40:47 +02:00
tong 13cf22ecc2 ArcBall axis trait prop 2021-07-26 19:26:17 +02:00
QuantumCoderQC 9f7af6a1cd Change terrain material bump output to normal 2021-07-26 18:02:23 +02:00
QuantumCoderQC 9ec425f6ae get file name only if specifiedin node 2021-07-26 13:50:52 +02:00
QuantumCoderQC 3bad878650 Add exception for null data 2021-07-26 13:48:44 +02:00
Lubos Lenco ce75516c59
Merge pull request #2283 from MoritzBrueckner/live-patch
Live patch: refactor and logic nodes support
2021-07-26 09:00:31 +02:00
Moritz Brückner bce14549ac Fix LN live patch when using multiple instances of the same logic tree 2021-07-26 00:02:54 +02:00
Moritz Brückner ee208ece18 Live patch: ignore two more operators 2021-07-25 20:20:43 +02:00
Moritz Brückner 549040fc09 Fix publishing with live patch enabled 2021-07-25 20:13:47 +02:00
Moritz Brückner 62ff11747b Update nodes for live patch support (merge conflict cleanup) 2021-07-25 19:50:51 +02:00
Moritz Brückner 6a3045477f Fix physics node if arm_physics is false + cleanup 2021-07-25 18:36:36 +02:00
Moritz Brückner 8a758bbe26 Only stop live patch session if live patch was active 2021-07-25 17:07:51 +02:00
Moritz Brückner ffee6dc521 Fix error caused by resolving merge conflict 2021-07-25 17:02:29 +02:00
Moritz Brückner cca82a69bf
Merge branch 'master' into live-patch 2021-07-25 16:47:32 +02:00
Moritz Brückner c52d25e471 arm_props.py: cleanup and fix docstrings 2021-07-25 00:15:32 +02:00
Moritz Brückner cf1dd0ac71 Live patch: fix creation of node outputs 2021-07-24 23:07:22 +02:00
Moritz Brückner 8e1aa2fb0d Live patch: fix creation of nodes without properties 2021-07-24 23:06:53 +02:00
Moritz Brückner 6d2b825dd5 Fix LN replacement and take arm_watch and arm_logic_id properties into account 2021-07-24 20:24:04 +02:00
Moritz Brückner 6050bceaf1 Fix nodes with "..._get" property names 2021-07-24 20:17:29 +02:00
Moritz Brückner dbb0764ca7 Fix typo 2021-07-24 20:16:54 +02:00
Moritz Brückner 568f4f3a6d Remove unnecessary privateAccess metadata 2021-07-24 19:51:01 +02:00
Moritz Brückner 0a58279756 Live patch: add more ignored operators 2021-07-24 19:49:35 +02:00
Moritz Brückner ff5fd9c7ed Fix generator usage 2021-07-24 19:49:19 +02:00
Moritz Brückner 0456d5f080 Remove legacy code stub 2021-07-24 18:21:06 +02:00
Moritz Brückner af520b518e Fix compatibility for frames and reroutes 2021-07-24 13:54:48 +02:00
Moritz Brückner 82c7302dd9 Live patch: simplify node deletion code 2021-07-24 13:54:31 +02:00
Moritz Brückner 2b6a7a4f78 Live patch: add another ignored operator 2021-07-24 13:37:13 +02:00
Moritz Brückner 7eced3b4f5 Live patch: don't try to re-export if live patch is not active 2021-07-24 13:36:55 +02:00
Moritz Brückner aa21402221 Live patch: handle deletion of node links 2021-07-24 13:36:16 +02:00
Lubos Lenco 97cc737aff
Merge pull request #2280 from MoritzBrueckner/fix-multi-uv
Fix usage of multiple UV maps on mobile render path
2021-07-22 09:28:00 +02:00
Moritz Brückner 256d27e289 Fix vector/color handling for logic node live patching 2021-07-21 22:58:21 +02:00
Moritz Brückner 9ff726bac1 Fix usage of multiple UV maps on mobile render path 2021-07-21 00:01:27 +02:00
Moritz Brückner 5276711094 Live patch: add more ignored operators 2021-07-20 23:34:06 +02:00
Moritz Brückner 4f13ebc439 Live patch: fix updates of object sockets 2021-07-20 23:33:47 +02:00
Moritz Brückner 1edc7a9469 Live patch: support for adding nodes 2021-07-20 20:53:37 +02:00
Moritz Brückner 3ed915b654 Cleanup LivePatch.hx 2021-07-20 14:28:42 +02:00
Moritz Brückner e930da7388 Live patch: support for node deletion 2021-07-20 14:25:35 +02:00
Moritz Brückner c65764be99 Cleanup LivePatch.hx 2021-07-20 14:24:00 +02:00
Moritz Brückner bdee03873b Live patch: support for node duplication 2021-07-18 22:29:18 +02:00
Lubos Lenco 1b758ec32c
Merge pull request #2276 from tong/fix-fetch-prop-type
Fix fetch trait prop type
2021-07-18 09:12:44 +02:00
tong cfa941eab4 Fix fetch trait prop type 2021-07-17 18:49:37 +02:00
niacdoial 9b1393ea41
Make rotation its own socket in logic nodes, add rotation-specific math node
(part 1: conversion code not developed)
2021-07-11 11:28:00 +02:00
Lubos Lenco bfde181da6
Merge pull request #2268 from MoritzBrueckner/fix-gbuffer2-overlay
Fix visual artifacts caused by invalid gbuffer2 on OpenGL
2021-07-11 10:48:00 +02:00
Lubos Lenco a59c789528
Merge pull request #2266 from QuantumCoderQC/staticTargetFix
Static target fix
2021-07-11 10:46:52 +02:00
Lubos Lenco caa03b59c5
Merge pull request #2263 from knowledgenude/master
Fix nullability
2021-07-11 10:42:12 +02:00
Lubos Lenco ff93e5f824
Merge pull request #2262 from tong/replace-deprecated-std-is
Replace deprecated Std.is with Std.isOfType
2021-07-11 10:41:53 +02:00
Moritz Brückner b3162d8f6e Fix visual artifacts caused by invalid gbuffer2 on OpenGL 2021-07-10 22:41:19 +02:00
Moritz Brückner 823cc379b6 Live patch: ignore more operators & fix code style 2021-07-10 22:08:19 +02:00
Moritz Brückner ee194a1806 Live patch: support for socket default values 2021-07-10 21:50:30 +02:00
Moritz Brückner 96aa0ee890 Use custom sockets for default data types
This allows to listen for socket updates for the live patch system
2021-07-10 21:46:44 +02:00
QuantumCoderQC c0333db44f Fix typos. Fix fast float 2021-07-10 19:22:26 +02:00
QuantumCoderQC e0ff256f40 make boolean objects nullable for static targets 2021-07-10 19:20:56 +02:00
Henrique 28d21bf35f Fix nullability 2021-07-09 21:42:12 -03:00
tong b452acaebb Replace deprecated Std.is with Std.isOfType 2021-07-09 22:04:50 +02:00
Lubos Lenco 3c88ddcb23
Merge pull request #2260 from knowledgenude/master
Improved Input Map and added nodes to it
2021-07-09 07:52:24 +02:00
Henrique db5aed465e Add static method to remove keys 2021-07-08 16:10:45 -03:00
Henrique 55cf2896a2 Cleanup 2021-07-07 17:11:56 -03:00
Henrique daee309ad8 Add Remove Input Map Key node 2021-07-07 17:08:01 -03:00
Henrique 43a574eb15 Remove unnecessary read access 2021-07-07 16:51:07 -03:00
Henrique 919512fad0 Improve InputMap and add nodes to it 2021-07-07 16:33:20 -03:00
Lubos Lenco 14b18408aa
Merge pull request #2254 from N8n5h/fix-atlas-ui
Fix compiling error with shadow map atlas shadow size option
2021-07-06 23:35:35 +02:00
N8n5h 801668a0c2 Fix compiling error with shadow map atlas shadow size option
Added 512 option to Inc so it doesn't fails compilation because of
missing option as explained here
https://github.com/armory3d/armory/issues/2252#issue-937328497
2021-07-06 10:39:07 -03:00
Lubos Lenco dc6753c2ca Bump version 2021-07-06 10:46:44 +02:00
Lubos Lenco 6dd27bfeed
Merge pull request #2251 from MoritzBrueckner/ui-canvas-font
UI Canvas: use font from asset for all element types
2021-07-06 09:20:43 +02:00
Moritz Brückner e7da337530 UI Canvas: use font from asset for all element types 2021-07-05 19:25:47 +02:00
Lubos Lenco 50c8ceec1e
Merge pull request #2247 from QuantumCoderQC/LogicNodeFix
Add check to LogicNode run
2021-07-04 23:19:57 +02:00
Moritz Brückner a6ec652d5f Fix identity node replacement for Blender 2.93 2021-07-03 22:49:19 +02:00
QuantumCoderQC cb800729d2 Add check
Add a check to see if input socket is linked to correct output socket
2021-07-03 20:12:10 +02:00
Moritz Brückner 4e19ddfeb0 Do not send live patch events if live patch isn't running 2021-07-03 19:47:09 +02:00
Moritz Brückner b2153dbcd2 Cleanup 2021-07-03 19:45:35 +02:00
Moritz Brückner 4387d774cc Live patch: add support for node property updates 2021-07-03 19:45:05 +02:00
Lubos Lenco 738a217001
Merge pull request #2245 from QuantumCoderQC/NavMeshNodeFix
A few small fixes to UniformsManager calss and go to location node
2021-07-02 19:24:34 +02:00
QuantumCoderQC 9e493f6e79 do not remove trait at init 2021-07-01 15:33:28 +02:00
QuantumCoderQC 7504d5d92b Change input type from shader to vector 2021-07-01 15:32:53 +02:00
Lubos Lenco e308d9058b
Merge pull request #2242 from knowledgenude/master
Add "Parent Relative" option to location nodes
2021-06-29 08:44:17 +02:00
Henrique c7e1f5d0a9 Fix nodes 2021-06-28 16:12:05 -03:00
Moritz Brückner 202138304a Live patch: add support for creating connections between nodes 2021-06-28 12:17:13 +02:00
Lubos Lenco 8c0b6ba13e
Merge pull request #2240 from QuantumCoderQC/RelativePhysConstraint
Add option for relative physics constraint
2021-06-28 11:44:39 +02:00
Henrique 7515a20d93 Keep compatibility 2021-06-27 20:47:55 -03:00
Henrique 3d49edee71 Add Subtract option to World Vector to Local Space node 2021-06-27 19:13:00 -03:00
Henrique 34b816b4d9 Remove default value 2021-06-27 18:27:16 -03:00
Henrique 13c1e0508d Fix for null parent 2021-06-27 18:26:13 -03:00
Henrique f940e8566e Remove unused quat 2021-06-27 18:21:59 -03:00
Henrique dfad6902af Add Relative Parent option to location nodes 2021-06-27 18:18:04 -03:00
Moritz Brückner 441f42383e Correctly stop live patching after player terminates 2021-06-27 22:35:37 +02:00
Lubos Lenco 616a0e230d
Merge pull request #2239 from QuantumCoderQC/MatParamPerObject
Set material parameter on per object basis
2021-06-26 12:17:40 +02:00
Lubos Lenco 20540ccb11
Merge pull request #2238 from MoritzBrueckner/background-mode
Fix threading and publishing in background mode
2021-06-25 08:57:16 +02:00
QuantumCoderQC 31a2c9c4d3 Modify export of physics constraint 2021-06-24 23:13:29 +02:00
QuantumCoderQC ac48fd0bc9 Implement adding of relative physics constraint 2021-06-24 23:12:51 +02:00
QuantumCoderQC f2cf3bdeda Add new property to set if constraint is relative 2021-06-24 23:12:15 +02:00
QuantumCoderQC 112c00a649 Modify unifroms manager 2021-06-24 21:33:53 +02:00
QuantumCoderQC ee43724b97 Change imports 2021-06-24 21:33:21 +02:00
QuantumCoderQC 5a2f952f89 Add uniforms manager to mesh objects 2021-06-24 21:32:48 +02:00
QuantumCoderQC 936f11ed8e Move uniforms manager back to armory 2021-06-24 18:19:33 +02:00
QuantumCoderQC eab7fcbbe6 update node implementations 2021-06-24 18:13:13 +02:00
QuantumCoderQC 5a591fa15a set default vector and float parameter 2021-06-24 18:13:13 +02:00
QuantumCoderQC 975ec76f38 Set default image file 2021-06-24 18:13:13 +02:00
QuantumCoderQC 340f7e8af4 Pass default image file name when adding uniform 2021-06-24 18:13:13 +02:00
QuantumCoderQC fa44147ee6 Pass default float value when adding uniform 2021-06-24 18:13:13 +02:00
QuantumCoderQC c44e2cf555 Pass default vector value when adding uniform 2021-06-24 18:13:13 +02:00
QuantumCoderQC 15da6ccf58 Upgrade nodes to include object. Add documnetation 2021-06-24 18:13:12 +02:00
QuantumCoderQC 39922bc0f3 move uniforms manager to iron 2021-06-24 18:13:12 +02:00
QuantumCoderQC ea4f88aca8 remove debug trace 2021-06-24 18:13:12 +02:00
QuantumCoderQC 39bd32f9d3 get image from node 2021-06-24 18:13:12 +02:00
QuantumCoderQC e744fd901c create and implement uniforms manager
remove node mapping

Revert "remove node mapping"

This reverts commit e70aa60e120e71236cba885bd7e0e5f1b6acf39d.
2021-06-24 18:13:12 +02:00
QuantumCoderQC dbd348ad5d update node scripts 2021-06-24 18:13:12 +02:00
QuantumCoderQC f542dc00ca update similar nodes 2021-06-24 18:13:12 +02:00
QuantumCoderQC aaa21bc019 implement per object option 2021-06-24 18:13:12 +02:00
QuantumCoderQC a9f430c374 Add per object option 2021-06-24 18:13:12 +02:00
QuantumCoderQC 6cf3299ffe Implement and upgrade set material value parameter node 2021-06-24 18:13:12 +02:00
QuantumCoderQC a588396dea Update set material value param node 2021-06-24 18:13:12 +02:00
Moritz Brückner dc34e48c52 Unregister timers on exit/disabling addon 2021-06-24 17:26:21 +02:00
Moritz Brückner fce97a5ddf Cleanup 2021-06-24 15:03:30 +02:00
Moritz Brückner d18aede964 run_proc: no thread in background mode and call done in main thread 2021-06-24 14:59:32 +02:00
Moritz Brückner 66856e7ecc Build/publish: add poll() function to prevent exception when executing from outside of UI
If there was no exporter, calling `bpy.ops.arm.publish_project()` would result in an exception before. Now the call is simply ignored and a "poll failed" message is emitted instead.
2021-06-23 20:19:08 +02:00
Lubos Lenco ebfbb68bb4
Merge pull request #2235 from knowledgenude/master
Cleanup in ui Ext
2021-06-20 20:53:48 +02:00
Henrique 3c1264378b Cleanup in ui Ext 2021-06-19 13:39:18 -03:00
Lubos Lenco cbcc3e4e04
Merge pull request #2233 from MoritzBrueckner/fix-compilation
Fix compilation of some nodes
2021-06-16 08:10:03 +02:00
Moritz Brückner f45304ea10 Fix compilation of some nodes 2021-06-15 15:24:58 +02:00
Lubos Lenco dd13cc31b7
Merge pull request #2231 from knowledgenude/master
Fix GetBoneFkIkOnly node & add new input nodes
2021-06-14 10:41:51 +02:00
Henrique a5fa3445d9 Add new input nodes 2021-06-13 22:43:13 -03:00
Henrique 1ece052aee Fix GetBoneFkIkOnly node 2021-06-13 21:38:15 -03:00
Lubos Lenco 24d917ae6a
Merge pull request #2228 from MoritzBrueckner/fix-movie-texture
Fix rendering multiple movie textures
2021-06-10 21:12:32 +02:00
Moritz Brückner 3288c3dcf5 MovieTexture: add documentation 2021-06-10 20:28:30 +02:00
Moritz Brückner 251ad8e47e Cache and reuse movietexture render targets with same size 2021-06-10 20:27:23 +02:00
Moritz Brückner b2619828eb Fix rendering multiple movie textures
Fixes https://github.com/armory3d/armory/issues/1562
2021-06-09 23:11:22 +02:00
Lubos Lenco 6c6d7c1419
Merge pull request #2221 from QuantumCoderQC/ExtraLogicNodes
Extra logic nodes
2021-06-09 07:38:28 +02:00
QuantumCoderQC 0e6ba5b0b0 reverse call order 2021-06-07 13:49:12 +02:00
QuantumCoderQC 107dc730f4 Create and implement once per frame node 2021-06-07 13:41:24 +02:00
Lubos Lenco b72e85894a
Merge pull request #2220 from MoritzBrueckner/select-node
Add Select node
2021-06-07 11:03:42 +02:00
Moritz Brückner 20f9f8a5f4 Add Select node
Implements feature requests #2200 and #2201
2021-06-05 20:35:21 +02:00
Moritz Brückner 088bc0f666 Exporter: remove no longer used Blender version checks 2021-05-20 20:38:43 +02:00
Moritz Brückner 9fbc3d6cd4 Small cleanup 2021-05-19 21:17:57 +02:00
Moritz Brückner 7b2961459c Live patch support for light strength and color 2021-05-19 21:17:38 +02:00
Moritz Brückner 3d46910530 Live patch: don't re-export on trackball rotation (double 'r' key) 2021-05-18 21:34:46 +02:00
Moritz Brückner 68825516c9 Live patch: more robust shader paths 2021-05-18 21:31:02 +02:00
Moritz Brückner 07ffe06c1d Live patch: move to dedicated module and use msgbus 2021-05-18 21:24:45 +02:00
524 changed files with 9297 additions and 3531 deletions

View file

@ -4,7 +4,9 @@
uniform sampler2D tex;
#ifdef _CPostprocess
uniform vec3 PPComp10;
#endif
in vec2 texCoord;
out vec4 fragColor;

View file

@ -8,7 +8,8 @@
"links": [
{
"name": "PPComp10",
"link": "_PPComp10"
"link": "_PPComp10",
"ifdef": ["_CPostprocess"]
}
],
"texture_params": [],

View file

@ -7,7 +7,9 @@ uniform sampler2D tex;
uniform vec2 dir;
uniform vec2 screenSize;
#ifdef _CPostprocess
uniform vec3 PPComp11;
#endif
in vec2 texCoord;
out vec4 fragColor;
@ -26,7 +28,7 @@ void main() {
fragColor.rgb += textureLod(tex, texCoord + s, 0.0).rgb * weight[i];
fragColor.rgb += textureLod(tex, texCoord - s, 0.0).rgb * weight[i];
}
#ifdef _CPostprocess
fragColor.rgb *= PPComp11.x / 5;
#else

View file

@ -16,7 +16,8 @@
},
{
"name": "PPComp11",
"link": "_PPComp11"
"link": "_PPComp11",
"ifdef": ["_CPostprocess"]
}
],
"texture_params": [],
@ -39,7 +40,8 @@
},
{
"name": "PPComp11",
"link": "_PPComp11"
"link": "_PPComp11",
"ifdef": ["_CPostprocess"]
}
],
"texture_params": [],
@ -65,7 +67,8 @@
},
{
"name": "PPComp11",
"link": "_PPComp11"
"link": "_PPComp11",
"ifdef": ["_CPostprocess"]
}
],
"texture_params": [],

View file

@ -3,7 +3,10 @@
#include "compiled.inc"
uniform sampler2D tex;
#ifdef _CPostprocess
uniform vec3 PPComp13;
#endif
in vec2 texCoord;
out vec4 fragColor;

View file

@ -9,7 +9,8 @@
"links": [
{
"name": "PPComp13",
"link": "_PPComp13"
"link": "_PPComp13",
"ifdef": ["_CPostprocess"]
}
],
"texture_params": [],

View file

@ -21,7 +21,9 @@
uniform sampler2D gbufferD;
uniform sampler2D gbuffer0;
uniform sampler2D gbuffer1;
#ifdef _gbuffer2
uniform sampler2D gbuffer2;
#endif
#ifdef _VoxelAOvar
uniform sampler3D voxels;
@ -206,7 +208,9 @@ void main() {
vec3 v = normalize(eye - p);
float dotNV = max(dot(n, v), 0.0);
#ifdef _gbuffer2
vec4 g2 = textureLod(gbuffer2, texCoord, 0.0);
#endif
#ifdef _MicroShadowing
occspec.x = mix(1.0, occspec.x, dotNV); // AO Fresnel
@ -221,14 +225,16 @@ void main() {
vec3 envl = shIrradiance(n, shirr);
if (g2.b < 0.5) {
envl = envl;
} else {
envl = vec3(1.0);
}
#ifdef _gbuffer2
if (g2.b < 0.5) {
envl = envl;
} else {
envl = vec3(1.0);
}
#endif
#ifdef _EnvTex
envl /= PI;
envl /= PI;
#endif
#else
vec3 envl = vec3(1.0);

View file

@ -12,8 +12,10 @@ uniform vec3 eyeLook;
uniform vec2 screenSize;
uniform mat4 invVP;
#ifdef _CPostprocess
uniform vec3 PPComp11;
uniform vec3 PPComp12;
#endif
in vec2 texCoord;
in vec3 viewRay;
@ -23,12 +25,12 @@ void main() {
float depth = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0;
if (depth == 1.0) { fragColor = 1.0; return; }
vec2 enc = textureLod(gbuffer0, texCoord, 0.0).rg;
vec2 enc = textureLod(gbuffer0, texCoord, 0.0).rg;
vec3 n;
n.z = 1.0 - abs(enc.x) - abs(enc.y);
n.xy = n.z >= 0.0 ? enc.xy : octahedronWrap(enc.xy);
n = normalize(n);
vec3 vray = normalize(viewRay);
vec3 currentPos = getPosNoEye(eyeLook, vray, depth, cameraProj);
// vec3 currentPos = getPos2NoEye(eye, invVP, depth, texCoord);
@ -53,7 +55,7 @@ void main() {
vec3 pos = getPos2NoEye(eye, invVP, depth, texCoord + k) - currentPos;
fragColor += max(0, dot(pos, n) - currentDistanceB) / (dot(pos, pos) + 0.015);
}
#ifdef _CPostprocess
fragColor *= (PPComp12.x * 0.3) / samples;
#else

View file

@ -28,11 +28,13 @@
},
{
"name": "PPComp11",
"link": "_PPComp11"
"link": "_PPComp11",
"ifdef": ["_CPostprocess"]
},
{
"name": "PPComp12",
"link": "_PPComp12"
"link": "_PPComp12",
"ifdef": ["_CPostprocess"]
}
],
"texture_params": [],

View file

@ -12,8 +12,10 @@ uniform mat4 P;
uniform mat3 V3;
uniform vec2 cameraProj;
#ifdef _CPostprocess
uniform vec3 PPComp9;
uniform vec3 PPComp10;
#endif
in vec3 viewRay;
in vec2 texCoord;

View file

@ -24,11 +24,13 @@
},
{
"name": "PPComp9",
"link": "_PPComp9"
"link": "_PPComp9",
"ifdef": ["_CPostprocess"]
},
{
"name": "PPComp10",
"link": "_PPComp10"
"link": "_PPComp10",
"ifdef": ["_CPostprocess"]
}
],
"texture_params": [],

View file

@ -23,7 +23,7 @@ vec2 rand2(const vec2 coord) {
const float width = 1100.0;
const float height = 500.0;
float noiseX = ((fract(1.0 - coord.s * (width / 2.0)) * 0.25) + (fract(coord.t * (height / 2.0)) * 0.75)) * 2.0 - 1.0;
float noiseY = ((fract(1.0 - coord.s * (width / 2.0)) * 0.75) + (fract(coord.t * (height / 2.0)) * 0.25)) * 2.0 - 1.0;
float noiseY = ((fract(1.0 - coord.s * (width / 2.0)) * 0.75) + (fract(coord.t * (height / 2.0)) * 0.25)) * 2.0 - 1.0;
return vec2(noiseX, noiseY);
}
@ -40,4 +40,9 @@ float attenuate(const float dist) {
// 1.0 / (quadratic * dist * dist);
}
float safe_acos(const float x) {
// acos is undefined if |x| > 1
return acos(clamp(x, -1.0, 1.0));
}
#endif

View file

@ -0,0 +1,53 @@
uniform sampler2D morphDataPos;
uniform sampler2D morphDataNor;
uniform vec2 morphScaleOffset;
uniform vec2 morphDataDim;
uniform vec4 morphWeights[8];
void getMorphedVertex(vec2 uvCoord, inout vec3 A){
for(int i = 0; i<8; i++ )
{
vec4 tempCoordY = vec4( uvCoord.y - (i * 4) * morphDataDim.y,
uvCoord.y - (i * 4 + 1) * morphDataDim.y,
uvCoord.y - (i * 4 + 2) * morphDataDim.y,
uvCoord.y - (i * 4 + 3) * morphDataDim.y);
vec3 morph = texture(morphDataPos, vec2(uvCoord.x, tempCoordY.x)).rgb * morphScaleOffset.x + morphScaleOffset.y;
A += morphWeights[i].x * morph;
morph = texture(morphDataPos, vec2(uvCoord.x, tempCoordY.y)).rgb * morphScaleOffset.x + morphScaleOffset.y;
A += morphWeights[i].y * morph;
morph = texture(morphDataPos, vec2(uvCoord.x, tempCoordY.z)).rgb * morphScaleOffset.x + morphScaleOffset.y;
A += morphWeights[i].z * morph;
morph = texture(morphDataPos, vec2(uvCoord.x, tempCoordY.w)).rgb * morphScaleOffset.x + morphScaleOffset.y;
A += morphWeights[i].w * morph;
}
}
void getMorphedNormal(vec2 uvCoord, vec3 oldNor, inout vec3 morphNor){
for(int i = 0; i<8; i++ )
{
vec4 tempCoordY = vec4( uvCoord.y - (i * 4) * morphDataDim.y,
uvCoord.y - (i * 4 + 1) * morphDataDim.y,
uvCoord.y - (i * 4 + 2) * morphDataDim.y,
uvCoord.y - (i * 4 + 3) * morphDataDim.y);
vec3 norm = oldNor + morphWeights[i].x * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.x)).rgb * 2.0 - 1.0);
morphNor += norm;
norm = oldNor + morphWeights[i].y * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.y)).rgb * 2.0 - 1.0);
morphNor += norm;
norm = oldNor + morphWeights[i].z * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.z)).rgb * 2.0 - 1.0);
morphNor += norm;
norm = oldNor + morphWeights[i].w * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.w)).rgb * 2.0 - 1.0);
morphNor += norm;
}
morphNor = normalize(morphNor);
}

View file

@ -20,6 +20,8 @@
#ifndef _SKY_GLSL_
#define _SKY_GLSL_
#include "std/math.glsl"
uniform sampler2D nishitaLUT;
uniform vec2 nishitaDensity;
@ -44,20 +46,9 @@ uniform vec2 nishitaDensity;
#define nishita_mie_dir 0.76 // Aerosols anisotropy ("direction")
#define nishita_mie_dir_sq 0.5776 // Squared aerosols anisotropy
// The ozone absorption coefficients are taken from Cycles code.
// Because Cycles calculates 21 wavelengths, we use the coefficients
// which are closest to the RGB wavelengths (645nm, 510nm, 440nm).
// Precalculating values by simulating Blender's spec_to_xyz() function
// to include all 21 wavelengths gave unrealistic results
#define nishita_ozone_coeff vec3(1.59051840791988e-6, 0.00000096707041180970, 0.00000007309568762914)
// Values from [Hill: 60]
#define sun_limb_darkening_col vec3(0.397, 0.503, 0.652)
float random(vec2 coords) {
return fract(sin(dot(coords.xy, vec2(12.9898,78.233))) * 43758.5453);
}
vec3 nishita_lookupLUT(const float height, const float sunTheta) {
vec2 coords = vec2(
sqrt(height * (1 / nishita_atmo_radius)),
@ -124,18 +115,19 @@ vec3 nishita_atmosphere(const vec3 r, const vec3 r0, const vec3 pSun, const floa
// Idea behind this: "Rotate" everything by iPos (-> iPos is the new zenith) and then all calculations for the
// inner integral only depend on the sample height (iHeight) and sunTheta (angle between sun and new zenith).
float sunTheta = acos(dot(normalize(iPos), normalize(pSun)));
vec3 jODepth = nishita_lookupLUT(iHeight, sunTheta);
// Apply dithering to reduce visible banding
jODepth += mix(-1000, 1000, random(r.xy));
float sunTheta = safe_acos(dot(normalize(iPos), normalize(pSun)));
vec3 jAttn = nishita_lookupLUT(iHeight, sunTheta);
// Calculate attenuation
vec3 attn = exp(-(
nishita_mie_coeff * (iOdMie + jODepth.y)
+ (nishita_rayleigh_coeff) * (iOdRlh + jODepth.x)
+ nishita_ozone_coeff * jODepth.z
vec3 iAttn = exp(-(
nishita_mie_coeff * iOdMie
+ nishita_rayleigh_coeff * iOdRlh
// + 0 for ozone
));
vec3 attn = iAttn * jAttn;
// Apply dithering to reduce visible banding
attn *= 0.98 + rand(r.xy) * 0.04;
// Accumulate scattering
totalRlh += odStepRlh * attn;

View file

@ -44,7 +44,15 @@ uniform vec2 cameraPlane;
uniform vec3 sunDir;
uniform vec3 sunCol;
#ifdef _ShadowMap
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
uniform sampler2DShadow shadowMapAtlasSun;
#else
uniform sampler2DShadow shadowMapAtlas;
#endif
#else
uniform sampler2DShadow shadowMap;
#endif
uniform float shadowsBias;
#ifdef _CSM
//!uniform vec4 casData[shadowmapCascades * 4 + 4];
@ -95,7 +103,17 @@ void rayStep(inout vec3 curPos, inout float curOpticalDepth, inout float scatter
#endif
vec4 lPos = LWVP * vec4(curPos, 1.0);
lPos.xyz /= lPos.w;
visibility = texture(shadowMap, vec3(lPos.xy, lPos.z - shadowsBias));
visibility = texture(
#ifdef _ShadowMapAtlas
#ifndef _SingleAtlas
shadowMapAtlasSun
#else
shadowMapAtlas
#endif
#else
shadowMap
#endif
, vec3(lPos.xy, lPos.z - shadowsBias));
#endif
#ifdef _SinglePoint

1
Sources/armory/import.hx Normal file
View file

@ -0,0 +1 @@
import armory.system.Assert.*;

View file

@ -1,13 +1,11 @@
package armory.logicnode;
import armory.trait.physics.PhysicsConstraint;
#if arm_physics
import armory.trait.physics.bullet.PhysicsConstraint.ConstraintType;
import armory.trait.physics.bullet.PhysicsConstraint.ConstraintAxis;
#end
import iron.object.Object;
import armory.trait.physics.RigidBody;
import armory.logicnode.PhysicsConstraintNode;
#if arm_physics
import armory.trait.physics.PhysicsConstraint;
import armory.trait.physics.bullet.PhysicsConstraint.ConstraintType;
#end
class AddPhysicsConstraintNode extends LogicNode {
@ -21,115 +19,96 @@ class AddPhysicsConstraintNode extends LogicNode {
}
override function run(from: Int) {
var pivotObject:Object = inputs[1].get();
var pivotObject: Object = inputs[1].get();
rb1 = inputs[2].get();
rb2 = inputs[3].get();
var disableCollisions: Bool = inputs[4].get();
var breakable: Bool = inputs[5].get();
var breakingThreshold: Float = inputs[6].get();
var type: ConstraintType = 0;
if (pivotObject == null || rb1 == null || rb2 == null) return;
#if arm_physics
var disableCollisions: Bool = inputs[4].get();
var breakable: Bool = inputs[5].get();
var breakingThreshold: Float = inputs[6].get();
var type: ConstraintType = 0;
var con: PhysicsConstraint = pivotObject.getTrait(PhysicsConstraint);
if(con == null)
{
switch(property0)
{
case 'Fixed':
type = Fixed;
case 'Point':
type = Point;
case 'Hinge':
type = Hinge;
case 'Slider':
type = Slider;
case 'Piston':
type = Piston;
case 'Generic Spring':
type = Generic;
if (con == null) {
switch (property0) {
case "Fixed": type = Fixed;
case "Point": type = Point;
case "Hinge": type = Hinge;
case "Slider": type = Slider;
case "Piston": type = Piston;
case "Generic Spring": type = Generic;
}
if(! breakable) breakingThreshold = 0.0;
if (!breakable) breakingThreshold = 0.0;
if (type != Generic) {
if(type != Generic) {
con = new PhysicsConstraint(rb1, rb2, type, disableCollisions, breakingThreshold);
switch (type)
{
switch (type) {
case Hinge:
var setLimit:Bool = inputs[7].get();
var low:Float = inputs[8].get();
var up:Float = inputs[9].get();
var setLimit: Bool = inputs[7].get();
var low: Float = inputs[8].get();
var up: Float = inputs[9].get();
con.setHingeConstraintLimits(setLimit, low, up);
case Slider:
var setLimit:Bool = inputs[7].get();
var low:Float = inputs[8].get();
var up:Float = inputs[9].get();
var setLimit: Bool = inputs[7].get();
var low: Float = inputs[8].get();
var up: Float = inputs[9].get();
con.setSliderConstraintLimits(setLimit, low, up);
case Piston:
var setLinLimit:Bool = inputs[7].get();
var linLow:Float = inputs[8].get();
var linUp:Float = inputs[9].get();
var setAngLimit:Bool = inputs[10].get();
var angLow:Float = inputs[11].get();
var angUp:Float = inputs[12].get();
var setLinLimit: Bool = inputs[7].get();
var linLow: Float = inputs[8].get();
var linUp: Float = inputs[9].get();
var setAngLimit: Bool = inputs[10].get();
var angLow: Float = inputs[11].get();
var angUp: Float = inputs[12].get();
con.setPistonConstraintLimits(setLinLimit, linLow, linUp, setAngLimit, angLow, angUp);
default:
default:
}
}
else
{
else {
var spring: Bool = false;
var prop: PhysicsConstraintNode;
for(inp in 7...inputs.length)
{
for (inp in 7...inputs.length) {
prop = inputs[inp].get();
if(prop == null) continue;
if(prop.isSpring)
{
if (prop == null) continue;
if (prop.isSpring) {
spring = true;
break;
}
}
if(spring) {
if (spring) {
con = new PhysicsConstraint(rb1, rb2, GenericSpring, disableCollisions, breakingThreshold);
}
}
else {
con = new PhysicsConstraint(rb1, rb2, Generic, disableCollisions, breakingThreshold);
}
for(inp in 7...inputs.length)
{
for (inp in 7...inputs.length) {
prop = inputs[inp].get();
if(prop == null) continue;
(inp + ': ');
if (prop == null) continue;
if(prop.isSpring)
{
if (prop.isSpring) {
con.setSpringParams(prop.isSpring, prop.value1, prop.value2, prop.axis, prop.isAngular);
}
else
{
else {
con.setGenericConstraintLimits(true, prop.value1, prop.value2, prop.axis, prop.isAngular);
}
}
}
pivotObject.addTrait(con);
}
#end
runOutput(0);
}
}

View file

@ -1,15 +1,17 @@
package armory.logicnode;
import iron.object.Object;
#if arm_physics
import armory.trait.physics.RigidBody;
import armory.trait.physics.bullet.RigidBody.Shape;
#end
import iron.object.Object;
import armory.trait.physics.RigidBody;
class AddRigidBodyNode extends LogicNode {
public var property0: String;//Shape
public var property1: String;//Advanced
public var property0: String; //Shape
public var property1: Bool; //Advanced
public var object: Object;
public function new(tree: LogicTree) {
@ -18,6 +20,10 @@ class AddRigidBodyNode extends LogicNode {
override function run(from: Int) {
object = inputs[1].get();
if (object == null) return;
#if arm_physics
var mass: Float = inputs[2].get();
var active: Bool = inputs[3].get();
var animated: Bool = inputs[4].get();
@ -38,8 +44,7 @@ class AddRigidBodyNode extends LogicNode {
var shape: Shape = 1;
if(property1 == 'true')
{
if (property1) {
margin = inputs[9].get();
marginLen = inputs[10].get();
linDamp = inputs[11].get();
@ -49,50 +54,34 @@ class AddRigidBodyNode extends LogicNode {
angVelThreshold = inputs[15].get();
group = inputs[16].get();
mask = inputs[17].get();
}
if (object == null) return;
#if arm_physics
var rb: RigidBody = object.getTrait(RigidBody);
if((group < 0) || (group > 32)) group = 1; //Limiting max groups to 32
if((mask < 0) || (mask > 32)) mask = 1; //Limiting max masks to 32
if(rb == null)
{
switch (property0){
case 'Box':
shape = Box;
case 'Sphere':
shape = Sphere;
case 'Capsule':
shape = Capsule;
case 'Cone':
shape = Cone;
case 'Cylinder':
shape = Cylinder;
case 'Convex Hull':
shape = ConvexHull;
case 'Mesh':
shape = Mesh;
if ((group < 0) || (group > 32)) group = 1; //Limiting max groups to 32
if ((mask < 0) || (mask > 32)) mask = 1; //Limiting max masks to 32
if (rb == null) {
switch (property0) {
case "Box": shape = Box;
case "Sphere": shape = Sphere;
case "Capsule": shape = Capsule;
case "Cone": shape = Cone;
case "Cylinder": shape = Cylinder;
case "Convex Hull": shape = ConvexHull;
case "Mesh": shape = Mesh;
}
rb = new RigidBody(shape, mass, friction, bounciness, group, mask);
rb.animated = animated;
rb.staticObj = ! active;
rb.staticObj = !active;
rb.isTriggerObject(trigger);
if(property1 == 'true')
{
if (property1) {
rb.linearDamping = linDamp;
rb.angularDamping = angDamp;
if(margin) rb.collisionMargin = marginLen;
if(useDeactiv) {
if (margin) rb.collisionMargin = marginLen;
if (useDeactiv) {
rb.setUpDeactivation(true, linearVelThreshold, angVelThreshold, 0.0);
}
}
object.addTrait(rb);

View file

@ -10,10 +10,17 @@ class AddTraitNode extends LogicNode {
override function run(from: Int) {
var object: Object = inputs[1].get();
var trait: Dynamic = inputs[2].get();
var traitName: String = inputs[2].get();
if (object == null || trait == null) return;
assert(Error, object != null, "Object should not be null");
assert(Error, traitName != null, "Trait name should not be null");
var cname = Type.resolveClass(Main.projectPackage + "." + traitName);
if (cname == null) cname = Type.resolveClass(Main.projectPackage + ".node." + traitName);
assert(Error, cname != null, 'No trait with the name "$traitName" found, make sure that the trait is exported!');
assert(Warning, object.getTrait(cname) == null, 'Object already has the trait "$traitName" applied');
var trait = Type.createInstance(cname, []);
object.addTrait(trait);
runOutput(0);

View file

@ -12,11 +12,17 @@ class CallHaxeStaticNode extends LogicNode {
var path: String = inputs[1].get();
if (path != "") {
var args: Array<Dynamic> = [];
for (i in 2...inputs.length) {
args.push(inputs[i].get());
}
var dotIndex = path.lastIndexOf(".");
var classPath = path.substr(0, dotIndex);
var classType = Type.resolveClass(classPath);
var funName = path.substr(dotIndex + 1);
result = Reflect.callMethod(classType, Reflect.field(classType, funName), [tree]);
result = Reflect.callMethod(classType, Reflect.field(classType, funName), args);
}
runOutput(0);

View file

@ -0,0 +1,39 @@
package armory.logicnode;
import iron.Scene;
import armory.trait.internal.CanvasScript;
import kha.Color;
import iron.math.Vec4;
class CanvasSetProgressBarColorNode extends LogicNode {
var canvas: CanvasScript;
var element: String;
var color = new Vec4();
public function new(tree: LogicTree) {
super(tree);
}
#if arm_ui
function update() {
if (!canvas.ready) return;
tree.removeUpdate(update);
var e = canvas.getElement(element);
if (e != null) e.color_progress = Color.fromFloats(color.x, color.y, color.z, color.w);
runOutput(0);
}
override function run(from: Int) {
element = inputs[1].get();
color = inputs[2].get();
canvas = Scene.active.getTrait(CanvasScript);
if (canvas == null) canvas = Scene.active.camera.getTrait(CanvasScript);
// Ensure canvas is ready
tree.notifyOnUpdate(update);
update();
}
#end
}

View file

@ -19,9 +19,11 @@ class CompareNode extends LogicNode {
switch (property0) {
case "Equal":
cond = Std.is(v1, Vec4) ? v1.equals(v2) : v1 == v2;
cond = Std.isOfType(v1, Vec4) ? v1.equals(v2) : v1 == v2;
case "Not Equal":
cond = Std.isOfType(v1, Vec4) ? !v1.equals(v2) : v1 != v2;
case "Almost Equal":
cond = Std.is(v1, Vec4) ? v1.almostEquals(v2, property1) : Math.abs(v1 - v2) < property1;
cond = Std.isOfType(v1, Vec4) ? v1.almostEquals(v2, property1) : Math.abs(v1 - v2) < property1;
case "Greater":
cond = v1 > v2;
case "Greater Equal":
@ -30,6 +32,9 @@ class CompareNode extends LogicNode {
cond = v1 < v2;
case "Less Equal":
cond = v1 <= v2;
case "Between":
var v3: Dynamic = inputs[2].get();
cond = v2 <= v1 && v1 <= v3;
case "Or":
for (input in inputs) {
if (input.get()) {

View file

@ -18,9 +18,11 @@ class GateNode extends LogicNode {
switch (property0) {
case "Equal":
cond = Std.is(v1, Vec4) ? v1.equals(v2) : v1 == v2;
cond = Std.isOfType(v1, Vec4) ? v1.equals(v2) : v1 == v2;
case "Not Equal":
cond = Std.isOfType(v1, Vec4) ? !v1.equals(v2) : v1 != v2;
case "Almost Equal":
cond = Std.is(v1, Vec4) ? v1.almostEquals(v2, property1) : Math.abs(v1 - v2) < property1;
cond = Std.isOfType(v1, Vec4) ? v1.almostEquals(v2, property1) : Math.abs(v1 - v2) < property1;
case "Greater":
cond = v1 > v2;
case "Greater Equal":
@ -29,6 +31,9 @@ class GateNode extends LogicNode {
cond = v1 < v2;
case "Less Equal":
cond = v1 <= v2;
case "Between":
var v3: Dynamic = inputs[3].get();
cond = v2 <= v1 && v1 <= v3;
case "Or":
for (i in 1...inputs.length) {
if (inputs[i].get()) {

View file

@ -0,0 +1,25 @@
package armory.logicnode;
import iron.object.Object;
class GetAgentDataNode extends LogicNode {
public function new(tree: LogicTree) {
super(tree);
}
override function get(from: Int): Float {
var object: Object = inputs[0].get();
assert(Error, object != null, "The object to naviagte should not be null");
#if arm_navigation
var agent: armory.trait.NavAgent = object.getTrait(armory.trait.NavAgent);
assert(Error, agent != null, "The object does not have NavAgent Trait");
if(from == 0) return agent.speed;
else return agent.turnDuration;
#else
return null;
#end
}
}

View file

@ -23,7 +23,7 @@ class GetBoneFkIkOnlyNode extends LogicNode {
var bone = anim.getBone(boneName);
//Get bone transform in world coordinates
return bone.is_IK_FK_only;
return bone.is_ik_fk_only;
#end

View file

@ -0,0 +1,33 @@
package armory.logicnode;
import iron.system.Input;
class GetGamepadStartedNode extends LogicNode {
var buttonStarted: Null<String>;
public function new(tree: LogicTree) {
super(tree);
}
override function run(from: Int) {
var g = Input.getGamepad(inputs[0].get());
buttonStarted = null;
for (b in Gamepad.buttons) {
if (g.started(b)) {
buttonStarted = b;
break;
}
}
if (buttonStarted != null) {
runOutput(0);
}
}
override function get(from: Int) {
return buttonStarted;
}
}

View file

@ -0,0 +1,22 @@
package armory.logicnode;
import iron.Scene;
import armory.trait.internal.CanvasScript;
class GetGlobalCanvasFontSizeNode extends LogicNode {
var canvas: CanvasScript;
public function new(tree: LogicTree) {
super(tree);
}
#if arm_ui
override function get(from: Int): Dynamic {
canvas = Scene.active.getTrait(CanvasScript);
if (canvas == null) canvas = Scene.active.camera.getTrait(CanvasScript);
return canvas.getCanvasFontSize();
}
#end
}

View file

@ -0,0 +1,22 @@
package armory.logicnode;
import iron.Scene;
import armory.trait.internal.CanvasScript;
class GetGlobalCanvasScaleNode extends LogicNode {
var canvas: CanvasScript;
public function new(tree: LogicTree) {
super(tree);
}
#if arm_ui
override function get(from: Int): Dynamic {
canvas = Scene.active.getTrait(CanvasScript);
if (canvas == null) canvas = Scene.active.camera.getTrait(CanvasScript);
return canvas.getUiScale();
}
#end
}

View file

@ -0,0 +1,24 @@
package armory.logicnode;
import armory.system.InputMap;
class GetInputMapKeyNode extends LogicNode {
public function new(tree: LogicTree) {
super(tree);
}
override function get(from: Int): Dynamic {
var inputMap = inputs[0].get();
var key = inputs[1].get();
var k = InputMap.getInputMapKey(inputMap, key);
if (k != null) {
if (from == 0) return k.scale;
else if (from == 1) return k.deadzone;
}
return null;
}
}

View file

@ -0,0 +1,32 @@
package armory.logicnode;
import iron.system.Input;
class GetKeyboardStartedNode extends LogicNode {
var kb = Input.getKeyboard();
var keyStarted: Null<String>;
public function new(tree: LogicTree) {
super(tree);
}
override function run(from: Int) {
keyStarted = null;
for (k in Keyboard.keys) {
if (kb.started(k)) {
keyStarted = k;
break;
}
}
if (keyStarted != null) {
runOutput(0);
}
}
override function get(from: Int) {
return keyStarted;
}
}

View file

@ -10,9 +10,22 @@ class GetLocationNode extends LogicNode {
override function get(from: Int): Dynamic {
var object: Object = inputs[0].get();
var relative: Bool = inputs[1].get();
if (object == null) return null;
return object.transform.world.getLoc();
var loc = object.transform.world.getLoc();
if (relative && object.parent != null) {
loc.sub(object.parent.transform.world.getLoc()); // Add parent location influence
// Convert loc to parent local space
var dotX = loc.dot(object.parent.transform.right());
var dotY = loc.dot(object.parent.transform.look());
var dotZ = loc.dot(object.parent.transform.up());
loc.set(dotX, dotY, dotZ);
}
return loc;
}
}

View file

@ -1,6 +1,7 @@
package armory.logicnode;
import iron.object.MeshObject;
import iron.object.DecalObject;
class GetMaterialNode extends LogicNode {
@ -9,11 +10,27 @@ class GetMaterialNode extends LogicNode {
}
override function get(from: Int): Dynamic {
var object: MeshObject = inputs[0].get();
var slot: Int = inputs[1].get();
if (object == null) return null;
var object = inputs[0].get();
return object.materials[slot];
assert(Error, object != null, "The object input must not be null");
#if rp_decals
if (Std.isOfType(object, DecalObject)) {
var decal = cast(object, DecalObject);
return decal.material;
}
#end
if (Std.isOfType(object, MeshObject)) {
var mesh = cast(object, MeshObject);
var slot: Int = inputs[1].get();
if (mesh == null) return null;
return mesh.materials[slot];
}
return null;
}
}

View file

@ -0,0 +1,32 @@
package armory.logicnode;
import iron.system.Input;
class GetMouseStartedNode extends LogicNode {
var m = Input.getMouse();
var buttonStarted: Null<String>;
public function new(tree: LogicTree) {
super(tree);
}
override function run(from: Int) {
buttonStarted = null;
for (b in Mouse.buttons) {
if (m.started(b)) {
buttonStarted = b;
break;
}
}
if (buttonStarted != null) {
runOutput(0);
}
}
override function get(from: Int) {
return buttonStarted;
}
}

View file

@ -0,0 +1,22 @@
package armory.logicnode;
import iron.data.SceneFormat;
import iron.object.Object;
class GetObjectByUidNode extends LogicNode {
public function new(tree: LogicTree) {
super(tree);
}
override function get(from: Int): Dynamic {
var objectUid: Int = inputs[0].get();
var obj = iron.Scene.active.getChildren(true);
for (obji in obj) if (obji.uid == objectUid) return obji;
return null;
}
}

View file

@ -1,10 +1,13 @@
package armory.logicnode;
import iron.object.Object;
import iron.math.Vec3;
import iron.math.Quat;
import iron.math.Vec4;
class GetRotationNode extends LogicNode {
public var property0: String;
public function new(tree: LogicTree) {
super(tree);
}
@ -16,34 +19,16 @@ class GetRotationNode extends LogicNode {
return null;
}
var rot = object.transform.rot;
switch (from) {
case 0:
// euler angles
return object.transform.rot.getEuler();
case 1:
// vector
var sqrtW = Math.sqrt(1 - (rot.w * rot.w));
if (sqrtW == 0) {
return new Vec3(0, 0, 1);
}
return new Vec3(rot.x / sqrtW, rot.y / sqrtW, rot.z / sqrtW);
case 2:
// angle radians
var angle = 2 * Math.acos(rot.w);
return angle;
case 3:
// angle degrees
var angle = 2 * Math.acos(rot.w);
return angle * (180 / Math.PI);
case 4:
//quaternion xyz
return new Vec3(rot.x, rot.y, rot.z);
case 5:
//quaternion w
return rot.w;
}
switch(property0){
case "Local":
return object.transform.rot;
case "Global":{
var useless1 = new Vec4();
var ret = new Quat();
object.transform.world.decompose(useless1, ret, useless1);
return ret;
}}
return null;
}
}

View file

@ -16,12 +16,12 @@ class GetTraitNameNode extends LogicNode {
case 0: {
// Check CanvasScript
var cname = cast Type.resolveClass("armory.trait.internal.CanvasScript");
if (Std.is(trait, cname)) {
if (Std.isOfType(trait, cname)) {
return trait.cnvName;
}
// Check WasmScript
var cname = cast Type.resolveClass("armory.trait.internal.WasmScript");
if (Std.is(trait, cname)) {
if (Std.isOfType(trait, cname)) {
return trait.wasmName;
}
// Other

View file

@ -0,0 +1,18 @@
package armory.logicnode;
import iron.object.Object;
class GetUidNode extends LogicNode {
public function new(tree: LogicTree) {
super(tree);
}
override function get(from: Int): Dynamic {
var object: Object = inputs[0].get();
if (object == null) return null;
return object.uid;
}
}

View file

@ -13,15 +13,26 @@ class GoToLocationNode extends LogicNode {
override function run(from: Int) {
var object: Object = inputs[1].get();
var location: Vec4 = inputs[2].get();
var speed: Float = inputs[3].get();
var turnDuration: Float = inputs[4].get();
if (object == null || location == null) return;
assert(Error, object != null, "The object input not be null");
assert(Error, location != null, "The location to navigate to must not be null");
assert(Error, speed != null, "Speed of Nav Agent should not be null");
assert(Warning, speed >= 0, "Speed of Nav Agent should be positive");
assert(Error, turnDuration != null, "Turn Duration of Nav Agent should not be null");
assert(Warning, turnDuration >= 0, "Turn Duration of Nav Agent should be positive");
#if arm_navigation
// Assume navmesh exists..
var from = object.transform.world.getLoc();
var to = location;
assert(Error, Navigation.active.navMeshes.length > 0, "No Navigation Mesh Present");
Navigation.active.navMeshes[0].findPath(from, to, function(path: Array<iron.math.Vec4>) {
var agent: armory.trait.NavAgent = object.getTrait(armory.trait.NavAgent);
assert(Error, agent != null, "Object does not have a NavAgent trait");
agent.speed = speed;
agent.turnDuration = turnDuration;
agent.setPath(path);
});
#end

View file

@ -1,31 +1,136 @@
package armory.logicnode;
#if arm_patch @:keep @:keepSub #end
class LogicNode {
var tree: LogicTree;
var inputs: Array<LogicNodeInput> = [];
var outputs: Array<Array<LogicNode>> = [];
var inputs: Array<LogicNodeLink> = [];
var outputs: Array<Array<LogicNodeLink>> = [];
#if arm_debug
#if (arm_debug || arm_patch)
public var name = "";
public function watch(b: Bool) { // Watch in debug console
var nodes = armory.trait.internal.DebugConsole.watchNodes;
b ? nodes.push(this) : nodes.remove(this);
}
#if (arm_debug)
public function watch(b: Bool) { // Watch in debug console
var nodes = armory.trait.internal.DebugConsole.watchNodes;
b ? nodes.push(this) : nodes.remove(this);
}
#end
#end
public function new(tree: LogicTree) {
this.tree = tree;
}
public function addInput(node: LogicNode, from: Int) {
inputs.push(new LogicNodeInput(node, from));
/**
Resize the inputs array to a given size to minimize dynamic
reallocation and over-allocation later.
**/
inline function preallocInputs(amount: Int) {
this.inputs.resize(amount);
}
public function addOutputs(nodes: Array<LogicNode>) {
outputs.push(nodes);
/**
Resize the outputs array to a given size to minimize dynamic
reallocation and over-allocation later.
**/
inline function preallocOutputs(amount: Int) {
this.outputs.resize(amount);
for (i in 0...outputs.length) {
outputs[i] = [];
}
}
/**
Add a link between to nodes to the tree.
**/
public static function addLink(fromNode: LogicNode, toNode: LogicNode, fromIndex: Int, toIndex: Int): LogicNodeLink {
var link = new LogicNodeLink(fromNode, toNode, fromIndex, toIndex);
if (toNode.inputs.length <= toIndex) {
toNode.inputs.resize(toIndex + 1);
}
toNode.inputs[toIndex] = link;
var fromNodeOuts = fromNode.outputs;
var outLen = fromNodeOuts.length;
if (outLen <= fromIndex) {
fromNodeOuts.resize(fromIndex + 1);
// Initialize with empty arrays
for (i in outLen...fromIndex + 1) {
fromNodeOuts[i] = [];
}
}
fromNodeOuts[fromIndex].push(link);
return link;
}
#if arm_patch
/**
Removes a link from the tree.
**/
static function removeLink(link: LogicNodeLink) {
link.fromNode.outputs[link.fromIndex].remove(link);
// Reuse the same link and connect a default input node to it.
// That's why this function is only available in arm_patch mode, we need
// access to the link's type and value.
link.fromNode = LogicNode.createSocketDefaultNode(link.toNode.tree, link.toType, link.toValue);
link.fromIndex = 0;
}
/**
Removes all inputs and their links from this node.
Warning: this function changes the amount of node inputs to 0!
**/
function clearInputs() {
for (link in inputs) {
link.fromNode.outputs[link.fromIndex].remove(link);
}
inputs.resize(0);
}
/**
Removes all outputs and their links from this node.
Warning: this function changes the amount of node inputs to 0!
**/
function clearOutputs() {
for (links in outputs) {
for (link in links) {
var defaultNode = LogicNode.createSocketDefaultNode(tree, link.toType, link.toValue);
link.fromNode = defaultNode;
link.fromIndex = 0;
defaultNode.outputs[0] = [link];
}
}
outputs.resize(0);
}
/**
Creates a default node for a socket so that get() and set() can be
used without null checks.
Loosely equivalent to `make_logic.build_default_node()` in Python.
**/
static inline function createSocketDefaultNode(tree: LogicTree, socketType: String, value: Dynamic): LogicNode {
// Make sure to not add these nodes to the LogicTree.nodes array as they
// won't be garbage collected then if unlinked later.
return switch (socketType) {
case "VECTOR": new armory.logicnode.VectorNode(tree, value[0], value[1], value[2]);
case "RGBA": new armory.logicnode.ColorNode(tree, value[0], value[1], value[2], value[3]);
case "RGB": new armory.logicnode.ColorNode(tree, value[0], value[1], value[2]);
case "VALUE": new armory.logicnode.FloatNode(tree, value);
case "INT": new armory.logicnode.IntegerNode(tree, value);
case "BOOLEAN": new armory.logicnode.BooleanNode(tree, value);
case "STRING": new armory.logicnode.StringNode(tree, value);
case "NONE": new armory.logicnode.NullNode(tree);
case "OBJECT": new armory.logicnode.ObjectNode(tree, value);
default: new armory.logicnode.DynamicNode(tree, value);
}
}
#end
/**
Called when this node is activated.
@param from impulse index
@ -38,42 +143,45 @@ class LogicNode {
**/
function runOutput(i: Int) {
if (i >= outputs.length) return;
for (o in outputs[i]) {
// Check which input activated the node
for (j in 0...o.inputs.length) {
if (o.inputs[j].node == this) {
o.run(j);
break;
}
}
for (outLink in outputs[i]) {
outLink.toNode.run(outLink.toIndex);
}
}
@:allow(armory.logicnode.LogicNodeInput)
@:allow(armory.logicnode.LogicNodeLink)
function get(from: Int): Dynamic { return this; }
@:allow(armory.logicnode.LogicNodeInput)
@:allow(armory.logicnode.LogicNodeLink)
function set(value: Dynamic) {}
}
class LogicNodeInput {
@:allow(armory.logicnode.LogicNode)
@:allow(armory.logicnode.LogicTree)
class LogicNodeLink {
@:allow(armory.logicnode.LogicNode)
var node: LogicNode;
var from: Int; // Socket index
var fromNode: LogicNode;
var toNode: LogicNode;
var fromIndex: Int;
var toIndex: Int;
public function new(node: LogicNode, from: Int) {
this.node = node;
this.from = from;
#if arm_patch
var fromType: String;
var toType: String;
var toValue: Dynamic;
#end
inline function new(fromNode: LogicNode, toNode: LogicNode, fromIndex: Int, toIndex: Int) {
this.fromNode = fromNode;
this.toNode = toNode;
this.fromIndex = fromIndex;
this.toIndex = toIndex;
}
@:allow(armory.logicnode.LogicNode)
function get(): Dynamic {
return node.get(from);
inline function get(): Dynamic {
return fromNode.get(fromIndex);
}
@:allow(armory.logicnode.LogicNode)
function set(value: Dynamic) {
node.set(value);
inline function set(value: Dynamic) {
fromNode.set(value);
}
}

View file

@ -2,10 +2,26 @@ package armory.logicnode;
class LogicTree extends iron.Trait {
#if arm_patch
/**
Stores all trait instances of the tree via its name.
**/
public static var nodeTrees = new Map<String, Array<LogicTree>>();
/**
[node name => logic node] for later node replacement for live patching.
**/
public var nodes: Map<String, LogicNode>;
#end
public var loopBreak = false; // Trigger break from loop nodes
public function new() {
super();
#if arm_patch
nodes = new Map<String, LogicNode>();
#end
}
public function add() {}

View file

@ -37,6 +37,6 @@ class LookAtNode extends LogicNode {
v2.setFrom(vto).sub(vfrom).normalize();
q.fromTo(v1, v2);
return q.getEuler();
return q;
}
}

View file

@ -3,7 +3,7 @@ package armory.logicnode;
class MathNode extends LogicNode {
public var property0: String; // Operation
public var property1: String; // Clamp
public var property1: Bool; // Clamp
public function new(tree: LogicTree) {
super(tree);
@ -80,8 +80,8 @@ class MathNode extends LogicNode {
}
}
// Clamp
if (property1 == "true") r = r < 0.0 ? 0.0 : (r > 1.0 ? 1.0 : r);
if (property1) r = r < 0.0 ? 0.0 : (r > 1.0 ? 1.0 : r);
return r;
}
}
}

View file

@ -6,7 +6,7 @@ class MixNode extends LogicNode {
public var property0: String; // Type
public var property1: String; // Ease
public var property2: String; // Clamp
public var property2: Bool; // Clamp
var ease: Float->Float = null;
@ -50,7 +50,9 @@ class MixNode extends LogicNode {
var v2: Float = inputs[2].get();
var f = v1 + (v2 - v1) * ease(k);
if (property2 == "true") f = f < 0 ? 0 : f > 1 ? 1 : f;
// Clamp
if (property2) f = f < 0 ? 0 : f > 1 ? 1 : f;
return f;
}
}

View file

@ -21,7 +21,7 @@ class ObjectNode extends LogicNode {
override function set(value: Dynamic) {
if (inputs.length > 0) inputs[0].set(value);
else {
objectName = value.name;
objectName = value != null ? value.name : "";
this.value = value;
}
}

View file

@ -4,7 +4,7 @@ import armory.trait.internal.CanvasScript;
import iron.Scene;
#if arm_ui
import zui.Canvas.Anchor;
import armory.ui.Canvas.Anchor;
#end
class OnCanvasElementNode extends LogicNode {

View file

@ -0,0 +1,35 @@
package armory.logicnode;
import armory.system.InputMap;
class OnInputMapNode extends LogicNode {
var inputMap: Null<InputMap>;
public function new(tree: LogicTree) {
super(tree);
tree.notifyOnUpdate(update);
}
function update() {
var i = inputs[0].get();
inputMap = InputMap.getInputMap(i);
if (inputMap != null) {
if (inputMap.started()) {
runOutput(0);
}
if (inputMap.released()) {
runOutput(1);
}
}
}
override function get(from: Int): Dynamic {
if (from == 2) return inputMap.value();
else return inputMap.lastKeyPressed;
}
}

View file

@ -0,0 +1,22 @@
package armory.logicnode;
class OncePerFrameNode extends LogicNode {
var c = false;
public function new(tree: LogicTree) {
super(tree);
tree.notifyOnUpdate(update);
}
override function run(from: Int) {
if(c) {
c = false;
runOutput(0);
}
}
function update() {
c = true;
}
}

View file

@ -8,7 +8,7 @@ class PauseTraitNode extends LogicNode {
override function run(from: Int) {
var trait: Dynamic = inputs[1].get();
if (trait == null || !Std.is(trait, LogicTree)) return;
if (trait == null || !Std.isOfType(trait, LogicTree)) return;
cast(trait, LogicTree).pause();

View file

@ -3,50 +3,39 @@ package armory.logicnode;
#if arm_physics
import armory.trait.physics.bullet.PhysicsConstraint.ConstraintAxis;
#end
import iron.object.Object;
class PhysicsConstraintNode extends LogicNode {
public var property0: String;//Linear or Angular
public var property1: String;//Axis
public var property2: String;//Is a spring
public var value1: Float;//Lower limit or Spring Stiffness
public var value2: Float;//Upper limit or Spring Damping
public var property0: String; //Linear or Angular
public var property1: String; //Axis
public var property2: Bool; //Is a spring
#if arm_physics
public var value1: Float; //Lower limit or Spring Stiffness
public var value2: Float; //Upper limit or Spring Damping
public var isAngular: Bool;
public var axis: ConstraintAxis;
public var isSpring: Bool;
#end
public function new(tree: LogicTree) {
super(tree);
}
override function get(from: Int): PhysicsConstraintNode {
#if arm_physics
value1 = inputs[0].get();
value2 = inputs[1].get();
if(property0 == 'Linear') {
isAngular = false;
}
else{
isAngular = true;
}
isAngular = property0 != "Linear";
isSpring = property2;
if(property2 == 'true'){
isSpring = true;
switch (property1) {
case "X": axis = X;
case "Y": axis = Y;
case "Z": axis = Z;
}
else {
isSpring = false;
}
switch (property1){
case 'X':
axis = X;
case 'Y':
axis = Y;
case 'Z':
axis = Z;
}
#end
return this;
}
}

View file

@ -4,8 +4,6 @@ import iron.math.Vec4;
class PickObjectNode extends LogicNode {
var v = new Vec4();
public function new(tree: LogicTree) {
super(tree);
}
@ -24,9 +22,14 @@ class PickObjectNode extends LogicNode {
if (from == 0) { // Object
return rb.object;
}
else { // Hit
else if(from == 1){ // Hit
var v = new Vec4();
return v.set(physics.hitPointWorld.x, physics.hitPointWorld.y, physics.hitPointWorld.z);
}
else { // Normal
var v = new Vec4();
return v.set(physics.hitNormalWorld.x, physics.hitNormalWorld.y, physics.hitNormalWorld.z, 0);
}
#end
return null;
}

View file

@ -19,7 +19,7 @@ class QuaternionMathNode extends LogicNode {
override function get(from: Int): Dynamic {
switch (property0) {
// 1 argument: Module, Normalize, GetEuler
// 1 argument: Module, Normalize
case "Module": {
var q: Quat = inputs[0].get();
if (q == null) return null;
@ -32,159 +32,106 @@ class QuaternionMathNode extends LogicNode {
res_q.setFrom(q);
res_q = res_q.normalize();
}
case "GetEuler": {
var q: Quat = inputs[0].get();
if (q == null) return null;
res_q.setFrom(q);
res_v = res_q.getEuler();
}
// 2 arguments: FromTo, FromMat, FromRotationMat, ToAxisAngle
case "FromTo": {
var v1: Vec4 = inputs[0].get();
var v2: Vec4 = inputs[1].get();
if ((v1 == null) || (v2 == null)) return null;
res_q.fromTo(v1, v2);
}
case "FromMat": {
var q: Quat = inputs[0].get();
var m: Mat4 = inputs[1].get();
if ((q == null) || (m == null)) return null;
res_q.setFrom(q);
res_q = res_q.fromMat(m);
}
case "FromRotationMat": {
var q: Quat = inputs[0].get();
var m: Mat4 = inputs[1].get();
if ((q == null) || (m == null)) return null;
res_q.setFrom(q);
res_q = res_q.fromRotationMat(m);
}
case "ToAxisAngle": {
var q: Quat = inputs[0].get();
var v: Vec4 = inputs[1].get();
if ((q == null) || (v == null)) return null;
res_q.setFrom(q);
res_f = res_q.toAxisAngle(v);
}
// # 3 arguments: Lerp, Slerp, FromAxisAngle, FromEuler
case "Lerp": {
var from: Quat = inputs[0].get();
var to: Quat = inputs[1].get();
var f: Float = inputs[2].get();
if ((from == null) || (to == null)) return null;
res_q = res_q.lerp(from, to, f);
}
case "Slerp": {
var from: Quat = inputs[0].get();
var to: Quat = inputs[1].get();
var f: Float = inputs[2].get();
if ((from == null) || (to == null)) return null;
res_q = res_q.slerp(from, to, f);
}
case "FromAxisAngle": {
var q: Quat = inputs[0].get();
var axis: Vec4 = inputs[1].get();
var angle: Float = inputs[2].get();
if ((q == null) || (axis == null)) return null;
res_q.setFrom(q);
res_q = res_q.fromAxisAngle(axis, angle);
}
case "FromEuler": {
var x: Float = inputs[0].get();
var y: Float = inputs[1].get();
var z: Float = inputs[2].get();
res_q = res_q.fromEuler(x, y, z);
}
// Many arguments: Add, Subtract, DotProduct, Multiply
case "Add": {
var q: Quat = inputs[0].get();
if (q == null) return null;
res_q.setFrom(q);
var q2 = new Quat();
res_v = inputs[0].get();
res_f = inputs[1].get();
if (res_v == null || res_f == null) return null;
res_q.set(res_v.x, res_v.y, res_v.z, res_f);
var i = 1;
while (i < inputs.length) {
q2 = inputs[i].get();
if (q2 == null) return null;
res_q.add(q2);
while (2*i+1 < inputs.length) {
res_v = inputs[2*i].get();
res_f = inputs[2*i+1].get();
if (res_v == null || res_f == null) return null;
res_q.x += res_v.x;
res_q.y += res_v.y;
res_q.z += res_v.z;
res_q.w += res_f;
i++;
}
}
case "Subtract": {
var q: Quat = inputs[0].get();
if (q == null) return null;
res_q.setFrom(q);
var q2 = new Quat();
res_v = inputs[0].get();
res_f = inputs[1].get();
if (res_v == null || res_f == null) return null;
res_q.set(res_v.x, res_v.y, res_v.z, res_f);
var i = 1;
while (i < inputs.length) {
q2 = inputs[i].get();
if (q2 == null) return null;
res_q.sub(q2);
while (2*i+1 < inputs.length) {
res_v = inputs[2*i].get();
res_f = inputs[2*i+1].get();
if (res_v == null || res_f == null) return null;
res_q.x -= res_v.x;
res_q.y -= res_v.y;
res_q.z -= res_v.z;
res_q.w -= res_f;
i++;
}
}
case "Multiply": {
var q: Quat = inputs[0].get();
if (q == null) return null;
res_q.setFrom(q);
var q2 = new Quat();
res_v = inputs[0].get();
res_f = inputs[1].get();
if (res_v == null || res_f == null) return null;
res_q.set(res_v.x, res_v.y, res_v.z, res_f);
var i = 1;
while (i < inputs.length) {
q2 = inputs[i].get();
if (q2 == null) return null;
res_q.mult(q2);
while (2*i+1 < inputs.length) {
res_v = inputs[2*i].get();
res_f = inputs[2*i+1].get();
if (res_v == null || res_f == null) return null;
var temp_q = new Quat(res_v.x, res_v.y, res_v.z, res_f);
res_q.mult(temp_q);
i++;
}
}
case "MultiplyFloats": {
var q: Quat = inputs[0].get();
if (q == null) return null;
res_q.setFrom(q);
res_v = inputs[0].get();
res_f = inputs[1].get();
if (res_v == null || res_f == null) return null;
res_q.set(res_v.x, res_v.y, res_v.z, res_f);
var f: Float = 1.0;
var i = 1;
var i = 2;
while (i < inputs.length) {
f = inputs[i].get();
res_q.scale(f);
f *= inputs[i].get();
if (f == null) return null;
i++;
}
res_q.scale(f);
}
case "DotProduct": {
var q: Quat = inputs[0].get();
if (q == null) return null;
res_q.setFrom(q);
var q2 = new Quat();
case "DotProduct": { // what this does with more than 2 terms is not *remotely* intuitive. Heck, you could consider it a footgun!
res_v = inputs[0].get();
var temp_f = inputs[1].get();
if (res_v == null || temp_f == null) return null;
res_q.set(res_v.x, res_v.y, res_v.z, temp_f);
var i = 1;
while (i < inputs.length) {
q2 = inputs[i].get();
if (q2 == null) return null;
res_f = res_q.dot(q2);
while (2*i+1 < inputs.length) {
res_v = inputs[2*i].get();
temp_f = inputs[2*i+1].get();
if (res_v == null || temp_f == null) return null;
var temp_q = new Quat(res_v.x, res_v.y, res_v.z, temp_f);
res_f = res_q.dot(temp_q);
res_q.set(res_f, res_f, res_f, res_f);
i++;
}
}
}
// Return and check separator
switch (from) {
case 0: {
if (property0 == 'GetEuler')
return res_v;
else
return res_q;
return res_q;
}
case 1:
if (property1) {
return res_q.x;
} else {
if (property0 == "DotProduct" || property0 == "Module") {
return res_f;
} else {
return null;
}
case 2:
if (property1) return res_q.y;
case 3:
if (property1) return res_q.z;
case 4:
if (property1) return res_q.w;
case 5:
if (property1) return res_f;
default: {
return null;
}
}
return null;
}
}

View file

@ -11,10 +11,10 @@ class QuaternionNode extends LogicNode {
super(tree);
if (x != null) {
addInput(new FloatNode(tree, x), 0);
addInput(new FloatNode(tree, y), 0);
addInput(new FloatNode(tree, z), 0);
addInput(new FloatNode(tree, w), 0);
LogicNode.addLink(new FloatNode(tree, x), this, 0, 0);
LogicNode.addLink(new FloatNode(tree, y), this, 0, 1);
LogicNode.addLink(new FloatNode(tree, z), this, 0, 2);
LogicNode.addLink(new FloatNode(tree, w), this, 0, 3);
}
}
@ -27,15 +27,15 @@ class QuaternionNode extends LogicNode {
switch (from){
case 0:
return value;
case 1:
var value1 = new Vec4();
case 1:
var value1 = new Vec4();
value1.x = value.x;
value1.y = value.y;
value1.z = value.z;
value1.w = 0; // use 0 to avoid this vector being translated.
return value1;
case 2:
return value.w;
return value.w;
default:
return null;
}

View file

@ -4,18 +4,17 @@ import iron.math.Vec4;
class RandomColorNode extends LogicNode {
var v = new Vec4();
public function new(tree: LogicTree) {
super(tree);
}
override function get(from: Int): Dynamic {
var r = Math.random();
var g = Math.random();
var b = Math.random();
// var a = Math.random();
v.set(r, g, b);
var v = new Vec4(r, g, b);
return v;
}
}

View file

@ -0,0 +1,19 @@
package armory.logicnode;
import armory.system.InputMap;
class RemoveInputMapKeyNode extends LogicNode {
public function new(tree: LogicTree) {
super(tree);
}
override function run(from: Int) {
var inputMap = inputs[1].get();
var key = inputs[2].get();
if (InputMap.removeInputMapKey(inputMap, key)) {
runOutput(0);
}
}
}

View file

@ -8,7 +8,7 @@ class ResumeTraitNode extends LogicNode {
override function run(from: Int) {
var trait: Dynamic = inputs[1].get();
if (trait == null || !Std.is(trait, LogicTree)) return;
if (trait == null || !Std.isOfType(trait, LogicTree)) return;
cast(trait, LogicTree).resume();

View file

@ -2,13 +2,11 @@ package armory.logicnode;
import iron.object.Object;
import iron.math.Quat;
import iron.math.Vec4;
import armory.trait.physics.RigidBody;
class RotateObjectNode extends LogicNode {
public var property0 = "Euler Angles";
var q = new Quat();
public var property0 = "Local";
public function new(tree: LogicTree) {
super(tree);
@ -16,32 +14,19 @@ class RotateObjectNode extends LogicNode {
override function run(from: Int) {
var object: Object = inputs[1].get();
var vec: Vec4 = inputs[2].get();
var q: Quat = inputs[2].get();
// note: here, the next line is disabled because old versions of the node don't have a third input.
// when those old versions will be considered remove, feel free to uncomment that, and replace the other `inputs[3].get()` by `w` in this file.
//var w: Float = inputs[3].get();
if (object == null || q == null) return;
if (object == null || vec == null) return;
switch (property0) {
case "Euler Angles":
q.fromEuler(vec.x, vec.y, vec.z);
case "Angle Axies (Degrees)" | "Angle Axies (Radians)":
var angle: Float = inputs[3].get();
if (property0 == "Angle Axies (Degrees)") {
angle = angle * (Math.PI / 180);
}
var angleSin = Math.sin(angle / 2);
vec = vec.normalize();
var angleCos = Math.cos(angle / 2);
q = new Quat(vec.x * angleSin, vec.y * angleSin, vec.z * angleSin, angleCos);
case "Quaternion":
q = new Quat(vec.x, vec.y, vec.z, inputs[3].get());
q.normalize();
q.normalize();
switch (property0){
case "Local":
object.transform.rot.mult(q);
case "Global":
object.transform.rot.multquats(q, object.transform.rot);
// that function call (Quat.multquats) is weird: it both modifies the object, and returns `this`
}
object.transform.rot.mult(q);
object.transform.buildMatrix();
#if arm_physics

View file

@ -0,0 +1,96 @@
package armory.logicnode;
import iron.math.Quat;
import iron.math.Vec4;
import iron.math.Mat4;
import kha.FastFloat;
class RotationMathNode extends LogicNode {
public var property0: String; // Operation
var res_q = new Quat();
var res_v = new Vec4();
var res_f: FastFloat = 0.0;
public function new(tree: LogicTree) {
super(tree);
}
override function get(from: Int): Dynamic {
//var q: Quat = inputs[0].get();
//if (q==null) return null;
//var res_q: Quat = new Quat();
switch (property0) {
// 1 argument: Normalize, Inverse
case "Normalize": {
var q: Quat = inputs[0].get();
if (q==null) return null;
res_q.setFrom(q);
res_q = res_q.normalize();
}
case "Inverse": {
var q: Quat = inputs[0].get();
if (q==null) return null;
var modl = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w;
modl = -1/modl;
res_q.w = -q.w*modl;
res_q.x = q.x*modl;
res_q.y = q.y*modl;
res_q.z = q.z*modl;
}
// 2 arguments: Compose, Amplify, FromTo, FromRotationMat,
case "FromTo": {
var v1: Vec4 = inputs[0].get();
var v2: Vec4 = inputs[1].get();
if ((v1 == null) || (v2 == null)) return null;
res_q.fromTo(v1, v2);
}
case "Compose": {
var v1: Quat = inputs[0].get();
var v2: Quat = inputs[1].get();
if ((v1 == null) || (v2 == null)) return null;
res_q.multquats(v1,v2);
}
case "Amplify": {
var v1: Quat = inputs[0].get();
var v2: Float = inputs[1].get();
if ((v1 == null) || (v2 == null)) return null;
res_q.setFrom(v1);
var fac2 = Math.sqrt(1- res_q.w*res_q.w);
if (fac2 > 0.001) {
var fac1 = v2*Math.acos(res_q.w);
res_q.w = Math.cos(fac1);
fac1 = Math.sin(fac1)/fac2;
res_q.x *= fac1;
res_q.y *= fac1;
res_q.z *= fac1;
}
}
//case "FromRotationMat": {
// var m: Mat4 = inputs[1].get();
// if (m == null) return null;
// res_q = res_q.fromMat(m);
//}
// # 3 arguments: Lerp, Slerp, FromAxisAngle, FromEuler
case "Lerp": {
//var from = q;
var from: Quat = inputs[0].get();
var to: Quat = inputs[1].get();
var f: Float = inputs[2].get();
if ((from == null) || (f == null) || (to == null)) return null;
res_q = res_q.lerp(from, to, f);
}
case "Slerp": {
//var from = q;
var from:Quat = inputs[0].get();
var to: Quat = inputs[1].get();
var f: Float = inputs[2].get();
if ((from == null) || (f == null) || (to == null)) return null;
res_q = res_q.slerp(from, to, f);
}
}
return res_q;
}
}

View file

@ -0,0 +1,120 @@
package armory.logicnode;
import iron.math.Vec4;
import iron.math.Quat;
import kha.FastFloat;
class RotationNode extends LogicNode {
static inline var toRAD: FastFloat = 0.017453292519943295; // 180/pi
public var property0: String; // type of input (EulerAngles, AxisAngle, Quaternion)
public var property1: String; // angle unit (Deg, Rad)
public var property2: String; // euler order (XYZ, XZY, etc…)
public var value: Quat;
//var input0_cache: Vec4 = new Vec4();
//var input1_cache: Float = 0;
var input_length: Int = 0;
public function new(tree: LogicTree, x: Null<Float> = null,
y: Null<Float> = null,
z: Null<Float> = null,
w: Null<Float> = null
) {
super(tree);
this.value = new Quat();
if (x!=null) this.value.set(x,y,z,w);
for (input in inputs) {
if (input !=null)
this.input_length +=1;
else
break;
}
}
override function get(from: Int): Dynamic {
//var inp0 = inputs[0].get();
//var inp
//if (inputs[0].get())
if (inputs.length == 0){
return this.value;
}
switch (property0){
case "Quaternion": {
if (inputs[0]!=null && inputs[1]!=null) {
var vect: Vec4 = inputs[0].get();
value.x = vect.x;
value.y = vect.y;
value.z = vect.z;
value.w = inputs[1].get();
}
}
case "AxisAngle": {
if (inputs[0]!=null && inputs[1]!=null){
var vec: Vec4 = inputs[0].get();
var angle: FastFloat = inputs[1].get();
if (property1=="Deg")
angle *= toRAD;
value.fromAxisAngle(vec, angle);
}
}
case "EulerAngles": {
if (inputs[0] != null){
var vec: Vec4 = new Vec4().setFrom(inputs[0].get());
if (property1=="Deg"){
vec.x *= toRAD;
vec.y *= toRAD;
vec.z *= toRAD;
}
this.value.fromEulerOrdered(vec, property2);
}
}
default: {
return property0;
}
}
return this.value;
}
override function set(value: Dynamic) {
switch (property0){
case "Quaternion": {
if (input_length>1) {
var vect = new Vec4();
vect.x = value.x;
vect.y = value.y;
vect.z = value.z;
inputs[0].set(vect);
inputs[1].set(value.w);
}
}
case "AxisAngle": {
if (input_length>1){
var vec = new Vec4();
var angle = this.value.toAxisAngle(vec);
if (property1=="Deg")
angle /= toRAD;
inputs[0].set(vec);
inputs[1].set(angle);
}
}
case "EulerAngles": {
if (input_length>0){
var vec:Vec4 = value.toEulerOrdered(property2);
if (property1=="Deg"){
vec.x /= toRAD;
vec.y /= toRAD;
vec.z /= toRAD;
}
inputs[0].set(vec);
}
}
}
if (input_length > 0){
// NYI
}else this.value=value;
}
}

View file

@ -0,0 +1,37 @@
package armory.logicnode;
class SelectNode extends LogicNode {
/** Execution mode. **/
public var property0: String;
var value: Dynamic = null;
public function new(tree: LogicTree) {
super(tree);
}
override function run(from: Int) {
// Get value according to the activated input (run() can only be called
// if the execution mode is from_input).
value = inputs[from + Std.int(inputs.length / 2)].get();
runOutput(0);
}
override function get(from: Int): Dynamic {
if (property0 == "from_index") {
var index = inputs[0].get() + 2;
// Return default value for invalid index
if (index < 2 || index >= inputs.length) {
return inputs[1].get();
}
return inputs[index].get();
}
// from_input
return value;
}
}

View file

@ -0,0 +1,60 @@
package armory.logicnode;
import kha.FastFloat;
import iron.math.Quat;
import iron.math.Vec4;
class SeparateRotationNode extends LogicNode {
public var property0 = "EulerAngles"; // EulerAngles, AxisAngle, or Quat
public var property1 = "Rad"; // Rad or Deg
public var property2 = "XYZ";
static inline var toDEG:FastFloat = 57.29577951308232; // 180/pi
var input_cache = new Quat();
var euler_cache = new Vec4();
var aa_axis_cache = new Vec4();
var aa_angle_cache: Float = 0;
public function new(tree: LogicTree) {
super(tree);
}
override function get(from: Int): Dynamic {
var q: Quat = inputs[0].get();
if (q == null) return null;
q.normalize();
switch (property0) {
case "EulerAngles":
if (q!=this.input_cache)
euler_cache = q.toEulerOrdered(property2);
if (from>0)
return null;
switch (property1){
case "Rad": return euler_cache;
case "Deg": return new Vec4(euler_cache.x*toDEG, euler_cache.y*toDEG, euler_cache.z*toDEG);
}
case "AxisAngle":
if (q!=this.input_cache)
aa_angle_cache = q.toAxisAngle(aa_axis_cache);
switch (from){
case 0: return aa_axis_cache;
case 1: switch(property1){
case "Rad": return aa_angle_cache;
case "Deg": return toDEG*aa_angle_cache;
}
}
case "Quaternion":
switch(from){
case 0: return new Vec4(q.x,q.y,q.z);
case 1: return q.w;
}
}
return null;
}
}

View file

@ -20,7 +20,7 @@ class SeparateTransformNode extends LogicNode {
matrix.decompose(loc, rot, scale);
if (from == 0) return loc;
else if (from == 1) return rot.getEuler();
else if (from == 1) return rot;
else return scale;
}
}

View file

@ -0,0 +1,25 @@
package armory.logicnode;
import iron.Scene;
import armory.trait.internal.CanvasScript;
class SetGlobalCanvasFontSizeNode extends LogicNode {
var canvas: CanvasScript;
var factor: Int;
public function new(tree: LogicTree) {
super(tree);
}
#if arm_ui
override function run(from: Int) {
factor = inputs[1].get();
canvas = Scene.active.getTrait(CanvasScript);
if (canvas == null) canvas = Scene.active.camera.getTrait(CanvasScript);
canvas.setCanvasFontSize(factor);
runOutput(0);
}
#end
}

View file

@ -0,0 +1,25 @@
package armory.logicnode;
import iron.Scene;
import armory.trait.internal.CanvasScript;
class SetGlobalCanvasScaleNode extends LogicNode {
var canvas: CanvasScript;
var factor: Float;
public function new(tree: LogicTree) {
super(tree);
}
#if arm_ui
override function run(from: Int) {
factor = inputs[1].get();
canvas = Scene.active.getTrait(CanvasScript);
if (canvas == null) canvas = Scene.active.camera.getTrait(CanvasScript);
canvas.setUiScale(factor);
runOutput(0);
}
#end
}

View file

@ -0,0 +1,46 @@
package armory.logicnode;
import armory.system.InputMap;
class SetInputMapKeyNode extends LogicNode {
public var property0: String;
public function new(tree: LogicTree) {
super(tree);
}
override function run(from: Int) {
var inputMap = inputs[1].get();
var key = inputs[2].get();
var scale = inputs[3].get();
var deadzone = inputs[4].get();
var index = inputs[5].get();
var i = InputMap.getInputMap(inputMap);
if (i == null) {
i = InputMap.addInputMap(inputMap);
}
var k = InputMap.getInputMapKey(inputMap, key);
if (k == null) {
switch(property0) {
case "keyboard": k = i.addKeyboard(key, scale);
case "mouse": k = i.addMouse(key, scale, deadzone);
case "gamepad": {
k = i.addGamepad(key, scale, deadzone);
k.setIndex(index);
}
}
} else {
k.scale = scale;
k.deadzone = deadzone;
k.setIndex(index);
}
runOutput(0);
}
}

View file

@ -13,9 +13,21 @@ class SetLocationNode extends LogicNode {
override function run(from: Int) {
var object: Object = inputs[1].get();
var vec: Vec4 = inputs[2].get();
var relative: Bool = inputs[3].get();
if (object == null || vec == null) return;
if (!relative && object.parent != null) {
var loc = vec.clone();
loc.sub(object.parent.transform.world.getLoc()); // Remove parent location influence
// Convert vec to parent local space
var dotX = loc.dot(object.parent.transform.right());
var dotY = loc.dot(object.parent.transform.look());
var dotZ = loc.dot(object.parent.transform.up());
vec.set(dotX, dotY, dotZ);
}
object.transform.loc.setFrom(vec);
object.transform.buildMatrix();
@ -26,4 +38,4 @@ class SetLocationNode extends LogicNode {
runOutput(0);
}
}
}

View file

@ -1,40 +1,40 @@
package armory.logicnode;
import iron.Scene;
import iron.data.MaterialData;
import iron.object.Object;
import armory.trait.internal.UniformsManager;
class SetMaterialImageParamNode extends LogicNode {
static var registered = false;
static var map = new Map<MaterialData, Map<String, kha.Image>>();
public function new(tree: LogicTree) {
super(tree);
if (!registered) {
registered = true;
iron.object.Uniforms.externalTextureLinks.push(textureLink);
}
}
override function run(from: Int) {
var mat = inputs[1].get();
if (mat == null) return;
var entry = map.get(mat);
if (entry == null) {
entry = new Map();
map.set(mat, entry);
var perObject: Null<Bool>;
var object = inputs[1].get();
if(object == null) return;
perObject = inputs[2].get();
if(perObject == null) perObject = false;
var mat = inputs[3].get();
if(mat == null) return;
if(! perObject){
UniformsManager.removeObjectFromMap(object, Texture);
object = Scene.active.root;
}
iron.data.Data.getImage(inputs[3].get(), function(image: kha.Image) {
entry.set(inputs[2].get(), image); // Node name, value
var img = inputs[5].get();
if(img == null) return;
iron.data.Data.getImage(img, function(image: kha.Image) {
UniformsManager.setTextureValue(mat, object, inputs[4].get(), image);
});
runOutput(0);
}
static function textureLink(object: Object, mat: MaterialData, link: String): kha.Image {
if (mat == null) return null;
var entry = map.get(mat);
if (entry == null) return null;
return entry.get(link);
}
}

View file

@ -1,38 +1,35 @@
package armory.logicnode;
import iron.Scene;
import iron.math.Vec4;
import iron.data.MaterialData;
import iron.object.Object;
import armory.trait.internal.UniformsManager;
class SetMaterialRgbParamNode extends LogicNode {
static var registered = false;
static var map = new Map<MaterialData, Map<String, Vec4>>();
public function new(tree: LogicTree) {
super(tree);
if (!registered) {
registered = true;
iron.object.Uniforms.externalVec3Links.push(vec3Link);
}
}
override function run(from: Int) {
var mat = inputs[1].get();
if (mat == null) return;
var entry = map.get(mat);
if (entry == null) {
entry = new Map();
map.set(mat, entry);
var perObject: Null<Bool>;
var object = inputs[1].get();
if(object == null) return;
perObject = inputs[2].get();
if(perObject == null) perObject = false;
var mat = inputs[3].get();
if(mat == null) return;
if(! perObject){
UniformsManager.removeObjectFromMap(object, Vector);
object = Scene.active.root;
}
entry.set(inputs[2].get(), inputs[3].get()); // Node name, value
UniformsManager.setVec3Value(mat, object, inputs[4].get(), inputs[5].get());
runOutput(0);
}
static function vec3Link(object: Object, mat: MaterialData, link: String): iron.math.Vec4 {
if (mat == null) return null;
var entry = map.get(mat);
if (entry == null) return null;
return entry.get(link);
}
}

View file

@ -1,37 +1,35 @@
package armory.logicnode;
import iron.Scene;
import iron.data.MaterialData;
import iron.object.Object;
import armory.trait.internal.UniformsManager;
class SetMaterialValueParamNode extends LogicNode {
static var registered = false;
static var map = new Map<MaterialData, Map<String, Null<kha.FastFloat>>>();
public function new(tree: LogicTree) {
super(tree);
if (!registered) {
registered = true;
iron.object.Uniforms.externalFloatLinks.push(floatLink);
}
}
override function run(from: Int) {
var mat = inputs[1].get();
if (mat == null) return;
var entry = map.get(mat);
if (entry == null) {
entry = new Map();
map.set(mat, entry);
var perObject: Null<Bool>;
var object = inputs[1].get();
if(object == null) return;
perObject = inputs[2].get();
if(perObject == null) perObject = false;
var mat = inputs[3].get();
if(mat == null) return;
if(! perObject){
UniformsManager.removeObjectFromMap(object, Float);
object = Scene.active.root;
}
entry.set(inputs[2].get(), inputs[3].get()); // Node name, value
UniformsManager.setFloatValue(mat, object, inputs[4].get(), inputs[5].get());
runOutput(0);
}
static function floatLink(object: Object, mat: MaterialData, link: String): Null<kha.FastFloat> {
if (mat == null) return null;
var entry = map.get(mat);
if (entry == null) return null;
return entry.get(link);
}
}

View file

@ -0,0 +1,25 @@
package armory.logicnode;
import iron.object.MeshObject;
class SetObjectShapeKeyNode extends LogicNode {
public function new(tree: LogicTree) {
super(tree);
}
override function run(from: Int) {
#if arm_morph_target
var object: Dynamic = inputs[1].get();
var shapeKey: String = inputs[2].get();
var value: Dynamic = inputs[3].get();
assert(Error, object != null, "Object should not be null");
var morph = cast(object, MeshObject).morphTarget;
assert(Error, morph != null, "Object does not have shape keys");
morph.setMorphValue(shapeKey, value);
#end
runOutput(0);
}
}

View file

@ -14,8 +14,9 @@ class SetParentNode extends LogicNode {
var parent: Object;
var isUnparent = false;
if (Std.is(inputs[2].node, ObjectNode)) {
var parentNode = cast(inputs[2].node, ObjectNode);
if (Std.isOfType(inputs[2].fromNode, ObjectNode)) {
var parentNode = cast(inputs[2].fromNode, ObjectNode);
isUnparent = parentNode.objectName == "";
}
if (isUnparent) parent = iron.Scene.active.root;
@ -24,7 +25,7 @@ class SetParentNode extends LogicNode {
if (object == null || parent == null || object.parent == parent) return;
object.parent.removeChild(object, isUnparent); // keepTransform
#if arm_physics
var rigidBody = object.getTrait(RigidBody);
if (rigidBody != null) rigidBody.setActivationState(0);

View file

@ -1,13 +1,13 @@
package armory.logicnode;
import iron.object.Object;
import iron.math.Quat;
import iron.math.Vec4;
import armory.trait.physics.RigidBody;
class SetRotationNode extends LogicNode {
public var property0: String;
public var property0: String; // UNUSED
public function new(tree: LogicTree) {
super(tree);
@ -16,27 +16,13 @@ class SetRotationNode extends LogicNode {
override function run(from: Int) {
var object: Object = inputs[1].get();
if (object == null) return;
var vec: Vec4 = inputs[2].get();
if (vec == null) return;
var w: Float = inputs[3].get();
var q: Quat = inputs[2].get();
if (q == null) return;
switch (property0) {
case "Euler Angles":
object.transform.rot.fromEuler(vec.x, vec.y, vec.z);
case "Angle Axies (Degrees)" | "Angle Axies (Radians)":
var angle: Float = w;
if (property0 == "Angle Axies (Degrees)") {
angle = angle * (Math.PI / 180);
}
var angleSin = Math.sin(angle / 2);
vec = vec.normalize();
var angleCos = Math.cos(angle / 2);
object.transform.rot = new Quat(vec.x * angleSin, vec.y * angleSin, vec.z * angleSin, angleCos);
case "Quaternion":
object.transform.rot = new Quat(vec.x, vec.y, vec.z, w);
object.transform.rot.normalize();
}
q.normalize();
object.transform.rot = q;
object.transform.buildMatrix();
#if arm_physics
var rigidBody = object.getTrait(RigidBody);
if (rigidBody != null) {

View file

@ -10,7 +10,7 @@ class SetTraitPausedNode extends LogicNode {
var trait: Dynamic = inputs[1].get();
var paused: Bool = inputs[2].get();
if (trait == null || !Std.is(trait, LogicTree)) return;
if (trait == null || !Std.isOfType(trait, LogicTree)) return;
paused ? cast(trait, LogicTree).pause() : cast(trait, LogicTree).resume();

View file

@ -7,9 +7,9 @@ import iron.math.Quat;
class TransformNode extends LogicNode {
var value: Mat4 = Mat4.identity();
static var q = new Quat();
static var v1 = new Vec4();
static var v2 = new Vec4();
var q = new Quat();
var v1 = new Vec4();
var v2 = new Vec4();
public function new(tree: LogicTree) {
super(tree);
@ -17,18 +17,21 @@ class TransformNode extends LogicNode {
override function get(from: Int): Dynamic {
var loc: Vec4 = inputs[0].get();
var rot: Vec4 = inputs[1].get();
var rot: Quat = new Quat().setFrom(inputs[1].get());
rot.normalize();
var scale: Vec4 = inputs[2].get();
if (loc == null && rot == null && scale == null) return this.value;
if (loc == null || rot == null || scale == null) return null;
q.fromEuler(rot.x, rot.y, rot.z);
value.compose(loc, q, scale);
return value;
this.value.compose(loc, rot, scale);
return this.value;
}
override function set(value: Dynamic) {
cast(value, Mat4).decompose(v1, q, v2);
inputs[0].set(v1);
inputs[1].set(q.getEuler());
inputs[2].set(v2);
if (inputs.length>0){
cast(value, Mat4).decompose(v1, q, v2);
inputs[0].set(v1);
inputs[1].set(q);
inputs[2].set(v2);
}else this.value = value;
}
}

View file

@ -0,0 +1,114 @@
package armory.logicnode;
import iron.system.Tween;
class TweenFloatNode extends LogicNode {
public var property0:String;
public var anim: TAnim;
public var fromValue:Float = 0.0;
public var toValue:Float = 1.0;
public var duration:Float = 1.0;
public function new(tree:LogicTree) {
super(tree);
}
override function run(from:Int) {
if(from == 0){
if(anim != null){
Tween.stop(anim);
}
fromValue = inputs[2].get();
toValue = inputs[3].get();
duration = inputs[4].get();
var type:Dynamic = Linear;
switch (property0) {
case "Linear":
type = Linear;
case "SineIn":
type = SineIn;
case "SineOut":
type = SineOut;
case "SineInOut":
type = SineInOut;
case "QuadIn":
type = QuadIn;
case "QuadOut":
type = QuadOut;
case "QuadInOut":
type = QuadInOut;
case "CubicIn":
type = CubicIn;
case "CubicOut":
type = CubicOut;
case "CubicInOut":
type = CubicInOut;
case "QuartIn":
type = QuartIn;
case "QuartOut":
type = QuartOut;
case "QuartInOut":
type = QuartInOut;
case "QuintIn":
type = QuintIn;
case "QuintOut":
type = QuintOut;
case "QuintInOut":
type = QuintInOut;
case "ExpoIn":
type = ExpoIn;
case "ExpoOut":
type = ExpoOut;
case "ExpoInOut":
type = ExpoInOut;
case "CircIn":
type = CircIn;
case "CircOut":
type = CircOut;
case "CircInOut":
type = CircInOut;
case "BackIn":
type = BackIn;
case "BackOut":
type = BackOut;
case "BackInOut":
type = BackInOut;
}
anim = Tween.to({
target: this,
props: { fromValue: toValue },
duration: duration,
ease: type,
tick: update,
done: done
});
}
else{
if(anim != null){
Tween.stop(anim);
}
}
runOutput(0);
}
override function get(from: Int): Dynamic {
if(from == 3) return fromValue;
return null;
}
function update() {
runOutput(1);
}
function done() {
runOutput(2);
}
}

View file

@ -0,0 +1,116 @@
package armory.logicnode;
import iron.math.Quat;
import iron.system.Tween;
class TweenRotationNode extends LogicNode {
public var property0:String;
public var anim: TAnim;
public var fromValue:Quat = new Quat();
public var toValue:Quat = new Quat();
public var duration:Float = 1.0;
public function new(tree:LogicTree) {
super(tree);
}
override function run(from:Int) {
if(from == 0){
if(anim != null){
Tween.stop(anim);
}
fromValue.setFrom(inputs[2].get());
toValue.setFrom(inputs[3].get());
duration = inputs[4].get();
var type:Dynamic = Linear;
switch (property0) {
case "Linear":
type = Linear;
case "SineIn":
type = SineIn;
case "SineOut":
type = SineOut;
case "SineInOut":
type = SineInOut;
case "QuadIn":
type = QuadIn;
case "QuadOut":
type = QuadOut;
case "QuadInOut":
type = QuadInOut;
case "CubicIn":
type = CubicIn;
case "CubicOut":
type = CubicOut;
case "CubicInOut":
type = CubicInOut;
case "QuartIn":
type = QuartIn;
case "QuartOut":
type = QuartOut;
case "QuartInOut":
type = QuartInOut;
case "QuintIn":
type = QuintIn;
case "QuintOut":
type = QuintOut;
case "QuintInOut":
type = QuintInOut;
case "ExpoIn":
type = ExpoIn;
case "ExpoOut":
type = ExpoOut;
case "ExpoInOut":
type = ExpoInOut;
case "CircIn":
type = CircIn;
case "CircOut":
type = CircOut;
case "CircInOut":
type = CircInOut;
case "BackIn":
type = BackIn;
case "BackOut":
type = BackOut;
case "BackInOut":
type = BackInOut;
}
anim = Tween.to({
target: this,
props: { fromValue: toValue },
duration: duration,
ease: type,
tick: update,
done: done
});
}
else{
if(anim != null){
Tween.stop(anim);
}
}
runOutput(0);
}
override function get(from: Int): Dynamic {
if(from == 3) return fromValue;
return null;
}
function update() {
runOutput(1);
}
function done() {
runOutput(2);
}
}

View file

@ -0,0 +1,116 @@
package armory.logicnode;
import iron.math.Vec4;
import iron.system.Tween;
class TweenVectorNode extends LogicNode {
public var property0:String;
public var anim: TAnim;
public var fromValue:Vec4 = new Vec4();
public var toValue:Vec4 = new Vec4();
public var duration:Float = 1.0;
public function new(tree:LogicTree) {
super(tree);
}
override function run(from:Int) {
if(from == 0){
if(anim != null){
Tween.stop(anim);
}
fromValue.setFrom(inputs[2].get());
toValue.setFrom(inputs[3].get());
duration = inputs[4].get();
var type:Dynamic = Linear;
switch (property0) {
case "Linear":
type = Linear;
case "SineIn":
type = SineIn;
case "SineOut":
type = SineOut;
case "SineInOut":
type = SineInOut;
case "QuadIn":
type = QuadIn;
case "QuadOut":
type = QuadOut;
case "QuadInOut":
type = QuadInOut;
case "CubicIn":
type = CubicIn;
case "CubicOut":
type = CubicOut;
case "CubicInOut":
type = CubicInOut;
case "QuartIn":
type = QuartIn;
case "QuartOut":
type = QuartOut;
case "QuartInOut":
type = QuartInOut;
case "QuintIn":
type = QuintIn;
case "QuintOut":
type = QuintOut;
case "QuintInOut":
type = QuintInOut;
case "ExpoIn":
type = ExpoIn;
case "ExpoOut":
type = ExpoOut;
case "ExpoInOut":
type = ExpoInOut;
case "CircIn":
type = CircIn;
case "CircOut":
type = CircOut;
case "CircInOut":
type = CircInOut;
case "BackIn":
type = BackIn;
case "BackOut":
type = BackOut;
case "BackInOut":
type = BackInOut;
}
anim = Tween.to({
target: this,
props: { fromValue: toValue },
duration: duration,
ease: type,
tick: update,
done: done
});
}
else{
if(anim != null){
Tween.stop(anim);
}
}
runOutput(0);
}
override function get(from: Int): Dynamic {
if(from == 3) return fromValue;
return null;
}
function update() {
runOutput(1);
}
function done() {
runOutput(2);
}
}

View file

@ -7,7 +7,7 @@ class VectorMixNode extends LogicNode {
public var property0: String; // Type
public var property1: String; // Ease
public var property2: String; // Clamp
public var property2: Bool; // Clamp
var v = new Vec4();
@ -57,7 +57,7 @@ class VectorMixNode extends LogicNode {
v.y = v1.y + (v2.y - v1.y) * f;
v.z = v1.z + (v2.z - v1.z) * f;
if (property2 == "true") v.clamp(0, 1);
if (property2) v.clamp(0, 1);
return v;
}
}

View file

@ -10,9 +10,9 @@ class VectorNode extends LogicNode {
super(tree);
if (x != null) {
addInput(new FloatNode(tree, x), 0);
addInput(new FloatNode(tree, y), 0);
addInput(new FloatNode(tree, z), 0);
LogicNode.addLink(new FloatNode(tree, x), this, 0, 0);
LogicNode.addLink(new FloatNode(tree, y), this, 0, 1);
LogicNode.addLink(new FloatNode(tree, z), this, 0, 2);
}
}

View file

@ -3,8 +3,6 @@ package armory.logicnode;
import iron.object.Object;
import iron.math.Vec4;
using armory.object.TransformExtension;
class VectorToObjectOrientationNode extends LogicNode {
public function new(tree: LogicTree) {
@ -18,7 +16,7 @@ class VectorToObjectOrientationNode extends LogicNode {
if (object == null || vec == null) return null;
return object.transform.worldVecToOrientation(vec);
return vec.applyQuat(object.transform.rot);
}
}

View file

@ -1,6 +1,8 @@
package armory.logicnode;
import iron.math.Vec4;
import iron.math.Vec2;
import iron.App;
class WorldToScreenSpaceNode extends LogicNode {
@ -19,7 +21,10 @@ class WorldToScreenSpaceNode extends LogicNode {
v.setFrom(v1);
v.applyproj(cam.V);
v.applyproj(cam.P);
var w = App.w();
var h = App.h();
return v;
return new Vec2((v.x + 1) * 0.5 * w, (-v.y + 1) * 0.5 * h);
}
}

View file

@ -3,8 +3,6 @@ package armory.logicnode;
import iron.math.Vec4;
import iron.object.Object;
using armory.object.TransformExtension;
class WorldVectorToLocalSpaceNode extends LogicNode {
public function new(tree: LogicTree) {
@ -17,7 +15,8 @@ class WorldVectorToLocalSpaceNode extends LogicNode {
if (object == null || worldVec == null) return null;
var localVec: Vec4 = new Vec4();
var localVec = new Vec4();
localVec.sub(object.transform.world.getLoc());
localVec.x = worldVec.dot(object.transform.right());
localVec.y = worldVec.dot(object.transform.look());

View file

@ -34,6 +34,14 @@ class Uniforms {
return armory.data.ConstData.ltcMagTex;
}
#end
#if arm_morph_target
case "_morphDataPos": {
return cast(object, iron.object.MeshObject).morphTarget.morphDataPos;
}
case "_morphDataNor": {
return cast(object, iron.object.MeshObject).morphTarget.morphDataNor;
}
#end
}
var target = iron.RenderPath.active.renderTargets.get(link.endsWith("_depth") ? link.substr(0, link.length - 6) : link);

View file

@ -715,7 +715,9 @@ class ShadowMapAtlas {
public static inline function getMaxAtlasSize(type: String): Int {
#if arm_shadowmap_atlas_single_map
#if (rp_shadowmap_atlas_max_size == 1024)
#if (rp_shadowmap_atlas_max_size == 512)
return 512;
#elseif (rp_shadowmap_atlas_max_size == 1024)
return 1024;
#elseif (rp_shadowmap_atlas_max_size == 2048)
return 2048;
@ -742,7 +744,9 @@ class ShadowMapAtlas {
#end
}
case "spot": {
#if (rp_shadowmap_atlas_max_size_spot == 1024)
#if (rp_shadowmap_atlas_max_size_spot == 512)
return 512;
#elseif (rp_shadowmap_atlas_max_size_spot == 1024)
return 1024;
#elseif (rp_shadowmap_atlas_max_size_spot == 2048)
return 2048;
@ -755,7 +759,9 @@ class ShadowMapAtlas {
#end
}
case "sun": {
#if (rp_shadowmap_atlas_max_size_sun == 1024)
#if (rp_shadowmap_atlas_max_size_sun == 512)
return 512;
#elseif (rp_shadowmap_atlas_max_size_sun == 1024)
return 1024;
#elseif (rp_shadowmap_atlas_max_size_sun == 2048)
return 2048;
@ -768,7 +774,9 @@ class ShadowMapAtlas {
#end
}
default: {
#if (rp_shadowmap_atlas_max_size == 1024)
#if (rp_shadowmap_atlas_max_size == 512)
return 512;
#elseif (rp_shadowmap_atlas_max_size == 1024)
return 1024;
#elseif (rp_shadowmap_atlas_max_size == 2048)
return 2048;

View file

@ -80,11 +80,24 @@ class NishitaData {
**/
public static var radiusPlanet = 6360000;
/** Rayleigh scattering coefficient. **/
public static var rayleighCoeff = new Vec3(5.5e-6, 13.0e-6, 22.4e-6);
/** Rayleigh scattering scale parameter. **/
public static var rayleighScale = 8e3;
/** Mie scattering coefficient. **/
public static var mieCoeff = 2e-5;
/** Mie scattering scale parameter. **/
public static var mieScale = 1.2e3;
/** Ozone scattering coefficient. **/
// The ozone absorption coefficients are taken from Cycles code.
// Because Cycles calculates 21 wavelengths, we use the coefficients
// which are closest to the RGB wavelengths (645nm, 510nm, 440nm).
// Precalculating values by simulating Blender's spec_to_xyz() function
// to include all 21 wavelengths gave unrealistic results.
public static var ozoneCoeff = new Vec3(1.59051840791988e-6, 0.00000096707041180970, 0.00000007309568762914);
public function new() {}
/** Approximates the density of ozone for a given sample height. **/
@ -185,6 +198,29 @@ class NishitaData {
jTime += jStepSize;
}
return jODepth.mult(jStepSize);
jODepth.mult(jStepSize);
// Precalculate a part of the secondary attenuation.
// For one variable (e.g. x) in the vector, the formula is as follows:
//
// attn.x = exp(-(coeffX * (firstOpticalDepth.x + secondOpticalDepth.x)))
//
// We can split that up via:
//
// attn.x = exp(-(coeffX * firstOpticalDepth.x + coeffX * secondOpticalDepth.x))
// = exp(-(coeffX * firstOpticalDepth.x)) * exp(-(coeffX * secondOpticalDepth.x))
//
// The first factor of the resulting multiplication is calculated in the
// shader, but we can already precalculate the second one. As a side
// effect this keeps the range of the LUT values small because we don't
// store the optical depth but the attenuation.
var jAttenuation = new Vec3();
var mie = mieCoeff * jODepth.y;
jAttenuation.addf(mie, mie, mie);
jAttenuation.add(rayleighCoeff.clone().mult(jODepth.x));
jAttenuation.add(ozoneCoeff.clone().mult(jODepth.z));
jAttenuation.exp(jAttenuation.mult(-1));
return jAttenuation;
}
}

View file

@ -0,0 +1,131 @@
package armory.system;
import haxe.Exception;
import haxe.PosInfos;
import haxe.exceptions.PosException;
import haxe.macro.Context;
import haxe.macro.Expr;
using haxe.macro.ExprTools;
class Assert {
/**
Checks whether the given expression evaluates to true. If this is not
the case, an `ArmAssertionException` is thrown or a warning is printed
(depending on the assertion level).
The assert level describes the severity of the assertion. If the
severity is lower than the level stored in the `arm_assert_level` flag,
the assertion is omitted from the code so that it doesn't decrease the
runtime performance.
@param level The severity of this assertion.
@param condition The conditional expression to test.
@param message Optional message to display when the assertion fails.
@see `AssertLevel`
**/
macro public static function assert(level: ExprOf<AssertLevel>, condition: ExprOf<Bool>, ?message: ExprOf<String>): Expr {
final levelVal: AssertLevel = AssertLevel.fromExpr(level);
final assertThreshold = AssertLevel.fromString(Context.definedValue("arm_assert_level"));
if (levelVal < assertThreshold) {
return macro {};
}
switch (levelVal) {
case Warning:
return macro {
if (!$condition) {
@:pos(condition.pos)
trace(@:privateAccess armory.system.Assert.ArmAssertionException.formatMessage($v{condition.toString()}, ${message}));
}
}
case Error:
return macro {
if (!$condition) {
#if arm_assert_quit kha.System.stop(); #end
@:pos(condition.pos)
@:privateAccess armory.system.Assert.throwAssertionError($v{condition.toString()}, ${message});
}
}
default:
throw new Exception('Unsupported assert level: $levelVal');
}
}
/**
Helper function to prevent Haxe "bug" that actually throws an error
even when using `macro throw` (inlining this method also does not work).
**/
static function throwAssertionError(exprString: String, message: String, ?pos: PosInfos) {
throw new ArmAssertionException(exprString, message, pos);
}
}
/**
Exception that is thrown when an assertion fails.
@see `Assert`
**/
class ArmAssertionException extends PosException {
/**
@param exprString The string representation of the failed assert condition.
@param message Custom error message, use `null` to omit this.
**/
public inline function new(exprString: String, message: Null<String>, ?previous: Exception, ?pos: Null<PosInfos>) {
super('\n${formatMessage(exprString, message)}', previous, pos);
}
static inline function formatMessage(exprString: String, message: Null<String>): String {
final optMsg = message != null ? '\n\tMessage: $message' : "";
return 'Failed assertion:$optMsg\n\tExpression: ($exprString)';
}
}
enum abstract AssertLevel(Int) from Int to Int {
/**
Assertions with this severity don't throw exceptions and only print to
the console.
**/
var Warning: AssertLevel = 0;
/**
Assertions with this severity throw an `ArmAssertionException` if they
fail, and optionally quit the game if the `arm_assert_quit` flag is set.
**/
var Error: AssertLevel = 1;
/**
Completely disable assertions. Don't use this level in `assert()` calls!
**/
var NoAssertions: AssertLevel = 2;
public static function fromExpr(e: ExprOf<AssertLevel>): AssertLevel {
switch (e.expr) {
case EConst(CIdent(v)): return fromString(v);
default: throw new Exception('Unsupported expression: $e');
};
}
/**
Converts a string into an `AssertLevel`, the string must be spelled
exactly as the assert level. `null` defaults to
`AssertLevel.NoAssertions`.
**/
public static function fromString(s: Null<String>): AssertLevel {
return switch (s) {
case "Warning": Warning;
case "Error": Error;
case "NoAssertions" | null: NoAssertions;
default: throw new Exception('Could not convert "$s" to AssertLevel');
}
}
@:op(A < B) static function lt(a: AssertLevel, b: AssertLevel): Bool;
@:op(A > B) static function gt(a: AssertLevel, b: AssertLevel): Bool;
}

View file

@ -4,347 +4,223 @@ import kha.FastFloat;
import iron.system.Input;
class InputMap {
var commands = new Map<String, Null<Array<InputCommand>>>();
static var inputMaps = new Map<String, InputMap>();
public var keys(default, null) = new Array<InputMapKey>();
public var lastKeyPressed(default, null) = "";
public function new() {}
public function addKeyboard(config: String) {
var command = new KeyboardCommand();
return addCustomCommand(command, config);
public static function getInputMap(inputMap: String): Null<InputMap> {
if (inputMaps.exists(inputMap)) {
return inputMaps[inputMap];
}
return null;
}
public function addGamepad(config: String) {
var command = new GamepadCommand();
return addCustomCommand(command, config);
public static function addInputMap(inputMap: String): InputMap {
return inputMaps[inputMap] = new InputMap();
}
public function addCustomCommand(command: InputCommand, config: String) {
if (commands[config] == null) commands[config] = new Array<InputCommand>();
commands[config].push(command);
return command;
}
}
class ActionMap extends InputMap {
public inline function started(config: String) {
var started = false;
for (c in commands[config]) {
if (c.started()) {
started = true;
break;
public static function getInputMapKey(inputMap: String, key: String): Null<InputMapKey> {
if (inputMaps.exists(inputMap)) {
for (k in inputMaps[inputMap].keys) {
if (k.key == key) {
return k;
}
}
}
return started;
return null;
}
public inline function released(config: String) {
var released = false;
public static function removeInputMapKey(inputMap: String, key: String): Bool {
if (inputMaps.exists(inputMap)) {
var i = inputMaps[inputMap];
for (c in commands[config]) {
if (c.released()) {
released = true;
break;
for (k in i.keys) {
if (k.key == key) {
return i.removeKey(k);
}
}
}
return released;
return false;
}
}
class AxisMap extends InputMap {
var scale: FastFloat = 1.0;
public function addKeyboard(key: String, scale: FastFloat = 1.0): InputMapKey {
return addKey(new KeyboardKey(key, scale));
}
public inline function getAxis(config: String) {
var axis = 0.0;
public function addMouse(key: String, scale: FastFloat = 1.0, deadzone: FastFloat = 0.0): InputMapKey {
return addKey(new MouseKey(key, scale, deadzone));
}
for (c in commands[config]) {
var tempAxis = c.getAxis();
public function addGamepad(key: String, scale: FastFloat = 1.0, deadzone: FastFloat = 0.0): InputMapKey {
return addKey(new GamepadKey(key, scale, deadzone));
}
if (tempAxis != 0.0 && tempAxis != axis) {
axis += tempAxis;
scale = c.getScale();
}
public function addKey(key: InputMapKey): InputMapKey {
keys.push(key);
return key;
}
public function removeKey(key: InputMapKey): Bool {
return keys.remove(key);
}
public function value(): FastFloat {
var v = 0.0;
for (k in keys) {
v += k.value();
}
return axis;
}
public inline function getScale() {
return scale;
}
}
class InputCommand {
var keys = new Array<String>();
var modifiers = new Array<String>();
var displacementKeys = new Array<String>();
var displacementModifiers = new Array<String>();
var deadzone: FastFloat = 0.0;
var scale: FastFloat = 1.0;
public function new() {}
public function setKeys(keys: Array<String>) {
return this.keys = keys;
}
public function setMods(modifiers: Array<String>) {
return this.modifiers = modifiers;
}
public function setDisplacementKeys(keys: Array<String>) {
return displacementKeys = keys;
}
public function setDisplacementMods(modifiers: Array<String>) {
return displacementModifiers = modifiers;
}
public function setDeadzone(deadzone: FastFloat) {
return this.deadzone = deadzone;
}
public function setScale(scale: FastFloat) {
return this.scale = scale;
}
public function getScale() {
return scale;
return v;
}
public function started() {
for (k in keys) {
if (k.started()) {
lastKeyPressed = k.key;
return true;
}
}
return false;
}
public function released() {
for (k in keys) {
if (k.released()) {
lastKeyPressed = k.key;
return true;
}
}
return false;
}
}
class InputMapKey {
public var key: String;
public var scale: FastFloat;
public var deadzone: FastFloat;
public function new(key: String, scale = 1.0, deadzone = 0.0) {
this.key = key.toLowerCase();
this.scale = scale;
this.deadzone = deadzone;
}
public function started(): Bool {
return false;
}
public function getAxis(): FastFloat {
public function released(): Bool {
return false;
}
public function value(): FastFloat {
return 0.0;
}
}
class KeyboardCommand extends InputCommand {
var keyboard = Input.getKeyboard();
var mouse = Input.getMouse();
public function setIndex(index: Int) {}
public inline override function started() {
for (k in keys) {
if (keyboard.started(k)) {
for (m in modifiers) {
if (!keyboard.down(m)) return false;
}
function evalDeadzone(value: FastFloat): FastFloat {
var v = 0.0;
for (m in displacementModifiers) {
if (!mouse.down(m)) return false;
}
if (value > deadzone) {
v = value - deadzone;
return true;
}
} else if (value < -deadzone) {
v = value + deadzone;
}
for (k in displacementKeys) {
if (mouse.started(k)) {
for (m in modifiers) {
if (!keyboard.down(m)) return false;
}
for (m in displacementModifiers) {
if (!mouse.down(m)) return false;
}
return true;
}
}
return false;
return v * scale;
}
public inline override function released() {
for (k in keys) {
if (keyboard.released(k)) {
for (m in modifiers) {
if (!keyboard.down(m)) return false;
}
function evalPressure(value: FastFloat): FastFloat {
var v = value - deadzone;
for (m in displacementModifiers) {
if (!mouse.down(m)) return false;
}
if (v > 0.0) {
v /= (1.0 - deadzone);
return true;
}
} else {
v = 0.0;
}
for (k in displacementKeys) {
if (mouse.released(k)) {
for (m in modifiers) {
if (!keyboard.down(m)) return false;
}
for (m in displacementModifiers) {
if (!mouse.down(m)) return false;
}
return true;
}
}
return false;
}
public inline override function getAxis() {
var axis = 0.0;
var movementX = mouse.movementX;
var movementY = mouse.movementY;
var wheelDelta = mouse.wheelDelta;
for (k in keys) {
if (keyboard.down(k)) {
axis++;
break;
}
}
for (m in modifiers) {
if (keyboard.down(m)) {
axis --;
break;
}
}
for (k in displacementKeys) {
switch (k) {
case "moved x": if (movementX > deadzone) axis++;
case "moved y": if (movementY > deadzone) axis--;
case "wheel": if (wheelDelta < -deadzone) axis++;
case "movement x": if (movementX > deadzone) return movementX - deadzone;
case "movement y": if (movementY > deadzone) return movementY - deadzone;
default: {
if (mouse.down(k)) {
axis ++;
break;
}
}
}
}
for (m in displacementModifiers) {
switch (m) {
case "moved x": if (movementX < -deadzone) axis--;
case "moved y": if (movementY < -deadzone) axis++;
case "wheel": if (wheelDelta > deadzone) axis--;
case "movement x": if (movementX < -deadzone) return movementX + deadzone;
case "movement y": if (movementY < -deadzone) return movementY + deadzone;
default: {
if (mouse.down(m)) {
axis --;
break;
}
}
}
}
return axis > 1 ? 1 : axis < -1 ? -1 : axis;
return v;
}
}
class GamepadCommand extends InputCommand {
var gamepad = Input.getGamepad(0);
class KeyboardKey extends InputMapKey {
var kb = Input.getKeyboard();
public inline override function started() {
for (k in keys) {
if (gamepad.started(k)) {
for (m in modifiers) {
if (gamepad.down(m) < deadzone) return false;
}
return true;
}
}
return false;
return kb.started(key);
}
public inline override function released() {
for (k in keys) {
if (gamepad.released(k)) {
for (m in modifiers) {
if (gamepad.down(m) < deadzone) return false;
}
return true;
}
}
return false;
return kb.released(key);
}
public inline override function getAxis() {
var axis = 0.0;
var rsMovementX = gamepad.rightStick.movementX;
var rsMovementY = gamepad.rightStick.movementY;
var lsMovementX = gamepad.leftStick.movementX;
var lsMovementY = gamepad.leftStick.movementY;
var rtPressure = gamepad.down("r2") > 0.0 ? (gamepad.down("r2") - deadzone) / (1 - deadzone) : 0.0;
var ltPressure = gamepad.down("l2") > 0.0 ? (gamepad.down("r2") - deadzone) / (1 - deadzone) : 0.0;
for (k in keys) {
switch(k) {
case "rtPressure": axis += rtPressure;
case "ltPressure": axis += ltPressure;
default: {
if (gamepad.down(k) > deadzone) {
axis++;
break;
}
}
}
}
for (m in modifiers) {
switch (m) {
case "rtPressure": axis -= rtPressure;
case "ltPressure": axis -= ltPressure;
default: {
if (gamepad.down(m) > deadzone) {
axis--;
break;
}
}
}
}
for (k in displacementKeys) {
switch(k) {
case "rs moved x": if (rsMovementX > deadzone) axis++;
case "rs moved y": if (rsMovementY > deadzone) axis++;
case "ls moved x": if (lsMovementX > deadzone) axis++;
case "ls moved y": if (lsMovementY > deadzone) axis++;
case "rs movement x": if (rsMovementX > deadzone) return rsMovementX - deadzone;
case "rs movement y": if (rsMovementY > deadzone) return rsMovementY - deadzone;
case "ls movement x": if (lsMovementX > deadzone) return lsMovementX - deadzone;
case "ls movement y": if (lsMovementY > deadzone) return lsMovementY - deadzone;
}
}
for (m in displacementModifiers) {
switch (m) {
case "rs moved x": if (rsMovementX < -deadzone) axis--;
case "rs moved y": if (rsMovementY < -deadzone) axis--;
case "ls moved x": if (lsMovementX < -deadzone) axis--;
case "ls moved y": if (lsMovementY < -deadzone) axis--;
case "rs movement x": if (rsMovementX < -deadzone) return rsMovementX + deadzone;
case "rs movement y": if (rsMovementY < -deadzone) return rsMovementY + deadzone;
case "ls movement x": if (lsMovementX < -deadzone) return lsMovementX + deadzone;
case "ls movement y": if (lsMovementY < -deadzone) return lsMovementY + deadzone;
}
}
return axis > 1 ? 1 : axis < -1 ? -1 : axis;
public inline override function value(): FastFloat {
return kb.down(key) ? scale : 0.0;
}
}
}
class MouseKey extends InputMapKey {
var m = Input.getMouse();
public inline override function started() {
return m.started(key);
}
public inline override function released() {
return m.released(key);
}
public override function value(): FastFloat {
return switch (key) {
case "movement x": evalDeadzone(m.movementX);
case "movement y": evalDeadzone(m.movementY);
case "wheel": evalDeadzone(m.wheelDelta);
default: m.down(key) ? scale : 0.0;
}
}
}
class GamepadKey extends InputMapKey {
var g = Input.getGamepad();
public inline override function started() {
return g.started(key);
}
public inline override function released() {
return g.released(key);
}
public override function value(): FastFloat {
return switch(key) {
case "ls movement x": evalDeadzone(g.leftStick.movementX);
case "ls movement y": evalDeadzone(g.leftStick.movementY);
case "rs movement x": evalDeadzone(g.rightStick.movementX);
case "rs movement y": evalDeadzone(g.rightStick.movementY);
case "lt pressure": evalDeadzone(evalPressure(g.down("l2")));
case "rt pressure": evalDeadzone(evalPressure(g.down("r2")));
default: evalDeadzone(g.down(key));
}
}
public override function setIndex(index: Int) {
g = Input.getGamepad(index);
}
}

View file

@ -94,6 +94,10 @@ class Logic {
var v = createClassInstance(node.type, [tree]);
nodeMap.set(name, v);
#if arm_patch
tree.nodes.set(name, v);
#end
// Properties
for (i in 0...5) {
for (b in node.buttons) {
@ -103,9 +107,13 @@ class Logic {
}
}
@:privateAccess v.preallocInputs(node.inputs.length);
@:privateAccess v.preallocOutputs(node.outputs.length);
// Create inputs
var inp_node: armory.logicnode.LogicNode = null;
var inp_from = 0;
var from_type: String;
for (i in 0...node.inputs.length) {
var inp = node.inputs[i];
// Is linked - find node
@ -117,6 +125,7 @@ class Logic {
for (i in 0...n.outputs.length) {
if (n.outputs[i] == socket) {
inp_from = i;
from_type = socket.type;
break;
}
}
@ -124,27 +133,33 @@ class Logic {
else { // Not linked - create node with default values
inp_node = build_default_node(inp);
inp_from = 0;
from_type = inp.type;
}
// Add input
v.addInput(inp_node, inp_from);
var link = LogicNode.addLink(inp_node, v, inp_from, i);
#if arm_patch
link.fromType = from_type;
link.toType = inp.type;
link.toValue = getSocketDefaultValue(inp);
#end
}
// Create outputs
for (out in node.outputs) {
var outNodes: Array<armory.logicnode.LogicNode> = [];
for (i in 0...node.outputs.length) {
var out = node.outputs[i];
var ls = getOutputLinks(out);
if (ls != null && ls.length > 0) {
for (l in ls) {
var n = getNode(l.to_id);
var out_name = build_node(n);
outNodes.push(nodeMap.get(out_name));
}
// Linked outputs are already handled after iterating over inputs
// above, so only unconnected outputs are handled here
if (ls == null || ls.length == 0) {
var link = LogicNode.addLink(v, build_default_node(out), i, 0);
#if arm_patch
link.fromType = out.type;
link.toType = out.type;
link.toValue = getSocketDefaultValue(out);
#end
}
else { // Not linked - create node with default values
outNodes.push(build_default_node(out));
}
// Add outputs
v.addOutputs(outNodes);
}
return name;
@ -212,6 +227,22 @@ class Logic {
return v;
}
static function getSocketDefaultValue(socket: TNodeSocket): Any {
var v: armory.logicnode.LogicNode = null;
return switch (socket.type) {
case "OBJECT" | "VALUE" | "INT" | "BOOLEAN" | "STRING":
socket.default_value;
case "VECTOR" | "RGB":
socket.default_value == null ? [0, 0, 0] : [socket.default_value[0], socket.default_value[1], socket.default_value[2]];
case "RGBA":
socket.default_value == null ? [0, 0, 0, 1] : [socket.default_value[0], socket.default_value[1], socket.default_value[2], socket.default_value[3]];
default:
null;
}
}
static function createClassInstance(className: String, args: Array<Dynamic>): Dynamic {
var cname = Type.resolveClass(packageName + "." + className);
if (cname == null) return null;

View file

@ -6,6 +6,9 @@ import iron.math.Vec4;
class ArcBall extends Trait {
@prop
public var axis = new Vec4(0, 0, 1);
public function new() {
super();
@ -17,10 +20,8 @@ class ArcBall extends Trait {
var mouse = Input.getMouse();
if (mouse.down()) {
object.transform.rotate(new Vec4(0, 0, 1), -mouse.movementX / 100);
object.transform.buildMatrix();
object.transform.rotate(axis, -mouse.movementX / 100);
object.transform.rotate(object.transform.world.right(), -mouse.movementY / 100);
object.transform.buildMatrix();
}
}
}

View file

@ -33,7 +33,7 @@ class FollowCamera extends iron.Trait {
trace("FollowCamera error, unable to set target object");
}
if (Std.is(object, iron.object.CameraObject)) {
if (Std.isOfType(object, iron.object.CameraObject)) {
disabled = true;
trace("FollowCamera error, this trait should not be placed directly on a camera objet. It should be placed on another object such as an Empty. The camera should be placed as a child to the Empty object with offset, creating a camera boom.");
}

View file

@ -8,9 +8,9 @@ import iron.system.Tween;
class NavAgent extends Trait {
@prop
var speed: Float = 5;
public var speed: Float = 5;
@prop
var turnDuration: Float = 0.4;
public var turnDuration: Float = 0.4;
var path: Array<Vec4> = null;
var index = 0;

View file

@ -2,6 +2,7 @@ package armory.trait;
import iron.Trait;
import iron.system.Input;
import iron.math.Vec3;
import iron.math.Vec4;
import iron.math.Mat4;
import iron.math.RayCaster;
@ -14,6 +15,11 @@ class PhysicsDrag extends Trait {
public function new() { super(); }
#else
@prop public var linearLowerLimit = new Vec3(0,0,0);
@prop public var linearUpperLimit = new Vec3(0,0,0);
@prop public var angularLowerLimit = new Vec3(-10,-10,-10);
@prop public var angularUpperLimit = new Vec3(10,10,10);
var pickConstraint: bullet.Bt.Generic6DofConstraint = null;
var pickDist: Float;
var pickedBody: RigidBody = null;
@ -56,10 +62,10 @@ class PhysicsDrag extends Trait {
tr.setOrigin(localPivot);
pickConstraint = new bullet.Bt.Generic6DofConstraint(b.body, tr, false);
pickConstraint.setLinearLowerLimit(new bullet.Bt.Vector3(0, 0, 0));
pickConstraint.setLinearUpperLimit(new bullet.Bt.Vector3(0, 0, 0));
pickConstraint.setAngularLowerLimit(new bullet.Bt.Vector3(-10, -10, -10));
pickConstraint.setAngularUpperLimit(new bullet.Bt.Vector3(10, 10, 10));
pickConstraint.setLinearLowerLimit(new bullet.Bt.Vector3(linearLowerLimit.x, linearLowerLimit.y, linearLowerLimit.z));
pickConstraint.setLinearUpperLimit(new bullet.Bt.Vector3(linearUpperLimit.x, linearUpperLimit.y, linearUpperLimit.z));
pickConstraint.setAngularLowerLimit(new bullet.Bt.Vector3(angularLowerLimit.x, angularLowerLimit.y, angularLowerLimit.z));
pickConstraint.setAngularUpperLimit(new bullet.Bt.Vector3(angularUpperLimit.x, angularUpperLimit.y, angularUpperLimit.z));
physics.world.addConstraint(pickConstraint, false);
/*pickConstraint.setParam(4, 0.8, 0);

View file

@ -11,6 +11,7 @@ class Bridge {
public static var Input = iron.system.Input;
public static var Object = iron.object.Object;
public static var Data = iron.data.Data;
public static var Vec4 = iron.math.Vec4;
public static function log(s: String) { trace(s); };
}

View file

@ -29,7 +29,7 @@ class CanvasScript extends Trait {
iron.data.Data.getBlob(canvasName + ".json", function(blob: kha.Blob) {
iron.data.Data.getBlob("_themes.json", function(tBlob: kha.Blob) {
if (tBlob.get_length() != 0) {
if (@:privateAccess tBlob.get_length() != 0) {
Canvas.themes = haxe.Json.parse(tBlob.toString());
}
else {
@ -40,20 +40,27 @@ class CanvasScript extends Trait {
Canvas.themes.push(armory.ui.Themes.light);
}
iron.data.Data.getFont(font, function(f: kha.Font) {
iron.data.Data.getFont(font, function(defaultFont: kha.Font) {
var c: TCanvas = haxe.Json.parse(blob.toString());
if (c.theme == null) c.theme = Canvas.themes[0].NAME;
cui = new Zui({font: f, theme: Canvas.getTheme(c.theme)});
cui = new Zui({font: defaultFont, theme: Canvas.getTheme(c.theme)});
if (c.assets == null || c.assets.length == 0) canvas = c;
else { // Load canvas assets
var loaded = 0;
for (asset in c.assets) {
var file = asset.name;
iron.data.Data.getImage(file, function(image: kha.Image) {
Canvas.assetMap.set(asset.id, image);
if (++loaded >= c.assets.length) canvas = c;
});
if (Canvas.isFontAsset(file)) {
iron.data.Data.getFont(file, function(f: kha.Font) {
Canvas.assetMap.set(asset.id, f);
if (++loaded >= c.assets.length) canvas = c;
});
} else {
iron.data.Data.getImage(file, function(image: kha.Image) {
Canvas.assetMap.set(asset.id, image);
if (++loaded >= c.assets.length) canvas = c;
});
}
}
}
});
@ -107,13 +114,19 @@ class CanvasScript extends Trait {
}
/**
* Set UI scale factor.
* @param factor Scale factor.
*/
public function setUiScale(factor:Float) {
Set the UI scale factor.
**/
public inline function setUiScale(factor: Float) {
cui.setScale(factor);
}
/**
Get the UI scale factor.
**/
public inline function getUiScale(): Float {
return cui.ops.scaleFactor;
}
/**
* Set visibility of canvas
* @param visible Whether canvas should be visible or not
@ -137,6 +150,11 @@ class CanvasScript extends Trait {
*/
public function setCanvasFontSize(fontSize: Int) {
cui.t.FONT_SIZE = fontSize;
cui.setScale(cui.ops.scaleFactor);
}
public function getCanvasFontSize(): Int {
return cui.t.FONT_SIZE;
}
// Contains data

View file

@ -25,6 +25,8 @@ class DebugConsole extends Trait {
#else
public static var visible = true;
public static var traceWithPosition = true;
static var ui: Zui;
var scaleFactor = 1.0;
@ -75,16 +77,18 @@ class DebugConsole extends Trait {
#end
public function new(scaleFactor = 1.0, scaleDebugConsole = 1.0, positionDebugConsole = 2, visibleDebugConsole = 1,
keyCodeVisible = kha.input.KeyCode.Tilde, keyCodeScaleIn = kha.input.KeyCode.OpenBracket, keyCodeScaleOut = kha.input.KeyCode.CloseBracket) {
traceWithPosition = 1, keyCodeVisible = kha.input.KeyCode.Tilde, keyCodeScaleIn = kha.input.KeyCode.OpenBracket,
keyCodeScaleOut = kha.input.KeyCode.CloseBracket) {
super();
this.scaleFactor = scaleFactor;
DebugConsole.traceWithPosition = traceWithPosition == 1;
iron.data.Data.getFont("font_default.ttf", function(font: kha.Font) {
ui = new Zui({scaleFactor: scaleFactor, font: font});
// Set settings
setScale(scaleDebugConsole);
setVisible(visibleDebugConsole == 1);
switch(positionDebugConsole) {
switch (positionDebugConsole) {
case 0: setPosition(PositionStateEnum.LEFT);
case 1: setPosition(PositionStateEnum.CENTER);
case 2: setPosition(PositionStateEnum.RIGHT);
@ -104,11 +108,11 @@ class DebugConsole extends Trait {
// DebugFloat
if (key == kha.input.KeyCode.OpenBracket) {
debugFloat -= 0.1;
trace("debugFloat = "+ debugFloat);
trace("debugFloat = " + debugFloat);
}
else if (key == kha.input.KeyCode.CloseBracket){
debugFloat += 0.1;
trace("debugFloat = "+ debugFloat);
trace("debugFloat = " + debugFloat);
}
// Shortcut - Visible
if (key == shortcut_visible) visible = !visible;
@ -170,7 +174,7 @@ class DebugConsole extends Trait {
static var haxeTrace: Dynamic->haxe.PosInfos->Void = null;
static var lastTraces: Array<String> = [""];
static function consoleTrace(v: Dynamic, ?inf: haxe.PosInfos) {
lastTraces.unshift(haxe.Log.formatOutput(v,inf));
lastTraces.unshift(haxe.Log.formatOutput(v, traceWithPosition ? inf : null));
if (lastTraces.length > 10) lastTraces.pop();
haxeTrace(v, inf);
}
@ -186,10 +190,10 @@ class DebugConsole extends Trait {
var wh = iron.App.h();
// Check position
switch (position_console) {
case PositionStateEnum.LEFT: wx = 0;
case PositionStateEnum.CENTER: wx = Math.round(iron.App.w() / 2 - ww / 2);
case PositionStateEnum.RIGHT: wx = iron.App.w() - ww;
}
case PositionStateEnum.LEFT: wx = 0;
case PositionStateEnum.CENTER: wx = Math.round(iron.App.w() / 2 - ww / 2);
case PositionStateEnum.RIGHT: wx = iron.App.w() - ww;
}
// var bindG = ui.windowDirty(hwin, wx, wy, ww, wh) || hwin.redraws > 0;
var bindG = true;
@ -276,7 +280,7 @@ class DebugConsole extends Trait {
ui.indent();
if (selectedObject != null) {
if (Std.is(selectedObject, iron.object.CameraObject)) {
if (Std.isOfType(selectedObject, iron.object.CameraObject)) {
ui.row([1/2, 1/2]);
}
@ -284,7 +288,7 @@ class DebugConsole extends Trait {
h.selected = selectedObject.visible;
selectedObject.visible = ui.check(h, "Visible");
if (Std.is(selectedObject, iron.object.CameraObject)) {
if (Std.isOfType(selectedObject, iron.object.CameraObject)) {
if (ui.button("Set Active Camera")) {
iron.Scene.active.camera = cast(selectedObject, iron.object.CameraObject);
}
@ -428,7 +432,7 @@ class DebugConsole extends Trait {
ui.text("This scene has no world data to edit.");
}
}
else if (Std.is(selectedObject, iron.object.LightObject)) {
else if (Std.isOfType(selectedObject, iron.object.LightObject)) {
selectedType = "(Light)";
var light = cast(selectedObject, iron.object.LightObject);
var lightHandle = Id.handle();
@ -439,7 +443,7 @@ class DebugConsole extends Trait {
ui.text("shadow map size: " + light.data.raw.shadowmap_size);
#end
}
else if (Std.is(selectedObject, iron.object.CameraObject)) {
else if (Std.isOfType(selectedObject, iron.object.CameraObject)) {
selectedType = "(Camera)";
var cam = cast(selectedObject, iron.object.CameraObject);
var fovHandle = Id.handle();
@ -594,7 +598,7 @@ class DebugConsole extends Trait {
}
function drawTiles(tile: ShadowMapTile, atlas: ShadowMapAtlas, atlasVisualSize: Float) {
var color = kha.Color.fromFloats(0.1, 0.1, 0.1);
var color: Null<kha.Color> = kha.Color.fromFloats(0.1, 0.1, 0.1);
var borderColor = color;
var tileScale = (tile.size / atlas.sizew) * atlasVisualSize; //* 0.95;
var x = (tile.coordsX / atlas.sizew) * atlasVisualSize;
@ -775,11 +779,23 @@ class DebugConsole extends Trait {
#end
if (ui.panel(Id.handle({selected: true}), "Log")) {
ui.indent();
final h = Id.handle();
h.selected = DebugConsole.traceWithPosition;
DebugConsole.traceWithPosition = ui.check(h, "Print With Position");
if (ui.isHovered) ui.tooltip("Whether to prepend the position of print/trace statements to the printed text");
if (ui.button("Clear")) {
lastTraces[0] = "";
lastTraces.splice(1, lastTraces.length - 1);
}
if (ui.isHovered) ui.tooltip("Clear the log output");
final eh = ui.t.ELEMENT_H;
ui.t.ELEMENT_H = ui.fontSize;
for (t in lastTraces) ui.text(t);
ui.t.ELEMENT_H = eh;
ui.unindent();
}
}

View file

@ -1,8 +1,17 @@
package armory.trait.internal;
import armory.logicnode.LogicNode;
import armory.logicnode.LogicTree;
#if arm_patch @:expose("LivePatch") #end
@:access(armory.logicnode.LogicNode)
@:access(armory.logicnode.LogicNodeLink)
class LivePatch extends iron.Trait {
#if arm_patch
#if !arm_patch
public function new() { super(); }
#else
static var patchId = 0;
@ -23,9 +32,164 @@ class LivePatch extends iron.Trait {
});
}
#else
public static function patchCreateNodeLink(treeName: String, fromNodeName: String, toNodeName: String, fromIndex: Int, toIndex: Int) {
if (!LogicTree.nodeTrees.exists(treeName)) return;
var trees = LogicTree.nodeTrees[treeName];
public function new() { super(); }
for (tree in trees) {
var fromNode = tree.nodes[fromNodeName];
var toNode = tree.nodes[toNodeName];
if (fromNode == null || toNode == null) return;
LogicNode.addLink(fromNode, toNode, fromIndex, toIndex);
}
}
public static function patchSetNodeLinks(treeName: String, nodeName: String, inputDatas: Array<Dynamic>, outputDatas: Array<Array<Dynamic>>) {
if (!LogicTree.nodeTrees.exists(treeName)) return;
var trees = LogicTree.nodeTrees[treeName];
for (tree in trees) {
var node = tree.nodes[nodeName];
if (node == null) return;
node.clearInputs();
node.clearOutputs();
for (inputData in inputDatas) {
var fromNode: LogicNode;
var fromIndex: Int;
if (inputData.isLinked) {
fromNode = tree.nodes[inputData.fromNode];
if (fromNode == null) continue;
fromIndex = inputData.fromIndex;
}
else {
fromNode = LogicNode.createSocketDefaultNode(node.tree, inputData.socketType, inputData.socketValue);
fromIndex = 0;
}
LogicNode.addLink(fromNode, node, fromIndex, inputData.toIndex);
}
for (outputData in outputDatas) {
for (linkData in outputData) {
var toNode: LogicNode;
var toIndex: Int;
if (linkData.isLinked) {
toNode = tree.nodes[linkData.toNode];
if (toNode == null) continue;
toIndex = linkData.toIndex;
}
else {
toNode = LogicNode.createSocketDefaultNode(node.tree, linkData.socketType, linkData.socketValue);
toIndex = 0;
}
LogicNode.addLink(node, toNode, linkData.fromIndex, toIndex);
}
}
}
}
public static function patchUpdateNodeProp(treeName: String, nodeName: String, propName: String, value: Dynamic) {
if (!LogicTree.nodeTrees.exists(treeName)) return;
var trees = LogicTree.nodeTrees[treeName];
for (tree in trees) {
var node = tree.nodes[nodeName];
if (node == null) return;
Reflect.setField(node, propName, value);
}
}
public static function patchUpdateNodeInputVal(treeName: String, nodeName: String, socketIndex: Int, value: Dynamic) {
if (!LogicTree.nodeTrees.exists(treeName)) return;
var trees = LogicTree.nodeTrees[treeName];
for (tree in trees) {
var node = tree.nodes[nodeName];
if (node == null) return;
node.inputs[socketIndex].set(value);
}
}
public static function patchNodeDelete(treeName: String, nodeName: String) {
if (!LogicTree.nodeTrees.exists(treeName)) return;
var trees = LogicTree.nodeTrees[treeName];
for (tree in trees) {
var node = tree.nodes[nodeName];
if (node == null) return;
node.clearOutputs();
node.clearInputs();
tree.nodes.remove(nodeName);
}
}
public static function patchNodeCreate(treeName: String, nodeName: String, nodeType: String, propDatas: Array<Array<Dynamic>>, inputDatas: Array<Array<Dynamic>>, outputDatas: Array<Array<Dynamic>>) {
if (!LogicTree.nodeTrees.exists(treeName)) return;
var trees = LogicTree.nodeTrees[treeName];
for (tree in trees) {
// No further constructor parameters required here, all variable nodes
// use optional further parameters and all values are set later in this
// function.
var newNode: LogicNode = Type.createInstance(Type.resolveClass(nodeType), [tree]);
newNode.name = nodeName;
tree.nodes[nodeName] = newNode;
for (propData in propDatas) {
Reflect.setField(newNode, propData[0], propData[1]);
}
var i = 0;
for (inputData in inputDatas) {
LogicNode.addLink(LogicNode.createSocketDefaultNode(newNode.tree, inputData[0], inputData[1]), newNode, 0, i++);
}
i = 0;
for (outputData in outputDatas) {
LogicNode.addLink(newNode, LogicNode.createSocketDefaultNode(newNode.tree, outputData[0], outputData[1]), i++, 0);
}
}
}
public static function patchNodeCopy(treeName: String, nodeName: String, newNodeName: String, copyProps: Array<String>, inputDatas: Array<Array<Dynamic>>, outputDatas: Array<Array<Dynamic>>) {
if (!LogicTree.nodeTrees.exists(treeName)) return;
var trees = LogicTree.nodeTrees[treeName];
for (tree in trees) {
var node = tree.nodes[nodeName];
if (node == null) return;
// No further constructor parameters required here, all variable nodes
// use optional further parameters and all values are set later in this
// function.
var newNode: LogicNode = Type.createInstance(Type.getClass(node), [tree]);
newNode.name = newNodeName;
tree.nodes[newNodeName] = newNode;
for (propName in copyProps) {
Reflect.setField(newNode, propName, Reflect.field(node, propName));
}
var i = 0;
for (inputData in inputDatas) {
LogicNode.addLink(LogicNode.createSocketDefaultNode(newNode.tree, inputData[0], inputData[1]), newNode, 0, i++);
}
i = 0;
for (outputData in outputDatas) {
LogicNode.addLink(newNode, LogicNode.createSocketDefaultNode(newNode.tree, outputData[0], outputData[1]), i++, 0);
}
}
}
#end
}

View file

@ -2,14 +2,29 @@ package armory.trait.internal;
import kha.Image;
import kha.Video;
import iron.Trait;
import iron.object.MeshObject;
/**
Replaces the diffuse texture of the first material of the trait's object
with a video texture.
@see https://github.com/armory3d/armory_examples/tree/master/material_movie
**/
class MovieTexture extends Trait {
/**
Caches all render targets used by this trait for re-use when having
multiple videos of the same size. The lookup only takes place on trait
initialization.
Map layout: `[width => [height => image]]`
**/
static var imageCache: Map<Int, Map<Int, Image>> = new Map();
var video: Video;
public static var image: Image;
public static var created = false;
var image: Image;
var videoName: String;
@ -33,10 +48,7 @@ class MovieTexture extends Trait {
this.videoName = videoName;
if (!created) {
created = true;
notifyOnInit(init);
}
notifyOnInit(init);
}
function init() {
@ -44,9 +56,21 @@ class MovieTexture extends Trait {
video = vid;
video.play(true);
image = Image.createRenderTarget(getPower2(video.width()), getPower2(video.height()));
var w = getPower2(video.width());
var h = getPower2(video.height());
var o = cast(object, iron.object.MeshObject);
// Lazily fill the outer map
var hMap: Map<Int, Image> = imageCache[w];
if (hMap == null) {
imageCache[w] = new Map<Int, Image>();
}
image = imageCache[w][h];
if (image == null) {
imageCache[w][h] = image = Image.createRenderTarget(w, h);
}
var o = cast(object, MeshObject);
o.materials[0].contexts[0].textures[0] = image; // Override diffuse texture
notifyOnRender2D(render);
});

View file

@ -0,0 +1,328 @@
package armory.trait.internal;
import iron.object.DecalObject;
import iron.object.MeshObject;
import iron.Trait;
import kha.Image;
import iron.math.Vec4;
import iron.data.MaterialData;
import iron.Scene;
import iron.object.Object;
import iron.object.Uniforms;
class UniformsManager extends Trait{
static var floatsRegistered = false;
static var floatsMap = new Map<Object, Map<MaterialData, Map<String, Null<kha.FastFloat>>>>();
static var vectorsRegistered = false;
static var vectorsMap = new Map<Object, Map<MaterialData, Map<String, Vec4>>>();
static var texturesRegistered = false;
static var texturesMap = new Map<Object, Map<MaterialData, Map<String, kha.Image>>>();
static var sceneRemoveInitalized = false;
public var uniformExists = false;
public function new(){
super();
notifyOnInit(init);
notifyOnRemove(removeObject);
if(! sceneRemoveInitalized){
Scene.active.notifyOnRemove(removeScene);
}
}
function init() {
if(Std.isOfType(object, MeshObject)){
var materials = cast(object, MeshObject).materials;
for (material in materials){
var exists = registerShaderUniforms(material);
if(exists) {
uniformExists = true;
}
}
}
#if rp_decals
if(Std.isOfType(object, DecalObject)){
var material = cast(object, DecalObject).material;
var exists = registerShaderUniforms(material);
if(exists) {
uniformExists = true;
}
}
#end
}
static function removeScene() {
removeObjectFromAllMaps(Scene.active.root);
}
function removeObject() {
removeObjectFromAllMaps(object);
}
// Helper method to register float, vec3 and texture getter functions
static function register(type: UniformType){
switch (type){
case Float:
if(! floatsRegistered){
floatsRegistered = true;
Uniforms.externalFloatLinks.push(floatLink);
}
case Vector:
if(! vectorsRegistered){
vectorsRegistered = true;
Uniforms.externalVec3Links.push(vec3Link);
}
case Texture:
if(! texturesRegistered){
texturesRegistered = true;
Uniforms.externalTextureLinks.push(textureLink);
}
}
}
// Register and map shader uniforms if it is an armory shader parameter
public static function registerShaderUniforms(material: MaterialData) : Bool {
var uniformExist = false;
if(! floatsMap.exists(Scene.active.root)) floatsMap.set(Scene.active.root, null);
if(! vectorsMap.exists(Scene.active.root)) vectorsMap.set(Scene.active.root, null);
if(! texturesMap.exists(Scene.active.root)) texturesMap.set(Scene.active.root, null);
for(context in material.shader.raw.contexts){ // For each context in shader
for (constant in context.constants){ // For each constant in the context
if(constant.is_arm_parameter){ // Check if armory parameter
uniformExist = true;
var object = Scene.active.root; // Map default uniforms to scene root
switch (constant.type){
case "float":{
var link = constant.link;
var value = constant.float;
setFloatValue(material, object, link, value);
register(Float);
}
case "vec3":{
var vec = new Vec4();
vec.x = constant.vec3.get(0);
vec.y = constant.vec3.get(1);
vec.z = constant.vec3.get(2);
setVec3Value(material, object, constant.link, vec);
register(Vector);
}
}
}
}
for (texture in context.texture_units){
if(texture.is_arm_parameter){ // Check if armory parameter
uniformExist = true;
var object = Scene.active.root; // Map default texture to scene root
if(texture.default_image_file == null){
setTextureValue(material, object, texture.link, null);
}
else{
iron.data.Data.getImage(texture.default_image_file, function(image: kha.Image) {
setTextureValue(material, object, texture.link, image);
});
}
register(Texture);
}
}
}
return uniformExist;
}
// Method to set map Object -> Material -> Link -> FLoat
public static function setFloatValue(material: MaterialData, object: Object, link: String, value: Null<kha.FastFloat>){
if(object == null || material == null || link == null) return;
var map = floatsMap;
var matMap = map.get(object);
if (matMap == null) {
matMap = new Map();
map.set(object, matMap);
}
var entry = matMap.get(material);
if (entry == null) {
entry = new Map();
matMap.set(material, entry);
}
entry.set(link, value); // parameter name, value
}
// Method to set map Object -> Material -> Link -> Vec3
public static function setVec3Value(material: MaterialData, object: Object, link: String, value: Vec4){
if(object == null || material == null || link == null) return;
var map = vectorsMap;
var matMap = map.get(object);
if (matMap == null) {
matMap = new Map();
map.set(object, matMap);
}
var entry = matMap.get(material);
if (entry == null) {
entry = new Map();
matMap.set(material, entry);
}
entry.set(link, value); // parameter name, value
}
// Method to set map Object -> Material -> Link -> Texture
public static function setTextureValue(material: MaterialData, object: Object, link: String, value: kha.Image){
if(object == null || material == null || link == null) return;
var map = texturesMap;
var matMap = map.get(object);
if (matMap == null) {
matMap = new Map();
map.set(object, matMap);
}
var entry = matMap.get(material);
if (entry == null) {
entry = new Map();
matMap.set(material, entry);
}
entry.set(link, value); // parameter name, value
}
// Mehtod to get object specific material parameter float value
static function floatLink(object: Object, mat: MaterialData, link: String): Null<kha.FastFloat> {
if(object == null || mat == null) return null;
if(! floatsMap.exists(object)){
object = Scene.active.root;
}
var material = floatsMap.get(object);
if (material == null) return null;
var entry = material.get(mat);
if (entry == null) return null;
return entry.get(link);
}
// Mehtod to get object specific material parameter vec3 value
static function vec3Link(object: Object, mat: MaterialData, link: String): iron.math.Vec4 {
if(object == null || mat == null) return null;
if(! vectorsMap.exists(object)){
object = Scene.active.root;
}
var material = vectorsMap.get(object);
if (material == null) return null;
var entry = material.get(mat);
if (entry == null) return null;
return entry.get(link);
}
// Mehtod to get object specific material parameter texture value
static function textureLink(object: Object, mat: MaterialData, link: String): kha.Image {
if(object == null || mat == null) return null;
if(! texturesMap.exists(object)){
object = Scene.active.root;
}
var material = texturesMap.get(object);
if (material == null) return null;
var entry = material.get(mat);
if (entry == null) return null;
return entry.get(link);
}
// Returns complete map of float value material paramets
public static function getFloatsMap():Map<Object, Map<MaterialData, Map<String, Null<kha.FastFloat>>>>{
return floatsMap;
}
// Returns complete map of vec3 value material paramets
public static function getVectorsMap():Map<Object, Map<MaterialData, Map<String, Vec4>>>{
return vectorsMap;
}
// Returns complete map of texture value material paramets
public static function getTexturesMap():Map<Object, Map<MaterialData, Map<String, kha.Image>>>{
return texturesMap;
}
// Remove all object specific material paramenter keys
public static function removeObjectFromAllMaps(object: Object) {
floatsMap.remove(object);
vectorsMap.remove(object);
texturesMap.remove(object);
}
// Remove object specific material paramenter keys
public static function removeObjectFromMap(object: Object, type: UniformType) {
switch (type){
case Float: floatsMap.remove(object);
case Vector: vectorsMap.remove(object);
case Texture: texturesMap.remove(object);
}
}
}
@:enum abstract UniformType(Int) from Int to Int {
var Float = 0;
var Vector = 1;
var Texture = 2;
}

View file

@ -18,8 +18,9 @@ class PhysicsConstraintExportHelper extends iron.Trait {
var breakingThreshold: Float;
var limits: Array<Float>;
var constraintAdded: Bool = false;
var relativeConstraint: Bool = false;
public function new(body1: String, body2: String, type: Int, disableCollisions: Bool, breakingThreshold: Float, limits: Array<Float> = null) {
public function new(body1: String, body2: String, type: Int, disableCollisions: Bool, breakingThreshold: Float, relatieConstraint: Bool = false, limits: Array<Float> = null) {
super();
this.body1 = body1;
@ -27,14 +28,26 @@ class PhysicsConstraintExportHelper extends iron.Trait {
this.type = type;
this.disableCollisions = disableCollisions;
this.breakingThreshold = breakingThreshold;
this.relativeConstraint = relatieConstraint;
this.limits = limits;
notifyOnInit(init);
notifyOnUpdate(update);
}
function init() {
var target1 = Scene.active.getChild(body1);
var target2 = Scene.active.getChild(body2);
var target1;
var target2;
if(relativeConstraint) {
target1 = object.parent.getChild(body1);
target2 = object.parent.getChild(body2);
}
else {
target1 = Scene.active.getChild(body1);
target2 = Scene.active.getChild(body2);
}
object.addTrait(new PhysicsConstraint(target1, target2, type, disableCollisions, breakingThreshold, limits));
constraintAdded = true;
}

View file

@ -207,7 +207,7 @@ class PhysicsWorld extends Trait {
/**
Used to get intersecting rigid bodies with the passed in RigidBody as reference. Often used when checking for object collisions.
@param body The passed in RigidBody to be checked for intersecting rigid bodies.
@return Array<RigidBody> or null.
@return `Array<RigidBody>`
**/
public function getContacts(body: RigidBody): Array<RigidBody> {
if (contacts.length == 0) return null;

View file

@ -149,7 +149,7 @@ class RigidBody extends iron.Trait {
if (ready) return;
ready = true;
if (!Std.is(object, MeshObject)) return; // No mesh data
if (!Std.isOfType(object, MeshObject)) return; // No mesh data
transform = object.transform;
physics = armory.trait.physics.PhysicsWorld.active;

Some files were not shown because too many files have changed in this diff Show more