Change object order in Scene View

svArtistsvArtist Posts: 19
edited July 2022 in Freebies

As outlined in https://www.daz3d.com/forums/discussion/140571/order-objects-in-scene-tab-drag-drop, I would love to have a way to change the order of items in your scene view/structure tab.

I'm a visually oriented guy, and I like to have things that are close in meaning visually close as well.

So I bit the bullet and looked at how scripts in DAZ work.
Surprisingly, it's a rather easy JS based simply API system that doesn't require a ton of getting used to. (Except for the GUI part - they're quite ugly to set up IMO (Although I haven't spent much time with it yet)) - and I wrote a script that lets you move nodes around, AS LONG AS THEY HAVE PARENTS.
Because the API exposes methods to insert nodes into a specific index of other nodes, I can move items around when there is a parent to "move" the item into.

When you've selected an item that does not have a parent, you should be presented with the option to move all scene contents in a group which I simply call "Scene Root".

- Usage should be pretty easy: Move the selected item up and down past its siblings under a parent by clicking "up" and "down"
- Move the selected item out of its parent node by clicking "Unparent"
- Make the upper sibling the parent of the currently selected node (i.e. move selected node into the one above it)

And navigate the selection via the corresponding buttons below.

Video:

// V0.1var selSize = 0;var hasParent = false;var numChildren = 0;var isFirst = false;var isLast = false;var fos = Scene.getSelectedNode(0);var los = Scene.getSelectedNode(selSize-1);var par = fos.getNodeParent();var startSelect = null;var statAbsPos;var statAbsRot;var statAbsScale;function moveInView(a){	var selnums = Scene.getNumSelectedNodes();	for(var i = 0; i<selnums; i++){		var selnode = Scene.getSelectedNode(i);		if(selnode.getNodeParent()){			var parnode = selnode.getNodeParent();			var cind = parnode.findChildIndex(selnode);			// msg(selnode.getLabel() + " = " + cind + " + " + a + " /" + parnode.getNumNodeChildren());			if(cind + a>=0 && cind + a < parnode.getNumNodeChildren()){				parnode.moveNodeChildToIndex(selnode, cind + a, true);			}		}		else{			if(ask("No parent node available.\nShould all scene Nodes be moved into a Root Node (Group)?", "No Parent", "Cancel", "OK")){				rootAll();				moveInView(a);			}					}	}	updateButtons();}function unparent(){	var selnums = Scene.getNumSelectedNodes();	for(var i = 0; i<selnums; i++){		var selnode = Scene.getSelectedNode(i);		var parentnode = selnode.getNodeParent();		if(parentnode){			if(selnode.getNodeParent().getNodeParent()){				saveTrans(selnode);				selnode.getNodeParent().getNodeParent().addNodeChild(selnode);				restoreTrans(selnode);			}			else{				parentnode.removeNodeChild(selnode, true);			}		}	}	updateButtons();}function doparent(){	var selnums = Scene.getNumSelectedNodes();	for(var i = 0; i<selnums; i++){		var selnode = Scene.getSelectedNode(i);		var parentnode = selnode.getNodeParent();		if(parentnode){			var selfid = parentnode.findChildIndex(selnode);			var newpar = parentnode.getNodeChild(selfid-1);			newpar.addNodeChild(selnode, true);		}	}	updateButtons();}function selectSibling(i){	getStates();	if(selSize<=0){		Scene.getNodeChild(0).selected = true;		return;	}	var sib = getSibling(fos,i);	sib.select(true);	fos.select(false);	updateButtons();}function selectParent(){	getStates();	if(hasParent && fos.getNodeParent().getNodeParent()){		selectNone();		fos.getNodeParent().select(true);	}	updateButtons();}function selectChild(){	getStates();	if(numChildren>0){		selectNone();		fos.getNodeChild(0).select(true);	}	updateButtons();}var wDlg = new DzDialog;wDlg.caption = "Re-Order Scene Object Tree View";var wDlgLayout = new DzGridLayout( wDlg );wDlgLayout.margin = 5;wDlgLayout.spacing = 5;var wBtnGrp = new DzGroupBox( wDlg );wBtnGrp.title = "Move in List";wBtnGrp.columns = 3;var bugrid = new DzGridLayout(wBtnGrp);bugrid.margin = 10;bugrid.spacing = 4;var wMoveUp = new DzPushButton(wBtnGrp);wMoveUp.text = "Up";wMoveUp.minWidth = 80;wMoveUp.maxHeight = 20;wMoveUp.clicked.connect(function(){moveInView(-1)});bugrid.addWidget(wMoveUp, 0, 1);var wUnparent = new DzPushButton(wBtnGrp);wUnparent.text = "Unparent";wUnparent.minWidth = 80;wUnparent.maxHeight = 20;wUnparent.clicked.connect(unparent);bugrid.addWidget(wUnparent, 1, 0);var wParent = new DzPushButton(wBtnGrp);wParent.text = "Parent";wParent.minWidth = 80;wParent.maxHeight = 20;wParent.clicked.connect(doparent);bugrid.addWidget(wParent, 1, 3);var wMoveDown = new DzPushButton(wBtnGrp);wMoveDown.text = "Down";wMoveDown.minWidth = 80;wMoveDown.maxHeight = 20;wMoveDown.clicked.connect(function(){moveInView(1)});bugrid.addWidget(wMoveDown, 3, 1);/////////////// Navigation. I'm lazy, so I basically just copied the above buttonsvar wNavGrp = new DzGroupBox( wDlg );wNavGrp.title = "Navigate Selection in List";wNavGrp.columns = 3;var nagrid = new DzGridLayout(wNavGrp);nagrid.margin = 10;nagrid.spacing = 4;var wnMoveUp = new DzPushButton(wNavGrp);wnMoveUp.text = "Up";wnMoveUp.minWidth = 80;wnMoveUp.maxHeight = 20;wnMoveUp.clicked.connect(function(){selectSibling(-1)});nagrid.addWidget(wnMoveUp, 0, 1);var wnUnparent = new DzPushButton(wNavGrp);wnUnparent.text = "Up Parent";wnUnparent.minWidth = 80;wnUnparent.maxHeight = 20;wnUnparent.clicked.connect(selectParent);nagrid.addWidget(wnUnparent, 1, 0);var wnParent = new DzPushButton(wNavGrp);wnParent.text = "Into Children";wnParent.minWidth = 80;wnParent.maxHeight = 20;wnParent.clicked.connect(selectChild);nagrid.addWidget(wnParent, 1, 3);var wnMoveDown = new DzPushButton(wNavGrp);wnMoveDown.text = "Down";wnMoveDown.minWidth = 80;wnMoveDown.maxHeight = 20;wnMoveDown.clicked.connect(function(){selectSibling(1)});nagrid.addWidget(wnMoveDown, 3, 1);wDlgLayout.addMultiCellWidget( wBtnGrp, 0, 0, 0, 1 );wDlgLayout.addMultiCellWidget( wNavGrp, 1, 1, 0, 1 );var wDlgBtnsGB = new DzGroupBox( wDlg );wDlgBtnsGB.flat = true;var wDlgBtnsLyt = new DzGridLayout( wDlgBtnsGB );wDlgBtnsLyt.margin = 5;wDlgBtnsLyt.spacing = 5;var wAcceptBtn = new DzPushButton( wDlgBtnsGB );wAcceptBtn.text = "OK";wAcceptBtn.minWidth = 80;wAcceptBtn.maxHeight = 20;wDlg.setAcceptButton( wAcceptBtn );wDlgBtnsLyt.addWidget( wAcceptBtn, 1, 0 );wDlgLayout.addMultiCellWidget( wDlgBtnsGB, 2, 2, 0, 1 );wDlg.maxWidth = wDlg.minWidth;wDlg.maxHeight = wDlg.minHeight;updateButtons();if(selSize<=0){	Scene.getNodeChild(0).select(true);	updateButtons();}if(!hasParent && ask("No parent node available.\nShould all scene Nodes be moved into a Root Node (Group)?", "No Parent", "Cancel", "OK")){	startSelect = Scene.getSelectedNode(0);	rootAll();}var rn = Scene.findNode("Scene Root");if(!hasParent && rn && rn.getNumNodeChildren()>0){	selectNone();	if(startSelect!=null){		startSelect.select(true);	}	else{		rn.getNodeChild(0).select(true);	}	updateButtons();}wDlg.exec()function msg(s,t,c1,c2,c3){	t = t==undefined?"Message":t;	c1 = c1==undefined?"OK":c1;	c2 = c2==undefined?"":c2;	c3 = c3==undefined?"":c3;	if(c2!=""){		if(c3!=""){			return MessageBox.information(s,t,c1,c2,c3);		}		return MessageBox.information(s,t,c1,c2);	}	return MessageBox.information(s,t,c1);}function warn(s,t,c1,c2,c3){	t = t==undefined?"Warning":t;	c1 = c1==undefined?"OK":c1;	c2 = c2==undefined?"Cancel":c2;	c3 = c3==undefined?"":c3;	if(c2!=""){		if(c3!=""){			return MessageBox.warning(s,t,c1,c2,c3);		}		return MessageBox.warning(s,t,c1,c2);	}	return MessageBox.warning(s,t,c1);}function ask(s,t,c1,c2,c3){	t = t==undefined?"Message":t;	c1 = c1==undefined?"No":c1;	c2 = c2==undefined?"Yes":c2;	c3 = c3==undefined?"":c3;	if(c2!=""){		if(c3!=""){			return MessageBox.question(s,t,c1,c2,c3);		}		return MessageBox.question(s,t,c1,c2);	}	return MessageBox.question(s,t,c1);}function rootAll(){	var baseNode = new DzGroupNode();	baseNode.name = "Scene Root";	baseNode.label = "Scene Root";	Scene.addNode(baseNode);		var allnodes = Scene.getNodeList();		var j = 0;	for(var i = 0; i<allnodes.length; i++){		if(allnodes[i].name == "Scene Root" || allnodes[i].getNodeParent())			continue;		baseNode.addNodeChild(allnodes[i], true);		baseNode.moveNodeChildToIndex(allnodes[i],j);		j++;	}}function getSibling(n, i){	var parnode = n.getNodeParent();		var cur = parnode.findChildIndex(n);	if(cur + i < 0){		return parnode;	}	if(cur + i >= parnode.getNumNodeChildren()){		return nextOrParent(n, i);	}	return parnode.getNodeChild(cur + i);}function nextOrParent(c, i){	var n = c;	var parnode = n.getNodeParent();	var cur = parnode.findChildIndex(n);	if(cur + i >= parnode.getNumNodeChildren()){		if(n.getNodeParent()){			return nextOrParent(n.getNodeParent(), i);		}		else{return null;}	}	return parnode.getNodeChild(cur + i);}function getStates(){	selSize = Scene.getNumSelectedNodes();	hasParent = false;	isFirst = true;	isLast = true;		fos = Scene.getSelectedNode(0);	los = Scene.getSelectedNode(selSize-1);	par = fos.getNodeParent();		numChildren = fos.getNumNodeChildren();		if(selSize>0 && par)		hasParent = true;		if(hasParent && par.findChildIndex(fos)>0)		isFirst = false;		if(hasParent && par.findChildIndex(los) < par.getNumNodeChildren()-1)		isLast = false;}function setButtons(){	wMoveUp.enabled = !isFirst;	wParent.enabled = !isFirst;	wMoveDown.enabled = !isLast;	wUnparent.enabled = hasParent;	wnMoveDown.enabled = !isLast;	wnMoveUp.enabled = hasParent;	wnParent.enabled = numChildren>0;	wnUnparent.enabled = hasParent && fos.getNodeParent().getNodeParent();}function updateButtons(){	getStates();	setButtons();}function selectNone(){	for(var i = 0; i<Scene.getNumSelectedNodes(); i++){		Scene.getSelectedNode(i).select(false);	}}function saveTrans(n){	statAbsPos = n.getWSPos();	statAbsRot = n.getWSRot();	statAbsScale = n.getWSScale();}function restoreTrans(n){	n.setWSPos(statAbsPos);	n.setWSRot(statAbsRot);	n.setWSScale(statAbsScale);}

