func (r *GlowRenderer) Draw() (err error) { gl.UseProgram(r.shader) gl.Uniform1i(r.textureUnitLoc, 0) gl.BindBuffer(gl.ARRAY_BUFFER, r.coords) gl.EnableVertexAttribArray(r.positionLoc) gl.VertexAttribPointer(r.positionLoc, 3, gl.FLOAT, false, 5*4, gl.PtrOffset(0)) gl.EnableVertexAttribArray(r.textureLoc) gl.VertexAttribPointer(r.textureLoc, 2, gl.FLOAT, false, 5*4, gl.PtrOffset(3*4)) gl.Uniform1i(r.blurAmountLoc, int32(r.blur)) gl.Uniform1f(r.blurScaleLoc, r.scale) gl.Uniform1f(r.blurStrengthLoc, r.strength) gl.Uniform2f(r.bufferDimensionsLoc, float32(r.width), float32(r.height)) gl.BindFramebuffer(gl.FRAMEBUFFER, r.BlurFb) gl.Viewport(0, 0, int32(r.width), int32(r.height)) gl.ActiveTexture(gl.TEXTURE0) gl.BindTexture(gl.TEXTURE_2D, r.GlowTex) gl.Uniform1i(r.orientationLoc, 0) gl.DrawArrays(gl.TRIANGLE_STRIP, 0, 4) gl.BindFramebuffer(gl.FRAMEBUFFER, 0) gl.Viewport(0, 0, int32(r.oldwidth), int32(r.oldheight)) gl.BlendFunc(gl.ONE, gl.ONE) gl.BindTexture(gl.TEXTURE_2D, r.BlurTex) gl.Uniform1i(r.orientationLoc, 1) gl.DrawArrays(gl.TRIANGLE_STRIP, 0, 4) gl.BindBuffer(gl.ARRAY_BUFFER, 0) gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) return nil }
func (lr *LinesRenderer) Draw(line *LineGeometry, mv mgl32.Mat4, style *LineStyle) (err error) { var ( dataBytes int = len(line.Vertices) * int(lr.stride) indexBytes int = len(line.Indices) * int(lr.stride) elementCount int32 = int32(len(line.Indices)) r, g, b, a = style.Color.RGBA() ) gl.Uniform1f(lr.thicknessLoc, style.Thickness) gl.Uniform1f(lr.innerLoc, style.Inner) gl.Uniform4f(lr.colorLoc, float32(r)/255.0, float32(g)/255.0, float32(b)/255.0, float32(a)/255.0) gl.UniformMatrix4fv(lr.modelviewLoc, 1, false, &mv[0]) if dataBytes > lr.bufferBytes { lr.bufferBytes = dataBytes gl.BufferData(gl.ARRAY_BUFFER, dataBytes, gl.Ptr(line.Vertices), gl.STREAM_DRAW) gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, indexBytes, gl.Ptr(line.Indices), gl.STREAM_DRAW) } else { gl.BufferSubData(gl.ARRAY_BUFFER, 0, dataBytes, gl.Ptr(line.Vertices)) gl.BufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, indexBytes, gl.Ptr(line.Indices)) } gl.DrawElements(gl.TRIANGLES, elementCount, gl.UNSIGNED_INT, gl.PtrOffset(0)) if e := gl.GetError(); e != 0 { err = fmt.Errorf("ERROR: OpenGL error %X", e) } return }
// Rendering this LineRender. func (lr *LineRender) Render() { // Binding the appropriate information. gl.BindVertexArray(lr.vao) gl.BindBuffer(gl.ARRAY_BUFFER, lr.vbo) gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, lr.ebo) gl.UseProgram(lr.shaderProgram) // Loading up vertex attributes. vertAttrib := uint32(gl.GetAttribLocation(lr.shaderProgram, gl.Str("vert\x00"))) gl.EnableVertexAttribArray(vertAttrib) gl.VertexAttribPointer(vertAttrib, 2, gl.FLOAT, false, 2*4, gl.PtrOffset(0)) // Line thickness information. gl.Uniform1f( gl.GetUniformLocation(lr.shaderProgram, gl.Str("in_thickness\x00")), lr.weight) // Fragment shader color information. gl.Uniform4f( gl.GetUniformLocation(lr.shaderProgram, gl.Str("in_color\x00")), lr.color.Red, lr.color.Green, lr.color.Blue, lr.color.Alpha) gl.BindFragDataLocation(lr.shaderProgram, 0, gl.Str("out_color\x00")) // Performing the render. gl.DrawElements(gl.LINE_STRIP, lr.points, gl.UNSIGNED_INT, nil) }
func renderMarker(metrics *metrics, m *game.Marker, x, y int) { switch m.State { case game.MarkerShowing: var val string switch { case m.ChainLevel > 0: val = "x" + strconv.Itoa(m.ChainLevel+1) case m.ComboLevel > 3: val = strconv.Itoa(m.ComboLevel) default: return } sc := float32(0.5) tx := -float32(len(val)-1) * sc ty := metrics.globalTranslationY + cellTranslationY*-float32(y) + easeOutCubic(m.StateProgress(metrics.fudge), 0, 0.5) tz := metrics.globalTranslationZ + cellTranslationZ/2 + 0.1 ry := metrics.globalRotationY + metrics.cellRotationY*-float32(x) yq := newAxisAngleQuaternion(yAxis, ry) qm := newQuaternionMatrix(yq.normalize()) gl.Uniform1f(brightnessUniform, 0) gl.Uniform1f(alphaUniform, easeOutCubic(m.StateProgress(metrics.fudge), 1, -1)) for _, rune := range val { text := markerRuneText[rune] m := newScaleMatrix(sc, sc, sc) m = m.mult(newTranslationMatrix(tx, ty, tz)) m = m.mult(qm) gl.UniformMatrix4fv(modelMatrixUniform, 1, false, &m[0]) gl.Uniform1i(textureUniform, int32(text.texture)-1) squareMesh.drawElements() tx++ } gl.Uniform1i(textureUniform, int32(boardTexture)-1) } }
func SetUniformF(shader, variable string, f float32) error { prog, ok := shader_progs[shader] if !ok { return fmt.Errorf("Tried to set a uniform in an unknown shader '%s'", shader) } bvariable := []byte(fmt.Sprintf("%s\x00", variable)) loc := gl.GetUniformLocation(prog, (*uint8)(unsafe.Pointer(&bvariable[0]))) gl.Uniform1f(loc, f) return nil }
func renderCellBlock(metrics *metrics, c *game.Cell, x, y int) { bv := float32(0) if c.Block.State == game.BlockFlashing { bv = pulse(metrics.g.GlobalPulse+metrics.fudge, 0, 0.5, 1.5) } gl.Uniform1f(brightnessUniform, bv) m := metrics.blockMatrix(c.Block, x, y) gl.UniformMatrix4fv(modelMatrixUniform, 1, false, &m[0]) blockMeshes[c.Block.Color].drawElements() }
func renderHUD(g *game.Game, fudge float32) { gl.UniformMatrix4fv(projectionViewMatrixUniform, 1, false, &orthoProjectionViewMatrix[0]) gl.Uniform1f(grayscaleUniform, 0) gl.Uniform1f(brightnessUniform, 0) gl.Uniform1f(alphaUniform, 1) i := 1 renderText := func(item game.HUDItem, val string) { text := hudItemText[item] x := float32(winWidth)/4*float32(i) - text.width/2 y := float32(winHeight) - text.height*2 text.render(x, y) var valWidth, valHeight float32 for _, rune := range val { text := hudRuneText[rune] valWidth += text.width if text.height > valHeight { valHeight = text.height } } x = float32(winWidth)/4*float32(i) - valWidth/2 y -= valHeight * 1.5 for _, rune := range val { text := hudRuneText[rune] text.render(x, y) x += text.width } i++ } renderText(game.HUDItemSpeed, formattedSpeed(g)) renderText(game.HUDItemTime, formattedTime(g)) renderText(game.HUDItemScore, formattedScore(g)) }
// RenderString must be called on the render thread. x and y are the initial position of the pen, // in screen coordinates, and height is the height of a full line of text, in screen coordinates. func (d *Dictionary) RenderString(str string, x, y, height float64) { if str == "" { return } // No synchronization necessary because everything is run serially on the render thread anyway. if d.strs == nil { d.strs = make(map[string]strData) } data, ok := d.strs[str] if !ok { data = d.bindString(str) d.strs[str] = data } render.EnableShader("glop.font") defer render.EnableShader("") gl.ActiveTexture(gl.TEXTURE0) gl.BindTexture(gl.TEXTURE_2D, d.atlas.texture) location, _ := render.GetUniformLocation("glop.font", "tex") gl.Uniform1i(location, 0) gl.BindSampler(0, d.atlas.sampler) location, _ = render.GetUniformLocation("glop.font", "height") gl.Uniform1f(location, float32(height)) var viewport [4]int32 gl.GetIntegerv(gl.VIEWPORT, &viewport[0]) location, _ = render.GetUniformLocation("glop.font", "screen") gl.Uniform2f(location, float32(viewport[2]), float32(viewport[3])) location, _ = render.GetUniformLocation("glop.font", "pen") gl.Uniform2f(location, float32(x)+float32(viewport[0]), float32(y)+float32(viewport[1])) location, _ = render.GetUniformLocation("glop.font", "textColor") gl.Uniform3f(location, d.color[0], d.color[1], d.color[2]) gl.Enable(gl.BLEND) gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) gl.BindVertexArray(data.varrays[0]) gl.DrawArrays(gl.TRIANGLES, 0, data.count) }
func renderMenu(g *game.Game, fudge float32) { ease := func(start, change float32) float32 { return easeOutCubic(g.StateProgress(fudge), start, change) } alpha := float32(1) switch g.State { case game.GameInitial, game.GamePaused: alpha = ease(0, 1) case game.GamePlaying, game.GameExiting: alpha = ease(1, -1) } // Don't render the menu if it is invisible. if alpha == 0 { return } gl.UniformMatrix4fv(projectionViewMatrixUniform, 1, false, &orthoProjectionViewMatrix[0]) gl.Uniform1f(grayscaleUniform, 0) gl.Uniform1f(brightnessUniform, 0) gl.Uniform1f(alphaUniform, alpha) gl.Uniform1f(mixAmountUniform, 0) menu := g.Menu titleText := menuTitleText[menu.ID] totalHeight := titleText.height * 2 for _, item := range menu.Items { totalHeight += float32(menuItemFontSize) * 2 if !item.SingleChoice() { totalHeight += float32(menuItemFontSize) * 2 } } currentY := (float32(winHeight) + totalHeight) / 2 centerX := func(txt *renderableText) float32 { return (float32(winWidth) - txt.width) / 2 } renderText := func(text *renderableText) { currentY -= text.height text.render(centerX(text), currentY) currentY -= text.height // add spacing for next item } // TODO(btmura): split these out into separate functions renderSlider := func(item *game.MenuItem) { val := strconv.Itoa(item.Slider.Value) var valWidth, valHeight float32 for _, rune := range val { text := menuRuneText[rune] valWidth += text.width if text.height > valHeight { valHeight = text.height } } currentY -= valHeight x := (float32(winWidth) - valWidth) / 2 for _, rune := range val { text := menuRuneText[rune] text.render(x, currentY) x += text.width } currentY -= valHeight } renderMenuItem := func(index int, item *game.MenuItem) { var brightness float32 if menu.FocusedIndex == index { switch { case menu.Selected: brightness = pulse(g.GlobalPulse+fudge, 1, 1, 1) case item.SingleChoice(): brightness = pulse(g.GlobalPulse+fudge, 1, 0.3, 0.06) default: brightness = 1 } } gl.Uniform1f(brightnessUniform, brightness) renderText(menuItemText[item.ID]) switch { case item.Selector != nil: renderText(menuChoiceText[item.Selector.Value()]) case item.Slider != nil: renderSlider(item) } } renderText(titleText) for i, item := range menu.Items { renderMenuItem(i, item) } }
func renderCellFragments(metrics *metrics, c *game.Cell, x, y int) { const ( nw = iota ne se sw ) render := func(sc, rx, ry, rz float32, dir int) { m := newScaleMatrix(sc, sc, sc) m = m.mult(newTranslationMatrix(rx, ry, rz)) m = m.mult(metrics.blockMatrix(c.Block, x, y)) gl.UniformMatrix4fv(modelMatrixUniform, 1, false, &m[0]) fragmentMeshes[c.Block.Color][dir].drawElements() } ease := func(start, change float32) float32 { return easeOutCubic(c.Block.StateProgress(metrics.fudge), start, change) } var bv float32 var av float32 switch c.Block.State { case game.BlockCracking, game.BlockCracked: av = 1 case game.BlockExploding: bv = ease(0, 1) av = ease(1, -1) } gl.Uniform1f(brightnessUniform, bv) gl.Uniform1f(alphaUniform, av) const ( maxCrack = 0.03 maxExpand = 0.02 ) var rs float32 var rt float32 var j float32 switch c.Block.State { case game.BlockCracking: rs = ease(1, 1+maxExpand) rt = ease(0, maxCrack) j = pulse(c.Block.StateProgress(metrics.fudge), 0, 0.5, 1.5) case game.BlockCracked: rs = 1 rt = maxCrack case game.BlockExploding: rs = ease(1, -1) rt = ease(maxCrack, math.Pi*0.75) } const szt = 0.5 // starting z translation since model is 0.5 in depth wx, ex := -rt, rt fz, bz := rt+szt, -rt-szt const amp = 1 ny := rt + amp*float32(math.Sin(float64(rt))) sy := -rt + amp*(float32(math.Cos(float64(-rt)))-1) render(rs, wx+j, ny+j, fz, nw) // front north west render(rs, ex+j, ny+j, fz, ne) // front north east render(rs, wx+j, ny+j, bz, nw) // back north west render(rs, ex+j, ny+j, bz, ne) // back north east render(rs, wx+j, sy+j, fz, sw) // front south west render(rs, ex+j, sy+j, fz, se) // front south east render(rs, wx+j, sy+j, bz, sw) // back south west render(rs, ex+j, sy+j, bz, se) // back south east }
func renderBoard(g *game.Game, fudge float32) bool { if g.Board == nil { return false } b := g.Board metrics := newMetrics(g, fudge) gl.UniformMatrix4fv(projectionViewMatrixUniform, 1, false, &perspectiveProjectionViewMatrix[0]) gl.Uniform3fv(mixColorUniform, 1, &blackColor[0]) globalGrayscale := float32(1) globalDarkness := float32(0.8) var boardDarkness float32 gameEase := func(start, change float32) float32 { return easeOutCubic(g.StateProgress(fudge), start, change) } boardEase := func(start, change float32) float32 { return easeOutCubic(b.StateProgress(fudge), start, change) } switch g.State { case game.GamePlaying: globalGrayscale = gameEase(1, -1) globalDarkness = gameEase(0.8, -0.8) case game.GamePaused: globalGrayscale = gameEase(0, 1) globalDarkness = gameEase(0, 0.8) case game.GameExiting: globalGrayscale = 1 globalDarkness = gameEase(0.8, 1) } switch b.State { case game.BoardEntering: boardDarkness = boardEase(1, -1) case game.BoardExiting: boardDarkness = boardEase(0, 1) } finalDarkness := globalDarkness if finalDarkness < boardDarkness { finalDarkness = boardDarkness } gl.Uniform1f(mixAmountUniform, finalDarkness) gl.Uniform1i(textureUniform, int32(boardTexture)-1) for i := 0; i <= 2; i++ { gl.Uniform1f(grayscaleUniform, globalGrayscale) gl.Uniform1f(brightnessUniform, 0) gl.Uniform1f(alphaUniform, 1) if i == 0 { renderSelector(metrics) } for y, r := range b.Rings { for x, c := range r.Cells { switch i { case 0: // draw opaque objects switch c.Block.State { case game.BlockStatic, game.BlockSwappingFromLeft, game.BlockSwappingFromRight, game.BlockDroppingFromAbove, game.BlockFlashing: renderCellBlock(metrics, c, x, y) case game.BlockCracking, game.BlockCracked: renderCellFragments(metrics, c, x, y) } case 1: // draw transparent objects switch c.Block.State { case game.BlockExploding: renderCellFragments(metrics, c, x, y) } renderMarker(metrics, c.Marker, x, y) } } } } // Render the spare rings. // Set brightness to zero for all spare rings. gl.Uniform1f(brightnessUniform, 0) for y, r := range b.SpareRings { // Set grayscale value. First spare rings becomes colored. Rest are gray. grayscale := float32(1) if y == 0 { grayscale = easeInExpo(b.RiseProgress(fudge), 1, -1) } if grayscale < globalGrayscale { grayscale = globalGrayscale } gl.Uniform1f(grayscaleUniform, grayscale) // Set alpha value. Last spare ring fades in. Rest are opaque. alpha := float32(1) if y == len(b.SpareRings)-1 { alpha = easeInExpo(b.RiseProgress(fudge), 0, 1) } gl.Uniform1f(alphaUniform, alpha) // Render the spare rings below the normal rings. for x, c := range r.Cells { renderCellBlock(metrics, c, x, y+b.RingCount) } } return true }