1
0
mirror of https://github.com/lxsang/Diya-API.git synced 2025-03-12 02:32:46 +01:00

WIP: add support for input events

This commit is contained in:
Dany LE 2022-03-19 02:18:29 +01:00
parent e2a8bc046c
commit ac764da076
22 changed files with 340 additions and 90 deletions

View File

@ -12,3 +12,30 @@ Array2D >> asGLBuffer [
buffer autoRelease.
^buffer
]
{ #category : #'*Diya' }
Array2D class >> rotationMatrix2D: rotation [
^Array2D rows: 3 columns: 3 contents:{
rotation cos. (rotation sin) negated. 0.0.
rotation sin. rotation cos. 0.0.
0.0. 0.0. 1.0
}
]
{ #category : #'*Diya' }
Array2D class >> scaleMatrix2D: scale [
^Array2D rows: 3 columns: 3 contents:{
scale x. 0.0. 0.0.
0.0. scale y. 0.0.
0.0. 0.0. 1.0
}
]
{ #category : #'*Diya' }
Array2D class >> translateMatrix2D: translation [
^Array2D rows: 3 columns: 3 contents: {
1.0. 0.0. translation x.
0.0. 1.0. translation y.
0.0. 0.0. 1.0
}
]

View File

@ -3,15 +3,21 @@ Class {
#superclass : #DiyaNode,
#instVars : [
'color',
'vbuffer',
'bbox'
'vbuffer'
],
#category : #'Diya-Graphics'
}
{ #category : #accessing }
Diya2DNode >> boundingBox [
^ bbox
|rec|
children ifNil: [ ^ Rectangle origin: 0@0 corner: 0@0 ].
rec := children first boundingBox.
children do:[:c|
rec = c ifFalse:[
rec := rec merge: c boundingBox]
].
^rec
]
{ #category : #accessing }
@ -24,9 +30,14 @@ Diya2DNode >> color: anObject [
color := anObject
]
{ #category : #accessing }
Diya2DNode >> draw [
]
{ #category : #accessing }
Diya2DNode >> extent [
^ bbox extent
^ self boundingBox extent
]
{ #category : #accessing }
@ -46,6 +57,18 @@ Diya2DNode >> initialize [
vbuffer := nil.
]
{ #category : #'as yet unclassified' }
Diya2DNode >> inner: aPoint [
^ self boundingBox containsPoint: (self local: aPoint)
]
{ #category : #accessing }
Diya2DNode >> local: aPoint [
^((aPoint - (0@0 applyTf: self tf) )/ scale)
rotateBy: rotation about: 0@0.
]
{ #category : #'as yet unclassified' }
Diya2DNode >> recFromBuffer [
|maxX maxY minX minY x y|
@ -72,23 +95,11 @@ Diya2DNode >> setUpShader [
Diya2DNode >> updateTF [
tf := Array2D identity:3.
"translation"
tf := tf +* (Array2D rows: 3 columns: 3 contents: {
1.0. 0.0. translation x.
0.0. 1.0. translation y.
0.0. 0.0. 1.0
}).
tf := tf +* (Array2D translateMatrix2D: translation).
"rotation"
tf := tf +* (Array2D rows: 3 columns: 3 contents:{
rotation cos. (rotation sin) negated. 0.0.
rotation sin. rotation cos. 0.0.
0.0. 0.0. 1.0
}).
tf := tf +* (Array2D rotationMatrix2D: rotation ).
"scale"
tf := tf +* (Array2D rows: 3 columns: 3 contents:{
scale x. 0.0. 0.0.
0.0. scale y. 0.0.
0.0. 0.0. 1.0
}).
tf := tf +* (Array2D scaleMatrix2D: scale).
self parent isRoot ifFalse: [ tf := self parent tf +* tf ].
children ifNotNil: [
children do:[:c| c updateTF ]].

View File

@ -5,7 +5,8 @@ Class {
'texture',
'type',
'border',
'bcolor'
'bcolor',
'bbox'
],
#category : #'Diya-Graphics'
}
@ -20,6 +21,11 @@ Diya2DPrimShape >> borderWidth: w [
border := w
]
{ #category : #accessing }
Diya2DPrimShape >> boundingBox [
^ bbox applyTf: self tf.
]
{ #category : #initialization }
Diya2DPrimShape >> draw [
vbuffer ifNil: [ ^self ].
@ -62,6 +68,11 @@ Diya2DPrimShape >> drawLines [
self subclassResponsibility
]
{ #category : #accessing }
Diya2DPrimShape >> extent [
^ bbox extent
]
{ #category : #initialization }
Diya2DPrimShape >> initialize [
super initialize.
@ -70,15 +81,21 @@ Diya2DPrimShape >> initialize [
type := GL_TRIANGLES.
border := 0.
bcolor := Color white.
bbox := Rectangle origin: 0@0 corner: 0@0.
]
{ #category : #'as yet unclassified' }
Diya2DPrimShape >> inner: aPoint [
bbox ifNil: [ ^false ].
^ bbox containsPoint: (self local: aPoint)
]
{ #category : #initialization }
Diya2DPrimShape >> setUpShader [
super setUpShader
texture ifNotNil:[
super setUpShader.
self shader
setUniform: #u_texture_type value: texture format.
].
setUniform: #u_texture_type value:
(self texture ifNil: [ 0 ] ifNotNil:[self texture format]).
]
{ #category : #accessing }

View File

@ -38,10 +38,27 @@ DiyaBoot >> GLinit. [
OpenGL enable: GL_TEXTURE_2D.
]
{ #category : #events }
DiyaBoot >> bindGlobalEventTo: aNode [
|pointer|
pointer := aNode addNode: (DiyaCircle r: 10) at: 200@200.
pointer color: Color orange.
"pointer borderColor: Color red.
pointer borderWidth: 3."
aNode on: #keydown do:[:e| Transcript show: 'keydown...';cr. running := false.].
aNode on: #quit do: [:e| running := false].
aNode on: #fingerdown do:[:e| self setCursorPosition: e mapped ].
aNode on: #fingermotion do:[:e| self setCursorPosition: e mapped ].
aNode on: #mousemotion do:[:e|
pointer position: e mapped worldPosition.
DiyaRendererContext uniqueInstance mouse: (e mapped x) @ (e mapped y).
].
]
{ #category : #events }
DiyaBoot >> createGLContext [
context := SDL2 glCreateContext: window.
context ifNil: [ ^self error: SDL2 getErrorMessage ].
context ifNil: [ ^DiyaCoreAPIError signal: SDL2 getErrorMessage ].
^context
]
@ -60,7 +77,7 @@ DiyaBoot >> createWindow [
width: display w
height: display h
flags: SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL.
window ifNil: [ ^self error: SDL2 getErrorMessage ].
window ifNil: [ ^DiyaCoreAPIError signal: SDL2 getErrorMessage ].
"handle fullscreen: SDL_WINDOW_FULLSCREEN."
"SDL2 glSetAttribute: SDL_GL_CONTEXT_PROFILE_MASK value: SDL_GL_CONTEXT_PROFILE_ES.
SDL2 glSetAttribute: SDL_GL_CONTEXT_MAJOR_VERSION value: 2.
@ -73,10 +90,20 @@ DiyaBoot >> createWindow [
]
{ #category : #events }
DiyaBoot >> exampleNodes [
|root node tex|
root := DiyaRootNode new.
DiyaBoot >> exampleNodes: tree [
|root node node1 ell tex txtNode|
root := tree addNode: (Diya2DNode new) at: 0@10.
tex := (DiyaImageTex fromFile:Smalltalk imageDirectory / 'assets'/'mrsang.png').
txtNode := root addNode: (DiyaText data: 'Event') at: 10@50.
txtNode color: Color orange.
txtNode extent: 200@40.
node1 := root addNode: (DiyaRectangle size:100@150 shader: DiyaExampleShader uniqueInstance) at: 100 @ 400.
node1 rotation: (Float pi / 8.0).
node1 scale: 1.2@1.2.
node1 on: #mousebuttondown do:[:e| txtNode data: 'Mouse ', (node1 local: e mapped worldPosition) asIntegerPoint asString].
node1 on: #fingerdown do:[:e| Transcript show:'finger down on image'; cr].
node := root addNode: (DiyaRectangle size: 200@200) at: 250 @ 430.
node texture: tex.
node color: (Color r: 1.0 g:1.0 b:1.0 alpha:1.0 ).
@ -92,11 +119,8 @@ DiyaBoot >> exampleNodes [
node borderColor: Color red.
node borderWidth: 3.0."
node := root addNode: (DiyaRectangle size:100@150 shader: DiyaExampleShader uniqueInstance) at: 20 @ 400.
node rotation: (Float pi / -8.0).
node scale: 1.5@1.5.
node := root addNode: (DiyaText data: String loremIpsum) at: 10@340.
node := root addNode: (DiyaText data: String loremIpsum) at: 10@400.
node extent: 250@320.
node wordWrap: true.
@ -105,11 +129,15 @@ DiyaBoot >> exampleNodes [
node borderWidth: 2.0.
node := root addNode: (DiyaCircle r: 100) at: 320@300.
node borderColor: Color red.
node color: Color white.
node borderWidth: 3.0.
node texture: tex.
ell := root addNode: (DiyaEllipse rx:150 ry: 100) at: 320@300.
ell borderColor: Color red.
ell color: Color white.
ell rotation: Float pi / 6.0.
ell borderWidth: 3.0.
"node rotation: Float pi / 2.0."
ell texture: tex.
ell on: #mousebuttondown do:[:e| txtNode data: 'Ellipse clicked', (ell local:e mapped worldPosition) asIntegerPoint asString].
ell on: #fingerdown do:[:e| Transcript show:'finger down on ellipse'; cr].
node := root addNode: (DiyaConvexPolygon points:{250@100. 400@250. 450@80. 350@60}).
@ -117,6 +145,7 @@ DiyaBoot >> exampleNodes [
node borderColor: Color red.
node texture: tex.
node borderWidth: 3.0.
^ root
]
@ -126,7 +155,7 @@ DiyaBoot >> init [
SDL2 setHint: 'SDL_RENDER_DRIVER' value: 'opengles2'.
status := SDL2 init: SDL_INIT_EVERYTHING.
status = 0
ifFalse: [ ^ self error: SDL2 getErrorMessage ].
ifFalse: [ ^ DiyaCoreAPIError signal: SDL2 getErrorMessage ].
display := SDL_DisplayMode externalNew autoRelease.
SDL2 SDLGetCurrentDisplayMode: display from:0.
DiyaRendererContext reset.
@ -144,18 +173,6 @@ DiyaBoot >> initialize [
clock := DiyaClock uniqueInstance.
]
{ #category : #events }
DiyaBoot >> processEvent: event [
|mappedEvt|
mappedEvt := event mapped.
mappedEvt type = SDL_KEYDOWN ifTrue: [ Transcript show: 'keydown...';cr. ^running := false. ].
mappedEvt type = SDL_QUIT ifTrue:[ ^running:= false ].
mappedEvt type = SDL_FINGERDOWN ifTrue:[^self setCursorPosition: mappedEvt ].
mappedEvt type = SDL_FINGERMOTION ifTrue:[^self setCursorPosition: mappedEvt ].
mappedEvt type = SDL_MOUSEMOTION ifTrue:[DiyaRendererContext uniqueInstance mouse: (mappedEvt x) @ (mappedEvt y)].
mappedEvt free.
]
{ #category : #events }
DiyaBoot >> randomColorChannel [
| rand |
@ -169,12 +186,14 @@ DiyaBoot >> randomColorChannel [
DiyaBoot >> render [
|event root text delta fps|
event := SDL_Event new.
root := self exampleNodes.
root := DiyaRootNode new.
self exampleNodes: root.
DiyaRenderer uniqueInstance root: root.
text := root addNode:(DiyaText data: 'tick') at: (display w - 80)@40.
text extent: 80@40.
text fontSize: 18.
text color: Color red.
self bindGlobalEventTo: root.
DiyaRendererContext uniqueInstance.
self GLinit.
[ running ] whileTrue: [
@ -183,7 +202,7 @@ DiyaBoot >> render [
text data: ('FPS:', fps asString).
DiyaClock uniqueInstance tick.
[(SDL2 pollEvent: event) > 0] whileTrue: [
self processEvent: event
root trigger: (DiyaEvent from: event mapped).
].
DiyaRenderer uniqueInstance render.
@ -264,7 +283,7 @@ DiyaBoot >> showSystemInfo [
{ #category : #events }
DiyaBoot >> startx [
display ifNil: [ ^self error: 'Please run #init before this method' ].
display ifNil: [ ^DiyaCoreAPIError signal: 'Please run #init before this method' ].
self createWindow.
self createGLContext.
"SDL2 glMakeCurrent: window context: context."

View File

@ -0,0 +1,5 @@
Class {
#name : #DiyaCoreAPIError,
#superclass : #DiyaError,
#category : #'Diya-Events'
}

View File

@ -47,6 +47,13 @@ DiyaEllipse >> initialize [
shader := DiyaEllipseShader uniqueInstance.
]
{ #category : #initialization }
DiyaEllipse >> inner: aPoint [
|dxy|
dxy := self local: aPoint.
^ ((((dxy x) ** 2)/(rx**2)) + (((dxy y) ** 2) / (ry**2))) < 1.
]
{ #category : #accessing }
DiyaEllipse >> rx [
^ rx

5
Diya/DiyaError.class.st Normal file
View File

@ -0,0 +1,5 @@
Class {
#name : #DiyaError,
#superclass : #Error,
#category : #'Diya-Events'
}

40
Diya/DiyaEvent.class.st Normal file
View File

@ -0,0 +1,40 @@
Class {
#name : #DiyaEvent,
#superclass : #DiyaBaseObject,
#instVars : [
'enable',
'mapped'
],
#category : #'Diya-Events'
}
{ #category : #'instance creation' }
DiyaEvent class >> from: mappedEvt [
^ self new mapped: mappedEvt; yourself
]
{ #category : #accessing }
DiyaEvent >> enable [
^ enable
]
{ #category : #initialization }
DiyaEvent >> initialize [
super initialize.
enable := true.
]
{ #category : #accessing }
DiyaEvent >> mapped [
^ mapped
]
{ #category : #accessing }
DiyaEvent >> mapped: anObject [
mapped := anObject
]
{ #category : #initialization }
DiyaEvent >> preventDefault [
enable := false
]

View File

@ -26,7 +26,7 @@ DiyaFFIBase class >> moduleName [
^ aName
].
].
self error: 'Unable to find FFI library (', self checkSymbol, ')'.
DiyaCoreAPIError signal: 'Unable to find FFI library (', self checkSymbol, ')'.
]
{ #category : #'library path' }

View File

@ -22,7 +22,7 @@ DiyaFontManager class >> searchPaths [
{ #category : #initialization }
DiyaFontManager >> defaultFamily [
^self error: 'No font found'
^'Ubuntu'
]
@ -66,7 +66,7 @@ DiyaFontManager >> loadFile: aFile [
numfaces ifNil: [ numfaces := face numFaces ].
self loadFace: face.
i := i + 1.
i < numfaces ] whileTrue: [ ]
i < numfaces ] whileTrue
]
{ #category : #initialization }

View File

@ -76,6 +76,11 @@ DiyaLine >> initialize [
border := 1.0
]
{ #category : #'as yet unclassified' }
DiyaLine >> inner: aPoint [
^false
]
{ #category : #accessing }
DiyaLine >> to [
^ to

View File

@ -10,11 +10,14 @@ Class {
'tf',
'shader',
'context',
'dirty'
'dirty',
'ehandlers',
'root'
],
#pools : [
'OpenGLConstants',
'OpenGLTypes'
'OpenGLTypes',
'SDL2Constants'
],
#category : #'Diya-Graphics'
}
@ -35,6 +38,7 @@ DiyaNode >> addNode: node at: pos [
node parent: self.
node position: pos.
children add: node.
node root: self root.
^ node
]
@ -65,12 +69,28 @@ DiyaNode >> initialize [
shader := nil.
context := DiyaRendererContext uniqueInstance.
children := OrderedCollection new.
dirty := false
dirty := false.
ehandlers := Dictionary new.
root := nil.
]
{ #category : #'as yet unclassified' }
DiyaNode >> inner: aPoint [
^ self subclassResponsibility
]
{ #category : #testing }
DiyaNode >> isRoot [
^false
^ false
]
{ #category : #convenience }
DiyaNode >> on: eventName do: aBlock [
|evtCode|
evtCode := SDL2Constants bindingOf: ('SDL_', eventName asUppercase).
evtCode ifNil: [ ^DiyaCoreAPIError signal: 'Unknow event ', eventName ].
ehandlers at: evtCode value put: aBlock.
]
{ #category : #accessing }
@ -103,6 +123,16 @@ DiyaNode >> render [
children do: [:c | c render ].
]
{ #category : #accessing }
DiyaNode >> root [
^ root
]
{ #category : #accessing }
DiyaNode >> root: anObject [
root := anObject
]
{ #category : #accessing }
DiyaNode >> rotation [
^ rotation
@ -159,10 +189,21 @@ DiyaNode >> shader: anObject [
{ #category : #accessing }
DiyaNode >> tf [
parent ifNil: [ self error: 'TF: This node is not attached to the main tree' ].
parent ifNil: [ ^ DiyaCoreAPIError signal: 'TF: This node is not attached to the main tree' ].
^ tf
]
{ #category : #'as yet unclassified' }
DiyaNode >> trigger: evt [
evt enable ifFalse:[^self].
ehandlers at: evt mapped type ifPresent:[:handler| handler value: evt].
children ifNil: [^self].
evt enable ifTrue: [
"evt mapped triggableOn: children first."
children select: [:node | evt mapped triggableOn: node ] thenDo:[:node| node trigger: evt]
].
]
{ #category : #accessing }
DiyaNode >> update [
^self subclassResponsibility

View File

@ -4,6 +4,11 @@ Class {
#category : #'Diya-Graphics'
}
{ #category : #accessing }
DiyaRootNode >> boundingBox [
^ Rectangle origin: 0@0 corner: context resolution
]
{ #category : #accessing }
DiyaRootNode >> draw [
OpenGL clearColorR: 0.0 G: 0.0 B: 0.0 A:0.
@ -11,16 +16,27 @@ DiyaRootNode >> draw [
context vbo bind: GL_ARRAY_BUFFER.
]
{ #category : #accessing }
DiyaRootNode >> extent [
^ context resolution
]
{ #category : #initialization }
DiyaRootNode >> initialize [
super initialize.
parent := self.
shader := nil.
root := self.
]
{ #category : #testing }
{ #category : #'as yet unclassified' }
DiyaRootNode >> inner: aPoint [
^true
]
{ #category : #accessing }
DiyaRootNode >> isRoot [
^ true
^true
]
{ #category : #initialization }

View File

@ -19,7 +19,7 @@ DiyaSingleton class >> initialize [
{ #category : #'instance creation' }
DiyaSingleton class >> new [
self error: 'Use #uniqueInstance'
^ DiyaCoreAPIError signal: 'Use #uniqueInstance'
]
{ #category : #'instance creation' }

View File

@ -1,13 +1,14 @@
Class {
#name : #DiyaText,
#superclass : #Diya2DNode,
#superclass : #Diya2DPrimShape,
#instVars : [
'fontStyle',
'fontSize',
'fontName',
'data',
'style',
'wrap'
'wrap',
'texheight'
],
#pools : [
'FT2Types'
@ -36,23 +37,8 @@ DiyaText >> data: anObject [
dirty := true
]
{ #category : #accessing }
DiyaText >> draw [
data ifNil: [ ^self ].
self shader
setUniform: #u_texture_type value: self texture format.
"configure vao vbo for texture QUAD"
self texture setup.
context texture0 setImage2D: self texture.
context texture0 active.
context vao enableAttribute: 0.
OpenGLVertexArray vertexAttributePointerIndex: 0 size:4 type: GL_FLOAT normalized: GL_FALSE stride: 16 pointer: nil .
context vbo data: GL_ARRAY_BUFFER data: vbuffer usage: GL_STATIC_DRAW.
OpenGL drawArrays: GL_TRIANGLES first:0 count: ((vbuffer size) >> 2).
context vao disableAttribute: 0.
"reset value"
self texture drop.
{ #category : #initialization }
DiyaText >> drawBorder [
]
{ #category : #accessing }
@ -146,7 +132,8 @@ DiyaText >> initialize [
self fontName: 'Ubuntu' style:'Regular' size: 16.
data := nil.
wrap := false.
bbox := nil
bbox := nil.
texheight := 0.
]
{ #category : #'as yet unclassified' }
@ -159,7 +146,14 @@ DiyaText >> nextSpaceFrom: index [
{ #category : #accessing }
DiyaText >> texture [
^style textureOf: self fontSize
|tex|
tex := style textureOf: self fontSize.
texheight = tex height ifFalse: [
texheight := tex height.
self update.
dirty := false.
].
^tex
]
{ #category : #initialization }

View File

@ -136,7 +136,7 @@ OpenGLSL >> checkStatus:status of: id [
] ifFalse: [
OpenGLSL getShaderInfoLogOf: id maxLength: (infoLength at: 1) lengthPtr: nil buffer: buffer
].
^self error: buffer asString
^DiyaCoreAPIError signal: buffer asString
].
^self
@ -222,7 +222,7 @@ OpenGLSL >> locateUniforms [
OpenGLSL >> setUniform: uname value: values [
|uniform|
uniform := uniforms at: uname asSymbol ifAbsent:[
^self error: 'Uniform ', uname, ' is not defined in this program'].
^DiyaCoreAPIError signal: 'Uniform ', uname, ' is not defined in this program'].
uniform value: values
]

View File

@ -1,5 +1,20 @@
Extension { #name : #Point }
{ #category : #'*Diya' }
Point >> applyTf: tf [
^(tf +* (self asArray3F)) asPoint
]
{ #category : #'*Diya' }
Point >> asArray3F [
^ self asArray3F: 1.0
]
{ #category : #'*Diya' }
Point >> asArray3F: z [
^ { self x. self y. z }
]
{ #category : #'*Diya' }
Point >> asGLCoord [
|res|

View File

@ -0,0 +1,8 @@
Extension { #name : #Rectangle }
{ #category : #'*Diya' }
Rectangle >> applyTf: tf [
^ Rectangle
origin: (self origin applyTf: tf)
corner: (self corner applyTf: tf)
]

View File

@ -0,0 +1,6 @@
Extension { #name : #SDL2MappedEvent }
{ #category : #'*Diya' }
SDL2MappedEvent >> triggableOn: aNode [
^aNode isRoot
]

View File

@ -0,0 +1,11 @@
Extension { #name : #'SDL_MouseButtonEvent' }
{ #category : #'*Diya' }
SDL_MouseButtonEvent >> triggableOn: aNode [
^ aNode inner: self worldPosition
]
{ #category : #'*Diya' }
SDL_MouseButtonEvent >> worldPosition [
^ (self x) @ (DiyaRendererContext uniqueInstance resolution y - self y )
]

View File

@ -0,0 +1,12 @@
Extension { #name : #'SDL_MouseMotionEvent' }
{ #category : #'*Diya' }
SDL_MouseMotionEvent >> triggableOn: aNode [
^false
"^ aNode inner: self worldPosition "
]
{ #category : #'*Diya' }
SDL_MouseMotionEvent >> worldPosition [
^ (self x) @ (DiyaRendererContext uniqueInstance resolution y - self y )
]

View File

@ -0,0 +1,11 @@
Extension { #name : #'SDL_TouchFingerEvent' }
{ #category : #'*Diya' }
SDL_TouchFingerEvent >> triggableOn: aNode [
^ aNode inner: self worldPosition
]
{ #category : #'*Diya' }
SDL_TouchFingerEvent >> worldPosition [
^ (self x) @ (DiyaRendererContext uniqueInstance resolution y - self y )
]