TFrame


Croquet-Teapot

Comment:

The TFrame class is designed as a heirarchical transformation frame. The rendering engine walks through the frame, performing the appropriate transformation associated with it, then it calls the render methods of the tObjects. Two passes are made through the frame heirarchy. The first is for non-alpha objects, the second for alpha. The base frame is owned by a TSpace object. We render from the root up because we want to minimize setting transforms.


" ***** This is how you rotate an object using a quaternion ball. The base position is stored in the selectedPoint and compared with the pointer selectedPoint.
	pointer event2D shiftPressed ifTrue:[
		pointer frame: self pickSphere: B3DVector3 new radiusSquared: selectedRadiusSquared.
		spin _ self rotFromBallPoints: selectedPoint to: pointer selectedPoint.
		trans _ self translation.
		self translationX: 0.0 y:0.0 z:0.0.
		self localTransform: (self localTransform composeWith: spin).
		self translation: trans.
		]"
" ***** This is how you move an object relative to a specified plane. In this case, it is the plane determined by either the camera or the surface normal when the object is selected. 
	- cameraNorm defines the plane perpendicular to the line of sight of the camera
	- selectedNorm defines the norm of the selected surface of the object.
	- slab frontNorm forces a normal to the front of the slab (for example).
	ifFalse:[
	(pointer frame: self pickPlane: selectedPoint normal: cameraNorm) ifTrue:[
		delta _ selectedPoint - pointer selectedPoint.
		self translation: (self translation - (self orientation localPointToGlobal: delta)).
		^ true.].]."
	^ false.

Hierarchy:

ProtoObject
Object
TObject
TFrame

Summary:

instance variables:

frameChanged frameChildren frameParent globalPosition globalTransform localTransform myBehaviors myEventMap myPlayer myScripts objectName objectOwner singleParent solid stepTime stepperActive stepsOn streamingSound test timeStamp visible

class instance variables:

scriptIcon

Pool:

GLExtConstants OpenGLConstants

methods:

instance class
*JIVE accessing copying debugging error handling
events fileIn/Out frameManagement hierarchy initialize properties render scripts stepping synchronization testing toys transform voice chat yellow-scripts yellow-scripts-compiled yellow-scripts-support
accessing class initialization

Detail:

instance variables:

frameChanged
frameChildren
frameParent
globalPosition
globalTransform
localTransform
myBehaviors
myEventMap
myPlayer
myScripts
objectName
objectOwner
singleParent
solid
stepTime
stepperActive
stepsOn
streamingSound
test
timeStamp
visible

class instance variables:

scriptIcon
InitialValue:
ColorForm(11x11x8)
inferredType:
<ColorForm ?>

instance methods:

*JIVE
addUserCostume: tFrame sketch: aSketch

	self player addUserCostume: tFrame sketch: aSketch
replaceUserCostume: tFrame sketch: aSketch

	self player replaceUserCostume: tFrame sketch: aSketch
setUserCostume: tFrame

	self player setUserCostume: tFrame
userType

	"Answer the 'type' property of the receiver"
	^self class name allButFirst "remove T"

accessing
addViewingParticipant: aParticipant

	^self addViewingParticipants: { aParticipant }.
addViewingParticipants: someParticipants

	teaParty join: someParticipants.
	self frameChildrenDo: [ :fc | fc addViewingParticipants: someParticipants ].
boundSphere


	" This will return a render bounds object if one exists. This is a TBoundSphere (location + radius), which is used to determine if a particular object is inside the clipping planes or for collision detection tests. Otherwise, the object will be assumed to have a local coordinate location of 0,0,0 and a radius of 1.0."
	^ nil.

boundingBox

	"Answer the bounding box for the receiver and its children"
	^ self boundingBox: B3DMatrix4x4 identity.
boundingBox: trans

	"Answer the bounding box for the receiver and its children"
	| box childBox |
	box := self frameBox.
	self frameChildrenDo:[:child|
		childBox := child boundingBox transformedBy: (trans composeWith: child localTransform).
		box := box merge: childBox.
	].
	^box
boundsChanged


	" Does nothing"
boundsDepth: depth


	frameChildren ifNotNil:[
		frameChildren do:[:fc | fc boundsDepth: depth].].
changed: frame


	" Does nothing, just forward on to parent."
	self parent ifNotNil:[self parent changed: frame.]
childChanged
	
	
	self childChanged: self.
childChanged: frame


	" Does nothing, just forward on to parent."
	self parent ifNotNil:[self parent changed: frame.]
colorize: color


	"used by buttons and things - this just keeps us out of trouble for now, though I may need it later..."
compositeBoundSphere

	"Answer either my own or a composition of my children's bound spheres"
	| children bSphere |
	children := OrderedCollection new.
	self boundSpheresDo:[:bs|
		bSphere := bs union: bSphere.
		children add: bs.
	].
	children size = 0 ifTrue:[^nil].
	children size = 1 ifTrue:[^children first].
	bSphere children: children.
	bSphere frame: self.
	bSphere transform: self globalTransform.
	^bSphere
disableCaching.

disableCachingTree
.

	self do:[ :f | f disableCaching ].
distanceTo: aFrame

	^(self globalPosition - aFrame globalPosition) length
do: block


	block value: self.
	frameChildren ifNotNil:[ 
		frameChildren do:[ :fc | fc do: block].].
extent

	"Answer the bounding box for the receiver and its children"
	^ self boundingBox extent.
extent: ext

	"Does nothing"
find: blockTest

	| list |
	blockTest ifNil:[^ nil].
	list _ OrderedCollection new.
	self find: blockTest into: list.
	^ list.
find: blockTest into: list


	frameChildren ifNotNil:[ 
		frameChildren do:[ :fc | fc find: blockTest into: list].].
	(blockTest value: self) ifTrue:[ list add: self].
frameBox

	"Answer the local bounding box of this frame"
	^TBox origin: 0@0@0 corner: 0@0@0
