diff --git a/Diya/Diya2DNode.class.st b/Diya/Diya2DNode.class.st index d9b3026..27c4682 100644 --- a/Diya/Diya2DNode.class.st +++ b/Diya/Diya2DNode.class.st @@ -77,11 +77,22 @@ Diya2DNode >> recFromBuffer [ Diya2DNode >> updateTF [ tf := MatrixTransform2x3 identity. "translation" - tf setOffset: translation. + tf setOffset: translation + pivot. "rotation" rotation = 0 ifFalse:[tf setAngle: rotation ]. - "scale" - tf setScale: scale. + "translate back to pivot" + pivot isZero ifFalse:[ + tf := tf composedWithLocal: + (MatrixTransform2x3 identity + setOffset: pivot negated; + yourself) + ]. + scale isZero ifFalse: [ + tf := tf composedWithLocal: + (MatrixTransform2x3 identity + setScale: scale; + yourself) + ]. self parent ifNil: [ ^self ]. self parent isRoot ifFalse: [tf := self parent tf composedWithLocal: tf ]. children ifNotNil: [children do:[:c| c updateTF ]]. diff --git a/Diya/Diya2DPrimShape.class.st b/Diya/Diya2DPrimShape.class.st index c5b187d..0635422 100644 --- a/Diya/Diya2DPrimShape.class.st +++ b/Diya/Diya2DPrimShape.class.st @@ -75,7 +75,6 @@ Diya2DPrimShape >> extent [ Diya2DPrimShape >> initialize [ super initialize. texture := nil. - children := nil. type := GL_TRIANGLES. bbox := Rectangle origin: 0@0 corner: 0@0. ] diff --git a/Diya/DiyaApplicationLauncher.class.st b/Diya/DiyaApplicationLauncher.class.st index 4349f65..84ef12b 100644 --- a/Diya/DiyaApplicationLauncher.class.st +++ b/Diya/DiyaApplicationLauncher.class.st @@ -47,9 +47,11 @@ DiyaApplicationLauncher >> launch: app [ root empty. ]. currapp := app uniqueInstance. - self appNode addNode: currapp root. + currapp setup. + currapp root forceReload. + Transcript show: 'APPLICATION INIT'; cr. self context assets: currapp am. - currapp setup + self appNode addNode: currapp root. ] { #category : #initialization } @@ -64,7 +66,7 @@ DiyaApplicationLauncher >> main [ ]. currapp ifNotNil: [currapp main.]. "root render." - [ root stepDown ] fork. + root stepDown. self process. root render. ] @@ -73,11 +75,12 @@ DiyaApplicationLauncher >> main [ DiyaApplicationLauncher >> process [ |Q node maxProcessingTime| maxProcessingTime := 1000 / DiyaBoot maxFPS - 100. - Q := DiyaRendererContext uniqueInstance processQueue. + Q := root processingQueue select:[:e| e visibility]. Q ifEmpty:[^ self]. [ node := Q removeFirst. node process. + root cleanDirtyNode: node. Q isNotEmpty and: DiyaClock uniqueInstance lapDelta asMilliSeconds < maxProcessingTime ] whileTrue @@ -102,7 +105,7 @@ DiyaApplicationLauncher >> setup [ #bgColor -> Color orange. #border -> 3 }. - root addNode: (Diya2DNode new) at: 0@0. + root addNode: (DiyaCompositeNode new) at: 0@0. txtFPS := root addNode:(DiyaText data: '') at: ( self context resolution x - 80)@(self context resolution y - 40). txtFPS extent: 80@40. txtFPS styleName: #fps_text. diff --git a/Diya/DiyaBaseApplication.class.st b/Diya/DiyaBaseApplication.class.st index 68d9a88..13789c6 100644 --- a/Diya/DiyaBaseApplication.class.st +++ b/Diya/DiyaBaseApplication.class.st @@ -7,6 +7,7 @@ Class { { #category : #initialization } DiyaBaseApplication >> initialize [ super initialize. - root := Diya2DNode new. + root := DiyaCompositeNode new. + root styleName: #global. am := AssetManager new. ] diff --git a/Diya/DiyaCompositeNode.class.st b/Diya/DiyaCompositeNode.class.st new file mode 100644 index 0000000..a09521f --- /dev/null +++ b/Diya/DiyaCompositeNode.class.st @@ -0,0 +1,22 @@ +Class { + #name : #DiyaCompositeNode, + #superclass : #Diya2DNode, + #category : #'Diya-Graphics' +} + +{ #category : #accessing } +DiyaCompositeNode >> process [ + +] + +{ #category : #accessing } +DiyaCompositeNode >> setClean [ + + +] + +{ #category : #accessing } +DiyaCompositeNode >> setDirty [ + + +] diff --git a/Diya/DiyaEllipse.class.st b/Diya/DiyaEllipse.class.st index b3ba463..37c3304 100644 --- a/Diya/DiyaEllipse.class.st +++ b/Diya/DiyaEllipse.class.st @@ -92,6 +92,6 @@ DiyaEllipse >> setUpShader [ self shader setUniform: #u_border value: (self ? #border); setUniform: #u_border_color value: ( self ? #borderColor) asGL4FArray; - setUniform: #u_rx value: rx; - setUniform: #u_ry value: ry. + setUniform: #u_rx value: (rx * (scale x)) ; + setUniform: #u_ry value: (ry * (scale y)) . ] diff --git a/Diya/DiyaExampleApp.class.st b/Diya/DiyaExampleApp.class.st index 3459eae..4f3795b 100644 --- a/Diya/DiyaExampleApp.class.st +++ b/Diya/DiyaExampleApp.class.st @@ -89,7 +89,7 @@ DiyaExampleApp >> setup [ node1 rotation: 45. node1 scale: 2.0@2.0. node1 on: #(mousebuttondown fingerdown) do:[:e| - label txt: 'Mouse ', (node1 local: e mapped worldPosition) asIntegerPoint asString]. + label txt: 'RECT ', (node1 local: e mapped worldPosition) asIntegerPoint asString]. img := root addNode: (DiyaImageView from:'mrsang.png') at: 10 @ 400. img styleName: #image_view. @@ -117,15 +117,17 @@ DiyaExampleApp >> setup [ node := root addNode: (DiyaLine from: 10@10 to: 200@200). node styleName: #line_view. - ell := root addNode: (DiyaEllipse rx:100 ry: 70) at: 100@300. - ell rotation: 30. + ell := root addNode: (DiyaEllipse rx:100 ry: 70) at: 120@300. + ell scale: 1.2 @ 1.2. ell styleName: #ell_view. - "node rotation: Float pi / 2.0." ell textureNamed:'mrsang.png'. + ell addNode: (DiyaTimerNode timeout: 1000 / 6 do:[:n | + n parent rotation: n parent rotation + 10. + ] ). ell on: #(mousebuttondown fingerdown) do:[:e| label txt: 'Ellipse clicked', (ell local:e mapped worldPosition) asIntegerPoint asString]. - node := root addNode: (DiyaConvexPolygon points:{250@100. 400@250. 450@80. 350@60}). + node := root addNode: (DiyaConvexPolygon points:{0@40. 150@190. 200@20. 100@0}) at: 250@60. node textureNamed: 'mrsang.png'. node styleName: #poly_view. @@ -133,8 +135,13 @@ DiyaExampleApp >> setup [ icon styleName: #text_icon_2. button := root addNode: (DiyaButton text: 'Click me !') at: 240@460. + icon := DiyaFontIcon data: 16rF130. + "icon := DiyaImageIcon from: 'mrsang.png'." + icon addNode:(DiyaTimerNode timeout: 1000 / 12 do:[:n | + n parent rotation: n parent rotation + 10 pivot: n parent extent / 2. + ] ). button extent: 200@40. - button icon:16rF185"'mrsang.png'". + button icon: icon "'mrsang.png'". "button rotation: Float pi / 2.0." button styleName: #button_view. Transcript show: 'Application setup';cr. diff --git a/Diya/DiyaLabel.class.st b/Diya/DiyaLabel.class.st index 3c0184d..7b16f9e 100644 --- a/Diya/DiyaLabel.class.st +++ b/Diya/DiyaLabel.class.st @@ -32,7 +32,7 @@ DiyaLabel >> icon: anObject [ anObject isString ifTrue: [ icon := self addNode: (DiyaImageIcon from: anObject). ]. - icon ifNil: [ ^ DiyaCoreAPIError signal: 'Invalid icon identification']. + icon ifNil: [ icon := self addNode: anObject ]. self setDirty ] diff --git a/Diya/DiyaMetaNode.class.st b/Diya/DiyaMetaNode.class.st new file mode 100644 index 0000000..f30b8ff --- /dev/null +++ b/Diya/DiyaMetaNode.class.st @@ -0,0 +1,40 @@ +Class { + #name : #DiyaMetaNode, + #superclass : #DiyaNode, + #category : #'Diya-Graphics' +} + +{ #category : #accessing } +DiyaMetaNode >> addNode: node at: pos [ + self shouldNotBeCalled +] + +{ #category : #accessing } +DiyaMetaNode >> boundingBox [ + ^ 0@0 +] + +{ #category : #rendering } +DiyaMetaNode >> draw [ + +] + +{ #category : #accessing } +DiyaMetaNode >> extent [ + ^ 0@0 +] + +{ #category : #testing } +DiyaMetaNode >> inner: aPoint [ + ^ false +] + +{ #category : #processing } +DiyaMetaNode >> process [ + +] + +{ #category : #processing } +DiyaMetaNode >> updateTF [ + +] diff --git a/Diya/DiyaNode.class.st b/Diya/DiyaNode.class.st index 3fcf07b..77bf113 100644 --- a/Diya/DiyaNode.class.st +++ b/Diya/DiyaNode.class.st @@ -14,7 +14,9 @@ Class { 'root', 'styleName', 'style', - 'id' + 'id', + 'visibility', + 'pivot' ], #pools : [ 'OpenGLConstants', @@ -53,25 +55,13 @@ DiyaNode >> addNode: node [ DiyaNode >> addNode: node at: pos [ children ifNil: [ ^self ]. node parent: self. - node position: pos. children add: node. + node position: pos. node root: self root. + node setDirtyAll. ^ node ] -{ #category : #accessing } -DiyaNode >> allChildren [ - |nodes| - nodes := OrderedCollection new. - children ifNil: [ ^ nodes ]. - children do:[:c| - nodes add: c. - c allChildren do:[:e| nodes add:e]. - ]. - ^nodes - -] - { #category : #accessing } DiyaNode >> boundingBox [ ^ self subclassResponsibility @@ -82,7 +72,7 @@ DiyaNode >> children [ ^children ] -{ #category : #accessing } +{ #category : #rendering } DiyaNode >> draw [ self subclassResponsibility ] @@ -97,6 +87,15 @@ DiyaNode >> extent [ ^ self subclassResponsibility ] +{ #category : #processing } +DiyaNode >> forceReload [ + self process. + children ifNotNil: [ + children do:[:c| + c forceReload + ]] +] + { #category : #accessing } DiyaNode >> id [ ^ id @@ -113,10 +112,12 @@ DiyaNode >> initialize [ styleName := nil. style := nil. root := nil. + visibility := true. + pivot := 0@0. id := self className,'#',(Random new nextInt: 1e6) asString. ] -{ #category : #accessing } +{ #category : #testing } DiyaNode >> inner: aPoint [ ^ self subclassResponsibility ] @@ -143,6 +144,11 @@ DiyaNode >> parent: anObject [ parent := anObject ] +{ #category : #accessing } +DiyaNode >> pivot [ + ^ pivot +] + { #category : #accessing } DiyaNode >> position [ ^ translation @@ -154,7 +160,7 @@ DiyaNode >> position: anObject [ self updateTF. ] -{ #category : #accessing } +{ #category : #processing } DiyaNode >> process [ ^self subclassResponsibility ] @@ -163,13 +169,26 @@ DiyaNode >> process [ DiyaNode >> register: aBlock to: eventName [ |evtCode| evtCode := SDL2Constants bindingOf: ('SDL_', eventName asUppercase). - evtCode ifNil: [ ^DiyaCoreAPIError signal: 'Unknow event ', eventName ]. + evtCode ifNil: [ evtCode := eventName ]. ehandlers at: evtCode value put: aBlock. ] -{ #category : #accessing } +{ #category : #removing } +DiyaNode >> remove [ + self setClean. + root := nil. + parent ifNotNil: [ parent removeChild: self ] +] + +{ #category : #removing } +DiyaNode >> removeChild: c [ + children ifNotNil: [ children remove: c ifAbsent:[ ]] +] + +{ #category : #rendering } DiyaNode >> render [ + visibility ifFalse:[^self]. shader ifNotNil: [self setUpShader]. self draw. children ifNil: [ ^self ]. @@ -201,6 +220,13 @@ DiyaNode >> rotation: anObject [ self updateTF. ] +{ #category : #accessing } +DiyaNode >> rotation: anObject pivot: p [ + rotation := anObject. + pivot := p. + self updateTF. +] + { #category : #accessing } DiyaNode >> scale [ ^ scale @@ -212,21 +238,26 @@ DiyaNode >> scale: anObject [ self updateTF. ] -{ #category : #initialization } +{ #category : #'changing state' } DiyaNode >> setClean [ - |Q| - Q := DiyaRendererContext uniqueInstance processQueue. - Q remove: self ifAbsent: [ ] + root ifNil: [ ^self ]. + root cleanDirtyNode: self. ] -{ #category : #initialization } +{ #category : #'changing state' } DiyaNode >> setDirty [ - |Q| - Q := DiyaRendererContext uniqueInstance processQueue. - (Q includes: self ) ifFalse:[ Q add: self]. + root ifNil: [ ^self ]. + self root enqueueDirtyNode: self. ] -{ #category : #accessing } +{ #category : #'changing state' } +DiyaNode >> setDirtyAll [ + self setDirty. + children ifNotNil: [ + children do:[:c| c setDirtyAll] ] +] + +{ #category : #rendering } DiyaNode >> setUpShader [ |mem| mem := self tf asGLBuffer. @@ -264,7 +295,7 @@ DiyaNode >> step [ { #category : #stepping } DiyaNode >> stepDown [ - self step + self step. children ifNotNil: [ children do:[:c | c stepDown ] ] ] @@ -303,7 +334,17 @@ DiyaNode >> trigger: evt [ ]. ] -{ #category : #accessing } +{ #category : #processing } DiyaNode >> updateTF [ self subclassResponsibility ] + +{ #category : #accessing } +DiyaNode >> visibility [ + ^ visibility +] + +{ #category : #accessing } +DiyaNode >> visibility: anObject [ + visibility := anObject +] diff --git a/Diya/DiyaPolygon.class.st b/Diya/DiyaPolygon.class.st index f4b4cb6..fd9cb43 100644 --- a/Diya/DiyaPolygon.class.st +++ b/Diya/DiyaPolygon.class.st @@ -42,9 +42,6 @@ DiyaPolygon >> points: anObject [ { #category : #accessing } DiyaPolygon >> process [ - bbox := self recFromPoints. - translation = bbox origin ifFalse:[ self position: bbox origin]. - points := points collect:[:e | e - bbox origin]. bbox := self recFromPoints. self calculateVertices. ^true diff --git a/Diya/DiyaRendererContext.class.st b/Diya/DiyaRendererContext.class.st index ccc2bab..414b990 100644 --- a/Diya/DiyaRendererContext.class.st +++ b/Diya/DiyaRendererContext.class.st @@ -9,8 +9,7 @@ Class { 'texture0', 'projection', 'assets', - 'window', - 'rqueue' + 'window' ], #pools : [ 'OpenGLConstants', @@ -67,7 +66,6 @@ DiyaRendererContext >> initialize [ vbo bind: GL_ARRAY_BUFFER. projection := Array2D identity: 4. assets := AssetManager new. - rqueue := OrderedCollection new. ] { #category : #accessing } @@ -80,11 +78,6 @@ DiyaRendererContext >> mouse: anObject [ mouse := anObject ] -{ #category : #accessing } -DiyaRendererContext >> processQueue [ - ^ rqueue -] - { #category : #accessing } DiyaRendererContext >> projection [ ^ projection diff --git a/Diya/DiyaRootNode.class.st b/Diya/DiyaRootNode.class.st index d526dff..e4004ae 100644 --- a/Diya/DiyaRootNode.class.st +++ b/Diya/DiyaRootNode.class.st @@ -12,16 +12,16 @@ DiyaRootNode >> Q [ ^ Q ] -{ #category : #accessing } -DiyaRootNode >> Q: v [ - Q := v -] - { #category : #accessing } DiyaRootNode >> boundingBox [ ^ Rectangle origin: 0@0 corner: context resolution ] +{ #category : #'add/remove' } +DiyaRootNode >> cleanDirtyNode: aNode [ + Q remove: aNode ifAbsent:[] +] + { #category : #accessing } DiyaRootNode >> draw [ |c| @@ -31,6 +31,11 @@ DiyaRootNode >> draw [ context vbo bind: GL_ARRAY_BUFFER. ] +{ #category : #'add/remove' } +DiyaRootNode >> enqueueDirtyNode: aNode [ + (Q includes: aNode ) ifFalse:[ Q add: aNode]. +] + { #category : #accessing } DiyaRootNode >> extent [ ^ context resolution @@ -58,7 +63,7 @@ DiyaRootNode >> isRoot [ { #category : #initialization } DiyaRootNode >> process [ - ^true + ] { #category : #accessing } @@ -66,6 +71,19 @@ DiyaRootNode >> processingQueue [ ^ Q ] +{ #category : #initialization } +DiyaRootNode >> setClean [ + + + +] + +{ #category : #initialization } +DiyaRootNode >> setDirty [ + + +] + { #category : #accessing } DiyaRootNode >> updateTF [ "donothing" diff --git a/Diya/DiyaTimerNode.class.st b/Diya/DiyaTimerNode.class.st new file mode 100644 index 0000000..7f42ef8 --- /dev/null +++ b/Diya/DiyaTimerNode.class.st @@ -0,0 +1,68 @@ +Class { + #name : #DiyaTimerNode, + #superclass : #DiyaMetaNode, + #instVars : [ + 'timeout', + 'elapsedTime', + 'handlers' + ], + #category : #'Diya-Graphics' +} + +{ #category : #accessing } +DiyaTimerNode class >> timeout: ms [ + ^ self new timeout: ms; yourself +] + +{ #category : #accessing } +DiyaTimerNode class >> timeout: ms do: aBlock [ + ^ (self timeout: ms) onTimeout: aBlock;yourself +] + +{ #category : #accessing } +DiyaTimerNode class >> timeout: ms doOnce: aBlock [ + ^ (self timeout: ms) onceTimeout: aBlock;yourself +] + +{ #category : #accessing } +DiyaTimerNode >> delta [ + ^ DiyaClock uniqueInstance delta asMilliSeconds +] + +{ #category : #accessing } +DiyaTimerNode >> initialize [ + super initialize. + elapsedTime := 0. + handlers := OrderedCollection new. +] + +{ #category : #'as yet unclassified' } +DiyaTimerNode >> onTimeout: aBlock [ + handlers add: aBlock +] + +{ #category : #'as yet unclassified' } +DiyaTimerNode >> onceTimeout: aBlock [ + |newBlock| + handlers := OrderedCollection new. + newBlock := [ :node | aBlock value: node. self remove ]. + handlers add: newBlock +] + +{ #category : #stepping } +DiyaTimerNode >> step [ + elapsedTime := elapsedTime + self delta. + elapsedTime >= timeout ifFalse:[^ self]. + handlers do:[:e| e value: self ]. + elapsedTime := 0 +] + +{ #category : #accessing } +DiyaTimerNode >> timeout [ + ^ timeout +] + +{ #category : #accessing } +DiyaTimerNode >> timeout: anObject [ + timeout := anObject +]