Animating Dome Rotation in DAZ Studio – is it possible?

Hello!
I need to animate the Dome Rotation in Render Settings (Environment) to create an animation where the HDRI rotates over time. My goal is to have the Dome Rotation value change frame by frame on the Timeline.

I tried the ERC Freeze method:

  • created a Null,

  • added a custom property (for example DomeRotationCtrl),

  • then attempted to link it to Dome Rotation in Environment Options using ERC Freeze.

Unfortunately, this approach did not work for me — I could not get Dome Rotation to animate through such a controller.

Is there another method, plugin, or script that allows animating Dome Rotation (and possibly other Environment Options)? Any advice would be greatly appreciated.

Comments

  • I would also like to add that I tried a workaround by rotating the entire scene. This approach turned out to be very inconvenient: the rotation center does not match the point where it needs to be, so the result looks incorrect. In the end, this method creates more problems than it solves.

  • felisfelis Posts: 5,770

    You could create a null and place it where you want it to rotate, and then parent the scene to that.

  • felis said:

    You could create a null and place it where you want it to rotate, and then parent the scene to that.

    Thank you for your reply. However, I am still trying to find a solution that is not a workaround but actually rotates the dome itself. Perhaps this can be achieved with a script?

  • WendyLuvsCatzWendyLuvsCatz Posts: 40,085

    well, technically the camera is always fixed and everything animates around it however the dome is part of the render engine not the scene

    nonetheless one can animate environment domes in other software 

    I know none of the existing texture animation scripts work on it and people have said it isn't accessible in the API (I wouldn't know, just take their word for it) 

    hence I too group everything or parent to a niull, got to watch D|S though as it does weird things parenting too like scaling recursively

  • This would be a job for a post-load proxy creation, to link the rotation to an aniamtable node proeprty http://docs.daz3d.com/doku.php/public/software/dazstudio/4/referenceguide/scripting/api_reference/samples/start#post-load_data_items

  • MaX PupkinMaX Pupkin Posts: 49
    edited September 29

    Richard Haseltine said:

    This would be a job for a post-load proxy creation, to link the rotation to an aniamtable node proeprty http://docs.daz3d.com/doku.php/public/software/dazstudio/4/referenceguide/scripting/api_reference/samples/start#post-load_data_items

    Buddy, what you wrote sounds interesting, but it is not clear to me at all. Could you please show a more practical example or explain it in a simpler way? I am not such an advanced user, so I can’t fully grasp it right away. If it involves code, a sample would really help me understand how to use it. Thanks in advance for your efforts.

    Post edited by MaX Pupkin on
  • // DomeRotationProxy.dsa// Create a proxy property on a Null to control Dome Rotation// 1. Create a Null controller if it does not existvar domeCtrl = Scene.findNodeByLabel("DomeCtrl");if (!domeCtrl) {    domeCtrl = new DzNode();    domeCtrl.setLabel("DomeCtrl");    Scene.addNode(domeCtrl);}// 2. Add custom property if it does not existvar prop = domeCtrl.findProperty("DomeRotationProxy");if (!prop) {    prop = new DzFloatProperty("DomeRotationProxy");    prop.setLabel("Dome Rotation Proxy");    prop.setMin(0);    prop.setMax(360);    prop.setIsClamped(true);    prop.setValue(0);    domeCtrl.addProperty(prop);}// 3. Access Environment Options and find Dome Rotation propertyvar env = App.getRenderMgr().getRenderSettings();var domeRot = env.findProperty("Dome Rotation");if (!domeRot) {    MessageBox.warning("Not Found", "Dome Rotation property not found. Switch to Iray and enable Dome.");} else {    // 4. Link proxy property to Dome Rotation    domeRot.removeController();     domeRot.setController(prop);    MessageBox.information("OK", "Proxy linked: use DomeCtrl → Dome Rotation Proxy to animate.");}

    Did I understand you correctly? The code that was proposed creates a proxy property on a Null and links it to Dome Rotation, so the dome’s rotation can be animated in the Timeline. I would like to confirm if this matches your recommendation about post-load proxy creation.

  • MaX Pupkin said:

    // DomeRotationProxy.dsa// Create a proxy property on a Null to control Dome Rotation// 1. Create a Null controller if it does not existvar domeCtrl = Scene.findNodeByLabel("DomeCtrl");if (!domeCtrl) {    domeCtrl = new DzNode();    domeCtrl.setLabel("DomeCtrl");    Scene.addNode(domeCtrl);}// 2. Add custom property if it does not existvar prop = domeCtrl.findProperty("DomeRotationProxy");if (!prop) {    prop = new DzFloatProperty("DomeRotationProxy");    prop.setLabel("Dome Rotation Proxy");    prop.setMin(0);    prop.setMax(360);    prop.setIsClamped(true);    prop.setValue(0);    domeCtrl.addProperty(prop);}// 3. Access Environment Options and find Dome Rotation propertyvar env = App.getRenderMgr().getRenderSettings();var domeRot = env.findProperty("Dome Rotation");if (!domeRot) {    MessageBox.warning("Not Found", "Dome Rotation property not found. Switch to Iray and enable Dome.");} else {    // 4. Link proxy property to Dome Rotation    domeRot.removeController();     domeRot.setController(prop);    MessageBox.information("OK", "Proxy linked: use DomeCtrl → Dome Rotation Proxy to animate.");}

    Did I understand you correctly? The code that was proposed creates a proxy property on a Null and links it to Dome Rotation, so the dome’s rotation can be animated in the Timeline. I would like to confirm if this matches your recommendation about post-load proxy creation.

    That is right, the script runs in the background to create the links each time you load the scene.

  • MaX PupkinMaX Pupkin Posts: 49
    edited September 29
    // DAZ Studio 4.24 Script // Persistent Dome Rotation Synchronization (function() { var domeCtrl = Scene.findNodeByLabel("DomeCtrl"); if (!domeCtrl) { domeCtrl = new DzNode(); domeCtrl.setLabel("DomeCtrl"); Scene.addNode(domeCtrl); print("Created DomeCtrl node"); } else { print("Found existing DomeCtrl node"); } var prop = domeCtrl.findProperty("DomeRotationProxy"); if (!prop) { prop = new DzFloatProperty("DomeRotationProxy", 0, 360, 0); prop.setLabel("Dome Rotation Proxy"); prop.setCanAnimate(true); prop.setHidden(false); prop.setIsClamped(true); domeCtrl.addProperty(prop); print("Created Dome Rotation Proxy property"); } else { print("Found existing Dome Rotation Proxy property"); } var envNode = Scene.findNode("Environment Options"); if (!envNode) { print("ERROR: Environment Options node not found"); return; } var domeRot = envNode.findProperty("Dome Rotation"); if (!domeRot) { print("ERROR: Dome Rotation property not found"); return; } print("Starting persistent synchronization service"); print("Proxy value: " + prop.getValue() + ", Dome value: " + domeRot.getValue()); var isSyncing = false; var lastSyncTime = 0; var lastFrame = -1; function syncAnimationKeys() { if (isSyncing) return; isSyncing = true; try { if (prop.hasKeys()) { var numKeys = prop.getNumKeys(); print("Syncing " + numKeys + " animation keys"); if (domeRot.hasKeys()) { var domeRange = domeRot.getKeyRange(); if (domeRange) { domeRot.deleteKeys(domeRange); print("Cleared existing Dome Rotation keys"); } } for (var i = 0; i < numKeys; i++) { try { var keyTime = prop.getKeyTime(i); var keyValue = prop.getValue(keyTime); var validatedValue = Math.max(0, Math.min(360, keyValue)); domeRot.setValue(validatedValue, keyTime); print("Key at frame " + keyTime + ": " + validatedValue + " degrees"); } catch(e) { print("Error copying key " + i + ": " + e.toString()); } } print("Animation keys sync completed"); } else { print("No animation keys found in proxy"); } } catch(e) { print("Error in syncAnimationKeys: " + e.toString()); } isSyncing = false; lastSyncTime = new Date().getTime(); } function syncCurrentValue() { if (isSyncing) return; var currentValue = prop.getValue(); var validatedValue = Math.max(0, Math.min(360, currentValue)); var currentDomeValue = domeRot.getValue(); print("Sync check - Proxy: " + currentValue + ", Dome: " + currentDomeValue); if (Math.abs(currentDomeValue - validatedValue) > 0.001) { domeRot.setValue(validatedValue); print("VALUE SYNCED: " + currentDomeValue + " → " + validatedValue); } } function syncFrameValue(frame) { if (isSyncing) return; try { var frameValue = prop.getValue(frame); var validatedValue = Math.max(0, Math.min(360, frameValue)); var currentDomeValue = domeRot.getValue(frame); if (Math.abs(currentDomeValue - validatedValue) > 0.001) { domeRot.setValue(validatedValue, frame); print("FRAME SYNC: Frame " + frame + " - " + currentDomeValue + " → " + validatedValue); } if (frame !== lastFrame) { print("FRAME CHANGE: " + lastFrame + " → " + frame + " | Value: " + validatedValue + " degrees"); lastFrame = frame; } } catch(e) { print("Error syncing frame " + frame + ": " + e.toString()); } } // Main value change handler prop.valueChanged.connect(function() { print("Proxy value changed: " + prop.getValue()); syncCurrentValue(); }); // Frame change detection and synchronization var frameTimer = new QTimer(); frameTimer.timeout.connect(function() { // Try to get current frame var currentFrame = 0; // Method 1: Try to get from scene if (typeof Scene.getCurrentTime === 'function') { currentFrame = Scene.getCurrentTime(); } // Sync value for current frame syncFrameValue(currentFrame); // Periodic key synchronization every 3 seconds var currentTime = new Date().getTime(); if (currentTime - lastSyncTime > 3000) { if (prop.hasKeys()) { syncAnimationKeys(); } } }); // Start the timer - THIS IS CRITICAL frameTimer.start(100); // Initial synchronization syncAnimationKeys(); syncCurrentValue(); print("Frame synchronization service RUNNING"); print("Timer started - service should work continuously"); // Store reference to keep service alive this.domeSyncService = { timer: frameTimer, stop: function() { frameTimer.stop(); print("Sync service stopped"); } }; })();

    The script currently works, produces no errors, and successfully updates the Dome Rotation value through the timeline. However, there is one issue: it only functions once after being launched. This approach is not suitable for rendering. Is there a way to run the script once and have it continue working in the background? Or is that fundamentally impossible in DAZ? A manual launch is not a viable option for rendering.

    Post edited by MaX Pupkin on
  • # Problem: Script executes only once, no continuous synchronization in DAZ Studio 4.24

    Problem Description

    I'm creating a proxy property on a Null node to control Dome Rotation via script. The script correctly creates properties and sets initial values, but doesn't work in the background - synchronization only occurs when the script is launched, but not when changing frames on the timeline.

    What I've Tried

    1. Basic approach with valueChanged

    prop.valueChanged.connect(function() {    domeRot.setValue(prop.getValue());});

    Result: Only works when manually changing values in Properties panel, but not when scrubbing through timeline.

    2. Timer for continuous synchronization

    var syncTimer = new QTimer();syncTimer.timeout.connect(syncFunction);syncTimer.start(100);

    Result: Timer starts, but script terminates and timer stops.

    3. Binding timer to scene node

    domeCtrl.syncTimer = new QTimer();domeCtrl.syncTimer.start(100);

    Result: Timer persists on the node, but still doesn't work after script completion.

    4. Using QDialog to keep script alive

    var syncDialog = new DzDialog();syncDialog.exec();

    Result: Dialog shows service is running, but synchronization doesn't work.

    5. Connecting to scene events

    Scene.timeChanged.connect(syncFunction);Scene.timeChanging.connect(syncFunction);

    Result: Events don't trigger or script doesn't receive them after completion.

    6. Object-oriented approach with reference storage

    this.globalSyncService = service;var activeSyncService = service;

    Result: References are stored, but script still doesn't work in background.

    Key Issues

    • Script executes and terminates, doesn't remain in memory
    • All event handlers stop working after script completion
    • Timers stop even when bound to scene objects
    • No errors in console - script just "goes to sleep"

    Questions for the Community

    1. Is there a mechanism in DAZ Studio for persistent background scripts?
    2. How can I make a script run continuously, not just on launch?
    3. Is there another approach for synchronizing properties across frames?
    4. Has anyone encountered this problem and found a solution?

    Any ideas or alternative approaches would be greatly appreciated!

  • FrankTheTankFrankTheTank Posts: 1,481

    You could turn the HDR into an actual physical skydome in your scene. Then you can rotate it easily. Just set the base color and emission color of your skydome to your exr/hdr, adjust the luminance as desired.  I noticed this kind of bogs down my GPU when I tried this way though (I only have 6GB VRAM), so your mileage may vary depending on GPU size and size of the HDR.

    Screenshot 2025-09-29 151059.jpg
    1503 x 929 - 125K
  • What  a re you trying to do with repeating callbacks? The object is to have the script run once at load, create the null and its property (don't forget to name them, name matters more than label) and make the property a controller of the dome rotation property. Once that is done, for the session, you can  animate changes to the node proeprty as you can any other and the dome rotation will follow without any script action required. http://docs.daz3d.com/doku.php/public/software/dazstudio/4/referenceguide/scripting/api_reference/samples/start#post-load_callbacks is the source from which IBL Master grew in this respect.

  • MaX PupkinMaX Pupkin Posts: 49
    edited September 30

    Thank you for your previous answer. I'm trying to follow your advice to create a property controller that links my custom "Dome Rotation Proxy" property on a null node to the "Dome Rotation" property of the "Environment Options" node. However, when attempting to use DzProxyPropertyController, I get a ReferenceError: Can't find variable: DzProxyPropertyController.

    After reviewing the official script samples in the Documentation Center, I couldn't find any use of DzProxyPropertyController. Instead, the examples often use other methods, such as DzExpression or creating a DzCallBack to link properties.

    Could you please clarify which specific controller class should be used to create a permanent link between the two properties for animation without recurring script calls? There might be a recommended approach I am missing.

    Thank you for your time and assistance.

     

     

    // DAZ Studio 4.24 Script// Create Dome Rotation Proxy and Controller(function() {    // 1. Find or create the control node    var domeCtrl = Scene.findNodeByLabel("DomeCtrl");    if (!domeCtrl) {        domeCtrl = new DzNode();        domeCtrl.setLabel("DomeCtrl");        domeCtrl.setName("DomeRotationController");        Scene.addNode(domeCtrl);        print("Created DomeCtrl node");    }    // 2. Find or create the proxy property    var prop = domeCtrl.findProperty("DomeRotationProxy");    if (!prop) {        prop = new DzFloatProperty("DomeRotationProxy", 0, 360, 0);        prop.setLabel("Dome Rotation Proxy");        prop.setCanAnimate(true);        prop.setHidden(false);        prop.setIsClamped(true);        domeCtrl.addProperty(prop);        print("Created Dome Rotation Proxy property");    }    // 3. Find the target property (Dome Rotation)    var envNode = Scene.findNode("Environment Options");    if (!envNode) {        print("ERROR: Environment Options node not found");        return;    }        var domeRot = envNode.findProperty("Dome Rotation");    if (!domeRot) {        print("ERROR: Dome Rotation property not found");        return;    }    // 4. Create and set the controller через Expression    if (domeRot.getNumControllers() === 0) {        try {            var expr = new DzExpression();            expr.setExpression("source"); // Простое выражение для прямой связи            expr.addInput(prop, "source"); // Связываем с нашим свойством                        domeRot.setController(expr);            print("SUCCESS: Expression controller created and linked!");            print("Now you can animate 'Dome Rotation Proxy' and Dome Rotation will follow automatically");        } catch(e) {            print("Error creating expression controller: " + e.toString());        }    } else {        print("Controller already exists for Dome Rotation");    }})();

     

    Post edited by MaX Pupkin on
  • http://docs.daz3d.com/doku.php/public/software/dazstudio/4/referenceguide/scripting/api_reference/object_index/erclink_dz you need to create an ERCLink on the rotation control that makes the actual dome rotation conrol (what you are  adding it to) subject to the new property on your control node - using DeltaAdd  or Add I would think (the former allows a scalar, multiplier, value to be included so that you can have a different range if desired on your controller). Once the node and controller exist the script then fires on loading the item to reinstate that link - once the link exists in the currently open scene it has no  further role to play.

  • MaX PupkinMaX Pupkin Posts: 49
    edited September 30
    // DAZ Studio 4.24 Script// Create Dome Rotation Proxy with ERC Link - CORRECTED VERSION(function() {    // 1. Find or create the control node    var domeCtrl = Scene.findNodeByLabel("DomeCtrl");    if (!domeCtrl) {        domeCtrl = new DzNode();        domeCtrl.setLabel("DomeCtrl");        domeCtrl.setName("DomeRotationController");        Scene.addNode(domeCtrl);        print("Created DomeCtrl node");    } else {        print("Found existing DomeCtrl node");    }    // 2. Find or create the proxy property    var prop = domeCtrl.findProperty("DomeRotationProxy");    if (!prop) {        prop = new DzFloatProperty("DomeRotationProxy", 0, 360, 0);        prop.setLabel("Dome Rotation Proxy");        prop.setCanAnimate(true);        prop.setHidden(false);        prop.setIsClamped(true);        domeCtrl.addProperty(prop);        print("Created Dome Rotation Proxy property");    } else {        print("Found existing Dome Rotation Proxy property");    }    // 3. Find the target property (Dome Rotation)    var envNode = Scene.findNode("Environment Options");    if (!envNode) {        print("ERROR: Environment Options node not found");        return;    }    var domeRot = envNode.findProperty("Dome Rotation");    if (!domeRot) {        print("ERROR: Dome Rotation property not found");        return;    }    print("Found Dome Rotation property");    // 4. Create ERC Link using the correct API    try {        // Check if a controller already exists        if (domeRot.getNumControllers() === 0) {            // Create ERC Link            var ercLink = new DzERCLink(prop, 1.0, 0.0);            ercLink.setProperty(prop); // Set master property            // Add ERC Link to the target property            domeRot.insertController(ercLink);            print("SUCCESS: ERC Link created successfully!");            print("Dome Rotation is now controlled by Dome Rotation Proxy");            print("You can now animate 'Dome Rotation Proxy' and Dome Rotation will follow automatically");            // Test the connection            var testValue = prop.getValue();            var domeValue = domeRot.getValue();            print("Connection test - Proxy: " + testValue + ", Dome: " + domeValue);        } else {            print("A controller already exists for Dome Rotation");        }    } catch(e) {        print("Error creating ERC Link: " + e.toString());        print("This might be an API issue in your Daz Studio version");    }})();

    Log:

    Executing Script...
    Found existing DomeCtrl node
    Found existing Dome Rotation Proxy property
    Found Dome Rotation property
    SUCCESS: ERC Link created successfully!
    Dome Rotation is now controlled by Dome Rotation Proxy
    You can now animate 'Dome Rotation Proxy' and Dome Rotation will follow automatically
    Connection test - Proxy: 0, Dome: 0
    Result:
    Script executed in 0 secs 113 msecs.

    Based on the code and the log, does the solution you suggested and the code I wrote look correct? I just want to double-check with you as a developer if everything is done properly, because honestly I’m a bit confused and can’t tell what’s right and what isn’t. Just let me know if it’s correct or if I messed something up again.

    Post edited by MaX Pupkin on
  • Your previous sample looked promising (I didn't go through it in detail) - just in the final sectrion, where you have lots of ?s, you need to use the ERC Link constructor to make an ERC_Add link to your dome rotation control property and then set that as a controller of the actual proerpty (which ytou already have) on the Environment Settings node (using http://docs.daz3d.com/doku.php/public/software/dazstudio/4/referenceguide/scripting/api_reference/object_index/numericproperty_dz#a_1a7a4e7c2103a43b2a14afd3cdb6a8113c if I am recalling correctly, it's been a while since I did this)

  • MaX PupkinMaX Pupkin Posts: 49
    edited September 30
    // DAZ Studio 4.24 Script// Create Dome Rotation Proxy with ERC_Add Link and Strict Clamping(function() {    // 1. Find or create the control node    var domeCtrl = Scene.findNodeByLabel("Dome Control");    if (!domeCtrl) {        domeCtrl = new DzNode();        domeCtrl.setLabel("Dome Control");        domeCtrl.setName("DomeRotationController");        Scene.addNode(domeCtrl);        print("Created Dome Control node");    } else {        print("Found existing Dome Control node");    }    // 2. Find or create the proxy property    var prop = domeCtrl.findProperty("DomeRotationProxy");    if (!prop) {        prop = new DzFloatProperty("DomeRotationProxy", 0, 360, 0);        prop.setLabel("Dome Rotation Proxy");        prop.setCanAnimate(true);        prop.setHidden(false);        prop.setIsClamped(true);        prop.setMin(0);        prop.setMax(360);        domeCtrl.addProperty(prop);        print("Created Dome Rotation Proxy property");    } else {        prop.setIsClamped(true);        prop.setMin(0);        prop.setMax(360);        print("Found existing Dome Rotation Proxy property and applied clamping");    }    // 3. Find the target property (Dome Rotation)    var envNode = Scene.findNode("Environment Options");    if (!envNode) {        print("ERROR: Environment Options node not found");        return;    }    var domeRot = envNode.findProperty("Dome Rotation");    if (!domeRot) {        print("ERROR: Dome Rotation property not found");        return;    }    print("Found Dome Rotation property");    // 4. Sync value from Dome Rotation to Proxy with clamping    var domeValue = domeRot.getValue();    domeValue = Math.max(0, Math.min(360, domeValue));    prop.setValue(domeValue);    print("Set DomeRotationProxy value to: " + domeValue);    // 5. Create ERC_Add Link and apply as controller    try {        if (domeRot.getNumControllers() === 0) {            var ercLink = new DzERCLink(prop, 1.0, 0.0);            ercLink.setProperty(prop);            domeRot.insertController(ercLink);            print("SUCCESS: ERC_Add Link created successfully!");            print("Dome Rotation is now controlled by Dome Rotation Proxy");        } else {            print("A controller already exists for Dome Rotation");        }        var proxyValue = prop.getValue();        var finalDomeValue = domeRot.getValue();        print("Connection test - Proxy: " + proxyValue + ", Dome: " + finalDomeValue);    } catch(e) {        print("Error creating ERC Link: " + e.toString());        print("This might be an API issue in your Daz Studio version");    }})();

     

    Now the code is written correctly, right?

    Post edited by MaX Pupkin on
  • work thx a LOT!

  • MaX PupkinMaX Pupkin Posts: 49
    // DAZ Studio version 4.24.0.3 filetype DAZ Script// DAZ Studio 4.24 Script// Create ERC_Add proxies for Dome Rotation, Environment Intensity, Dome Orientation X/Y/Z// With correct clamping and value sync from Dome(function() {    // 1. Find or create the control node    var domeCtrl = Scene.findNodeByLabel("Dome Control");    if (!domeCtrl) {        domeCtrl = new DzNode();        domeCtrl.setLabel("Dome Control");        domeCtrl.setName("DomeRotationController");        Scene.addNode(domeCtrl);        print("Created Dome Control node");    } else {        print("Found existing Dome Control node");    }    // 2. Define proxy properties and target names    var proxyDefs = [        { name: "DomeRotationProxy", label: "Dome Rotation Proxy", min: 0, max: 360, target: "Dome Rotation" },        { name: "EnvIntensityProxy", label: "Environment Intensity Proxy", min: -1000, max: 1000, target: "Environment Intensity" },        { name: "DomeOrientXProxy", label: "Dome Orientation X Proxy", min: -1000, max: 1000, target: "Dome Orientation X" },        { name: "DomeOrientYProxy", label: "Dome Orientation Y Proxy", min: -1000, max: 1000, target: "Dome Orientation Y" },        { name: "DomeOrientZProxy", label: "Dome Orientation Z Proxy", min: -1000, max: 1000, target: "Dome Orientation Z" }    ];    // 3. Find Environment Options node    var envNode = Scene.findNode("Environment Options");    if (!envNode) {        print("ERROR: Environment Options node not found");        return;    }    // 4. Process each proxy definition    for (var i = 0; i &lt; proxyDefs.length; i++) {        var def = proxyDefs[i];        // Find target property        var targetProp = envNode.findProperty(def.target);        if (!targetProp) {            print("ERROR: Target property not found: " + def.target);            continue;        }        print("Found target property: " + def.target);        // Temporarily remove controllers to read clean value        var originalControllers = [];        var numControllers = targetProp.getNumControllers();        for (var c = 0; c &lt; numControllers; c++) {            originalControllers.push(targetProp.getController(c));        }        for (var c = numControllers - 1; c &gt;= 0; c--) {            targetProp.removeController(c);        }        var actualValue = targetProp.getValue();        for (var c = 0; c &lt; originalControllers.length; c++) {            targetProp.insertController(originalControllers[c]);        }        actualValue = Math.max(def.min, Math.min(def.max, actualValue));        // Create or find proxy property        var prop = domeCtrl.findProperty(def.name);        if (!prop) {            prop = new DzFloatProperty(def.name, def.min, def.max, actualValue);            prop.setLabel(def.label);            prop.setCanAnimate(true);            prop.setHidden(false);            prop.setIsClamped(true);            prop.setMin(def.min);            prop.setMax(def.max);            domeCtrl.addProperty(prop);            print("Created proxy property: " + def.label + " with value: " + actualValue);        } else {            prop.setIsClamped(true);            prop.setMin(def.min);            prop.setMax(def.max);            prop.setValue(actualValue);            print("Found existing proxy property and updated value: " + def.label + " = " + actualValue);        }        // Create ERC_Add link if not already present        try {            if (targetProp.getNumControllers() === 0) {                var ercLink = new DzERCLink(prop, 1.0, 0.0);                ercLink.setProperty(prop);                targetProp.insertController(ercLink);                print("SUCCESS: ERC_Add link created for " + def.target);            } else {                print("Controller already exists for " + def.target);            }            var proxyVal = prop.getValue();            var targetVal = targetProp.getValue();            print("Connection test - Proxy: " + proxyVal + ", Target: " + targetVal);        } catch(e) {            print("Error creating ERC link for " + def.target + ": " + e.toString());        }    }})();

     

    I followed your instructions, and the interesting part is that the script almost works. Keys are being set, and the required parameters change synchronously. Yes, there are some minor discrepancies with the limits, but those are not critical. The main issue appears after reloading the scene: the script stops working and no longer synchronizes the keys. Is there a way to fix this? Could you test it on your side and point out what I might be doing wrong so I can correct it?

  • Richard HaseltineRichard Haseltine Posts: 108,072

    That is what the Ppost Load script is for, are you setting that part up? Or is it that the script isn't working when it is run that way?

  • MaX PupkinMaX Pupkin Posts: 49

    Please check both scripts. One handles the loading process, and the other, the main one, performs all actions. It is possible that I missed something or did something wrong, although no errors appear. I tested it with ChatGPT, which says everything looks correct, but I understand that ChatGPT is just a tool without emotions, and the best solution can, of course, come from a human.

    The main script that is used to run in DAZ Studio.

    (function() {    // === 1. Find or create the control node ===    var domeCtrl = Scene.findNodeByLabel("Dome Control");    if (!domeCtrl) {        domeCtrl = new DzNode();        domeCtrl.setLabel("Dome Control");        domeCtrl.setName("DomeRotationController");        Scene.addNode(domeCtrl);        print("Created Dome Control node");    } else {        print("Found existing Dome Control node");    }    // === 2. Proxy definitions ===    var proxyDefs = [        { name: "DomeRotationProxy", label: "Dome Rotation Proxy", min: 0, max: 360, target: "Dome Rotation" },        { name: "EnvIntensityProxy", label: "Environment Intensity Proxy", min: -1000, max: 1000, target: "Environment Intensity" },        { name: "DomeOrientXProxy", label: "Dome Orientation X Proxy", min: -1000, max: 1000, target: "Dome Orientation X" },        { name: "DomeOrientYProxy", label: "Dome Orientation Y Proxy", min: -1000, max: 1000, target: "Dome Orientation Y" },        { name: "DomeOrientZProxy", label: "Dome Orientation Z Proxy", min: -1000, max: 1000, target: "Dome Orientation Z" }    ];    // === 3. Find Environment Options node ===    var envNode = Scene.findNode("Environment Options");    if (!envNode) {        print("ERROR: Environment Options node not found");        return;    }    // === 4. Create proxies and ERC links with safety check ===    for (var i = 0; i &lt; proxyDefs.length; i++) {        var def = proxyDefs[i];        var targetProp = envNode.findProperty(def.target);        if (!targetProp) {            print("ERROR: Target property not found: " + def.target);            continue;        }        print("Found target property: " + def.target);        // Get current value without removing controllers        var actualValue = targetProp.getValue();        actualValue = Math.max(def.min, Math.min(def.max, actualValue));        var prop = domeCtrl.findProperty(def.name);        if (!prop) {            prop = new DzFloatProperty(def.name, def.min, def.max, actualValue);            prop.setLabel(def.label);            prop.setCanAnimate(true);            prop.setHidden(false);            prop.setIsClamped(true);            domeCtrl.addProperty(prop);            print("Created proxy property: " + def.label + " with value: " + actualValue);        } else {            prop.setIsClamped(true);            prop.setMin(def.min);            prop.setMax(def.max);            prop.setValue(actualValue);            print("Found existing proxy property and updated value: " + def.label + " = " + actualValue);        }        // Create ERC_Add link with improved safety check        try {            if (targetProp.getNumControllers() === 0) {                var ercLink = new DzERCLink(prop, 1.0, 0.0);                ercLink.setProperty(prop);                targetProp.insertController(ercLink);                print("SUCCESS: ERC_Add link created for " + def.target);            } else {                print("Controller already exists for " + def.target);            }            var proxyVal = prop.getValue();            var targetVal = targetProp.getValue();            print("Connection test - Proxy: " + proxyVal + ", Target: " + targetVal);        } catch(e) {            print("Error creating ERC link for " + def.target + ": " + e.toString());        }    }// === 5. Post-Load Script and MessageBox ===var dataItemName = "DomeRotationProxy_Loader";var scriptRelPath = "data/DomeRotationProxy/DomeRotationProxy_Loader.dsa";var existingDataItem = domeCtrl.findDataItem(dataItemName);var message = "";try {    var contentMgr = App.getContentMgr();    var filePath = contentMgr.findFile(scriptRelPath, DzContentMgr.AllDirs);    if (filePath &amp;&amp; filePath !== "") {        var fileCheck = new DzFile(filePath);        if (fileCheck.exists()) {            message = "File found: DomeRotationProxy_Loader.dsa\n" +                      "File path: " + filePath + "\n" +                      "Data Item attached: " + (existingDataItem ? "YES" : "NO");        } else {            message = "File not found: DomeRotationProxy_Loader.dsa\n" +                      "Expected path: " + filePath;        }    } else {        message = "File not found: DomeRotationProxy_Loader.dsa\n" +                  "Expected path: data/DomeRotationProxy/DomeRotationProxy_Loader.dsa in your Daz Studio content directory";    }} catch(e) {    message = "Error checking file: " + e.toString();}// Correct MessageBox call with all info in titleMessageBox.information(message, "", "OK");print("Script execution completed");})();

    The script starts running only after the scene has been reloaded.

    // Save as: DomeRotationProxy_Loader.dsa(function() {    // This script runs automatically when scene loads    if (typeof DataItem !== "undefined") {        var element = DataItem.getOwner();        print("Running post-load script for: " + element.getName());    }    // Recreate ERC links on scene load    var domeCtrl = Scene.findNodeByLabel("Dome Control");    if (!domeCtrl) return;        var envNode = Scene.findNode("Environment Options");    if (!envNode) return;        // Your existing proxy setup logic here    print("Dome Rotation Proxy system auto-initialized");})();

     

  • jbowlerjbowler Posts: 841

    MaX Pupkin said:

    I would also like to add that I tried a workaround by rotating the entire scene. This approach turned out to be very inconvenient: the rotation center does not match the point where it needs to be, so the result looks incorrect. In the end, this method creates more problems than it solves.

    Make the entire scene **including the camera** into a group (select everything, create group...)  Rotate that group.

    If you are using a close-up HDRI then that won't work; you need to create a second group of the first and move that to the center of the HDRI and invert the move on the original group.  Be aware that if you try to rotate an HDRI with close-up objects around a scene your chance of getting it to work is minimal; the HDRI has to be rotated about the **camera** not the scene otherwise the perspective will be messed up.

    This is a 3D issue; an HDRI is taken with a camera at the center of the captured scene, when the HDRI is used the camera used to view the scene it is illuminating has to be at the same center or everything goes screwy (out of perspective).  For "infinite dome" HDRIs it doesn't matter because anything/∞ = 0.  For "finite dome" HDRIs it really does mattter; dome-raduis/camera-displacement must be close to 0.

    The base problem is that "dome rotation" is a property of the "Environment" which cannot be animated.  If you want to animate it in a script you need to find a way to trigger that script on every single frame change.  The same issue applies to things in "Environment" which certainly should be animable; EV is an excellent example (it should be an animable Camera property.)

  • jbowler said:

    MaX Pupkin said:

    I would also like to add that I tried a workaround by rotating the entire scene. This approach turned out to be very inconvenient: the rotation center does not match the point where it needs to be, so the result looks incorrect. In the end, this method creates more problems than it solves.

    Make the entire scene **including the camera** into a group (select everything, create group...)  Rotate that group.

    If you are using a close-up HDRI then that won't work; you need to create a second group of the first and move that to the center of the HDRI and invert the move on the original group.  Be aware that if you try to rotate an HDRI with close-up objects around a scene your chance of getting it to work is minimal; the HDRI has to be rotated about the **camera** not the scene otherwise the perspective will be messed up.

    This is a 3D issue; an HDRI is taken with a camera at the center of the captured scene, when the HDRI is used the camera used to view the scene it is illuminating has to be at the same center or everything goes screwy (out of perspective).  For "infinite dome" HDRIs it doesn't matter because anything/∞ = 0.  For "finite dome" HDRIs it really does mattter; dome-raduis/camera-displacement must be close to 0.

    The base problem is that "dome rotation" is a property of the "Environment" which cannot be animated.  If you want to animate it in a script you need to find a way to trigger that script on every single frame change.  The same issue applies to things in "Environment" which certainly should be animable; EV is an excellent example (it should be an animable Camera property.)

    Why should I use a sphere, spend time setting it up, adjusting lighting, and resizing it, when user Richard Haseltine mentioned that the documentation already describes a solution that allows the Dome to rotate when a scene is loaded? What I’m currently writing isn’t fully working yet, but it already retrieves and sets the Dome coordinates. I understand that everyone is looking for a simple solution, and if I manage to finish this script, users will be able to use it effortlessly- no need to create spheres, groups, or make manual adjustments. It will be a truly useful solution that will make life easier for many (and spare some from unnecessary suffering)).

  • Richard Haseltine said:

    That is what the Ppost Load script is for, are you setting that part up? Or is it that the script isn't working when it is run that way?

    I followed the documentation and the script mostly works, but for some reason, it doesn’t run when the scene is loaded. Could you please advise what I might have missed or done wrong?

Sign In or Register to comment.