I hope this is useful to at least one other person :)

Make it available via the main menu by right clicking -> Create Custom Action.

If the attachment doesn't work, try: https://mega.nz/file/orx10KqY#LbQL0C3Bm1Rw1yk1Cp9KdYHt8MNoHaqC_pj4q_UjqwQ

zip
zip
SceneOrder.zip
35K
Post edited by svArtist on

Comments

  • AlecGrafAlecGraf Posts: 18

    Thank you so much for this -- it's an absolute mind and time saver!

  • KooreoKooreo Posts: 48

    Thank You.

  • jardinejardine Posts: 1,202

    cool!  thank you!

    j

  • tring01tring01 Posts: 305

    This is awesome.  No way to thank you enough!

  • gitika1gitika1 Posts: 948

    Thanks!

  • Thanks a lot, very useful! 

  • Wow. Thank you so much for this script.

  • DoctorJellybeanDoctorJellybean Posts: 8,517
    edited December 2019

    Saving the above in a script and executing it results in a An error occurred while reading the file, see the log for more details message when the scene is empty or no selection was made. The log states:

    2019-12-04 11:32:19.041 Loading script: D:/Library/Scripts/Utilities/Scene Object.dsa
    2019-12-04 11:32:19.043 WARNING: ..\..\..\..\..\src\sdksource\general\dzscene.cpp(5693): Index out of range in DzScene::getSelectedNode()
    2019-12-04 11:32:19.043 WARNING: ..\..\..\..\..\src\sdksource\general\dzscene.cpp(5693): Index out of range in DzScene::getSelectedNode()
    2019-12-04 11:32:19.043 WARNING: Script Error: Line 13
    2019-12-04 11:32:19.043 WARNING: TypeError: Result of expression 'fos' [null] is not an object.
    2019-12-04 11:32:19.043 WARNING: Stack Trace:
        <anonymous>()@D:/Library/Scripts/Utilities/Scene Object.dsa:13
    2019-12-04 11:32:19.047 Error in script execution: D:/Library/Scripts/Utilities/Scene Object.dsa

    Otherwise it seems to work well, thank you.

    Post edited by DoctorJellybean on
  • How do you get the icon in the list (Which list is the Main?) so I could click it? What do I do with the .dsa file? It shows a dialog as if I should paste it in.

  • Catherine3678abCatherine3678ab Posts: 8,363

    How do you get the icon in the list (Which list is the Main?) so I could click it? What do I do with the .dsa file? It shows a dialog as if I should paste it in.

    Put the .dsa file in the Scripts folder found in My Library. Then from the Content Library [in D/S], browse to that folder and create the Custom Action to add it to the top bar.

  • Catherine3678abCatherine3678ab Posts: 8,363

    Thank you, looks awesome :-)

  • How do you get the icon in the list (Which list is the Main?) so I could click it? What do I do with the .dsa file? It shows a dialog as if I should paste it in.

    Put the .dsa file in the Scripts folder found in My Library. Then from the Content Library [in D/S], browse to that folder and create the Custom Action to add it to the top bar.

    Brilliant!  Kisses for all!!!

  • KeironKeiron Posts: 413

    THATS brill thank you

  • AntonoAntono Posts: 24

    Just what I was looking for, thanks !

  • mavantemavante Posts: 734

    Brilliant. Thank you!

  • svArtistsvArtist Posts: 19

    Oh my, it seems I'm not getting any comment notifications indecision

    If I take too long to respond to something, let me know in the comments of the above YouTube video.
    I just got a comment that brought me back here and I think I never got notified of the comments :/

    But disclaimer: I haven't fiddled with DAZ in years and I'm really not up-to-date or even able to check anything, I don't have it currently installed indecision

  • stitlownstitlown Posts: 284
    edited November 2022

    Woo Hoo. You're a star Beared-P. Thanks

    Post edited by stitlown on
  • LimunLimun Posts: 182

    Thank you :)

  • DrHemulenDrHemulen Posts: 87

    Thank you so much. This is such a weird omission, I thought something was wrong with my DAZ installation.

    Installed and still working fine on DAZ 4.22

Sign In or Register to comment.