Compare commits

...

3 Commits

Author SHA1 Message Date
4e0598e321 cleaned code, custom boundbox (works for anim.nodes too), targetting
nodes in the middle of a hierarchy or nodes with multiple childs is now
possible
2026-07-08 01:09:08 +02:00
565d4c31f4 control mode for objects (view space or object space) 2026-07-04 11:53:09 +02:00
61c7a29113 relying on implicit.context.js to set drawto and listening to world
events
2026-07-04 11:22:52 +02:00
2 changed files with 467 additions and 304 deletions

View File

@@ -9,16 +9,49 @@
"modernui": 1 "modernui": 1
}, },
"classnamespace": "box", "classnamespace": "box",
"rect": [ 636.0, 100.0, 1444.0, 848.0 ], "rect": [ 400.0, 100.0, 967.0, 848.0 ],
"integercoordinates": 1, "integercoordinates": 1,
"boxes": [ "boxes": [
{
"box": {
"id": "obj-46",
"maxclass": "message",
"numinlets": 2,
"numoutlets": 1,
"outlettype": [ "" ],
"patching_rect": [ 160.0, 70.0, 48.0, 22.0 ],
"text": "fps 120"
}
},
{
"box": {
"id": "obj-25",
"maxclass": "message",
"numinlets": 2,
"numoutlets": 1,
"outlettype": [ "" ],
"patching_rect": [ 287.0, 86.0, 107.0, 22.0 ],
"text": "sendnode getanim"
}
},
{
"box": {
"id": "obj-22",
"maxclass": "message",
"numinlets": 2,
"numoutlets": 1,
"outlettype": [ "" ],
"patching_rect": [ 203.0, 78.0, 41.0, 22.0 ],
"text": "fps 30"
}
},
{ {
"box": { "box": {
"id": "obj-21", "id": "obj-21",
"maxclass": "newobj", "maxclass": "newobj",
"numinlets": 1, "numinlets": 1,
"numoutlets": 0, "numoutlets": 0,
"patching_rect": [ 1395.0, 188.0, 88.0, 22.0 ], "patching_rect": [ 1408.0, 188.0, 88.0, 22.0 ],
"text": "s object_select" "text": "s object_select"
} }
}, },
@@ -39,7 +72,7 @@
"numinlets": 0, "numinlets": 0,
"numoutlets": 1, "numoutlets": 1,
"outlettype": [ "" ], "outlettype": [ "" ],
"patching_rect": [ 273.0, 493.0, 86.0, 22.0 ], "patching_rect": [ 327.0, 482.0, 86.0, 22.0 ],
"text": "r object_select" "text": "r object_select"
} }
}, },
@@ -50,7 +83,7 @@
"maxclass": "newobj", "maxclass": "newobj",
"numinlets": 1, "numinlets": 1,
"numoutlets": 0, "numoutlets": 0,
"patching_rect": [ 1299.0, 188.0, 93.0, 22.0 ], "patching_rect": [ 1312.0, 188.0, 93.0, 22.0 ],
"text": "s to_tc.fpcontrol" "text": "s to_tc.fpcontrol"
} }
}, },
@@ -860,7 +893,7 @@
} }
] ]
}, },
"patching_rect": [ 1299.0, 155.0, 115.0, 22.0 ], "patching_rect": [ 1312.0, 155.0, 115.0, 22.0 ],
"text": "p gamepad_buttons" "text": "p gamepad_buttons"
} }
}, },
@@ -868,11 +901,11 @@
"box": { "box": {
"id": "obj-4", "id": "obj-4",
"maxclass": "newobj", "maxclass": "newobj",
"numinlets": 2, "numinlets": 1,
"numoutlets": 2, "numoutlets": 1,
"outlettype": [ "", "" ], "outlettype": [ "" ],
"patching_rect": [ 239.0, 374.0, 55.0, 22.0 ], "patching_rect": [ 211.0, 405.0, 125.0, 22.0 ],
"text": "route init" "text": "loadmess controllable"
} }
}, },
{ {
@@ -882,8 +915,8 @@
"maxclass": "comment", "maxclass": "comment",
"numinlets": 1, "numinlets": 1,
"numoutlets": 0, "numoutlets": 0,
"patching_rect": [ 315.0, 532.0, 272.0, 24.0 ], "patching_rect": [ 369.0, 521.0, 181.0, 24.0 ],
"text": "Select which object to control in camera space" "text": "Select which object to control"
} }
}, },
{ {
@@ -893,21 +926,10 @@
"numinlets": 1, "numinlets": 1,
"numoutlets": 1, "numoutlets": 1,
"outlettype": [ "" ], "outlettype": [ "" ],
"patching_rect": [ 53.0, 612.0, 72.0, 22.0 ], "patching_rect": [ 53.0, 595.0, 72.0, 22.0 ],
"text": "prepend set" "text": "prepend set"
} }
}, },
{
"box": {
"color": [ 0.282352941176471, 0.537254901960784, 0.905882352941176, 1.0 ],
"id": "obj-32",
"maxclass": "newobj",
"numinlets": 1,
"numoutlets": 0,
"patching_rect": [ 211.0, 589.0, 93.0, 22.0 ],
"text": "s to_tc.fpcontrol"
}
},
{ {
"box": { "box": {
"id": "obj-31", "id": "obj-31",
@@ -915,7 +937,7 @@
"numinlets": 2, "numinlets": 2,
"numoutlets": 1, "numoutlets": 1,
"outlettype": [ "" ], "outlettype": [ "" ],
"patching_rect": [ 211.0, 561.0, 61.0, 22.0 ], "patching_rect": [ 265.0, 550.0, 61.0, 22.0 ],
"text": "control $2" "text": "control $2"
} }
}, },
@@ -969,7 +991,7 @@
"numinlets": 1, "numinlets": 1,
"numoutlets": 1, "numoutlets": 1,
"outlettype": [ "int" ], "outlettype": [ "int" ],
"patching_rect": [ 249.0, 218.0, 22.0, 22.0 ], "patching_rect": [ 208.0, 217.0, 22.0, 22.0 ],
"text": "t 1" "text": "t 1"
} }
}, },
@@ -1002,8 +1024,8 @@
"numinlets": 2, "numinlets": 2,
"numoutlets": 2, "numoutlets": 2,
"outlettype": [ "", "" ], "outlettype": [ "", "" ],
"patching_rect": [ 184.0, 186.0, 310.0, 22.0 ], "patching_rect": [ 184.0, 186.0, 239.0, 22.0 ],
"text": "zl.sect jit.gl.skybox jit.gl.sketch jit.gl.camera jit.anim.node" "text": "zl.sect jit.gl.skybox jit.gl.sketch jit.gl.camera"
} }
}, },
{ {
@@ -1014,7 +1036,7 @@
"maxclass": "comment", "maxclass": "comment",
"numinlets": 1, "numinlets": 1,
"numoutlets": 0, "numoutlets": 0,
"patching_rect": [ 499.0, 165.0, 200.0, 64.0 ], "patching_rect": [ 425.0, 165.0, 200.0, 64.0 ],
"text": "For the purpose of a seamless experience, filtering out objects we don't want to control in this patch, even if you could!" "text": "For the purpose of a seamless experience, filtering out objects we don't want to control in this patch, even if you could!"
} }
}, },
@@ -1138,19 +1160,19 @@
} }
] ]
}, },
"patching_rect": [ 129.0, 493.0, 133.0, 22.0 ], "patching_rect": [ 183.0, 482.0, 133.0, 22.0 ],
"text": "p controllable_populate" "text": "p controllable_populate"
} }
}, },
{ {
"box": { "box": {
"id": "obj-5", "id": "obj-5",
"linecount": 27, "linecount": 26,
"maxclass": "comment", "maxclass": "comment",
"numinlets": 1, "numinlets": 1,
"numoutlets": 0, "numoutlets": 0,
"patching_rect": [ 611.0, 459.0, 568.0, 368.0 ], "patching_rect": [ 611.0, 459.0, 568.0, 355.0 ],
"text": "HOW TO USE IN YOUR OWN PATCH\n\n0. Add [tc.fpcontrol] and, depending on the controls you want, [p mouse_keyboard_controls] and/or [gamepad.drive] . You can also create your own input method as it relies on simple 'move X 0 Z' and 'turn X Y 0' messages\n\n1. In order to initialize [tc.fpcontrol] you need to send 'camera' followed by the name of your jit.gl.camera or a jit.anid.node attached to it. Will serve as the basis for moving objects in view space. This is necessary to control any object. A caveheat is that this message must be sent after the world has been initalized. You can [route init] from [jit.world] right outlet for that.\n\n2. Send 'controllable' to get a list of all controllable jit.gl objects in your patch.\n \n4. Send 'control_camera' followed by the the name of your jit.gl.camera or a jit.anid.node attached to it to control the camera. If flymode is disabled, the camera will remain on the same elevation (Y world pos) unless using the elevation controls (gamepad triggers). If flymode is enabled, the camera will go in the direction it faces and elevation controls will make it move up-down relatively to that direction. \n\n5. Send 'control' followed by the name of a jit.gl or jit.anim.node object to control it in camera space. When using the name of a jit.gl object, its top-level user-created jit.anim.node will be used as entry point for the controls from v8. If there is no user-created jit.anim.node used by this object, its implicit internal jit.anim.node will be used as entry point. When using the name of a jit.anim.node directly, it will be the entry point for the controls comming from v8. If show_bounds is enabled and the target object is a bounds-compatible jit.gl object (or a jit.anim.node controlling bounds-compatible jit.gl objects), its bounds will be displayed\n\n6. Use the mouse+keyboard or gamepad to control the object or camera." "text": "HOW TO USE IN YOUR OWN PATCH\n\n0. Add [tc.fpcontrol] and, depending on the controls you want, [p mouse_keyboard_controls] and/or [gamepad.drive] . You can also create your own input method as it relies on simple 'move X 0 Z' and 'turn X Y 0' messages\n\n1. In order to use [tc.fpcontrol] you to tell it which camera (or jit.anid.node attached to it) to use. You can pass that as an attribute as shown here. This camera/anim.node will serve as the basis for moving objects in view space. This is necessary to control any object.\n\n2. Send 'controllable' to get a list of all controllable jit.gl objects in your patch.\n \n4. Send 'control_camera' followed by the the name of your jit.gl.camera or a jit.anid.node attached to it to control the camera. If flymode is disabled, the camera will remain on the same elevation (Y world pos) unless using the elevation controls (gamepad triggers). If flymode is enabled, the camera will go in the direction it faces and elevation controls will make it move up-down relatively to that direction. \n\n5. Send 'control' followed by the name of a jit.gl or jit.anim.node object to control it in camera space. When using the name of a jit.gl object, its top-level user-created jit.anim.node will be used as entry point for the controls from v8. If there is no user-created jit.anim.node used by this object, its implicit internal jit.anim.node will be used as entry point. When using the name of a jit.anim.node directly, it will be the entry point for the controls comming from v8. If show_bounds is enabled and the target object is a bounds-compatible jit.gl object (or a jit.anim.node controlling bounds-compatible jit.gl objects), its bounds will be displayed\n\n6. Use the mouse+keyboard or gamepad to control the object or camera."
} }
}, },
{ {
@@ -1170,9 +1192,42 @@
"modernui": 1 "modernui": 1
}, },
"classnamespace": "box", "classnamespace": "box",
"rect": [ 478.0, 100.0, 405.0, 755.0 ], "rect": [ 573.0, 140.0, 866.0, 755.0 ],
"integercoordinates": 1, "integercoordinates": 1,
"boxes": [ "boxes": [
{
"box": {
"id": "obj-15",
"maxclass": "newobj",
"numinlets": 1,
"numoutlets": 2,
"outlettype": [ "", "" ],
"patching_rect": [ 448.0, 296.0, 275.0, 22.0 ],
"text": "jit.anim.node @name plato_anim @position -4 0 0"
}
},
{
"box": {
"id": "obj-14",
"maxclass": "newobj",
"numinlets": 1,
"numoutlets": 2,
"outlettype": [ "", "" ],
"patching_rect": [ 27.0, 296.0, 272.0, 22.0 ],
"text": "jit.anim.node @name torus_anim @position 4 0 0"
}
},
{
"box": {
"id": "obj-3",
"maxclass": "newobj",
"numinlets": 1,
"numoutlets": 2,
"outlettype": [ "", "" ],
"patching_rect": [ 27.0, 231.0, 199.0, 22.0 ],
"text": "jit.anim.node @name torus_&_plato"
}
},
{ {
"box": { "box": {
"id": "obj-6", "id": "obj-6",
@@ -1180,7 +1235,7 @@
"numinlets": 0, "numinlets": 0,
"numoutlets": 1, "numoutlets": 1,
"outlettype": [ "" ], "outlettype": [ "" ],
"patching_rect": [ 59.0, 595.0, 61.0, 22.0 ], "patching_rect": [ 20.0, 635.0, 61.0, 22.0 ],
"text": "r reset_all" "text": "r reset_all"
} }
}, },
@@ -1192,7 +1247,7 @@
"numinlets": 2, "numinlets": 2,
"numoutlets": 1, "numoutlets": 1,
"outlettype": [ "" ], "outlettype": [ "" ],
"patching_rect": [ 59.0, 625.0, 91.0, 35.0 ], "patching_rect": [ 20.0, 665.0, 91.0, 35.0 ],
"text": "anim_reset, position 0 0 0" "text": "anim_reset, position 0 0 0"
} }
}, },
@@ -1203,7 +1258,7 @@
"numinlets": 1, "numinlets": 1,
"numoutlets": 1, "numoutlets": 1,
"outlettype": [ "" ], "outlettype": [ "" ],
"patching_rect": [ 163.0, 514.0, 54.0, 22.0 ], "patching_rect": [ 124.0, 554.0, 54.0, 22.0 ],
"text": "deferlow" "text": "deferlow"
} }
}, },
@@ -1216,7 +1271,7 @@
"numinlets": 2, "numinlets": 2,
"numoutlets": 1, "numoutlets": 1,
"outlettype": [ "" ], "outlettype": [ "" ],
"patching_rect": [ 531.0, 638.0, 137.0, 23.0 ], "patching_rect": [ 492.0, 678.0, 137.0, 23.0 ],
"text": "read baluster-vase.gltf" "text": "read baluster-vase.gltf"
} }
}, },
@@ -1229,7 +1284,7 @@
"numinlets": 2, "numinlets": 2,
"numoutlets": 1, "numoutlets": 1,
"outlettype": [ "" ], "outlettype": [ "" ],
"patching_rect": [ 330.0, 638.0, 189.0, 23.0 ], "patching_rect": [ 291.0, 678.0, 189.0, 23.0 ],
"text": "read pocillopora-damicornis.gltf" "text": "read pocillopora-damicornis.gltf"
} }
}, },
@@ -1240,7 +1295,7 @@
"numinlets": 1, "numinlets": 1,
"numoutlets": 1, "numoutlets": 1,
"outlettype": [ "" ], "outlettype": [ "" ],
"patching_rect": [ 483.0, 547.0, 70.0, 22.0 ], "patching_rect": [ 444.0, 587.0, 70.0, 22.0 ],
"text": "loadmess 2" "text": "loadmess 2"
} }
}, },
@@ -1253,7 +1308,7 @@
"numoutlets": 2, "numoutlets": 2,
"outlettype": [ "", "bang" ], "outlettype": [ "", "bang" ],
"parameter_enable": 0, "parameter_enable": 0,
"patching_rect": [ 576.0, 576.0, 50.0, 22.0 ] "patching_rect": [ 537.0, 616.0, 50.0, 22.0 ]
} }
}, },
{ {
@@ -1263,7 +1318,7 @@
"numinlets": 2, "numinlets": 2,
"numoutlets": 1, "numoutlets": 1,
"outlettype": [ "" ], "outlettype": [ "" ],
"patching_rect": [ 576.0, 603.0, 86.0, 22.0 ], "patching_rect": [ 537.0, 643.0, 86.0, 22.0 ],
"text": "metalness $1" "text": "metalness $1"
} }
}, },
@@ -1276,7 +1331,7 @@
"numoutlets": 2, "numoutlets": 2,
"outlettype": [ "", "bang" ], "outlettype": [ "", "bang" ],
"parameter_enable": 0, "parameter_enable": 0,
"patching_rect": [ 483.0, 576.0, 50.0, 22.0 ] "patching_rect": [ 444.0, 616.0, 50.0, 22.0 ]
} }
}, },
{ {
@@ -1286,7 +1341,7 @@
"numinlets": 2, "numinlets": 2,
"numoutlets": 1, "numoutlets": 1,
"outlettype": [ "" ], "outlettype": [ "" ],
"patching_rect": [ 483.0, 603.0, 87.0, 22.0 ], "patching_rect": [ 444.0, 643.0, 87.0, 22.0 ],
"text": "roughness $1" "text": "roughness $1"
} }
}, },
@@ -1297,7 +1352,7 @@
"numinlets": 1, "numinlets": 1,
"numoutlets": 1, "numoutlets": 1,
"outlettype": [ "bang" ], "outlettype": [ "bang" ],
"patching_rect": [ 163.0, 482.0, 58.0, 22.0 ], "patching_rect": [ 124.0, 522.0, 58.0, 22.0 ],
"text": "loadbang" "text": "loadbang"
} }
}, },
@@ -1308,7 +1363,7 @@
"numinlets": 2, "numinlets": 2,
"numoutlets": 2, "numoutlets": 2,
"outlettype": [ "", "" ], "outlettype": [ "", "" ],
"patching_rect": [ 163.0, 603.0, 264.0, 22.0 ], "patching_rect": [ 124.0, 643.0, 264.0, 22.0 ],
"text": "substitute jit_gl_texture environment_texture" "text": "substitute jit_gl_texture environment_texture"
} }
}, },
@@ -1319,7 +1374,7 @@
"numinlets": 1, "numinlets": 1,
"numoutlets": 1, "numoutlets": 1,
"outlettype": [ "" ], "outlettype": [ "" ],
"patching_rect": [ 163.0, 638.0, 145.0, 22.0 ], "patching_rect": [ 124.0, 678.0, 145.0, 22.0 ],
"text": "prepend sendmaterial 1" "text": "prepend sendmaterial 1"
} }
}, },
@@ -1330,7 +1385,7 @@
"numinlets": 1, "numinlets": 1,
"numoutlets": 2, "numoutlets": 2,
"outlettype": [ "", "" ], "outlettype": [ "", "" ],
"patching_rect": [ 163.0, 671.0, 378.0, 22.0 ], "patching_rect": [ 124.0, 711.0, 378.0, 22.0 ],
"text": "jit.gl.model @file baluster-vase.gltf @material_mode 3 @name model" "text": "jit.gl.model @file baluster-vase.gltf @material_mode 3 @name model"
} }
}, },
@@ -1341,7 +1396,7 @@
"numinlets": 1, "numinlets": 1,
"numoutlets": 2, "numoutlets": 2,
"outlettype": [ "", "" ], "outlettype": [ "", "" ],
"patching_rect": [ 249.0, 514.0, 100.0, 22.0 ], "patching_rect": [ 210.0, 554.0, 100.0, 22.0 ],
"text": "jit.gl.skybox" "text": "jit.gl.skybox"
} }
}, },
@@ -1352,7 +1407,7 @@
"numinlets": 1, "numinlets": 1,
"numoutlets": 2, "numoutlets": 2,
"outlettype": [ "jit_gl_texture", "" ], "outlettype": [ "jit_gl_texture", "" ],
"patching_rect": [ 163.0, 547.0, 293.0, 22.0 ], "patching_rect": [ 124.0, 587.0, 293.0, 22.0 ],
"text": "jit.gl.environment @file umhlanga_sunrise_2k.exr" "text": "jit.gl.environment @file umhlanga_sunrise_2k.exr"
} }
}, },
@@ -1363,7 +1418,7 @@
"numinlets": 2, "numinlets": 2,
"numoutlets": 1, "numoutlets": 1,
"outlettype": [ "" ], "outlettype": [ "" ],
"patching_rect": [ 210.0, 301.0, 265.0, 22.0 ], "patching_rect": [ 780.0, 351.0, 265.0, 22.0 ],
"text": "position 0 -2 0, rotatexyz -90 0 0, scale 10 10 10" "text": "position 0 -2 0, rotatexyz -90 0 0, scale 10 10 10"
} }
}, },
@@ -1374,7 +1429,7 @@
"numinlets": 1, "numinlets": 1,
"numoutlets": 2, "numoutlets": 2,
"outlettype": [ "bang", "bang" ], "outlettype": [ "bang", "bang" ],
"patching_rect": [ 27.0, 236.0, 32.0, 22.0 ], "patching_rect": [ 780.0, 187.0, 32.0, 22.0 ],
"text": "t b b" "text": "t b b"
} }
}, },
@@ -1385,7 +1440,7 @@
"numinlets": 0, "numinlets": 0,
"numoutlets": 1, "numoutlets": 1,
"outlettype": [ "" ], "outlettype": [ "" ],
"patching_rect": [ 27.0, 209.0, 61.0, 22.0 ], "patching_rect": [ 780.0, 160.0, 61.0, 22.0 ],
"text": "r reset_all" "text": "r reset_all"
} }
}, },
@@ -1396,7 +1451,7 @@
"numinlets": 2, "numinlets": 2,
"numoutlets": 1, "numoutlets": 1,
"outlettype": [ "" ], "outlettype": [ "" ],
"patching_rect": [ 119.0, 301.0, 84.0, 22.0 ], "patching_rect": [ 448.0, 267.0, 84.0, 22.0 ],
"text": "position -4 0 0" "text": "position -4 0 0"
} }
}, },
@@ -1407,7 +1462,7 @@
"numinlets": 2, "numinlets": 2,
"numoutlets": 1, "numoutlets": 1,
"outlettype": [ "" ], "outlettype": [ "" ],
"patching_rect": [ 27.0, 301.0, 80.0, 22.0 ], "patching_rect": [ 220.0, 267.0, 80.0, 22.0 ],
"text": "position 4 0 0" "text": "position 4 0 0"
} }
}, },
@@ -1429,7 +1484,7 @@
"numinlets": 2, "numinlets": 2,
"numoutlets": 1, "numoutlets": 1,
"outlettype": [ "" ], "outlettype": [ "" ],
"patching_rect": [ 40.0, 268.0, 68.0, 22.0 ], "patching_rect": [ 793.0, 216.0, 68.0, 22.0 ],
"text": "anim_reset" "text": "anim_reset"
} }
}, },
@@ -1440,8 +1495,8 @@
"numinlets": 1, "numinlets": 1,
"numoutlets": 1, "numoutlets": 1,
"outlettype": [ "" ], "outlettype": [ "" ],
"patching_rect": [ 440.0, 140.0, 559.0, 22.0 ], "patching_rect": [ 364.0, 110.0, 650.0, 22.0 ],
"text": "jit.gl.light @shadows 1 @shadowquality hi @direction 0.4 -0.8 -1. @shadowblur 0.1 @shadowrange 50." "text": "jit.gl.light @name dir_light @shadows 1 @shadowquality hi @direction 0.4 -0.8 -1. @shadowblur 0.1 @shadowrange 50."
} }
}, },
{ {
@@ -1462,7 +1517,7 @@
"numinlets": 8, "numinlets": 8,
"numoutlets": 2, "numoutlets": 2,
"outlettype": [ "", "" ], "outlettype": [ "", "" ],
"patching_rect": [ 210.0, 236.0, 117.0, 22.0 ], "patching_rect": [ 27.0, 453.0, 117.0, 22.0 ],
"text": "jit.gl.pbr @name pbr" "text": "jit.gl.pbr @name pbr"
} }
}, },
@@ -1473,7 +1528,7 @@
"numinlets": 1, "numinlets": 1,
"numoutlets": 2, "numoutlets": 2,
"outlettype": [ "", "" ], "outlettype": [ "", "" ],
"patching_rect": [ 27.0, 404.0, 613.0, 22.0 ], "patching_rect": [ 27.0, 402.0, 613.0, 22.0 ],
"text": "jit.gl.gridshape @name plane @shape plane @material pbr @position 0 -2 0 @rotatexyz -90 0 0 @scale 10 10 10" "text": "jit.gl.gridshape @name plane @shape plane @material pbr @position 0 -2 0 @rotatexyz -90 0 0 @scale 10 10 10"
} }
}, },
@@ -1484,8 +1539,8 @@
"numinlets": 1, "numinlets": 1,
"numoutlets": 2, "numoutlets": 2,
"outlettype": [ "", "" ], "outlettype": [ "", "" ],
"patching_rect": [ 27.0, 368.0, 383.0, 22.0 ], "patching_rect": [ 448.0, 351.0, 293.0, 22.0 ],
"text": "jit.gl.plato @name plato @shape icosa @material pbr @position -4 0 0", "text": "jit.gl.plato @name plato @shape icosa @material pbr",
"varname": "icosa" "varname": "icosa"
} }
}, },
@@ -1496,8 +1551,8 @@
"numinlets": 1, "numinlets": 1,
"numoutlets": 2, "numoutlets": 2,
"outlettype": [ "", "" ], "outlettype": [ "", "" ],
"patching_rect": [ 27.0, 332.0, 405.0, 22.0 ], "patching_rect": [ 27.0, 351.0, 319.0, 22.0 ],
"text": "jit.gl.gridshape @name torus @shape torus @material pbr @position 4 0 0", "text": "jit.gl.gridshape @name torus @shape torus @material pbr",
"varname": "torus" "varname": "torus"
} }
}, },
@@ -1522,7 +1577,7 @@
"numoutlets": 1, "numoutlets": 1,
"outlettype": [ "" ], "outlettype": [ "" ],
"parameter_enable": 0, "parameter_enable": 0,
"patching_rect": [ 440.0, 109.0, 206.0, 22.0 ] "patching_rect": [ 364.0, 79.0, 206.0, 22.0 ]
} }
}, },
{ {
@@ -1546,7 +1601,7 @@
"numoutlets": 1, "numoutlets": 1,
"outlettype": [ "" ], "outlettype": [ "" ],
"parameter_enable": 0, "parameter_enable": 0,
"patching_rect": [ 654.0, 109.0, 150.0, 22.0 ] "patching_rect": [ 585.0, 79.0, 150.0, 22.0 ]
} }
}, },
{ {
@@ -1558,7 +1613,7 @@
"numoutlets": 1, "numoutlets": 1,
"outlettype": [ "" ], "outlettype": [ "" ],
"parameter_enable": 0, "parameter_enable": 0,
"patching_rect": [ 812.0, 109.0, 210.0, 22.0 ] "patching_rect": [ 736.0, 79.0, 210.0, 22.0 ]
} }
} }
], ],
@@ -1581,6 +1636,12 @@
"source": [ "obj-12", 0 ] "source": [ "obj-12", 0 ]
} }
}, },
{
"patchline": {
"destination": [ "obj-5", 0 ],
"source": [ "obj-14", 0 ]
}
},
{ {
"patchline": { "patchline": {
"destination": [ "obj-30", 0 ], "destination": [ "obj-30", 0 ],
@@ -1595,13 +1656,19 @@
}, },
{ {
"patchline": { "patchline": {
"destination": [ "obj-5", 0 ], "destination": [ "obj-7", 0 ],
"source": [ "obj-15", 0 ]
}
},
{
"patchline": {
"destination": [ "obj-14", 0 ],
"source": [ "obj-170", 0 ] "source": [ "obj-170", 0 ]
} }
}, },
{ {
"patchline": { "patchline": {
"destination": [ "obj-7", 0 ], "destination": [ "obj-15", 0 ],
"source": [ "obj-172", 0 ] "source": [ "obj-172", 0 ]
} }
}, },
@@ -1629,6 +1696,20 @@
"source": [ "obj-29", 0 ] "source": [ "obj-29", 0 ]
} }
}, },
{
"patchline": {
"destination": [ "obj-14", 0 ],
"order": 1,
"source": [ "obj-3", 0 ]
}
},
{
"patchline": {
"destination": [ "obj-15", 0 ],
"order": 0,
"source": [ "obj-3", 0 ]
}
},
{ {
"patchline": { "patchline": {
"destination": [ "obj-4", 0 ], "destination": [ "obj-4", 0 ],
@@ -1703,22 +1784,29 @@
}, },
{ {
"patchline": { "patchline": {
"destination": [ "obj-5", 0 ], "destination": [ "obj-14", 0 ],
"order": 2, "order": 2,
"source": [ "obj-83", 0 ] "source": [ "obj-83", 0 ]
} }
}, },
{ {
"patchline": { "patchline": {
"destination": [ "obj-7", 0 ], "destination": [ "obj-15", 0 ],
"order": 1, "order": 0,
"source": [ "obj-83", 0 ]
}
},
{
"patchline": {
"destination": [ "obj-3", 0 ],
"order": 3,
"source": [ "obj-83", 0 ] "source": [ "obj-83", 0 ]
} }
}, },
{ {
"patchline": { "patchline": {
"destination": [ "obj-8", 0 ], "destination": [ "obj-8", 0 ],
"order": 0, "order": 1,
"source": [ "obj-83", 0 ] "source": [ "obj-83", 0 ]
} }
}, },
@@ -1855,13 +1943,13 @@
{ {
"box": { "box": {
"id": "obj-56", "id": "obj-56",
"items": [ "jit.gl.gridshape", "torus", ",", "jit.gl.plato", "plato", ",", "jit.gl.gridshape", "plane", ",", "jit.gl.light", "u966000628", ",", "jit.gl.model", "model" ], "items": [ "jit.gl.gridshape", "torus", ",", "jit.gl.plato", "plato", ",", "jit.gl.gridshape", "plane", ",", "jit.gl.light", "dir_light", ",", "jit.gl.model", "model", ",", "jit.anim.node", "torus_&_plato", ",", "jit.anim.node", "torus_anim", ",", "jit.anim.node", "plato_anim" ],
"maxclass": "umenu", "maxclass": "umenu",
"numinlets": 1, "numinlets": 1,
"numoutlets": 3, "numoutlets": 3,
"outlettype": [ "int", "", "" ], "outlettype": [ "int", "", "" ],
"parameter_enable": 0, "parameter_enable": 0,
"patching_rect": [ 129.0, 533.0, 184.0, 22.0 ] "patching_rect": [ 183.0, 522.0, 184.0, 22.0 ]
} }
}, },
{ {
@@ -1886,7 +1974,7 @@
"maxclass": "comment", "maxclass": "comment",
"numinlets": 1, "numinlets": 1,
"numoutlets": 0, "numoutlets": 0,
"patching_rect": [ 18.0, 684.0, 257.0, 66.0 ], "patching_rect": [ 36.0, 654.0, 257.0, 66.0 ],
"text": "'control' outputs the given object name to be controlled (jit.gl or jit.anim.node), followed by the actual one being controlled (jit.anim.node)" "text": "'control' outputs the given object name to be controlled (jit.gl or jit.anim.node), followed by the actual one being controlled (jit.anim.node)"
} }
}, },
@@ -2427,19 +2515,8 @@
"numinlets": 1, "numinlets": 1,
"numoutlets": 2, "numoutlets": 2,
"outlettype": [ "", "" ], "outlettype": [ "", "" ],
"patching_rect": [ 720.0, 121.0, 598.0, 22.0 ], "patching_rect": [ 720.0, 121.0, 611.0, 22.0 ],
"text": "gamepad.drive @l_deadzone 0.1 @r_deadzone 0.2 @l_sensitivity 0.4 @r_sensitivity 3 @trigger_sensitivity 0.4" "text": "gamepad.drive @l_deadzone 0.15 @r_deadzone 0.15 @l_sensitivity 0.4 @r_sensitivity 3 @trigger_sensitivity 0.4"
}
},
{
"box": {
"id": "obj-125",
"maxclass": "message",
"numinlets": 2,
"numoutlets": 1,
"outlettype": [ "" ],
"patching_rect": [ 239.0, 405.0, 143.0, 22.0 ],
"text": "camera cam, controllable"
} }
}, },
{ {
@@ -2451,12 +2528,12 @@
"numinlets": 1, "numinlets": 1,
"numoutlets": 2, "numoutlets": 2,
"outlettype": [ "", "" ], "outlettype": [ "", "" ],
"patching_rect": [ 53.0, 448.0, 95.0, 22.0 ], "patching_rect": [ 53.0, 448.0, 149.0, 22.0 ],
"saved_object_attributes": { "saved_object_attributes": {
"embed": 0, "embed": 0,
"parameter_enable": 0 "parameter_enable": 0
}, },
"text": "tc.fpcontrol", "text": "tc.fpcontrol @camera cam",
"textfile": { "textfile": {
"filename": "tc.controller.js", "filename": "tc.controller.js",
"flags": 0, "flags": 0,
@@ -2472,8 +2549,8 @@
"maxclass": "comment", "maxclass": "comment",
"numinlets": 1, "numinlets": 1,
"numoutlets": 0, "numoutlets": 0,
"patching_rect": [ 444.0, 987.0, 567.0, 114.0 ], "patching_rect": [ 612.0, 834.0, 567.0, 114.0 ],
"text": "TODO:\n- use worldtolocal anim.node feature instead of manually calculating transform matrix?\n- better object rotation logic?\n- other ideas?\n\nWHAT DOESN'T WORK\n- using the implicit anim node of a jit.gl.camera doesn't work (need to declare an explicit anim node)\n- controlling a skybox doesn't work although it's technically possible and works when done manually" "text": "TODO:\n- use worldtolocal anim.node feature instead of manually calculating transform matrix?\n- better object rotation logic?\n- other ideas?\n\nWHAT DOESN'T WORK\n- using the implicit anim node of a jit.gl.camera doesn't work (need to create an anim.node for it)\n- controlling a skybox doesn't work although it's technically possible and works when done manually"
} }
}, },
{ {
@@ -2503,13 +2580,11 @@
"box": { "box": {
"dontreplace": 1, "dontreplace": 1,
"id": "obj-60", "id": "obj-60",
"linecount": 2,
"maxclass": "message", "maxclass": "message",
"numinlets": 2, "numinlets": 2,
"numoutlets": 1, "numoutlets": 1,
"outlettype": [ "" ], "outlettype": [ "" ],
"patching_rect": [ 53.0, 647.0, 222.0, 35.0 ], "patching_rect": [ 53.0, 630.0, 222.0, 22.0 ]
"text": "control camera_node_implicit camera_node_implicit"
} }
}, },
{ {
@@ -2569,7 +2644,7 @@
"numoutlets": 1, "numoutlets": 1,
"outlettype": [ "" ], "outlettype": [ "" ],
"parameter_enable": 0, "parameter_enable": 0,
"patching_rect": [ 53.0, 346.0, 97.0, 22.0 ], "patching_rect": [ 53.0, 274.0, 97.0, 22.0 ],
"text_width": 46.0 "text_width": 46.0
} }
}, },
@@ -2582,7 +2657,7 @@
"numoutlets": 1, "numoutlets": 1,
"outlettype": [ "" ], "outlettype": [ "" ],
"parameter_enable": 0, "parameter_enable": 0,
"patching_rect": [ 53.0, 318.0, 87.0, 22.0 ], "patching_rect": [ 53.0, 298.0, 87.0, 22.0 ],
"text_width": 63.0 "text_width": 63.0
} }
}, },
@@ -2595,7 +2670,8 @@
"numoutlets": 1, "numoutlets": 1,
"outlettype": [ "" ], "outlettype": [ "" ],
"parameter_enable": 0, "parameter_enable": 0,
"patching_rect": [ 53.0, 376.0, 121.0, 22.0 ] "patching_rect": [ 53.0, 346.0, 121.0, 22.0 ],
"text_width": 97.0
} }
}, },
{ {
@@ -2610,6 +2686,32 @@
"patching_rect": [ 35.5, 63.0, 83.0, 22.0 ], "patching_rect": [ 35.5, 63.0, 83.0, 22.0 ],
"text_width": 61.0 "text_width": 61.0
} }
},
{
"box": {
"attr": "camera",
"id": "obj-7",
"maxclass": "attrui",
"numinlets": 1,
"numoutlets": 1,
"outlettype": [ "" ],
"parameter_enable": 0,
"patching_rect": [ 53.0, 370.0, 186.0, 22.0 ],
"text_width": 61.0
}
},
{
"box": {
"attr": "controlmode",
"id": "obj-8",
"maxclass": "attrui",
"numinlets": 1,
"numoutlets": 1,
"outlettype": [ "" ],
"parameter_enable": 0,
"patching_rect": [ 53.0, 322.0, 186.0, 22.0 ],
"text_width": 87.0
}
} }
], ],
"lines": [ "lines": [
@@ -2637,12 +2739,6 @@
"source": [ "obj-11", 0 ] "source": [ "obj-11", 0 ]
} }
}, },
{
"patchline": {
"destination": [ "obj-109", 0 ],
"source": [ "obj-125", 0 ]
}
},
{ {
"patchline": { "patchline": {
"destination": [ "obj-109", 0 ], "destination": [ "obj-109", 0 ],
@@ -2685,6 +2781,18 @@
"source": [ "obj-202", 0 ] "source": [ "obj-202", 0 ]
} }
}, },
{
"patchline": {
"destination": [ "obj-3", 0 ],
"source": [ "obj-22", 0 ]
}
},
{
"patchline": {
"destination": [ "obj-3", 0 ],
"source": [ "obj-25", 0 ]
}
},
{ {
"patchline": { "patchline": {
"destination": [ "obj-56", 0 ], "destination": [ "obj-56", 0 ],
@@ -2699,13 +2807,8 @@
}, },
{ {
"patchline": { "patchline": {
"destination": [ "obj-4", 0 ], "destination": [ "obj-109", 0 ],
"source": [ "obj-3", 2 ] "midpoints": [ 274.5, 573.0, 39.0, 573.0, 39.0, 444.0, 62.5, 444.0 ],
}
},
{
"patchline": {
"destination": [ "obj-32", 0 ],
"source": [ "obj-31", 0 ] "source": [ "obj-31", 0 ]
} }
}, },
@@ -2729,10 +2832,16 @@
}, },
{ {
"patchline": { "patchline": {
"destination": [ "obj-125", 0 ], "destination": [ "obj-109", 0 ],
"source": [ "obj-4", 0 ] "source": [ "obj-4", 0 ]
} }
}, },
{
"patchline": {
"destination": [ "obj-3", 0 ],
"source": [ "obj-46", 0 ]
}
},
{ {
"patchline": { "patchline": {
"destination": [ "obj-109", 0 ], "destination": [ "obj-109", 0 ],
@@ -2762,6 +2871,18 @@
"destination": [ "obj-109", 0 ], "destination": [ "obj-109", 0 ],
"source": [ "obj-62", 0 ] "source": [ "obj-62", 0 ]
} }
},
{
"patchline": {
"destination": [ "obj-109", 0 ],
"source": [ "obj-7", 0 ]
}
},
{
"patchline": {
"destination": [ "obj-109", 0 ],
"source": [ "obj-8", 0 ]
}
} }
], ],
"autosave": 0 "autosave": 0

