--try (destroyDialog RsLodViewToolRoll) catch () rollout RsLodViewToolRoll "LOD Visualisation" ( local lodViewObj local toolIsOpen = false checkButton btnVisActive "LOD Visualisation" offset:[-12,4] across:2 checkBox chkLodSpheres "Show as spheres" offset:[-14,-2] checkBox chkShowSourceLods "Show source-obj distances" pos:(chkLodSpheres.pos + [0, 20]) radioButtons rdoShowSelAll "Show for:" labels:#("Sel. Objs", "Sel. Hierarchy", "All Objs") offset:[0,-6] -- Used to define the lod-render bits: struct renderItem (clr, class = #line, points = #(), extra) local renderList = #() fn renderLodView = ( -- Only continue drawing if editor-tool is still open: if not toolIsOpen do ( unregisterRedrawViewsCallback renderLodView return OK ) gw.setTransform (Matrix3 1) local lineClr, textClr for item in renderList do ( local points = for pos in item.points collect (gw.wtransPoint pos) case item.class of ( #line: ( gw.setColor #line item.clr gw.wPolyline points item.extra ) #marker: ( gw.Marker points[1] item.extra color:item.clr ) #text: ( gw.setColor #text item.clr gw.text points[1] item.extra ) ) ) ) -- Returns position of point val (0->1) along bezier defined by three points: fn quadraticBezierPoint posA posB posC val = ( local t = 1 - val local sn = 1 - t local retVal = [0,0,0] retVal.X = posA.x*t*t + posB.x*2*t*Sn + posC.x*sn*sn; retVal.Y = posA.y*t*t + posB.y*2*t*Sn + posC.y*sn*sn; retVal.Z = posA.z*t*t + posB.z*2*t*Sn + posC.z*sn*sn; return retVal ) fn getArcPoints startPoint endPoint height arrow:true = ( local points = #() local lineVec = endPoint - startPoint local midPoint = startPoint + (0.5 * lineVec) local startPoint2d = [startPoint.x, startPoint.y] local endPoint2d = [endPoint.x, endPoint.y] local dist2d = distance startPoint2d endPoint2d local absHeight = abs height if (dist2d < absHeight) do ( height *= (dist2d / absHeight) ) midPoint += [0, 0, height] local arrowSize = 3.0 -- Don't arc line if nearly vertical: local stepper = if (abs height > 0.1) then (1.0 / 6) else 1.0 local points = for val = 0.0 to 1.0 by stepper collect ( quadraticBezierPoint startPoint midPoint endPoint val ) if arrow do ( local arrowStartVal = 1.0 - (arrowSize / (distance startPoint endPoint)) local arrowStartPos = quadraticBezierPoint startPoint midPoint endPoint arrowStartVal local arrowVec = (endPoint - arrowStartPos) local arrowParaVec = arrowSize * (normalize (cross arrowVec (arrowVec + [0,0,1]))) append points (arrowStartPos + arrowParaVec) append points (arrowStartPos - arrowParaVec) append points endPoint ) return points ) fn getCirclePoints pos radius segs = ( local slice = 360.0 / segs for n = 0 to (segs - 1) collect ( local ang = slice * n [pos.x + (radius * (cos ang)), pos.y + (radius * (sin ang)), pos.z] ) ) local showObjItems = #() -- Update render-data: fn updateLodView updateObjList:true doRedraw:true = ( -- Clear the current render-list: renderList.count = 0 if not btnVisActive.checked do ( deleteAllChangeHandlers id:#RsLodViewTool return OK ) unregisterRedrawViewsCallback renderLodView -- Get render-options from UI: local showAll = (rdoShowSelAll.state == 3) local showHierarchy = (rdoShowSelAll.state == 2) local drawSpheres = chkLodSpheres.checked local showSourceLods = chkShowSourceLods.checked if updateObjList then ( -- Get scene-objects per lod-level: local lodLevelledObjs = RsGetLodLevelObjs() showObjItems = #() for lodLevel = 1 to lodLevelledObjs.count do ( local objList = lodLevelledObjs[lodLevel] join showObjItems (for obj in objList where (showAll or obj.isSelected) and (not obj.isHidden) collect (dataPair obj:obj lodLevel:lodLevel)) ) -- Add selected-object parents/child hierarchies to to-render list: if showHierarchy do ( local parentObjItems = deepCopy showObjItems local objNum = 0 while (objNum < parentObjItems.count) do ( objNum += 1 local objItem = parentObjItems[objNum] local objPar = RsSceneLink.getparent LinkType_LOD objItem.obj if (objPar != undefined) do ( append parentObjItems (dataPair obj:objPar lodLevel:(objItem.lodLevel + 1)) ) ) parentObjItems = for n = (showObjItems.count + 1) to parentObjItems.count collect parentObjItems[n] local childObjItems = deepCopy showObjItems local objNum = 0 while (objNum < childObjItems.count) do ( objNum += 1 local objItem = childObjItems[objNum] local childLodLevel = objItem.lodLevel - 1 local objChildItems = for childObj in (RsGetLODChildren objItem.obj) collect (dataPair obj:childObj lodLevel:childLodLevel) join childObjItems objChildItems ) childObjItems = for n = (showObjItems.count + 1) to childObjItems.count collect childObjItems[n] -- Add unique-object items to main list: local objsList = for item in showObjItems collect item.obj for item in parentObjItems do ( if (not item.obj.isHidden) and (appendIfUnique objsList item.obj) do (append showObjItems item) ) for item in childObjItems do ( if (not item.obj.isHidden) and (appendIfUnique objsList item.obj) do (append showObjItems item) ) ) -- Set up change-handers for our objects: local objs = for item in showObjItems collect item.obj deleteAllChangeHandlers id:#RsLodViewTool when objs deleted id:#RsLodViewTool do (updateLodView updateObjList:false doRedraw:false) when transform objs changes id:#RsLodViewTool do (updateLodView updateObjList:false doRedraw:false) when parameters objs changes id:#RsLodViewTool do (updateLodView updateObjList:false doRedraw:false) ) -- Collect refDefs first, to preload lod-distances if required. -- (Using refNums bitarray to avoid doing isRsRef check multiple times) local refNums = #{} local contRefNums = #{} refNums.count = showObjItems.count contRefNums.count = showObjItems.count local refDefs = for objNum = 1 to showObjItems.count collect ( local obj = showObjItems[objNum].obj case of ( (isRsRef obj): ( refNums[objNum] = true obj.refDef ) (RsIsContainerLod obj): ( contRefNums[objNum] = true obj.refDef ) default:dontCollect ) ) refDefs = makeUniqueArray refDefs RsRefFuncs.loadLodDistances refDefs struct lodObjStruct (obj, isRef = false, isContRef = false, refdef, instLod = false, lodDist, sourceLodDist, lodParent, lodLevel) local objItems = for objNum = 1 to showObjItems.count collect ( local objItem = showObjItems[objNum] local obj = objItem.obj local listItem = lodObjStruct obj:obj isRef:refNums[objNum] isContRef:contRefNums[objNum] lodLevel:objItem.lodLevel local instLod = true local refDef case of ( (listItem.isRef): ( refDef = listItem.refDef = obj.refDef instLod = getAttr obj idxInstLodDistance ) (listItem.isContRef): ( refDef = listItem.refDef = obj.refDef instLod = false ) ) listItem.instLod = instLod listItem.lodDist = if instLod then ( listItem.sourceLodDist = if (refDef == undefined) then undefined else ( refDef.lodDistance ) getAttr obj idxLodDistance ) else ( if (refDef == undefined) then undefined else ( refDef.lodDistance ) ) if (listItem.lodDist != undefined) then listItem else dontCollect ) -- Paramblock list causes updates whenever these objects are changed: drawNodes = for item in objItems collect item.obj local instLodClr = (color 180 255 180) local refLodClr = Black -- Avoid adding similar circles more than once: local addedCircleHashes = #() for item in objItems do ( local drawColour = if item.instLod then instLodClr else refLodClr local obj = item.obj local objPos = obj.pos local lodDist = item.lodDist local sourceLodDist = item.sourceLodDist local lodParent = item.lodParent = RsSceneLink.getparent LinkType_LOD obj local hasParent = (lodParent != undefined) local lodPivot = if hasParent then lodParent.pos else obj.pos local backXform, rotXformA, rotXformB if drawSpheres do ( backXform = transMatrix -lodPivot rotXformA = rotateXMatrix 90 rotXformA[4] = lodPivot rotXformB = rotateZMatrix 90 rotXformB[4] = lodPivot ) -- Add main LOD circles: local thisLodDist = lodDist local thisClr = drawColour local hasLevels = (item.lodLevel != 1) -- Draw non-selected objects with slightly darker circles: if (not obj.isSelected) do (thisClr *= 0.7) local circleHash = getHashValue #(thisClr, lodPivot, thisLodDist, item.lodLevel, 0) 1 if (appendIfUnique addedCircleHashes circleHash) do ( for levelNum = 1 to item.lodLevel do ( local circlePoints = getCirclePoints [0,0,0] thisLodDist 28 append renderList (renderItem clr:thisClr class:#line points:(for pos in circlePoints collect (pos + lodPivot)) extra:true) -- Only draw extra lod-level circles for main circle, not for sphere-circles: if drawSpheres and (levelNum == 1) do ( append renderList (renderItem clr:thisClr class:#line points:(for pos in circlePoints collect (pos * rotXformA)) extra:true) append renderList (renderItem clr:thisClr class:#line points:(for pos in circlePoints collect (pos * rotXformB)) extra:true) ) -- Draw extra lines per lod-level - 10m spacing, darker as they go out if hasLevels do ( thisLodDist += 5.0 thisClr *= 0.7 ) ) ) -- Add dashed circle for prop's source-object lod-distance: if showSourceLods and (sourceLodDist != undefined) do ( local circleHash = getHashValue #(refLodClr, lodPivot, sourceLodDist, 1, 1) 1 if (appendIfUnique addedCircleHashes circleHash) do ( local circlePoints = getCirclePoints [0,0,0] sourceLodDist 40 append circlePoints circlePoints[1] local circleDashes = for n = 1 to (circlePoints.count - 1) by 2 collect #(circlePoints[n] + lodPivot, circlePoints[n + 1] + lodPivot) if drawSpheres do ( join circleDashes (for n = 1 to (circlePoints.count - 1) by 2 collect #(circlePoints[n] * rotXformA, circlePoints[n + 1] * rotXformA)) join circleDashes (for n = 1 to (circlePoints.count - 1) by 2 collect #(circlePoints[n] * rotXformB, circlePoints[n + 1] * rotXformB)) ) join renderList (for item in circleDashes collect (renderItem clr:refLodClr class:#line points:item extra:false)) ) ) -- Add arc to parent, if pivots are different: if hasParent and ((distance objPos lodPivot) > 0.1) do ( local arcClr = drawColour * 0.7 local arcPoints = getArcPoints objPos lodPivot (-0.15 * lodDist) append renderList (renderItem clr:arcClr class:#line points:arcPoints extra:false) append renderList (renderItem clr:drawColour class:#marker points:#(objPos) extra:#xMarker) ) -- Add lod-pivot marker: append renderList (renderItem clr:drawColour class:#marker points:#(lodPivot) extra:#circle) ) registerRedrawViewsCallback renderLodView if doRedraw do ( completeRedraw() ) return OK ) fn removeCallbacks = ( renderList.count = 0 deleteAllChangeHandlers id:#RsLodViewTool callbacks.removeScripts id:#RsLodViewTool unregisterRedrawViewsCallback renderLodView ) fn setupCallbacks = ( callbacks.addScript #selectionSetChanged "RsLodViewToolRoll.updateLodView()" id:#RsLodViewTool callbacks.addScript #filePostOpenProcess "RsLodViewToolRoll.updateLodView()" id:#RsLodViewTool callbacks.addScript #filePostSaveProcess "RsLodViewToolRoll.updateLodView()" id:#RsLodViewTool callbacks.addScript #systemPostReset "RsLodViewToolRoll.updateLodView()" id:#RsLodViewTool callbacks.addScript #systemPostNew "RsLodViewToolRoll.updateLodView()" id:#RsLodViewTool registerRedrawViewsCallback renderLodView ) on btnVisActive changed state do ( updateLodView() if not state do (completeRedraw()) ) on chkLodSpheres changed state do (updateLodView updateObjList:false) on chkShowSourceLods changed state do (updateLodView updateObjList:false) on rdoShowSelAll changed state do (updateLodView()) on RsLodViewToolRoll open do ( toolIsOpen = true setupCallbacks() updateLodView() ) on RsLodViewToolRoll close do ( toolIsOpen = false removeCallbacks() if btnVisActive.checked do (completeRedraw()) ) -- Save rolled-up state: on RsLodViewToolRoll rolledUp down do ( RsSettingWrite "RsLodViewToolRoll" "rollup" (not down) ) ) --createDialog RsLodViewToolRoll width:300