frameChanged


	" This guarantees that if we make a change in a parent frame, all of the children are aware and can deal with it properly."
	frameChanged _ true.
	frameChildren ifNotNil:[
		frameChildren do:[ :child | child frameChanged ].].
	myEventMap ifNotNil:[self signal: #frameChanged]. "to inform interested parties"
	myPlayer ifNotNil:[myPlayer signal: #frameChanged].
frameChildren


	^ frameChildren.
frameChildrenDo: aBlock

	frameChildren ifNil:[^self].
	^frameChildren do: aBlock
fullBright: bool


	frameChildren ifNotNil:[frameChildren do:[:fc | fc fullBright: bool]].
getFrameChanged


	^ frameChanged.
globalToLocal: anObject

	^self globalTransform inverseTransformation localPointToGlobal: anObject
hasAlpha

	" Does this object have an alpha channel to render? Return true if it does."
	^ false.
inertiaTensor

	
	^ nil.
localToGlobal: anObject

	^self globalTransform localPointToGlobal: anObject
locator

	"Return a locator telling us where to find this guy on the net"
	^nil
lookAt


	^ self globalTransform column3.
lookSide


	^ self globalTransform column1.
lookUp


	^ self globalTransform column2.
material

	frameChildren isEmptyOrNil ifTrue:[^nil].
	^frameChildren anyOne material
material: mat

	frameChildren ifNil:[^self].
	frameChildren do:[:each| each material: mat].
materialAlpha: ignored

	"backstop"
objectName


	^objectName
objectName: oName


	objectName _ oName.
objectOwner


	objectOwner ifNil:[
		frameParent ifNotNil:[^ frameParent objectOwner.].].
	^ objectOwner.
objectOwner: oOwner


	objectOwner _ oOwner.
	self isComponent ifFalse:[
		frameChildren ifNotNil:[
			frameChildren do:[ :child | child objectOwner: oOwner. ].].].
octreeBox

	| box childBox bs |

	box _ TBox new.
	frameChildren ifNotNil:[
		frameChildren do:[ :fc | 	
			childBox _ fc octreeBox.
			childBox ifNotNil:[
				box _ box unionBox: childBox.].
			].
		].
	bs _ self boundSphere.
	bs ifNotNil:[
		bs transform: self globalTransform.
		box growVertex: bs globalPosition.
		].
	box min x > box max x ifTrue:[^ nil ].
	^ box.
octreeRadius

	| rad childRad |

" Find the max bound sphere radius of the tree. This is used for quadtree/octree construction "
	self boundSphere ifNotNil:[ rad _ self boundSphere radius.] ifNil:[ rad _ 0 ].
	frameChildren ifNotNil:[
		frameChildren do:[ :fc | 	
			
			childRad _ fc octreeRadius.
			rad _ rad max: childRad.
			].
		].

	^ rad.
octreeSieve: octree

	| bs |

	bs _ self boundSphere.
	bs ifNotNil:[ 		
		bs transform: self globalTransform.
		octree add: bs. 
		].
	frameChildren ifNotNil:[
		frameChildren do:[ :fc | 	fc octreeSieve: octree. ].
		].
parentChanged


	self parentChanged: self.
parentChanged: frame


	" Does nothing, just forward on to children."
	frameChildren ifNotNil:[frameChildren do:[:fc | fc parentChanged: frame].].
scriptIcon

	^self class scriptIcon
selected: fc


"Inform the parent frame that this frame has been selected"
	frameParent selected: fc.
showFrame


" TFrame >> showFrame recursively climbs the frame's tree and outputs it the Transcript."
	self showFrame: 0.
showFrame: depth


	1 to: depth do:[ :i | Transcript show:'---|'].
	Transcript show: self objectName; cr.
	self frameChildren ifNotNil:[
		frameChildren do:[ : fc |
			fc showFrame: depth+1.].].
solid


	^ solid.
solid: bool


	solid _ bool.
solidTree: bool


	self do:[ :f | f solid: bool].
solidVisibleTree: bool


	self solidTree: bool.
	self visibleTree: bool.
sphereTree


	| bs |
	
	bs _ self boundSphere.
	bs ifNotNil:[
		frameChildren ifNotNil:[ frameChildren do:[ :fc | bs _ bs union: (fc sphereTree).].].
		^ bs.
		].
	frameChildren ifNotNil:[
		frameChildren do:[ :fc |
			bs ifNil:[ bs _ fc sphereTree. ] 
				ifNotNil: [bs _ bs union: (fc sphereTree). ].
		].
	^ bs.
	].
	^ nil.
test


	^ test.
test: tst


	test _ tst.
transparency: trans

	
	frameChildren ifNotNil:[
		frameChildren do:[ :fc | fc transparency: trans].].
viewingParticipants

	^#(). "for frame roots that aren't spaces"
visible


	^ visible.
visible: bool


	visible _ bool.
visibleTree


	self visible ifTrue:[^ true].
	frameChildren ifNotNil:[
		frameChildren do:[ :fc | fc visibleTree ifTrue:[^ true].].].
	^ false.
visibleTree: bool


	self do:[ :f | f visible: bool].

copying
copy

	^self shallowCopy postCopy
postCopy

	
	super postCopy.
	localTransform _ localTransform copy.
" Only make copies ofthe frame heirarchy. Everything else stays as-is."
	frameChildren ifNotNil:[
		frameChildren := frameChildren collect:[:fc|
			fc isTexture ifTrue:[fc] ifFalse: [fc copy]
		].
	].
	^self

debugging
makeInspector: camera

	| win teaWorld trans browser tmorph htp |

" This is just an example of how to make new objects remotely. You should not do it this way. There will be a more robust mechanism later on."

	tmorph _ TMorphic new initializeOpaque: true extent: 256@256.
	htp _ Croquet world homeTeaParty.	
	teaWorld _ (htp global: #TMorphMonitor) new initializeWithWorld: nil extent: tmorph targetExtent.
	browser _ Inspector openAsMorphOn: self withLabel: self name.
	browser beSticky.
	teaWorld color: browser paneColor.
	teaWorld addMorphCentered: browser.
	browser expandBoxHit.
	teaWorld eventsTo: tmorph.	

	win _ TWindow new.
	trans _ camera translation - (camera lookAt *6).
	win translation: trans.
	win rotationAroundY: camera yaw.
	win contents: tmorph.
	win isBrowser: true.
	camera root addChild: win.
	^ win.

events
collidesInto: aFrame

	"Answer whether I collide into the given frame"
	| mySphere itsSphere |
	mySphere := self compositeBoundSphere ifNil:[^false].
	itsSphere := aFrame compositeBoundSphere ifNil:[^false].
	^mySphere collideSphere: itsSphere
dropFiles: aFile pointer: aPointer

	"subclasses can accept the dropped file if they want"
	aFile close. "we close it, since we don't know what to do with it"
event2D: event2D

handlesEvent2D


	^ false.
handlesKeyboard: pointer

"Do I want to receive the keyboard events -  keyDown, keyUp?  The default response is false." 
^ false.
handlesPointerDown: pointer

	"Do I want to receive pointerDown events (pointerDown:, pointerMove:, pointerUp:)?"
	^self handlesAnyOf: #(pointerDown pointerMove pointerUp).
handlesPointerOver: pointer

	"Do I want to receive the pointerOver events - pointerEnter:, pointerLeave:, and pointerOver: when the button is up and the hand is empty?  The default response is false." 
	^self handlesAnyOf: #(pointerEnter pointerOver pointerLeave)
keyDown: pointer

	(self handlesEvent: #keyDown) ifFalse:[^false].
	^self signal: #keyDown with: pointer
keyStroke: pointer

	(self handlesEvent: #keyStroke) ifFalse:[^false].
	^self signal: #keyStroke with: pointer
keyUp: pointer

	(self handlesEvent: #keyUp) ifFalse:[^false].
	^self signal: #keyUp with: pointer
pointerDown: pointer

	self signal: #pointerDown with: pointer.
pointerEnter: pointer

	self signal: #pointerEnter with: pointer.
pointerLeave: pointer

	self signal: #pointerLeave with: pointer.
pointerMove: pointer
 
	self signal: #pointerMove with: pointer.
pointerOver: pointer

	self signal: #pointerOver with: pointer.
pointerUp: pointer
 
	self signal: #pointerUp with: pointer.
wantsBlueButton

	^false

fileIn/Out
postImportFrom: importer

	super postImportFrom: importer.
	frameChanged _ true. 
	frameChildren ifNotNil:[
		frameChildren do:[:child| child setFrameParent: self].
	].
	"this seems like a bug: self isComponent ifTrue:[self startStepping]."
prepareToExportOn: exporter

	super prepareToExportOn: exporter.
	frameParent := nil.
	frameChanged := nil.
	timeStamp := nil.
	globalTransform := nil.
	streamingSound := nil.
	"This should not be necessary but apparently there is a problem with the spinners otherwise..."
	localTransform := localTransform clone.
	frameChildren ifNotNil:[
		frameChildren := frameChildren reject:[:tframe| tframe isCamera].
	].
setFrameParent: fParent

	frameParent := fParent.

frameManagement
addLightFrame: litefrm


	frameParent ifNotNil:[frameParent addLightFrame: litefrm.]
addLightFrames


	" When a frame is added as a child, any lights included in the heirarchy are reported to the base TRoom."
	self isLight ifTrue:[ self addLightFrame: self].
	frameChildren ifNotNil: [frameChildren do:[ :fc | fc addLightFrames ].].
	
addPortalFrame: prtlFrm


	frameParent ifNotNil:[frameParent addPortalFrame: prtlFrm.]
addPortalFrames


	" When a frame is added as a child, any portals included in the heirarchy are reported to the base TRoom."
	self isPortal ifTrue:[ self addPortalFrame: self].
	frameChildren ifNotNil: [frameChildren do:[ :fc | fc addPortalFrames ].].
	
addRayFrame: rayfrm


	frameParent ifNotNil:[frameParent addRayFrame: rayfrm.]
addRayFrames


	" When a frame is added as a child, any rays included in the heirarchy are reported to the base TSpace."
	self isRay ifTrue:[ self addRayFrame: self].
	frameChildren ifNotNil: [frameChildren do:[ :fc | fc addRayFrames ].].
	
forceGlobalToLocal


"This is used by TMesh when the imported vertices are pre-transformed, and all of the nodes are in global coordinates and we want to put them back into their untransformed state. This occurs with the 3DS Max ASE files."

	frameChildren ifNotNil:[ frameChildren do:[ :fc | fc forceGlobalToLocal.]].
	frameParent ifNotNil:[
		self localTransform: (
			frameParent localTransform orthoNormInverse composeWith: self localTransform).
		].
removeLightFrame: litefrm


	frameParent ifNotNil:[frameParent removeLightFrame: litefrm.]
removeLightFrames


	" When a frame is disconnected from its parent, we need to remove the references to the lights in the TRoom."
	self isLight ifTrue:[self removeLightFrame: self.].
	frameChildren ifNotNil:[frameChildren do:[ :fc | fc removeLightFrames ].].
	
removePortalFrame: prtlFrm


	frameParent ifNotNil:[frameParent removePortalFrame: prtlFrm.]
removePortalFrames


	" When a frame is disconnected from its parent, we need to remove the references to the lights in the TRoom."
	self isPortal ifTrue:[self removePortalFrame: self.].
	frameChildren ifNotNil:[frameChildren do:[ :fc | fc removePortalFrames ].].
	
removeRayFrame: rayfrm


	frameParent ifNotNil:[frameParent removeRayFrame: rayfrm.]
removeRayFrames


	" When a frame is disconnected from its parent, we need to remove the references to the rays in the TSpace."
	self isRay ifTrue:[self removeRayFrame: self.].
	frameChildren ifNotNil:[frameChildren do:[ :fc | fc removeRayFrames ].].
	
replaceChild: oldChild with: newChild

	| |
	oldChild removeLightFrames.
	oldChild removePortalFrames.	
	oldChild removeRayFrames.
	frameChildren replace: oldChild with: newChild.
	oldChild isComponent ifTrue:[ oldChild stopStepping.].
	oldChild parent: nil.
	newChild isComponent ifTrue: [ newChild startStepping. ].
	newChild parent: self.
	newChild addLightFrames.
	newChild addPortalFrames.
	newChild addRayFrames.


hierarchy
addChild: child


	frameChildren ifNil:[frameChildren _ OrderedCollection new.].

"If child singleParent is true, then we can not add this frame until we have removed it from a previous parent."

	child singleParent ifTrue:[ 
		child parent ifNotNil:[child parent removeChild: child].
		self singleParent: true.].

	"make sure child and all its subframes are informed of all viewers so they can be added to their tea parties"
	child addViewingParticipants: self root viewingParticipants.

	frameChildren add: child.
	child isComponent ifTrue: [ child startStepping].
	child parent: self.
	child addLightFrames.
	child addPortalFrames.
	child addRayFrames.
	myPlayer ifNotNil:[myPlayer signal: #structureChanged].
boundSpheresDo: aBlock

	| bSphere |
	bSphere := self boundSphere.
	bSphere ifNotNil:[^aBlock value: bSphere].
	frameChildren ifNil:[^self].
	self frameChildren do:[:each| each boundSpheresDo: aBlock].
child: index


	^frameChildren at: index.
collapse


"Forces all frames to identity transform while retaining translation, and pushes the previous transform to the frame children. This is used primarily for meshes and their supporting groups."
	| orient trans |

	trans _ self translation.
	orient _ self orientation.
	frameChildren ifNotNil:[ 
		frameChildren do:[ :fc |  
			fc localTransform: (orient composeWith: fc localTransform).
			fc collapse.
		].
	].
	self localTransform: B3DMatrix4x4 identity.
	self translation: trans.
currentParent: fParent

	
	frameParent _ fParent.
destroy

	frameParent ifNotNil:[frameParent removeChild: self].
doesNotUnderstand: aMessage

	"Check for new user messages"
Transcript cr; show: 'Error: '; show: self; show:' '; show: aMessage.
	(self compileYellowScript: aMessage) ifTrue:[^aMessage sentTo: self].
	^super doesNotUnderstand: aMessage
hasChild: child


	frameChildren ifNotNil:[
		frameChildren do:[:fc | 
			fc = child ifTrue:[^ true ].].].
	^ false.
insertFrame: frm


	| parent |

	self parent ifNotNil:[
		parent _ self parent.
		parent removeChild: self.
		parent addChild: frm.
		].
	frm addChild: self.
isChild: parent


	^ parent hasChild: self.
parent

	
	 ^frameParent.
parent: fParent

	
	self frameChanged.
	frameParent _ fParent.
prune

"
	frameChildren ifNotNil:[
		frameChildren do:[ :fc | fc prune ifTrue:[
			self removeChild: fc.].].
		frameChildren size = 0 ifTrue:[frameChildren _ nil].
		].
	(self class = TFrame and:[ frameChildren = nil ])ifTrue:[^true].
	^ false."
removeAll

	| child |
	
	frameChildren ifNil:[^nil.].

	[frameChildren size = 0] whileFalse:[
		child _ frameChildren at: 1.
		frameChildren removeAt: 1.
		child removeLightFrames.
		child removePortalFrames.
		].
	frameChildren _ nil.
removeChild: child

	| |
	child removeLightFrames.
	child removePortalFrames.	
	child removeRayFrames.
	frameChildren ifNotNil:[frameChildren remove: child ifAbsent:[].].
	child isComponent ifTrue:[ child stopStepping. ].
	child parent: nil.
	myPlayer ifNotNil:[myPlayer signal: #structureChanged].
removeSelf


	self parent ifNotNil:[
		self parent removeChild: self.].
root


	frameParent ifNil:[^ self] ifNotNil:[^ frameParent root.].
singleParent

	
" singleParent forces a frame to only have one parent. Most frames enable multiple parents to easily enable instancing of the objects. However, certain object types such as lights and cameras can NOT be instanced. If singleParent is set to true, if a frame is added as a child to a new parent, it will be removed from the previous parent frame first."

	 ^singleParent.
singleParent: bool

	
	 singleParent _ bool.
transferTo: frame


	self parent ifNotNil:[ self parent removeChild: self].
	frame addChild: self.

initialize
initBounds

	frameChildren ifNotNil:[
		frameChildren do:[:fc | fc initBounds].].
initialize

	frameChanged _ true. 
	localTransform _ B3DMatrix4x4 identity.
	globalTransform _ nil.
	frameChildren _ nil.
	self solid: true.
	singleParent _ false.
	self visible: true.
	self objectOwner: self.
	stepsOn _ false.
	stepperActive _ false.
	stepTime _ 80.0.
	test _ 0.
	^self

properties
fullName

	^self getName, ', (', self class name,')'
getAlpha

	^1.0
getBoundingBox

	^self boundingBox
getChildren

	^Array streamContents:[:s| self frameChildrenDo:[:child| s nextPut: child player]]
getColor

	^Color white
getDepth

	^self getBoundingBox extent z
getHeight

	^self getBoundingBox extent y
getMaterial

	^self material
getName

	^objectName ifNil:[self class name allButFirst withFirstCharacterDownshifted]
getOpacity

	^1.0 - self getAlpha
getOrientation

	^B3DRotation identity
getPointOfView


	^self globalTransform
getPosition


	^self globalTransform translation
getSolid

	^self solid
getSpecularColor

	^Color black
getTexture

	^nil
getUrl

	^'http://foo.bar.com/'
getValueOf: varName

	"For dynamic lookup - answer the player responding to varName.
	TODO: Put in a cache here."
	frameChildren do:[:any| any getName = varName ifTrue:[^any player]].
	^nil
getVisible

	^self visible
getWidth

	^self getBoundingBox extent x
printOn: aStream

	aStream nextPutAll: self fullName.
setAlpha: alphaValue

	self frameChildrenDo:[:each| each setAlpha: alphaValue].
	"self setProperty: #alpha toValue: alphaValue."
setBoundingBox: newBox

	self scaleBy: (newBox extent / self getBoundingBox extent).
	self translation: newBox min.
setChildren: children

	self frameChildrenDo:[:each| self removeChild: each].
	children do:[:each| self addChild: each].
setColor: newColor

	self frameChildrenDo:[:child| child setColor: newColor].
	"self setProperty: #color toValue: newColor."
setDepth: newDepth

	^self scaleBy: 1@1@(newDepth / self getDepth)
setHeight: newHeight

	^self scaleBy: 1 @ (newHeight / self getHeight) @ 1
setMaterial: mat

	self material: mat.
	self frameChildrenDo:[:child| child setMaterial: mat].
setName: aString

	objectName := aString asString.
setOpacity: aNumber

	^self setAlpha: 1.0 - aNumber
setOrientation: aRotation

setPointOfView: pov

setPosition: pos

	self translation: self translation + (self globalToLocal: pos)
setSolid: isSolid

	self solid: isSolid.
	self frameChildrenDo:[:child| child setSolid: isSolid].
setSpecularColor: newColor

	self frameChildrenDo:[:child| child setSpecularColor: newColor].
	"self setProperty: #specularColor toValue: newColor."
setTexture: aTexture

setUrl: aString

setVisible: vis

	self visible: vis.
	self frameChildrenDo:[:child| child setVisible: vis].
setWidth: newWidth

	^self scaleBy: (newWidth / self getWidth) @ 1 @ 1

render
makeThumb: ogl


	| rect thumb vis|
	vis _ self visible.
	self visible: false."avoid recursion."
	rect _(0@0 corner: 128@96).
	ogl renderWait:[thumb _ self root snapShot: ogl rectangle: rect atLoc: self globalTransform view: 45.0.].
	self visible: vis.
	^ thumb.
pick: pointer


^ false
pickFloor: pointer


^ self pick: pointer.
render: ogl


	ogl glDisable: GLLighting;
	" This is used to render solid objects (if any). "
		glColor3fv: #(1.0 0 0)asFloatArray;
		glBegin: GLLineStrip;
    
			glVertex3fv:#(0.0 0.0 0.0)asFloatArray;
    
			glVertex3fv:#(1.0 0.0 0.0)asFloatArray;
    	
			glVertex3fv:#(0.75 0.25 0.0)asFloatArray;
    
			glVertex3fv:#(0.75 -0.25 0.0)asFloatArray;
    
			glVertex3fv:#(1.0 0.0 0.0)asFloatArray;
    
			glVertex3fv:#(0.75 0.0 0.25)asFloatArray;
    
			glVertex3fv:#(0.75 0.0 -0.25)asFloatArray;
    
			glVertex3fv:#(1.0 0.0 0.0)asFloatArray;
    
		glEnd;
    

		glColor3fv: #(0.0 1.0 0)asFloatArray;
		glBegin: GLLineStrip;
    
			glVertex3fv:#(0.0 0.0 0.0)asFloatArray;
    
			glVertex3fv:#(0.0 1.0 0.0)asFloatArray;
    
			glVertex3fv:#(0.0 0.75 0.25)asFloatArray;
    
			glVertex3fv:#(0.0 0.75 -0.25)asFloatArray;
    
			glVertex3fv:#(0.0 1.0 0.0)asFloatArray;
    
			glVertex3fv:#(0.25 0.75 0.0)asFloatArray;
    
			glVertex3fv:#(-0.25 0.75 0.0)asFloatArray;
    
			glVertex3fv:#(0.0 1.0 0.0)asFloatArray;
    
		glEnd;
    

		glColor3fv: #(0.0 0.0 1.0)asFloatArray;
		glBegin: GLLineStrip;
    
			glVertex3fv:#(0.0 0.0 0.0)asFloatArray;
    
			glVertex3fv:#(0.0 0.0 1.0)asFloatArray;
    
			glVertex3fv:#(0.25 0.0 0.75)asFloatArray;
   
			glVertex3fv:#(-0.25 0.0 0.75)asFloatArray;
    
			glVertex3fv:#(0.0 0.0 1.0)asFloatArray;
    
			glVertex3fv:#(0.0 0.25 0.75)asFloatArray;
    
			glVertex3fv:#(0.0 -0.25 0.75)asFloatArray;
    
			glVertex3fv:#(0.0 0.0 1.0)asFloatArray;
    
		glEnd;
	glEnable: GLLighting.
render: ogl box: box

	|  |

	self render: ogl cube: box location: box center scale: 1.0.
render: ogl cube: cube location: loc scale: scale

	| dx dy dz x y z |

" This message is used only to make sure a frame is actually drawing something. In this case, I draw a cube. "
	dx _ scale/2.0.
	dy _ scale/2.0.
	dz _ scale/2.0.
	x _ loc x.
	y _ loc y.
	z _ loc z.

	ogl glBegin: GLQuads;
			glNormal3f: 0.0 with: 0.0 with: 1.0;
			glTexCoord2f:0.0 with:1.0;	glVertex3f: x+(dx negated) with: y+(dy negated) with: z+dz;
			glTexCoord2f:1.0 with:1.0;	glVertex3f: x+dx with: y+(dy negated) with: z+dz;
			glTexCoord2f:1.0 with:0.0;	glVertex3f: x+dx with: y+dy with: z+dz;
			glTexCoord2f:0.0 with: 0.0;	glVertex3f: x+(dx negated) with: y+dy with: z+dz.

		dz _ dz negated.
		ogl glNormal3f: 0.0 with: 0.0 with: -1.0;
			glTexCoord2f:1.0 with: 0.0;	glVertex3f: x+(dx negated) with: y+dy with: z+dz;
			glTexCoord2f:0.0 with:0.0;	glVertex3f: x+dx with: y+dy with: z+dz;
			glTexCoord2f:0.0 with:1.0;	glVertex3f: x+dx with: y+(dy negated) with: z+dz;
			glTexCoord2f:1.0 with:1.0;	glVertex3f: x+(dx negated) with: y+(dy negated) with: z+dz.

		dz _ dz negated.

		ogl glNormal3f: 1.0 with: 0.0 with: 0.0;
			glTexCoord2f:0.0 with:1.0;	glVertex3f: x+dx with: y+(dy negated) with: z+dz;
			glTexCoord2f:1.0 with:1.0;	glVertex3f: x+dx with: y+(dy negated) with: z+(dz negated);
			glTexCoord2f:1.0 with: 0.0;	glVertex3f: x+dx with: y+dy with: z+(dz negated);
			glTexCoord2f:0.0 with: 0.0;	glVertex3f: x+dx with: y+dy with: z+dz.
		dx _ dx negated.

			ogl glNormal3f: -1.0 with: 0.0 with: 0.0;
			glTexCoord2f:1.0 with: 0.0;	glVertex3f: x+dx with: y+dy with: z+dz;
			glTexCoord2f:0.0 with:0.0;	glVertex3f: x+dx with: y+dy with: z+(dz negated);
			glTexCoord2f:0.0 with:1.0;	glVertex3f: x+dx with: y+(dy negated) with: z+(dz negated);
			glTexCoord2f:1.0 with:1.0;	glVertex3f: x+dx with: y+(dy negated) with: z+dz.
		dx _ dx negated.

		ogl glNormal3f: 0.0 with: 1.0 with: 0.0;
			glTexCoord2f:1.0 with: 0.0;	glVertex3f: x+dx with: y+dy with: z+dz;
			glTexCoord2f:0.0 with:0.0;	glVertex3f: x+dx with: y+dy with: z+(dz negated);
			glTexCoord2f:0.0 with:1.0;	glVertex3f: x+(dx negated) with: y+dy with: z+(dz negated);
			glTexCoord2f:1.0 with:1.0;	glVertex3f: x+(dx negated) with: y+dy with: z+dz.

		dy _ dy negated.

		ogl	glNormal3f: 0.0 with: -1.0 with: 0.0;
			glTexCoord2f:0.0 with:1.0;	glVertex3f: x+(dx negated) with: y+dy with: z+dz;
			glTexCoord2f:1.0 with:1.0;	glVertex3f: x+(dx negated) with: y+dy with: z+(dz negated);
			glTexCoord2f:1.0 with:0.0;	glVertex3f: x+dx with: y+dy with: z+(dz negated);
			glTexCoord2f:0.0 with: 0.0;	glVertex3f: x+dx with: y+dy with: z+dz.

	ogl glEnd.
render: ogl cube: box scale: scale


	self renderCube: ogl location: 0.0@0.0@0.0 scale: scale.

render: ogl sphere: bndSphr segments: segments

	| position radius ringSin ringCos pi2 seg2 vert rts rbs rtc rbc ax u du v dv |
	position _ bndSphr localPosition.
	radius _ bndSphr radius.
	seg2 _ 1+ (segments * 2) .
	ringSin _ FloatArray ofSize: seg2.
	ringCos _ FloatArray ofSize: seg2.
	vert _ B3DVector3 new.
	pi2 _ Float pi *2.0.

	1 to: seg2-1 do:[ :index | 
		ax _ ((index) * pi2)/ (seg2-1).
		ringSin at:index put: ax sin.
		ringCos at:index put: ax cos.].
	ringSin at: seg2 put: (ringSin at: 1).
	ringCos at: seg2 put: (ringCos at: 1).

	rts _ 0.0.
	rtc _ 1.0.
	rbs _ringSin at: 1.
	rbc _ ringCos at: 1.
	du _ 1.0/(seg2-1).
	v _ 0.0.
	dv _ 1.0/segments.
	1 to: segments do:[ :iv |
		ogl glBegin: GLTriangleStrip.
		u _ 0.0.
		1 to: seg2 do: [ :ih | 
			vert x: rts*(ringSin at: ih) y: rtc z: rts*(ringCos at: ih).
			ogl glNormal3fv: vert;
				glTexCoord2f: u with: v;
				glVertex3fv:  (vert * radius)+ position.

			vert x: rbs*(ringSin at: ih) y: rbc z: rbs*(ringCos at: ih).
			ogl glNormal3fv: vert;
				glTexCoord2f: u with: v+dv;
				glVertex3fv:  (vert * radius)+ position.

			u_ u+du.].
		v _ v+dv.
		rts _ rbs.
		rtc _ rbc.
		rbs _ ringSin at: iv+1.
		rbc _ ringCos at: iv+1.
		ogl glEnd.].

renderAlpha: ogl


	"This is used to render transparent ( alpha blended ) objects."
renderCube: ogl location: loc scale: scale

	| dx dy dz x y z |

" This message is used only to make sure a frame is actually drawing something. In this case, I draw a cube. "
	dx _ scale/2.0.
	dy _ scale/2.0.
	dz _ scale/2.0.
	x _ loc x.
	y _ loc y.
	z _ loc z.

	ogl glBegin: GLQuads;
			glNormal3f: 0.0 with: 0.0 with: 1.0;
			glTexCoord2f:0.0 with:1.0;	glVertex3f: x+(dx negated) with: y+(dy negated) with: z+dz;
			glTexCoord2f:1.0 with:1.0;	glVertex3f: x+dx with: y+(dy negated) with: z+dz;
			glTexCoord2f:1.0 with:0.0;	glVertex3f: x+dx with: y+dy with: z+dz;
			glTexCoord2f:0.0 with: 0.0;	glVertex3f: x+(dx negated) with: y+dy with: z+dz.

		dz _ dz negated.
		ogl glNormal3f: 0.0 with: 0.0 with: -1.0;
			glTexCoord2f:1.0 with: 0.0;	glVertex3f: x+(dx negated) with: y+dy with: z+dz;
			glTexCoord2f:0.0 with:0.0;	glVertex3f: x+dx with: y+dy with: z+dz;
			glTexCoord2f:0.0 with:1.0;	glVertex3f: x+dx with: y+(dy negated) with: z+dz;
			glTexCoord2f:1.0 with:1.0;	glVertex3f: x+(dx negated) with: y+(dy negated) with: z+dz.

		dz _ dz negated.

		ogl glNormal3f: 1.0 with: 0.0 with: 0.0;
			glTexCoord2f:0.0 with:1.0;	glVertex3f: x+dx with: y+(dy negated) with: z+dz;
			glTexCoord2f:1.0 with:1.0;	glVertex3f: x+dx with: y+(dy negated) with: z+(dz negated);
			glTexCoord2f:1.0 with: 0.0;	glVertex3f: x+dx with: y+dy with: z+(dz negated);
			glTexCoord2f:0.0 with: 0.0;	glVertex3f: x+dx with: y+dy with: z+dz.
		dx _ dx negated.

			ogl glNormal3f: -1.0 with: 0.0 with: 0.0;
			glTexCoord2f:1.0 with: 0.0;	glVertex3f: x+dx with: y+dy with: z+dz;
			glTexCoord2f:0.0 with:0.0;	glVertex3f: x+dx with: y+dy with: z+(dz negated);
			glTexCoord2f:0.0 with:1.0;	glVertex3f: x+dx with: y+(dy negated) with: z+(dz negated);
			glTexCoord2f:1.0 with:1.0;	glVertex3f: x+dx with: y+(dy negated) with: z+dz.
		dx _ dx negated.

		ogl glNormal3f: 0.0 with: 1.0 with: 0.0;
			glTexCoord2f:1.0 with: 0.0;	glVertex3f: x+dx with: y+dy with: z+dz;
			glTexCoord2f:0.0 with:0.0;	glVertex3f: x+dx with: y+dy with: z+(dz negated);
			glTexCoord2f:0.0 with:1.0;	glVertex3f: x+(dx negated) with: y+dy with: z+(dz negated);
			glTexCoord2f:1.0 with:1.0;	glVertex3f: x+(dx negated) with: y+dy with: z+dz.

		dy _ dy negated.

		ogl	glNormal3f: 0.0 with: -1.0 with: 0.0;
			glTexCoord2f:0.0 with:1.0;	glVertex3f: x+(dx negated) with: y+dy with: z+dz;
			glTexCoord2f:1.0 with:1.0;	glVertex3f: x+(dx negated) with: y+dy with: z+(dz negated);
			glTexCoord2f:1.0 with:0.0;	glVertex3f: x+dx with: y+dy with: z+(dz negated);
			glTexCoord2f:0.0 with: 0.0;	glVertex3f: x+dx with: y+dy with: z+dz.

	ogl glEnd.
renderCube: ogl scale: scale


	self renderCube: ogl location: 0.0@0.0@0.0 scale: scale.

renderFrame: ogl parent: parent root: root


	| count childCount globalTrans ac |

" I am running a parallel set of transforms here. Why? Because I am told the OGL is inefficient at reading the current transform. Easy to change."

" Any given frame can have multiple parents. This means that the frame can't keep a static reference to the parent frame. This must be dynamic, essentially filled out at render time. This allows for pure -instancing- of frame objects. That is, the same frame and all of its contents can easily be in multiple locations in the render tree without significant additional bookkeeping. It also means that when an object DOES need to refer to its parent or the space frame (read -root- for traditional approaches), this can ONLY be done at render-time via this mechanism. It also means that a child really must not modify the parent directly, as the parent has other responsibilities."

" #renderFrame: must return the number of objects that were rendered - failure to do so will just crash."

	count _ 0.
	(visible or:[frameChildren notNil])ifFalse:[^ 0 ].
	
	self parent ~= parent ifTrue:[ self isSpace ifFalse:[self frameChanged. ]].

	self currentParent: parent.

	" apply the local transform to this matrix "
	frameChanged ifTrue:[
		self globalTransform: (parent globalTransform composeWith: localTransform).].
	ogl glPushMatrix. 
	ogl glMultMatrixf: localTransform transposed. 
	self boundSphere ifNotNil:[
		self boundSphere transform: globalTransform.
		solid ifTrue:[ root testRayFrames: self boundSphere.].
	].

"------ is this inside the viewing pyramid?"
	ac _ ogl camera.
	(visible and:[ac testBounds: self boundSphere]) ifTrue:[ 
		ac pointer ifNotNil:[
			ac inPortal ifTrue:[
				(ac pointer pointerPick: self boundSphere).
				]
			ifFalse:[
				self isPortal ifFalse:[
					(ac pointer pointerPick: self boundSphere).
					(ac downPointer pick: self boundSphere).
					].
				].
			].
		timeStamp _ ogl timeStamp.
		count _ 1.
		self render: ogl.
	
		self hasAlpha ifTrue: [ 
			globalTrans _ B3DMatrix4x4 new.
			ogl glGetFloatv: GLModelviewMatrix with: globalTrans.
			root addAlphaObject: self 
				transform: globalTrans
				distance: ((ac globalPosition - globalPosition ) squaredLength * Processor activeProcess croquetWorld frameScaleSquared)
				parent: parent.].].

	frameChildren ifNotNil:[
		" render all of the child frames "
		childCount _ 0.
		frameChildren do:[:rFrame | childCount _ childCount+(rFrame renderFrame: ogl parent: self root: root).].
		childCount > 0 ifTrue:[count _ count+childCount. timeStamp _ ogl timeStamp.]
		].

	ogl glPopMatrix.
	^ count.


renderFrame: ogl space: space


	| saveParent saveTransform count childCount globalTrans ac |

" I am running a parallel set of transforms here. Why? Because I am told the OGL is inefficient at reading the current transform. Easy to change."
Transcript show:'TFrame>>#renderFrame:space: is deprecated.';cr.
" Any given frame can have multiple parents. This means that the frame can't keep a static reference to the parent frame. This must be dynamic, essentially filled out at render time. This allows for pure -instancing- of frame objects. That is, the same frame and all of its contents can easily be in multiple locations in the render tree without significant additional bookkeeping. It also means that when an object DOES need to refer to its parent or the space frame (read -root- for traditional approaches), this can ONLY be done at render-time via this mechanism. It also means that a child really must not modify the parent directly, as the parent has other responsibilities."

" #renderFrame: must return the number of objects that were rendered - failure to do so will just crash."

	count _ 0.
	(visible or:[frameChildren notNil])ifFalse:[^ 0 ].
	saveParent _ space currentParent.

	self parent ~= saveParent ifTrue:[ self isSpace ifFalse:[self frameChanged. ]].

	self currentParent: saveParent.
	space currentParent: self. 

	" apply the local transform to this matrix "
	saveTransform _ space currentTransform.
	saveTransform ifNotNil: [
		space currentTransform: (saveTransform composeWith: localTransform).]
		ifNil:[space currentTransform: localTransform. ].		
	ogl glPushMatrix. 
	ogl glMultMatrixf: localTransform transposed. 
	self boundSphere ifNotNil:[
		self boundSphere transform: space currentTransform.
		solid ifTrue:[ space testRayFrames: self boundSphere.].
	].

"------ is this inside the viewing pyramid?"
	ac _ ogl camera.
	(visible and:[ac testBounds: self boundSphere]) ifTrue:[ 
		ac pointer ifNotNil:[
			ac inPortal ifTrue:[
				(ac pointer pointerPick: self boundSphere)
				]
			ifFalse:[
				self isPortal ifFalse:[
					(ac pointer pointerPick: self boundSphere)
					].
				].
			].
		timeStamp _ ogl timeStamp.
		count _ 1.
		self render: ogl.
	
		self hasAlpha ifTrue: [ 
			globalTrans _ B3DMatrix4x4 new.
			ogl glGetFloatv: GLModelviewMatrix with: globalTrans.
			space addAlphaObject: self 
				transform: globalTrans "space currentTransform clone"
				distance: ((ac globalPosition - space currentTranslation ) squaredLength * Processor activeProcess croquetWorld frameScaleSquared)
				parent: space currentParent.].].

	frameChildren ifNotNil:[
		" render all of the child frames "
		childCount _ 0.
		frameChildren do:[:rFrame | childCount _ childCount+(rFrame renderFrame: ogl space: space).].
		childCount > 0 ifTrue:[count _ count+childCount. timeStamp _ ogl timeStamp.]
		].

	ogl glPopMatrix.
	space currentTransform: saveTransform.

	space currentParent: saveParent.
	^ count.



scripts
addEventScript: eventName logged: logFlag

	myPlayer
		ifNil:[myPlayer := TPlayer newFor: self]
		ifNotNil:[myPlayer becomeUniClass].
	myPlayer addEventScript: eventName logged: logFlag
addScript: logFlag

	myPlayer
		ifNil:[myPlayer := TPlayer newFor: self]
		ifNotNil:[myPlayer becomeUniClass].
	myPlayer addScript: logFlag
allBindingsOf: varName

	"Answer all possible bindings for the given variable name"
	^Array streamContents:[:s| self allBindingsOf: varName do:[:prefix :frame|
			s nextPut: (prefix copyWith: frame getName) -> frame.
	]]
allBindingsOf: varName do: aBlock

	"Answer all possible bindings for the given variable name"
	^self allBindingsOf: varName prefix: #() do: aBlock
allBindingsOf: varName prefix: aPath do: aBlock

	"Answer all possible bindings for the given variable name"
	frameChildren ifNil:[^self].
	frameChildren do:[:child|
		child getName = varName ifTrue:[aBlock value: aPath value: child].
		child allBindingsOf: varName prefix: (aPath copyWith: child getName) do: aBlock.
	].
bindingOf: varName

	self frameChildrenDo:[:each| each getName = varName ifTrue:[^varName -> each player]].
	^nil
compileScript: aText inBehavior: className

	(Smalltalk at: className) compile: aText notifying: nil.
compileScriptUnlogged: aText

	^self player compileScript: aText notifying: nil logged: false
getParent

	frameParent ifNil:[^nil].
	^frameParent player
hasScriptMethods

	myPlayer ifNil:[^false].
	^myPlayer hasScriptMethods
myEventMap

	^myEventMap
myEventMap: anEventMap

	myEventMap _ anEventMap
myPlayer

	^myPlayer
myPlayer: aPlayer

	myPlayer := aPlayer
myScripts: scriptList

	myScripts _ scriptList
player

	^myPlayer ifNil:[
		Smalltalk at: #CFramePlayer ifPresent:[:aClass|
			^myPlayer := aClass new myFrame: self
		].
		myPlayer := TPlayer new myTarget: self.
	].
playerScriptsDo: aBlock

	myPlayer ifNil:[^self].
	myPlayer playerScriptsDo: aBlock.
privateScriptSourceCode

	myPlayer ifNil:[^TPlayer privateScriptSourceCode].
	^myPlayer privateScriptSourceCode
removeScript: aSelector

	self player removeScript: aSelector.
removeScript: aSelector inBehavior: className

	(Smalltalk at: className) removeSelector: aSelector
signalEvent: anEvent

	"Signal the occurance of anEvent"
	myPlayer ifNotNil:[
		anEvent sender == self ifTrue:[myPlayer signalEvent: anEvent clone]].
	^super signalEvent: anEvent
startPlayerScript: aSelector

	myPlayer startScript: aSelector.

stepping
doStep


	"stop stepping when requested"
	stepsOn ifFalse: [ stepperActive _ false. ^self ].

	self wantsSteps ifTrue: [
		self step.
		self future: self stepTime deferRelative: 0.0 perform: #doStep.
	].
startStepping

	stepsOn _ true.
	stepperActive ifFalse: [
		stepperActive _ true.
		self future: self stepTime deferRelative: 0.0 perform: #doStep.
		].
step

	self subclassResponsibility.
stepTime

	"adjust step time if we are more than 2 seconds behind"
	| tardiness |
	tardiness _ Croquet tardiness.
	tardiness >= 2000.0 ifTrue: [ ^stepTime + tardiness. ].
^ stepTime.
stepTime: aNumber

	stepTime _ aNumber asFloat.
stopStepping

	stepsOn _ false. "request stepper to stop"
	"when actually stopped, sets stepperActive to false"
wantsSteps


	" This needs to be overridden by the component if we want to step."

	^ false.

synchronization
syncNames: names


	| fc |

	1 to: names size do:[ :index | 
		fc _ frameChildren at: index.
		fc croquetObjectName: (names at: index).
		].


testing
isBrowser


	^ false.
isCamera


	^ false.
isComponent


	"^ false."
	^myEventMap notNil.
isFrame


	^ true.
isGroup


	^ false.
isLight


	^ false.
isMesh


	^ false.
isPortal


	^ false.
isRay


	^ false.
isRigidBody


	^ false.
isSolid


	^ solid
isSpace


	^ false.
isTexture


	^ false.
isWindow


	^ false.

toys
jump: dist


	self translation: self translation + (B3DVector3 x: 0 y: dist z: 0).
makeThumbnail

	"Create a thumbnail of this object only"
	^self makeThumbnail: 128@96
makeThumbnail: extent

	"Create a thumbnail of this object only"

	| space lt |
	space _ TSpace new."we need *some* space here"
	lt _ TLight new.
	lt ambientColor: #(1 1 1 1) asFloatArray.
	lt diffuseColor: #(1 1 1 1) asFloatArray.
	lt specularColor: #(0.2 0.15 0.15 1.0) asFloatArray.
	lt visible: false.
	lt addRotationAroundZ: 120.
	lt addRotationAroundY:-10.
	space addChild: lt.	
	^space makeThumbnailOf: self extent: extent
makeThumbnailWithName

	"Create a thumbnail of this object only"
	^self makeThumbnailWithName: 128@96
makeThumbnailWithName: extent

	"Create a thumbnail of this object only"
	| aForm nameString font nameForm box rectForName aCanvas |
	aForm := self makeThumbnail: extent.
	nameString := objectName ifNil:['???'].
	font _ TextStyle defaultFont emphasized: 1.
	nameForm _ (StringMorph contents: nameString font: font) imageForm.
	nameForm _ nameForm scaledToSize: (aForm extent - (4@2) min: nameForm extent).
	box _ aForm boundingBox.
	rectForName _ box bottomLeft + 
			(box width - nameForm width // 2 @ (nameForm height + 2) negated)
				extent: nameForm extent.
	aCanvas _ aForm getCanvas.
	rectForName topLeft eightNeighbors do: [ :pt |
		aCanvas
			stencil: nameForm 
			at: pt
			color: Color white.
	].
	aCanvas
		stencil: nameForm 
		at: rectForName topLeft 
		color: Color black.
	^aForm

transform
addRotationAroundX: anAngle


	| trans rtrans |
	self frameChanged.
	rtrans _ B3DMatrix4x4 identity.
	rtrans rotationAroundX: anAngle.

	trans _ self translation.
	localTransform _ rtrans composeWith:localTransform.
	self translation: trans.
	^ localTransform.
addRotationAroundY: anAngle


	| trans rtrans |
	self frameChanged.
	rtrans _ B3DMatrix4x4 identity.
	rtrans rotationAroundY: anAngle.

	trans _ self translation.
	localTransform _ rtrans composeWith:localTransform.
	self translation: trans.
	^ localTransform.
addRotationAroundZ: anAngle


	| trans rtrans |
	self frameChanged.

	rtrans _ B3DMatrix4x4 identity.
	rtrans rotationAroundZ: anAngle.

	trans _ self translation.
	localTransform _ rtrans composeWith:localTransform.
	self translation: trans.
	^ localTransform.
addYaw: y

	self yaw: self yaw + y.
globalMatrixOut

" This simply reverses the direction of the matrix. An example of use is for TPortals, where the global matrix used for rendering the outside of the portal is the reverse of the exiting render, hence we need to flip the matrix around to get what we want."

	| gt |
	gt _ self globalTransform copy.
	gt a11: 0.0 - gt a11.
	gt a21: 0.0 - gt a21.
	gt a31: 0.0 - gt a31.
	gt a13: 0.0 - gt a13.
	gt a23: 0.0 - gt a23.
	gt a33: 0.0 - gt a33.
	^ gt

globalOrientation

"This is used to return just the orientation part of the matrix.The translation part is 0.0."
	| mat |

	mat _ self globalTransform clone.
	mat translationX: 0.0 y: 0.0 z: 0.0.
	^ mat.
globalPitch


	^self globalPitchYawRoll x
globalPitchYawRoll

	globalTransform ifNil:[^ 0.0@0.0@0.0].
	^globalTransform pitchYawRoll
globalPosition


	frameChanged ifTrue:[	self globalTransform. ].
	^ globalPosition.
"
	^ self globalTransform translation.
"
globalPosition: gp


	self globalTransform.
	frameChanged _ false.
	globalPosition _ gp.
	globalTransform translation: gp.

	
globalRoll

	^self globalPitchYawRoll z
globalTransform

	" This is the global transform of the frame - its location and orientation in global world coordinates. We only
	calculate this when we have to, which is only when a local coordinate frame has been changed AND when someone 	asks for it. "

	frameChanged ifTrue:[	
		frameParent ifNotNil: [ self globalTransform: 
			((frameParent globalTransform) composeWith: self localTransform)] ifNil:[
		self globalTransform:  self localTransform.].
		globalPosition _ globalTransform translation.
	].
	^ globalTransform.
" NOT ^ self globalTransform - this causes bad recursion error!"
					 
globalTransform: gTrans

	
	frameChanged _ false.
	globalTransform _ gTrans.
	globalPosition _ globalTransform translation.
	self globalTransformUpdate.
	^ globalTransform.
globalTransformUpdate


	" This is called when a new global transfom is calculated. It is designed to be extended by other frame sub-classes, such as TRigidBody, that would have variables that should be updated once when the frame gets changed."
globalYaw

	^self globalPitchYawRoll y
gotoCamera


	self translation: (Croquet world activeCamera translation)
inverseGlobalOrientation


	^ self globalOrientation orthoNormInverse.
inverseGlobalTransform


	^ self globalTransform orthoNormInverse.
localTransform


	^localTransform.
localTransform: m4x4


	self frameChanged.
	localTransform _ m4x4.
lookAt: v up: u


	| at side m trans up |
	up _ u.
	trans _ self translation.
	at _ trans - v.
	at normalize.
	up ifNil:[
		at y abs = 1.0 ifFalse:[ up _ B3DVector3 x: 0 y: 1 z: 0.] ifTrue:[
			up _ B3DVector3 x:0 y:0 z:-1].].
	side _ (at cross: up) normalized negated.
	up _ (side cross: at) normalized negated.
	m _ B3DMatrix4x4 identity.
	m a11: side x.
	m a21: side y.
	m a31: side z.
	m a12: up x.
	m a22: up y.
	m a32: up z.
	m a13: at x.
	m a23: at y.
	m a33: at z.
	self localTransform: m.
	self translation: trans.
orientation

"This is used to return just the orientation part of the matrix.The translation part is 0.0."
	| mat |

	mat _ localTransform clone.
	mat translationX: 0.0 y: 0.0 z: 0.0.
	^ mat.
orientation: mat

"This is used to set just the orientation part of the matrix. It keeps the translation parts intact."
	| trans |
	self frameChanged.
	trans _ self translation.
	localTransform _ mat clone.
	self translation: trans.
	^ localTransform.
pitch


	^self pitchYawRoll x
pitch: p

	| angles |
	angles := self pitchYawRoll.
	angles x: p.
	self pitchYawRoll: angles.
pitchYawRoll

	^localTransform pitchYawRoll
pitchYawRoll: aVector

	| tfm |
	tfm := localTransform copy pitchYawRoll: aVector.
	self localTransform: tfm.
pitchYawRollBy: delta

	self pitchYawRoll: self pitchYawRoll + delta
quaternion


	^ localTransform asQuaternion.
relativeTransform: frame


	^ self globalTransform orthoNormInverse composeWith: frame globalTransform.
releaseToRoot


	"Transfer the frame from the current parent to the root frame while keeping the pose in exactly the same orientation"

	| trans root |
	trans _ self globalTransform.
	root _ self root.
	self transferTo: root.
	self localTransform: trans.
roll

	^self pitchYawRoll z
roll: r

	| angles |
	angles := self pitchYawRoll.
	angles z: r.
	self pitchYawRoll: angles.
rotFromBallPoints: from to: to

 |  f t |
" This matrix needs to be transposed to convert from left handed to right (which is OpenGL's world)."
	f _ from normalized.
	t _ to normalized.
	f _ (f+t) normalized.
	f _ (f+t) normalized.
	^  (B3DRotation 	a: (f x * t x) + (f y * t y) + (f z * t z )
				b: (f y * t z) - (f z * t y)
			  	c: (f z * t x) - (f x * t z)
			  	d: (f x * t y) - (f y * t x)
			  	).
rotation: anAngle around: aVector3


	| trans |
	self frameChanged.
	trans _ self translation.
	localTransform rotation: anAngle around: aVector3.
	self translation: trans.
	^ localTransform.
rotation: anAngle aroundX: xValue y: yValue z: zValue


	| trans |
	self frameChanged.

	trans _ self translation.
	localTransform rotation: anAngle aroundX: xValue y: yValue z: zValue.
	self translation: trans.
	^ localTransform.
rotationAroundX: anAngle


	| trans |
	self frameChanged.
	trans _ self translation.
	localTransform rotationAroundX: anAngle.
	self translation: trans.
	^ localTransform.
rotationAroundY: anAngle


	| trans |
	self frameChanged.
	trans _ self translation.
	localTransform rotationAroundY: anAngle.
	self translation: trans.
	^ localTransform.
rotationAroundZ: anAngle


	| trans |
	self frameChanged.
	trans _ self translation.
	localTransform rotationAroundZ: anAngle.
	self translation: trans.
	^ localTransform.
scale

	
	^ 1.0.
scale: scale


	self translation: scale * self translation.
	frameChildren ifNotNil:[
		frameChildren do:[ :fc | fc scale: scale.]].
transformBy: m4x4


	self frameChanged.
	localTransform _ localTransform composedWithLocal: m4x4.
translation


	^localTransform translation.
translation: aVector


	self frameChanged.
	^localTransform translation: aVector.
translationX: xValue y: yValue z: zValue

	
	self frameChanged.

	^ localTransform translationX: xValue y: yValue z: zValue.
yaw

	^self pitchYawRoll y
yaw: y

	| angles |
	angles := self pitchYawRoll.
	angles y: y.
	self pitchYawRoll: angles.
yawTransform

	| v |
	v _ localTransform row3.
	v x > 0 ifTrue:[self yaw: v z arcCos radiansToDegrees negated] ifFalse:
							[self yaw: v z arcCos radiansToDegrees.].

voice chat
playSoundBuffer: soundBuffer at: deltaMSecs stereo: stereoFlag samplingRate: samplingRate codec: codec worldsExcept: aCWorld

	Croquet world = aCWorld ifTrue: [^self].
	"Queue in the given sound buffer at deltaMSecs from the start of the sound."
	self streamingSound addSoundBuffer: soundBuffer at: deltaMSecs stereo: stereoFlag samplingRate: samplingRate codec: codec.
streamingSound

	^streamingSound ifNil:[streamingSound := TSampledSound new play ]
streamingSound: aSound

	streamingSound := aSound.

yellow-scripts
dropFart: aRadius rate: aSamplingRate

	| radius rate fartLoc newLoc fart stepT nextT script |
	radius _ aRadius ifNil:[0.3].
	rate _ aSamplingRate ifNil:[4].
	stepT _ 1000.0 / rate.
	nextT _ Processor activeProcess new + stepT.
	fartLoc _ self translation.
	script := Processor activeProcess.
	[true] whileTrue:[
		nextT waitUntil.
		nextT _ nextT + stepT.
		newLoc _ self translation.
		fartLoc = newLoc ifFalse:[
			fartLoc _ newLoc.
			fart _ TSphere new.
			fart material: ((TMaterial new) color: Color white).
			fart radius: radius.
			fart translation: fartLoc.
			fart segments: 2.
			self root addChild: fart.
			fart startScript: #fadeOut:style:rate: withArguments:{3. nil. nil}.
		].
		"see if a stop was requested"
		script isStopRequested ifTrue:[^self].
	].
fadeOut: aDuration style: aStyle rate: aSamplingRate

	| duration style rate endState styleFunc lastState startState deltaTime msecsDuration proportion newState progressT startT script |
	duration _ aDuration ifNil:[self defaultDuration].
	style _ aStyle ifNil:[self defaultInterpolationStyle].
	rate _ aSamplingRate ifNil:[self defaultSamplingRate].

	"Get the ultimate state we're interested in"
	endState := 0.0.
	duration <= 0 ifTrue:[^self setAlpha: endState].
	"Compute msecs since that's what we'll be working on here"
	msecsDuration := duration * 1000.
	"Get the interpolation function (evaluating from 0-1 and returning values from 0-1)"
	styleFunc := style asScriptStyle.
	"Reset the interpolation state"
	lastState := startState := self getAlpha.
	"And go moving until we're out of time"
	rate _ msecsDuration * rate // 1000.
	deltaTime _ msecsDuration // rate.
	startT _ Croquet teaTime.
	script := Processor activeProcess.
	1 to: rate do:[:i|
		progressT _ (i * deltaTime) asFloat.
		(startT + progressT) waitUntil. "<-- here is where tea scheduling kicks in"
		"Compute the proportion of time that's over"
		proportion := progressT / msecsDuration asFloat.
		"Map it through the interpolation style"
		proportion := styleFunc value: proportion.
		"Compute the state (e.g., offset) at our new proportion"
		newState := startState interpolateTo: endState at: proportion.
		"Transform incrementally - this is so that we can superimpose animations."
		self setAlpha: (self getAlpha + newState - lastState).
		"Remember last state for the next round"
		lastState := newState.
		"see if a stop was requested"
		script isStopRequested ifTrue:[^self].
	].
	(startT + msecsDuration) waitUntil. "<-- here is where tea scheduling kicks in"
	"Apply the remaining part of the transform (if any)"
	self setAlpha: (self getAlpha + endState - lastState).
	frameParent removeChild: self.
forwardBy: aDistance duration: aDuration style: aStyle rate: aSamplingRate

	^self move: #forward distance: aDistance duration: aDuration style: aStyle rate: aSamplingRate
move: aDirection distance: aDistance duration: aDuration style: aStyle rate: aSamplingRate

	| direction distance duration style rate endState styleFunc lastState startState deltaTime msecsDuration proportion newState startT progressT script |
	direction _ aDirection ifNil:[self defaultDirection].
	distance _ aDistance ifNil:[self defaultDistance].
	duration _ aDuration ifNil:[self defaultDuration].
	style _ aStyle ifNil:[self defaultInterpolationStyle].
	rate _ aSamplingRate ifNil:[self defaultSamplingRate].

	"Get the ultimate state we're interested in"
	endState := direction asMoveDirection * distance.
	duration <= 0 ifTrue:[^self transformBy: (B3DMatrix4x4 withOffset: endState)].
	"Compute msecs since that's what we'll be working on here"
	msecsDuration := duration * 1000.
	"Get the interpolation function (evaluating from 0-1 and returning values from 0-1)"
	styleFunc := style asScriptStyle.
	"Reset the interpolation state"
	lastState := startState := 0@0@0.
	"And go moving until we're out of time"
	rate _ msecsDuration * rate // 1000.
	deltaTime _ msecsDuration // rate.
	startT _ Croquet teaTime.
	script := Processor activeProcess.
	1 to: rate do:[:i|
		progressT _ (i*deltaTime) asFloat.
		(startT + progressT) waitUntil. "<-- here is where tea scheduling kicks in"
		"Compute the proportion of time that's over"
		proportion :=  progressT / msecsDuration asFloat.
		"Map it through the interpolation style"
		proportion := styleFunc value: proportion.
		"Compute the state (e.g., offset) at our new proportion"
		newState := startState interpolateTo: endState at: proportion.
		"Transform incrementally - this is so that we can superimpose animations."
		self transformBy: (B3DMatrix4x4 withOffset: newState - lastState).
		"Remember last state for the next round"
		lastState := newState.
		"see if a stop was requested"
		script isStopRequested ifTrue:[^self].
	].
	(startT + msecsDuration) waitUntil. "<-- here is where tea scheduling kicks in"
	"Apply the remaining part of the transform (if any)"
	self transformBy: (B3DMatrix4x4 withOffset: endState - lastState).
moveBy: aDirection duration: aDuration style: aStyle rate: aSamplingRate

	| direction duration style rate endState styleFunc lastState startState deltaTime msecsDuration proportion newState startT progressT script |
	direction _ aDirection ifNil:[self defaultDirection asMoveDirection * self defaultDistance].
	duration _ aDuration ifNil:[self defaultDuration].
	style _ aStyle ifNil:[self defaultInterpolationStyle].
	rate _ aSamplingRate ifNil:[self defaultSamplingRate].

	"Get the ultimate state we're interested in"
	endState := direction.
	duration <= 0 ifTrue:[^self transformBy: (B3DMatrix4x4 withOffset: endState)].
	"Compute msecs since that's what we'll be working on here"
	msecsDuration := duration * 1000.
	"Get the interpolation function (evaluating from 0-1 and r