View File

@@ -22,8 +22,9 @@ autowatch = 1;
outlets = 2; outlets = 2;
var drawto = ""; let world_bangs = false; // is the world banging?
declareattribute({ name: "drawto", setter: "setdrawto" }); const ctx_finder = require("implicit.context.js");
ctx_finder.register_drawto(this, dosetdrawto, set_root);
var ease = 0.1; var ease = 0.1;
declareattribute({ declareattribute({
@@ -35,10 +36,10 @@ declareattribute({
default: 0.1, default: 0.1,
}); });
var camera_node = ""; var camera = "";
declareattribute({ declareattribute({
name: "camera_node", name: "camera",
setter: "camera", setter: "set_camera",
label: "Camera anim node", label: "Camera anim node",
type: "string", type: "string",
}); });
@@ -51,6 +52,15 @@ declareattribute({
default: 0, default: 0,
}); });
var controlmode = 0;
declareattribute({
name: "controlmode",
setter: "set_controlmode",
style: "enumindex",
enumvals: ["View space", "Object space"],
label: "Control Mode",
})
var show_bounds = 1; var show_bounds = 1;
declareattribute({ declareattribute({
name: "show_bounds", name: "show_bounds",
@@ -81,7 +91,6 @@ const ANIMABLE_GL_OBJECTS = [
"jit_gl_camera", "jit_gl_camera",
]; ];
let controlled_gl_objects = []; // Used for enabling.disabling drawbounds on object(s) animated by the controller
let controllable_objects = []; // Used for listing all controllable objects in a patch let controllable_objects = []; // Used for listing all controllable objects in a patch
let top_level_patcher; let top_level_patcher;
@@ -92,7 +101,9 @@ class Controller {
this.ease = 0.1; // anim.drive easing this.ease = 0.1; // anim.drive easing
this.flymode = false; this.flymode = false;
this.flymode_applyed = false; // Flag this.flymode_applyed = false; // Flag
this.control_mode; // 'cam' if the camera is the target, 'obj' otherwise this.control_obj_type; // 'cam' if the camera is the target, 'obj' otherwise
this.root_anim_node = null; // The implicit drawto context anim node
this.world_up = [0, 1, 0]; this.world_up = [0, 1, 0];
this.camera_target = { this.camera_target = {
@@ -100,7 +111,7 @@ class Controller {
class: '', class: '',
anim: '', anim: '',
} }
this.camera_node_implicit = new JitterObject("jit.anim.node"); // Used if camera isn't attached to an anim.node this.camera_node_implicit = new JitterObject("jit.anim.node"); // Used if camera isn't attached to an anim.node (using the pre-made anim node of a jit.gl.camera doesn't work for some reason, so we use our own "implicit" one)
this.camera_node_implicit.name = "camera_node_implicit"; this.camera_node_implicit.name = "camera_node_implicit";
this.camera_node = new JitterObject("jit.proxy"); // Either proxy of a jit.anim.node defined by user, or of this.camera_node_implicit this.camera_node = new JitterObject("jit.proxy"); // Either proxy of a jit.anim.node defined by user, or of this.camera_node_implicit
this.cam_direction, this.cam_up, this.cam_right; this.cam_direction, this.cam_up, this.cam_right;
@@ -121,7 +132,8 @@ class Controller {
this.main_drive.targetname = this.main_node.name; this.main_drive.targetname = this.main_node.name;
// pitch_node used with pitch_drive for pitch rotation (over the objects local x axis) // pitch_node used with pitch_drive for pitch rotation (over the objects local x axis)
this.pitch_node = new JitterObject("jit.anim.node"); this.pitch_node = new JitterObject("jit.anim.node");
this.pitch_node.name = "fp_pitch_node";
this.pitch_node.anim = this.main_node.name; this.pitch_node.anim = this.main_node.name;
this.pitch_node.turnmode = "parent"; this.pitch_node.turnmode = "parent";
this.pitch_node.movemode = "parent"; this.pitch_node.movemode = "parent";
@@ -131,73 +143,143 @@ class Controller {
this.pitch_drive.ease = this.ease; this.pitch_drive.ease = this.ease;
this.pitch_drive.targetname = this.pitch_node.name; this.pitch_drive.targetname = this.pitch_node.name;
this.target = new JitterObject("jit.proxy"); // Stores the object to control this.target = new JitterObject("jit.proxy"); // Stores the object to control (always an anim.node)
this.last_target_anim_node = null; // Stores which parent anim.node was bound to our target, so we can restore it later
this.target_type; // Class of the target object
// For displaying bounds, we rely on our own jit.gl.mesh cube. It's simpler than keeping track of each objects draw_bounds attribute, and we can customize its appearance.
this.show_bounds = show_bounds;
this.cube_matrix = new JitterMatrix(3, "float32", 8);
const cube_points = new Float32Array([-1., -1., 1., 1., -1., 1., 1., 1., 1., -1., 1., 1., -1., -1., -1., 1., -1., -1., 1., 1., -1., -1., 1., -1.]);
this.cube_matrix.copyarraytomatrix(cube_points);
this.cube_indexes = new JitterMatrix(1, "char", 24);
const cube_point_indexes = new Uint8Array([0, 1, 1, 2, 2, 3, 3, 0, 0, 4, 1, 5, 2, 6, 3, 7, 4, 5, 5, 6, 6, 7, 7, 4]);
this.cube_indexes.copyarraytomatrix(cube_point_indexes);
this.bounds = new JitterObject("jit.gl.mesh");
this.bounds.drawto = drawto;
this.bounds.draw_mode = "lines";
this.bounds.vertex_matrix(this.cube_matrix.name);
this.bounds.index_matrix(this.cube_indexes.name);
this.bounds.anim = this.pitch_node.name;
this.bounds.enable = 0;
this.dummy_node = new JitterObject("jit.anim.node"); // Only used for quat to euler conversion
} }
// Taking control over a new target // Taking control over a new target
control(obj_name, ctrl_mode) { control(obj_name, obj_type) {
if (!this.camera_target.name) { if (!this.camera_target.name) {
error("No camera defined. Cannot control object.\n"); error("No camera defined. Cannot control object.\n");
return false; return false;
} }
// Make sure the requested object is a jit.anim.node or a controllable object (a jit.gl object with an anim attribute)
let new_target; let new_target;
if (obj_name != undefined) { if (obj_name != undefined) {
new_target = get_anim_node(obj_name, this.pitch_node.name); const p = new JitterObject("jit.proxy");
p.name = obj_name;
if (p.class === "jit_anim_node") {
new_target = obj_name;
} else if (p.class && is_controllable(p.class)) {
new_target = p.send("getanim");
};
this.target_type = p.class;
p.freepeer();
} }
if ( if (
new_target != undefined && new_target != undefined &&
(this.target.name != "" || this.target.name != new_target) (this.target.name != "" || this.target.name != new_target)
) { ) {
this.control_mode = ctrl_mode; // New object to target
this.control_obj_type = obj_type;
if (this.target.name != "") {
// If there is already a targeted object, we unbind it and make it preserve its transform
const world_pos = this.target.send("getworldpos");
const world_quat = this.target.send("getworldquat");
const world_scale = this.target.send("getworldscale");
if (this.target.name != "") { if (this.last_target_anim_node) {
const transform = this.target.send("getworldtransform"); // Get the current targets tranfsorm, this.target.send("anim", this.last_target_anim_node);
this.target.send("anim"); // Unbind it (makes it loose its transform part coming from Controller nodes) } else {
this.target.send("anim_reset"); // Reset it this.target.send("anim");
this.target.send("transform", transform); // And re-apply the transform so it stays in place }
} this.target.send("anim_reset");
this.target.name = new_target; // Bind to new target const local = this.world_to_local(this.last_target_anim_node, world_pos, world_quat);
this.reset(); // Reset this.Controller anim nodes this.target.send("position", local.pos);
this.main_node.position = this.target.send("getworldpos"); // Take the target position, rotatexyz and scale and pass them to this.Controller anim nodes this.target.send("quat", local.quat);
let target_rotatexyz = this.target.send("getrotatexyz"); this.target.send("scale", world_scale);
this.main_node.rotatexyz = [0, target_rotatexyz[1], 0]; }
this.pitch_node.rotatexyz = [target_rotatexyz[0], 0, target_rotatexyz[2]];
this.pitch_node.scale = this.target.send("getscale");
// And select the new target this.target.name = new_target; // Bind to new target
this.target.send("anim_reset"); this.reset(); // Reset controller anim nodes
this.target.send("anim", this.pitch_node.name);
// Get target transform
const world_pos = this.target.send("getworldpos");
const world_quat = this.target.send("getworldquat");
const world_scale = this.target.send("getworldscale");
this.dummy_node.quat = world_quat;
const world_rotatexyz = this.dummy_node.rotatexyz;
// Apply it to our control nodes
this.main_node.position = world_pos;
this.main_node.rotatexyz = [0, world_rotatexyz[1], 0];
this.pitch_node.rotatexyz = [world_rotatexyz[0], 0, world_rotatexyz[2]];
this.pitch_node.scale = world_scale;
// And select the new target
this.last_target_anim_node = this.target.send("getanim");
this.target.send("anim_reset");
this.target.send("anim", this.pitch_node.name);
this.target.send("update_node"); //Sometimes targetting an anim.node parent with anim.node children doesn't work (children don't get their transform updated). This solves it for some reason.
// Apply specific rules depending on if controlling the camera or an object // Apply specific rules depending on if controlling the camera or an object
if (this.control_mode == "cam") { if (this.control_obj_type == "cam") {
// If this.camera() is set before the jit.world starts, there is a chance that camera_node is bound to the wrong node.
// this.camera(this.camera_target.name);
this.main_node.movemode = "local"; this.main_node.movemode = "local";
this.main_node.turnmode = "local"; this.main_node.turnmode = "local";
this.pitch_node.turnmode = "parent"; this.pitch_node.turnmode = "parent";
this.has_rotated = false; this.set_flymode(this.flymode);
this.set_flymode(this.flymode);
} else { } else {
this.flymode_applyed = false; this.flymode_applyed = false;
// When controlling an object, we convert the translation vector if (controlmode == 0) {
// from screen space to world space in this.move // When controlling an object, we convert the translation vector
// So we need the cam base matrix // from screen space to world space in this.move
this.main_node.movemode = "world"; // So we need the cam base matrix
this.main_node.turnmode = "world"; this.main_node.movemode = "world";
this.pitch_node.turnmode = "world"; this.main_node.turnmode = "world";
this.get_cam_base_matrix(); this.pitch_node.turnmode = "world";
this.get_cam_base_matrix();
} else {
this.main_node.movemode = "local";
this.main_node.turnmode = "local";
this.pitch_node.turnmode = "parent";
}
} }
// No target } else if (obj_name == undefined && this.target.name != "") {
} else if (obj_name == undefined && this.target.name != "") { // No target, unbinding previous target
this.target.send("anim"); const world_pos = this.target.send("getworldpos");
this.target.send("anim_reset"); const world_quat = this.target.send("getworldquat");
this.target.send("transform", this.pitch_node.worldtransform); const world_scale = this.target.send("getworldscale");
this.target.name = "";
} if (this.last_target_anim_node) {
this.target.send("anim", this.last_target_anim_node);
} else {
this.target.send("anim");
}
this.target.send("anim_reset");
const local = this.world_to_local(this.last_target_anim_node, world_pos, world_quat);
this.target.send("position", local.pos);
this.target.send("quat", local.quat);
this.target.send("scale", world_scale);
this.last_target_anim_node = null;
this.target.name = "";
}
this.set_bounds();
outlet(0, "control", obj_name, this.target.name); outlet(0, "control", obj_name, this.target.name);
return true; return true;
} }
@@ -219,7 +301,6 @@ class Controller {
const transform = cam_anim.send("getworldtransform"); const transform = cam_anim.send("getworldtransform");
cam_anim.freepeer(); cam_anim.freepeer();
// Then we attach our camera_node_implicit to the camera and restore its world transform // Then we attach our camera_node_implicit to the camera and restore its world transform
// this.camera_node_implicit.anim_reset();
proxy.send("anim_reset"); proxy.send("anim_reset");
proxy.send("anim", this.camera_node_implicit.name); proxy.send("anim", this.camera_node_implicit.name);
this.camera_node_implicit.transform = transform; this.camera_node_implicit.transform = transform;
@@ -227,13 +308,12 @@ class Controller {
} }
if (target) { if (target) {
this.camera_node.name = target; this.camera_node.name = target;
camera_node = target;
this.camera_node.send("animmode", "world"); this.camera_node.send("animmode", "world");
this.get_cam_base_matrix(); // this.get_cam_base_matrix();
this.camera_target.name = proxy.name; this.camera_target.name = proxy.name;
this.camera_target.class = proxy.class; this.camera_target.class = proxy.class;
this.camera_target.anim = proxy.anim; this.camera_target.anim = proxy.anim;
camera_node = target; camera = target;
} else { } else {
this.unbind_camera(); this.unbind_camera();
} }
@@ -248,11 +328,11 @@ class Controller {
} else if (this.camera_target.class === "jit_gl_camera") { } else if (this.camera_target.class === "jit_gl_camera") {
// If it was a camera, we set it back as it was, but with the current transform // If it was a camera, we set it back as it was, but with the current transform
const transform = this.camera_node.send("getworldtransform"); const transform = this.camera_node.send("getworldtransform");
const cam = new JitterObject("jit_proxy"); const cam = new JitterObject("jit.proxy");
cam.name = this.camera_target.name; cam.name = this.camera_target.name;
cam.send("anim"); cam.send("anim");
cam.send("anim_reset"); cam.send("anim_reset");
const cam_anim = new JitterObject("jit_proxy"); const cam_anim = new JitterObject("jit.proxy");
cam_anim.name = cam.send("getanim"); cam_anim.name = cam.send("getanim");
cam_anim.send("transform", transform); cam_anim.send("transform", transform);
cam.freepeer(); cam.freepeer();
@@ -262,7 +342,7 @@ class Controller {
this.camera_target.name = ""; this.camera_target.name = "";
this.camera_target.class = ""; this.camera_target.class = "";
this.camera_target.anim = ""; this.camera_target.anim = "";
camera_node = ""; camera = "";
} }
} }
@@ -278,12 +358,13 @@ class Controller {
this.cam_up, this.cam_up,
this.cam_direction, this.cam_direction,
]; ];
} }
} }
move(x, y, z) { move(x, y, z) {
let translat_vec = [x, y, z]; let translat_vec = [x, y, z];
if (this.control_mode == "obj") { if (this.control_obj_type == "obj" && controlmode == 0) {
this.get_cam_base_matrix();
let cam_x_rot = this.camera_node.send("getrotatexyz")[0]; let cam_x_rot = this.camera_node.send("getrotatexyz")[0];
// Inverse translation direction if cam is upside down // Inverse translation direction if cam is upside down
if (cam_x_rot < -90 || cam_x_rot > 90) { if (cam_x_rot < -90 || cam_x_rot > 90) {
@@ -296,12 +377,13 @@ class Controller {
} }
turn(x, y, z) { turn(x, y, z) {
let rot_vec = [x, y, z]; let rot_vec = [x, y, z];
if (this.control_mode == "obj") { if (this.control_obj_type == "obj") {
this.get_cam_base_matrix();
rot_vec = multiplyMatrixVector(this.camera_base_matrix, [-x, 0, 0]); rot_vec = multiplyMatrixVector(this.camera_base_matrix, [-x, 0, 0]);
rot_vec[1] = -y; rot_vec[1] = -y;
} }
if (this.flymode && this.control_mode == "cam") { if (this.flymode && this.control_obj_type == "cam") {
// When flymode is enabled, we apply all rotations to the main_node // When flymode is enabled, we apply all rotations to the main_node
// so that the cam can move toward the direction it is pointing to // so that the cam can move toward the direction it is pointing to
this.main_drive.turn(rot_vec[0], rot_vec[1], 0); this.main_drive.turn(rot_vec[0], rot_vec[1], 0);
@@ -328,7 +410,7 @@ class Controller {
} }
apply_flymode() { apply_flymode() {
if (this.control_mode == "cam") { if (this.control_obj_type == "cam") {
if (this.flymode) { if (this.flymode) {
// When enabling flymode, we move all rotations to main_node // When enabling flymode, we move all rotations to main_node
this.main_node.rotatexyz = [ this.main_node.rotatexyz = [
@@ -351,7 +433,8 @@ class Controller {
set_drawto(v) { set_drawto(v) {
this.main_drive.drawto = v; this.main_drive.drawto = v;
this.pitch_drive.drawto = v; this.pitch_drive.drawto = v;
this.bounds.drawto = v;
} }
set_ease(v) { set_ease(v) {
@@ -362,14 +445,47 @@ class Controller {
reset() { reset() {
this.main_node.anim_reset(); this.main_node.anim_reset();
this.pitch_node.anim_reset(); this.pitch_node.anim_reset();
} }
resync() { resync() {
if (this.target.name != "") { if (this.target.name != "") {
this.control(this.target.name, this.control_mode); this.control(this.target.name, this.control_obj_type);
} }
} }
set_show_bounds(v) {
this.show_bounds = v ? 1 : 0;
this.set_bounds();
}
set_bounds() {
if (this.show_bounds && this.control_obj_type === "obj") {
switch (this.target_type) {
case "jit_anim_node":
this.bounds.color = [1, 0, 1, 1];
break;
default:
this.bounds.color = [1, 1, 1, 1];
break;
}
this.bounds.enable = 1;
} else {
this.bounds.enable = 0;
}
}
world_to_local(anim_node_name, world_pos, world_quat) {
if (!anim_node_name) {
return { pos: world_pos, quat: world_quat };
}
const parent = new JitterObject("jit.proxy");
parent.name = anim_node_name;
const pos = parent.send("worldtolocal", world_pos);
const quat = parent.send("worldtolocal_quat", world_quat);
parent.freepeer();
return { pos, quat };
}
destroy() { destroy() {
if (this.target.name != "") { if (this.target.name != "") {
@@ -381,36 +497,34 @@ class Controller {
this.main_drive.freepeer(); this.main_drive.freepeer();
this.pitch_node.freepeer(); this.pitch_node.freepeer();
this.pitch_drive.freepeer(); this.pitch_drive.freepeer();
this.target.freepeer(); this.target.freepeer();
this.cube_indexes.freepeer();
this.cube_matrix.freepeer();
this.bounds.freepeer();
this.dummy_node.freepeer();
} }
} }
loadbang(); loadbang();
// Select the object to use as basis for view-space transforms // Select the object to use as basis for view-space transforms
function camera(name) { function set_camera(name) {
ctlr.camera(name); camera = name;
camera_node = ctlr.camera_node.name; if (world_bangs) {
ctlr.camera(name);
}
} }
// Select which object to control in view-space // Select which object to control in view-space
function control(obj_name) { function control(obj_name) {
ctlr.control(obj_name, "obj"); ctlr.control(obj_name, "obj");
if (show_bounds) {
do_show_bounds(0);
}
controlled_gl_objects = [];
get_gl_obj_controlled_by_ctlr();
if (show_bounds) {
do_show_bounds(show_bounds);
}
} }
// Select which camera to control // Select which camera to control
function control_camera(obj_name) { function control_camera(obj_name) {
if (obj_name !== undefined) { if (obj_name !== undefined) {
const answer = ctlr.control(obj_name, "cam"); const answer = ctlr.control(obj_name, "cam");
if (answer) do_show_bounds(0); // if (answer) do_show_bounds(0);
} }
} }
@@ -472,31 +586,26 @@ function set_ease(v) {
ctlr.set_ease(v); ctlr.set_ease(v);
ease = v; ease = v;
} }
set_ease.local = 1;
function set_flymode(v) { function set_flymode(v) {
flymode = v == undefined ? !flymode : v == 1; // If flymode with no argument is provided, act as a toggle flymode = v == undefined ? !flymode : v == 1; // If flymode with no argument is provided, act as a toggle
ctlr.set_flymode(flymode); ctlr.set_flymode(flymode);
} }
set_flymode.local = 1;
function set_show_bounds(v) { function set_show_bounds(v) {
show_bounds = v == true; show_bounds = v == true;
// ctlr.show_bounds = show_bounds; ctlr.set_show_bounds(show_bounds);
do_show_bounds(show_bounds);
} }
set_show_bounds.local = 1;
function do_show_bounds(v) { function set_controlmode(v) {
const objects_without_bounds = [ const new_val = v > 0 ? 1 : 0;
"jit.gl.sketch", if (controlmode === new_val) return;
"jit.gl.skybox", controlmode = new_val;
"jit.gl.camera", ctlr.control(ctlr.target.name, ctlr.control_obj_type);
];
for (const obj of controlled_gl_objects) {
if (!objects_without_bounds.includes(obj.maxclass)) {
obj.setattr("drawbounds", v);
}
}
} }
do_show_bounds.local = 1;
function bang() { function bang() {
outlet(0, ctlr.pitch_node.worldtransform); outlet(0, ctlr.pitch_node.worldtransform);
@@ -511,63 +620,21 @@ function loadbang() {
function notifydeleted() { function notifydeleted() {
ctlr.destroy(); ctlr.destroy();
implicit_tracker.freepeer(); ctx_finder.dispose();
if (ctx_lstnr) ctx_lstnr.freepeer();
// implicit_tracker.freepeer();
} }
///////////////////////////////////////////// /////////////////////////////////////////////
// HELPER METHODS // HELPER METHODS
///////////////////////////////////////////// /////////////////////////////////////////////
function get_anim_node(jit_obj_name, ctlr_node) {
const proxy = new JitterObject("jit.proxy");
proxy.name = jit_obj_name;
const proxy_class = proxy.class;
proxy.freepeer();
if (proxy_class == "jit_anim_node") {
return jit_obj_name;
} else if (proxy_class != "" && is_controllable(proxy_class)) {
const context_anim = get_context_anim(jit_obj_name);
return get_top_level_anim_node(jit_obj_name, context_anim, ctlr_node);
}
return;
}
get_anim_node.local = 1;
function is_controllable(obj_class) { function is_controllable(obj_class) {
// Using jit.proxy syntax (ie 'jit_gl_mesh' and not 'jit.gl.mesh') // Using jit.proxy syntax (ie 'jit_gl_mesh' and not 'jit.gl.mesh')
return ANIMABLE_GL_OBJECTS.includes(obj_class); return ANIMABLE_GL_OBJECTS.includes(obj_class);
} }
is_controllable.local = 1; is_controllable.local = 1;
function get_context_anim(jit_obj_name) {
// Get the first implicit jit.anim.node level for the object's rendering context
const proxy = new JitterObject("jit.proxy");
proxy.name = jit_obj_name;
proxy.name = proxy.send("getdrawto");
const context_name = proxy.send("getanim");
proxy.freepeer();
return context_name;
}
get_context_anim.local = 1;
function get_top_level_anim_node(jit_obj_name, context_anim, ctlr_node) {
// Recursively get the top level jit.anim.node before reaching context_anim, or no anim.node, or this.pitch_node (if trying to controller currently controlled object)
const proxy = new JitterObject("jit.proxy");
proxy.name = jit_obj_name;
const parent_name = proxy.send("getanim").toString();
proxy.freepeer();
if (
parent_name == "" ||
parent_name == context_anim ||
parent_name == ctlr_node
) {
return jit_obj_name;
} else {
return get_top_level_anim_node(parent_name, context_anim, ctlr_node);
}
}
get_top_level_anim_node.local = 1;
function get_top_level_patcher() { function get_top_level_patcher() {
let prev = 0; let prev = 0;
let owner = this.patcher.box; let owner = this.patcher.box;
@@ -579,37 +646,6 @@ function get_top_level_patcher() {
} }
get_top_level_patcher.local = 1; get_top_level_patcher.local = 1;
// Get gl object(s) controlled by ctlr (for the sole purpose of drawing bounds of selected object)
function get_gl_obj_controlled_by_ctlr() {
top_level_patcher.applydeepif(
add_to_controlled_obj_list,
is_controlled_by_ctlr
);
}
get_gl_obj_controlled_by_ctlr.local = 1;
function is_controlled_by_ctlr(obj) {
if (is_controllable(obj.maxclass.replaceAll(".", "_"))) {
const obj_anim = obj.getattr("anim");
if (obj_anim == ctlr.pitch_node.name) {
return true;
} else if (obj_anim != "") {
const proxy = new JitterObject("jit.proxy");
proxy.name = obj_anim;
const parent_name = proxy.send("getanim").toString();
proxy.freepeer();
return parent_name == ctlr.pitch_node.name;
}
}
return false;
}
is_controlled_by_ctlr.local = 1;
function add_to_controlled_obj_list(obj) {
controlled_gl_objects.push(obj);
}
add_to_controlled_obj_list.local = 1;
///////////////////////////////////////////// /////////////////////////////////////////////
// MATHS // MATHS
///////////////////////////////////////////// /////////////////////////////////////////////
@@ -647,29 +683,33 @@ multiplyMatrixVector.local = 1;
///////////////////////////////////////////// /////////////////////////////////////////////
// GL Context // GL Context
///////////////////////////////////////////// /////////////////////////////////////////////
let ctx_root;
let implicitdrawto = ""; let ctx_lstnr;
let explicitdrawto = false; function set_root(new_root) {
const implicit_tracker = new JitterObject("jit_gl_implicit"); ctx_root = new_root;
const implicit_lstnr = new JitterListener( if (ctx_lstnr) {
implicit_tracker.name, ctx_lstnr.freepeer();
implicit_callback ctw_lstnr = null;
); }
ctx_lstnr = new JitterListener(new_root, ctx_callback);
function implicit_callback(event) {
if (!explicitdrawto && implicitdrawto != implicit_tracker.drawto[0]) {
// important! drawto is an array so get first element
implicitdrawto = implicit_tracker.drawto[0];
dosetdrawto(implicitdrawto);
}
} }
implicit_callback.local = 1; set_root.local = 1;
function setdrawto(val) { function ctx_callback(event) {
explicitdrawto = true; switch (event.eventname) {
dosetdrawto(val); case "swap": case "draw":
if (!world_bangs) {
// First world bang: world initialized, we can set the camera
world_bangs = true;
set_camera(camera);
}
break;
default:
// post(event.args); post();
break;
}
} }
setdrawto.local = 1; ctx_callback.local = 1;
function dosetdrawto(arg) { function dosetdrawto(arg) {
if (arg === drawto || !arg) { if (arg === drawto || !arg) {
@@ -679,5 +719,7 @@ function dosetdrawto(arg) {
drawto = arg; drawto = arg;
ctlr.set_drawto(drawto); ctlr.set_drawto(drawto);
ctx_proxy.name = drawto;
} }
dosetdrawto.local = 1; dosetdrawto.local = 1;