-- OS 3DeBUG BROKER -- -- -- © October 2003, OpenSpark Interactive Ltd -- -- ---------------------------------------------------------------------- -- 040212 JN: Added Debug3DToggle() and Debug3D([#type: #face, ...]) ---------------------------------------------------------------------- -- This broker script provides a global Debug3D() method which allows -- you to display points and planes in a chosen shockwave3D member. -- -- DEPENDENCIES -- ------------ -- This script may requires the MUI Dialogs script, to allow the -- author to choose between a number of #shockwave3D members. ---------------------------------------------------------------------- -- PUBLIC METHODS -- on Debug3DRegister(a3DMember) ---------------------------------------- -- INPUT: should be a pointer to a shockwave3D member. -- ACTION: Registers the given 3D member for all future 3Debug -- activity. -------------------------------------------------------------------- tBroker = mDebug3DBroker(a3DMember) return tBroker.mDebug3DRegister(a3DMember) end Debug3DRegister on Debug3D(aData, aScale) -------------------------------------------- -- INPUT: may be an object with a position, such as a -- vector, transform, group, model, camera or light, in -- in which case the absolute position of the object will be -- shown. It may also be a propList containing more complex -- data: -- -- [#type: #point, -- #ID: , -- #position: , -- #color: , -- #scale: ] -- -- [#type: #transform, -- #ID: , -- #transform: , -- #scale: 0.01>] -- -- [#type: #plane, -- #ID: , -- #position: , -- #normal: , -- #yAxis: , -- #scale: ] -- -- [#type: #face, -- #ID: , -- #model: , -- #meshID: , -- #faceID: , -- #vertices: [, , ], -- #transform: ] -- -- may be a positive floating point scale factor. -- It is ignored if a propList is used for aData. -- ACTION: Shows the position of object defined by aData in the -- registered 3D member, or a transform, plane or face, as -- required. -------------------------------------------------------------------- tBroker = mDebug3DBroker() if objectP(tBroker) then return tBroker.mDebug3D(aData, aScale) else return tBroker -- error symbol end if end Debug3D on Debug3DToggle(aState) --------------------------------------------- -- INPUT: may be TRUE or FALSE. Any other value is -- interpreted as "toggle to the opposite state" -- ACTION: Activates and disactivates the 3D Debug feature. If the -- feature is disactivated, all debug objects are hidden. -------------------------------------------------------------------- tBroker = mDebug3DBroker() if objectP(tBroker) then return tBroker.mDebug3DToggle(aState) else return tBroker -- error symbol end if end Debug3DToggle -- SCRIPT PROPERTY AND METHOD -- property broker on mDebug3DBroker(a3DMember) ----------------------------------------- -- INPUT: will be void unless the call comes from -- Debug3DRegister(), in which case it should be a -- shockwave3D member. -- ACTION: Creates an instance of this script. If a shockwave3D -- member is available, and the choose member dialog is not -- canceled by the user, adopts the instance for all -- subsequent Debug3D() calls. -- OUTPUT: Returns a pointer to this script if it is correctly -- initialized. -------------------------------------------------------------------- tScript = script("OS 3Debug Broker") -- HARD-CODED script name tBroker = tScript.broker if not objectP(tBroker) then tBroker = tScript.new() if tBroker.mInitialize(a3DMember) then -- Adopt the initialized broker for future calls tScript.broker = tBroker else -- Debug3D() calls will fail tBroker = #no3DMember end if end if return tBroker end mDebug3DBroker on kill(me) ---------------------------------------------------------- -- SENT BY the resetBrokers() movie script handler -------------------------------------------------------------------- if objectP(broker) then broker.kill() broker = VOID end if end kill -- PRIVATE PROPERTIES -- property p3DMember -- the shockwave 3D member in which to display -- debug models property plIDs -- [#idSymbol: "associated model name", ...] property plNames -- ["generic object name": , ...] property pActive -- debug objects will appear only if this is TRUE -- PRIVATE METHODS -- on mInitialize(me, a3DMember) ---------------------------------------- -- CALLED by mDebug3DBroker() the first time one of the public -- methods in this script is called -- INPUT: may be a shockwave3D member. If not, the -- castLibs are checked for the presence of a shockwave3D -- member. -- ACTION: Initializes the plIDs and plNames property lists and -- may adopt a shockwave3D member for p3DMember. -- OUTPUT: Returns TRUE if a shockwave3D member can be adopted, -- FALSE if not. -------------------------------------------------------------------- plIDs = [:] plNames = [:] pActive = FALSE -- TRUE if ilk(a3DMember, #member) then if a3DMember.type = #shockwave3D then -- The call came from Debug3DRegister(), with a valid member as -- the parameter. The member will be registered by the -- mDebug3DRegister() handler as soon as this handler returns. return TRUE end if end if -- If we get here, no 3D member has been declared. Ask the user to -- choose a 3D member before proceeding. p3DMember = Choose3DMember() p3DMember = value(p3DMember) return ilk(p3DMember, #member) end mInitialize on mDebug3DRegister(me, aPropList) ---------------------------------- -- INPUT: should be a property list with the format: -- [#member: , ...] -- It may also be a direct pointer to a shockwave3D member. -- ACTION: Registers the given 3D member for all future 3Debug -- activity. -------------------------------------------------------------------- case ilk(aPropList) of #member: if aPropList.type = #shockwave3D then -- *** Clear all existing 3D objects from the current member? p3DMember = aPropList end if #propList: tMember = aPropList[#member] if ilk(tMember) = #member then me.mDebug3DRegister(tMember) end if end case end mDebug3DRegister on mDebug3D(me, aData, aScale) --------------------------------------- -- INPUT: may be an object with a position, such as a -- vector, transform, group, model, camera or light, in -- in which case the absolute position of the object will be -- shown. It may also be a propList containing more complex -- data: -- -- [#type: #point, -- #ID: , -- #position: , -- #color: , -- #scale: ] -- -- [#type: #transform, -- #ID: , -- #transform: , -- #scale: 0.01>] -- -- [#type: #plane, -- #ID: , -- #position: , -- #normal: , -- #yAxis: , -- #scale: ] -- -- [#type: #face, -- #ID: , -- #model: , -- #meshID: , -- #faceID: , -- #vertices: [, , ], -- #transform: ] -- -- may be a positive floating point scale factor. -- It is ignored if a propList is used for aData. -- ACTION: Despatches the call to the appropriate method, depending -- on the type of data sent as a parameter. -------------------------------------------------------------------- if not pActive then -- Debugging is off exit end if case ilk(aData) of #vector: me._ShowPoint(aData, aScale) #transform: me._ShowPoint(aData.position, aScale) #group, #model, #light, # camera: me._ShowPoint(aData.worldPosition, aScale) #propList: case aData[#type] of #face: me.mShowFace(aData) #plane: me.mShowPlane(aData) #point: me.mShowPoint(aData) #transform: me.mShowTransform(aData) -- Add other cases here end case end case end mDebug3D on mDebug3DToggle(me, aState) ----------------------------------------- -- INPUT: may be TRUE or FALSE. Any other value is -- interpreted as "toggle to the opposite state" -- ACTION: Activates and disactivates the 3D Debug feature. If the -- feature is disactivated, all debug objects are hidden. -------------------------------------------------------------------- if integerP(aState) then pActive = aState <> 0 else pActive = not pActive end if i = plIDs.count repeat while i tModel = p3DMember.model(plIDs[i]) if pActive then if not tModel.isInWorld() then tModel.addToWorld() end if else if tModel.isInWorld() then tModel.removeFromWorld() end if end if i = i - 1 end repeat end mDebug3DToggle on _ShowPoint(me, aPosition, aScale) --------------------------------- -- INPUT: should be a position vector in world space -- may be a positive floating point scale factor -- ACTION: Shows a sphere model at the chosen position. The same -- model will be re-used to show a new position on each -- subsequent call. -------------------------------------------------------------------- tName = "OS3Debug_Point_" tModel = p3DMember.model(tName) if ilk(tModel) <> #model then tResource = p3DMember.newModelResource(tName, #sphere) tResource.radius = 25 tModel = p3DMember.newModel(tName, tResource) end if tModel.worldPosition = aPosition if floatP(aScale) then aScale = max(0.01, abs(aScale)) tModel.transform.scale = vector(aScale, aScale, aScale) end if end mShowPoint -- OBJECT CREATION -- on mGetUniqueName(me, aSeed) ----------------------------------------- -- INPUT: should be a symbol, but any Lingo value will do -- OUTPUT: Returns a string with the format "aSeedX", where X will -- be a positive integer. -------------------------------------------------------------------- aSeed = string(aSeed) tCounter = plNames[aSeed] + 1 -- + 1 = 1 plNames[aSeed] = tCounter return aSeed&tCounter end mGetUniqueName on mShowPlane(me, aPropList) ----------------------------------------- -- INPUT: will be a property list which should have the -- format: -- [#type: #plane, -- #ID: , -- #position: , -- #normal: , -- #yAxis: , -- #scale: ] -- None of these properties is in fact essential; default -- values will be used if any are missing. -- ACTION: Creates a plane model with the given ID and shows it at -- the given world position and orientation. -------------------------------------------------------------------- anID = aPropList[#ID] if not symbolP(anID) then -- (Re-)use a default model anID = #plane end if aPosition = aPropList[#position] aNormal = aPropList[#normal] aYAxis = aPropList[#yAxis] aScale = aPropList[#scale] if ilk(aScale, #number) then aScale = max(0.01, abs(aScale)) else aScale = 1.0 end if tName = plIDs.getaProp(anID) if stringP(tName) then -- The model may already exist. If so, reuse it. tModel = p3DMember.model(tName) end if if ilk(tModel) <> #model then -- No plane exists yet with this ID. Create a new plane -- model using a shared resource with a red x-axis, green -- y-axis and a semi-opaque background tName = "OS3Debug_Plane_" tResource = p3DMember.modelResource(tName) if ilk(tResource) <> #mesh then -- The model resource, shader and texture required for -- visualizing a plane do not exist yet. Create them. We use a -- mesh, not a plane, so that we can map the axes in mirror -- image on the other side. tResource = me.mCreatePlane(tName) tShader = me.mCreatePlaneShader(tName) else -- The resource, shader and texture were created earlier tShader = p3DMember.shader(tName) end if -- ilk(tResource) <> #plane -- We may need to create many plane models, each with a -- unique name, so we add a number at the end. tName = me.mGetUniqueName(tName) tModel = p3DMember.newModel(tName, tResource) tModel.shaderList = tShader plIDs[anID] = tName end if -- ilk(tModel) <> #model if ilk(aPosition) <> #vector then aPosition = vector(0, 0, 0) end if if ilk(aNormal) <> #vector then aNormal = vector(0, 0, 1) end if -- Create a transform with its z-axis aligned with aNormal tTransform = GetTransform(aNormal, aYAxis, #z, #y) -- Place the center of the transform at aPosition tTransform.position = aPosition tTransform.scale = vector(aScale, aScale, aScale) tModel.transform = tTransform end mShowPlane on mCreatePlane(me, aName) ------------------------------------------- -- SENT BY mShowPlane() the first time it is called. -- ACTION: Creates a flat rectangular mesh resource where any given -- texture will be mapped with the left half on the front -- face and the right half on the back face. -------------------------------------------------------------------- -- Prepare values for the newMesh() command tVertexList = [ \ vector(-256, -256, 0), \ vector( 256, -256, 0), \ vector( 256, 256, 0), \ vector(-256, 256, 0) \ ] -- Define how these vertices will be joined together as triangles tFaceList = [] tFaceList.append([1, 3, 4]) -- front-facing, upper left tFaceList.append([1, 2, 3]) -- front-facing, lower right tFaceList.append([1, 4, 3]) -- back-facing, upper left tFaceList.append([1, 3, 2]) -- back-facing, lower right tNormalCount = 0 -- Set by generateNormals.build() tColorList = [rgb(255, 255, 255)] tTextureCoordinateList = [ \ [0.0, 0.0], \ [0.5, 0.0], \ [1.0, 0.0], \ [0.0, 1.0], \ [0.5, 1.0], \ [1.0, 1.0]\ ] tCoordinates = [] tCoordinates.append([1, 5, 4]) -- upper left of left half tCoordinates.append([1, 2, 5]) -- lower right of left half tCoordinates.append([3, 6, 5]) -- upper right of right half tCoordinates.append([3, 5, 2]) -- lower left of right half tTextureCount = tTextureCoordinateList.count -- Create a new mesh ready to receive the vertexList, colorList and -- textureCoordinateList data tMeshResource = p3DMember.newMesh( \ aName, \ tFaceList.count, \ tVertexList.count, \ tNormalCount, \ tColorList.count, \ tTextureCount \ ) -- Apply the vertex, color, face and texture data tMeshResource.textureCoordinateList = tTextureCoordinateList tMeshResource.vertexList = tVertexList tMeshResource.colorList = tColorList repeat with i = 1 to 4 tMeshResource.face[i].vertices = tFaceList[i] tMeshResource.face[i].colors = [1, 1, 1] tMeshResource.face[i].textureCoordinates = tCoordinates[i] end repeat -- Create a flat mesh tMeshResource.generateNormals(#flat) tMeshResource.build() return tMeshResource end mCreatePlane on mCreatePlaneShader(me, aName) ------------------------------------- -- INPUT: will be a string not currently used for an texture -- or shader -- ACTION: Creates a semi-transparent texture with an red x- and -- green y-axis, and applies this to a shader -- OUTPUT: Returns the shader -------------------------------------------------------------------- tShader = p3DMember.shader(aName) if ilk(tShader) <> #shader then tImage = image(256, 128, 32, 8) tImage.fill( 0, 0, 256, 128, rgb(255, 255, 0)) tImage.fill( 64, 0, 65, 64, rgb(0, 255, 0)) --y-axis tImage.fill(191, 0, 192, 64, rgb(0, 255, 0)) --y-axis tImage.fill( 65, 64, 191, 65, rgb(255, 0, 0)) --x-axis tImage.fill( 64, 64, 65, 65, rgb(0, 0, 255)) --z-axis tImage.fill(191, 64, 192, 65, rgb(0, 0, 255)) --z-axis tAlpha = image(256, 128, 8, #grayscale) tAlpha.fill( 0, 0, 256, 128, paletteIndex(192)) tAlpha.fill( 64, 0, 65, 65, paletteIndex(128))--y-axis tAlpha.fill(191, 0, 192, 65, paletteIndex(128))--y-axis tAlpha.fill(64, 64, 192, 65, paletteIndex(128))--x-axis tImage.setAlpha(tAlpha) tTexture = p3DMember.newTexture(aName) tTexture.image = tImage tTexture.renderFormat = #rgba4444 tTexture.nearFiltering = FALSE tShader = p3DMember.newShader(aName, #standard) tShader.texture = tTexture end if return tShader end mCreatePlaneShader on mShowPoint(me, aPropList) ----------------------------------------- -- INPUT: will be a property list which should have the -- format: -- [#type: #point, -- #ID: , -- #position: , -- #color: , -- #scale: ] -- None of these properties is in fact essential; default -- values will be used if any are missing. -- ACTION: Creates a plane model with the given ID and shows it at -- the given world position and orientation. -------------------------------------------------------------------- anID = aPropList[#ID] if not symbolP(anID) then -- (Re-)use a default model anID = #point end if aPosition = aPropList[#position] aScale = aPropList[#scale] if floatP(aScale) then aScale = max(0.01, abs(aScale)) else aScale = 1.0 end if tName = plIDs.getaProp(anID) if stringP(tName) then -- The model may already exist. If so, reuse it. tModel = p3DMember.model(tName) end if if ilk(tModel) <> #model then -- No point model exists yet with this ID. Create a new sphere -- model tName = "OS3Debug_Point_" tResource = p3DMember.modelResource(tName) if ilk(tResource) <> #sphere then -- The model resource, shader and texture required for -- visualizing a plane do not exist yet. Create them. We use a -- mesh, not a plane, so that we can map the axes in mirror -- image on the other side. tResource = p3DMember.newModelResource(tName, #sphere) tResource.resolution = 4 end if -- We may need to create many plane models, each with a unique -- name and color, so we add a number at the end. tName = me.mGetUniqueName(tName) tModel = p3DMember.newModel(tName, tResource) tShader = me.mCreatePointShader(tName, aPropList[#color]) tModel.shaderList = tShader plIDs[anID] = tName end if -- ilk(tModel) <> #model case ilk(aPosition) of #vector: #transform: aPosition = aPosition.position #group, #model, #light, #camera: aPosition = aPosition.worldPosition otherwise: aPosition = vector(0, 0, 0) end case -- Place the center of the transform at aPosition tTransform = tModel.transform tTransform.position = aPosition tTransform.scale = vector(aScale, aScale, aScale) --tModel.transform = tTransform end mShowPoint on mCreatePointShader(me, aName, aColor) ----------------------------- -- -------------------------------------------------------------------- if symbolP(aColor) then case aColor of #white: aColor = rgb(255, 255, 255) #red: aColor = rgb(255, 0, 0) #orange: aColor = rgb(255, 128, 0) #yellow: aColor = rgb(255, 255, 0) #lemon: aColor = rgb(128, 255, 0) #green: aColor = rgb( 0, 255, 0) #turquoise: aColor = rgb( 0, 255, 128) #cyan: aColor = rgb( 0, 255, 255) #azure: aColor = rgb( 0, 128, 255) #blue: aColor = rgb( 0, 0, 255) #purple: aColor = rgb(128, 0, 255) #magenta: aColor = rgb(255, 0, 255) #mauve: aColor = rgb(255, 0, 128) #black: aColor = rgb( 0, 0, 0) end case end if if ilk(aColor) <> #color then aColor = rgb(0, 0, 0) end if tShader = p3DMember.newShader(aName, #standard) tShader.texture = VOID tShader.shininess = 0 tShader.emissive = aColor tShader.renderStyle = #wire return tShader end mCreatePointShader on mShowTransform(me, aPropList) ------------------------------------ -- INPUT: will be a property list which should have the -- format: -- [#type: #transform, -- #ID: , -- #transform: , -- #scale: 0.01>] -- None of these properties is in fact essential; default -- values will be used if any are missing. -- ACTION: Creates a plane model with the given ID and shows it at -- the given world position and orientation. -------------------------------------------------------------------- anID = aPropList[#ID] if not symbolP(anID) then -- (Re-)use a default model anID = #transform end if aTransform = aPropList[#transform] case ilk(aTransform) of #transform: aTransform = aTransform.duplicate() #group, #model, #light, #camera: aTransform = aTransform.getWorldTransform() otherwise: aTransform = transform() end case aScale = aPropList[#scale] if floatP(aScale) then aScale = max(0.01, abs(aScale)) else aScale = 1.0 end if tName = plIDs.getaProp(anID) if stringP(tName) then -- The model may already exist. If so, reuse it. tModel = p3DMember.model(tName) end if if ilk(tModel) <> #model then -- No transform model exists yet with this ID. Create a new -- transform model using a shared resource with a red x-plane, -- green y-plane and a blue z-plane tName = "OS3Debug_Transform_" tResource = p3DMember.modelResource(tName) if ilk(tResource) <> #mesh then -- The model resource, shader and texture required for -- visualizing a plane do not exist yet. Create them. We use a -- mesh, not a plane, so that we can map the axes in mirror -- image on the other side. tResource = me.mCreateTransform(tName) --tShader = me.mCreatePlaneShader(tName) else -- The resource, shader and texture were created earlier --tShader = p3DMember.shader(tName) end if -- ilk(tResource) <> #plane -- We may need to create many plane models, each with a -- unique name, so we add a number at the end. tName = me.mGetUniqueName(tName) tModel = p3DMember.newModel(tName, tResource) -- tModel.shaderList = tShader plIDs[anID] = tName end if -- ilk(tModel) <> #model aTransform.scale = vector(aScale, aScale, aScale) tModel.transform = aTransform end mShowTransform on mCreateTransform(me, aName) --------------------------------------- -- -------------------------------------------------------------------- -- Prepare values for the newMesh() command tVertexList = [ \ vector( 0, 128, 128), \ vector( 0, 0, 128), \ vector(128, 0, 128), \ vector( 0, 128, 0), \ vector( 0, 0, 0), \ vector(128, 0, 0), \ vector(128, 128, 0) \ ] -- Define how these vertices will be joined together as triangles tFaceList = [] tFaceList.append([1, 2, 5]) -- x = 0, y < z, x+ tFaceList.append([1, 5, 2]) -- x = 0, y < z, x- tFaceList.append([1, 5, 4]) -- x = 0, y > z, x+ tFaceList.append([1, 4, 5]) -- x = 0, y > z, x- tFaceList.append([2, 3, 5]) -- y = 0, x < z, y+ tFaceList.append([2, 5, 3]) -- y = 0, x < z, y- tFaceList.append([3, 6, 5]) -- y = 0, x > z, y+ tFaceList.append([3, 5, 6]) -- y = 0, x > z, y- tFaceList.append([4, 5, 7]) -- z = 0, x < y, z+ tFaceList.append([4, 7, 5]) -- z = 0, x < y, z- tFaceList.append([5, 6, 7]) -- z = 0, x > y, z+ tFaceList.append([5, 7 ,6]) -- z = 0, x > y, z- tNormalCount = 0 -- Set by generateNormals.build() tColorList = [rgb(255, 0, 0), rgb(0, 255, 0), rgb(0, 0, 255)] tTextureCoordinateList = [] -- Create a new mesh ready to receive the vertexList, colorList and -- textureCoordinateList data tMeshResource = p3DMember.newMesh( \ aName, \ tFaceList.count, \ tVertexList.count, \ tNormalCount, \ tColorList.count, \ tTextureCoordinateList.count \ ) -- Apply the vertex, color and face tMeshResource.vertexList = tVertexList tMeshResource.colorList = tColorList -- Plane x = 0 repeat with i = 1 to 4 tMeshResource.face[i].vertices = tFaceList[i] tMeshResource.face[i].colors = [1, 1, 1] end repeat -- Plane y = 0 repeat with i = 5 to 8 tMeshResource.face[i].vertices = tFaceList[i] tMeshResource.face[i].colors = [2, 2, 2] end repeat -- Plane z = 0 repeat with i = 9 to 12 tMeshResource.face[i].vertices = tFaceList[i] tMeshResource.face[i].colors = [3, 3, 3] end repeat -- Create a flat mesh tMeshResource.generateNormals(#flat) tMeshResource.build() return tMeshResource end mCreateTransform on mShowFace(me, aPropList) ------------------------------------------ -- INPUT: will be a property list which should have the -- format: -- [#type: #face, -- #ID: , -- #model: , -- #meshID: , -- #faceID: , -- #vertices: [, , ], -- #transform: ] -- None of these properties is in fact essential; default -- values will be used if any are missing. -- ACTION: Creates a three-vertex mesh with the given ID and shows -- it at the given world position and orientation. -- TIP: Obtain information on the face using the #detailed -- version of modelsUnderLoc, then addProp(#type, #face) to -- the resulting list. -------------------------------------------------------------------- anID = aPropList[#ID] if not symbolP(anID) then -- (Re-)use a default model anID = #face end if -- Use the model to determine the vertexList and transform, if -- possible aModel = aPropList[#model] if ilk(aModel, #model) then -- Determine which shader is applied to the face aMesh = aPropList[#meshID] if not integerP(aMesh) then aMesh = 1 else if aMesh < 1 then aMesh = 1 else tCount = aModel.shaderList.count if aMesh > tCount then aMesh = tCount end if end if -- Force the model or mesh to appear as a wire frame tShader = aModel.shaderList[aMesh] tShader.renderStyle = #wire -- Use .vertices if available aVertexList = aPropList[#vertices] if ilk(aVertexList) <> #list then aVertexList = 0 else if aVertexList.count <> 3 then aVertexList = 0 else repeat with i = 1 to 3 if ilk(aVertexList[i]) <> #vector then aVertexList = 0 exit repeat end if end repeat end if if not aVertexList then -- Determine which face to show manually aTransform = aModel.getWorldTransform() aFace = aPropList[#faceID] if not integerP(aFace) then aFace = 1 else if aFace < 1 then aFace = 1 end if tResource = aModel.resource if ilk(tResource, #mesh) then -- aMesh is 1 by definition tCount = tResource.face.count if aFace > tCount then aFace = tCount end if aVertexList = tResource.face[aFace].vertices tVertexList = tResource.vertexList tShader = aModel.shader else -- Add the meshDeform modifier if need be tModifiers = aModel.modifier if not tModifiers.getPos(#meshDeform) then aModel.addModifier(#meshDeform) end if tCount = aModel.meshDeform.mesh[aMesh].face.count if aFace > tCount then aFace = tCount end if aVertexList = aModel.meshDeform.mesh[aMesh].face[aFace] tVertexList = aModel.meshDeform.mesh[aMesh].vertexList end if repeat with i = 1 to 3 aVertexList[i] = tVertexList[aVertexList[i]] end repeat else -- A model is given and vertices are given: assume that the user -- is providing a detailed list from modelsUnderLoc aTransform = transform() end if else -- no model is given -- Ensure that the vertexList is a list of at least three vectors aVertexList = aPropList[#vertices] if ilk(aVertexList) <> #list then aVertexList = [vector(1,0,0), vector(0,1,0), vector(0,0,1)] else aVertexList = aVertexList.duplicate() tCount = aVertexList.count if aVertexList.count < 3 then aVertexList[3] = vector(0, 0, 1) else repeat with i = 4 to tCount aVertexList.deleteAt(i) end repeat end if end if i = 3 repeat while i if ilk(aVertexList[i]) <> #vector then tVector = vector(i = 1, i = 2, i = 3) aVertexList[i] = tVector end if i = i - 1 end repeat -- Ensure that the transform is a transform aTransform = aPropList[#transform] case ilk(aTransform) of #transform: -- use this as it stands #model: --, #group, #light, #camera: aTransform = aTransform.getWorldTransform() otherwise: aTransform = transform() end case end if tName = plIDs.getaProp(anID) if stringP(tName) then -- The model may already exist. If so, reuse it. tModel = p3DMember.model(tName) end if -- If the model resource exists, and it is destroyed, the model -- will be unable to adopt a new resource. Remove its resource -- proactively. tModelExists = ilk(tModel, #model) if tModelExists then tModel.resource = VOID else -- No face model exists yet with this ID. Create a new name for -- both the model and its resource. tName = me.mGetUniqueName("OS3Debug_Face_") plIDs[anID] = tName end if -- (Re-)create the mesh using the given vertices tResource = me.mCreateFace(tName, aVertexList) if tModelExists then tModel.resource = tResource else tModel = p3DMember.newModel(tName, tResource) end if tShader = p3DMember.shader(tName) if ilk(tShader) <> #shader then tShader = me.mCreatePointShader(tName, #red, aPropList[#color]) end if tModel.shaderList = tShader -- Place the model in worldSpace tModel.transform = aTransform end mShowFace on mCreateFace(me, aName, aVertexList) ------------------------------- -- CALLED by mShowFace() -- INPUT: should be a unique model and resource name -- should be a list of three vertices -- ACTION: Deletes any existing resource with the given name and -- recreates a new one. -------------------------------------------------------------------- p3DMember.deleteModelResource(aName) -- Create a new mesh ready to receive the vertexList, colorList and -- textureCoordinateList data tMeshResource = p3DMember.newMesh(aName, 2, 3) --2 faces, 3 vertices tMeshResource.vertexList = aVertexList tMeshResource.face[1].vertices = [1, 2, 3] tMeshResource.face[2].vertices = [1, 3, 2] -- Create a flat mesh tMeshResource.generateNormals(#flat) tMeshResource.build() return tMeshResource end mCreateFace -- UTILITIES -- on GetTransform(aVector, anUpVector, anAxis, anUpAxis) --------------- -- INPUT: and should be directional vectors. -- aVector is required, but anUpVector may be VOID and -- should not be parallel to aVector. If anUpVector is -- VOID, the vector(0, 1, 0) is assumed. -- and should be a symbols, chosen from -- #x | #y | #z. The two axes must not be the same. If no -- axes are specified, anAxis is assumed to be negative #z -- (aVector is reversed) and anUpAxis is assumed to be #y. -- This makes this method similar to pointAt(), but for -- transforms. -- OUTPUT: Returns a transform at the center of the world with its -- given axis aligned with the given vector. If anUpAxis -- and anUpVector are valid, then the transform is also -- rotated to align the chosen secondary axis, as near as -- possible, with the given vector. -- NOTE: Not completely checked for all axes. anAxis = #y with -- anUpAxis = #x may not be correct. -------------------------------------------------------------------- tTransform = transform() -- Check that the required aVector parameter is a vector... if ilk(aVector) <> #vector then return tTransform -- identity transform end if -- ... and work with a normalized copy of it aVector = aVector.getNormalized() -- Check that the required anAxis parameter is valid, and that it is -- not aligned with anUpAxis case anAxis of #x: tWorldAxis = vector(1, 0, 0) case anUpAxis of #y, #z: -- use up axis otherwise: anUpAxis = #y end case #y: tWorldAxis = vector(0, 1, 0) case anUpAxis of #x, #z: -- use up axis otherwise: anUpAxis = #x end case #z: tWorldAxis = vector(0, 0, 1) case anUpAxis of #x, #y: -- use up axis otherwise: anUpAxis = #y end case otherwise: -- Align the transform with the world's negative -z axis anAxis = #z aVector = -aVector tWorldAxis = vector(0, 0, 1) -- Use a default value for anUpAxis if anAxis is invalid anUpAxis = #y -- Provide a default value fon anUpVector if necessary if ilk(anUpVector) <> #vector then anUpVector = vector(0, 1, 0) end if end case -- If we get here, aVector and anAxis are valid. Rotate tTransform -- as necessary to align it with the given axis. if aVector = tWorldAxis then -- No rotation needed to align with aVector else if aVector = -tWorldAxis then if anAxis = #x then -- Rotate 180° around the zAxis (arbitrarily?) tTransform.rotate(0, 0, 180) else -- Rotate 180° around the xAxis (arbitrarily?) tTransform.rotate(180, 0, 0) end if else -- aVector is not parallel to its world counterpart. -- Determine an axis perpendicular to both aVector and tWorldAxis. tAxis = tWorldAxis.cross(aVector) -- Determine angle between aVector and zAxis tAngle = tWorldAxis.angleBetween(aVector) -- Rotate the transform to align its yAxis with pYAxis tCenter = vector(0, 0, 0) tTransform.rotate(tCenter, tAxis, tAngle) end if -- Align with the up axis only if the input parameters are valid if symbolP(anUpAxis) then -- Check that anUpAxis has a usable value if it is to be used if ilk(anUpVector, #vector) then anUpVector = anUpVector.getNormalized() if anUpVector = aVector or anUpVector = -aVector then return tTransform -- aligned with aVector end if else return tTransform -- aligned with aVector end if else return tTransform -- aligned with aVector end if -- If we get here, we also need to take the up axis into account. -- Align the given up axis of the transform as close as possible -- with anUpAxis, without altering its alignment with aVector. -- Find a vector perpendicular to both aVector and anUpVector... tUpVector = anUpVector.cross(aVector) -- ...then find a vector perpendicular to both aVector and that. -- This new vector will point in the best "up" direction. tUpVector = aVector.cross(tUpVector) -- Determine angle between tVector and tTransform.yAxis case anUpAxis of #x: tAxis = tTransform.xAxis #y: tAxis = tTransform.yAxis #z: tAxis = tTransform.zAxis end case tAngle = tUpVector.angleBetween(tAxis) -- Ensure the rotation is in the correct direction tAxis = tUpVector.cross(tAxis) if tAxis * aVector > 0 then tAngle = -tAngle end if -- Rotate the transform to align its up axis with anUpVector tCenter = vector(0, 0, 0) tTransform.rotate(tCenter, aVector, tAngle) return tTransform end GetTransform on Choose3DMember() -------------------------------------------------- -- CALLED by mInitialize() -- ACTION: Allows the user to select which #shockwave3D member to -- use for debugging, if there is more than one, or warns -- if there are no #shockwave3D members available. -------------------------------------------------------------------- -- Tell the Stage to provide a list of 3D members... t3DMembers = [] -- tell the stage tLastLib = the number of castLibs repeat with tCastLib = 1 to tLastLib tCastName = QUOTE&castLib(tCastLib).name"E tLastMember = the number of members of castLib(tCastLib) repeat with tMemberNum = 1 to tLastMember tMember = member(tMemberNum, tCastLib) if tMember.type = #shockwave3D then tMemberName = tMember.name if tMemberName = "" then tMemberName = tMemberNum else tMemberName = QUOTE&tMemberName"E end if tMemberName = "member("&tMemberName&", "&tCastName&")" t3DMembers.append(tMemberName) end if end repeat end repeat -- end tell -- ... then get the user to choose one. tCount = t3DMembers.count if not tCount then alert \ "There are no Shockwave 3D members in the movie on the Stage." t3DMember = 0 else if tCount = 1 then t3DMember = t3DMembers.getLast() else -- Ask the user to choose tDataList = [ \ #title: "Choose 3D Member", \ #instructions: "Choose a Shockwave 3D member for debugging:", \ #menuItems: t3DMembers, \ #default: t3DMembers[1], \ #OK: "Debug", \ #cancel: "Cancel" \ ] t3DMember = MuiPopupChoice(tDataList) -- in MUI Dialogs end if -- tell the stage -- t3DMember = value(t3DMember) -- end tell return t3DMember end Choose3DMember on ShowTransform(aTransform, aScope, aPrecision) --------------------- -- ACTION: Creates a string representation of a transform suitable -- for displaying in the Message window for debugging -- purposes -- INPUT: should be a transform or an node object -- only has an effect if it is #world and aTransform -- is in fact a node object. In this case, the -- worldTransform of the node is used. If aScope is an -- integer, it may be used for aPrecision. -- may be a positive integer indicating the -- number of figures to show after the decimal point. 5 is -- used by default -- OUTPUT: Returns a string where the transform matrix is displayed -- in neat columns, so long as a mono-spaced font is used. -- If aTransform is not a transform or a node object, -- the error symbol #invalidTransform is returned instead. -------------------------------------------------------------------- vString = RETURN -- output value -- Parameter checking if not integerP(aPrecision) then if integerP(aScope) then aPrecision = aScope end if end if case ilk(aTransform) of #transform: -- continue #group, #model, #light, #camera: case aScope of #world: aTransform = aTransform.getWorldTransform() otherwise: -- #self aTransform = aTransform.transform end case otherwise: return #invalidTransform end case if integerP(aPrecision) then if aPrecision < 0 then aPrecision = 5 end if else aPrecision = 5 end if -- End of parameter checking -- Determine the maximum width of this column vlWidths = [1, 1, 1, 1] -- number of spaces required by each column vlFigures = [] -- number of spaces required by each item -- Determine the number of spaces for each column repeat with i = 1 to 16 vColumn = ((i - 1) mod 4) + 1 vColumnMax = vlWidths[vColumn] vItem = aTransform[i] vNegative = vItem < 0 if vNegative then vFigures = 1 -- for the minus sign else vFigures = 0 end if vItem = bitXor(vItem, 0) -- strips decimal part -- Thanks to Jonas Brink Worsøe for the tip if not vItem then vFigures = vFigures + 1 else repeat while vItem vFigures = vFigures + 1 vItem = vItem / 10 end repeat end if if vNegative then vlFigures[i] = -vFigures -- so -0.00000001 is shown correctly else vlFigures[i] = vFigures end if if vColumnMax < vFigures then vlWidths[vColumn] = vFigures end if end repeat -- Determine how the numbers should display vPrecision = the floatPrecision if integerP(aPrecision) then if aPrecision > 0 then the floatPrecision = aPrecision end if end if -- Create the display string repeat with i = 1 to 16 vColumn = ((i - 1) mod 4) + 1 vItem = string(aTransform[i]) vSpaces = vlFigures[i] -- Ensure that very small negative numbers don't lose their sign if vSpaces < 0 then if vItem.char[1] <> "-" then put "-" before vItem end if vSpaces = -vSpaces end if -- Pad this item with the right number of spaces vSpaceCount = vlWidths[vColumn] - vSpaces vSpaces = " " repeat while vSpaceCount put " " after vSpaces vSpaceCount= vSpaceCount - 1 end repeat put vSpaces&vItem after vString if vColumn = 4 then put RETURN after vString end if end repeat the floatPrecision = vPrecision return vString end ShowTransform