'From Jasmine-rc1 of 7 October 2004 [latest update: #196] on 7 January 2005 at 3:53:38 pm'! "Change Set: GhostFrames Date: 7 January 2005 Author: David A. Smith Ghost frames can be added to a TSpace at render time. They are not actually in the space itself, but are rendered as if they were. Typically, a ghost space will be added by a filter/task portal to display additional objects and content that are not visible via the normal space. Also added a small fix to the wireframe portal to make it align properly."! !TOverlayButtons methodsFor: 'popups' stamp: 'das 1/7/2005 15:50'! makeWireFilter: ptr | win p | p _ TWirePortal meta new. win _ ptr avatar meta makeWindowInFront. win meta contents: p. " p meta linkPortal: win rectBack." self killPopup. ! ! !TPortal methodsFor: 'render' stamp: 'das 1/7/2005 15:42'! render: ogl depth: depth | trans m2 toSpace gt containerPortal portalClip color clipPlanes renderedObjects ac saveSelected saveDoSelect saveMinDistance portalDistance saveForceWire top portalPoint | " This method is the recursive portal renderer. enter refers to the renderer entering the portal. If you want the camera entering check out TPortal>>testEnter: ac." renderedObjects _ 1. "We are also rendering the portal." " ------- We keep track of which portal we are currently working in to avoid a tight recursion error,where the portal re-enters itself to render. It is (somewhat) OK to re-enter from another portal. This is equivalent to a mirror in a mirror situation. Here we need to keep a count of the re-entries.------" toSpace _ toPortal root. toSpace = toPortal ifTrue:[ top _ self. toSpace _ self root. ] "The portal is not installed on the other side!!" ifFalse:[top _ toPortal]. containerPortal _ ogl currentPortal. ogl currentPortal: self. " ------ For rendering the portal ONLY - we turn off depth testing. We just want to write into the stencil buffer here and set the color of the toSpace. Depth testing is turned back on before we recurse into the connected portal.------" gt _ self globalTransform. depth = 1 ifTrue:[ogl glEnable: GLStencilTest.]. ogl glPushMatrix. ogl glMultMatrixf: gt transposed. ogl glDisable: GLDepthTest. " ------ Paint the portal with the color of the TSpace ------ " ogl glDisable: GLLighting. color _ B3DVector3 x: toSpace color red y: toSpace color green z: toSpace color blue. ogl glColor3fv: color. ogl glStencilFunc:GLEqual with:depth-1 with: -1. ogl glStencilOp:GLKeep with:GLKeep with:GLIncr. " ------ Render the stencil here. ------ " saveForceWire _ ogl forceWire. ogl forceWire:false. self renderPortal: ogl. ogl forceWire: saveForceWire. ogl glStencilFunc: GLEqual with: depth with: -1. ogl glStencilOp: GLKeep with: GLKeep with: GLKeep. ogl glEnable: GLDepthTest. ogl glEnable: GLLighting. ogl glPopMatrix. ogl glPushMatrix. " m1 _ self globalTransform." top isPortal ifTrue:[m2 _ top globalMatrixOut.] ifFalse:[m2 _ top globalTransform.]. " ------- For simplicity - and because I can't think of a good reason not to do this - if a portal points back to itself, it is considered to be a mirror. Note that we flip the directions of the polyon tests as well. This is restored at the end. We are inverting the x-coordinate of the matrix here. ------" self = top ifTrue:[ m2 a11: m2 a11 negated. m2 a21: m2 a21 negated. m2 a31: m2 a31 negated. ogl flipFace.]. ac _ ogl camera. portalClip _ac portalClip. ac portalClip: m2. " ------ This math is simply: m1 = trans*m2 m1 * m2**-1 = trans * m2 * m2**-1 m1 * m2**-1 = trans. We then replace the transform matrix of the TCamera with the appropriate transform, which places the TCamera into the correct position in the new space.------" clipPlanes _ ac clipPlanes. ac clipPlanes: (self initClipPlanes: ac globalTransform mirror: ogl isMirror). self = top ifTrue:[ ogl mirrorFlip.]. trans _ m2 composeWith: gt orthoNormInverse. ac globalTransform: (trans composeWith: ac globalTransform). ac pointer ifNotNil:[ saveSelected _ ac pointer copiedSelection. saveMinDistance _ ac pointer minDistance. saveDoSelect _ ac pointer doSelect. (ac pointer pointerPick: self boundSphere) ifTrue:[ portalDistance _ ac pointer selectedDistance. ac pointer minDistance: portalDistance. portalPoint _ ac pointer selectedPoint. ac pointer selected: saveSelected. "restore the current selections" ac pointer selectedPortal: self point: portalPoint. "if we picked a portal" ] ifFalse:[ ac pointer isDown ifTrue:[ ac pointer selectedPortal = self ifTrue:[ (ac pointer frame: self pickPlane: (B3DVector3 new) normal: outVector) ifTrue:[ portalPoint _ ac pointer selectedPoint. ac pointer selected: saveSelected. ac pointer selectedPortalPoint: portalPoint. ]. ]. ]. portalDistance _ Float infinity. ac pointer doSelect: false. ac pointer selected: saveSelected. ]. ac pointer globalTransform: (ac globalTransform composeWith: ac pointer localTransform). ]. renderedObjects _ renderedObjects + (toSpace renderSpace: ogl port: top depth: depth+1 ghostFrame: nil). "<------ Render TSpace here!! -------" ac clipPlanes: clipPlanes. self = top ifTrue:[ogl flipFace. ogl mirrorFlip.]. ogl glPopMatrix. " ------ Just in case we jumped into a portal, restore the stencil depth ------" ogl glStencilFunc: GLLequal with: depth-1 with: -1. ogl glStencilOp: GLKeep with: GLKeep with: GLKeep. "------ Render the front face invisibly to set the z-buffer to ensure nothing bleeds into the portal space from our space. ------" ogl glPushMatrix. ogl glPolygonOffset: 2.1 with: 4.0. ogl glEnable: GLPolygonOffsetFill. ogl glColorMask: 0 with: 0 with: 0 with: 0. ogl glMultMatrixf: gt transposed. ogl forceWire: false. self renderPortal: ogl. ogl forceWire: saveForceWire. ogl glColorMask:GLTrue with: GLTrue with:GLTrue with:GLTrue. ogl glDisable: GLPolygonOffsetFill. ogl glPopMatrix. depth = 1 ifTrue:[ogl glDisable: GLStencilTest.]. ogl currentPortal: containerPortal. ac portalClip: portalClip. ac pointer ifNotNil:[ ac pointer doSelect: saveDoSelect. ac pointer minDistance: saveMinDistance. ac pointer maxDistance: (ac pointer maxDistance min: portalDistance). ]. ^ renderedObjects. ! ! !TSpace methodsFor: 'render' stamp: 'das 1/7/2005 15:43'! doRender: ogl ^ self renderSpace: ogl port: ogl camera depth: 1 ghostFrame: nil. ! ! !TSpace methodsFor: 'render' stamp: 'das 1/7/2005 15:43'! makeThumbnailOf: aFrame using: aCamera "Create a snapshot of the given object using aCamera" | oldChildren ogl | ogl _ CroquetGlobals ogl. ogl glClearColor: 0.0 with: 0.0 with: 0.0 with: 0.0. ogl glClear: (GLColorBufferBit bitOr: (GLDepthBufferBit bitOr: GLStencilBufferBit)). ogl glEnable: GLLighting. ogl camera: aCamera. oldChildren := frameChildren. aCamera inPortal:true. [ frameChildren := OrderedCollection with: aFrame. self renderSpace: ogl port: aCamera depth: 1 ghostFrame: nil. ] ensure:[ frameChildren := oldChildren. ]. ^ogl screenShot: aCamera bounds.! ! !TSpace methodsFor: 'render' stamp: 'das 1/7/2005 15:43'! renderSpace: ogl | delta ac | ac _ ogl camera. "----- test if enter the portal here -----" portalFrames do: [ :pf | "------ is the portal visible? ------" pf visible ifTrue:[ " ------ portal near avatar?-----" delta _ ac avatar globalTransform translation - pf globalTransform translation. delta squaredLength < pf boundSphere radiusSquared ifTrue:[ (pf testEnter: ogl avatar: ac avatar) ifTrue:[^ self.]. ].].]. ^self renderSpace: ogl port: ac depth: 1 ghostFrame: nil. ! ! !TSpace methodsFor: 'render' stamp: 'das 1/7/2005 15:42'! renderSpace: ogl port: viewPort depth: depth ghostFrame: ghost | currentCamTrans currentPointerTrans currentCameraInv cpt ip renderPortals delta renderedObjects tstRays ac | ac _ ogl camera. "------ The TSpace render method makes two passes. The first pass is used to render the non-alpha objects and to find the alpha objects. The alpha objects and their transforms are put into the SortedCollection alphaObjects. This array is sorted by distance using the bounding spheres of the objects and the second render pass using the renderAlpha method is made in using this sorted order from furthest to closest. Ghost frames are interleaved TFrames that render along side the space. Ghost frames can not contain portals or lights - as these are just ignored. ------" "------ reset the rayframes if this is the first time through...." timeStamp ~= ogl timeStamp ifTrue:[ tstRays _ true. timeStamp _ ogl timeStamp. rayFrames do:[ :rf | rf resetSelected.].] ifFalse:[ tstRays _ false.]. renderedObjects _ 0. ac transformClipPlanes. cpt _ ac clipPlanesTransform. currentCamTrans _ ac globalTransform copy. ac pointer ifNotNil:[currentPointerTrans _ ac pointer globalTransform copy.]. currentCameraInv _ currentCamTrans orthoNormInverse. ogl glLoadMatrixf: currentCameraInv transposed. "------ The portals will have to be sorted here.... ------" "------ Render the portals from back to front. ------" ogl maxPortalDepth > depth ifTrue:[ renderPortals _ OrderedCollection new. portalFrames do: [ :pf | (pf ~= ogl currentPortal or:[pf reentrant])ifTrue:[ " ------ is the portal even visible? ------" "xyzzy pf visible: true." pf visible ifTrue:[ " ------ portal facing camera?-----" delta _ ac globalTransform translation - pf globalTransform translation. (delta dot: pf lookAt) > 0 ifTrue:[ "------- is the portal inside the view frustum? ------" pf boundSphere transform: pf globalTransform. (ac testBounds: pf boundSphere) ifTrue:[ pf cameraDistance: delta squaredLength. renderPortals add: pf. ]. "(ac testBounds: pf boundSphere) ifTrue:" ]. "(delta dot: pf lookAt)>0 ifTrue:" ]. "pf visible" ]. "pf~=ogl currentPortal or:[pf reentrant]" ]. "portalFrames do:" (renderPortals asArray sort:[:a1 :a2| a1 cameraDistance < a2 cameraDistance]) do: [:pf | ip _ ac inPortal. ac inPortal: true. renderedObjects _ renderedObjects + (pf render: ogl depth: depth). ac inPortal: ip. " ------ restore the camera for the next go round ------ " ac globalTransform: currentCamTrans. ac clipPlanesTransform: cpt.]. ]. "ogl maxPortalDepth > depth ifTrue:" "------ restore camera position and set up clip planes in this pose.------" ac pointer ifNotNil:[ac pointer globalTransform: currentPointerTrans.]. ogl glLoadMatrixf: currentCameraInv transposed. "------ Once we have rendered all of the subportals, if this is a portal, set its clip plane so that we don't render objects in front of the portal.------" viewPort isPortal ifTrue:[ viewPort enableClipPlane: ogl]. " create a new array " alphaObjects _ OrderedCollection new: 0. " turn on the lights and setup the fog " self setupLights: ogl. fogOn ifTrue:[ self setupFog: ogl. ]. " cull back faces if necessary." self setCull: ogl. tstRays ifTrue:[ self testRays: true.]. "------ Render non-alpha geometry. While we are at it, find the alpha geometry for the next pass. All TFrames know how to do this, including TSpace. TSpace is a root frame (there is no THE root frame in TEA) so we need only call the TSpace >> renderFrame message to render everything inside of this. ------" renderedObjects _ renderedObjects + (self renderFrame: ogl parent: self root: self). ghost ifNotNil:[ renderedObjects _ renderedObjects + (ghost renderFrame: ogl parent: self root: self). ]. "------ If there are any alpha objects, set render mode to alpha blending and make a second pass. Use alphaLength because alphaObjects size may be bigger than the actual objects rendered. ------" (alphaObjects size ~= 0 or:[self forceAlpha])ifTrue:[ self renderSpaceAlpha: ogl transform: currentCamTrans. ]. 0 to: 7 do:[ :index | ogl glDisable: GLLight0 + index.]. ogl glDisable: GLCullFace. viewPort isPortal ifTrue:[viewPort disableClipPlane:ogl.]. fogOn ifTrue:[ ogl glDisable: GLFog.]. self testRays: false. ^ renderedObjects. "Return the number of objects we have rendered."! ! !TWirePortal methodsFor: 'as yet unclassified' stamp: 'das 1/7/2005 15:50'! initialize | view | super initialize. view _ TGroup new. self addChild: view. self linkPortal: view.! ! TSpace removeSelector: #renderSpace:port:depth:!