local function yesOrNo(condition) if condition then return "YES" else return "NO" end end local function checkAttributeAnimation(node, attributeId, speedAttributeId) if # node:getAttribute(attributeId) ~= 0 then print(string.format(" %s animated? %s - has %s? %s", octane.apiinfo.getAttributeIdName(attributeId), yesOrNo(node:isAnimated(attributeId)), octane.apiinfo.getAttributeIdName(speedAttributeId), yesOrNo(# node:getAttribute(speedAttributeId) ~= 0))) end end local function fetchTopoAttribute(node, attributeId, attributes) attributes[octane.apiinfo.getAttributeIdName(attributeId)] = node:getAttribute(attributeId) end local function fetchTopoAttributeSize(node, attributeId, attributes) attributes[octane.apiinfo.getAttributeIdName(attributeId)] = # node:getAttribute(attributeId) end local function fetchTopoAttributes(node) local attributes = {} fetchTopoAttributeSize(node, octane.A_VERTICES, attributes) fetchTopoAttribute(node, octane.A_VERTICES_PER_POLY, attributes) fetchTopoAttribute(node, octane.A_POLY_VERTEX_INDICES, attributes) fetchTopoAttributeSize(node, octane.A_HAIR_VERTICES, attributes) fetchTopoAttribute(node, octane.A_VERTICES_PER_HAIR, attributes) fetchTopoAttributeSize(node, octane.A_SPHERE_CENTERS, attributes) return attributes end local function compareTopoAttributes(previousAttributes, currentAttributes, frameIx) for attributeName, previousAttribute in pairs(previousAttributes) do local currentAttribute = currentAttributes[attributeName] if type(previousAttribute) == "table" then local previousSize = #previousAttribute local currentSize = #currentAttribute if previousSize ~= currentSize then print(string.format(" topo attribute %s changed size at frame %d from %d to %d", attributeName, frameIx, previousSize, currentSize)) return false end for ix, previousValue in ipairs(previousAttribute) do if previousValue ~= currentAttribute[ix] then print(string.format(" topo attribute %s changed at index %d at frame %d", attributeName, ix-1, frameIx)) return false end end else if previousAttribute ~= currentAttribute then print(string.format(" topo attribute %s changed size at frame %d from %d to %d", attributeName, frameIx, previousAttribute, currentAttribute)) return false end end end return true end local function checkTopology(node) -- get time information local timeSpan = node.rootGraph:getAnimationTimeSpan() local frameRate = octane.project.getProjectSettings():getAttribute(octane.A_FRAMES_PER_SECOND) local numberOfFrames = (timeSpan[2] - timeSpan[1]) * frameRate local startFrame = timeSpan[1] * frameRate -- create a copy of the node in a temporary root node graph local tempRoot = octane.nodegraph.createRootGraph() tempRoot:updateTime(node.rootGraph.time) local copies = tempRoot:copyFrom({node}, {node}) local copyNode = copies[1] -- loop over all frames and check if specific attributes stay constant tempRoot:updateTime(timeSpan[1]) local previousAttributes = fetchTopoAttributes(copyNode) for frameIx = 2, numberOfFrames do local time = timeSpan[1] + (frameIx - 1) / frameRate tempRoot:updateTime(time) local currentAttributes = fetchTopoAttributes(copyNode) if not compareTopoAttributes(previousAttributes, currentAttributes, startFrame + frameIx - 1) then break end previousAttributes = currentAttributes end -- clean up the temporary root node graph tempRoot:destroy() end local function checkMeshAnimation(node) print(string.format("\nanimation data for mesh node '%s':", node.name)) print(string.format(" constant topology? %s", yesOrNo(node:getAttribute(octane.A_CONSTANT_TOPOLOGY)))) checkAttributeAnimation(node, octane.A_VERTICES, octane.A_POLY_VERTEX_SPEED) checkAttributeAnimation(node, octane.A_HAIR_VERTICES, octane.A_HAIR_VERTEX_SPEED) checkAttributeAnimation(node, octane.A_SPHERE_CENTERS, octane.A_SPHERE_SPEED) checkTopology(node) end -- MAIN -- local selection = octane.project.getSelection() for _,item in pairs(selection) do if item.isNode and item.type == octane.NT_GEO_MESH then checkMeshAnimation(item) end if item.isGraph then local meshNodes = item:findNodes(octane.NT_GEO_MESH, false) for _,meshNode in pairs(meshNodes) do checkMeshAnimation(meshNode) end end end