I fixed the bug not to display well when you resize window.
[rs-app/mswp.git] / src / jsMain / kotlin / net / ocsoft / mswp / ui / ShadowMap.kt
1 package net.ocsoft.mswp.ui
2
3 import kotlin.collections.sort
4 import kotlin.collections.last
5 import kotlin.math.pow
6 import kotlin.math.sqrt
7 import kotlin.math.absoluteValue
8
9 import org.w3c.dom.HTMLCanvasElement
10
11 import org.khronos.webgl.WebGLRenderingContext
12 import org.khronos.webgl.WebGLTexture
13 import org.khronos.webgl.WebGLFramebuffer
14 import org.khronos.webgl.WebGLProgram
15 import org.khronos.webgl.WebGLBuffer
16 import org.khronos.webgl.WebGLRenderbuffer
17 import org.khronos.webgl.Int32Array
18 import org.khronos.webgl.Float64Array
19 import org.khronos.webgl.Float32Array
20 import org.khronos.webgl.get
21
22 import net.ocsoft.mswp.Camera
23 import net.ocsoft.mswp.Orthographic
24 import net.ocsoft.mswp.ui.grid.Buttons
25 import net.ocsoft.mswp.ui.grid.Display
26
27 class ShadowMap {
28
29     /**
30      * orthographic parameter
31      */
32     var orthoGraphic: Orthographic? = null
33  
34     /**
35      * frame buffer size
36      */
37     var frameBufferSize: IntArray? = null 
38        
39     /**
40      * set up 
41      */
42     fun setup(
43         grid: Grid,
44         gl: WebGLRenderingContext) {
45         
46         val viewport = gl.getParameter(
47             WebGLRenderingContext.VIEWPORT) as Int32Array?
48         val canvas = gl.canvas as HTMLCanvasElement 
49
50         frameBufferSize = intArrayOf(
51             Display.calcPower2Value(canvas.width),
52             Display.calcPower2Value(canvas.height))
53
54
55         val texture = createShadowDepthTexture(gl,
56             frameBufferSize!![0], frameBufferSize!![1])
57         val depthBuffer = createRenderDepthBuffer(gl,
58             frameBufferSize!![0], frameBufferSize!![1])
59
60         val renderingCtx = grid.renderingCtx
61         renderingCtx.shadowDepthTexture = texture
62         renderingCtx.depthForShadowDepth = depthBuffer
63         renderingCtx.shadowDepthFramebuffer =
64             createFrameBufferForShadowDepth(gl, texture!!, depthBuffer!!)
65
66         orthoGraphic = calcOrtho(grid)
67     } 
68
69
70     
71     /**
72      * draw shadow depth
73      */
74     fun drawScene(
75         grid: Grid,
76         gl: WebGLRenderingContext) {
77
78         val savedFramebuffer = gl.getParameter(
79             WebGLRenderingContext.FRAMEBUFFER_BINDING) as WebGLFramebuffer?
80         val savedViewport = gl.getParameter(
81             WebGLRenderingContext.VIEWPORT) as Int32Array
82
83         gl.bindFramebuffer(
84             WebGLRenderingContext.FRAMEBUFFER,
85             grid.renderingCtx.shadowDepthFramebuffer)
86         gl.viewport(0, 0, frameBufferSize!![0], frameBufferSize!![1])
87         beginEnv(grid, gl)
88         drawSceneI(grid, gl)
89         endEnv(grid, gl)
90         gl.flush()
91         
92         gl.viewport(savedViewport[0], savedViewport[1],
93             savedViewport[2], savedViewport[3])
94         gl.bindFramebuffer(
95             WebGLRenderingContext.FRAMEBUFFER,
96             savedFramebuffer)
97
98     }
99
100     /**
101      * draw shadow depth
102      */
103     fun drawSceneI(
104         grid: Grid,
105         gl: WebGLRenderingContext) {
106
107         val savedProgram = gl.getParameter(
108             WebGLRenderingContext.CURRENT_PROGRAM) as WebGLProgram?
109         gl.useProgram(grid.renderingCtx.shadowMapShaderProgram) 
110         updateProjectionMatrix(grid, gl)
111         drawButtons(grid, gl)
112         drawBoard(grid, gl)
113         gl.useProgram(savedProgram) 
114     }
115
116     /**
117      * draw buttons
118      */
119     fun drawButtons(
120         grid: Grid,
121         gl: WebGLRenderingContext) {
122         val savedArrayBuffer = gl.getParameter(
123                 WebGLRenderingContext.ARRAY_BUFFER_BINDING) as WebGLBuffer?
124
125         grid.display.bindButtonVerticesBuffer(gl)
126         for (rowIndex in 0 until grid.rowCount) {
127             for (colIndex in 0 until grid.columnCount) { 
128                 grid.display.drawButtonI(gl, rowIndex, colIndex)
129             }
130         }
131         gl.bindBuffer(
132             WebGLRenderingContext.ARRAY_BUFFER,
133             savedArrayBuffer)
134     }
135
136     /**
137      * draw board 
138      */
139     fun drawBoard(
140         grid: Grid,
141         gl: WebGLRenderingContext) {
142         val savedArrayBuffer = gl.getParameter(
143                 WebGLRenderingContext.ARRAY_BUFFER_BINDING) as WebGLBuffer?
144
145         grid.display.drawBoardI(gl)
146         gl.bindBuffer(
147             WebGLRenderingContext.ARRAY_BUFFER,
148             savedArrayBuffer)
149     }
150
151
152     /**
153      * debug button vertices
154      */
155     fun debugButtonVertics(
156         grid: Grid,
157         rowIndex: Int, colIndex: Int) {
158         val glrs = grid.glrs!!
159         val projMat = calcProjectionMatrix(grid)
160         val lookFromMat = getLookFromLightPoint(grid)!!
161         val orthoMat = getOrthoGraphicMatrix(grid)!!
162         val modelMat = grid.display.getButtonMatrixForDrawing(
163             rowIndex, colIndex)
164         val lookFromMatRef = glrs.matrix_create_with_components_col_order(
165             Float64Array(Array<Double>(lookFromMat.length) {
166                 lookFromMat[it].toDouble() }))
167  
168         val orthoMatRef = glrs.matrix_create_with_components_col_order(
169             Float64Array(Array<Double>(orthoMat.length) {
170                 orthoMat[it].toDouble() }))
171
172         val matRef = grid.glrs!!.matrix_create_with_components_col_order(
173             Float64Array(Array<Double>(projMat!!.length) {
174                 projMat[it].toDouble() }))
175         val modelMatRef = grid.glrs!!.matrix_create_with_components_col_order(
176             Float64Array(Array<Double>(modelMat.size) {
177                 modelMat[it].toDouble() }))
178         grid.glrs!!.matrix_multiply_mut(matRef, modelMatRef)
179         val vertices = grid.buttons.mineButton.vertices
180         println("buton[${rowIndex}][${colIndex}]")
181         for (idx in 0 until vertices.size / 3) {
182             val srcPt = Float32Array(
183                 Array<Float>(4) {
184                     idx1 -> 
185                     if (idx1 < 3) {
186                         vertices[3 * idx + idx1]
187                     } else {
188                         1f
189                     } 
190                 })
191
192             val pt = glrs.matrix_apply_r_32(matRef, srcPt)
193             println("${pt!![0]}, ${pt!![1]}, ${pt!![2]}, ${pt!![3]}")
194         }
195         
196         glrs.matrix_release(lookFromMatRef)
197         glrs.matrix_release(orthoMatRef) 
198         glrs.matrix_release(modelMatRef)
199         glrs.matrix_release(matRef) 
200
201     }
202
203     /**
204      * setup env
205      */
206     fun beginEnv(grid: Grid,
207         gl: WebGLRenderingContext) {
208         val savedBackColor = grid.backColor.copyOf()
209         for (i in 0 until grid.backColor.size) {
210             grid.backColor[i] = 0.0f
211         } 
212         grid.backColor[0] = 1f
213         grid.setupEnv(gl)
214
215         for (i in 0 until grid.backColor.size) {
216             grid.backColor[i] = savedBackColor[i]
217         } 
218
219         gl.disable(WebGLRenderingContext.BLEND) 
220     }
221
222     /**
223      * restore gl 
224      */
225     fun endEnv(grid: Grid,
226         gl: WebGLRenderingContext) {
227     }
228
229
230
231     /**
232      * update projection matrix
233      */
234     fun updateProjectionMatrix(
235         grid: Grid,
236         gl: WebGLRenderingContext) {
237          val shaderProg = gl.getParameter(
238             WebGLRenderingContext.CURRENT_PROGRAM) as WebGLProgram?
239         if (shaderProg != null) {
240             val uProjMat = gl.getUniformLocation(shaderProg, 
241                 "uProjectionMatrix")    
242             if (uProjMat != null) { 
243                 val projMat = calcProjectionMatrix(grid)
244                 gl.uniformMatrix4fv(uProjMat, false, projMat!!)
245             }
246         }
247     } 
248     /**
249      * setup shadow setting for drawing
250      */
251     fun setupShadowSettingForDrawing(
252         grid: Grid,
253         gl: WebGLRenderingContext) {
254         attachShadowProjectionMatrix(gl, grid)
255         attachShadowTexture(gl, grid.renderingCtx)
256     }
257     /**
258      * attach shadow projection matrix into current program
259      */
260     fun attachShadowProjectionMatrix(
261         gl: WebGLRenderingContext,
262         grid: Grid) {
263         val shaderProg = gl.getParameter(
264             WebGLRenderingContext.CURRENT_PROGRAM) as WebGLProgram?
265         if (shaderProg != null) {
266             val projMat = getShadowProjectionMatrixForTexture(grid) 
267             if (projMat != null) {
268                 val uProjMat = gl.getUniformLocation(shaderProg, 
269                     "uShadowMapProjectionMatrix")    
270                 if (uProjMat != null) { 
271                     gl.uniformMatrix4fv(uProjMat, false, projMat)
272                 }
273             }
274         } 
275     }
276     /**
277      * attach shadow mapping texture
278      */
279     fun attachShadowTexture(
280         gl: WebGLRenderingContext,
281         renderingCtx: RenderingCtx) {
282         val shaderProg = gl.getParameter(
283             WebGLRenderingContext.CURRENT_PROGRAM) as WebGLProgram?
284         if (shaderProg != null) {
285             val uTexLoc = gl.getUniformLocation(shaderProg, 
286                     "uShadowDepthSampler") 
287             if (uTexLoc != null) {
288                 var txtNumber = Textures.ShadowmappingTextureIndex
289                 txtNumber -= WebGLRenderingContext.TEXTURE0
290                 gl.uniform1i(uTexLoc, txtNumber)
291            }
292         }
293     }
294
295     /**
296      * create frame buffer for shadow depth
297      */
298     fun createFrameBufferForShadowDepth(
299         gl: WebGLRenderingContext,
300         texture: WebGLTexture,
301         depthBuffer: WebGLRenderbuffer): WebGLFramebuffer? {
302         val result = gl.createFramebuffer()
303
304         val savedFramebuffer = gl.getParameter(
305             WebGLRenderingContext.FRAMEBUFFER_BINDING) as WebGLFramebuffer?
306
307         gl.bindFramebuffer(WebGLRenderingContext.FRAMEBUFFER, result)
308         
309         gl.framebufferTexture2D(WebGLRenderingContext.FRAMEBUFFER,
310             WebGLRenderingContext.COLOR_ATTACHMENT0,
311             WebGLRenderingContext.TEXTURE_2D,
312             texture, 0)
313
314         gl.framebufferRenderbuffer(WebGLRenderingContext.FRAMEBUFFER,
315             WebGLRenderingContext.DEPTH_ATTACHMENT,
316             WebGLRenderingContext.RENDERBUFFER, depthBuffer)
317          
318
319         gl.bindFramebuffer(WebGLRenderingContext.FRAMEBUFFER, savedFramebuffer)
320
321         return result
322     }
323
324
325     /**
326      * create render buffer for shadow depth
327      */
328     fun createRenderDepthBuffer(
329         gl: WebGLRenderingContext,
330         width: Int,
331         height: Int): WebGLRenderbuffer? {
332         val result = gl.createRenderbuffer()
333         val savedRenderbuffer = gl.getParameter(
334             WebGLRenderingContext.RENDERBUFFER_BINDING) as WebGLRenderbuffer?
335         gl.bindRenderbuffer(WebGLRenderingContext.RENDERBUFFER, result)
336
337         gl.renderbufferStorage(WebGLRenderingContext.RENDERBUFFER,
338             WebGLRenderingContext.DEPTH_COMPONENT16,
339             width, height)  
340        
341         gl.bindRenderbuffer(
342             WebGLRenderingContext.RENDERBUFFER,
343             savedRenderbuffer) 
344         return result
345     }
346
347
348     /**
349      * create shadow depth texture
350      */
351     fun createShadowDepthTexture(
352         gl: WebGLRenderingContext,
353         width: Int,
354         height: Int): WebGLTexture? {
355         
356         val savedTexture = gl.getParameter(
357             WebGLRenderingContext.TEXTURE_BINDING_2D) as WebGLTexture?
358         val savedActiveTexture = gl.getParameter(
359             WebGLRenderingContext.ACTIVE_TEXTURE) as Int  
360         val result = gl.createTexture()
361         var txtNumber = Textures.ShadowmappingTextureIndex
362         gl.activeTexture(txtNumber)
363         gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, result)
364         gl.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0,
365             WebGLRenderingContext.RGBA, width, height,
366             0, WebGLRenderingContext.RGBA,
367             WebGLRenderingContext.UNSIGNED_BYTE, null)
368         gl.texParameteri(WebGLRenderingContext.TEXTURE_2D,
369             WebGLRenderingContext.TEXTURE_WRAP_S,
370             WebGLRenderingContext.CLAMP_TO_EDGE);
371         gl.texParameteri(WebGLRenderingContext.TEXTURE_2D,
372             WebGLRenderingContext.TEXTURE_WRAP_T,
373             WebGLRenderingContext.CLAMP_TO_EDGE);
374         gl.texParameteri(WebGLRenderingContext.TEXTURE_2D,
375             WebGLRenderingContext.TEXTURE_MIN_FILTER,
376             WebGLRenderingContext.LINEAR);
377
378         gl.activeTexture(savedActiveTexture)
379         gl.bindTexture(WebGLRenderingContext.TEXTURE_2D, savedTexture)
380
381         return result
382     }
383     /**
384      * calc orth graphic parameter
385      */
386     fun calcOrtho(grid: Grid): Orthographic {
387         return calcOrtho(grid.glrs!!,
388             grid.pointLight!!,
389             grid.camera!!,  
390             grid.display!!) 
391     }    
392
393     /**
394      * calc orth graphic parameter
395      */
396     fun calcOrtho(glrs: glrs.InitOutput,
397         pointLight: net.ocsoft.mswp.PointLight,
398         camera: Camera,
399         display: Display): Orthographic {
400         val bounds = calcBoundsForRendering(
401             glrs, pointLight, camera, display)
402         val nearFarIndices = intArrayOf(0, 1)  
403         if (bounds[2][0].absoluteValue >  bounds[2][1].absoluteValue) {
404             nearFarIndices[0] = 1
405             nearFarIndices[1] = 0
406         } 
407         val adjustments = floatArrayOf(1f, 1f) 
408         
409         return Orthographic(
410             bounds[0][0] * adjustments[0], bounds[0][1] * adjustments[1],
411             bounds[1][0] * adjustments[0], bounds[1][1] * adjustments[1],
412             -bounds[2][nearFarIndices[0]], -bounds[2][nearFarIndices[1]])
413     }
414
415     /**
416      * calculate bounds for rendering
417      */
418     fun calcBoundsForRendering(
419         glrs: glrs.InitOutput,
420         pointLight: net.ocsoft.mswp.PointLight,
421         camera: Camera,
422         display: Display): Array<FloatArray> {
423         val ptLight = pointLight.point
424         val center = camera.center
425         val normVec = Float64Array(Array<Double>(ptLight.size) { 
426             ptLight[it].toDouble() - center[it].toDouble()
427         })
428         var normVecLength = 0.0
429         
430         for (i in 0 until normVec.length) {
431             normVecLength += normVec[i].pow(2.0)
432         }
433         normVecLength = sqrt(normVecLength)
434         
435
436         val vertices = getGameBounds(display)
437
438         val matRef = getLookFromLightPointI(glrs, camera, pointLight)
439
440          
441         var bounds = Array<Array<Float?>>(3) { Array<Float?>(2) { null } }
442         val valueSelector: Array<(Int, Int, Float)->Boolean> = arrayOf(
443             {
444                 idx0, idx1, value -> value < bounds[idx0][idx1]!! 
445             },
446             {
447                 idx0, idx1, value -> value > bounds[idx0][idx1]!! 
448             }
449         )
450         vertices.forEach {
451             vertexElem ->  
452             val vertexSrc = Float32Array(Array<Float>(4) {
453                 if (it < vertexElem.size) {
454                     vertexElem[it]
455                 } else {
456                     1f
457                 }
458             })
459             val vertex = glrs.matrix_apply_r_32(matRef, vertexSrc)!!
460             for (idx0 in 0 until bounds.size) {
461                 for (idx1 in 0 until bounds[idx0].size) { 
462                     if (bounds[idx0][idx1] == null) {
463                         bounds[idx0][idx1] = vertex[idx0] 
464                     } else if (valueSelector[idx1](
465                         idx0, idx1, vertex[idx0])) {
466                         bounds[idx0][idx1] = vertex[idx0]
467                     } 
468                 }
469             }
470         } 
471         
472         val result = Array(bounds.size) {
473             idx0 ->
474             FloatArray(bounds[idx0].size) {
475                 bounds[idx0][it]!!
476             }
477         }
478             
479         glrs.matrix_release(matRef)
480
481         return result
482     }
483    
484      
485     
486
487     /**
488      * calc near vertex distance from plane
489      * planeRef is a plane on ceter
490      */
491     private fun calcFarFromEye(glrs: glrs.InitOutput,
492         planeRef: Number,
493         vertices: Array<FloatArray>):FloatArray?  {
494         
495         val floatIdxRef = glrs.plane_sort_points_0(planeRef, vertices) 
496        
497         val floatKeysRef = glrs.float_indices_get_float_keys(
498             floatIdxRef) 
499         var result : FloatArray? = null
500         if (glrs.float_vec_size(floatKeysRef).toInt() > 0) {
501             val floatRef = glrs.float_vec_get(floatKeysRef, 0)
502             
503             val indices = glrs.float_indices_get_indices(
504                 floatIdxRef, floatRef)
505             if (indices != null && indices.length > 0) {
506                 result = vertices[indices[0]]
507             }
508             glrs.float_release(floatRef)
509         }
510         glrs.float_vec_release(floatKeysRef)
511         glrs.float_indices_release(floatIdxRef)
512         return result 
513     }
514     
515           
516      
517
518     /**
519      * get buttons movement vertices
520      */
521     fun getButtonsMovementVertices(
522         display: Display): Array<FloatArray> {
523         return display.calcButtonsMovingBounds() 
524     }
525
526     /**
527      * calculate all of coordinates for game
528      */
529     fun getGameBounds(
530         display: Display): Array<FloatArray> {
531         return display.calcGameBounds()
532     }
533
534     /**
535      * calculate projection matrix from light point view
536      */
537     fun calcProjectionMatrix(grid: Grid): Float32Array? {
538         val glrs = grid.glrs!!
539         val matRef = calcProjectionMatrixI(grid)
540         val result = glrs.matrix_get_components_col_order_32(matRef)
541         glrs.matrix_release(matRef)
542         return result
543     }
544     /**
545      * calculate projection matrix from light point view
546      */
547     fun calcProjectionMatrixI(grid: Grid): Number {
548         val glrs = grid.glrs!!
549         val ortho = orthoGraphic!!
550         val result = glrs.matrix_new_ortho(
551             ortho.left, ortho.right,
552             ortho.bottom, ortho.top, ortho.zNear, ortho.zFar)
553         val lookatMatRef = getLookFromLightPointI(grid)
554         glrs.matrix_multiply_mut(result, lookatMatRef)
555         glrs.matrix_release(lookatMatRef)
556         return result
557     }
558
559
560     /**
561      * get the matrix looking at from light point.
562      */
563     fun getLookFromLightPoint(grid: Grid): Float32Array? {
564
565         val glrs = grid.glrs!!
566         val matRef = getLookFromLightPointI(grid)
567         val result = glrs.matrix_get_components_col_order_32(matRef) 
568         glrs.matrix_release(matRef)
569         return result
570     }
571
572
573     /**
574      * get matrix looking at from light poing
575      */
576     private fun getLookFromLightPointI(grid: Grid): Number {
577         val result = getLookFromLightPointI(
578             grid.glrs!!, grid.camera!!, grid.pointLight!!)
579         return result
580      }
581     
582     /**
583      * get matrix looking at from light poing
584      */
585     private fun getLookFromLightPointI(
586         glrs: glrs.InitOutput,
587         camera: Camera,
588         pointLight: net.ocsoft.mswp.PointLight): Number {
589         val ptLight = pointLight.point
590         val camCenter = camera.center
591         val camUp = camera.up
592
593         val result = glrs.matrix_new_look_at(
594             ptLight[0], ptLight[1], ptLight[2],
595             camCenter[0], camCenter[1], camCenter[2],
596             camUp[0], camUp[1], camUp[2])
597         return result
598      }
599  
600     /**
601      * othographic matrix
602      */
603     private fun getOrthoGraphicMatrix(grid: Grid) :Float32Array? {
604         return getOrthoGraphicMatrix(grid.glrs!!)
605     }
606  
607     /**
608      * othographic matrix
609      */
610     private fun getOrthoGraphicMatrix(glrs: glrs.InitOutput) :Float32Array? {
611         val ortho = this.orthoGraphic
612         var result: Float32Array? = null
613         if (ortho != null) {
614             val matRef = glrs.matrix_new_ortho(
615                 ortho.left, ortho.right,
616                 ortho.bottom, ortho.top, ortho.zNear, ortho.zFar)
617             result = glrs.matrix_get_components_col_order_32(matRef)
618             glrs.matrix_release(matRef) 
619         }
620         return result
621     }
622
623     /**
624      * get shadow mapping projection matrix for texture coordinate
625      */
626     fun getShadowProjectionMatrixForTexture(grid: Grid): 
627         Float32Array? {
628         val ortho = this.orthoGraphic
629         var result: Float32Array? = null
630         if (ortho != null) {
631             val glrs = grid.glrs!!
632             val projRef = calcProjectionMatrixI(grid)
633             val matRef = glrs.matrix_create_with_components_col_order(
634                Textures.matrixFromProjectToTexture)
635             glrs.matrix_multiply_mut(matRef, projRef)
636             result = glrs.matrix_get_components_col_order_32(matRef)
637             glrs.matrix_release(matRef) 
638             glrs.matrix_release(projRef)
639         }
640         return result
641  
642     }
643  
644 }
645
646 // vi: se ts=4 sw=4 et: