├── LICENSE ├── README.md ├── cmd └── font2go │ ├── input │ ├── cursor.png │ └── monozela10.png │ └── main.go ├── color ├── c64 │ └── c64.go ├── color.go ├── cpc │ └── cpc.go ├── doc.go ├── lrgb.go ├── lrgba.go ├── lrgbna.go ├── msx │ └── msx.go ├── msx2 │ └── msx2.go ├── palette.go ├── pico8 │ └── pico8.go ├── srgb.go ├── srgb8.go ├── srgba.go ├── srgba8.go ├── srgbna.go └── srgbna8.go ├── coord ├── coords.go ├── cr.go ├── crd.go ├── doc.go ├── ra.go ├── xy.go ├── xy64.go ├── xyz.go ├── xyzw.go ├── z01_test.go └── z02_test.go ├── doc.go ├── error.go ├── examples ├── match3 │ ├── ecs │ │ ├── component.go │ │ └── entity.go │ ├── graphics │ │ ├── _bg.png │ │ ├── blue.png │ │ ├── blue_big.png │ │ ├── dark.png │ │ ├── dark_big.png │ │ ├── green.png │ │ ├── green_big.png │ │ ├── multi.png │ │ ├── multi_big.png │ │ ├── pink.png │ │ ├── pink_big.png │ │ ├── red.png │ │ ├── red_big.png │ │ ├── violet.png │ │ ├── violet_big.png │ │ ├── yellow.png │ │ └── yellow_big.png │ ├── grid │ │ ├── grid.go │ │ ├── match.go │ │ └── screen.go │ ├── init.json │ ├── input.json │ ├── main.go │ └── render.go └── snake │ └── main.go ├── formats ├── ciziel │ ├── ciziel.go │ ├── doc.go │ ├── testdata │ │ ├── init.czl │ │ ├── input.czl │ │ └── palette.czl │ └── z01_test.go └── obj │ ├── parser.go │ ├── testdata │ └── cube.obj │ └── z01_test.go ├── input ├── action.go ├── axis.go ├── bind.go ├── binders.go ├── button.go ├── context.go ├── cursor.go ├── delta.go ├── device.go ├── doc.go ├── dualaxis.go ├── error.go ├── gpaxis.go ├── gpbutton.go ├── gpstick.go ├── gptrigger.go ├── halfaxis.go ├── joysticks.go ├── kbkey.go ├── keycodes.go ├── keylabels.go ├── mouse.go ├── msaxis.go ├── msbutton.go ├── mscoord.go ├── mswheel.go ├── newframe.go ├── setup.go ├── source.go ├── testdata │ ├── init.json │ └── input.json ├── z01_test.go └── z_test.go ├── internal ├── dialog.go ├── error.go ├── events.go ├── globals.go ├── hooks.go ├── joystick.go ├── keyboard.go ├── mouse.go ├── sdl.h ├── setup.go ├── time.go └── window.go ├── noise ├── doc.go ├── gradients.go ├── perlin.go ├── simplex.go └── z01_test.go ├── options.go ├── pixel ├── canvas.go ├── color.go ├── commands.go ├── cursor.go ├── doc.go ├── error.go ├── font.go ├── gl.go ├── glblitfrag.go ├── glblitvert.go ├── gldrawfrag.go ├── gldrawvert.go ├── load.go ├── monozela10.go ├── mousecursor.go ├── palette.go ├── picture.go ├── testdata │ ├── fonts │ │ ├── chaotela12.png │ │ ├── cozela10.png │ │ ├── cozela12.png │ │ ├── monozela10.png │ │ ├── simpela10.png │ │ ├── simpela12.png │ │ └── tinela9.png │ ├── frankenstein.txt │ ├── graphics │ │ ├── mire.png │ │ ├── paletteswatch.png │ │ ├── shape1.png │ │ ├── shape2.png │ │ ├── shape3.png │ │ ├── shape4.png │ │ ├── srgb-blue.png │ │ ├── srgb-gray.png │ │ ├── srgb-green.png │ │ └── srgb-red.png │ ├── init.json │ ├── input.json │ └── sourcecode.txt ├── xy.go ├── z01_test.go ├── z02_test.go ├── z03_test.go ├── z04_test.go ├── z05_test.go ├── z06_test.go ├── z07_test.go └── z_test.go ├── plane ├── doc.go ├── matrices.go ├── predicates.go ├── quadedge │ ├── ORIGINAL_LICENSE │ ├── delaunay.go │ ├── doc.go │ ├── edge.go │ ├── operators.go │ ├── pool.go │ ├── pool_test.go │ ├── testdata │ │ ├── init.json │ │ └── input.json │ ├── z01_test.go │ └── z_test.go ├── transforms.go ├── z01_test.go └── z_test.go ├── run.go ├── space ├── doc.go ├── matrices.go ├── transforms.go ├── z01_test.go └── z02_test.go ├── testdata ├── graphics │ └── cozely.png ├── init.json └── input.json ├── window ├── events.go ├── window.go └── xy.go ├── x ├── atlas │ ├── atlas.go │ ├── doc.go │ └── region.go ├── gl │ ├── buffer.go │ ├── doc.go │ ├── draw.go │ ├── error.go │ ├── framebuffer.go │ ├── glad.c │ ├── glad.h │ ├── glerror.c │ ├── glerror.go │ ├── misc.go │ ├── options.go │ ├── pipeline.go │ ├── renderbuffer.go │ ├── sampler.go │ ├── setup.go │ ├── shader.go │ ├── state.go │ ├── testdata │ │ ├── init.json │ │ ├── input.json │ │ ├── shader01.frag │ │ ├── shader01.vert │ │ ├── shader02.frag │ │ ├── shader02.vert │ │ ├── shader03.frag │ │ ├── shader03.vert │ │ ├── shader04.frag │ │ ├── shader04.vert │ │ ├── shader05.frag │ │ ├── shader05.vert │ │ ├── shader06.frag │ │ ├── shader06.vert │ │ ├── shader07.frag │ │ ├── shader07.vert │ │ ├── shader08.frag │ │ ├── shader08.vert │ │ ├── testpattern.png │ │ └── testpattern.svg │ ├── texture.go │ ├── texture1d.go │ ├── texture2d.go │ ├── texture3d.go │ ├── texturearray1d.go │ ├── texturearray2d.go │ ├── vertex.go │ ├── ze1_first_triangle_test.go │ ├── ze2_vertex_buffer_test.go │ ├── ze3_uniform_buffer_test.go │ ├── ze4_firstcube_test.go │ ├── ze5_texture_test.go │ ├── ze6_instanceddraw_test.go │ ├── ze7_indirectdraw_test.go │ └── ze8_streaming_test.go ├── machine │ ├── fsm.go │ ├── z01_test.go │ └── z02_test.go ├── math32 │ ├── ORIGINAL_LICENSE │ ├── abs.go │ ├── abs_386.s │ ├── abs_amd64.s │ ├── abs_test.go │ ├── bits.go │ ├── constants.go │ ├── copysign.go │ ├── cos.go │ ├── cos_test.go │ ├── doc.go │ ├── fastcos.go │ ├── fastcos_386.s │ ├── fastcos_amd64.s │ ├── fastcos_arm.s │ ├── fastcos_test.go │ ├── fastfloor.go │ ├── fastfloor_386.s │ ├── fastfloor_amd64.s │ ├── fastfloor_test.go │ ├── fastsin.go │ ├── fastsin_386.s │ ├── fastsin_amd64.s │ ├── fastsin_arm.s │ ├── fastsin_test.go │ ├── floor.go │ ├── floor_386.s │ ├── floor_amd64.s │ ├── floor_arm.s │ ├── floor_test.go │ ├── frexp.go │ ├── isalmostequal.go │ ├── isnearlyequal.go │ ├── isroughlyequal.go │ ├── ldexp.go │ ├── mix.go │ ├── mix_test.go │ ├── mod.go │ ├── modf.go │ ├── round.go │ ├── round_386.s │ ├── round_amd64.s │ ├── round_test.go │ ├── sin.go │ ├── sin_test.go │ ├── sqrt.go │ ├── sqrt_386.s │ ├── sqrt_amd64.s │ ├── sqrt_arm.s │ ├── sqrt_test.go │ ├── tan.go │ ├── tan_test.go │ └── unsafe.go └── poly │ ├── camera.go │ ├── doc.go │ ├── error.go │ ├── lights.go │ ├── mesh.go │ ├── palette.go │ ├── pipeline.go │ ├── references.go │ ├── testdata │ ├── cube.obj │ ├── init.json │ ├── input.json │ ├── shader.frag │ ├── shader.vert │ ├── suzanne.obj │ └── teapot.obj │ ├── tonemap.go │ ├── utils.go │ ├── vertshader.go │ ├── vertshader2.go │ ├── z01_test.go │ └── z_test.go └── z_example_test.go /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2018 Laurent Moussault. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cozely 2 | 3 | [![GoDoc](https://godoc.org/github.com/cozely/cozely?status.svg)](https://godoc.org/github.com/cozely/cozely) 4 | [![Go Report Card](https://goreportcard.com/badge/github.com/cozely/cozely)](https://goreportcard.com/report/github.com/cozely/cozely) 5 | 6 | Cozely aims to be a simple, all-in-one framework for making games in Go. It 7 | focuses on pixel art for 2D, and polygonal art (aka low-poly) for 3D. 8 | 9 | **THIS IS A WORK IN PROGRESS**, not usable yet: the framework is *very* 10 | incomplete, and the API is subject to frequent changes. 11 | 12 | ## Platforms 13 | 14 | The framework currently supports windows and linux. 15 | 16 | ## Dependencies 17 | 18 | The only dependancies are SDL 2 and OpenGL 4.6. 19 | 20 | ## License 21 | 22 | The code is under a simplified BSD license (see LICENSE file). When a 23 | sub-package is derived from another source, the directory contain the 24 | appropriate LICENSE file. 25 | 26 | ## Credits 27 | 28 | The Perlin and Simplex noise functions are adapted from ["Simplex Noise 29 | Demystified"](http://www.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf) by 30 | Stefan Gustavson (code in the public domain). 31 | 32 | The pixel font was originally based on ["Pixel Operator 33 | Mono"](https://notabug.org/HarvettFox96/ttf-pixeloperator) by Jayvee Enayas, but 34 | has been so modified that it's now a completely different font. It is still 35 | licensed under the SIL OFL. 36 | 37 | Some implementations of the single-precision math functions are derived from the 38 | [Go source code](https://github.com/golang/go) (BSD-style license). 39 | -------------------------------------------------------------------------------- /cmd/font2go/input/cursor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/cmd/font2go/input/cursor.png -------------------------------------------------------------------------------- /cmd/font2go/input/monozela10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/cmd/font2go/input/monozela10.png -------------------------------------------------------------------------------- /cmd/font2go/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "image" 9 | stdcolor "image/color" 10 | _ "image/png" // activate png support 11 | "log" 12 | "os" 13 | "path/filepath" 14 | "strings" 15 | ) 16 | 17 | func main() { 18 | log.SetFlags(0) 19 | 20 | if len(os.Args) != 3 { 21 | log.Fatal("Usage: font2go ") 22 | } 23 | 24 | n := os.Args[1] 25 | f, err := os.Open(n) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | defer f.Close() 30 | 31 | m, _, err := image.Decode(f) 32 | if err != nil { 33 | log.Fatal(err) 34 | } 35 | mm, ok := m.(*image.Paletted) 36 | if !ok { 37 | log.Fatal("font image file should be in indexed color format") 38 | } 39 | _, ok = mm.ColorModel().(stdcolor.Palette) 40 | if !ok { 41 | log.Fatal("unable to retrieve source image color") 42 | } 43 | 44 | o, err := os.Create(os.Args[2]) 45 | if err != nil { 46 | log.Fatal(err) 47 | } 48 | 49 | on := strings.TrimSuffix(filepath.Base(os.Args[2]), ".go") 50 | 51 | fmt.Fprintf(o, "package whatever\n\n") 52 | fmt.Fprintf(o, "import \"image\"\n\n") 53 | fmt.Fprintf(o, "var %s = image.Paletted{\n", on) 54 | fmt.Fprintf( 55 | o, "\tRect: image.Rectangle{Max: image.Point{%d, %d}},\n", 56 | mm.Bounds().Dx(), 57 | mm.Bounds().Dy(), 58 | ) 59 | fmt.Fprintf(o, "\tStride: %d,\n", mm.Bounds().Dx()) 60 | fmt.Fprint(o, "\tPix: []uint8{") 61 | for i, c := range mm.Pix { 62 | if i%24 == 0 { 63 | fmt.Fprint(o, "\n\t\t") 64 | } 65 | fmt.Fprintf(o, "%d, ", c) 66 | } 67 | fmt.Fprint(o, "\n\t},") 68 | fmt.Fprint(o, "\n}\n") 69 | 70 | } 71 | -------------------------------------------------------------------------------- /color/c64/c64.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | // Package c64 provides the color palette of C64 microcomputers. 5 | package c64 6 | 7 | import ( 8 | "github.com/cozely/cozely/color" 9 | ) 10 | 11 | // Palette is the C64 palette. 12 | var Palette = color.Palette{ 13 | ByName: map[string]color.Index{ 14 | "Black": 1, 15 | "White": 2, 16 | "Red": 3, 17 | "Cyan": 4, 18 | "Violet": 5, 19 | "Green": 6, 20 | "Blue": 7, 21 | "Yellow": 8, 22 | "Orange": 9, 23 | "Brown": 10, 24 | "Light Red": 11, 25 | "Dark Grey": 12, 26 | "Grey": 13, 27 | "Light Green": 14, 28 | "Light Blue": 15, 29 | "Light Grey": 16, 30 | }, 31 | 32 | Colors: []color.LRGBA{ 33 | color.LRGBAof(color.SRGB8{0x00, 0x00, 0x00}), 34 | color.LRGBAof(color.SRGB8{0xff, 0xff, 0xff}), 35 | color.LRGBAof(color.SRGB8{0x68, 0x37, 0x2b}), 36 | color.LRGBAof(color.SRGB8{0x70, 0xa4, 0xb2}), 37 | color.LRGBAof(color.SRGB8{0x6f, 0x3d, 0x86}), 38 | color.LRGBAof(color.SRGB8{0x58, 0x8d, 0x43}), 39 | color.LRGBAof(color.SRGB8{0x35, 0x28, 0x79}), 40 | color.LRGBAof(color.SRGB8{0xb8, 0xc7, 0x6f}), 41 | color.LRGBAof(color.SRGB8{0x6f, 0x4f, 0x25}), 42 | color.LRGBAof(color.SRGB8{0x43, 0x39, 0x00}), 43 | color.LRGBAof(color.SRGB8{0x9a, 0x67, 0x59}), 44 | color.LRGBAof(color.SRGB8{0x44, 0x44, 0x44}), 45 | color.LRGBAof(color.SRGB8{0x6c, 0x6c, 0x6c}), 46 | color.LRGBAof(color.SRGB8{0x9a, 0xd2, 0x84}), 47 | color.LRGBAof(color.SRGB8{0x6c, 0x5e, 0xb5}), 48 | color.LRGBAof(color.SRGB8{0x95, 0x95, 0x95}), 49 | }, 50 | } 51 | -------------------------------------------------------------------------------- /color/color.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package color 5 | 6 | import ( 7 | "math" 8 | ) 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | // Color can convert itself to alpha-premultipled RGBA as 32 bit floats, in 13 | // both linear and standard (sRGB) color spaces. 14 | type Color interface { 15 | // Linear returns the red, green, blue and alpha values in linear color space. 16 | // The red, gren and blue values have been alpha-premultiplied in linear 17 | // space. Each value ranges within [0, 1] and can be used directly by GPU 18 | // shaders. 19 | Linear() (r, g, b, a float32) 20 | 21 | // Standard returns the red, green, blue and alpha values in standard (sRGB) 22 | // color space. The red, gren and blue values have been alpha-premultiplied in 23 | // linear space. Each value ranges within [0, 1]. 24 | Standard() (r, g, b, a float32) 25 | } 26 | 27 | //////////////////////////////////////////////////////////////////////////////// 28 | 29 | func linearOf(c float32) float32 { 30 | if c <= 0.04045 { 31 | return c / 12.92 32 | } 33 | return float32(math.Pow(float64(c+0.055)/(1+0.055), 2.4)) 34 | } 35 | 36 | func standardOf(c float32) float32 { 37 | if c <= 0.0031308 { 38 | return 12.92 * c 39 | } 40 | return (1+0.055)*float32(math.Pow(float64(c), 1/2.4)) - 0.055 41 | } 42 | 43 | //////////////////////////////////////////////////////////////////////////////// 44 | -------------------------------------------------------------------------------- /color/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | /* 5 | Package color provides types and functions to manipulate colors. 6 | 7 | The types defined here are compatible with the standard library package 8 | "image/color", but are designed to work well with the GPU. 9 | 10 | The first difference with the standard library is that most of them are based on 11 | float32 (instead of uint32), so they can be directly passed to GPU shaders. 12 | 13 | The second difference is that they make an explicit distinction between linear 14 | and standard (sRGB) color spaces, while the standard library only makes 15 | distinction between alpha-premultiplied and alpha-postmultiplied. Both 16 | distinction are equally important for correct color handling. 17 | 18 | Linear and sRGB 19 | 20 | Structs prefixed by "L" are in linear color space, while structs prefixed by "S" 21 | are in standard (sRGB) color space. 22 | 23 | For the importance of this distinction, see: 24 | 25 | http://blog.johnnovak.net/2016/09/21/what-every-coder-should-know-about-gamma/ 26 | 27 | Alpha Pre-Multipled and Post-Multiplied 28 | 29 | Structs ending with "nA" are alpha post-multplied; all others are alpha 30 | pre-multiplied. 31 | 32 | In an alpha-premultiplied color, the three RGB component have been scaled by 33 | alpha; valid values are therefore within [0, alpha]. This is the most useful 34 | choice for alpha-blending. 35 | 36 | For the importance of alpha pre-multipled, see: 37 | 38 | https://blogs.msdn.microsoft.com/shawnhar/2009/11/06/premultiplied-alpha/ 39 | */ 40 | package color 41 | -------------------------------------------------------------------------------- /color/lrgb.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package color 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // LRGB represents a color in linear color space. Each value ranges within 9 | // [0, 1], and can be used directly by GPU shaders. 10 | type LRGB struct { 11 | R float32 12 | G float32 13 | B float32 14 | } 15 | 16 | //////////////////////////////////////////////////////////////////////////////// 17 | 18 | // LRGBof converts any color to linear color space with no alpha. 19 | func LRGBof(c Color) LRGB { 20 | r, g, b, _ := c.Linear() 21 | return LRGB{r, g, b} 22 | } 23 | 24 | //////////////////////////////////////////////////////////////////////////////// 25 | 26 | // Linear implements the Colour interface. 27 | func (c LRGB) Linear() (r, g, b, a float32) { 28 | return c.R, c.G, c.B, 1 29 | } 30 | 31 | // Standard implements the Colour interface. 32 | func (c LRGB) Standard() (r, g, b, a float32) { 33 | r = standardOf(c.R) 34 | g = standardOf(c.G) 35 | b = standardOf(c.B) 36 | return r, g, b, 1 37 | } 38 | 39 | //////////////////////////////////////////////////////////////////////////////// 40 | 41 | // RGBA implements the image.Color interface: it returns the four components 42 | // scaled by 0xFFFF. 43 | func (c LRGB) RGBA() (r, g, b, a uint32) { 44 | r = uint32(c.R * 0xFFFF) 45 | g = uint32(c.G * 0xFFFF) 46 | b = uint32(c.B * 0xFFFF) 47 | a = uint32(0xFFFF) 48 | return r, g, b, a 49 | } 50 | 51 | //////////////////////////////////////////////////////////////////////////////// 52 | -------------------------------------------------------------------------------- /color/lrgba.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package color 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // LRGBA represents a color in alpha-premultiplied linear color space. Each 9 | // value ranges within [0, 1], and can be used directly by GPU shaders. 10 | // 11 | // An alpha-premultiplied color component c has been scaled by alpha (a), so has 12 | // valid values 0 <= c <= a. 13 | // 14 | // Note that additive blending can also be achieved when alpha is set to 0 while 15 | // the color components are non-null. 16 | type LRGBA struct { 17 | R float32 18 | G float32 19 | B float32 20 | A float32 21 | } 22 | 23 | //////////////////////////////////////////////////////////////////////////////// 24 | 25 | // LRGBAof converts any color to alpha-premultiplied, linear color space. 26 | func LRGBAof(c Color) LRGBA { 27 | r, g, b, a := c.Linear() 28 | return LRGBA{r, g, b, a} 29 | } 30 | 31 | //////////////////////////////////////////////////////////////////////////////// 32 | 33 | // Linear implements the Colour interface. 34 | func (c LRGBA) Linear() (r, g, b, a float32) { 35 | return c.R, c.G, c.B, c.A 36 | } 37 | 38 | // Standard implements the Colour interface. 39 | func (c LRGBA) Standard() (r, g, b, a float32) { 40 | r = standardOf(c.R) 41 | g = standardOf(c.G) 42 | b = standardOf(c.B) 43 | return r, g, b, c.A 44 | } 45 | 46 | //////////////////////////////////////////////////////////////////////////////// 47 | 48 | // RGBA implements the image.Color interface: it returns the four components 49 | // scaled by 0xFFFF. 50 | func (c LRGBA) RGBA() (r, g, b, a uint32) { 51 | r = uint32(c.R * 0xFFFF) 52 | g = uint32(c.G * 0xFFFF) 53 | b = uint32(c.B * 0xFFFF) 54 | a = uint32(c.A * 0xFFFF) 55 | return r, g, b, a 56 | } 57 | 58 | //////////////////////////////////////////////////////////////////////////////// 59 | -------------------------------------------------------------------------------- /color/lrgbna.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package color 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // LRGBnA represents a color in *non* alpha-premultiplied linear color 9 | // space. Each value ranges within [0, 1], and can be used directly by GPU 10 | // shaders. 11 | // 12 | // Note: prefer RGBA for use in shaders. 13 | type LRGBnA struct { 14 | R float32 15 | G float32 16 | B float32 17 | A float32 18 | } 19 | 20 | //////////////////////////////////////////////////////////////////////////////// 21 | 22 | // LRGBnAof converts any color to non alpha-premultiplied linear color 23 | // space. 24 | func LRGBnAof(c Color) LRGBnA { 25 | r, g, b, a := c.Linear() 26 | return LRGBnA{r / a, g / a, b / a, a} 27 | } 28 | 29 | //////////////////////////////////////////////////////////////////////////////// 30 | 31 | // Linear implements the Colour interface. 32 | func (c LRGBnA) Linear() (r, g, b, a float32) { 33 | return c.A * c.R, c.A * c.G, c.A * c.B, c.A 34 | } 35 | 36 | // Standard implements the Colour interface. 37 | func (c LRGBnA) Standard() (r, g, b, a float32) { 38 | r = standardOf(c.A * c.R) 39 | g = standardOf(c.A * c.G) 40 | b = standardOf(c.A * c.B) 41 | return r, g, b, c.A 42 | } 43 | 44 | //////////////////////////////////////////////////////////////////////////////// 45 | 46 | // RGBA implements the image.Color interface: it returns the four components 47 | // scaled by 0xFFFF. 48 | func (c LRGBnA) RGBA() (r, g, b, a uint32) { 49 | return uint32(c.A * c.R * 0xFFFF), uint32(c.A * c.G * 0xFFFF), uint32(c.A * c.B * 0xFFFF), uint32(c.A * 0xFFFF) 50 | } 51 | 52 | //////////////////////////////////////////////////////////////////////////////// 53 | -------------------------------------------------------------------------------- /color/msx2/msx2.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | // Package msx2 provides the color palette of MSX2 microcomputers. 5 | package msx2 6 | 7 | import ( 8 | "strconv" 9 | 10 | "github.com/cozely/cozely/color" 11 | ) 12 | 13 | //////////////////////////////////////////////////////////////////////////////// 14 | 15 | // Palette is the MSX2 palette. 16 | var Palette = color.Palette{ 17 | ByName: map[string]color.Index{}, 18 | } 19 | 20 | func init() { 21 | for i := 1; i < 256; i++ { 22 | g, r, b := i>>5, (i&0x1C)>>2, i&0x3 23 | n := "#" + strconv.Itoa(r) + strconv.Itoa(g) + strconv.Itoa(b) 24 | Palette.ByName[n] = color.Index(i) 25 | Palette.Colors = append( 26 | Palette.Colors, 27 | color.LRGBA{ 28 | float32(r) / 7.0, 29 | float32(g) / 7.0, 30 | float32(b) / 3.0, 31 | 1.0, 32 | }) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /color/palette.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package color 5 | 6 | import ( 7 | "image" 8 | stdcolor "image/color" 9 | "os" 10 | 11 | "github.com/cozely/cozely/internal" 12 | ) 13 | 14 | //////////////////////////////////////////////////////////////////////////////// 15 | 16 | // A Palette is an ordered list of colors (defined by their LRGBA values), and a 17 | // name-to-index dictionary. 18 | type Palette struct { 19 | ByName map[string]Index 20 | Colors []LRGBA 21 | } 22 | 23 | // An Index is used to refer to colors inside a palette. 24 | type Index uint8 25 | 26 | //////////////////////////////////////////////////////////////////////////////// 27 | 28 | // PaletteFrom returns a new Palette created from the file at the specified 29 | // path. 30 | func PaletteFrom(path string) Palette { 31 | var pal = Palette{ 32 | ByName: map[string]Index{}, 33 | } 34 | 35 | f, err := os.Open(internal.Path + path + ".png") 36 | if err != nil { 37 | //TODO: errors.New("unable to open file for palette " + name) 38 | return pal 39 | } 40 | defer f.Close() //TODO: error handling 41 | cf, _, err := image.DecodeConfig(f) 42 | if err != nil { 43 | //TODO: errors.New("unable to decode file for palette " + name) 44 | return pal 45 | } 46 | 47 | p, ok := cf.ColorModel.(stdcolor.Palette) 48 | if !ok { 49 | //TODO: errors.New("image file not paletted for palette " + name) 50 | return pal 51 | } 52 | 53 | //TODO: clear the palette? 54 | 55 | for i := range p { 56 | r, g, b, al := p[i].RGBA() 57 | if i == 0 { 58 | //TODO: check if first entry is transparent 59 | continue 60 | } 61 | if i > 255 { 62 | //TODO:errors.New("too many colors for palette " + name) 63 | return pal 64 | } 65 | c := SRGBA{ 66 | R: float32(r) / float32(0xFFFF), 67 | G: float32(g) / float32(0xFFFF), 68 | B: float32(b) / float32(0xFFFF), 69 | A: float32(al) / float32(0xFFFF), 70 | } 71 | //TODO: append name 72 | pal.Colors = append(pal.Colors, LRGBAof(c)) 73 | } 74 | 75 | internal.Debug.Printf("Loaded color palette (%d entries) from %s", len(p), path) 76 | 77 | return pal 78 | } 79 | -------------------------------------------------------------------------------- /color/pico8/pico8.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | // Package pico8 provides the color palette of the PICO-8 fantasy console. 5 | // 6 | // The palette was created by Joseph White, and is licensed under CC-0. 7 | // 8 | // Source: 9 | // https://twitter.com/lexaloffle/status/732649035165667329 10 | // https://www.lexaloffle.com/pico-8.php?page=faq 11 | // http://www.lexaloffle.com/gfx/pico8_palette.png 12 | package pico8 13 | 14 | import ( 15 | "github.com/cozely/cozely/color" 16 | ) 17 | 18 | //////////////////////////////////////////////////////////////////////////////// 19 | 20 | // Palette is the PICO-8 palette. 21 | var Palette = color.Palette{ 22 | ByName: map[string]color.Index{ 23 | "Dark Blue": 1, 24 | "Dark Purple": 2, 25 | "Dark Green": 3, 26 | "Brown": 4, 27 | "Dark Gray": 5, 28 | "Light Gray": 6, 29 | "White": 7, 30 | "Red": 8, 31 | "Orange": 9, 32 | "Yellow": 10, 33 | "Green": 11, 34 | "Blue": 12, 35 | "Indigo": 13, 36 | "Pink": 14, 37 | "Peach": 15, 38 | "Black": 16, 39 | }, 40 | Colors: []color.LRGBA{ 41 | color.LRGBAof(color.SRGB8{0x28, 0x22, 0x53}), 42 | color.LRGBAof(color.SRGB8{0x7E, 0x25, 0x53}), 43 | color.LRGBAof(color.SRGB8{0x00, 0x87, 0x51}), 44 | color.LRGBAof(color.SRGB8{0xAB, 0x52, 0x36}), 45 | color.LRGBAof(color.SRGB8{0x5F, 0x57, 0x4F}), 46 | color.LRGBAof(color.SRGB8{0xC2, 0xC3, 0xC7}), 47 | color.LRGBAof(color.SRGB8{0xFF, 0xF1, 0xE8}), 48 | color.LRGBAof(color.SRGB8{0xFF, 0x00, 0x4D}), 49 | color.LRGBAof(color.SRGB8{0xFF, 0xA3, 0x00}), 50 | color.LRGBAof(color.SRGB8{0xFF, 0xEC, 0x27}), 51 | color.LRGBAof(color.SRGB8{0x00, 0xE4, 0x36}), 52 | color.LRGBAof(color.SRGB8{0x29, 0xAD, 0xFF}), 53 | color.LRGBAof(color.SRGB8{0x83, 0x76, 0x9C}), 54 | color.LRGBAof(color.SRGB8{0xFF, 0x77, 0xA8}), 55 | color.LRGBAof(color.SRGB8{0xFF, 0xCC, 0xAA}), 56 | color.LRGBAof(color.SRGB8{0x00, 0x00, 0x00}), 57 | }, 58 | } 59 | -------------------------------------------------------------------------------- /color/srgb.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package color 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // SRGB represents a color in sRGB color space. Each value ranges within 9 | // [0, 1], and can be used directly by GPU shaders. 10 | type SRGB struct { 11 | R float32 12 | G float32 13 | B float32 14 | } 15 | 16 | //////////////////////////////////////////////////////////////////////////////// 17 | 18 | // SRGBof converts any color to sRGB color space with no alpha. 19 | func SRGBof(c Color) SRGB { 20 | r, g, b, _ := c.Standard() 21 | return SRGB{r, g, b} 22 | } 23 | 24 | //////////////////////////////////////////////////////////////////////////////// 25 | 26 | // Linear implements the Colour interface. 27 | func (c SRGB) Linear() (r, g, b, a float32) { 28 | r = linearOf(c.R) 29 | g = linearOf(c.G) 30 | b = linearOf(c.B) 31 | return r, g, b, 1 32 | } 33 | 34 | // Standard implements the Colour interface. 35 | func (c SRGB) Standard() (r, g, b, a float32) { 36 | return c.R, c.G, c.B, 1 37 | } 38 | 39 | //////////////////////////////////////////////////////////////////////////////// 40 | 41 | // RGBA implements the image.Color interface: it returns the four components 42 | // scaled by 0xFFFF. 43 | func (c SRGB) RGBA() (r, g, b, a uint32) { 44 | return uint32(c.R * 0xFFFF), uint32(c.G * 0xFFFF), uint32(c.B * 0xFFFF), uint32(0xFFFF) 45 | } 46 | 47 | //////////////////////////////////////////////////////////////////////////////// 48 | -------------------------------------------------------------------------------- /color/srgb8.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package color 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // SRGB8 represents a 24-bit color in sRGB color space. There is 8 bits for each 9 | // components. 10 | type SRGB8 struct { 11 | R uint8 12 | G uint8 13 | B uint8 14 | } 15 | 16 | //////////////////////////////////////////////////////////////////////////////// 17 | 18 | // SRGB8of converts any color to sRGB color space. 19 | func SRGB8of(c Color) SRGB8 { 20 | cc, ok := c.(SRGB8) 21 | if ok { 22 | return cc 23 | } 24 | ccc, ok := c.(SRGBA8) 25 | if ok { 26 | return SRGB8{ccc.R, ccc.G, ccc.B} 27 | } 28 | r, g, b, _ := c.Standard() 29 | return SRGB8{uint8(r * 0xFF), uint8(g * 0xFF), uint8(b * 0xFF)} 30 | } 31 | 32 | //////////////////////////////////////////////////////////////////////////////// 33 | 34 | // Linear implements the Colour interface. 35 | func (c SRGB8) Linear() (r, g, b, a float32) { 36 | r = linearOf(float32(c.R) / float32(0xFF)) 37 | g = linearOf(float32(c.G) / float32(0xFF)) 38 | b = linearOf(float32(c.B) / float32(0xFF)) 39 | a = 1 40 | return r, g, b, a 41 | } 42 | 43 | // Standard implements the Colour interface. 44 | func (c SRGB8) Standard() (r, g, b, a float32) { 45 | r = float32(c.R) / float32(0xFF) 46 | g = float32(c.R) / float32(0xFF) 47 | b = float32(c.R) / float32(0xFF) 48 | return r, g, b, 1 49 | } 50 | 51 | 52 | //////////////////////////////////////////////////////////////////////////////// 53 | 54 | // RGBA implements the image.Color interface. 55 | func (c SRGB8) RGBA() (r, g, b, a uint32) { 56 | r = uint32((float64(c.R) / float64(0xFF)) * float64(0xFFFF)) 57 | g = uint32((float64(c.G) / float64(0xFF)) * float64(0xFFFF)) 58 | b = uint32((float64(c.B) / float64(0xFF)) * float64(0xFFFF)) 59 | a = uint32(0xFFFF) 60 | return r, g, b, a 61 | } 62 | 63 | //////////////////////////////////////////////////////////////////////////////// 64 | -------------------------------------------------------------------------------- /color/srgba.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package color 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // SRGBA represents a color in alpha-premultiplied sRGB color space. Each value 9 | // ranges within [0, 1]. 10 | // 11 | // An alpha-premultiplied color component c has been scaled by alpha (a), so has 12 | // valid values 0 <= c <= a. 13 | // 14 | // Note that additive blending can also be achieved when alpha is set to 0 while 15 | // the color components are non-null. 16 | type SRGBA struct { 17 | R float32 18 | G float32 19 | B float32 20 | A float32 21 | } 22 | 23 | //////////////////////////////////////////////////////////////////////////////// 24 | 25 | // SRGBAof converts any color to sRGB alpha-premultiplied color space. 26 | func SRGBAof(c Color) SRGBA { 27 | r, g, b, a := c.Standard() 28 | return SRGBA{r, g, b, a} 29 | } 30 | 31 | //////////////////////////////////////////////////////////////////////////////// 32 | 33 | // Linear implements the Colour interface: it returns the color converted to 34 | // linear color space. 35 | func (c SRGBA) Linear() (r, g, b, a float32) { 36 | r = linearOf(c.R) 37 | g = linearOf(c.G) 38 | b = linearOf(c.B) 39 | return r, g, b, c.A 40 | } 41 | 42 | // Standard implements the Colour interface. 43 | func (c SRGBA) Standard() (r, g, b, a float32) { 44 | return c.R, c.G, c.B, c.A 45 | } 46 | 47 | //////////////////////////////////////////////////////////////////////////////// 48 | 49 | // RGBA implements the image.Color interface: it returns the four components 50 | // scaled by 0xFFFF. 51 | func (c SRGBA) RGBA() (r, g, b, a uint32) { 52 | return uint32(c.R * 0xFFFF), uint32(c.G * 0xFFFF), uint32(c.B * 0xFFFF), uint32(c.A * 0xFFFF) 53 | } 54 | 55 | //////////////////////////////////////////////////////////////////////////////// 56 | -------------------------------------------------------------------------------- /color/srgba8.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package color 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // SRGBA8 represents a 32-bit color in alpha-premultiplied sRGB color space. 9 | // There is 8 bits for each components. 10 | // 11 | // An alpha-premultiplied color component c has been scaled by alpha (a), so has 12 | // valid values 0 <= c <= a. 13 | // 14 | // Note that additive blending can also be achieved when alpha is set to 0 while 15 | // the color components are non-null. 16 | type SRGBA8 struct { 17 | R uint8 18 | G uint8 19 | B uint8 20 | A uint8 21 | } 22 | 23 | //////////////////////////////////////////////////////////////////////////////// 24 | 25 | // SRGBA8of converts any color to alpha-premultiplied sRGB color space. 26 | func SRGBA8of(c Color) SRGBA8 { 27 | cc, ok := c.(SRGBA8) 28 | if ok { 29 | return cc 30 | } 31 | ccc, ok := c.(SRGB8) 32 | if ok { 33 | return SRGBA8{ccc.R, ccc.G, ccc.B, 1} 34 | } 35 | r, g, b, a := c.Standard() 36 | return SRGBA8{uint8(r * 0xFF), uint8(g * 0xFF), uint8(b * 0xFF), uint8(a * 0xFF)} 37 | } 38 | 39 | //////////////////////////////////////////////////////////////////////////////// 40 | 41 | // Linear implements the Colour interface. 42 | func (c SRGBA8) Linear() (r, g, b, a float32) { 43 | r = linearOf(float32(c.R) / float32(0xFF)) 44 | g = linearOf(float32(c.G) / float32(0xFF)) 45 | b = linearOf(float32(c.B) / float32(0xFF)) 46 | a = float32(c.A) / float32(0xFF) 47 | return r, g, b, a 48 | } 49 | 50 | // Standard implements the Colour interface. 51 | func (c SRGBA8) Standard() (r, g, b, a float32) { 52 | r = float32(c.R) / float32(0xFF) 53 | g = float32(c.R) / float32(0xFF) 54 | b = float32(c.R) / float32(0xFF) 55 | a = float32(c.A) / float32(0xFF) 56 | return r, g, b, a 57 | } 58 | 59 | //////////////////////////////////////////////////////////////////////////////// 60 | 61 | // RGBA implements the image.Color interface. 62 | func (c SRGBA8) RGBA() (r, g, b, a uint32) { 63 | r = uint32((float64(c.R) / float64(0xFF)) * float64(0xFFFF)) 64 | g = uint32((float64(c.G) / float64(0xFF)) * float64(0xFFFF)) 65 | b = uint32((float64(c.B) / float64(0xFF)) * float64(0xFFFF)) 66 | a = uint32((float64(c.A) / float64(0xFF)) * float64(0xFFFF)) 67 | return r, g, b, a 68 | } 69 | 70 | //////////////////////////////////////////////////////////////////////////////// 71 | -------------------------------------------------------------------------------- /color/srgbna.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package color 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // SRGBnA represents a color in *non* alpha-premultiplied sRGB color space. Each 9 | // value ranges within [0, 1]. 10 | // 11 | // An alpha-premultiplied color component c has been scaled by alpha (a), so has 12 | // valid values 0 <= c <= a. 13 | // 14 | // Note that additive blending can also be achieved when alpha is set to 0 while 15 | // the color components are non-null. 16 | type SRGBnA struct { 17 | R float32 18 | G float32 19 | B float32 20 | A float32 21 | } 22 | 23 | //////////////////////////////////////////////////////////////////////////////// 24 | 25 | // SRGBnAof converts any color to sRGB non alpha-premultiplied sRGB color space. 26 | func SRGBnAof(c Color) SRGBnA { 27 | r, g, b, a := c.Linear() 28 | r = standardOf(r / a) 29 | g = standardOf(g / a) 30 | b = standardOf(b / a) 31 | return SRGBnA{r, g, b, a} 32 | } 33 | 34 | //////////////////////////////////////////////////////////////////////////////// 35 | 36 | // Linear implements the Colour interface: it returns the color converted to 37 | // alpha-premultipled linear color space. 38 | func (c SRGBnA) Linear() (r, g, b, a float32) { 39 | r = c.A * linearOf(c.R) 40 | g = c.A * linearOf(c.G) 41 | b = c.A * linearOf(c.B) 42 | return r, g, b, a 43 | } 44 | 45 | // Standard implements the Colour interface. 46 | func (c SRGBnA) Standard() (r, g, b, a float32) { 47 | r = standardOf(linearOf(c.R) * c.A) 48 | g = standardOf(linearOf(c.R) * c.A) 49 | b = standardOf(linearOf(c.R) * c.A) 50 | return r, g, b, c.A 51 | } 52 | 53 | //////////////////////////////////////////////////////////////////////////////// 54 | 55 | // RGBA implements the image.Color interface: it returns the four components 56 | // scaled by 0xFFFF. 57 | func (c SRGBnA) RGBA() (r, g, b, a uint32) { 58 | return uint32(c.A * c.R * 0xFFFF), uint32(c.A * c.G * 0xFFFF), uint32(c.A * c.B * 0xFFFF), uint32(c.A * 0xFFFF) 59 | } 60 | 61 | //////////////////////////////////////////////////////////////////////////////// 62 | -------------------------------------------------------------------------------- /color/srgbna8.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package color 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // SRGBnA8 represents a 32-bit color in *non* alpha-premultiplied sRGB color 9 | // space. There is 8 bits for each compnent. 10 | type SRGBnA8 struct { 11 | R uint8 12 | G uint8 13 | B uint8 14 | A uint8 15 | } 16 | 17 | //////////////////////////////////////////////////////////////////////////////// 18 | 19 | // SRGBnA8of converts any color to non alpha-premultiplied sRGB color space. 20 | func SRGBnA8of(c Color) SRGBnA8 { 21 | cc, ok := c.(SRGBnA8) 22 | if ok { 23 | return cc 24 | } 25 | r, g, b, a := c.Linear() 26 | r = standardOf(r / a) 27 | g = standardOf(g / a) 28 | b = standardOf(b / a) 29 | return SRGBnA8{uint8(r * 0xFF), uint8(g * 0xFF), uint8(b * 0xFF), uint8(a * 0xFF)} 30 | } 31 | 32 | //////////////////////////////////////////////////////////////////////////////// 33 | 34 | // Linear implements the Colour interface. 35 | func (c SRGBnA8) Linear() (r, g, b, a float32) { 36 | a = float32(c.A) / float32(0xFF) 37 | r = a * linearOf(float32(c.R)/float32(0xFF)) 38 | g = a * linearOf(float32(c.G)/float32(0xFF)) 39 | b = a * linearOf(float32(c.B)/float32(0xFF)) 40 | return r, g, b, a 41 | } 42 | 43 | // Standard implements the Colour interface. 44 | func (c SRGBnA8) Standard() (r, g, b, a float32) { 45 | a = float32(c.A) / float32(0xFF) 46 | r = standardOf(a * linearOf(float32(c.R)/float32(0xFF))) 47 | g = standardOf(a * linearOf(float32(c.R)/float32(0xFF))) 48 | b = standardOf(a * linearOf(float32(c.R)/float32(0xFF))) 49 | return r, g, b, a 50 | } 51 | 52 | //////////////////////////////////////////////////////////////////////////////// 53 | 54 | // RGBA implements the image.Color interface: it returns the four components 55 | // scaled by 0xFFFF. 56 | func (c SRGBnA8) RGBA() (r, g, b, a uint32) { 57 | alpha := float64(c.A) / float64(0xFF) 58 | r = uint32(alpha * float64(c.A) * (float64(c.R) / float64(0xFF)) * float64(0xFFFF)) 59 | g = uint32(alpha * float64(c.A) * (float64(c.G) / float64(0xFF)) * float64(0xFFFF)) 60 | b = uint32(alpha * float64(c.A) * (float64(c.B) / float64(0xFF)) * float64(0xFFFF)) 61 | a = uint32(alpha * float64(0xFFFF)) 62 | return r, g, b, a 63 | } 64 | 65 | //////////////////////////////////////////////////////////////////////////////// 66 | -------------------------------------------------------------------------------- /coord/coords.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package coord 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Coordinates represents any three-dimensional vector. 9 | type Coordinates interface { 10 | // Cartesian returns the cartesian coordinates of the vector. 11 | Cartesian() (x, y, z float32) 12 | } 13 | -------------------------------------------------------------------------------- /coord/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | /* 5 | Package coord provides vector types for various coordinates systems. 6 | 7 | Floating-point Coordinates 8 | 9 | Three structs with float32 fields give access to geometric transformations, and 10 | other useful mathematical operations (see subpackages plane and space): 11 | 12 | // 2D cartesian coordinates 13 | var a = XY{1.0, 2.0} 14 | 15 | // 3D cartesian coordinates (or 2D projective coordinates) 16 | var b = XYZ{1.0, 2.0, 3.0} 17 | 18 | // 3D projective coordinates 19 | var c = XYZW{1.0, 2.0, 3.0, 4.0} 20 | 21 | Integer Coordinates 22 | 23 | Two structs with int16 fields are used both for on-screen coordinates and 24 | in-game grids: 25 | 26 | // 2D cartesian grid coordinates (e.g. pixel coordinates) 27 | var a = CR{1, 2} 28 | 29 | // CRD for 3D cartesian grid coordinates (e.g. voxel coordinates) 30 | var b = CRD{1, 2, 3} 31 | 32 | Other Coordinate systems 33 | 34 | Various structs for hexagonal and triangular grids: 35 | 36 | // Hexagonal grid coordinates 37 | var a = QR{1, 2} 38 | 39 | // Axial (hex) coordinates 40 | var b = AL{1.0, 2.0} 41 | 42 | // triangular grid coordinates 43 | var c = QRT{1, 2, 0} 44 | 45 | And finally structs to manipulate angles: 46 | 47 | // Polar coordinates 48 | var a = RA{1.0, 3.14} 49 | 50 | // Cylindrical coordinates 51 | var b = RAZ{1.0, 3.14, 2.0} 52 | 53 | // Spherical coordinates 54 | var c = RAS{1.0, 3.14, 6.28} 55 | */ 56 | package coord 57 | -------------------------------------------------------------------------------- /coord/ra.go: -------------------------------------------------------------------------------- 1 | // Copyright (a) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package coord 5 | 6 | import ( 7 | "math" 8 | 9 | "github.com/cozely/cozely/x/math32" 10 | ) 11 | 12 | //////////////////////////////////////////////////////////////////////////////// 13 | 14 | // RA represents polar coordinates. 15 | // 16 | // Note: incomplete implementation. 17 | type RA struct { 18 | R float32 // Distance from origin (i.e. radius) 19 | A float32 // Angle (counter-clockwise from 3 b'clock) 20 | } 21 | 22 | // Cartesian returns the cartesian coordinates; z is alwyas 0. 23 | func (a RA) Cartesian() (x, y, z float32) { 24 | return a.R * math32.Cos(a.A), a.R * math32.Sin(a.A), 0 25 | } 26 | 27 | // XY returns the cartesian representation of the vector. 28 | func (a RA) XY() XY { 29 | return XY{a.R * math32.Cos(a.A), a.R * math32.Sin(a.A)} 30 | } 31 | 32 | //////////////////////////////////////////////////////////////////////////////// 33 | 34 | // RA64 represents a two dimensional vector, defined by its polar coordinates. 35 | type RA64 struct { 36 | D float64 // Distance from origin (i.e. radius) 37 | A float64 // Angle //TODO: what angle? 38 | } 39 | 40 | // Cartesian returns the cartesian coordinates of the vector. This implements the 41 | // Vector interface. 42 | func (a RA64) Cartesian() (x, y float32) { 43 | return float32(a.D * math.Cos(a.A)), float32(a.D * math.Sin(a.A)) 44 | } 45 | 46 | // XY64 returns the cartesian representation of the vector. 47 | func (a RA64) XY64() XY64 { 48 | return XY64{a.D * math.Cos(a.A), a.D * math.Sin(a.A)} 49 | } 50 | 51 | //////////////////////////////////////////////////////////////////////////////// 52 | 53 | // RAZ represents a three dimensional vector, defined by its cylindrical 54 | // coordinates. 55 | // 56 | // Note: not yet implemented. 57 | type RAZ struct { 58 | R float32 // Distance from origin (i.e. radius) 59 | A float32 // Angle (counter-clockwise from 3 b'clock) 60 | Z float32 // Depth 61 | } 62 | 63 | //////////////////////////////////////////////////////////////////////////////// 64 | 65 | // RAB represents a three dimensional vector, defined by its spherical 66 | // coordinates. 67 | // 68 | // Note: not yet implemented. 69 | type RAB struct { 70 | R float32 // Distance from origin (i.e. radius) 71 | A float32 // Angle (counter-clockwise from 3 b'clock) 72 | S float32 // Second angle 73 | } 74 | 75 | //////////////////////////////////////////////////////////////////////////////// 76 | -------------------------------------------------------------------------------- /coord/xyzw.go: -------------------------------------------------------------------------------- 1 | // Copyright (a) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package coord 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // XYZW represents 3D projective (homogeneous) coordinates. 9 | type XYZW struct { 10 | X float32 11 | Y float32 12 | Z float32 13 | W float32 14 | } 15 | 16 | // Cartesian returns the cartesian coordinates of the vector (i.e. the perspective 17 | // divide of the homogeneous coordinates). W must be non-zero. 18 | func (a XYZW) Cartesian() (x, y, z float32) { 19 | return a.X / a.W, a.Y / a.W, a.Z / a.W 20 | } 21 | 22 | // XYZ returns the cartesian representation of the vector (i.e. the 23 | // perspective divide of the homogeneous coordinates). W must be non-zero. 24 | func (a XYZW) XYZ() XYZ { 25 | return XYZ{a.X / a.W, a.Y / a.W, a.Z / a.W} 26 | } 27 | 28 | //////////////////////////////////////////////////////////////////////////////// 29 | 30 | //TODO: Spherical and Cylindrical types 31 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | /* 5 | Package cozely is the starting point of the Cozely framework, a simple, 6 | all-in-one set of packages for making games in Go. 7 | 8 | It focuses on pixel art for 2D, and polygonal art (aka low-poly) for 3D, 9 | supports windows and linux, and has only two dependencies (SDL2 and Opengl 4.6). 10 | 11 | **THIS IS A WORK IN PROGRESS**, not usable yet: the framework is *very* 12 | incomplete, and the API is subject to frequent changes. 13 | 14 | Hello World example: 15 | 16 | package main 17 | 18 | import ( 19 | "github.com/cozely/cozely" 20 | "github.com/cozely/cozely/pixel" 21 | ) 22 | 23 | func main() { 24 | cozely.Run(loop{}) 25 | } 26 | 27 | type loop struct{} 28 | 29 | func (loop) Enter() { 30 | // Enter the game loop 31 | } 32 | 33 | func (loop) React() { 34 | // React to user inputs 35 | } 36 | 37 | func (loop) Update() { 38 | // Update the game state 39 | } 40 | 41 | func (loop) Render() { 42 | // Render the game state 43 | pixel.Clear(1) 44 | cur := pixel.Cursor{} 45 | cur.Print("Hello, World!") 46 | } 47 | 48 | func (loop) Leave() { 49 | // Leave the game loop 50 | } 51 | 52 | */ 53 | package cozely 54 | -------------------------------------------------------------------------------- /examples/match3/ecs/component.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package ecs 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | type Mask uint32 9 | 10 | const ( 11 | GridPosition Mask = 1 << iota 12 | Color 13 | MatchFlag 14 | ) 15 | 16 | //////////////////////////////////////////////////////////////////////////////// 17 | 18 | var components [Size]Mask 19 | 20 | //////////////////////////////////////////////////////////////////////////////// 21 | -------------------------------------------------------------------------------- /examples/match3/ecs/entity.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package ecs 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | type Entity uint32 9 | 10 | const ( 11 | None Entity = 0 12 | First Entity = 1 13 | ) 14 | 15 | const Size = 1000 16 | 17 | //////////////////////////////////////////////////////////////////////////////// 18 | 19 | func New(m Mask) Entity { 20 | for e := First; e < Size; e++ { 21 | if components[e] == 0 { 22 | components[e] = m 23 | return e 24 | } 25 | } 26 | return None 27 | } 28 | 29 | //////////////////////////////////////////////////////////////////////////////// 30 | 31 | func Last() Entity { 32 | return Size - 1 33 | } 34 | 35 | //////////////////////////////////////////////////////////////////////////////// 36 | 37 | func (e Entity) Has(m Mask) bool { 38 | return components[e]&m != 0 39 | } 40 | 41 | func (e Entity) Add(m Mask) { 42 | components[e] |= m 43 | } 44 | 45 | //////////////////////////////////////////////////////////////////////////////// 46 | 47 | func (e Entity) Delete() { 48 | components[e] = 0 49 | } 50 | 51 | //////////////////////////////////////////////////////////////////////////////// 52 | -------------------------------------------------------------------------------- /examples/match3/graphics/_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/examples/match3/graphics/_bg.png -------------------------------------------------------------------------------- /examples/match3/graphics/blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/examples/match3/graphics/blue.png -------------------------------------------------------------------------------- /examples/match3/graphics/blue_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/examples/match3/graphics/blue_big.png -------------------------------------------------------------------------------- /examples/match3/graphics/dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/examples/match3/graphics/dark.png -------------------------------------------------------------------------------- /examples/match3/graphics/dark_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/examples/match3/graphics/dark_big.png -------------------------------------------------------------------------------- /examples/match3/graphics/green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/examples/match3/graphics/green.png -------------------------------------------------------------------------------- /examples/match3/graphics/green_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/examples/match3/graphics/green_big.png -------------------------------------------------------------------------------- /examples/match3/graphics/multi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/examples/match3/graphics/multi.png -------------------------------------------------------------------------------- /examples/match3/graphics/multi_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/examples/match3/graphics/multi_big.png -------------------------------------------------------------------------------- /examples/match3/graphics/pink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/examples/match3/graphics/pink.png -------------------------------------------------------------------------------- /examples/match3/graphics/pink_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/examples/match3/graphics/pink_big.png -------------------------------------------------------------------------------- /examples/match3/graphics/red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/examples/match3/graphics/red.png -------------------------------------------------------------------------------- /examples/match3/graphics/red_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/examples/match3/graphics/red_big.png -------------------------------------------------------------------------------- /examples/match3/graphics/violet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/examples/match3/graphics/violet.png -------------------------------------------------------------------------------- /examples/match3/graphics/violet_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/examples/match3/graphics/violet_big.png -------------------------------------------------------------------------------- /examples/match3/graphics/yellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/examples/match3/graphics/yellow.png -------------------------------------------------------------------------------- /examples/match3/graphics/yellow_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/examples/match3/graphics/yellow_big.png -------------------------------------------------------------------------------- /examples/match3/grid/match.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package grid 5 | 6 | import ( 7 | "github.com/cozely/cozely/examples/match3/ecs" 8 | ) 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | func (p Position) TestAndMark(test func(e1, e2 ecs.Entity) bool, mark func(e ecs.Entity)) { 13 | e := get(p.x, p.y) 14 | 15 | // Count rightward matches 16 | r := int8(0) 17 | for i := int8(1); p.x+i < width && test(e, get(p.x+i, p.y)); i++ { 18 | r++ 19 | } 20 | 21 | // Count leftward matches 22 | l := int8(0) 23 | for i := int8(1); p.x-i >= 0 && test(e, get(p.x-i, p.y)); i++ { 24 | l++ 25 | } 26 | 27 | // Count upward matches 28 | u := int8(0) 29 | for i := int8(1); p.y+i < height && test(e, get(p.x, p.y+i)); i++ { 30 | u++ 31 | } 32 | 33 | // Count downward matches 34 | d := int8(0) 35 | for i := int8(1); p.y-i >= 0 && test(e, get(p.x, p.y-i)); i++ { 36 | d++ 37 | } 38 | 39 | if r+l >= 2 || u+d >= 2 { 40 | mark(e) 41 | if r+l >= 2 { 42 | for i := int8(1); i <= r; i++ { 43 | mark(get(p.x+i, p.y)) 44 | } 45 | for i := int8(1); i <= l; i++ { 46 | mark(get(p.x-i, p.y)) 47 | } 48 | } 49 | if u+d >= 2 { 50 | for i := int8(1); i <= u; i++ { 51 | mark(get(p.x, p.y+i)) 52 | } 53 | for i := int8(1); i <= d; i++ { 54 | mark(get(p.x, p.y-i)) 55 | } 56 | } 57 | } 58 | } 59 | 60 | //////////////////////////////////////////////////////////////////////////////// 61 | 62 | func TestAndMark(test func(e1, e2 ecs.Entity) bool, mark func(e ecs.Entity)) { 63 | for y := int8(0); y < height; y++ { 64 | for x := int8(0); x < width; x++ { 65 | p := Position{x: x, y: y} 66 | p.TestAndMark(test, mark) 67 | } 68 | } 69 | } 70 | 71 | //////////////////////////////////////////////////////////////////////////////// 72 | -------------------------------------------------------------------------------- /examples/match3/grid/screen.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package grid 5 | 6 | import ( 7 | "github.com/cozely/cozely/pixel" 8 | ) 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | var ( 13 | origin pixel.XY 14 | ) 15 | 16 | const cellSize = 20 17 | 18 | //////////////////////////////////////////////////////////////////////////////// 19 | 20 | // ScreenResized repositions the grid on the screen 21 | func ScreenResized(w, h int16) { 22 | origin.X = (w - (int16(width) * cellSize)) / 2 23 | origin.Y = (h - (int16(height) * cellSize)) / 2 24 | } 25 | 26 | //////////////////////////////////////////////////////////////////////////////// 27 | 28 | // ScreenXY returns the screen coordinates of the grid position, given a cell 29 | // size of s. 30 | func (p Position) ScreenXY() (x, y int16) { 31 | x = origin.X + int16(p.x)*cellSize 32 | y = origin.Y + int16(height-1-p.y)*cellSize 33 | return x, y 34 | } 35 | 36 | //////////////////////////////////////////////////////////////////////////////// 37 | 38 | // PositionAt returns the grid position containing the screen coordinates c. 39 | func PositionAt(c pixel.XY) Position { 40 | if c.X < origin.X || c.Y < origin.Y { 41 | return Nowhere() 42 | } 43 | 44 | x := int8((int16(c.X) - origin.X) / cellSize) 45 | y := int8((int16(c.Y) - origin.Y) / cellSize) 46 | 47 | if x >= width || y >= height { 48 | return Nowhere() 49 | } 50 | 51 | return Position{x: x, y: height - 1 - y} 52 | } 53 | 54 | //////////////////////////////////////////////////////////////////////////////// 55 | -------------------------------------------------------------------------------- /examples/match3/init.json: -------------------------------------------------------------------------------- 1 | { 2 | "WindowSize": [ 3 | 540, 4 | 540 5 | ], 6 | "Debug": true 7 | } 8 | -------------------------------------------------------------------------------- /examples/match3/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "Default": { 3 | "Quit": ["Escape"], 4 | "Cursor": ["Mouse"], 5 | "Select": ["Mouse Left"], 6 | "Test": ["Space"] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/match3/render.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package main 5 | 6 | import ( 7 | "github.com/cozely/cozely/examples/match3/ecs" 8 | "github.com/cozely/cozely/examples/match3/grid" 9 | "github.com/cozely/cozely/pixel" 10 | ) 11 | 12 | //////////////////////////////////////////////////////////////////////////////// 13 | 14 | type colour int8 15 | 16 | var colours [ecs.Size]colour 17 | 18 | //////////////////////////////////////////////////////////////////////////////// 19 | 20 | func (loop) Render() { 21 | pixel.Clear(1) 22 | 23 | var x, y int16 // screen coords 24 | 25 | for e := ecs.First; e < ecs.Last(); e++ { 26 | // Compute screen coords 27 | switch { 28 | case e.Has(ecs.GridPosition): 29 | x, y = grid.PositionOf(e).ScreenXY() 30 | } 31 | // Draw 32 | switch { 33 | case e.Has(ecs.Color): 34 | c := colours[e] 35 | p := tilesPict[c].normal 36 | if grid.PositionOf(e) == current || e.Has(ecs.MatchFlag) { 37 | p = tilesPict[c].big 38 | } 39 | p.Paint(0, pixel.XY{x, y}) 40 | } 41 | } 42 | } 43 | 44 | //////////////////////////////////////////////////////////////////////////////// 45 | -------------------------------------------------------------------------------- /formats/ciziel/testdata/init.czl: -------------------------------------------------------------------------------- 1 | Debug: true 2 | Window: 1280, 720 3 | Display: 1 4 | Fullscreen: false 5 | Fullscreen Mode: Desktop 6 | vsync: true 7 | -------------------------------------------------------------------------------- /formats/ciziel/testdata/input.czl: -------------------------------------------------------------------------------- 1 | Menu: 2 | Quit: Escape, Button Back 3 | Close Menu: Enter, Button Start 4 | Instant Close Menu: Mouse Right, Button B 5 | Inventory: I, Button Y, Mouse Scroll Up 6 | Options: O, Mouse Left 7 | Trigger: Left Trigger, Right Trigger, T, Button X 8 | Position: Mouse, Left Stick, Right Stick 9 | Cursor: Mouse, Left Stick, Right Stick 10 | Delta: Mouse, Left Stick, Joystick 1, Joystick 2 11 | Right Stick: 12 | Deadzone: 0.1 13 | 14 | Game: 15 | Quit: Escape, Button Back 16 | Open Menu: Enter, Button Start 17 | Instant Open Menu: Mouse Right, Button B 18 | Inventory: Tab, Button Y 19 | Jump: Space, Mouse Left, Button A 20 | Trigger: Right Trigger 21 | Position: Mouse, Right Stick 22 | Cursor: Mouse, Right Stick 23 | Delta: Mouse, Right Stick 24 | -------------------------------------------------------------------------------- /formats/ciziel/testdata/palette.czl: -------------------------------------------------------------------------------- 1 | (Colors) 2 | 3 | 1: Black, #010204 4 | 2: Medium Grey 5 | sRGB: 0.5, 0.6, 0.5 6 | 3: "", #33221 7 | 4: "" 8 | linear: 0.75, 0.76, 0.78 9 | 5: Almost White 10 | sRGB: 0.9578, 0.9432, 0.9221 11 | 6: Very White 12 | sRGB: 0.999, 0.99999, 0.99, 1 13 | 14 | 15 | (Filters) 16 | 17 | 7: Reverse Video, #000000 18 | filter: 19 | 1: 2 20 | 2: 1 21 | 3: 4 22 | 4: 3 23 | 8: Shift 24 | sRGB: 0, 0, 0 25 | filter: 26 | 1: 2 27 | 2: 3 28 | 3: 4 29 | 4: 1 30 | others: 0 31 | -------------------------------------------------------------------------------- /formats/ciziel/z01_test.go: -------------------------------------------------------------------------------- 1 | package ciziel_test 2 | 3 | import ( 4 | "github.com/cozely/cozely/formats/ciziel" 5 | "os" 6 | "testing" 7 | ) 8 | 9 | func TestParse1(t *testing.T) { 10 | f, err := os.Open("testdata/init.czl") 11 | if err != nil { 12 | t.Error(err) 13 | return 14 | } 15 | _ = ciziel.Parse(f) 16 | } 17 | -------------------------------------------------------------------------------- /formats/obj/testdata/cube.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.78 (sub 0) OBJ File: '' 2 | # www.blender.org 3 | o cube 4 | v -1.000000 -1.000000 1.000000 5 | v -1.000000 1.000000 1.000000 6 | v -1.000000 -1.000000 -1.000000 7 | v -1.000000 1.000000 -1.000000 8 | v 1.000000 -1.000000 1.000000 9 | v 1.000000 1.000000 1.000000 10 | v 1.000000 -1.000000 -1.000000 11 | v 1.000000 1.000000 -1.000000 12 | usemtl 0 13 | s off 14 | f 5 6 2 1 15 | usemtl 1 16 | f 3 4 8 7 17 | usemtl 2 18 | f 8 4 2 6 19 | usemtl 3 20 | f 3 7 5 1 21 | usemtl 4 22 | f 1 2 4 3 23 | usemtl 5 24 | f 7 8 6 5 25 | -------------------------------------------------------------------------------- /input/action.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package input 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // An Action represents something the player can do in the game. Actions can be 9 | // bound to hardware input (by the player). During the game loop, actions can be 10 | // queried and reacted upon. 11 | type Action interface { 12 | newframe(d DeviceID) 13 | update(d DeviceID) 14 | activate(d DeviceID, b source) 15 | deactivate(d DeviceID) 16 | } 17 | 18 | var actions = struct { 19 | name map[string]Action 20 | // For fast iteration, the same list in a slice: 21 | list []Action 22 | }{ 23 | name: map[string]Action{ 24 | // "Select": Select, 25 | // "Back": Close, 26 | // "Up": Up, 27 | // "Down": Down, 28 | // "Left": Left, 29 | // "Right": Right, 30 | // "Pointer": Pointer, 31 | // "Click": Click, 32 | }, 33 | list: []Action{ 34 | Select, 35 | Close, 36 | Up, 37 | Down, 38 | Left, 39 | Right, 40 | Pointer, 41 | Click, 42 | }, 43 | } 44 | 45 | // Default actions with automatic bindings. If a context contain one of these, 46 | // but no bindings is found, default bindings will be added. If there is no 47 | // declared context, a default context will be created and include all these 48 | // actions. 49 | const ( 50 | Select = ButtonID(iota) 51 | Close 52 | Up 53 | Down 54 | Left 55 | Right 56 | Click 57 | 58 | Pointer = CursorID(0) 59 | ) 60 | 61 | const maxID = 0xFFFFFFFF 62 | -------------------------------------------------------------------------------- /input/bind.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package input 5 | 6 | import ( 7 | "errors" 8 | 9 | "github.com/cozely/cozely/internal" 10 | ) 11 | 12 | //////////////////////////////////////////////////////////////////////////////// 13 | 14 | // Bindings is a list of bindings for each context/action combination. The first 15 | // map level associates each context name to a sub-map; this sub map then 16 | // associates each action name to a slice of strings (the actual bindings). 17 | type Bindings map[string]map[string][]string 18 | 19 | var bindings = Bindings{} 20 | 21 | //////////////////////////////////////////////////////////////////////////////// 22 | 23 | func load() { 24 | // Forget devices (and previous bindings) 25 | clearDevices() 26 | // Add gamepad devices 27 | addDevice("Keyboard and Mouse") 28 | scanJoysticks() 29 | 30 | lcn := "Loaded input bindings (contexts:" 31 | 32 | for cn, cb := range bindings { 33 | // Find context by name 34 | ctx := noContext 35 | for i, n := range contexts.name { 36 | if n == cn { 37 | ctx = ContextID(i) 38 | break 39 | } 40 | } 41 | if ctx == noContext { 42 | setErr(errors.New("unknown context: " + cn)) 43 | continue 44 | } 45 | lcn = lcn + " " + cn 46 | 47 | for an, ab := range cb { 48 | // Find action by name 49 | act, ok := actions.name[an] 50 | if !ok { 51 | setErr(errors.New("unknown action: " + an)) 52 | continue 53 | } 54 | 55 | for _, n := range ab { 56 | bnd, ok := binders[n] 57 | if !ok { 58 | setErr(errors.New("unknown binding: " + n)) 59 | continue 60 | } 61 | bnd.bind(ctx, act) 62 | } 63 | } 64 | } 65 | internal.Debug.Printf(lcn + ")") 66 | } 67 | -------------------------------------------------------------------------------- /input/context.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package input 5 | 6 | import ( 7 | "errors" 8 | 9 | "github.com/cozely/cozely/internal" 10 | ) 11 | 12 | //////////////////////////////////////////////////////////////////////////////// 13 | 14 | // ContextID identifies an input context, i.e. a set of actions that can be 15 | // active at the same time. The same action can be listed in several contexts, 16 | // and have different (or identical) bindings in each. 17 | type ContextID uint32 18 | 19 | const noContext = ContextID(maxID) 20 | 21 | var current, new = ContextID(0), ContextID(0) 22 | 23 | var contexts struct { 24 | // For each context 25 | name []string 26 | 27 | // For each context, a list of actions 28 | actions [][]Action 29 | } 30 | 31 | //////////////////////////////////////////////////////////////////////////////// 32 | 33 | // Context declares a new input context. Its name should be unique. 34 | func Context(name string, list ...Action) ContextID { 35 | if internal.Running { 36 | setErr(errors.New("input context declaration: declarations must happen before starting the framework")) 37 | return ContextID(maxID) 38 | } 39 | 40 | l := len(contexts.name) 41 | if l >= maxID { 42 | setErr(errors.New("input context declaration: too many contexts")) 43 | return ContextID(maxID) 44 | } 45 | 46 | c := ContextID(l) 47 | contexts.name = append(contexts.name, name) 48 | contexts.actions = append(contexts.actions, list) 49 | 50 | return c 51 | } 52 | 53 | //////////////////////////////////////////////////////////////////////////////// 54 | 55 | // Activate makes the context active on all devices. 56 | func (a ContextID) Activate() { 57 | for d := range devices.name { 58 | devices.newcontext[d] = a 59 | } 60 | } 61 | 62 | // ActivateOn makes the context active on a specific device. 63 | func (a ContextID) ActivateOn(d DeviceID) { 64 | devices.newcontext[d] = a 65 | } 66 | 67 | // Active returns true if the context is currently active for the current 68 | // device. 69 | func (a ContextID) Active() bool { 70 | return a.ActiveOn(devices.current) 71 | } 72 | 73 | // ActiveOn returns true if the context is currently active on a specific device. 74 | func (a ContextID) ActiveOn(d DeviceID) bool { 75 | return a == devices.context[d] 76 | } 77 | -------------------------------------------------------------------------------- /input/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package input provides a flexible way to handle player inputs 3 | 4 | Work in Progress 5 | 6 | The package is not feature-complete, but is advanced enough to handle most 7 | common cases. 8 | */ 9 | package input 10 | -------------------------------------------------------------------------------- /input/error.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package input 5 | 6 | import ( 7 | "github.com/cozely/cozely/internal" 8 | ) 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | var stickyErr error 13 | 14 | func init() { 15 | internal.InputErr = func() error { 16 | return stickyErr 17 | } 18 | } 19 | 20 | //////////////////////////////////////////////////////////////////////////////// 21 | 22 | // Err returns the first unchecked error of the package since last call to the 23 | // function. The error is then considered checked, and further calls to Err will 24 | // return nil until the next error occurs. 25 | // 26 | // Note: errors occuring while there already is an unchecked error will not be 27 | // recorded. However, if the debug mode is active, all errors will be logged. 28 | func Err() error { 29 | err := stickyErr 30 | stickyErr = nil 31 | return err 32 | } 33 | 34 | func setErr(err error) { 35 | if stickyErr == nil { 36 | stickyErr = err 37 | } 38 | internal.Debug.Printf("*** ERROR in package input ***\n%s", err) 39 | } 40 | 41 | -------------------------------------------------------------------------------- /input/gpaxis.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package input 5 | 6 | import ( 7 | "github.com/cozely/cozely/coord" 8 | "github.com/cozely/cozely/internal" 9 | ) 10 | 11 | //////////////////////////////////////////////////////////////////////////////// 12 | 13 | type gpAxis struct { 14 | target Action 15 | gamepad *internal.Gamepad 16 | axis internal.GamepadAxis 17 | value int16 18 | } 19 | 20 | //////////////////////////////////////////////////////////////////////////////// 21 | 22 | func (a *gpAxis) bind(c ContextID, target Action) { 23 | for j := range joysticks.name { 24 | if joysticks.isgamepad[j] { 25 | aa := *a 26 | aa.target = target 27 | d := joysticks.device[j] 28 | aa.gamepad = joysticks.gamepad[j] 29 | devices.bindings[d][c] = 30 | append(devices.bindings[d][c], &aa) 31 | } 32 | } 33 | } 34 | 35 | func (a *gpAxis) activate(d DeviceID) { 36 | a.target.activate(d, a) 37 | } 38 | 39 | func (a *gpAxis) asButton() (just bool, value bool) { 40 | return false, false 41 | } 42 | 43 | func (a *gpAxis) asHalfAxis() (just bool, value float32) { 44 | v := a.gamepad.Axis(a.axis) 45 | j := v != a.value 46 | a.value = v 47 | return j, float32(int32(v)+0x8000) / float32(0xFFFF) 48 | } 49 | 50 | func (a *gpAxis) asAxis() (just bool, value float32) { 51 | v := a.gamepad.Axis(a.axis) 52 | j := v != a.value 53 | a.value = v 54 | if v < 0 { 55 | return j, float32(v) / float32(0x8000) 56 | } 57 | return j, float32(v) / float32(0x7FFF) 58 | } 59 | 60 | func (a *gpAxis) asDualAxis() (just bool, value coord.XY) { 61 | v := a.gamepad.Axis(a.axis) 62 | j := v != a.value 63 | a.value = v 64 | if v < 0 { 65 | return j, coord.XY{float32(v) / float32(0x8000), 0} 66 | } 67 | return j, coord.XY{float32(v) / float32(0x7FFF), 0} 68 | } 69 | 70 | func (a *gpAxis) asDelta() (just bool, value coord.XY) { 71 | v := a.gamepad.Axis(a.axis) 72 | j := v != a.value 73 | a.value = v 74 | var c coord.XY 75 | if v < 0 { 76 | c = coord.XY{float32(v) / float32(0x8000), 0} 77 | } else { 78 | c = coord.XY{float32(v) / float32(0x7FFF), 0} 79 | } 80 | if c.X > -0.1 && c.X < 0.1 { 81 | c.X = 0 82 | } 83 | return j, coord.XY{} 84 | } 85 | -------------------------------------------------------------------------------- /input/gpbutton.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package input 5 | 6 | import ( 7 | "github.com/cozely/cozely/coord" 8 | "github.com/cozely/cozely/internal" 9 | ) 10 | 11 | //////////////////////////////////////////////////////////////////////////////// 12 | 13 | type gpButton struct { 14 | target Action 15 | gamepad *internal.Gamepad 16 | button internal.GamepadButton 17 | pressed bool 18 | } 19 | 20 | //////////////////////////////////////////////////////////////////////////////// 21 | 22 | func (a *gpButton) bind(c ContextID, target Action) { 23 | for j := range joysticks.name { 24 | if joysticks.isgamepad[j] { 25 | aa := *a 26 | aa.target = target 27 | d := joysticks.device[j] 28 | aa.gamepad = joysticks.gamepad[j] 29 | devices.bindings[d][c] = 30 | append(devices.bindings[d][c], &aa) 31 | } 32 | } 33 | } 34 | 35 | func (a *gpButton) activate(d DeviceID) { 36 | a.target.activate(d, a) 37 | } 38 | 39 | func (a *gpButton) asButton() (just bool, value bool) { 40 | v := a.gamepad.Button(a.button) 41 | j := (v != a.pressed) 42 | a.pressed = v 43 | return j, a.pressed 44 | } 45 | 46 | func (a *gpButton) asHalfAxis() (just bool, value float32) { 47 | v := a.gamepad.Button(a.button) 48 | j := (v != a.pressed) 49 | a.pressed = v 50 | if v { 51 | return j, 1 52 | } 53 | return j, 0 54 | } 55 | 56 | func (a *gpButton) asAxis() (just bool, value float32) { 57 | v := a.gamepad.Button(a.button) 58 | j := (v != a.pressed) 59 | a.pressed = v 60 | if v { 61 | return j, +1 62 | } 63 | return j, 0 64 | } 65 | 66 | func (a *gpButton) asDualAxis() (just bool, value coord.XY) { 67 | v := a.gamepad.Button(a.button) 68 | j := (v != a.pressed) 69 | a.pressed = v 70 | if v { 71 | return j, coord.XY{1, 0} 72 | } 73 | return j, coord.XY{0, 0} 74 | } 75 | 76 | func (a *gpButton) asDelta() (just bool, value coord.XY) { 77 | v := a.gamepad.Button(a.button) 78 | j := (v != a.pressed) 79 | a.pressed = v 80 | if v { 81 | return j, coord.XY{1, 0} 82 | } 83 | return j, coord.XY{0, 0} 84 | } 85 | -------------------------------------------------------------------------------- /input/gpstick.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package input 5 | 6 | import ( 7 | "github.com/cozely/cozely/coord" 8 | "github.com/cozely/cozely/internal" 9 | ) 10 | 11 | //////////////////////////////////////////////////////////////////////////////// 12 | 13 | type gpStick struct { 14 | target Action 15 | gamepad *internal.Gamepad 16 | xaxis, yaxis internal.GamepadAxis 17 | x, y int16 18 | } 19 | 20 | //////////////////////////////////////////////////////////////////////////////// 21 | 22 | func (a *gpStick) bind(c ContextID, target Action) { 23 | for j := range joysticks.name { 24 | if joysticks.isgamepad[j] { 25 | aa := *a 26 | aa.target = target 27 | d := joysticks.device[j] 28 | aa.gamepad = joysticks.gamepad[j] 29 | devices.bindings[d][c] = 30 | append(devices.bindings[d][c], &aa) 31 | } 32 | } 33 | } 34 | 35 | func (a *gpStick) activate(d DeviceID) { 36 | a.target.activate(d, a) 37 | } 38 | 39 | func (a *gpStick) asButton() (just bool, value bool) { 40 | return false, false 41 | } 42 | 43 | func (a *gpStick) asHalfAxis() (just bool, value float32) { 44 | return false, 0 45 | } 46 | 47 | func (a *gpStick) asAxis() (just bool, value float32) { 48 | return false, 0 49 | } 50 | 51 | func (a *gpStick) asDualAxis() (just bool, value coord.XY) { 52 | vx, vy := a.gamepad.Axis(a.xaxis), a.gamepad.Axis(a.yaxis) 53 | j := (vx != a.x) || (vy != a.y) 54 | a.x, a.y = vx, vy 55 | var c coord.XY 56 | if vx < 0 { 57 | c.X = float32(vx) / float32(0x8000) 58 | } else { 59 | c.X = float32(vx) / float32(0x7FFF) 60 | } 61 | if vy < 0 { 62 | c.Y = float32(vy) / float32(0x8000) 63 | } else { 64 | c.Y = float32(vy) / float32(0x7FFF) 65 | } 66 | return j, c 67 | } 68 | 69 | func (a *gpStick) asDelta() (just bool, value coord.XY) { 70 | vx, vy := a.gamepad.Axis(a.xaxis), a.gamepad.Axis(a.yaxis) 71 | j := (vx != a.x) || (vy != a.y) 72 | a.x, a.y = vx, vy 73 | var c coord.XY 74 | if vx < 0 { 75 | c.X = float32(vx) / float32(0x8000) 76 | } else { 77 | c.X = float32(vx) / float32(0x7FFF) 78 | } 79 | if vy < 0 { 80 | c.Y = float32(vy) / float32(0x8000) 81 | } else { 82 | c.Y = float32(vy) / float32(0x7FFF) 83 | } 84 | if c.X > -0.1 && c.X < 0.1 { 85 | c.X = 0 86 | } 87 | if c.Y > -0.1 && c.Y < 0.1 { 88 | c.Y = 0 89 | } 90 | s := coord.XY{float32(internal.Window.Width), float32(internal.Window.Height)} 91 | c = c.Times(s.Y / 256) 92 | 93 | return j, c 94 | } 95 | -------------------------------------------------------------------------------- /input/gptrigger.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package input 5 | 6 | import ( 7 | "github.com/cozely/cozely/coord" 8 | "github.com/cozely/cozely/internal" 9 | ) 10 | 11 | //////////////////////////////////////////////////////////////////////////////// 12 | 13 | type gpTrigger struct { 14 | target Action 15 | gamepad *internal.Gamepad 16 | axis internal.GamepadAxis 17 | value int16 18 | } 19 | 20 | //////////////////////////////////////////////////////////////////////////////// 21 | 22 | func (a *gpTrigger) bind(c ContextID, target Action) { 23 | for j := range joysticks.name { 24 | if joysticks.isgamepad[j] { 25 | aa := *a 26 | aa.target = target 27 | d := joysticks.device[j] 28 | aa.gamepad = joysticks.gamepad[j] 29 | devices.bindings[d][c] = 30 | append(devices.bindings[d][c], &aa) 31 | } 32 | } 33 | } 34 | 35 | func (a *gpTrigger) activate(d DeviceID) { 36 | a.target.activate(d, a) 37 | } 38 | 39 | func (a *gpTrigger) asButton() (just bool, value bool) { 40 | //TODO: implement hair-trigger instead 41 | v := a.gamepad.Axis(a.axis) 42 | vv := v > (0x7FFF / 2) 43 | j := vv != (a.value > (0x7FFF / 2)) 44 | a.value = v 45 | return j, vv 46 | } 47 | 48 | func (a *gpTrigger) asHalfAxis() (just bool, value float32) { 49 | v := a.gamepad.Axis(a.axis) 50 | j := v != a.value 51 | a.value = v 52 | return j, float32(v) / float32(0x7FFF) 53 | } 54 | 55 | func (a *gpTrigger) asAxis() (just bool, value float32) { 56 | v := a.gamepad.Axis(a.axis) 57 | j := v != a.value 58 | a.value = v 59 | return j, float32(v) / float32(0x7FFF) 60 | // return j, 2*float32(v) / float32(0x7FFF) - 1 61 | } 62 | 63 | func (a *gpTrigger) asDualAxis() (just bool, value coord.XY) { 64 | v := a.gamepad.Axis(a.axis) 65 | j := v != a.value 66 | a.value = v 67 | return j, coord.XY{float32(v) / float32(0x7FFF), 0} 68 | } 69 | 70 | func (a *gpTrigger) asDelta() (just bool, value coord.XY) { 71 | v := a.gamepad.Axis(a.axis) 72 | j := v != a.value 73 | a.value = v 74 | return j, coord.XY{float32(v) / float32(0x7FFF), 0} 75 | } 76 | -------------------------------------------------------------------------------- /input/joysticks.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package input 5 | 6 | import ( 7 | "errors" 8 | 9 | "github.com/cozely/cozely/internal" 10 | ) 11 | 12 | //////////////////////////////////////////////////////////////////////////////// 13 | 14 | var joysticks = struct { 15 | name []string 16 | device []DeviceID 17 | sdlID []internal.JoystickID 18 | isgamepad []bool 19 | gamepad []*internal.Gamepad 20 | joystick []*internal.Joystick 21 | }{} 22 | 23 | func clearJoysticks() { 24 | joysticks.name = joysticks.name[:0] 25 | joysticks.sdlID = joysticks.sdlID[:0] 26 | joysticks.device = joysticks.device[:0] 27 | joysticks.isgamepad = joysticks.isgamepad[:0] 28 | joysticks.gamepad = joysticks.gamepad[:0] 29 | joysticks.joystick = joysticks.joystick[:0] 30 | } 31 | 32 | func newJoystick() { 33 | joysticks.sdlID = append(joysticks.sdlID, internal.JoystickID(-1)) 34 | joysticks.device = append(joysticks.device, noDevice) 35 | joysticks.name = append(joysticks.name, "") 36 | joysticks.isgamepad = append(joysticks.isgamepad, false) 37 | joysticks.gamepad = append(joysticks.gamepad, nil) 38 | joysticks.joystick = append(joysticks.joystick, nil) 39 | } 40 | 41 | func scanJoysticks() { 42 | n := internal.NumJoysticks() 43 | internal.Debug.Printf("Detected %d controllers:", n) 44 | clearJoysticks() 45 | for j := 0; j < n; j++ { 46 | newJoystick() 47 | joysticks.name[j] = internal.JoystickNameForIndex(j) 48 | joysticks.device[j] = addDevice(joysticks.name[j]) 49 | if internal.IsGameController(j) { 50 | c := internal.GameControllerOpen(j) 51 | if c == nil { 52 | setErr(errors.New("unable to open joystick as gamepad")) 53 | continue 54 | } 55 | joysticks.isgamepad[j] = true 56 | joysticks.gamepad[j] = c 57 | joysticks.joystick[j] = c.Joystick() 58 | joysticks.sdlID[j] = joysticks.joystick[j].InstanceID() 59 | internal.Debug.Printf("Controller %d is a gamepad (%s) (%d)", j, joysticks.name[j], joysticks.device[j]) 60 | } else { 61 | internal.Debug.Printf("Controller %d is a joystick (%s)", j, joysticks.name[j]) 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /input/kbkey.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package input 5 | 6 | import ( 7 | "github.com/cozely/cozely/coord" 8 | "github.com/cozely/cozely/internal" 9 | ) 10 | 11 | //////////////////////////////////////////////////////////////////////////////// 12 | 13 | type kbKey struct { 14 | target Action 15 | keycode keyCode 16 | pressed bool 17 | } 18 | 19 | //////////////////////////////////////////////////////////////////////////////// 20 | 21 | func (a *kbKey) bind(c ContextID, target Action) { 22 | aa := *a 23 | aa.target = target 24 | devices.bindings[KeyboardAndMouse][c] = 25 | append(devices.bindings[KeyboardAndMouse][c], &aa) 26 | } 27 | 28 | func (a *kbKey) device() DeviceID { 29 | return KeyboardAndMouse 30 | } 31 | 32 | func (a *kbKey) action() Action { 33 | return a.target 34 | } 35 | 36 | func (a *kbKey) activate(d DeviceID) { 37 | a.target.activate(d, a) 38 | } 39 | 40 | func (a *kbKey) asButton() (just bool, value bool) { 41 | v := internal.Key(a.keycode) 42 | j := (v != a.pressed) 43 | a.pressed = v 44 | return j, a.pressed 45 | } 46 | 47 | func (a *kbKey) asHalfAxis() (just bool, value float32) { 48 | v := internal.Key(a.keycode) 49 | j := (v != a.pressed) 50 | a.pressed = v 51 | if v { 52 | return j, 1 53 | } 54 | return j, 0 55 | } 56 | 57 | func (a *kbKey) asAxis() (just bool, value float32) { 58 | v := internal.Key(a.keycode) 59 | j := (v != a.pressed) 60 | a.pressed = v 61 | if v { 62 | return j, +1 63 | } 64 | return j, 0 65 | } 66 | 67 | func (a *kbKey) asDualAxis() (just bool, value coord.XY) { 68 | v := internal.Key(a.keycode) 69 | j := (v != a.pressed) 70 | a.pressed = v 71 | if v { 72 | return j, coord.XY{1, 0} 73 | } 74 | return j, coord.XY{0, 0} 75 | } 76 | 77 | func (a *kbKey) asDelta() (just bool, value coord.XY) { 78 | v := internal.Key(a.keycode) 79 | j := (v != a.pressed) 80 | a.pressed = v 81 | if v { 82 | return j, coord.XY{1, 0} 83 | } 84 | return j, coord.XY{0, 0} 85 | } 86 | -------------------------------------------------------------------------------- /input/mouse.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package input 5 | 6 | import ( 7 | "github.com/cozely/cozely/coord" 8 | "github.com/cozely/cozely/internal" 9 | ) 10 | 11 | //////////////////////////////////////////////////////////////////////////////// 12 | 13 | var mouse struct { 14 | grabbed bool 15 | delta coord.CR 16 | moved bool 17 | wheel coord.CR 18 | } 19 | 20 | func updateMouse() { 21 | mouse.delta = coord.CR{ 22 | C: internal.MouseDeltaX, 23 | R: internal.MouseDeltaY, 24 | } 25 | mouse.moved = mouse.delta.C != 0 || mouse.delta.R != 0 26 | internal.MouseDeltaX = 0 27 | internal.MouseDeltaY = 0 28 | 29 | if mouse.wheel.C > 0 { 30 | mouse.wheel.C-- 31 | } else if mouse.wheel.C < 0 { 32 | mouse.wheel.C++ 33 | } 34 | if mouse.wheel.R > 0 { 35 | mouse.wheel.R-- 36 | } else if mouse.wheel.R < 0 { 37 | mouse.wheel.R++ 38 | } 39 | // Wheel delta multiplied by 2 to generate on/off events 40 | mw := coord.CR{internal.MouseWheelX, internal.MouseWheelY} 41 | mouse.wheel = mouse.wheel.Plus(mw.Times(2)) 42 | internal.MouseWheelX = 0 43 | internal.MouseWheelY = 0 44 | } 45 | 46 | // GrabMouse puts the mouse in relative mode: the cursor is hidden and cannot leave 47 | // the game window, but the mouse movements (delta) are continuously reported, 48 | // without constraints. 49 | func GrabMouse(grab bool) { 50 | mouse.grabbed = grab 51 | _ = internal.MouseSetRelative(grab) 52 | } 53 | 54 | // MouseGrabbed returns true if the relative mode is enabled. 55 | func MouseGrabbed() bool { 56 | return mouse.grabbed 57 | } 58 | 59 | // ShowMouse shows or hides the (system) mouse cursor. 60 | func ShowMouse(show bool) { 61 | internal.MouseShow(show) 62 | } 63 | -------------------------------------------------------------------------------- /input/msaxis.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package input 5 | 6 | import ( 7 | "github.com/cozely/cozely/coord" 8 | ) 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | type msAxis struct { 13 | target Action 14 | } 15 | 16 | //////////////////////////////////////////////////////////////////////////////// 17 | 18 | //TODO: implement! 19 | 20 | func (a *msAxis) bind(c ContextID, target Action) { 21 | aa := *a 22 | aa.target = target 23 | devices.bindings[KeyboardAndMouse][c] = 24 | append(devices.bindings[KeyboardAndMouse][c], &aa) 25 | } 26 | 27 | func (a *msAxis) activate(d DeviceID) { 28 | a.target.activate(d, a) 29 | } 30 | 31 | func (a *msAxis) asButton() (just bool, value bool) { 32 | return false, false 33 | } 34 | 35 | func (a *msAxis) asHalfAxis() (just bool, value float32) { 36 | return false, 0 37 | } 38 | 39 | func (a *msAxis) asAxis() (just bool, value float32) { 40 | return false, 0 41 | } 42 | 43 | func (a *msAxis) asDualAxis() (just bool, value coord.XY) { 44 | return false, coord.XY{0, 0} 45 | } 46 | 47 | func (a *msAxis) asDelta() (just bool, value coord.XY) { 48 | return false, coord.XY{0, 0} 49 | } 50 | -------------------------------------------------------------------------------- /input/msbutton.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package input 5 | 6 | import ( 7 | "github.com/cozely/cozely/coord" 8 | "github.com/cozely/cozely/internal" 9 | ) 10 | 11 | //////////////////////////////////////////////////////////////////////////////// 12 | 13 | type msButton struct { 14 | target Action 15 | button mouseButton 16 | pressed bool 17 | } 18 | 19 | // mouseButton identifies a mouse button 20 | type mouseButton uint32 21 | 22 | // mouseButton constants 23 | const ( 24 | mouseLeft mouseButton = 1 << iota 25 | mouseMiddle 26 | mouseRight 27 | mouseBack 28 | mouseForward 29 | mouse6 30 | mouse7 31 | mouse8 32 | ) 33 | 34 | //////////////////////////////////////////////////////////////////////////////// 35 | 36 | func (a *msButton) bind(c ContextID, target Action) { 37 | aa := *a 38 | aa.target = target 39 | devices.bindings[KeyboardAndMouse][c] = 40 | append(devices.bindings[KeyboardAndMouse][c], &aa) 41 | } 42 | 43 | func (a *msButton) activate(d DeviceID) { 44 | a.target.activate(d, a) 45 | } 46 | 47 | func (a *msButton) asButton() (just bool, value bool) { 48 | v := (mouseButton(internal.MouseButtons) & a.button) != 0 49 | j := (v != a.pressed) 50 | a.pressed = v 51 | return j, a.pressed 52 | } 53 | 54 | func (a *msButton) asHalfAxis() (just bool, value float32) { 55 | v := (mouseButton(internal.MouseButtons) & a.button) != 0 56 | j := (v != a.pressed) 57 | a.pressed = v 58 | if v { 59 | return j, 1 60 | } 61 | return j, 0 62 | } 63 | 64 | func (a *msButton) asAxis() (just bool, value float32) { 65 | v := (mouseButton(internal.MouseButtons) & a.button) != 0 66 | j := (v != a.pressed) 67 | a.pressed = v 68 | if v { 69 | return j, 1 70 | } 71 | return j, 0 72 | } 73 | 74 | func (a *msButton) asDualAxis() (just bool, value coord.XY) { 75 | v := (mouseButton(internal.MouseButtons) & a.button) != 0 76 | j := (v != a.pressed) 77 | a.pressed = v 78 | if v { 79 | return j, coord.XY{1, 0} 80 | } 81 | return j, coord.XY{0, 0} 82 | } 83 | 84 | func (a *msButton) asDelta() (just bool, value coord.XY) { 85 | v := (mouseButton(internal.MouseButtons) & a.button) != 0 86 | j := (v != a.pressed) 87 | a.pressed = v 88 | if v { 89 | return j, coord.XY{1, 0} 90 | } 91 | return j, coord.XY{0, 0} 92 | } 93 | -------------------------------------------------------------------------------- /input/mscoord.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package input 5 | 6 | import ( 7 | "github.com/cozely/cozely/coord" 8 | "github.com/cozely/cozely/internal" 9 | ) 10 | 11 | //////////////////////////////////////////////////////////////////////////////// 12 | 13 | type msCoord struct { 14 | target Action 15 | } 16 | 17 | //////////////////////////////////////////////////////////////////////////////// 18 | 19 | func (a *msCoord) bind(c ContextID, target Action) { 20 | aa := *a 21 | aa.target = target 22 | devices.bindings[KeyboardAndMouse][c] = 23 | append(devices.bindings[KeyboardAndMouse][c], &aa) 24 | } 25 | 26 | func (a *msCoord) activate(d DeviceID) { 27 | a.target.activate(d, a) 28 | } 29 | 30 | func (a *msCoord) asButton() (just bool, value bool) { 31 | return false, false 32 | } 33 | 34 | func (a *msCoord) asHalfAxis() (just bool, value float32) { 35 | return false, 0 36 | } 37 | 38 | func (a *msCoord) asAxis() (just bool, value float32) { 39 | return false, 0 40 | } 41 | 42 | func (a *msCoord) asDualAxis() (just bool, value coord.XY) { 43 | //TODO: implement threshold 44 | j := mouse.delta.C != 0 || mouse.delta.R != 0 45 | c := coord.XY{ 46 | X: float32(internal.MousePositionX) / float32(internal.Window.Width), 47 | Y: float32(internal.MousePositionY) / float32(internal.Window.Height), 48 | } 49 | return j, c 50 | } 51 | 52 | func (a *msCoord) asDelta() (just bool, value coord.XY) { 53 | //TODO: implement threshold 54 | j := mouse.delta.C != 0 || mouse.delta.R != 0 55 | return j, mouse.delta.XY() 56 | } 57 | -------------------------------------------------------------------------------- /input/newframe.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package input 5 | 6 | import ( 7 | "github.com/cozely/cozely/internal" 8 | ) 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | func init() { 13 | internal.InputNewFrame = newframe 14 | } 15 | 16 | func newframe() error { 17 | updateMouse() 18 | 19 | for _, t := range actions.list { 20 | for d := range devices.name { 21 | t.newframe(DeviceID(d)) 22 | } 23 | } 24 | 25 | for d := range devices.name { 26 | c := devices.context[d] 27 | // Activate this device context if necessary 28 | if c != devices.newcontext[d] { 29 | if c != noContext { 30 | for _, t := range contexts.actions[c] { 31 | t.deactivate(DeviceID(d)) 32 | } 33 | } 34 | devices.context[d] = devices.newcontext[d] 35 | c = devices.newcontext[d] 36 | for _, b := range devices.bindings[d][c] { 37 | b.activate(DeviceID(d)) 38 | } 39 | } 40 | 41 | for _, a := range contexts.actions[c] { 42 | a.update(DeviceID(d)) 43 | } 44 | } 45 | 46 | return nil 47 | } 48 | 49 | //////////////////////////////////////////////////////////////////////////////// 50 | -------------------------------------------------------------------------------- /input/source.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package input 5 | 6 | import ( 7 | "github.com/cozely/cozely/coord" 8 | ) 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | type source interface { 13 | bind(c ContextID, a Action) 14 | activate(d DeviceID) 15 | asButton() (just bool, value bool) 16 | asHalfAxis() (just bool, value float32) 17 | asAxis() (just bool, value float32) 18 | asDualAxis() (just bool, value coord.XY) 19 | asDelta() (just bool, value coord.XY) 20 | } 21 | -------------------------------------------------------------------------------- /input/testdata/init.json: -------------------------------------------------------------------------------- 1 | { 2 | "Debug": true, 3 | "VSync": true 4 | } 5 | -------------------------------------------------------------------------------- /input/testdata/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "Menu": { 3 | "Quit": ["Escape", "Button Back"], 4 | "Close Menu": ["Enter", "Button Start"], 5 | "Instant Close Menu": ["Mouse Right", "Button B"], 6 | "Inventory": ["I", "Button Y", "Mouse Scroll Up"], 7 | "Options": ["O", "Mouse Left"], 8 | "Trigger": ["Left Trigger", "Right Trigger", "T", "Button X"], 9 | "Position": ["Left Stick", "Right Stick"], 10 | "Cursor": ["Mouse", "Left Stick", "Right Stick"], 11 | "Delta": ["Left Stick", "Right Stick"] 12 | }, 13 | "Game": { 14 | "Quit": ["Escape", "Button Back"], 15 | "Open Menu": ["Enter", "Button Start"], 16 | "Instant Open Menu": ["Mouse Right", "Button B"], 17 | "Inventory": ["Tab", "Button Y"], 18 | "Jump": ["Space", "Mouse Left", "Button A"], 19 | "Trigger": ["Right Trigger"], 20 | "Position": ["Right Stick", "Mouse"], 21 | "Cursor": ["Right Stick", "Mouse"], 22 | "Delta": ["Right Stick", "Mouse"] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /input/z_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package input_test 5 | 6 | import ( 7 | "os" 8 | "testing" 9 | ) 10 | 11 | //////////////////////////////////////////////////////////////////////////////// 12 | 13 | var tests = make(chan func()) 14 | 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | func do(f func()) { 18 | done := make(chan bool, 1) 19 | tests <- func() { 20 | f() 21 | done <- true 22 | } 23 | <-done 24 | } 25 | 26 | //////////////////////////////////////////////////////////////////////////////// 27 | 28 | func TestMain(m *testing.M) { 29 | result := make(chan int, 1) 30 | 31 | go func() { 32 | result <- m.Run() 33 | }() 34 | 35 | go func() { 36 | os.Exit(<-result) 37 | }() 38 | 39 | for f := range tests { 40 | f() 41 | } 42 | } 43 | 44 | //////////////////////////////////////////////////////////////////////////////// 45 | -------------------------------------------------------------------------------- /internal/dialog.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package internal 5 | 6 | import ( 7 | "errors" 8 | "fmt" 9 | "unsafe" 10 | ) 11 | 12 | //////////////////////////////////////////////////////////////////////////////// 13 | 14 | /* 15 | #include 16 | #include "sdl.h" 17 | */ 18 | import "C" 19 | 20 | //////////////////////////////////////////////////////////////////////////////// 21 | 22 | // ErrorDialog displays a dialog box. 23 | func ErrorDialog(format string, v ...interface{}) error { 24 | msg := fmt.Sprintf(format, v...) 25 | 26 | t := Title + " - Error" 27 | ct := C.CString(t) 28 | defer C.free(unsafe.Pointer(ct)) 29 | 30 | cmsg := C.CString(msg) 31 | defer C.free(unsafe.Pointer(cmsg)) 32 | 33 | errcode := C.SDL_ShowSimpleMessageBox( 34 | C.SDL_MESSAGEBOX_ERROR, 35 | ct, 36 | cmsg, 37 | Window.window, 38 | ) 39 | if errcode != 0 { 40 | return GetSDLError() 41 | } 42 | return nil 43 | } 44 | 45 | //////////////////////////////////////////////////////////////////////////////// 46 | 47 | // GetSDLError returns nil or the current SDL Error wrapped in a Go error. 48 | func GetSDLError() error { 49 | if s := C.SDL_GetError(); s != nil { 50 | return errors.New(C.GoString(s)) 51 | } 52 | return nil 53 | } 54 | 55 | //////////////////////////////////////////////////////////////////////////////// 56 | -------------------------------------------------------------------------------- /internal/error.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package internal 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | type logger interface { 9 | Print(v ...interface{}) 10 | Println(v ...interface{}) 11 | Printf(format string, v ...interface{}) 12 | } 13 | 14 | type nolog struct{} 15 | 16 | func (nolog) Print(v ...interface{}) {} 17 | func (nolog) Println(v ...interface{}) {} 18 | func (nolog) Printf(format string, v ...interface{}) {} 19 | 20 | //////////////////////////////////////////////////////////////////////////////// 21 | 22 | // Wrap returns nil if err is nil, or a wrapped error otherwise. 23 | func Wrap(context string, err error) error { 24 | if err == nil { 25 | return nil 26 | } 27 | return WrappedError{context, err} 28 | } 29 | 30 | type WrappedError struct { 31 | Context string 32 | Err error 33 | } 34 | 35 | func (e WrappedError) Error() string { 36 | return e.Context + ": " + e.Err.Error() 37 | } 38 | 39 | //////////////////////////////////////////////////////////////////////////////// 40 | -------------------------------------------------------------------------------- /internal/hooks.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package internal 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // GLSetup hook 9 | var GLSetup = func() error { return nil } 10 | 11 | // GLPrerender hook 12 | var GLPrerender = func() error { return nil } 13 | 14 | // GLCleanup hook 15 | var GLCleanup = func() error { return nil } 16 | 17 | // GLErr hook 18 | var GLErr = func() error { return nil } 19 | 20 | //////////////////////////////////////////////////////////////////////////////// 21 | 22 | // InputSetup hook 23 | var InputSetup = func() error { return nil } 24 | 25 | // InputNewFrame hook 26 | var InputNewFrame = func() error { return nil } 27 | 28 | // InputCleanup hook 29 | var InputCleanup = func() error { return nil } 30 | 31 | // InputErr hook 32 | var InputErr = func() error { return nil } 33 | 34 | //////////////////////////////////////////////////////////////////////////////// 35 | 36 | // PixelSetup hook 37 | var PixelSetup = func() error { return nil } 38 | 39 | // PixelResize hook 40 | var PixelResize = func() {} 41 | 42 | // PixelRender hook 43 | var PixelRender = func() error { return nil } 44 | 45 | // PixelCleanup hook 46 | var PixelCleanup = func() error { return nil } 47 | 48 | // PixelErr hook 49 | var PixelErr = func() error { return nil } 50 | 51 | //////////////////////////////////////////////////////////////////////////////// 52 | 53 | // PolySetup hook 54 | var PolySetup = func() error { return nil } 55 | 56 | // PolyCleanup hook 57 | var PolyCleanup = func() error { return nil } 58 | 59 | // PolyErr hook 60 | var PolyErr = func() error { return nil } 61 | -------------------------------------------------------------------------------- /internal/keyboard.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package internal 5 | 6 | import ( 7 | "unsafe" 8 | ) 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | /* 13 | #include "sdl.h" 14 | */ 15 | import "C" 16 | 17 | //////////////////////////////////////////////////////////////////////////////// 18 | 19 | // A KeyLabel designate a key by its label in the current layout of the keyboard. 20 | // For printable characters, the value is the rune that would be generated by 21 | // pressing the key without any modifiers. 22 | type KeyLabel rune 23 | 24 | // A KeyCode designate a key by its physical position on the keyboard. 25 | // It is not affected by the layout or any other language settings. 26 | type KeyCode uint32 27 | 28 | //////////////////////////////////////////////////////////////////////////////// 29 | 30 | var keys *C.Uint8 31 | 32 | func Key(k KeyCode) bool { 33 | s := *(*uint8)(unsafe.Pointer(uintptr(unsafe.Pointer(keys)) + uintptr(k))) 34 | return s != 0 35 | } 36 | 37 | func getKeyboardArray() { 38 | keys = C.SDL_GetKeyboardState(nil) 39 | } 40 | 41 | //////////////////////////////////////////////////////////////////////////////// 42 | 43 | // KeyLabelOf returns the key label at the specified position in the current 44 | // layout. 45 | func KeyLabelOf(pos KeyCode) KeyLabel { 46 | return KeyLabel(C.SDL_GetKeyFromScancode(C.SDL_Scancode(pos))) 47 | } 48 | 49 | // KeySearchPositionOf searches the current position of label in the current 50 | // layout. 51 | func KeySearchPositionOf(l KeyLabel) KeyCode { 52 | return KeyCode(C.SDL_GetScancodeFromKey(C.SDL_Keycode(l))) 53 | } 54 | 55 | //////////////////////////////////////////////////////////////////////////////// 56 | -------------------------------------------------------------------------------- /internal/mouse.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package internal 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | /* 9 | #include "sdl.h" 10 | */ 11 | import "C" 12 | 13 | //////////////////////////////////////////////////////////////////////////////// 14 | 15 | // MouseDelta holds the delta from last mouse position. 16 | var MouseDeltaX, MouseDeltaY int16 17 | 18 | // MousePosition holds the current mouse position. 19 | var MousePositionX, MousePositionY int16 20 | 21 | // MouseButtons holds the state of the mouse buttons. 22 | var MouseButtons uint32 23 | 24 | var MouseWheelX, MouseWheelY int16 25 | 26 | //////////////////////////////////////////////////////////////////////////////// 27 | 28 | // MouseSetRelative enables or disables the relative mode, where the mouse is 29 | // hidden and mouse motions are continuously reported. 30 | func MouseSetRelative(enabled bool) error { 31 | var m C.SDL_bool 32 | if enabled { 33 | m = 1 34 | } 35 | if C.SDL_SetRelativeMouseMode(m) != 0 { 36 | return Wrap("setting relative mouse mode", GetSDLError()) 37 | } 38 | return nil 39 | } 40 | 41 | // MouseRelative returns true if the relative mode is enabled. 42 | func MouseRelative() bool { 43 | return C.SDL_GetRelativeMouseMode() == C.SDL_TRUE 44 | } 45 | 46 | // MouseShow shows or hides the (system) mouse cursor 47 | func MouseShow(show bool) { 48 | if show { 49 | C.SDL_ShowCursor(C.SDL_ENABLE) 50 | } else { 51 | C.SDL_ShowCursor(C.SDL_DISABLE) 52 | } 53 | } 54 | 55 | // MouseWarp moves the (system) mouse cursor in the window 56 | func MouseWarp(x, y int16) { 57 | C.SDL_WarpMouseInWindow(Window.window, C.int(x), C.int(y)) 58 | } 59 | 60 | //////////////////////////////////////////////////////////////////////////////// 61 | -------------------------------------------------------------------------------- /internal/sdl.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | #if defined(__WIN32) 5 | #include 6 | #else 7 | #include 8 | #endif 9 | -------------------------------------------------------------------------------- /internal/time.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package internal 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | /* 9 | #include "sdl.h" 10 | */ 11 | import "C" 12 | 13 | //////////////////////////////////////////////////////////////////////////////// 14 | 15 | // GetSeconds returns the number of seconds elapsed since program start. 16 | // 17 | // Note: This functions use the performance counter. 18 | func GetSeconds() float64 { 19 | return float64(C.SDL_GetPerformanceCounter()) * perfUnit 20 | } 21 | 22 | func init() { 23 | perfUnit = 1.0 / float64(C.SDL_GetPerformanceFrequency()) 24 | // ms := C.SDL_GetTicks() 25 | // s := C.SDL_GetPerformanceCounter() 26 | // perfOffset = float64(ms)/1000.0 - float64(s)*perfUnit 27 | } 28 | 29 | var perfUnit float64 30 | 31 | //////////////////////////////////////////////////////////////////////////////// 32 | -------------------------------------------------------------------------------- /noise/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | /* 5 | Package noise provides several noise algorithms. 6 | 7 | The Perlin and Simplex noise functions are adapted from "Simplex Noise 8 | Demystified": http://www.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf by 9 | Stefan Gustavson (code in the public domain). 10 | */ 11 | package noise 12 | -------------------------------------------------------------------------------- /options.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package cozely 5 | 6 | import ( 7 | "errors" 8 | 9 | "github.com/cozely/cozely/internal" 10 | ) 11 | 12 | //////////////////////////////////////////////////////////////////////////////// 13 | 14 | // An Option represents a configuration option used to change some parameters of 15 | // the framework: see Configure. 16 | type Option = func() error 17 | 18 | //////////////////////////////////////////////////////////////////////////////// 19 | 20 | // Configure the framework. 21 | func Configure(c ...Option) error { 22 | for _, f := range c { 23 | err := f() 24 | if err != nil { 25 | return nil 26 | } 27 | } 28 | return nil 29 | } 30 | 31 | //////////////////////////////////////////////////////////////////////////////// 32 | 33 | // Title sets the title of the game. 34 | func Title(t string) Option { 35 | return func() error { 36 | internal.Title = t 37 | if internal.Running { 38 | internal.SetWindowTitle(internal.Title) 39 | } 40 | return nil 41 | } 42 | } 43 | 44 | //////////////////////////////////////////////////////////////////////////////// 45 | 46 | // UpdateStep sets the time step between calls to Update. 47 | func UpdateStep(t float64) Option { 48 | return func() error { 49 | internal.UpdateStep = t 50 | return nil 51 | } 52 | } 53 | 54 | //////////////////////////////////////////////////////////////////////////////// 55 | 56 | // Multisample activate multisampling for the game window. Note that this is 57 | // currently incompatible with the pixel package. 58 | func Multisample(s int32) Option { 59 | return func() error { 60 | if internal.Running { 61 | return errors.New("cannot change window multisampling while running") 62 | } 63 | internal.Window.Multisample = s 64 | return nil 65 | } 66 | } 67 | 68 | //////////////////////////////////////////////////////////////////////////////// 69 | -------------------------------------------------------------------------------- /pixel/color.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package pixel 5 | 6 | import ( 7 | "github.com/cozely/cozely/color" 8 | ) 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | // Transparent is the only reserved color index. All palettes start with it. 13 | const Transparent = color.Index(0) 14 | 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | // LRGBAof returns the color corresponding to a color index in the current 18 | // palette. 19 | func LRGBAof(c color.Index) color.LRGBA { 20 | return palette.colors[c] 21 | } 22 | -------------------------------------------------------------------------------- /pixel/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | /* 5 | Package pixel provides a drawing canvas specialized for pixel art. 6 | 7 | It implements a GPU pipeline that works entirely in indexed colors at a chosen, 8 | fixed, resolution (usually lower than the screen resolution). 9 | 10 | It does not provide any anti-aliasing, nor alpha-transparency, since the goal is 11 | to offer an easy way to work within the stricter definition of pixel art (i.e. 12 | limited color palette, no mixed-resolution or "mixels", and so on). 13 | */ 14 | package pixel 15 | -------------------------------------------------------------------------------- /pixel/error.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package pixel 5 | 6 | import ( 7 | "github.com/cozely/cozely/internal" 8 | ) 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | var stickyErr error 13 | 14 | func init() { 15 | internal.PixelErr = func() error { 16 | return stickyErr 17 | } 18 | } 19 | 20 | //////////////////////////////////////////////////////////////////////////////// 21 | 22 | // Err returns the first unchecked error of the package since last call to the 23 | // function. The error is then considered checked, and further calls to Err will 24 | // return nil until the next error occurs. 25 | // 26 | // Note: errors occuring while there already is an unchecked error will not be 27 | // recorded. However, if the debug mode is active, all errors will be logged. 28 | func Err() error { 29 | err := stickyErr 30 | stickyErr = nil 31 | return err 32 | } 33 | 34 | func setErr(err error) { 35 | if stickyErr == nil { 36 | stickyErr = err 37 | } 38 | internal.Debug.Printf("*** ERROR in package pixel ***\n%s", err) 39 | } 40 | 41 | -------------------------------------------------------------------------------- /pixel/glblitfrag.go: -------------------------------------------------------------------------------- 1 | package pixel 2 | 3 | const blitFragmentShader = "\n" + `#version 460 core 4 | 5 | //////////////////////////////////////////////////////////////////////////////// 6 | 7 | layout(origin_upper_left, pixel_center_integer) in vec4 gl_FragCoord; 8 | 9 | layout(binding = 0) uniform usampler2D screenTexture; 10 | layout(binding = 1) uniform usampler2D filterTexture; 11 | 12 | layout(std430, binding = 0) buffer Palette { 13 | vec4 Colours[256]; 14 | }; 15 | 16 | //////////////////////////////////////////////////////////////////////////////// 17 | 18 | out vec4 out_color; 19 | 20 | in PerVertex { 21 | layout(location=0) vec2 ScreenPosition; 22 | }; 23 | 24 | //////////////////////////////////////////////////////////////////////////////// 25 | 26 | void main(void) { 27 | // float cc = texelFetch(screenTexture, ivec2(int(ScreenPosition.x), int(ScreenPosition.y)), 0).r; 28 | // uint c = uint(cc*255); 29 | uint c = texelFetch(screenTexture, ivec2(int(ScreenPosition.x), int(ScreenPosition.y)), 0).r; 30 | 31 | // float ff = texelFetch(filterTexture, ivec2(int(ScreenPosition.x), int(ScreenPosition.y)), 0).r; 32 | // uint f = uint(ff*255); 33 | uint f = texelFetch(filterTexture, ivec2(int(ScreenPosition.x), int(ScreenPosition.y)), 0).r; 34 | 35 | if (c == 0) { 36 | discard; 37 | } 38 | 39 | out_color = Colours[c]; 40 | // if (f != 0) { 41 | // out_color = Colours[c-1]; 42 | // } 43 | } 44 | 45 | //////////////////////////////////////////////////////////////////////////////// 46 | 47 | ` 48 | -------------------------------------------------------------------------------- /pixel/glblitvert.go: -------------------------------------------------------------------------------- 1 | package pixel 2 | 3 | const blitVertexShader = "\n" + `#version 460 core 4 | 5 | //////////////////////////////////////////////////////////////////////////////// 6 | 7 | layout(std140, binding = 0) uniform BlitUniforms { 8 | vec2 ScreenSize; 9 | }; 10 | 11 | const vec2 srcCorners[4] = vec2[4]( 12 | vec2(0, 0), 13 | vec2(1, 0), 14 | vec2(0, 1), 15 | vec2(1, 1) 16 | ); 17 | 18 | const vec2 winCorners[4] = vec2[4]( 19 | vec2(-1, -1), 20 | vec2(1, -1), 21 | vec2(-1, 1), 22 | vec2(1, 1) 23 | ); 24 | 25 | //////////////////////////////////////////////////////////////////////////////// 26 | 27 | out gl_PerVertex { 28 | vec4 gl_Position; 29 | }; 30 | 31 | out PerVertex { 32 | layout(location=0) vec2 ScreenPosition; 33 | }; 34 | 35 | //////////////////////////////////////////////////////////////////////////////// 36 | 37 | void main(void) { 38 | ScreenPosition = srcCorners[gl_VertexID] * ScreenSize; 39 | gl_Position = vec4(winCorners[gl_VertexID], 0.5, 1.0); 40 | } 41 | ` 42 | 43 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 44 | // Licensed under a simplified BSD license (see LICENSE file). 45 | //////////////////////////////////////////////////////////////////////////////// 46 | -------------------------------------------------------------------------------- /pixel/load.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package pixel 5 | 6 | import ( 7 | "errors" 8 | 9 | "github.com/cozely/cozely/internal" 10 | ) 11 | 12 | //////////////////////////////////////////////////////////////////////////////// 13 | 14 | func loadAssets() error { 15 | if internal.Running { 16 | return errors.New("loading graphics while running not implemented") 17 | } 18 | 19 | prects := []uint32{} //TODO: move with pictures.image 20 | 21 | // Load all fonts 22 | 23 | c := len(pictures.path) 24 | 25 | for i := range fonts { 26 | err := FontID(i).load(&prects) 27 | if err != nil { 28 | //TODO: sticky error instead? 29 | return err 30 | } 31 | } 32 | 33 | internal.Debug.Printf( 34 | "Loaded %d fonts (%d glyphs)\n", 35 | len(fonts), 36 | len(pictures.path)-c, 37 | ) 38 | 39 | // Load all pictures 40 | 41 | for i := range pictures.path { 42 | err := PictureID(i).load(&prects) 43 | if err != nil { 44 | //TODO: sticky error instead? 45 | return err 46 | } 47 | } 48 | 49 | // Pack them into a texture atlas 50 | 51 | pictures.atlas.Pack(prects, pictSize, pictPut) 52 | 53 | internal.Debug.Printf( 54 | "Packed %d pictures in %d bins (%.1fkB unused)\n", 55 | len(prects), 56 | pictures.atlas.BinCount(), 57 | float64(pictures.atlas.Unused())/1024.0, 58 | ) 59 | 60 | return nil 61 | } 62 | -------------------------------------------------------------------------------- /pixel/mousecursor.go: -------------------------------------------------------------------------------- 1 | package pixel 2 | 3 | import "image" 4 | 5 | var mousecursor = image.Paletted{ 6 | Rect: image.Rectangle{Max: image.Point{16, 16}}, 7 | Stride: 16, 8 | Pix: []uint8{ 9 | 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 10 | 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11 | 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 7, 0, 0, 0, 12 | 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13 | 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 7, 7, 7, 7, 14 | 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 15 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 0, 0, 0, 0, 16 | 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17 | 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 18 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /pixel/testdata/fonts/chaotela12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/pixel/testdata/fonts/chaotela12.png -------------------------------------------------------------------------------- /pixel/testdata/fonts/cozela10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/pixel/testdata/fonts/cozela10.png -------------------------------------------------------------------------------- /pixel/testdata/fonts/cozela12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/pixel/testdata/fonts/cozela12.png -------------------------------------------------------------------------------- /pixel/testdata/fonts/monozela10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/pixel/testdata/fonts/monozela10.png -------------------------------------------------------------------------------- /pixel/testdata/fonts/simpela10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/pixel/testdata/fonts/simpela10.png -------------------------------------------------------------------------------- /pixel/testdata/fonts/simpela12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/pixel/testdata/fonts/simpela12.png -------------------------------------------------------------------------------- /pixel/testdata/fonts/tinela9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/pixel/testdata/fonts/tinela9.png -------------------------------------------------------------------------------- /pixel/testdata/graphics/mire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/pixel/testdata/graphics/mire.png -------------------------------------------------------------------------------- /pixel/testdata/graphics/paletteswatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/pixel/testdata/graphics/paletteswatch.png -------------------------------------------------------------------------------- /pixel/testdata/graphics/shape1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/pixel/testdata/graphics/shape1.png -------------------------------------------------------------------------------- /pixel/testdata/graphics/shape2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/pixel/testdata/graphics/shape2.png -------------------------------------------------------------------------------- /pixel/testdata/graphics/shape3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/pixel/testdata/graphics/shape3.png -------------------------------------------------------------------------------- /pixel/testdata/graphics/shape4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/pixel/testdata/graphics/shape4.png -------------------------------------------------------------------------------- /pixel/testdata/graphics/srgb-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/pixel/testdata/graphics/srgb-blue.png -------------------------------------------------------------------------------- /pixel/testdata/graphics/srgb-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/pixel/testdata/graphics/srgb-gray.png -------------------------------------------------------------------------------- /pixel/testdata/graphics/srgb-green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/pixel/testdata/graphics/srgb-green.png -------------------------------------------------------------------------------- /pixel/testdata/graphics/srgb-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/pixel/testdata/graphics/srgb-red.png -------------------------------------------------------------------------------- /pixel/testdata/init.json: -------------------------------------------------------------------------------- 1 | { 2 | "Debug": true, 3 | "VSync": false 4 | } 5 | -------------------------------------------------------------------------------- /pixel/testdata/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "Default": { 3 | "Quit": ["Escape"], 4 | "Next": ["Mouse Left", "Space"], 5 | "Previous": ["Mouse Right", "U"], 6 | "Scene1": ["1"], 7 | "Scene2": ["2"], 8 | "Scene3": ["3"], 9 | "Scene4": ["4"], 10 | "Scene5": ["5"], 11 | "Scene6": ["6"], 12 | "Scene7": ["7"], 13 | "Scene8": ["8"], 14 | "Scene9": ["9"], 15 | "Scene0": ["0"], 16 | "ScrollUp": ["Mouse Scroll Up"], 17 | "ScrollDown": ["Mouse Scroll Down"], 18 | "Cursor": ["Mouse"] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /pixel/z05_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package pixel_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/cozely/cozely" 10 | "github.com/cozely/cozely/input" 11 | "github.com/cozely/cozely/pixel" 12 | ) 13 | 14 | //////////////////////////////////////////////////////////////////////////////// 15 | 16 | type loop5 struct { 17 | points []pixel.XY 18 | } 19 | 20 | //////////////////////////////////////////////////////////////////////////////// 21 | 22 | func TestTest5(t *testing.T) { 23 | do(func() { 24 | defer cozely.Recover() 25 | 26 | l := loop5{} 27 | l.declare() 28 | 29 | err := cozely.Run(&l) 30 | if err != nil { 31 | t.Error(err) 32 | } 33 | }) 34 | } 35 | 36 | func (a *loop5) declare() { 37 | pixel.SetResolution(pixel.XY{128, 128}) 38 | 39 | a.points = []pixel.XY{ 40 | {4, 4}, 41 | {4 + 1, 4 + 20}, 42 | {4 + 1 + 20, 4 + 20 - 1}, 43 | {16, 32}, 44 | } 45 | } 46 | 47 | func (a *loop5) Enter() { 48 | input.ShowMouse(false) 49 | } 50 | 51 | func (loop5) Leave() { 52 | } 53 | 54 | //////////////////////////////////////////////////////////////////////////////// 55 | 56 | func (a *loop5) React() { 57 | if quit.Pressed() { 58 | cozely.Stop(nil) 59 | } 60 | 61 | if next.Pressed() { 62 | m := pixel.XYof(cursor.XY()) 63 | a.points = append(a.points, m) 64 | } 65 | 66 | if previous.Pressed() { 67 | if len(a.points) > 0 { 68 | a.points = a.points[:len(a.points)-1] 69 | } 70 | } 71 | } 72 | 73 | func (loop5) Update() { 74 | } 75 | 76 | func (a *loop5) Render() { 77 | pixel.Clear(1) 78 | m := pixel.XYof(cursor.XY()) 79 | if !scenes[3].Ongoing() { 80 | pixel.Triangles(2, 0, a.points...) 81 | } 82 | if !scenes[2].Ongoing() { 83 | pixel.Lines(14, 0, a.points...) 84 | pixel.Lines(13, 0, a.points[len(a.points)-1], m) 85 | } 86 | if !scenes[1].Ongoing() { 87 | for _, p := range a.points { 88 | pixel.Point(8, 0, p) 89 | } 90 | pixel.Point(7, 0, m) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /pixel/z06_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package pixel_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/cozely/cozely" 10 | "github.com/cozely/cozely/input" 11 | "github.com/cozely/cozely/pixel" 12 | ) 13 | 14 | //////////////////////////////////////////////////////////////////////////////// 15 | 16 | type loop6 struct { 17 | } 18 | 19 | //////////////////////////////////////////////////////////////////////////////// 20 | 21 | func TestTest6(t *testing.T) { 22 | do(func() { 23 | defer cozely.Recover() 24 | 25 | l := loop6{} 26 | l.declare() 27 | 28 | err := cozely.Run(&l) 29 | if err != nil { 30 | t.Error(err) 31 | } 32 | }) 33 | } 34 | 35 | func (a *loop6) declare() { 36 | pixel.SetZoom(3) 37 | } 38 | 39 | func (a *loop6) Enter() { 40 | input.ShowMouse(false) 41 | } 42 | 43 | func (loop6) Leave() { 44 | } 45 | 46 | //////////////////////////////////////////////////////////////////////////////// 47 | 48 | func (loop6) React() { 49 | if quit.Pressed() { 50 | cozely.Stop(nil) 51 | } 52 | } 53 | 54 | func (loop6) Update() { 55 | } 56 | 57 | func (a *loop6) Render() { 58 | pixel.Clear(0) 59 | 60 | const corner = 3 61 | 62 | o := pixel.XY{8, 8} 63 | s := pixel.XY{24, 24} 64 | dx := pixel.XY{32, 0} 65 | dy := pixel.XY{0, 32} 66 | 67 | for i := int16(0); i < 13; i++ { 68 | pixel.Box(9, 0, 0, i, o.Plus(dx.Times(i)), o.Plus(dx.Times(i)).Plus(s)) 69 | } 70 | 71 | o = o.Plus(dy) 72 | for i := int16(0); i < 13; i++ { 73 | pixel.Box(0, 8, 0, i, o.Plus(dx.Times(i)), o.Plus(dx.Times(i)).Plus(s)) 74 | } 75 | 76 | o = o.Plus(dy) 77 | for i := int16(0); i < 13; i++ { 78 | pixel.Box(9, 8, 0, i, o.Plus(dx.Times(i)), o.Plus(dx.Times(i)).Plus(s)) 79 | } 80 | 81 | o = o.Plus(dy) 82 | for i := int16(0); i < 13; i++ { 83 | pixel.Box(9, 9, 0, i, o.Plus(dx.Times(i)), o.Plus(dx.Times(i)).Plus(s)) 84 | } 85 | 86 | m := pixel.XYof(cursor.XY()) 87 | pixel.Point(7, 0, m) 88 | } 89 | -------------------------------------------------------------------------------- /pixel/z_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package pixel_test 5 | 6 | import ( 7 | "os" 8 | "testing" 9 | 10 | "github.com/cozely/cozely/input" 11 | ) 12 | 13 | //////////////////////////////////////////////////////////////////////////////// 14 | 15 | var ( 16 | quit = input.Button("Quit") 17 | next = input.Button("Next") 18 | previous = input.Button("Previous") 19 | scenes = []input.ButtonID{ 20 | input.Button("Scene0"), 21 | input.Button("Scene1"), 22 | input.Button("Scene2"), 23 | input.Button("Scene3"), 24 | input.Button("Scene4"), 25 | input.Button("Scene5"), 26 | input.Button("Scene6"), 27 | input.Button("Scene7"), 28 | input.Button("Scene8"), 29 | input.Button("Scene9"), 30 | } 31 | scrollup = input.Button("ScrollUp") 32 | scrolldown = input.Button("ScrollDown") 33 | cursor = input.Cursor("Cursor") 34 | ) 35 | 36 | //////////////////////////////////////////////////////////////////////////////// 37 | 38 | var tests = make(chan func()) 39 | 40 | //////////////////////////////////////////////////////////////////////////////// 41 | 42 | func do(f func()) { 43 | done := make(chan bool, 1) 44 | tests <- func() { 45 | f() 46 | done <- true 47 | } 48 | <-done 49 | } 50 | 51 | //////////////////////////////////////////////////////////////////////////////// 52 | 53 | func TestMain(m *testing.M) { 54 | result := make(chan int, 1) 55 | 56 | go func() { 57 | result <- m.Run() 58 | }() 59 | 60 | go func() { 61 | os.Exit(<-result) 62 | }() 63 | 64 | for f := range tests { 65 | f() 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /plane/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | /* 5 | Package plane implements mathematical objects and operations in the plane. 6 | */ 7 | package plane 8 | -------------------------------------------------------------------------------- /plane/quadedge/ORIGINAL_LICENSE: -------------------------------------------------------------------------------- 1 | The package in this directory was based on C code: 2 | http://www.ic.unicamp.br/~stolfi/EXPORT/software/c/2000-05-04/libquad/ 3 | 4 | Originally written by Jim Roth (DEC CADM Advanced Group) on May 1986. 5 | Modified by J. Stolfi on April 1993. 6 | 7 | Original Copyright notice: 8 | 9 | Copyright 1996 Institute of Computing, Unicamp. 10 | 11 | Permission to use this software for any purpose is hereby granted, 12 | provided that any substantial copy or mechanically derived version 13 | of this file that is made available to other parties is accompanied 14 | by this copyright notice in full, and is distributed under these same 15 | terms. 16 | 17 | DISCLAIMER: This software is provided "as is" with no explicit or 18 | implicit warranty of any kind. Neither the authors nor their 19 | employers can be held responsible for any losses or damages 20 | that might be attributed to its use. 21 | 22 | End of copyright notice. 23 | -------------------------------------------------------------------------------- /plane/quadedge/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package quadedge provides a way to describe and manipulate the topology of 3 | planar maps (i.e., the topology of any closed polygonal mesh). 4 | 5 | It implements the quad-edge data structure described in: 6 | 7 | "Primitives for the Manipulation of General Subdivisions and the Computation 8 | of Voronoi Diagrams" 9 | 10 | P. Guibas, J. Stolfi, ACM TOG, April 1985 11 | 12 | This structure represents at the same time the primal and dual of the map, as 13 | well as their mirror images. It allows to switch from one to another in constant 14 | time. 15 | 16 | For example, a list of quad-edges that describe the topology a Delaunay 17 | triangulation also describe the corresponding Voronoi diagram. 18 | 19 | The structure does not hold (nor use) any geometrical information, but contains 20 | 32bit IDs that can be used to identify vertices and faces. 21 | 22 | Note that in this package, each (undirected) edge of the map is represented by 23 | four directed Edge objects. 24 | */ 25 | package quadedge 26 | -------------------------------------------------------------------------------- /plane/quadedge/operators.go: -------------------------------------------------------------------------------- 1 | // Adapted by Laurent Moussault (2018) from C code: see ORIGINAL_LICENSE 2 | 3 | package quadedge 4 | 5 | //////////////////////////////////////////////////////////////////////////////// 6 | 7 | // Splice implements the second operator from Guibas and Stolfi Quad-Edge 8 | // article. 9 | // 10 | // It affects the two origin edge rings of a and b, as well as their two left 11 | // edge rings: if the two rings are distinct, they are combined into one; if the 12 | // two rings are the same, they are broken in separate pieces. 13 | // 14 | // In the origin edge rings, the change happens immediately after a.Orig() and 15 | // b.Orig() (in counterclockwise order); and in the left edge rings, the change 16 | // happens immediately before a.Left() and b.Left(). 17 | func Splice(a, b Edge) { 18 | p := a.pool 19 | alpha := p.next[a.id].rot() 20 | beta := p.next[b.id].rot() 21 | 22 | p.next[a.id], p.next[b.id] = p.next[b.id], p.next[a.id] 23 | 24 | p.next[alpha], p.next[beta] = p.next[beta], p.next[alpha] 25 | } 26 | 27 | //////////////////////////////////////////////////////////////////////////////// 28 | 29 | // Connect creates and returns a new edge connecting the destination of a to the 30 | // origin of b, in such a way that a, e and b share the same left face. For 31 | // convenience, it also sets the origin and destination vertex ID of the new 32 | // edge (but not the right and left face ID). 33 | func Connect(a, b Edge) Edge { 34 | e := New(a.pool) 35 | e.SetOrig(a.Dest()) 36 | e.SetDest(b.Orig()) 37 | Splice(e, a.LeftNext()) 38 | Splice(e.Sym(), b) 39 | return e 40 | } 41 | 42 | //////////////////////////////////////////////////////////////////////////////// 43 | 44 | // SwapTriangles disconnects e, which must have two triangles as left and right 45 | // faces, and reconnect it to the other two vertices of the quadrilateral formed 46 | // by the first step. For example, if v1, v2, v3 and v4 form a quadrilateral, 47 | // and e is a diagonal between v1, and v3, after the swap e will connect v2 to 48 | // v4. 49 | func SwapTriangles(e Edge) { 50 | a := e.OrigPrev() 51 | b := e.Sym().OrigPrev() 52 | Splice(e, a) 53 | Splice(e.Sym(), b) 54 | Splice(e, a.LeftNext()) 55 | Splice(e.Sym(), b.LeftNext()) 56 | e.SetOrig(a.Dest()) 57 | e.SetDest(b.Dest()) 58 | } 59 | 60 | //////////////////////////////////////////////////////////////////////////////// 61 | -------------------------------------------------------------------------------- /plane/quadedge/testdata/init.json: -------------------------------------------------------------------------------- 1 | { 2 | "Debug": true 3 | } 4 | -------------------------------------------------------------------------------- /plane/quadedge/testdata/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "Default": { 3 | "Next": ["Mouse Left", "Space"], 4 | "Previous": ["Mouse Right", "U"], 5 | "Scene1": ["1"], 6 | "Scene2": ["2"], 7 | "Scene3": ["3"], 8 | "Scene4": ["4"], 9 | "Scene5": ["5"], 10 | "Scene6": ["6"], 11 | "Scene7": ["7"], 12 | "Scene8": ["8"], 13 | "Scene9": ["9"], 14 | "Scene10": ["0"] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /plane/quadedge/z_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package quadedge_test 5 | 6 | import ( 7 | "os" 8 | "testing" 9 | 10 | "github.com/cozely/cozely/input" 11 | ) 12 | 13 | //////////////////////////////////////////////////////////////////////////////// 14 | 15 | var ( 16 | next = input.Button("Next") 17 | previous = input.Button("Previous") 18 | scene1 = input.Button("Scene1") 19 | scene2 = input.Button("Scene2") 20 | scene3 = input.Button("Scene3") 21 | scene4 = input.Button("Scene4") 22 | scene5 = input.Button("Scene5") 23 | scene6 = input.Button("Scene6") 24 | scene7 = input.Button("Scene7") 25 | scene8 = input.Button("Scene8") 26 | scene9 = input.Button("Scene9") 27 | scene10 = input.Button("Scene10") 28 | ) 29 | 30 | //////////////////////////////////////////////////////////////////////////////// 31 | 32 | var tests = make(chan func()) 33 | 34 | //////////////////////////////////////////////////////////////////////////////// 35 | 36 | func do(f func()) { 37 | done := make(chan bool, 1) 38 | tests <- func() { 39 | f() 40 | done <- true 41 | } 42 | <-done 43 | } 44 | 45 | //////////////////////////////////////////////////////////////////////////////// 46 | 47 | func TestMain(m *testing.M) { 48 | result := make(chan int, 1) 49 | 50 | go func() { 51 | result <- m.Run() 52 | }() 53 | 54 | go func() { 55 | os.Exit(<-result) 56 | }() 57 | 58 | for f := range tests { 59 | f() 60 | } 61 | } 62 | 63 | //////////////////////////////////////////////////////////////////////////////// 64 | -------------------------------------------------------------------------------- /plane/z_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package plane_test 5 | 6 | import ( 7 | "os" 8 | "testing" 9 | ) 10 | 11 | //////////////////////////////////////////////////////////////////////////////// 12 | 13 | var tests = make(chan func()) 14 | 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | func do(f func()) { 18 | done := make(chan bool, 1) 19 | tests <- func() { 20 | f() 21 | done <- true 22 | } 23 | <-done 24 | } 25 | 26 | //////////////////////////////////////////////////////////////////////////////// 27 | 28 | func TestMain(m *testing.M) { 29 | result := make(chan int, 1) 30 | 31 | go func() { 32 | result <- m.Run() 33 | }() 34 | 35 | go func() { 36 | os.Exit(<-result) 37 | }() 38 | 39 | for f := range tests { 40 | f() 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /space/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | /* 5 | Package space implements mathematical objets and operations in three-dimensional 6 | euclidean space. 7 | */ 8 | package space 9 | -------------------------------------------------------------------------------- /testdata/graphics/cozely.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/testdata/graphics/cozely.png -------------------------------------------------------------------------------- /testdata/init.json: -------------------------------------------------------------------------------- 1 | { 2 | "Debug": false 3 | } 4 | -------------------------------------------------------------------------------- /testdata/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "Default": { 3 | "Play": ["Space", "Mouse Left", "Button A"] 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /window/events.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package window 5 | 6 | import ( 7 | "github.com/cozely/cozely/internal" 8 | ) 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | // Events holds the callbacks for each window events. 13 | // 14 | // These callbacks can be modified at anytime, but should always contain valid 15 | // functions (i.e., non nil). The change will take effect at the next frame. 16 | var Events = struct { 17 | Resize func() 18 | Hide func() 19 | Show func() 20 | Focus func() 21 | Unfocus func() 22 | Quit func() 23 | }{ 24 | Resize: func() {}, 25 | Hide: func() {}, 26 | Show: func() {}, 27 | Focus: func() {}, 28 | Unfocus: func() {}, 29 | Quit: func() { internal.QuitRequested = true }, 30 | } 31 | -------------------------------------------------------------------------------- /window/window.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package window 5 | 6 | import ( 7 | "github.com/cozely/cozely/internal" 8 | ) 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | // HasFocus returns true if the game windows has focus. 13 | func HasFocus() bool { 14 | return internal.HasFocus 15 | } 16 | 17 | // HasMouseFocus returns true if the mouse is currently inside the game window. 18 | func HasMouseFocus() bool { 19 | return internal.HasMouseFocus 20 | } 21 | 22 | // Size returns the size of the window in (screen) pixels. 23 | func Size() XY { 24 | //TODO: move to package window 25 | return XY{internal.Window.Width, internal.Window.Height} 26 | } 27 | 28 | //////////////////////////////////////////////////////////////////////////////// 29 | -------------------------------------------------------------------------------- /x/atlas/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | /* 5 | Package atlas provides a packing algorithm for creating atlases of 2D textures. 6 | 7 | The current implementation is based on the algorithm presented by Jim Scott in: 8 | 9 | http://blackpawn.com/texts/lightmaps/default.html 10 | 11 | */ 12 | package atlas 13 | -------------------------------------------------------------------------------- /x/gl/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | /* 5 | Package gl provides simple abstractions over a modern subset of OpenGL. 6 | */ 7 | package gl 8 | -------------------------------------------------------------------------------- /x/gl/error.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package gl 5 | 6 | import ( 7 | "github.com/cozely/cozely/internal" 8 | ) 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | var stickyErr error 13 | 14 | func init() { 15 | internal.GLErr = func() error { 16 | return stickyErr 17 | } 18 | } 19 | 20 | //////////////////////////////////////////////////////////////////////////////// 21 | 22 | // Err returns the first unchecked error of the package since last call to the 23 | // function. The error is then considered checked, and further calls to Err will 24 | // return nil until the next error occurs. 25 | // 26 | // Note: errors occuring while there already is an unchecked error will not be 27 | // recorded. However, if the debug mode is active, all errors will be logged. 28 | func Err() error { 29 | err := stickyErr 30 | stickyErr = nil 31 | return err 32 | } 33 | 34 | func setErr(err error) { 35 | if stickyErr == nil { 36 | stickyErr = err 37 | } 38 | internal.Debug.Printf("*** ERROR in package gl***\n%s", err) 39 | } 40 | -------------------------------------------------------------------------------- /x/gl/glerror.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | #include "glad.h" 5 | #include "_cgo_export.h" 6 | 7 | void errorCallback( 8 | GLenum source, 9 | GLenum type, 10 | GLuint id, 11 | GLenum severity, 12 | GLsizei length, 13 | const GLchar *message, 14 | const void *userParam) 15 | { 16 | logGLError(source, type, id, severity, length, (char *)message); 17 | } 18 | -------------------------------------------------------------------------------- /x/gl/glerror.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package gl 5 | 6 | import ( 7 | "errors" 8 | 9 | "github.com/cozely/cozely/internal" 10 | ) 11 | 12 | //////////////////////////////////////////////////////////////////////////////// 13 | 14 | /* 15 | #include "glad.h" 16 | */ 17 | import "C" 18 | 19 | //////////////////////////////////////////////////////////////////////////////// 20 | 21 | //export logGLError 22 | func logGLError( 23 | source C.GLenum, 24 | typ C.GLenum, 25 | id C.GLuint, 26 | severity C.GLenum, 27 | length C.GLsizei, 28 | m *C.char, 29 | ) { 30 | highsev := false 31 | var sev string 32 | switch severity { 33 | case C.GL_DEBUG_SEVERITY_HIGH: 34 | highsev = true 35 | sev = "ERROR" 36 | case C.GL_DEBUG_SEVERITY_MEDIUM: 37 | sev = "WARNING" 38 | case C.GL_DEBUG_SEVERITY_LOW: 39 | sev = "warning" 40 | case C.GL_DEBUG_SEVERITY_NOTIFICATION: 41 | sev = "info" 42 | return 43 | } 44 | var sou string 45 | switch source { 46 | case C.GL_DEBUG_SOURCE_API: 47 | sou = "OpenGL" 48 | case C.GL_DEBUG_SOURCE_WINDOW_SYSTEM: 49 | sou = "Window-system API" 50 | case C.GL_DEBUG_SOURCE_SHADER_COMPILER: 51 | sou = "Shader compiler" 52 | case C.GL_DEBUG_SOURCE_THIRD_PARTY: 53 | sou = "Third party" 54 | case C.GL_DEBUG_SOURCE_APPLICATION: 55 | sou = "Application" 56 | case C.GL_DEBUG_SOURCE_OTHER: 57 | sou = "Other source" 58 | } 59 | var ty string 60 | switch typ { 61 | case C.GL_DEBUG_TYPE_ERROR: 62 | ty = "" // " (error)" 63 | case C.GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: 64 | ty = " (deprecated behavior)" 65 | case C.GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: 66 | ty = " (undefined behavior)" 67 | case C.GL_DEBUG_TYPE_PORTABILITY: 68 | ty = " (portability)" 69 | case C.GL_DEBUG_TYPE_PERFORMANCE: 70 | ty = " (performance)" 71 | case C.GL_DEBUG_TYPE_MARKER: 72 | ty = " (marker)" 73 | case C.GL_DEBUG_TYPE_PUSH_GROUP: 74 | ty = " (push group)" 75 | case C.GL_DEBUG_TYPE_POP_GROUP: 76 | ty = " (pop group)" 77 | case C.GL_DEBUG_TYPE_OTHER: 78 | ty = "" 79 | } 80 | 81 | if highsev { 82 | setErr(errors.New("*** " + sev + " in " + sou + ty + " ***\n" + C.GoString(m))) 83 | } 84 | internal.Debug.Printf("*** %s in %s%s ***\n%s", sev, sou, ty, C.GoString(m)) 85 | } 86 | 87 | //////////////////////////////////////////////////////////////////////////////// 88 | -------------------------------------------------------------------------------- /x/gl/misc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package gl 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | /* 9 | #include "glad.h" 10 | 11 | static void Viewport(GLint x, GLint y, GLsizei width, GLsizei height) { 12 | glViewport(x, y, width, height); 13 | } 14 | 15 | static void DepthRange(GLdouble n, GLdouble f) { 16 | glDepthRange(n, f); 17 | } 18 | */ 19 | import "C" 20 | 21 | //////////////////////////////////////////////////////////////////////////////// 22 | 23 | // Viewport sets the size in pixels of the GL viewport. 24 | // 25 | // Note that this function is automatically called each time the window is 26 | // resized. 27 | func Viewport(ox, oy, width, height int32) { 28 | C.Viewport(C.GLint(ox), C.GLint(oy), C.GLsizei(width), C.GLsizei(height)) 29 | } 30 | 31 | //////////////////////////////////////////////////////////////////////////////// 32 | 33 | // DepthRange specifies the mapping of depth values from normalized device 34 | // coordinates to window coordinates. 35 | func DepthRange(near, far float64) { 36 | C.DepthRange(C.GLdouble(near), C.GLdouble(far)) 37 | } 38 | 39 | //////////////////////////////////////////////////////////////////////////////// 40 | -------------------------------------------------------------------------------- /x/gl/options.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package gl 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // An Option represents a configuration option used to change some parameters of 9 | // the framework: see cozely.Configure. 10 | type Option = func() error 11 | 12 | var noclear = false 13 | 14 | //////////////////////////////////////////////////////////////////////////////// 15 | 16 | // NoClear prevents the default behavior of clearing the default framebuffer at 17 | // the start of each frame. 18 | func NoClear() Option { 19 | return func() error { 20 | noclear = true 21 | return nil 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /x/gl/renderbuffer.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package gl 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | /* 9 | #include "glad.h" 10 | 11 | static inline GLuint NewRenderbuffer(GLenum format, GLsizei width, GLsizei height) { 12 | GLuint r; 13 | glCreateRenderbuffers(1, &r); 14 | glNamedRenderbufferStorage(r, format, width, height); 15 | return r; 16 | } 17 | 18 | static inline void Texture2DSubImage( 19 | GLuint texture, 20 | GLint level, 21 | GLint xoffset, 22 | GLint yoffset, 23 | GLsizei width, 24 | GLsizei height, 25 | GLenum format, 26 | GLenum type, 27 | const void *pixels 28 | ) { 29 | glTextureSubImage2D(texture, level, xoffset, yoffset, width, height, format, type, pixels); 30 | } 31 | 32 | static inline void TextureGenerateMipmap(GLuint texture) { 33 | glGenerateTextureMipmap(texture); 34 | } 35 | 36 | static inline void BindTextureUnit(GLuint unit, GLuint texture) { 37 | glBindTextureUnit(unit, texture); 38 | } 39 | 40 | static inline void DeleteRenderbuffer(GLuint r) { 41 | glDeleteRenderbuffers(1, &r); 42 | } 43 | 44 | */ 45 | import "C" 46 | 47 | //////////////////////////////////////////////////////////////////////////////// 48 | 49 | // A Renderbuffer is a two-dimensional texture that can only be used for 50 | // rendering (attached a Framebuffer) 51 | type Renderbuffer struct { 52 | object C.GLuint 53 | format TextureFormat 54 | } 55 | 56 | // NewRenderbuffer returns a new render buffer. 57 | func NewRenderbuffer(f TextureFormat, width, height int32) Renderbuffer { 58 | var r Renderbuffer 59 | r.format = f 60 | r.object = C.NewRenderbuffer(C.GLenum(f), C.GLsizei(width), C.GLsizei(height)) 61 | //TODO: error handling? 62 | return r 63 | } 64 | 65 | // Delete frees the render buffer 66 | func (r *Renderbuffer) Delete() { 67 | C.DeleteRenderbuffer(r.object) 68 | } 69 | 70 | //////////////////////////////////////////////////////////////////////////////// 71 | -------------------------------------------------------------------------------- /x/gl/setup.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package gl 5 | 6 | import ( 7 | "errors" 8 | 9 | "github.com/cozely/cozely/internal" 10 | ) 11 | 12 | //////////////////////////////////////////////////////////////////////////////// 13 | 14 | /* 15 | #cgo linux LDFLAGS: -ldl 16 | #include "glad.h" 17 | 18 | void errorCallback( 19 | GLenum source, 20 | GLenum type, 21 | GLuint id, 22 | GLenum severity, 23 | GLsizei length, 24 | const GLchar *message, 25 | const void *userParam); 26 | 27 | static inline int InitOpenGL(int debug){ 28 | if(!gladLoadGL()) { 29 | return -1; 30 | } 31 | 32 | if(debug) { 33 | glEnable(GL_DEBUG_OUTPUT); 34 | glDebugMessageCallback(errorCallback, NULL); 35 | } 36 | 37 | return 0; 38 | } 39 | */ 40 | import "C" 41 | 42 | //////////////////////////////////////////////////////////////////////////////// 43 | 44 | func init() { 45 | internal.GLSetup = setup 46 | } 47 | 48 | func setup() error { 49 | var d C.int 50 | if internal.Config.Debug { 51 | d = 1 52 | } 53 | if C.InitOpenGL(d) != 0 { 54 | return errors.New("gl setup: impossible to initialize OpenGL") 55 | } 56 | 57 | clearPipelineCurrentState() 58 | 59 | return nil 60 | } 61 | 62 | //////////////////////////////////////////////////////////////////////////////// 63 | 64 | func init() { 65 | internal.GLPrerender = prerender 66 | } 67 | 68 | func prerender() error { 69 | DefaultFramebuffer.Bind(DrawFramebuffer) 70 | if !noclear { 71 | ClearColorBuffer(struct{ R, G, B, A float32 }{0, 0, 0, 0}) 72 | } 73 | return nil 74 | } 75 | 76 | //////////////////////////////////////////////////////////////////////////////// 77 | 78 | func init() { 79 | internal.GLCleanup = cleanup 80 | } 81 | 82 | func cleanup() error { 83 | return nil 84 | } 85 | 86 | //////////////////////////////////////////////////////////////////////////////// 87 | -------------------------------------------------------------------------------- /x/gl/testdata/init.json: -------------------------------------------------------------------------------- 1 | { 2 | "WindowSize": [ 3 | 720, 4 | 720 5 | ], 6 | "Debug": false, 7 | "VSync": false 8 | } 9 | -------------------------------------------------------------------------------- /x/gl/testdata/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "Default": { 3 | "Quit": ["Escape"], 4 | "Rotate": ["Mouse Left"], 5 | "Move": ["Mouse Right"], 6 | "Zoom": ["Mouse Middle"], 7 | "Delta": ["Mouse"] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /x/gl/testdata/shader01.frag: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | out vec4 color; 4 | 5 | void main(void) 6 | { 7 | color = vec4(0.3, 0.1, 0.6, 1.0); 8 | } 9 | -------------------------------------------------------------------------------- /x/gl/testdata/shader01.vert: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | out gl_PerVertex { 4 | vec4 gl_Position; 5 | }; 6 | 7 | void main(void) 8 | { 9 | const vec4 triangle[3] = vec4[3]( 10 | vec4(0, 0.65, 0.5, 1), 11 | vec4(-0.65, -0.475, 0.5, 1), 12 | vec4(0.65, -0.475, 0.5, 1) 13 | ); 14 | gl_Position = triangle[gl_VertexID]; 15 | } 16 | -------------------------------------------------------------------------------- /x/gl/testdata/shader02.frag: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | in PerVertex { 4 | layout(location = 0) in vec3 Color; 5 | } vertex; 6 | 7 | out vec4 Color; 8 | 9 | void main(void) { 10 | Color = vec4(vertex.Color, 1); 11 | } 12 | -------------------------------------------------------------------------------- /x/gl/testdata/shader02.vert: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | layout(location = 0) in vec2 Position; 4 | layout(location = 1) in vec3 Color; 5 | 6 | out gl_PerVertex { 7 | vec4 gl_Position; 8 | }; 9 | 10 | out PerVertex { 11 | layout(location = 0) out vec3 Color; 12 | } vertex; 13 | 14 | void main(void) { 15 | gl_Position = vec4(Position, 0.5, 1); 16 | vertex.Color = Color; 17 | } 18 | -------------------------------------------------------------------------------- /x/gl/testdata/shader03.frag: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | in PerVertex { 4 | layout(location = 0) in vec3 Color; 5 | } vert; 6 | 7 | out vec4 Color; 8 | 9 | void main(void) { 10 | Color = vec4(vert.Color, 1); 11 | } 12 | -------------------------------------------------------------------------------- /x/gl/testdata/shader03.vert: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | layout(location = 0) in vec2 Position; 4 | layout(location = 1) in vec3 Color; 5 | 6 | layout(std140, binding = 0) uniform PerObject { 7 | mat3 Transform; 8 | } obj; 9 | 10 | out gl_PerVertex { 11 | vec4 gl_Position; 12 | }; 13 | 14 | out PerVertex { 15 | layout(location = 0) out vec3 Color; 16 | } vert; 17 | 18 | void main(void) { 19 | vec3 p = obj.Transform * vec3(Position, 1); 20 | gl_Position = vec4(p.xy, 0.5, 1); 21 | vert.Color = Color; 22 | } 23 | -------------------------------------------------------------------------------- /x/gl/testdata/shader04.frag: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | in PerVertex { 4 | layout(location = 0) in vec3 Color; 5 | } vert; 6 | 7 | out vec4 Color; 8 | 9 | void main(void) { 10 | Color = vec4(vert.Color, 1); 11 | } 12 | -------------------------------------------------------------------------------- /x/gl/testdata/shader04.vert: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | layout(location = 0) in vec3 Position; 4 | layout(location = 1) in vec3 Color; 5 | 6 | layout(std140, binding = 0) uniform PerObject { 7 | mat4 ScreenFromObject; 8 | } obj; 9 | 10 | out gl_PerVertex { 11 | vec4 gl_Position; 12 | }; 13 | 14 | out PerVertex { 15 | layout(location = 0) out vec3 Color; 16 | } vert; 17 | 18 | void main(void) { 19 | gl_Position = obj.ScreenFromObject * vec4(Position, 1); 20 | vert.Color = Color; 21 | } 22 | -------------------------------------------------------------------------------- /x/gl/testdata/shader05.frag: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | layout(binding = 0) uniform sampler2D Diffuse; 4 | 5 | in PerVertex { 6 | layout(location = 0) vec2 UV; 7 | } vert; 8 | 9 | out vec4 Color; 10 | 11 | void main(void) { 12 | Color = texture(Diffuse, vert.UV); 13 | } 14 | -------------------------------------------------------------------------------- /x/gl/testdata/shader05.vert: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | layout(location = 0) in vec3 Position; 4 | layout(location = 1) in vec2 UV; 5 | 6 | layout(std140, binding = 0) uniform PerObject { 7 | mat4 ScreenFromObject; 8 | } obj; 9 | 10 | out gl_PerVertex { 11 | vec4 gl_Position; 12 | }; 13 | 14 | out PerVertex { 15 | layout(location = 0) out vec2 UV; 16 | } vert; 17 | 18 | void main(void) { 19 | gl_Position = obj.ScreenFromObject * vec4(Position, 1); 20 | vert.UV = UV; 21 | } 22 | -------------------------------------------------------------------------------- /x/gl/testdata/shader06.frag: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | in PerVertex { 4 | layout(location = 0) in vec3 Color; 5 | } vert; 6 | 7 | out vec4 Color; 8 | 9 | void main(void) { 10 | Color = vec4(0.20, 0.06, 0.02, 1.0); 11 | } 12 | -------------------------------------------------------------------------------- /x/gl/testdata/shader06.vert: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | const int nbPoints = 512; 4 | 5 | layout(location = 0) in vec2 Position; 6 | layout(location = 1) in float Size; 7 | layout(location = 2) in int Numerator; 8 | layout(location = 3) in int Denominator; 9 | layout(location = 4) in float Offset; 10 | layout(location = 5) in float Speed; 11 | 12 | layout(std140, binding = 0) uniform PerFrame { 13 | float Ratio; 14 | float Time; 15 | } frame; 16 | 17 | out gl_PerVertex { 18 | vec4 gl_Position; 19 | }; 20 | 21 | void main(void) { 22 | // Calculate the rose 23 | vec2 p = Position; 24 | float k = float(Numerator) / float(Denominator); 25 | float theta = float(gl_VertexID) * 2 * 3.14159 / (-1 + float(nbPoints) / float(Denominator)); 26 | float r = (cos(k*theta) + Offset) / (1.0 + Offset); 27 | p.x = r * cos(theta + frame.Time*Speed); 28 | p.y = r * sin(theta + frame.Time*Speed); 29 | 30 | // Position and size 31 | p *= 0.11; 32 | p.x *= frame.Ratio; 33 | p += vec2( 34 | 1.0/8.0 + (gl_InstanceID % 8) / 4.0 - 1.0, 35 | 1.0/8.0 + (gl_InstanceID / 8) / 4.0 - 1.0 36 | ); 37 | 38 | gl_Position = vec4(p, 0.5, 1); 39 | } 40 | -------------------------------------------------------------------------------- /x/gl/testdata/shader07.frag: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | in PerVertex { 4 | layout(location = 0) in vec3 Color; 5 | } vert; 6 | 7 | out vec4 Color; 8 | 9 | void main(void) { 10 | Color = vec4(vert.Color, 1); 11 | } 12 | -------------------------------------------------------------------------------- /x/gl/testdata/shader07.vert: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | layout(location = 0) in vec3 Position; 4 | layout(location = 1) in vec3 Color; 5 | 6 | layout(std140, binding = 0) uniform PerObject { 7 | mat4 ScreenFromObject; 8 | } obj; 9 | 10 | out gl_PerVertex { 11 | vec4 gl_Position; 12 | }; 13 | 14 | out PerVertex { 15 | layout(location = 0) out vec3 Color; 16 | } vert; 17 | 18 | void main(void) { 19 | gl_Position = obj.ScreenFromObject * vec4(Position, 1); 20 | gl_Position.x += gl_InstanceID; 21 | vert.Color = Color; 22 | } 23 | -------------------------------------------------------------------------------- /x/gl/testdata/shader08.frag: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | out vec4 Color; 4 | 5 | void main(void) { 6 | Color = vec4(0.05, 0.04, 0.02, 0.05); 7 | } 8 | -------------------------------------------------------------------------------- /x/gl/testdata/shader08.vert: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | layout(location = 0) in vec2 Position; 4 | 5 | out gl_PerVertex { 6 | vec4 gl_Position; 7 | }; 8 | 9 | layout(std140, binding = 0) uniform PerFrame { 10 | vec2 Ratio; 11 | float Angle; 12 | } frame; 13 | 14 | void main(void) { 15 | float x = Position.x * cos(frame.Angle) - Position.y * sin(frame.Angle); 16 | float y = Position.x * sin(frame.Angle) + Position.y * cos(frame.Angle); 17 | gl_Position = vec4(frame.Ratio.x * x, frame.Ratio.y * y, 0.5, 1.0); 18 | } 19 | -------------------------------------------------------------------------------- /x/gl/testdata/testpattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drakmaniso/old-cozely/d322e897cad173c754c1eb5ef5e046fa75c615b4/x/gl/testdata/testpattern.png -------------------------------------------------------------------------------- /x/gl/texture1d.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package gl 5 | 6 | import ( 7 | "image" 8 | ) 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | /* 13 | #include "glad.h" 14 | 15 | static inline GLuint NewTexture1D(GLsizei levels, GLenum format, GLsizei width) { 16 | GLuint t; 17 | glCreateTextures(GL_TEXTURE_1D, 1, &t); 18 | glTextureStorage1D(t, levels, format, width); 19 | return t; 20 | } 21 | 22 | static inline void Texture1DSubImage( 23 | GLuint texture, 24 | GLint level, 25 | GLint xoffset, 26 | GLsizei width, 27 | GLenum format, 28 | GLenum type, 29 | const void *pixels 30 | ) { 31 | glTextureSubImage1D(texture, level, xoffset, width, format, type, pixels); 32 | } 33 | 34 | static inline void TextureGenerateMipmap(GLuint texture) { 35 | glGenerateTextureMipmap(texture); 36 | } 37 | 38 | static inline void BindTextureUnit(GLuint unit, GLuint texture) { 39 | glBindTextureUnit(unit, texture); 40 | } 41 | 42 | static inline void DeleteTexture(GLuint texture) { 43 | glDeleteTextures(1, &texture); 44 | } 45 | 46 | */ 47 | import "C" 48 | 49 | //////////////////////////////////////////////////////////////////////////////// 50 | 51 | // A Texture1D is a one-dimensional texture. 52 | type Texture1D struct { 53 | object C.GLuint 54 | format TextureFormat 55 | } 56 | 57 | // NewTexture1D returns a new one-dimensional texture. 58 | func NewTexture1D(levels int32, f TextureFormat, width int32) Texture1D { 59 | var t Texture1D 60 | t.format = f 61 | t.object = C.NewTexture1D(C.GLsizei(levels), C.GLenum(f), C.GLsizei(width)) 62 | //TODO: error handling? 63 | return t 64 | } 65 | 66 | // SubImage loads an image into a texture at a specific position offset and 67 | // mipmap level. 68 | func (t *Texture1D) SubImage(level int32, ox int32, img image.Image) { 69 | p, pf, pt := pointerFormatAndTypeOf(img) 70 | C.Texture1DSubImage(t.object, C.GLint(level), C.GLint(ox), C.GLsizei(img.Bounds().Dx()), pf, pt, p) 71 | } 72 | 73 | // GenerateMipmap generates mipmaps for the texture. 74 | func (t *Texture1D) GenerateMipmap() { 75 | C.TextureGenerateMipmap(t.object) 76 | } 77 | 78 | // Bind to a texture unit. 79 | func (t *Texture1D) Bind(index uint32) { 80 | C.BindTextureUnit(C.GLuint(index), t.object) 81 | } 82 | 83 | // Delete frees the texture 84 | func (t *Texture1D) Delete() { 85 | C.DeleteTexture(t.object) 86 | } 87 | 88 | //////////////////////////////////////////////////////////////////////////////// 89 | -------------------------------------------------------------------------------- /x/gl/ze1_first_triangle_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package gl_test 5 | 6 | import ( 7 | "github.com/cozely/cozely" 8 | "github.com/cozely/cozely/color" 9 | "github.com/cozely/cozely/window" 10 | "github.com/cozely/cozely/x/gl" 11 | ) 12 | 13 | // Declarations //////////////////////////////////////////////////////////////// 14 | 15 | type loop01 struct { 16 | // OpenGL Object 17 | pipeline *gl.Pipeline 18 | } 19 | 20 | // Initialization ////////////////////////////////////////////////////////////// 21 | 22 | func Example_firstTriangle() { 23 | defer cozely.Recover() 24 | 25 | window.Events.Resize = func() { 26 | s := window.Size() 27 | gl.Viewport(0, 0, int32(s.X), int32(s.Y)) 28 | } 29 | l := loop01{} 30 | err := cozely.Run(&l) 31 | if err != nil { 32 | panic(err) 33 | } 34 | //Output: 35 | } 36 | 37 | func (l *loop01) Enter() { 38 | // Create and configure the pipeline 39 | l.pipeline = gl.NewPipeline( 40 | gl.Shader(cozely.Path()+"shader01.vert"), 41 | gl.Shader(cozely.Path()+"shader01.frag"), 42 | gl.Topology(gl.Triangles), 43 | ) 44 | } 45 | 46 | func (loop01) Leave() { 47 | } 48 | 49 | // Game Loop /////////////////////////////////////////////////////////////////// 50 | 51 | func (loop01) React() { 52 | // if input.Default.Back.Pushed() { 53 | // cozely.Stop(nil) 54 | // } 55 | } 56 | 57 | func (loop01) Update() { 58 | } 59 | 60 | func (l *loop01) Render() { 61 | l.pipeline.Bind() 62 | gl.ClearColorBuffer(color.LRGBA{0.9, 0.9, 0.9, 1.0}) 63 | 64 | gl.Draw(0, 3) 65 | l.pipeline.Unbind() 66 | } 67 | -------------------------------------------------------------------------------- /x/gl/ze2_vertex_buffer_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package gl_test 5 | 6 | import ( 7 | "github.com/cozely/cozely" 8 | "github.com/cozely/cozely/color" 9 | "github.com/cozely/cozely/coord" 10 | "github.com/cozely/cozely/window" 11 | "github.com/cozely/cozely/x/gl" 12 | ) 13 | 14 | // Declarations //////////////////////////////////////////////////////////////// 15 | 16 | type loop02 struct { 17 | pipeline *gl.Pipeline 18 | } 19 | 20 | // Vertex buffer 21 | type mesh2d []struct { 22 | position coord.XY `layout:"0"` 23 | color color.LRGB `layout:"1"` 24 | } 25 | 26 | // Initialization ////////////////////////////////////////////////////////////// 27 | 28 | func Example_vertexBuffer() { 29 | defer cozely.Recover() 30 | 31 | window.Events.Resize = func() { 32 | s := window.Size() 33 | gl.Viewport(0, 0, int32(s.X), int32(s.Y)) 34 | } 35 | l := loop02{} 36 | err := cozely.Run(&l) 37 | if err != nil { 38 | panic(err) 39 | } 40 | //Output: 41 | } 42 | 43 | func (l *loop02) Enter() { 44 | var triangle mesh2d 45 | 46 | // Create and configure the pipeline 47 | l.pipeline = gl.NewPipeline( 48 | gl.Shader(cozely.Path()+"shader02.vert"), 49 | gl.Shader(cozely.Path()+"shader02.frag"), 50 | gl.VertexFormat(0, triangle), 51 | gl.Topology(gl.Triangles), 52 | ) 53 | gl.Enable(gl.FramebufferSRGB) 54 | 55 | // Create and fill the vertex buffer 56 | triangle = mesh2d{ 57 | {coord.XY{0, 0.65}, color.LRGB{R: 0.3, G: 0, B: 0.8}}, 58 | {coord.XY{-0.65, -0.475}, color.LRGB{R: 0.8, G: 0.3, B: 0}}, 59 | {coord.XY{0.65, -0.475}, color.LRGB{R: 0, G: 0.6, B: 0.2}}, 60 | } 61 | vbo := gl.NewVertexBuffer(triangle, gl.StaticStorage) 62 | 63 | // Bind the vertex buffer to the pipeline 64 | l.pipeline.Bind() 65 | vbo.Bind(0, 0) 66 | l.pipeline.Unbind() 67 | } 68 | 69 | func (loop02) Leave() { 70 | } 71 | 72 | // Game Loop /////////////////////////////////////////////////////////////////// 73 | 74 | func (loop02) React() { 75 | } 76 | 77 | func (loop02) Update() { 78 | } 79 | 80 | func (l *loop02) Render() { 81 | l.pipeline.Bind() 82 | gl.ClearColorBuffer(color.LRGBA{0.9, 0.9, 0.9, 1.0}) 83 | 84 | gl.Draw(0, 3) 85 | l.pipeline.Unbind() 86 | } 87 | -------------------------------------------------------------------------------- /x/machine/fsm.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package machine 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // State is used to implement a lightweight state machine. Each state is 9 | // represented by a closure, that, when run, returns the next state (or nil to 10 | // stop the machine). 11 | type State func() State 12 | 13 | //////////////////////////////////////////////////////////////////////////////// 14 | 15 | // Update runs s (i.e. the current state), and replaces it with the result of 16 | // that call. It's safe to call Update on a nil target. 17 | func (s *State) Update() { 18 | if *s != nil { 19 | *s = (*s)() 20 | } 21 | } 22 | 23 | //////////////////////////////////////////////////////////////////////////////// 24 | -------------------------------------------------------------------------------- /x/machine/z01_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package machine_test 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/cozely/cozely/x/machine" 10 | ) 11 | 12 | func Example() { 13 | counter := 0 14 | 15 | // Define the State Machine 16 | var s1, s2, s3 machine.State 17 | 18 | s1 = func() machine.State { 19 | fmt.Println("State 1") 20 | return s2 21 | } 22 | 23 | s2 = func() machine.State { 24 | fmt.Println("State 2") 25 | return s3 26 | } 27 | 28 | s3 = func() machine.State { 29 | fmt.Println("State 3") 30 | if counter > 6 { 31 | return nil 32 | } 33 | return s2 34 | } 35 | 36 | // Run the State Machine 37 | m := s1 38 | 39 | for m != nil { 40 | counter++ 41 | m.Update() 42 | } 43 | // Output: 44 | // State 1 45 | // State 2 46 | // State 3 47 | // State 2 48 | // State 3 49 | // State 2 50 | // State 3 51 | } 52 | -------------------------------------------------------------------------------- /x/machine/z02_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package machine_test 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/cozely/cozely/x/machine" 10 | ) 11 | 12 | var counter int 13 | 14 | func state1() machine.State { 15 | fmt.Println("State 1") 16 | return state2 17 | } 18 | 19 | func state2() machine.State { 20 | fmt.Println("State 2") 21 | return state3 22 | } 23 | 24 | func state3() machine.State { 25 | fmt.Println("State 3") 26 | if counter > 6 { 27 | return nil 28 | } 29 | return state2 30 | } 31 | 32 | func Example_noAllocations() { 33 | m := machine.State(state1) 34 | 35 | for m != nil { 36 | counter++ 37 | m.Update() 38 | } 39 | // Output: 40 | // State 1 41 | // State 2 42 | // State 3 43 | // State 2 44 | // State 3 45 | // State 2 46 | // State 3 47 | } 48 | -------------------------------------------------------------------------------- /x/math32/ORIGINAL_LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /x/math32/abs.go: -------------------------------------------------------------------------------- 1 | // Based on code from the Go standard library. 2 | // Copyright 2009 The Go Authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the ORIGINAL_LICENSE file. 5 | 6 | package math32 7 | 8 | //////////////////////////////////////////////////////////////////////////////// 9 | 10 | // Abs returns the absolute value of x. 11 | func Abs(x float32) float32 { 12 | // TODO: rewrite when golang.org/issue/13095 is fixed 13 | if x < 0 { 14 | return -x 15 | } 16 | if x == 0 { 17 | return 0 // return correctly abs(-0) 18 | } 19 | return x 20 | } 21 | 22 | //////////////////////////////////////////////////////////////////////////////// 23 | -------------------------------------------------------------------------------- /x/math32/abs_386.s: -------------------------------------------------------------------------------- 1 | // Based on code from the Go standard library. 2 | // Copyright 2010 The Go Authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the ORIGINALLICENSE file. 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // func Abs(x float32) float32 9 | TEXT ·absAsm(SB),7,$0 10 | MOVL $(1<<31), BX 11 | MOVL BX, X0 // movsd $(-0.0), x0 12 | MOVSS x+0(FP), X1 13 | ANDNPS X1, X0 14 | MOVSS X0, ret+4(FP) 15 | RET 16 | 17 | //////////////////////////////////////////////////////////////////////////////// 18 | -------------------------------------------------------------------------------- /x/math32/abs_amd64.s: -------------------------------------------------------------------------------- 1 | // Based on code from the Go standard library. 2 | // Copyright 2010 The Go Authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the ORIGINALLICENSE file. 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | #include "textflag.h" 9 | 10 | // func absAsm(x float32) float32 11 | TEXT ·absAsm(SB),NOSPLIT,$0 12 | MOVL x+0(FP), AX 13 | SHLL $1, AX 14 | SHRL $1, AX 15 | MOVL AX, ret+8(FP) 16 | RET 17 | // MOVL $(1<<31), BX 18 | // MOVL BX, X0 // movss $(-0.0), x0 19 | // MOVSS x+0(FP), X1 20 | // ANDNPS X1, X0 21 | // MOVSS X0, ret+8(FP) 22 | // RET 23 | 24 | //////////////////////////////////////////////////////////////////////////////// 25 | -------------------------------------------------------------------------------- /x/math32/bits.go: -------------------------------------------------------------------------------- 1 | // Based on code from the Go standard library. 2 | // Copyright 2009 The Go Authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the ORIGINAL_LICENSE file. 5 | 6 | package math32 7 | 8 | //////////////////////////////////////////////////////////////////////////////// 9 | 10 | const ( 11 | uvnan = 0x7F800001 12 | uvinf = 0x7F800000 13 | uvneginf = 0xFF800000 14 | mask = 0xFF 15 | shift = 32 - 8 - 1 16 | bias = 127 17 | ) 18 | 19 | //////////////////////////////////////////////////////////////////////////////// 20 | 21 | // Inf returns positive infinity if sign >= 0, negative infinity if sign < 0. 22 | func Inf(sign int) float32 { 23 | var v uint32 24 | if sign >= 0 { 25 | v = uvinf 26 | } else { 27 | v = uvneginf 28 | } 29 | return Float32frombits(v) 30 | } 31 | 32 | // NaN returns an IEEE 754 "not-a-number" value. 33 | func NaN() float32 { return Float32frombits(uvnan) } 34 | 35 | // IsNaN returns whether f is an IEEE 754 "not-a-number" value. 36 | func IsNaN(f float32) (is bool) { 37 | // IEEE 754 says that only NaNs satisfy `f != f`. 38 | // To avoid the floating-point hardware, could use: 39 | // `x := Float32bits(f)` 40 | // `return uint32(x>>shift)&mask == mask && x != uvinf && x != uvneginf` 41 | return f != f 42 | } 43 | 44 | // IsInf returns whether f is an infinity, according to sign. 45 | // If sign > 0, IsInf returns whether f is positive infinity. 46 | // If sign < 0, IsInf returns whether f is negative infinity. 47 | // If sign == 0, IsInf returns whether f is either infinity. 48 | func IsInf(f float32, sign int) bool { 49 | // Test for infinity by comparing against maximum float. 50 | // To avoid the floating-point hardware, could use: 51 | // `x := Float32bits(f)` 52 | // `return sign >= 0 && x == uvinf || sign <= 0 && x == uvneginf` 53 | return sign >= 0 && f > MaxFloat32 || sign <= 0 && f < -MaxFloat32 54 | } 55 | 56 | // Normalized returns a normal number y and exponent exp 57 | // satisfying x == y × 2**exp. It assumes x is finite and non-zero. 58 | func Normalized(x float32) (y float32, exp int) { 59 | if Abs(x) < SmallestNormalFloat32 { 60 | return x * (1 << 23), -23 61 | } 62 | return x, 0 63 | } 64 | 65 | func normalize(x float32) (y float32, exp int) { 66 | //TODO: ??? 67 | return Normalized(x) 68 | } 69 | 70 | //////////////////////////////////////////////////////////////////////////////// 71 | -------------------------------------------------------------------------------- /x/math32/copysign.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package math32 5 | 6 | // Copysign returns a value with the magnitude 7 | // of x and the sign of y. 8 | func Copysign(x, y float32) float32 { 9 | const sign = 1 << 31 10 | return Float32frombits(Float32bits(x)&^sign | Float32bits(y)&sign) 11 | } 12 | 13 | //////////////////////////////////////////////////////////////////////////////// 14 | -------------------------------------------------------------------------------- /x/math32/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | /* 5 | Package math provides efficient float32 math functions. 6 | */ 7 | package math32 8 | -------------------------------------------------------------------------------- /x/math32/fastcos.go: -------------------------------------------------------------------------------- 1 | // This code is adapted from: 2 | // http://devmaster.net/forums/topic/4648-fast-and-accurate-sinecosine/ 3 | // Copyright Nicolas Capens 4 | 5 | package math32 6 | 7 | //////////////////////////////////////////////////////////////////////////////// 8 | 9 | // FastCos returns an approximation of sin(x). 10 | // 11 | // Max absolute error in range [-Pi, Pi]: less than 1e-3 12 | // 13 | // Faster than Cos. 14 | func FastCos(x float32) float32 15 | 16 | func fastCos(x float32) float32 { 17 | const ( 18 | PISLASHTWO = Pi / 2 19 | TWOPI = 2 * Pi 20 | B = 4 / Pi 21 | C = -4 / (Pi * Pi) 22 | ) 23 | 24 | x += PISLASHTWO 25 | if x > Pi { 26 | x -= TWOPI 27 | } 28 | 29 | y := B*x + C*x*Abs(x) 30 | 31 | // const ( 32 | // P = 0.225 33 | // Q = 0.775 34 | // ) 35 | const ( 36 | P = 0.224008178776 37 | Q = 0.775991821224 38 | ) 39 | 40 | //y = P * (y * Abs(y) - y) + y 41 | y = Q*y + P*y*Abs(y) 42 | 43 | return y 44 | } 45 | 46 | //////////////////////////////////////////////////////////////////////////////// 47 | -------------------------------------------------------------------------------- /x/math32/fastcos_386.s: -------------------------------------------------------------------------------- 1 | // This code is adapted from: 2 | // http://devmaster.net/forums/topic/4648-fast-and-accurate-sinecosine/ 3 | // Copyright Nicolas Capens 4 | 5 | //////////////////////////////////////////////////////////////////////////////// 6 | 7 | #define PISLASHTWO 1.57079632679489661923132169164 8 | #define PI 3.14159265358979323846264338328 9 | #define TWOPI 6.283185307179586476925286766559 10 | 11 | #define ABS 0x7FFFFFFF 12 | 13 | #define B 1.27323954473516268615107010698 14 | #define C -0.4052847345693510857755178528389 15 | 16 | #define P 0.224008178776 17 | #define Q 0.775991821224 18 | 19 | // func FastCos(s float32) float32 20 | TEXT ·FastCos(SB),7,$0 21 | MOVL x+0(FP), X0 // X0 = x 22 | ADDSS $PISLASHTWO, X0 // X0 = x + Pi/2 23 | MOVSS $PI, X1 24 | UCOMISS X1, X0 25 | JLS inrange // if x + Pi/2 < Pi jump to inrange 26 | MOVSS $TWOPI, X1 27 | SUBSS X1, X0 // X0 = x + Pi/2 - 2*Pi 28 | 29 | /* MOVSS X0, X1 // X1 = x + Pi/2 30 | MOVSS $PI, X2 31 | CMPSS X2, X1, 5 // X1 = x + Pi/2 >= Pi 32 | MOVSS $TWOPI, X2 33 | ANDPS X2, X1 // X1 = (x + Pi/2 >= Pi) ? 2*Pi : 0 34 | SUBSS X1, X0 // X0 = x = (x + Pi/2) modulo (2*Pi)*/ 35 | 36 | inrange: 37 | 38 | MOVL X0, AX 39 | ANDL $ABS, AX 40 | MOVL AX, X1 // X1 = |x| 41 | MULSS X0, X1 // X1 = x * |x| 42 | MULSS $B, X0 // X0 = B * x 43 | MULSS $C, X1 // X1 = C * x * |x| 44 | ADDSS X1, X0 // X0 = y = C * x * |x| + B * x 45 | 46 | MOVL X0, AX 47 | ANDL $ABS, AX 48 | MOVL AX, X1 // X1 = |y| 49 | MULSS X0, X1 // X1 = y * |y| 50 | MULSS $Q, X0 // X0 = Q * y 51 | MULSS $P, X1 // X1 = P * y * |y| 52 | ADDSS X1, X0 // X0 = P * y * |y| + Q * y 53 | 54 | MOVSS X0, ret+4(FP) 55 | RET 56 | 57 | //////////////////////////////////////////////////////////////////////////////// 58 | -------------------------------------------------------------------------------- /x/math32/fastcos_amd64.s: -------------------------------------------------------------------------------- 1 | // This code is adapted from: 2 | // http://devmaster.net/forums/topic/4648-fast-and-accurate-sinecosine/ 3 | // Copyright Nicolas Capens 4 | 5 | //////////////////////////////////////////////////////////////////////////////// 6 | 7 | #define PISLASHTWO 1.57079632679489661923132169164 8 | #define PI 3.14159265358979323846264338328 9 | #define TWOPI 6.283185307179586476925286766559 10 | 11 | #define ABS 0x7FFFFFFF 12 | 13 | #define B 1.27323954473516268615107010698 14 | #define C -0.4052847345693510857755178528389 15 | 16 | #define P 0.224008178776 17 | #define Q 0.775991821224 18 | 19 | // func FastCos(s float32) float32 20 | TEXT ·FastCos(SB),7,$0 21 | MOVL x+0(FP), X0 // X0 = x 22 | ADDSS $PISLASHTWO, X0 // X0 = x + Pi/2 23 | MOVSS $PI, X1 24 | UCOMISS X1, X0 25 | JLS inrange // if x + Pi/2 < Pi jump to inrange 26 | MOVSS $TWOPI, X1 27 | SUBSS X1, X0 // X0 = x + Pi/2 - 2*Pi 28 | 29 | /* MOVSS X0, X1 // X1 = x + Pi/2 30 | MOVSS $PI, X2 31 | CMPSS X2, X1, 5 // X1 = x + Pi/2 >= Pi 32 | MOVSS $TWOPI, X2 33 | ANDPS X2, X1 // X1 = (x + Pi/2 >= Pi) ? 2*Pi : 0 34 | SUBSS X1, X0 // X0 = x = (x + Pi/2) modulo (2*Pi)*/ 35 | 36 | inrange: 37 | 38 | MOVL X0, AX 39 | ANDL $ABS, AX 40 | MOVL AX, X1 // X1 = |x| 41 | MULSS X0, X1 // X1 = x * |x| 42 | MULSS $B, X0 // X0 = B * x 43 | MULSS $C, X1 // X1 = C * x * |x| 44 | ADDSS X1, X0 // X0 = y = C * x * |x| + B * x 45 | 46 | MOVL X0, AX 47 | ANDL $ABS, AX 48 | MOVL AX, X1 // X1 = |y| 49 | MULSS X0, X1 // X1 = y * |y| 50 | MULSS $Q, X0 // X0 = Q * y 51 | MULSS $P, X1 // X1 = P * y * |y| 52 | ADDSS X1, X0 // X0 = P * y * |y| + Q * y 53 | 54 | MOVSS X0, ret+8(FP) 55 | RET 56 | 57 | //////////////////////////////////////////////////////////////////////////////// 58 | -------------------------------------------------------------------------------- /x/math32/fastcos_arm.s: -------------------------------------------------------------------------------- 1 | // This code is adapted from: 2 | // http://devmaster.net/forums/topic/4648-fast-and-accurate-sinecosine/ 3 | // Copyright Nicolas Capens 4 | 5 | //------------------------------------------------------------------------------ 6 | 7 | // func FastCos(s float32) float32 8 | TEXT ·FastCos(SB),7,$0 9 | B ·fastCos(SB) 10 | 11 | //------------------------------------------------------------------------------ 12 | -------------------------------------------------------------------------------- /x/math32/fastcos_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package math32 5 | 6 | import ( 7 | "testing" 8 | ) 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | func TestFastCos(t *testing.T) { 13 | var x float32 14 | maxDiff := float32(0) 15 | for _, tt := range cosTests { 16 | x = FastCos(tt.in) 17 | if Abs(x-tt.out) > maxDiff { 18 | maxDiff = Abs(x - tt.out) 19 | } 20 | if !IsRoughlyEqual(x, tt.out, 1e-3) { 21 | t.Errorf("ULP error for Cos(%.100g): %.100g instead of %.100g\n", tt.in, x, tt.out) 22 | } 23 | } 24 | t.Logf("Max absolute error: %1.8e\n", maxDiff) 25 | } 26 | 27 | //////////////////////////////////////////////////////////////////////////////// 28 | 29 | func BenchmarkFastCos_go(b *testing.B) { 30 | a := float32(0.5) 31 | for i := 0; i < b.N; i++ { 32 | _ = fastCos(a) 33 | } 34 | } 35 | 36 | //////////////////////////////////////////////////////////////////////////////// 37 | 38 | func BenchmarkFastCos_glam(b *testing.B) { 39 | a := float32(0.5) 40 | for i := 0; i < b.N; i++ { 41 | _ = FastCos(a) 42 | } 43 | } 44 | 45 | //////////////////////////////////////////////////////////////////////////////// 46 | -------------------------------------------------------------------------------- /x/math32/fastfloor.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package math32 5 | 6 | // FastFloor returns int32(x) if x>0, int32(x-1) otherwise. 7 | func FastFloor(x float32) int32 { 8 | if x > 0 { 9 | return int32(x) 10 | } 11 | return int32(x - 1) 12 | } 13 | 14 | //////////////////////////////////////////////////////////////////////////////// 15 | -------------------------------------------------------------------------------- /x/math32/fastfloor_386.s: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | // SLOWER than the Go function 7 | // func FastFloorAsm(s float32) int32 8 | TEXT ·fastFloorAsm(SB),7,$0 9 | CVTTSS2SL x+0(FP), BX 10 | MOVL x+0(FP), AX 11 | SHRL $31, AX 12 | SUBL AX, BX 13 | MOVL BX,ret+4(FP) 14 | RET 15 | 16 | //////////////////////////////////////////////////////////////////////////////// 17 | -------------------------------------------------------------------------------- /x/math32/fastfloor_amd64.s: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | // SLOWER than the Go function 7 | // func fastFloorAsm(s float32) int32 8 | TEXT ·fastFloorAsm(SB),7,$0 9 | CVTTSS2SL s+0(FP), BX 10 | MOVL s+0(FP), AX 11 | SHRL $31, AX 12 | SUBL AX, BX 13 | MOVL BX,ret+8(FP) 14 | RET 15 | 16 | //////////////////////////////////////////////////////////////////////////////// 17 | -------------------------------------------------------------------------------- /x/math32/fastfloor_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package math32 5 | 6 | import ( 7 | "testing" 8 | ) 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | func TestFastFloor(t *testing.T) { 13 | a := FastFloor(float32(3.3)) 14 | if a != 3 { 15 | t.Errorf("Wrong result for FastFloor(3.3): %v %T", a, a) 16 | } 17 | b := FastFloor(float32(-3.3)) 18 | if b != -4 { 19 | t.Errorf("Wrong result for FastFloor(-3.3): %v %T", b, b) 20 | } 21 | c := FastFloor(float32(3)) 22 | if c != 3 { 23 | t.Errorf("Wrong result for FastFloor(3): %v %T", c, c) 24 | } 25 | d := FastFloor(float32(-3)) 26 | if d != -4 { 27 | t.Errorf("Wrong result for FastFloor(-3): %v %T", d, d) 28 | } 29 | } 30 | 31 | //////////////////////////////////////////////////////////////////////////////// 32 | 33 | func fastFloorCast(x float32) int32 { 34 | if x > 0 { 35 | return int32(x) 36 | } 37 | return int32(x - 1) 38 | } 39 | 40 | func BenchmarkFastFloor_cast(b *testing.B) { 41 | x := float32(3.3) 42 | y := float32(-3.3) 43 | for i := 0; i < b.N; i++ { 44 | resultInt32 = fastFloorCast(x) 45 | resultInt32 = fastFloorCast(y) 46 | } 47 | } 48 | 49 | //////////////////////////////////////////////////////////////////////////////// 50 | 51 | func fastFloorAsm(s float32) int32 52 | 53 | func BenchmarkFastFloor_asm(b *testing.B) { 54 | x := float32(3.3) 55 | y := float32(-3.3) 56 | for i := 0; i < b.N; i++ { 57 | resultInt32 = fastFloorAsm(x) 58 | resultInt32 = fastFloorAsm(y) 59 | } 60 | } 61 | 62 | //////////////////////////////////////////////////////////////////////////////// 63 | 64 | func BenchmarkFastFloor_glam(b *testing.B) { 65 | x := float32(3.3) 66 | y := float32(-3.3) 67 | for i := 0; i < b.N; i++ { 68 | resultInt32 = FastFloor(x) 69 | resultInt32 = FastFloor(y) 70 | } 71 | } 72 | 73 | //////////////////////////////////////////////////////////////////////////////// 74 | -------------------------------------------------------------------------------- /x/math32/fastsin.go: -------------------------------------------------------------------------------- 1 | // This code is adapted from: 2 | // http://devmaster.net/forums/topic/4648-fast-and-accurate-sinecosine/ 3 | // Copyright Nicolas Capens 4 | 5 | package math32 6 | 7 | //////////////////////////////////////////////////////////////////////////////// 8 | 9 | // FastSin returns an approximation of sin(x). 10 | // 11 | // Max absolute error in range [-Pi, Pi]: less than 1e-3 12 | // 13 | // Faster than Sin. 14 | func FastSin(x float32) float32 15 | 16 | func fastSin(x float32) float32 { 17 | const ( 18 | B = 4 / Pi 19 | C = -4 / (Pi * Pi) 20 | ) 21 | 22 | y := B*x + C*x*Abs(x) 23 | 24 | // const ( 25 | // P = 0.225 26 | // Q = 0.775 27 | // ) 28 | const ( 29 | P = 0.224008178776 30 | Q = 0.775991821224 31 | ) 32 | 33 | //y = P * (y * Abs(y) - y) + y 34 | y = Q*y + P*y*Abs(y) 35 | 36 | return y 37 | } 38 | 39 | // func FastSin(x float32) float32 { 40 | // const ( 41 | // P = 0.225 42 | // A = 16 * 0.47434165 43 | // B = (1 - P) / 0.47434165 44 | // ) 45 | // 46 | // y := x / (2 * Pi) 47 | // 48 | // y = y - Floor(y + 0.5) // y in range -0.5..0.5 49 | // 50 | // y = A * y * (0.5 - Abs(y)) 51 | // 52 | // return y * (B + Abs(y)) 53 | // 54 | // } 55 | 56 | //////////////////////////////////////////////////////////////////////////////// 57 | -------------------------------------------------------------------------------- /x/math32/fastsin_386.s: -------------------------------------------------------------------------------- 1 | // This code is adapted from: 2 | // http://devmaster.net/forums/topic/4648-fast-and-accurate-sinecosine/ 3 | // Copyright Nicolas Capens 4 | 5 | //////////////////////////////////////////////////////////////////////////////// 6 | 7 | #define ABS 0x7FFFFFFF 8 | 9 | #define B 1.27323954473516268615107010698 10 | #define C -0.4052847345693510857755178528389 11 | 12 | #define P 0.224008178776 13 | #define Q 0.775991821224 14 | 15 | // func FastSin(s float32) float32 16 | TEXT ·FastSin(SB),7,$0 17 | MOVL x+0(FP), AX 18 | MOVL AX, X0 // X0 = x 19 | ANDL $ABS, AX 20 | MOVL AX, X1 // X1 = |x| 21 | MULSS X0, X1 // X1 = x * |x| 22 | MULSS $B, X0 // X0 = B * x 23 | MULSS $C, X1 // X1 = C * x * |x| 24 | ADDSS X1, X0 // X0 = y = C * x * |x| + B * x 25 | 26 | MOVL X0, AX 27 | ANDL $ABS, AX 28 | MOVL AX, X1 // X1 = |y| 29 | MULSS X0, X1 // X1 = y * |y| 30 | MULSS $Q, X0 // X0 = Q * y 31 | MULSS $P, X1 // X1 = P * y * |y| 32 | ADDSS X1, X0 // X0 = P * y * |y| + Q * y 33 | 34 | MOVSS X0, ret+4(FP) 35 | RET 36 | 37 | //////////////////////////////////////////////////////////////////////////////// 38 | -------------------------------------------------------------------------------- /x/math32/fastsin_amd64.s: -------------------------------------------------------------------------------- 1 | // This code is adapted from: 2 | // http://devmaster.net/forums/topic/4648-fast-and-accurate-sinecosine/ 3 | // Copyright Nicolas Capens 4 | 5 | //////////////////////////////////////////////////////////////////////////////// 6 | 7 | #define ABS 0x7FFFFFFF 8 | 9 | #define B 1.27323954473516268615107010698 10 | #define C -0.4052847345693510857755178528389 11 | 12 | #define P 0.224008178776 13 | #define Q 0.775991821224 14 | 15 | // func FastSin(s float32) float32 16 | TEXT ·FastSin(SB),7,$0 17 | MOVL x+0(FP), AX 18 | MOVL AX, X0 // X0 = x 19 | ANDL $ABS, AX 20 | MOVL AX, X1 // X1 = |x| 21 | MULSS X0, X1 // X1 = x * |x| 22 | MULSS $B, X0 // X0 = B * x 23 | MULSS $C, X1 // X1 = C * x * |x| 24 | ADDSS X1, X0 // X0 = y = C * x * |x| + B * x 25 | 26 | MOVL X0, AX 27 | ANDL $ABS, AX 28 | MOVL AX, X1 // X1 = |y| 29 | MULSS X0, X1 // X1 = y * |y| 30 | MULSS $Q, X0 // X0 = Q * y 31 | MULSS $P, X1 // X1 = P * y * |y| 32 | ADDSS X1, X0 // X0 = P * y * |y| + Q * y 33 | 34 | MOVSS X0, ret+8(FP) 35 | RET 36 | 37 | //////////////////////////////////////////////////////////////////////////////// 38 | -------------------------------------------------------------------------------- /x/math32/fastsin_arm.s: -------------------------------------------------------------------------------- 1 | // This code is adapted from: 2 | // http://devmaster.net/forums/topic/4648-fast-and-accurate-sinecosine/ 3 | // Copyright Nicolas Capens 4 | 5 | //------------------------------------------------------------------------------ 6 | 7 | // func FastSin(s float32) float32 8 | TEXT ·FastSin(SB),7,$0 9 | B ·fastSin(SB) 10 | 11 | //------------------------------------------------------------------------------ 12 | -------------------------------------------------------------------------------- /x/math32/fastsin_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package math32 5 | 6 | import ( 7 | "testing" 8 | ) 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | func TestFastSin(t *testing.T) { 13 | var x float32 14 | maxDiff := float32(0) 15 | for _, tt := range sinTests { 16 | x = FastSin(tt.in) 17 | if Abs(x-tt.out) > maxDiff { 18 | maxDiff = Abs(x - tt.out) 19 | } 20 | if !IsRoughlyEqual(x, tt.out, 1e-3) { 21 | t.Errorf("ULP error for Sin(%.100g): %.100g instead of %.100g\n", tt.in, x, tt.out) 22 | } 23 | } 24 | t.Logf("Max absolute error: %1.8e\n", maxDiff) 25 | } 26 | 27 | //////////////////////////////////////////////////////////////////////////////// 28 | 29 | func BenchmarkFastSin_go(b *testing.B) { 30 | a := float32(0.5) 31 | for i := 0; i < b.N; i++ { 32 | result = fastSin(a) 33 | } 34 | } 35 | 36 | //////////////////////////////////////////////////////////////////////////////// 37 | 38 | func BenchmarkFastSin_glam(b *testing.B) { 39 | a := float32(0.5) 40 | for i := 0; i < b.N; i++ { 41 | result = FastSin(a) 42 | } 43 | } 44 | 45 | //////////////////////////////////////////////////////////////////////////////// 46 | -------------------------------------------------------------------------------- /x/math32/floor.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package math32 5 | 6 | import ( 7 | "math" 8 | ) 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | // Floor returns the nearest integer less than or equal to x. 13 | func Floor(x float32) float32 14 | 15 | func floor(x float32) float32 { 16 | return float32(math.Floor(float64(x))) 17 | } 18 | -------------------------------------------------------------------------------- /x/math32/floor_386.s: -------------------------------------------------------------------------------- 1 | // Based on code from the Go standard library. 2 | // Copyright 2009 The Go Authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the ORIGINAL_LICENSE file. 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // func Floor(s float32) float32 9 | TEXT ·Floor(SB),7,$0 10 | MOVL x+0(FP), AX 11 | MOVL AX, X0 // X0 = x 12 | CVTTSS2SL X0, AX // AX = int(x) 13 | CVTSL2SS AX, X1 // X1 = float(int(x)) 14 | CMPSS X1, X0, 1 // compare LT; X0 = 0xffffffffffffffff or 0 15 | MOVSS $(-1.0), X2 16 | ANDPS X2, X0 // if x < float(int(x)) {X0 = -1} else {X0 = 0} 17 | ADDSS X1, X0 18 | MOVSS X0, ret+4(FP) 19 | RET 20 | 21 | //////////////////////////////////////////////////////////////////////////////// 22 | -------------------------------------------------------------------------------- /x/math32/floor_amd64.s: -------------------------------------------------------------------------------- 1 | // Based on code from the Go standard library. 2 | // Copyright 2009 The Go Authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the ORIGINAL_LICENSE file. 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // func Floor(s float32) float32 9 | TEXT ·Floor(SB),7,$0 10 | MOVL x+0(FP), AX 11 | MOVL AX, X0 // X0 = x 12 | CVTTSS2SL X0, AX // AX = int(x) 13 | CVTSL2SS AX, X1 // X1 = float(int(x)) 14 | CMPSS X1, X0, 1 // compare LT; X0 = 0xffffffffffffffff or 0 15 | MOVSS $(-1.0), X2 16 | ANDPS X2, X0 // if x < float(int(x)) {X0 = -1} else {X0 = 0} 17 | ADDSS X1, X0 18 | MOVSS X0, ret+8(FP) 19 | RET 20 | 21 | //////////////////////////////////////////////////////////////////////////////// 22 | -------------------------------------------------------------------------------- /x/math32/floor_arm.s: -------------------------------------------------------------------------------- 1 | // Based on code from the Go standard library. 2 | // Copyright 2009 The Go Authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the ORIGINAL_LICENSE file. 5 | 6 | //------------------------------------------------------------------------------ 7 | 8 | // func Floor(s float32) float32 9 | TEXT ·Floor(SB),7,$0 10 | B ·floor(SB) 11 | 12 | //------------------------------------------------------------------------------ 13 | -------------------------------------------------------------------------------- /x/math32/floor_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package math32 5 | 6 | import ( 7 | "math" 8 | "testing" 9 | ) 10 | 11 | //////////////////////////////////////////////////////////////////////////////// 12 | 13 | func TestFloor(t *testing.T) { 14 | a := Floor(float32(3.3)) 15 | if a != 3 { 16 | t.Errorf("Wrong result for Floor(3.3): %v %T", a, a) 17 | } 18 | b := Floor(float32(-3.3)) 19 | if b != -4 { 20 | t.Errorf("Wrong result for Floor(-3.3): %v %T", b, b) 21 | } 22 | c := Floor(float32(3)) 23 | if c != 3 { 24 | t.Errorf("Wrong result for Floor(3): %v %T", c, c) 25 | } 26 | d := Floor(float32(-3)) 27 | if d != -3 { 28 | t.Errorf("Wrong result for Floor(-3): %v %T", d, d) 29 | } 30 | } 31 | 32 | //////////////////////////////////////////////////////////////////////////////// 33 | 34 | func BenchmarkFloor_math(b *testing.B) { 35 | x := float64(3.3) 36 | y := float64(-3.3) 37 | for i := 0; i < b.N; i++ { 38 | result64 = math.Floor(x) 39 | result64 = math.Floor(y) 40 | } 41 | } 42 | 43 | //////////////////////////////////////////////////////////////////////////////// 44 | 45 | func BenchmarkFloor_float32math(b *testing.B) { 46 | x := float32(3.3) 47 | y := float32(-3.3) 48 | for i := 0; i < b.N; i++ { 49 | result = float32(math.Floor(float64(x))) 50 | result = float32(math.Floor(float64(y))) 51 | } 52 | } 53 | 54 | //////////////////////////////////////////////////////////////////////////////// 55 | 56 | func BenchmarkFloor_glam(b *testing.B) { 57 | x := float32(3.3) 58 | y := float32(-3.3) 59 | for i := 0; i < b.N; i++ { 60 | result = Floor(x) 61 | result = Floor(y) 62 | } 63 | } 64 | 65 | //////////////////////////////////////////////////////////////////////////////// 66 | -------------------------------------------------------------------------------- /x/math32/frexp.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package math32 5 | 6 | // Frexp breaks f into a normalized fraction 7 | // and an integral power of two. 8 | // It returns frac and exp satisfying f == frac × 2**exp, 9 | // with the absolute value of frac in the interval [½, 1). 10 | // 11 | // Special cases are: 12 | // Frexp(±0) = ±0, 0 13 | // Frexp(±Inf) = ±Inf, 0 14 | // Frexp(NaN) = NaN, 0 15 | func Frexp(f float32) (frac float32, exp int) { 16 | //TODO: asm? 17 | // special cases 18 | switch { 19 | case f == 0: 20 | return f, 0 // correctly return -0 21 | case IsInf(f, 0) || IsNaN(f): 22 | return f, 0 23 | } 24 | f, exp = normalize(f) 25 | x := Float32bits(f) 26 | exp += int((x>>shift)&mask) - bias + 1 27 | x &^= mask << shift 28 | x |= (-1 + bias) << shift 29 | frac = Float32frombits(x) 30 | return 31 | } 32 | 33 | //////////////////////////////////////////////////////////////////////////////// 34 | -------------------------------------------------------------------------------- /x/math32/isalmostequal.go: -------------------------------------------------------------------------------- 1 | // Code partly based on: 2 | // http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ 3 | // Copyright Bruce Dawson 4 | // No restrictions. Also no warranty. Use it for what you will. 5 | // I’d recommend a link back to the article just so people will understand how it works. 6 | 7 | package math32 8 | 9 | //////////////////////////////////////////////////////////////////////////////// 10 | 11 | // IsAlmostEqual returns true if the difference between a and b in ULPs 12 | // (Unit in the Last Place) is less than ulps. 13 | // 14 | // Handle special cases: zero, infinites, denormals. 15 | // 16 | // See also IsNearlyEqual and IsRoughlyEqual. 17 | func IsAlmostEqual(a, b float32, ulps uint32) bool { 18 | 19 | diff := Abs(a - b) 20 | 21 | if a == b { 22 | // Shortcut, handles infinities. 23 | return true 24 | } 25 | if a == 0 || b == 0 || diff < SmallestNormalFloat32 { 26 | // a or b is zero or both are extremely close to it. 27 | // Relative error is less meaningful here. 28 | return diff < float32(ulps)*SmallestNonzeroFloat32 29 | } 30 | ua := Float32bits(a) 31 | ub := Float32bits(b) 32 | 33 | // Different signs means they do not match. 34 | if ua>>31 != ub>>31 { 35 | return false 36 | } 37 | 38 | // Find the difference in ULPs. 39 | var ulpsDiff uint32 40 | if ua > ub { 41 | ulpsDiff = ua - ub 42 | } else { 43 | ulpsDiff = ub - ua 44 | } 45 | 46 | return ulpsDiff < ulps 47 | } 48 | 49 | // Original algorithm: 50 | func isAlmostEqual(a, b float32, ulps uint32) bool { 51 | largest := a 52 | if b > a { 53 | largest = b 54 | } 55 | 56 | // Check if the numbers are really close -- needed 57 | // when comparing numbers near zero. 58 | if Abs(a-b) < largest*EpsilonFloat32 { 59 | return true 60 | } 61 | 62 | ua := Float32bits(a) 63 | ub := Float32bits(b) 64 | 65 | // Different signs means they do not match. 66 | if ua>>31 != ub>>31 { 67 | return false 68 | } 69 | 70 | // Find the difference in ULPs. 71 | var ulpsDiff uint32 72 | if ua > ub { 73 | ulpsDiff = ua - ub 74 | } else { 75 | ulpsDiff = ub - ua 76 | } 77 | 78 | return ulpsDiff < ulps 79 | } 80 | 81 | //////////////////////////////////////////////////////////////////////////////// 82 | -------------------------------------------------------------------------------- /x/math32/isnearlyequal.go: -------------------------------------------------------------------------------- 1 | // Based on code from http://floating-point-gui.de/errors/comparison/ 2 | // Copyright Published at floating-point-gui.de 3 | // under the Creative Commons Attribution License (BY) 4 | // http://creativecommons.org/licenses/by/3.0/ 5 | 6 | package math32 7 | 8 | //////////////////////////////////////////////////////////////////////////////// 9 | 10 | // IsNearlyEqual Returns true if the relative error between a and b is less 11 | // than epsilon. 12 | // 13 | // Handles special cases: zero, infinites, denormals. 14 | // 15 | // See also IsAlmostEqual and IsRoughlyEqual. 16 | func IsNearlyEqual(a, b float32, epsilon float32) bool { 17 | // Source: http://floating-point-gui.de/errors/comparison/ 18 | 19 | absA := Abs(a) 20 | absB := Abs(b) 21 | diff := Abs(a - b) 22 | 23 | if a == b { 24 | // Shortcut, handles infinities. 25 | return true 26 | } 27 | if a == 0 || b == 0 || diff < SmallestNormalFloat32 { 28 | // a or b is zero or both are extremely close to it. 29 | // Relative error is less meaningful here. 30 | return diff < epsilon*SmallestNormalFloat32 31 | } 32 | // Use relative error. 33 | // Note in the original source, absA+absB was used instead of largest 34 | largest := absA 35 | if absB > absA { 36 | largest = absB 37 | } 38 | return diff/largest < epsilon 39 | } 40 | 41 | //////////////////////////////////////////////////////////////////////////////// 42 | -------------------------------------------------------------------------------- /x/math32/isroughlyequal.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package math32 5 | 6 | // IsRoughlyEqual Returns true if the absolute error between a and b is less 7 | // than epsilon. 8 | // 9 | // See also IsNearlyEqual and IsAlmostEqual. 10 | func IsRoughlyEqual(a, b float32, epsilon float32) bool { 11 | if a == b { 12 | // Shortcut, handles infinities 13 | return true 14 | } 15 | // Use absolute error 16 | return Abs(a-b) < epsilon 17 | } 18 | 19 | //////////////////////////////////////////////////////////////////////////////// 20 | -------------------------------------------------------------------------------- /x/math32/ldexp.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package math32 5 | 6 | // Ldexp is the inverse of Frexp. 7 | // It returns frac × 2**exp. 8 | // 9 | // Special cases are: 10 | // Ldexp(±0, exp) = ±0 11 | // Ldexp(±Inf, exp) = ±Inf 12 | // Ldexp(NaN, exp) = NaN 13 | func Ldexp(frac float32, exp int) float32 { 14 | //TODO: asm? 15 | // special cases 16 | switch { 17 | case frac == 0: 18 | return frac // correctly return -0 19 | case IsInf(frac, 0) || IsNaN(frac): 20 | return frac 21 | } 22 | frac, e := normalize(frac) 23 | exp += e 24 | x := Float32bits(frac) 25 | exp += int(x>>shift)&mask - bias 26 | //TODO: convert to 32bit 27 | // if exp < -1074 { 28 | // return Copysign(0, frac) // underflow 29 | // } 30 | // if exp > 1023 { // overflow 31 | // if frac < 0 { 32 | // return Inf(-1) 33 | // } 34 | // return Inf(1) 35 | // } 36 | var m float32 = 1 37 | // if exp < -1022 { // denormal 38 | // exp += 52 39 | // m = 1.0 / (1 << 52) // 2**-52 40 | // } 41 | x &^= mask << shift 42 | x |= uint32(exp+bias) << shift 43 | return m * Float32frombits(x) 44 | } 45 | 46 | //////////////////////////////////////////////////////////////////////////////// 47 | -------------------------------------------------------------------------------- /x/math32/mix.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package math32 5 | 6 | // Mix returns the linear interpolation between x and y using a to weight 7 | // between them. 8 | // The value is computed as follows: x*(1-a) + y*a 9 | func Mix(x, y float32, a float32) float32 { 10 | return x*(1-a) + y*a 11 | } 12 | 13 | //////////////////////////////////////////////////////////////////////////////// 14 | -------------------------------------------------------------------------------- /x/math32/mix_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package math32 5 | 6 | import ( 7 | // "math" 8 | // "testing" 9 | ) 10 | 11 | //TODO 12 | 13 | //////////////////////////////////////////////////////////////////////////////// 14 | -------------------------------------------------------------------------------- /x/math32/mod.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package math32 5 | 6 | // Mod returns the floating-point remainder of x/y. 7 | // The magnitude of the result is less than y and its 8 | // sign agrees with that of x. 9 | // 10 | // Special cases are: 11 | // Mod(±Inf, y) = NaN 12 | // Mod(NaN, y) = NaN 13 | // Mod(x, 0) = NaN 14 | // Mod(x, ±Inf) = x 15 | // Mod(x, NaN) = NaN 16 | func Mod(x, y float32) float32 { 17 | //TODO: asm? 18 | if y == 0 || IsInf(x, 0) || IsNaN(x) || IsNaN(y) { 19 | return NaN() 20 | } 21 | if y < 0 { 22 | y = -y 23 | } 24 | 25 | yfr, yexp := Frexp(y) 26 | sign := false 27 | r := x 28 | if x < 0 { 29 | r = -x 30 | sign = true 31 | } 32 | 33 | for r >= y { 34 | rfr, rexp := Frexp(r) 35 | if rfr < yfr { 36 | rexp = rexp - 1 37 | } 38 | r = r - Ldexp(y, rexp-yexp) 39 | } 40 | if sign { 41 | r = -r 42 | } 43 | return r 44 | } 45 | 46 | //////////////////////////////////////////////////////////////////////////////// 47 | -------------------------------------------------------------------------------- /x/math32/modf.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package math32 5 | 6 | // Modf returns integer and fractional floating-point numbers 7 | // that sum to f. Both values have the same sign as f. 8 | // 9 | // Special cases are: 10 | // Modf(±Inf) = ±Inf, NaN 11 | // Modf(NaN) = NaN, NaN 12 | func Modf(f float32) (int float32, frac float32) { 13 | //TODO: asm? 14 | if f < 1 { 15 | switch { 16 | case f < 0: 17 | int, frac = Modf(-f) 18 | return -int, -frac 19 | case f == 0: 20 | return f, f // Return -0, -0 when f == -0 21 | } 22 | return 0, f 23 | } 24 | 25 | x := Float32bits(f) 26 | e := uint(x>>shift)&mask - bias 27 | 28 | // Keep the top 12+e bits, the integer part; clear the rest. 29 | if e < 32-9 { 30 | x &^= 1<<(32-9-e) - 1 31 | } 32 | int = Float32frombits(x) 33 | frac = f - int 34 | return 35 | } 36 | 37 | //////////////////////////////////////////////////////////////////////////////// 38 | -------------------------------------------------------------------------------- /x/math32/round.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package math32 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Round returns the nearest integer to x. 9 | func Round(x float32) int32 { 10 | if x > 0 { 11 | return int32(x + 0.5) 12 | } 13 | return int32(x - 0.5) 14 | } 15 | 16 | //////////////////////////////////////////////////////////////////////////////// 17 | -------------------------------------------------------------------------------- /x/math32/round_386.s: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | // SLOWER than the Go function 7 | // func roundAsm(s float32) float32 8 | TEXT ·roundAsm(SB),7,$0 9 | CVTSS2SL x+0(FP), BX 10 | MOVL BX, ret+4(FP) 11 | RET 12 | 13 | //////////////////////////////////////////////////////////////////////////////// 14 | -------------------------------------------------------------------------------- /x/math32/round_amd64.s: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | // SLOWER than the Go function 7 | // func round_asm(s float32) float32 8 | TEXT ·roundAsm(SB),7,$0 9 | CVTSS2SL s+0(FP), BX 10 | MOVL BX, ret+8(FP) 11 | RET 12 | 13 | //////////////////////////////////////////////////////////////////////////////// 14 | -------------------------------------------------------------------------------- /x/math32/round_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package math32 5 | 6 | import ( 7 | "testing" 8 | ) 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | func TestRound(t *testing.T) { 13 | a := Round(float32(3.3)) 14 | if a != 3 { 15 | t.Errorf("Wrong result for Round(3.3): %v %T", a, a) 16 | } 17 | b := Round(float32(-3.3)) 18 | if b != -3 { 19 | t.Errorf("Wrong result for Round(-3.3): %v %T", b, b) 20 | } 21 | c := Round(float32(3)) 22 | if c != 3 { 23 | t.Errorf("Wrong result for Round(3): %v %T", c, c) 24 | } 25 | d := Round(float32(-3)) 26 | if d != -3 { 27 | t.Errorf("Wrong result for Round(-3): %v %T", d, d) 28 | } 29 | e := Round(float32(3.7)) 30 | if e != 4 { 31 | t.Errorf("Wrong result for Round(3.7): %v %T", e, e) 32 | } 33 | f := Round(float32(-3.7)) 34 | if f != -4 { 35 | t.Errorf("Wrong result for Round(-3.7): %v %T", f, f) 36 | } 37 | } 38 | 39 | //////////////////////////////////////////////////////////////////////////////// 40 | 41 | func roundCast(x float32) int32 { 42 | if x > 0 { 43 | return int32(x + 0.5) 44 | } 45 | return int32(x - 0.5) 46 | } 47 | 48 | func BenchmarkRound_cast(b *testing.B) { 49 | x := float32(3.3) 50 | y := float32(-3.3) 51 | for i := 0; i < b.N; i++ { 52 | resultInt32 = roundCast(x) 53 | resultInt32 = roundCast(y) 54 | } 55 | } 56 | 57 | //////////////////////////////////////////////////////////////////////////////// 58 | 59 | func roundAsm(s float32) float32 60 | 61 | func BenchmarkRound_asm(b *testing.B) { 62 | x := float32(3.3) 63 | y := float32(-3.3) 64 | for i := 0; i < b.N; i++ { 65 | result = roundAsm(x) 66 | result = roundAsm(y) 67 | } 68 | } 69 | 70 | //////////////////////////////////////////////////////////////////////////////// 71 | 72 | func BenchmarkRound_glam(b *testing.B) { 73 | x := float32(3.3) 74 | y := float32(-3.3) 75 | for i := 0; i < b.N; i++ { 76 | resultInt32 = Round(x) 77 | resultInt32 = Round(y) 78 | } 79 | } 80 | 81 | //////////////////////////////////////////////////////////////////////////////// 82 | -------------------------------------------------------------------------------- /x/math32/sqrt.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package math32 5 | 6 | import ( 7 | "math" 8 | ) 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | // Sqrt returns the square root of x. 13 | func Sqrt(x float32) float32 14 | 15 | func sqrt(x float32) float32 { 16 | return float32(math.Sqrt(float64(x))) 17 | } 18 | -------------------------------------------------------------------------------- /x/math32/sqrt_386.s: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | // Based on code from the Go standard library. 5 | // Copyright 2009 The Go Authors. All rights reserved. 6 | // Use of this source code is governed by a BSD-style 7 | // license that can be found in the ORIGINAL_LICENSE file. 8 | 9 | //////////////////////////////////////////////////////////////////////////////// 10 | 11 | // func Sqrt(x float32) float32 12 | TEXT ·Sqrt(SB),7,$0 13 | FMOVF x+0(FP),F0 14 | FSQRT 15 | FMOVFP F0,ret+4(FP) 16 | RET 17 | 18 | //////////////////////////////////////////////////////////////////////////////// 19 | -------------------------------------------------------------------------------- /x/math32/sqrt_amd64.s: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | // Based on code from the Go standard library. 5 | // Copyright 2009 The Go Authors. All rights reserved. 6 | // Use of this source code is governed by a BSD-style 7 | // license that can be found in the ORIGINAL_LICENSE file. 8 | 9 | #include "textflag.h" 10 | 11 | //////////////////////////////////////////////////////////////////////////////// 12 | 13 | // func Sqrt(x float32) float32 14 | TEXT ·Sqrt(SB), NOSPLIT, $0 15 | XORPS X0, X0 // break dependency 16 | SQRTSS x+0(FP), X0 17 | MOVSS X0, ret+8(FP) 18 | RET 19 | 20 | //////////////////////////////////////////////////////////////////////////////// 21 | -------------------------------------------------------------------------------- /x/math32/sqrt_arm.s: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | // Based on code from the Go standard library. 5 | // Copyright 2009 The Go Authors. All rights reserved. 6 | // Use of this source code is governed by a BSD-style 7 | // license that can be found in the ORIGINAL_LICENSE file. 8 | 9 | //------------------------------------------------------------------------------ 10 | 11 | // func Sqrt(x float32) float32 12 | TEXT ·Sqrt(SB),7,$0 13 | B ·sqrt(SB) 14 | 15 | //------------------------------------------------------------------------------ 16 | -------------------------------------------------------------------------------- /x/math32/unsafe.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the ORIGINAL_LICENSE file. 4 | 5 | package math32 6 | 7 | //////////////////////////////////////////////////////////////////////////////// 8 | 9 | import "unsafe" 10 | 11 | //////////////////////////////////////////////////////////////////////////////// 12 | 13 | // Float32bits returns the IEEE 754 binary representation of f. 14 | func Float32bits(f float32) uint32 { return *(*uint32)(unsafe.Pointer(&f)) } 15 | 16 | // Float32frombits returns the floating point number corresponding 17 | // to the IEEE 754 binary representation b. 18 | func Float32frombits(b uint32) float32 { return *(*float32)(unsafe.Pointer(&b)) } 19 | 20 | // Float64bits returns the IEEE 754 binary representation of f. 21 | func Float64bits(f float64) uint64 { return *(*uint64)(unsafe.Pointer(&f)) } 22 | 23 | // Float64frombits returns the floating point number corresponding 24 | // the IEEE 754 binary representation b. 25 | func Float64frombits(b uint64) float64 { return *(*float64)(unsafe.Pointer(&b)) } 26 | 27 | //////////////////////////////////////////////////////////////////////////////// 28 | -------------------------------------------------------------------------------- /x/poly/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | // Package poly will provide a 3D renderer to display polygonal art (aka 5 | // low-poly). 6 | // 7 | // Note: the rendrer is not yet functional. 8 | package poly 9 | -------------------------------------------------------------------------------- /x/poly/error.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package poly 5 | 6 | import ( 7 | "github.com/cozely/cozely/internal" 8 | ) 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | var stickyErr error 13 | 14 | func init() { 15 | internal.PolyErr = func() error { 16 | return stickyErr 17 | } 18 | } 19 | 20 | //////////////////////////////////////////////////////////////////////////////// 21 | 22 | // Err returns the first unchecked error of the package since last call to the 23 | // function. The error is then considered checked, and further calls to Err will 24 | // return nil until the next error occurs. 25 | // 26 | // Note: errors occuring while there already is an unchecked error will not be 27 | // recorded. However, if the debug mode is active, all errors will be logged. 28 | func Err() error { 29 | err := stickyErr 30 | stickyErr = nil 31 | return err 32 | } 33 | 34 | func setErr(err error) { 35 | if stickyErr == nil { 36 | stickyErr = err 37 | } 38 | internal.Debug.Printf("*** ERROR in package poly ***\n%s", err) 39 | } 40 | 41 | -------------------------------------------------------------------------------- /x/poly/lights.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package poly 5 | 6 | import ( 7 | "math" 8 | 9 | "github.com/cozely/cozely/color" 10 | "github.com/cozely/cozely/x/math32" 11 | ) 12 | 13 | //////////////////////////////////////////////////////////////////////////////// 14 | 15 | func TemperatureColor(temperature float64) color.LRGB { 16 | // Ported by Renaud Bédard (@renaudbedard), from original code 17 | // by Tanner Helland: 18 | // http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/ 19 | // https://www.shadertoy.com/view/lsSXW1 20 | // licensed and released under Creative Commons 3.0 Attribution 21 | // https://creativecommons.org/licenses/by/3.0/ 22 | 23 | var h color.LRGB 24 | 25 | temperature = clamp(temperature, 1000.0, 40000.0) / 100.0 26 | 27 | if temperature <= 66.0 { 28 | h.R = 1.0 29 | h.G = float32(saturate( 30 | 0.39008157876901960784*math.Log(temperature) - 0.63184144378862745098, 31 | )) 32 | } else { 33 | t := temperature - 60.0 34 | h.R = float32(saturate(1.29293618606274509804 * math.Pow(t, -0.1332047592))) 35 | h.G = float32(saturate(1.12989086089529411765 * math.Pow(t, -0.0755148492))) 36 | } 37 | 38 | switch { 39 | case temperature >= 66.0: 40 | h.B = 1.0 41 | case temperature <= 19.0: 42 | h.B = 0.0 43 | default: 44 | h.B = float32(saturate( 45 | 0.54320678911019607843*math.Log(temperature-10.0) - 1.19625408914, 46 | )) 47 | } 48 | 49 | return h 50 | } 51 | 52 | //////////////////////////////////////////////////////////////////////////////// 53 | 54 | func DirectionalLightSpectralIlluminance(illuminance, temperature float64) color.LRGB { 55 | h := TemperatureColor(temperature) 56 | s := float32(illuminance) * (1.0 / math32.Pi) 57 | h.R *= s 58 | h.G *= s 59 | h.B *= s 60 | 61 | return h 62 | } 63 | 64 | //////////////////////////////////////////////////////////////////////////////// 65 | -------------------------------------------------------------------------------- /x/poly/palette.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package poly 5 | 6 | import ( 7 | "github.com/cozely/cozely/color" 8 | "github.com/cozely/cozely/x/gl" 9 | ) 10 | 11 | //////////////////////////////////////////////////////////////////////////////// 12 | 13 | func SetupMaterialBuffer(p []Material) error { 14 | materialSSBO.Delete() 15 | return nil 16 | } 17 | 18 | //////////////////////////////////////////////////////////////////////////////// 19 | 20 | func BindMaterialBuffer() { 21 | materialSSBO.Bind(0) 22 | } 23 | 24 | //////////////////////////////////////////////////////////////////////////////// 25 | 26 | func DeleteMaterialBuffer() { 27 | materialSSBO.Delete() 28 | } 29 | 30 | //////////////////////////////////////////////////////////////////////////////// 31 | 32 | var materialSSBO gl.StorageBuffer 33 | 34 | type Material struct { 35 | diffuse color.LRGB 36 | } 37 | 38 | //////////////////////////////////////////////////////////////////////////////// 39 | -------------------------------------------------------------------------------- /x/poly/pipeline.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package poly 5 | 6 | import ( 7 | "strings" 8 | 9 | "github.com/cozely/cozely/x/gl" 10 | ) 11 | 12 | //////////////////////////////////////////////////////////////////////////////// 13 | 14 | func PipelineSetup() gl.PipelineConfig { 15 | return func(p *gl.Pipeline) { 16 | gl.VertexShader(strings.NewReader(vertshader))(p) 17 | gl.Topology(gl.Triangles)(p) 18 | gl.CullFace(false, true)(p) 19 | gl.DepthTest(true)(p) 20 | } 21 | } 22 | 23 | //////////////////////////////////////////////////////////////////////////////// 24 | -------------------------------------------------------------------------------- /x/poly/references.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package poly 5 | 6 | import "github.com/cozely/cozely/color" 7 | 8 | //////////////////////////////////////////////////////////////////////////////// 9 | 10 | var ( 11 | Gold = color.LRGB{1.000000, 0.765557, 0.336057} 12 | Silver = color.LRGB{0.971519, 0.959915, 0.915324} 13 | Aluminium = color.LRGB{0.913183, 0.921494, 0.924524} 14 | Copper = color.LRGB{0.955008, 0.637427, 0.538163} 15 | Chromium = color.LRGB{0.549585, 0.556114, 0.554256} 16 | Nickel = color.LRGB{0.659777, 0.608679, 0.525649} 17 | Titanium = color.LRGB{0.541931, 0.496791, 0.449419} 18 | Cobalt = color.LRGB{0.662124, 0.654864, 0.633732} 19 | Platinum = color.LRGB{0.672411, 0.637331, 0.585456} 20 | Iron = color.LRGB{0.560, 0.570, 0.580} 21 | ) 22 | 23 | //////////////////////////////////////////////////////////////////////////////// 24 | -------------------------------------------------------------------------------- /x/poly/testdata/cube.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.78 (sub 0) OBJ File: '' 2 | # www.blender.org 3 | o cube 4 | v -1.000000 -1.000000 1.000000 5 | v -1.000000 1.000000 1.000000 6 | v -1.000000 -1.000000 -1.000000 7 | v -1.000000 1.000000 -1.000000 8 | v 1.000000 -1.000000 1.000000 9 | v 1.000000 1.000000 1.000000 10 | v 1.000000 -1.000000 -1.000000 11 | v 1.000000 1.000000 -1.000000 12 | usemtl 0 13 | s off 14 | f 5 6 2 1 15 | usemtl 1 16 | f 3 4 8 7 17 | usemtl 2 18 | f 8 4 2 6 19 | usemtl 3 20 | f 3 7 5 1 21 | usemtl 4 22 | f 1 2 4 3 23 | usemtl 5 24 | f 7 8 6 5 25 | -------------------------------------------------------------------------------- /x/poly/testdata/init.json: -------------------------------------------------------------------------------- 1 | { 2 | "Display": 0, 3 | "Fullscreen": false, 4 | "FullscreenMode": "Desktop", 5 | "VSync": true, 6 | "Debug": true 7 | } 8 | -------------------------------------------------------------------------------- /x/poly/testdata/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "Default": { 3 | "Quit": ["Escape"], 4 | "Rotate": ["Mouse Right"], 5 | "Move": ["Mouse Left"], 6 | "Rotation": ["Mouse", "Right Stick"], 7 | "Onward": ["W", "Up"], 8 | "Left": ["A", "Left"], 9 | "Back": ["S", "Down"], 10 | "Right": ["D", "Right"], 11 | "Up": ["Space"], 12 | "Down": ["Left Shift"], 13 | "Roll Left": ["Q"], 14 | "Roll Right": ["E"], 15 | "Reset View": ["Mouse Back"], 16 | "Reset Object": ["Mouse Forward"], 17 | "Cursor": ["Mouse"] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /x/poly/testdata/shader.vert: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | void glam_PrepareVertex(); 4 | 5 | void main() { 6 | glam_PrepareVertex(); 7 | } 8 | -------------------------------------------------------------------------------- /x/poly/utils.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package poly 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | func clamp(v, min, max float64) float64 { 9 | switch { 10 | case v < min: 11 | return min 12 | case v > max: 13 | return max 14 | default: 15 | return v 16 | } 17 | } 18 | 19 | func saturate(v float64) float64 { 20 | return clamp(v, 0.0, 1.0) 21 | } 22 | 23 | //////////////////////////////////////////////////////////////////////////////// 24 | -------------------------------------------------------------------------------- /x/poly/z_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2018 Laurent Moussault. All rights reserved. 2 | // Licensed under a simplified BSD license (see LICENSE file). 3 | 4 | package poly_test 5 | 6 | import ( 7 | "os" 8 | "testing" 9 | ) 10 | 11 | //////////////////////////////////////////////////////////////////////////////// 12 | 13 | var tests = make(chan func()) 14 | 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | func do(f func()) { 18 | done := make(chan bool, 1) 19 | tests <- func() { 20 | f() 21 | done <- true 22 | } 23 | <-done 24 | } 25 | 26 | //////////////////////////////////////////////////////////////////////////////// 27 | 28 | func TestMain(m *testing.M) { 29 | result := make(chan int, 1) 30 | 31 | go func() { 32 | result <- m.Run() 33 | }() 34 | 35 | go func() { 36 | os.Exit(<-result) 37 | }() 38 | 39 | for f := range tests { 40 | f() 41 | } 42 | } 43 | 44 | //////////////////////////////////////////////////////////////////////////////// 45 | -------------------------------------------------------------------------------- /z_example_test.go: -------------------------------------------------------------------------------- 1 | package cozely_test 2 | 3 | import ( 4 | "math/rand" 5 | 6 | "github.com/cozely/cozely" 7 | "github.com/cozely/cozely/color" 8 | "github.com/cozely/cozely/input" 9 | "github.com/cozely/cozely/pixel" 10 | ) 11 | 12 | // Declarations //////////////////////////////////////////////////////////////// 13 | 14 | var ( 15 | play = input.Button("Play") 16 | ) 17 | 18 | type loop struct { 19 | logo pixel.PictureID 20 | monochrome color.Palette 21 | colorful color.Palette 22 | 23 | playing bool 24 | } 25 | 26 | // Initialization ////////////////////////////////////////////////////////////// 27 | 28 | func Example() { 29 | defer cozely.Recover() 30 | 31 | l := loop{} 32 | l.setup() 33 | 34 | cozely.Configure(cozely.UpdateStep(1.0 / 3)) 35 | err := cozely.Run(&l) 36 | if err != nil { 37 | panic(err) 38 | } 39 | // Output: 40 | } 41 | 42 | func (l *loop) setup() { 43 | pixel.SetResolution(pixel.XY{160, 100}) 44 | l.logo = pixel.Picture("graphics/cozely") 45 | l.monochrome = color.PaletteFrom("graphics/cozely") 46 | l.colorful = color.PaletteFrom("") 47 | } 48 | 49 | func (l *loop) Enter() { 50 | pixel.SetPalette(l.monochrome) 51 | } 52 | 53 | func (loop) Leave() { 54 | } 55 | 56 | // Game Loop /////////////////////////////////////////////////////////////////// 57 | 58 | func (l *loop) React() { 59 | if play.Pressed() { 60 | l.playing = !l.playing 61 | if l.playing { 62 | pixel.SetPalette(l.colorful) 63 | l.shufflecolors() 64 | } else { 65 | pixel.SetPalette(l.monochrome) 66 | } 67 | } 68 | if input.Close.Pressed() { 69 | cozely.Stop(nil) 70 | } 71 | } 72 | 73 | func (l *loop) Update() { 74 | if l.playing { 75 | l.shufflecolors() 76 | } 77 | } 78 | 79 | func (l *loop) shufflecolors() { 80 | for i := 2; i < 14; i++ { 81 | g := 0.2 + 0.7*rand.Float32() 82 | r := 0.2 + 0.7*rand.Float32() 83 | b := 0.2 + 0.7*rand.Float32() 84 | pixel.SetColor(color.Index(i), color.SRGB{r, g, b}) 85 | } 86 | } 87 | 88 | func (l *loop) Render() { 89 | pixel.Clear(0) 90 | 91 | o := pixel.Resolution().Minus(l.logo.Size()).Slash(2) 92 | l.logo.Paint(0, o) 93 | } 94 | --------------------------------------------------------------------------------