From 565d4c31f43c57afab846a425c0be9cb55562d11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Clet?= Date: Sat, 4 Jul 2026 11:53:09 +0200 Subject: [PATCH] control mode for objects (view space or object space) --- help/tc.controller_demo.maxhelp | 27 +++++++++++++---- javascript/tc.controller.js | 52 ++++++++++++++++++++++++--------- 2 files changed, 60 insertions(+), 19 deletions(-) diff --git a/help/tc.controller_demo.maxhelp b/help/tc.controller_demo.maxhelp index 182b91f..f60dbf3 100644 --- a/help/tc.controller_demo.maxhelp +++ b/help/tc.controller_demo.maxhelp @@ -9,9 +9,20 @@ "modernui": 1 }, "classnamespace": "box", - "rect": [ 334.0, 100.0, 1444.0, 848.0 ], + "rect": [ 131.0, 100.0, 1444.0, 848.0 ], "integercoordinates": 1, "boxes": [ + { + "box": { + "id": "obj-9", + "maxclass": "message", + "numinlets": 2, + "numoutlets": 1, + "outlettype": [ "" ], + "patching_rect": [ 361.0, 405.0, 75.0, 22.0 ], + "text": "camera cam" + } + }, { "box": { "id": "obj-21", @@ -1855,7 +1866,7 @@ { "box": { "id": "obj-56", - "items": "", + "items": [ "jit.gl.gridshape", "torus", ",", "jit.gl.plato", "plato", ",", "jit.gl.gridshape", "plane", ",", "jit.gl.light", "u034001065", ",", "jit.gl.model", "model" ], "maxclass": "umenu", "numinlets": 1, "numoutlets": 3, @@ -2461,8 +2472,8 @@ "maxclass": "comment", "numinlets": 1, "numoutlets": 0, - "patching_rect": [ 444.0, 987.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" + "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 create an anim.node for it)\n- controlling a skybox doesn't work although it's technically possible and works when done manually" } }, { @@ -2497,7 +2508,7 @@ "numoutlets": 1, "outlettype": [ "" ], "patching_rect": [ 53.0, 647.0, 222.0, 22.0 ], - "text": "control torus u787003285" + "text": "control u998010725 u998010725" } }, { @@ -2757,6 +2768,12 @@ "destination": [ "obj-109", 0 ], "source": [ "obj-7", 0 ] } + }, + { + "patchline": { + "destination": [ "obj-109", 0 ], + "source": [ "obj-9", 0 ] + } } ], "autosave": 0 diff --git a/javascript/tc.controller.js b/javascript/tc.controller.js index cc90e47..c56141e 100644 --- a/javascript/tc.controller.js +++ b/javascript/tc.controller.js @@ -55,6 +55,15 @@ declareattribute({ 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; declareattribute({ name: "show_bounds", @@ -96,7 +105,7 @@ class Controller { this.ease = 0.1; // anim.drive easing this.flymode = false; 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.world_up = [0, 1, 0]; this.camera_target = { @@ -139,7 +148,7 @@ class Controller { } // Taking control over a new target - control(obj_name, ctrl_mode) { + control(obj_name, obj_type) { if (!this.camera_target.name) { error("No camera defined. Cannot control object.\n"); return false; @@ -154,7 +163,7 @@ class Controller { new_target != undefined && (this.target.name != "" || this.target.name != new_target) ) { - this.control_mode = ctrl_mode; + this.control_obj_type = obj_type; if (this.target.name != "") { const transform = this.target.send("getworldtransform"); // Get the current targets tranfsorm, @@ -176,23 +185,28 @@ class Controller { this.target.send("anim", this.pitch_node.name); // 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.turnmode = "local"; this.pitch_node.turnmode = "parent"; - this.has_rotated = false; this.set_flymode(this.flymode); } else { this.flymode_applyed = false; // When controlling an object, we convert the translation vector // from screen space to world space in this.move // So we need the cam base matrix - this.main_node.movemode = "world"; - this.main_node.turnmode = "world"; - this.pitch_node.turnmode = "world"; - this.get_cam_base_matrix(); + if (controlmode == 0) { + this.main_node.movemode = "world"; + this.main_node.turnmode = "world"; + 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 @@ -286,7 +300,7 @@ class Controller { move(x, y, z) { let translat_vec = [x, y, z]; - if (this.control_mode == "obj") { + if (this.control_obj_type == "obj" && controlmode == 0) { let cam_x_rot = this.camera_node.send("getrotatexyz")[0]; // Inverse translation direction if cam is upside down if (cam_x_rot < -90 || cam_x_rot > 90) { @@ -300,11 +314,11 @@ class Controller { turn(x, y, z) { let rot_vec = [x, y, z]; - if (this.control_mode == "obj") { + if (this.control_obj_type == "obj") { rot_vec = multiplyMatrixVector(this.camera_base_matrix, [-x, 0, 0]); 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 // so that the cam can move toward the direction it is pointing to this.main_drive.turn(rot_vec[0], rot_vec[1], 0); @@ -331,7 +345,7 @@ class Controller { } apply_flymode() { - if (this.control_mode == "cam") { + if (this.control_obj_type == "cam") { if (this.flymode) { // When enabling flymode, we move all rotations to main_node this.main_node.rotatexyz = [ @@ -370,7 +384,7 @@ class Controller { resync() { if (this.target.name != "") { - this.control(this.target.name, this.control_mode); + this.control(this.target.name, this.control_obj_type); } } @@ -477,17 +491,27 @@ function set_ease(v) { ctlr.set_ease(v); ease = v; } +set_ease.local = 1; function set_flymode(v) { flymode = v == undefined ? !flymode : v == 1; // If flymode with no argument is provided, act as a toggle ctlr.set_flymode(flymode); } +set_flymode.local = 1; function set_show_bounds(v) { show_bounds = v == true; // ctlr.show_bounds = show_bounds; do_show_bounds(show_bounds); } +set_show_bounds.local = 1; + +function set_controlmode(v) { + const new_val = v > 0 ? 1 : 0; + if (controlmode === new_val) return; + controlmode = new_val; + ctlr.control(ctlr.target.name, ctlr.control_obj_type); +} function do_show_bounds(v) { const objects_without_bounds = [