Browse Source

first import

leonsal 8 years ago
commit
3585b36aec
100 changed files with 23611 additions and 0 deletions
  1. 24 0
      LICENSE
  2. 150 0
      README.md
  3. 656 0
      audio/al/AL/al.h
  4. 237 0
      audio/al/AL/alc.h
  5. 438 0
      audio/al/AL/alext.h
  6. 3 0
      audio/al/AL/efx-creative.h
  7. 402 0
      audio/al/AL/efx-presets.h
  8. 761 0
      audio/al/AL/efx.h
  9. 913 0
      audio/al/al.go
  10. 752 0
      audio/al/loader.c
  11. 160 0
      audio/al/loader.h
  12. 292 0
      audio/audio_file.go
  13. 8 0
      audio/doc.go
  14. 79 0
      audio/listener.go
  15. 9 0
      audio/ov/build.go
  16. 346 0
      audio/ov/loader.c
  17. 103 0
      audio/ov/loader.h
  18. 210 0
      audio/ov/ogg/ogg.h
  19. 147 0
      audio/ov/ogg/os_types.h
  20. 243 0
      audio/ov/vorbis/codec.h
  21. 436 0
      audio/ov/vorbis/vorbisenc.h
  22. 206 0
      audio/ov/vorbis/vorbisfile.h
  23. 232 0
      audio/ov/vorbisfile.go
  24. 365 0
      audio/player.go
  25. 9 0
      audio/vorbis/build.go
  26. 111 0
      audio/vorbis/loader.c
  27. 28 0
      audio/vorbis/loader.h
  28. 35 0
      audio/vorbis/vorbis.go
  29. 94 0
      audio/wave.go
  30. 143 0
      camera/camera.go
  31. 510 0
      camera/control/orbit_control.go
  32. 12 0
      camera/logger.go
  33. 68 0
      camera/orthographic.go
  34. 136 0
      camera/perspective.go
  35. 14 0
      core/RenderInfo.go
  36. 137 0
      core/dispatcher.go
  37. 7 0
      core/doc.go
  38. 9 0
      core/logger.go
  39. 497 0
      core/node.go
  40. 116 0
      core/raycaster.go
  41. 121 0
      core/timer.go
  42. 120 0
      geometry/box.go
  43. 82 0
      geometry/circle.go
  44. 260 0
      geometry/cylinder.go
  45. 301 0
      geometry/geometry.go
  46. 12 0
      geometry/logger.go
  47. 78 0
      geometry/plane.go
  48. 90 0
      geometry/sphere.go
  49. 75 0
      geometry/torus.go
  50. 1927 0
      gls/consts.go
  51. 529 0
      gls/gls.go
  52. 539 0
      gls/program.go
  53. 401 0
      gls/uniform.go
  54. 148 0
      gls/vbo.go
  55. 47 0
      graphic/axis_helper.go
  56. 177 0
      graphic/graphic.go
  57. 51 0
      graphic/grid_helper.go
  58. 48 0
      graphic/line_strip.go
  59. 148 0
      graphic/lines.go
  60. 12 0
      graphic/logger.go
  61. 200 0
      graphic/mesh.go
  62. 96 0
      graphic/normals_helper.go
  63. 117 0
      graphic/points.go
  64. 90 0
      graphic/skybox.go
  65. 161 0
      graphic/sprite.go
  66. 18 0
      gui/align.go
  67. 306 0
      gui/assets/data.go
  68. 899 0
      gui/assets/icodes.go
  69. 259 0
      gui/button.go
  70. 276 0
      gui/checkradio.go
  71. 119 0
      gui/control_folder.go
  72. 7 0
      gui/doc.go
  73. 89 0
      gui/docklayout.go
  74. 280 0
      gui/dropdown.go
  75. 340 0
      gui/edit.go
  76. 29 0
      gui/events.go
  77. 37 0
      gui/filllayout.go
  78. 182 0
      gui/folder.go
  79. 135 0
      gui/gridlayout.go
  80. 185 0
      gui/hboxlayout.go
  81. 9 0
      gui/ilayout.go
  82. 51 0
      gui/image.go
  83. 236 0
      gui/imagelabel.go
  84. 197 0
      gui/label.go
  85. 531 0
      gui/list.go
  86. 12 0
      gui/logger.go
  87. 806 0
      gui/panel.go
  88. 350 0
      gui/root.go
  89. 209 0
      gui/scrollbar.go
  90. 611 0
      gui/scroller.go
  91. 327 0
      gui/slider.go
  92. 252 0
      gui/splitter.go
  93. 710 0
      gui/style.go
  94. 467 0
      gui/tree.go
  95. 46 0
      gui/util.go
  96. 186 0
      gui/vboxlayout.go
  97. 360 0
      gui/window.go
  98. 94 0
      hellog3n/main.go
  99. 68 0
      light/ambient.go
  100. 0 0
      light/directional.go

+ 24 - 0
LICENSE

@@ -0,0 +1,24 @@
+Copyright (c) 2016 The G3N Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 150 - 0
README.md

@@ -0,0 +1,150 @@
+# G3N Go 3D Game Engine
+
+G3N is a basic (for now!) OpenGL 3D Game Engine written in Go.
+G3N was heavily inspired and based on the [three.js](https://threejs.org/) Javascript 3D library.
+If you are curious about G3N and would like to see it in action
+we recommend that you install the [G3N demo program](https://github.com/g3n/g3nd).
+
+# Dependencies
+
+The engine needs an OpenGL driver installed in the system and
+on Unix like systems depends on some C libraries that must be installed.
+In all cases it is necessary to have a C compiler installed.
+
+* For Ubuntu/Debian-like Linux distributions, install `libgl1-mesa-dev` and `xorg-dev` packages.
+* For CentOS/Fedora-like Linux distributions, install `libX11-devel libXcursor-devel libXrandr-devel libXinerama-devel mesa-libGL-devel libXi-devel` packages.
+* Currently it was not tested on OS X. We encourage some feedback.
+* For Windows it is recommended to have MingGW vx.x.x installed.
+
+G3N supports spatial audio using external libraries, but G3N loads these libraries
+dinamically on demand, so you can install G3N and build a 3D application
+(not using audio) without installing these libraries.
+
+The following libraries are necessary for the optional audio support:
+
+* For Ubuntu/Debian-like Linux distributions, install `libopenal1` and `libvorbisfile3`
+* For CentOS/Fedora-like Linux distributions, install `libopenal1` and `libvorbisfile3`
+* Currently it was not tested on OS X. We encourage some feedback.
+* For Windows install `OpenAL32.dll` and ???
+
+G3N was only tested with Go1.7.4+
+
+# Installation
+
+go get -u github.com/g3n/engine
+
+# Features
+
+* Scene graph based
+* supports perspective and orthographic cameras.
+  Orbit control to move (zoom, rotate and pan) camera using the mouse or keyboard
+* supports perspective and orthographic cameras
+
+
+# Basic application
+
+The following code shows a minimum G3N application [hellog3n](https://github.com/g3n/engine/hellog3n)
+which shows a wireframed sphere rotating.
+
+
+```
+package main
+
+import (
+	"github.com/g3n/engine/camera"
+	"github.com/g3n/engine/core"
+	"github.com/g3n/engine/geometry"
+	"github.com/g3n/engine/gls"
+	"github.com/g3n/engine/graphic"
+	"github.com/g3n/engine/light"
+	"github.com/g3n/engine/material"
+	"github.com/g3n/engine/math32"
+	"github.com/g3n/engine/renderer"
+	"github.com/g3n/engine/window"
+	"math"
+	"runtime"
+)
+
+func main() {
+
+	// Creates window and OpenGL context
+	win, err := window.New("glfw", 800, 600, "Hello G3N", false)
+	if err != nil {
+		panic(err)
+	}
+
+	// OpenGL functions must be executed in the same thread where
+	// the context was created (by window.New())
+	runtime.LockOSThread()
+
+	// Create OpenGL state
+	gs, err := gls.New()
+	if err != nil {
+		panic(err)
+	}
+
+	// Creates scene for 3D objects
+	scene := core.NewNode()
+
+	// Adds white ambient light to the scene
+	ambLight := light.NewAmbient(&math32.Color{1.0, 1.0, 1.0}, 0.5)
+	scene.Add(ambLight)
+
+	// Adds a perspective camera to the scene
+	width, height := win.GetSize()
+	aspect := float32(width) / float32(height)
+	camera := camera.NewPerspective(65, aspect, 0.01, 1000)
+	camera.SetPosition(0, 0, 5)
+
+	// Add an axis helper
+	axis := graphic.NewAxisHelper(2)
+	scene.Add(axis)
+
+	// Creates a wireframe sphere positioned at the center of the scene
+	geom := geometry.NewSphere(2, 16, 16, 0, math.Pi*2, 0, math.Pi)
+	mat := material.NewStandard(math32.NewColor(1, 1, 1))
+	mat.SetSide(material.SideDouble)
+	mat.SetWireframe(true)
+	sphere := graphic.NewMesh(geom, mat)
+	scene.Add(sphere)
+
+	// Creates a renderer and adds default shaders
+	rend := renderer.NewRenderer(gs)
+	err = rend.AddDefaultShaders()
+	if err != nil {
+		panic(err)
+	}
+
+	// Sets window background color
+	gs.ClearColor(0, 0, 0, 1.0)
+
+	// Render loop
+	for !win.ShouldClose() {
+
+		// Clear buffers
+		gs.Clear(gls.DEPTH_BUFFER_BIT | gls.STENCIL_BUFFER_BIT | gls.COLOR_BUFFER_BIT)
+
+		// Rotates the sphere a bit around the Z axis (up)
+		sphere.AddRotationY(0.005)
+
+		// Render the scene using the specified camera
+		rend.Render(scene, camera)
+
+		// Update window and checks for I/O events
+		win.SwapBuffers()
+		win.PollEvents()
+	}
+}
+```
+
+# Documentation
+
+* For the engine API reference, please see https://godoc.org/github.com/g3n/engine
+* For
+
+# Contributing
+
+If you spot a bug or create a new feature you are encouraged to
+send pull requests.
+
+

+ 656 - 0
audio/al/AL/al.h

@@ -0,0 +1,656 @@
+#ifndef AL_AL_H
+#define AL_AL_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#ifndef AL_API
+ #if defined(AL_LIBTYPE_STATIC)
+  #define AL_API
+ #elif defined(_WIN32)
+  #define AL_API __declspec(dllimport)
+ #else
+  #define AL_API extern
+ #endif
+#endif
+
+#if defined(_WIN32)
+ #define AL_APIENTRY __cdecl
+#else
+ #define AL_APIENTRY
+#endif
+
+
+/** Deprecated macro. */
+#define OPENAL
+#define ALAPI                                    AL_API
+#define ALAPIENTRY                               AL_APIENTRY
+#define AL_INVALID                               (-1)
+#define AL_ILLEGAL_ENUM                          AL_INVALID_ENUM
+#define AL_ILLEGAL_COMMAND                       AL_INVALID_OPERATION
+
+/** Supported AL version. */
+#define AL_VERSION_1_0
+#define AL_VERSION_1_1
+
+/** 8-bit boolean */
+typedef char ALboolean;
+
+/** character */
+typedef char ALchar;
+
+/** signed 8-bit 2's complement integer */
+typedef signed char ALbyte;
+
+/** unsigned 8-bit integer */
+typedef unsigned char ALubyte;
+
+/** signed 16-bit 2's complement integer */
+typedef short ALshort;
+
+/** unsigned 16-bit integer */
+typedef unsigned short ALushort;
+
+/** signed 32-bit 2's complement integer */
+typedef int ALint;
+
+/** unsigned 32-bit integer */
+typedef unsigned int ALuint;
+
+/** non-negative 32-bit binary integer size */
+typedef int ALsizei;
+
+/** enumerated 32-bit value */
+typedef int ALenum;
+
+/** 32-bit IEEE754 floating-point */
+typedef float ALfloat;
+
+/** 64-bit IEEE754 floating-point */
+typedef double ALdouble;
+
+/** void type (for opaque pointers only) */
+typedef void ALvoid;
+
+
+/* Enumerant values begin at column 50. No tabs. */
+
+/** "no distance model" or "no buffer" */
+#define AL_NONE                                  0
+
+/** Boolean False. */
+#define AL_FALSE                                 0
+
+/** Boolean True. */
+#define AL_TRUE                                  1
+
+
+/**
+ * Relative source.
+ * Type:    ALboolean
+ * Range:   [AL_TRUE, AL_FALSE]
+ * Default: AL_FALSE
+ *
+ * Specifies if the Source has relative coordinates.
+ */
+#define AL_SOURCE_RELATIVE                       0x202
+
+
+/**
+ * Inner cone angle, in degrees.
+ * Type:    ALint, ALfloat
+ * Range:   [0 - 360]
+ * Default: 360
+ *
+ * The angle covered by the inner cone, where the source will not attenuate.
+ */
+#define AL_CONE_INNER_ANGLE                      0x1001
+
+/**
+ * Outer cone angle, in degrees.
+ * Range:   [0 - 360]
+ * Default: 360
+ *
+ * The angle covered by the outer cone, where the source will be fully
+ * attenuated.
+ */
+#define AL_CONE_OUTER_ANGLE                      0x1002
+
+/**
+ * Source pitch.
+ * Type:    ALfloat
+ * Range:   [0.5 - 2.0]
+ * Default: 1.0
+ *
+ * A multiplier for the frequency (sample rate) of the source's buffer.
+ */
+#define AL_PITCH                                 0x1003
+
+/**
+ * Source or listener position.
+ * Type:    ALfloat[3], ALint[3]
+ * Default: {0, 0, 0}
+ *
+ * The source or listener location in three dimensional space.
+ *
+ * OpenAL, like OpenGL, uses a right handed coordinate system, where in a
+ * frontal default view X (thumb) points right, Y points up (index finger), and
+ * Z points towards the viewer/camera (middle finger).
+ *
+ * To switch from a left handed coordinate system, flip the sign on the Z
+ * coordinate.
+ */
+#define AL_POSITION                              0x1004
+
+/**
+ * Source direction.
+ * Type:    ALfloat[3], ALint[3]
+ * Default: {0, 0, 0}
+ *
+ * Specifies the current direction in local space.
+ * A zero-length vector specifies an omni-directional source (cone is ignored).
+ */
+#define AL_DIRECTION                             0x1005
+
+/**
+ * Source or listener velocity.
+ * Type:    ALfloat[3], ALint[3]
+ * Default: {0, 0, 0}
+ *
+ * Specifies the current velocity in local space.
+ */
+#define AL_VELOCITY                              0x1006
+
+/**
+ * Source looping.
+ * Type:    ALboolean
+ * Range:   [AL_TRUE, AL_FALSE]
+ * Default: AL_FALSE
+ *
+ * Specifies whether source is looping.
+ */
+#define AL_LOOPING                               0x1007
+
+/**
+ * Source buffer.
+ * Type:  ALuint
+ * Range: any valid Buffer.
+ *
+ * Specifies the buffer to provide sound samples.
+ */
+#define AL_BUFFER                                0x1009
+
+/**
+ * Source or listener gain.
+ * Type:  ALfloat
+ * Range: [0.0 - ]
+ *
+ * A value of 1.0 means unattenuated. Each division by 2 equals an attenuation
+ * of about -6dB. Each multiplicaton by 2 equals an amplification of about
+ * +6dB.
+ *
+ * A value of 0.0 is meaningless with respect to a logarithmic scale; it is
+ * silent.
+ */
+#define AL_GAIN                                  0x100A
+
+/**
+ * Minimum source gain.
+ * Type:  ALfloat
+ * Range: [0.0 - 1.0]
+ *
+ * The minimum gain allowed for a source, after distance and cone attenation is
+ * applied (if applicable).
+ */
+#define AL_MIN_GAIN                              0x100D
+
+/**
+ * Maximum source gain.
+ * Type:  ALfloat
+ * Range: [0.0 - 1.0]
+ *
+ * The maximum gain allowed for a source, after distance and cone attenation is
+ * applied (if applicable).
+ */
+#define AL_MAX_GAIN                              0x100E
+
+/**
+ * Listener orientation.
+ * Type: ALfloat[6]
+ * Default: {0.0, 0.0, -1.0, 0.0, 1.0, 0.0}
+ *
+ * Effectively two three dimensional vectors. The first vector is the front (or
+ * "at") and the second is the top (or "up").
+ *
+ * Both vectors are in local space.
+ */
+#define AL_ORIENTATION                           0x100F
+
+/**
+ * Source state (query only).
+ * Type:  ALint
+ * Range: [AL_INITIAL, AL_PLAYING, AL_PAUSED, AL_STOPPED]
+ */
+#define AL_SOURCE_STATE                          0x1010
+
+/** Source state value. */
+#define AL_INITIAL                               0x1011
+#define AL_PLAYING                               0x1012
+#define AL_PAUSED                                0x1013
+#define AL_STOPPED                               0x1014
+
+/**
+ * Source Buffer Queue size (query only).
+ * Type: ALint
+ *
+ * The number of buffers queued using alSourceQueueBuffers, minus the buffers
+ * removed with alSourceUnqueueBuffers.
+ */
+#define AL_BUFFERS_QUEUED                        0x1015
+
+/**
+ * Source Buffer Queue processed count (query only).
+ * Type: ALint
+ *
+ * The number of queued buffers that have been fully processed, and can be
+ * removed with alSourceUnqueueBuffers.
+ *
+ * Looping sources will never fully process buffers because they will be set to
+ * play again for when the source loops.
+ */
+#define AL_BUFFERS_PROCESSED                     0x1016
+
+/**
+ * Source reference distance.
+ * Type:    ALfloat
+ * Range:   [0.0 - ]
+ * Default: 1.0
+ *
+ * The distance in units that no attenuation occurs.
+ *
+ * At 0.0, no distance attenuation ever occurs on non-linear attenuation models.
+ */
+#define AL_REFERENCE_DISTANCE                    0x1020
+
+/**
+ * Source rolloff factor.
+ * Type:    ALfloat
+ * Range:   [0.0 - ]
+ * Default: 1.0
+ *
+ * Multiplier to exaggerate or diminish distance attenuation.
+ *
+ * At 0.0, no distance attenuation ever occurs.
+ */
+#define AL_ROLLOFF_FACTOR                        0x1021
+
+/**
+ * Outer cone gain.
+ * Type:    ALfloat
+ * Range:   [0.0 - 1.0]
+ * Default: 0.0
+ *
+ * The gain attenuation applied when the listener is outside of the source's
+ * outer cone.
+ */
+#define AL_CONE_OUTER_GAIN                       0x1022
+
+/**
+ * Source maximum distance.
+ * Type:    ALfloat
+ * Range:   [0.0 - ]
+ * Default: +inf
+ *
+ * The distance above which the source is not attenuated any further with a
+ * clamped distance model, or where attenuation reaches 0.0 gain for linear
+ * distance models with a default rolloff factor.
+ */
+#define AL_MAX_DISTANCE                          0x1023
+
+/** Source buffer position, in seconds */
+#define AL_SEC_OFFSET                            0x1024
+/** Source buffer position, in sample frames */
+#define AL_SAMPLE_OFFSET                         0x1025
+/** Source buffer position, in bytes */
+#define AL_BYTE_OFFSET                           0x1026
+
+/**
+ * Source type (query only).
+ * Type:  ALint
+ * Range: [AL_STATIC, AL_STREAMING, AL_UNDETERMINED]
+ *
+ * A Source is Static if a Buffer has been attached using AL_BUFFER.
+ *
+ * A Source is Streaming if one or more Buffers have been attached using
+ * alSourceQueueBuffers.
+ *
+ * A Source is Undetermined when it has the NULL buffer attached using
+ * AL_BUFFER.
+ */
+#define AL_SOURCE_TYPE                           0x1027
+
+/** Source type value. */
+#define AL_STATIC                                0x1028
+#define AL_STREAMING                             0x1029
+#define AL_UNDETERMINED                          0x1030
+
+/** Buffer format specifier. */
+#define AL_FORMAT_MONO8                          0x1100
+#define AL_FORMAT_MONO16                         0x1101
+#define AL_FORMAT_STEREO8                        0x1102
+#define AL_FORMAT_STEREO16                       0x1103
+
+/** Buffer frequency (query only). */
+#define AL_FREQUENCY                             0x2001
+/** Buffer bits per sample (query only). */
+#define AL_BITS                                  0x2002
+/** Buffer channel count (query only). */
+#define AL_CHANNELS                              0x2003
+/** Buffer data size (query only). */
+#define AL_SIZE                                  0x2004
+
+/**
+ * Buffer state.
+ *
+ * Not for public use.
+ */
+#define AL_UNUSED                                0x2010
+#define AL_PENDING                               0x2011
+#define AL_PROCESSED                             0x2012
+
+
+/** No error. */
+#define AL_NO_ERROR                              0
+
+/** Invalid name paramater passed to AL call. */
+#define AL_INVALID_NAME                          0xA001
+
+/** Invalid enum parameter passed to AL call. */
+#define AL_INVALID_ENUM                          0xA002
+
+/** Invalid value parameter passed to AL call. */
+#define AL_INVALID_VALUE                         0xA003
+
+/** Illegal AL call. */
+#define AL_INVALID_OPERATION                     0xA004
+
+/** Not enough memory. */
+#define AL_OUT_OF_MEMORY                         0xA005
+
+
+/** Context string: Vendor ID. */
+#define AL_VENDOR                                0xB001
+/** Context string: Version. */
+#define AL_VERSION                               0xB002
+/** Context string: Renderer ID. */
+#define AL_RENDERER                              0xB003
+/** Context string: Space-separated extension list. */
+#define AL_EXTENSIONS                            0xB004
+
+
+/**
+ * Doppler scale.
+ * Type:    ALfloat
+ * Range:   [0.0 - ]
+ * Default: 1.0
+ *
+ * Scale for source and listener velocities.
+ */
+#define AL_DOPPLER_FACTOR                        0xC000
+AL_API void AL_APIENTRY alDopplerFactor(ALfloat value);
+
+/**
+ * Doppler velocity (deprecated).
+ *
+ * A multiplier applied to the Speed of Sound.
+ */
+#define AL_DOPPLER_VELOCITY                      0xC001
+AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value);
+
+/**
+ * Speed of Sound, in units per second.
+ * Type:    ALfloat
+ * Range:   [0.0001 - ]
+ * Default: 343.3
+ *
+ * The speed at which sound waves are assumed to travel, when calculating the
+ * doppler effect.
+ */
+#define AL_SPEED_OF_SOUND                        0xC003
+AL_API void AL_APIENTRY alSpeedOfSound(ALfloat value);
+
+/**
+ * Distance attenuation model.
+ * Type:    ALint
+ * Range:   [AL_NONE, AL_INVERSE_DISTANCE, AL_INVERSE_DISTANCE_CLAMPED,
+ *           AL_LINEAR_DISTANCE, AL_LINEAR_DISTANCE_CLAMPED,
+ *           AL_EXPONENT_DISTANCE, AL_EXPONENT_DISTANCE_CLAMPED]
+ * Default: AL_INVERSE_DISTANCE_CLAMPED
+ *
+ * The model by which sources attenuate with distance.
+ *
+ * None     - No distance attenuation.
+ * Inverse  - Doubling the distance halves the source gain.
+ * Linear   - Linear gain scaling between the reference and max distances.
+ * Exponent - Exponential gain dropoff.
+ *
+ * Clamped variations work like the non-clamped counterparts, except the
+ * distance calculated is clamped between the reference and max distances.
+ */
+#define AL_DISTANCE_MODEL                        0xD000
+AL_API void AL_APIENTRY alDistanceModel(ALenum distanceModel);
+
+/** Distance model value. */
+#define AL_INVERSE_DISTANCE                      0xD001
+#define AL_INVERSE_DISTANCE_CLAMPED              0xD002
+#define AL_LINEAR_DISTANCE                       0xD003
+#define AL_LINEAR_DISTANCE_CLAMPED               0xD004
+#define AL_EXPONENT_DISTANCE                     0xD005
+#define AL_EXPONENT_DISTANCE_CLAMPED             0xD006
+
+/** Renderer State management. */
+AL_API void AL_APIENTRY alEnable(ALenum capability);
+AL_API void AL_APIENTRY alDisable(ALenum capability);
+AL_API ALboolean AL_APIENTRY alIsEnabled(ALenum capability);
+
+/** State retrieval. */
+AL_API const ALchar* AL_APIENTRY alGetString(ALenum param);
+AL_API void AL_APIENTRY alGetBooleanv(ALenum param, ALboolean *values);
+AL_API void AL_APIENTRY alGetIntegerv(ALenum param, ALint *values);
+AL_API void AL_APIENTRY alGetFloatv(ALenum param, ALfloat *values);
+AL_API void AL_APIENTRY alGetDoublev(ALenum param, ALdouble *values);
+AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum param);
+AL_API ALint AL_APIENTRY alGetInteger(ALenum param);
+AL_API ALfloat AL_APIENTRY alGetFloat(ALenum param);
+AL_API ALdouble AL_APIENTRY alGetDouble(ALenum param);
+
+/**
+ * Error retrieval.
+ *
+ * Obtain the first error generated in the AL context since the last check.
+ */
+AL_API ALenum AL_APIENTRY alGetError(void);
+
+/**
+ * Extension support.
+ *
+ * Query for the presence of an extension, and obtain any appropriate function
+ * pointers and enum values.
+ */
+AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extname);
+AL_API void* AL_APIENTRY alGetProcAddress(const ALchar *fname);
+AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *ename);
+
+
+/** Set Listener parameters */
+AL_API void AL_APIENTRY alListenerf(ALenum param, ALfloat value);
+AL_API void AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3);
+AL_API void AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values);
+AL_API void AL_APIENTRY alListeneri(ALenum param, ALint value);
+AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3);
+AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values);
+
+/** Get Listener parameters */
+AL_API void AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value);
+AL_API void AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3);
+AL_API void AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values);
+AL_API void AL_APIENTRY alGetListeneri(ALenum param, ALint *value);
+AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3);
+AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint *values);
+
+
+/** Create Source objects. */
+AL_API void AL_APIENTRY alGenSources(ALsizei n, ALuint *sources);
+/** Delete Source objects. */
+AL_API void AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources);
+/** Verify a handle is a valid Source. */
+AL_API ALboolean AL_APIENTRY alIsSource(ALuint source);
+
+/** Set Source parameters. */
+AL_API void AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value);
+AL_API void AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3);
+AL_API void AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values);
+AL_API void AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value);
+AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3);
+AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values);
+
+/** Get Source parameters. */
+AL_API void AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value);
+AL_API void AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3);
+AL_API void AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values);
+AL_API void AL_APIENTRY alGetSourcei(ALuint source,  ALenum param, ALint *value);
+AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3);
+AL_API void AL_APIENTRY alGetSourceiv(ALuint source,  ALenum param, ALint *values);
+
+
+/** Play, replay, or resume (if paused) a list of Sources */
+AL_API void AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources);
+/** Stop a list of Sources */
+AL_API void AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources);
+/** Rewind a list of Sources */
+AL_API void AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources);
+/** Pause a list of Sources */
+AL_API void AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources);
+
+/** Play, replay, or resume a Source */
+AL_API void AL_APIENTRY alSourcePlay(ALuint source);
+/** Stop a Source */
+AL_API void AL_APIENTRY alSourceStop(ALuint source);
+/** Rewind a Source (set playback postiton to beginning) */
+AL_API void AL_APIENTRY alSourceRewind(ALuint source);
+/** Pause a Source */
+AL_API void AL_APIENTRY alSourcePause(ALuint source);
+
+/** Queue buffers onto a source */
+AL_API void AL_APIENTRY alSourceQueueBuffers(ALuint source, ALsizei nb, const ALuint *buffers);
+/** Unqueue processed buffers from a source */
+AL_API void AL_APIENTRY alSourceUnqueueBuffers(ALuint source, ALsizei nb, ALuint *buffers);
+
+
+/** Create Buffer objects */
+AL_API void AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers);
+/** Delete Buffer objects */
+AL_API void AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers);
+/** Verify a handle is a valid Buffer */
+AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer);
+
+/** Specifies the data to be copied into a buffer */
+AL_API void AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq);
+
+/** Set Buffer parameters, */
+AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat value);
+AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3);
+AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *values);
+AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value);
+AL_API void AL_APIENTRY alBuffer3i(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3);
+AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *values);
+
+/** Get Buffer parameters. */
+AL_API void AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *value);
+AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3);
+AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *values);
+AL_API void AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value);
+AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3);
+AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values);
+
+/** Pointer-to-function type, useful for dynamically getting AL entry points. */
+typedef void          (AL_APIENTRY *LPALENABLE)(ALenum capability);
+typedef void          (AL_APIENTRY *LPALDISABLE)(ALenum capability);
+typedef ALboolean     (AL_APIENTRY *LPALISENABLED)(ALenum capability);
+typedef const ALchar* (AL_APIENTRY *LPALGETSTRING)(ALenum param);
+typedef void          (AL_APIENTRY *LPALGETBOOLEANV)(ALenum param, ALboolean *values);
+typedef void          (AL_APIENTRY *LPALGETINTEGERV)(ALenum param, ALint *values);
+typedef void          (AL_APIENTRY *LPALGETFLOATV)(ALenum param, ALfloat *values);
+typedef void          (AL_APIENTRY *LPALGETDOUBLEV)(ALenum param, ALdouble *values);
+typedef ALboolean     (AL_APIENTRY *LPALGETBOOLEAN)(ALenum param);
+typedef ALint         (AL_APIENTRY *LPALGETINTEGER)(ALenum param);
+typedef ALfloat       (AL_APIENTRY *LPALGETFLOAT)(ALenum param);
+typedef ALdouble      (AL_APIENTRY *LPALGETDOUBLE)(ALenum param);
+typedef ALenum        (AL_APIENTRY *LPALGETERROR)(void);
+typedef ALboolean     (AL_APIENTRY *LPALISEXTENSIONPRESENT)(const ALchar *extname);
+typedef void*         (AL_APIENTRY *LPALGETPROCADDRESS)(const ALchar *fname);
+typedef ALenum        (AL_APIENTRY *LPALGETENUMVALUE)(const ALchar *ename);
+typedef void          (AL_APIENTRY *LPALLISTENERF)(ALenum param, ALfloat value);
+typedef void          (AL_APIENTRY *LPALLISTENER3F)(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3);
+typedef void          (AL_APIENTRY *LPALLISTENERFV)(ALenum param, const ALfloat *values);
+typedef void          (AL_APIENTRY *LPALLISTENERI)(ALenum param, ALint value);
+typedef void          (AL_APIENTRY *LPALLISTENER3I)(ALenum param, ALint value1, ALint value2, ALint value3);
+typedef void          (AL_APIENTRY *LPALLISTENERIV)(ALenum param, const ALint *values);
+typedef void          (AL_APIENTRY *LPALGETLISTENERF)(ALenum param, ALfloat *value);
+typedef void          (AL_APIENTRY *LPALGETLISTENER3F)(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3);
+typedef void          (AL_APIENTRY *LPALGETLISTENERFV)(ALenum param, ALfloat *values);
+typedef void          (AL_APIENTRY *LPALGETLISTENERI)(ALenum param, ALint *value);
+typedef void          (AL_APIENTRY *LPALGETLISTENER3I)(ALenum param, ALint *value1, ALint *value2, ALint *value3);
+typedef void          (AL_APIENTRY *LPALGETLISTENERIV)(ALenum param, ALint *values);
+typedef void          (AL_APIENTRY *LPALGENSOURCES)(ALsizei n, ALuint *sources);
+typedef void          (AL_APIENTRY *LPALDELETESOURCES)(ALsizei n, const ALuint *sources);
+typedef ALboolean     (AL_APIENTRY *LPALISSOURCE)(ALuint source);
+typedef void          (AL_APIENTRY *LPALSOURCEF)(ALuint source, ALenum param, ALfloat value);
+typedef void          (AL_APIENTRY *LPALSOURCE3F)(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3);
+typedef void          (AL_APIENTRY *LPALSOURCEFV)(ALuint source, ALenum param, const ALfloat *values);
+typedef void          (AL_APIENTRY *LPALSOURCEI)(ALuint source, ALenum param, ALint value);
+typedef void          (AL_APIENTRY *LPALSOURCE3I)(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3);
+typedef void          (AL_APIENTRY *LPALSOURCEIV)(ALuint source, ALenum param, const ALint *values);
+typedef void          (AL_APIENTRY *LPALGETSOURCEF)(ALuint source, ALenum param, ALfloat *value);
+typedef void          (AL_APIENTRY *LPALGETSOURCE3F)(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3);
+typedef void          (AL_APIENTRY *LPALGETSOURCEFV)(ALuint source, ALenum param, ALfloat *values);
+typedef void          (AL_APIENTRY *LPALGETSOURCEI)(ALuint source, ALenum param, ALint *value);
+typedef void          (AL_APIENTRY *LPALGETSOURCE3I)(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3);
+typedef void          (AL_APIENTRY *LPALGETSOURCEIV)(ALuint source, ALenum param, ALint *values);
+typedef void          (AL_APIENTRY *LPALSOURCEPLAYV)(ALsizei n, const ALuint *sources);
+typedef void          (AL_APIENTRY *LPALSOURCESTOPV)(ALsizei n, const ALuint *sources);
+typedef void          (AL_APIENTRY *LPALSOURCEREWINDV)(ALsizei n, const ALuint *sources);
+typedef void          (AL_APIENTRY *LPALSOURCEPAUSEV)(ALsizei n, const ALuint *sources);
+typedef void          (AL_APIENTRY *LPALSOURCEPLAY)(ALuint source);
+typedef void          (AL_APIENTRY *LPALSOURCESTOP)(ALuint source);
+typedef void          (AL_APIENTRY *LPALSOURCEREWIND)(ALuint source);
+typedef void          (AL_APIENTRY *LPALSOURCEPAUSE)(ALuint source);
+typedef void          (AL_APIENTRY *LPALSOURCEQUEUEBUFFERS)(ALuint source, ALsizei nb, const ALuint *buffers);
+typedef void          (AL_APIENTRY *LPALSOURCEUNQUEUEBUFFERS)(ALuint source, ALsizei nb, ALuint *buffers);
+typedef void          (AL_APIENTRY *LPALGENBUFFERS)(ALsizei n, ALuint *buffers);
+typedef void          (AL_APIENTRY *LPALDELETEBUFFERS)(ALsizei n, const ALuint *buffers);
+typedef ALboolean     (AL_APIENTRY *LPALISBUFFER)(ALuint buffer);
+typedef void          (AL_APIENTRY *LPALBUFFERDATA)(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq);
+typedef void          (AL_APIENTRY *LPALBUFFERF)(ALuint buffer, ALenum param, ALfloat value);
+typedef void          (AL_APIENTRY *LPALBUFFER3F)(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3);
+typedef void          (AL_APIENTRY *LPALBUFFERFV)(ALuint buffer, ALenum param, const ALfloat *values);
+typedef void          (AL_APIENTRY *LPALBUFFERI)(ALuint buffer, ALenum param, ALint value);
+typedef void          (AL_APIENTRY *LPALBUFFER3I)(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3);
+typedef void          (AL_APIENTRY *LPALBUFFERIV)(ALuint buffer, ALenum param, const ALint *values);
+typedef void          (AL_APIENTRY *LPALGETBUFFERF)(ALuint buffer, ALenum param, ALfloat *value);
+typedef void          (AL_APIENTRY *LPALGETBUFFER3F)(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3);
+typedef void          (AL_APIENTRY *LPALGETBUFFERFV)(ALuint buffer, ALenum param, ALfloat *values);
+typedef void          (AL_APIENTRY *LPALGETBUFFERI)(ALuint buffer, ALenum param, ALint *value);
+typedef void          (AL_APIENTRY *LPALGETBUFFER3I)(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3);
+typedef void          (AL_APIENTRY *LPALGETBUFFERIV)(ALuint buffer, ALenum param, ALint *values);
+typedef void          (AL_APIENTRY *LPALDOPPLERFACTOR)(ALfloat value);
+typedef void          (AL_APIENTRY *LPALDOPPLERVELOCITY)(ALfloat value);
+typedef void          (AL_APIENTRY *LPALSPEEDOFSOUND)(ALfloat value);
+typedef void          (AL_APIENTRY *LPALDISTANCEMODEL)(ALenum distanceModel);
+
+#if defined(__cplusplus)
+}  /* extern "C" */
+#endif
+
+#endif /* AL_AL_H */

+ 237 - 0
audio/al/AL/alc.h

@@ -0,0 +1,237 @@
+#ifndef AL_ALC_H
+#define AL_ALC_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#ifndef ALC_API
+ #if defined(AL_LIBTYPE_STATIC)
+  #define ALC_API
+ #elif defined(_WIN32)
+  #define ALC_API __declspec(dllimport)
+ #else
+  #define ALC_API extern
+ #endif
+#endif
+
+#if defined(_WIN32)
+ #define ALC_APIENTRY __cdecl
+#else
+ #define ALC_APIENTRY
+#endif
+
+
+/** Deprecated macro. */
+#define ALCAPI                                   ALC_API
+#define ALCAPIENTRY                              ALC_APIENTRY
+#define ALC_INVALID                              0
+
+/** Supported ALC version? */
+#define ALC_VERSION_0_1                          1
+
+/** Opaque device handle */
+typedef struct ALCdevice_struct ALCdevice;
+/** Opaque context handle */
+typedef struct ALCcontext_struct ALCcontext;
+
+/** 8-bit boolean */
+typedef char ALCboolean;
+
+/** character */
+typedef char ALCchar;
+
+/** signed 8-bit 2's complement integer */
+typedef signed char ALCbyte;
+
+/** unsigned 8-bit integer */
+typedef unsigned char ALCubyte;
+
+/** signed 16-bit 2's complement integer */
+typedef short ALCshort;
+
+/** unsigned 16-bit integer */
+typedef unsigned short ALCushort;
+
+/** signed 32-bit 2's complement integer */
+typedef int ALCint;
+
+/** unsigned 32-bit integer */
+typedef unsigned int ALCuint;
+
+/** non-negative 32-bit binary integer size */
+typedef int ALCsizei;
+
+/** enumerated 32-bit value */
+typedef int ALCenum;
+
+/** 32-bit IEEE754 floating-point */
+typedef float ALCfloat;
+
+/** 64-bit IEEE754 floating-point */
+typedef double ALCdouble;
+
+/** void type (for opaque pointers only) */
+typedef void ALCvoid;
+
+
+/* Enumerant values begin at column 50. No tabs. */
+
+/** Boolean False. */
+#define ALC_FALSE                                0
+
+/** Boolean True. */
+#define ALC_TRUE                                 1
+
+/** Context attribute: <int> Hz. */
+#define ALC_FREQUENCY                            0x1007
+
+/** Context attribute: <int> Hz. */
+#define ALC_REFRESH                              0x1008
+
+/** Context attribute: AL_TRUE or AL_FALSE. */
+#define ALC_SYNC                                 0x1009
+
+/** Context attribute: <int> requested Mono (3D) Sources. */
+#define ALC_MONO_SOURCES                         0x1010
+
+/** Context attribute: <int> requested Stereo Sources. */
+#define ALC_STEREO_SOURCES                       0x1011
+
+/** No error. */
+#define ALC_NO_ERROR                             0
+
+/** Invalid device handle. */
+#define ALC_INVALID_DEVICE                       0xA001
+
+/** Invalid context handle. */
+#define ALC_INVALID_CONTEXT                      0xA002
+
+/** Invalid enum parameter passed to an ALC call. */
+#define ALC_INVALID_ENUM                         0xA003
+
+/** Invalid value parameter passed to an ALC call. */
+#define ALC_INVALID_VALUE                        0xA004
+
+/** Out of memory. */
+#define ALC_OUT_OF_MEMORY                        0xA005
+
+
+/** Runtime ALC version. */
+#define ALC_MAJOR_VERSION                        0x1000
+#define ALC_MINOR_VERSION                        0x1001
+
+/** Context attribute list properties. */
+#define ALC_ATTRIBUTES_SIZE                      0x1002
+#define ALC_ALL_ATTRIBUTES                       0x1003
+
+/** String for the default device specifier. */
+#define ALC_DEFAULT_DEVICE_SPECIFIER             0x1004
+/**
+ * String for the given device's specifier.
+ *
+ * If device handle is NULL, it is instead a null-char separated list of
+ * strings of known device specifiers (list ends with an empty string).
+ */
+#define ALC_DEVICE_SPECIFIER                     0x1005
+/** String for space-separated list of ALC extensions. */
+#define ALC_EXTENSIONS                           0x1006
+
+
+/** Capture extension */
+#define ALC_EXT_CAPTURE 1
+/**
+ * String for the given capture device's specifier.
+ *
+ * If device handle is NULL, it is instead a null-char separated list of
+ * strings of known capture device specifiers (list ends with an empty string).
+ */
+#define ALC_CAPTURE_DEVICE_SPECIFIER             0x310
+/** String for the default capture device specifier. */
+#define ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER     0x311
+/** Number of sample frames available for capture. */
+#define ALC_CAPTURE_SAMPLES                      0x312
+
+
+/** Enumerate All extension */
+#define ALC_ENUMERATE_ALL_EXT 1
+/** String for the default extended device specifier. */
+#define ALC_DEFAULT_ALL_DEVICES_SPECIFIER        0x1012
+/**
+ * String for the given extended device's specifier.
+ *
+ * If device handle is NULL, it is instead a null-char separated list of
+ * strings of known extended device specifiers (list ends with an empty string).
+ */
+#define ALC_ALL_DEVICES_SPECIFIER                0x1013
+
+
+/** Context management. */
+ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint* attrlist);
+ALC_API ALCboolean  ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context);
+ALC_API void        ALC_APIENTRY alcProcessContext(ALCcontext *context);
+ALC_API void        ALC_APIENTRY alcSuspendContext(ALCcontext *context);
+ALC_API void        ALC_APIENTRY alcDestroyContext(ALCcontext *context);
+ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void);
+ALC_API ALCdevice*  ALC_APIENTRY alcGetContextsDevice(ALCcontext *context);
+
+/** Device management. */
+ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *devicename);
+ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device);
+
+
+/**
+ * Error support.
+ *
+ * Obtain the most recent Device error.
+ */
+ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device);
+
+/**
+ * Extension support.
+ *
+ * Query for the presence of an extension, and obtain any appropriate
+ * function pointers and enum values.
+ */
+ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extname);
+ALC_API void*      ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcname);
+ALC_API ALCenum    ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumname);
+
+/** Query function. */
+ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *device, ALCenum param);
+ALC_API void           ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values);
+
+/** Capture function. */
+ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize);
+ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device);
+ALC_API void       ALC_APIENTRY alcCaptureStart(ALCdevice *device);
+ALC_API void       ALC_APIENTRY alcCaptureStop(ALCdevice *device);
+ALC_API void       ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples);
+
+/** Pointer-to-function type, useful for dynamically getting ALC entry points. */
+typedef ALCcontext*    (ALC_APIENTRY *LPALCCREATECONTEXT)(ALCdevice *device, const ALCint *attrlist);
+typedef ALCboolean     (ALC_APIENTRY *LPALCMAKECONTEXTCURRENT)(ALCcontext *context);
+typedef void           (ALC_APIENTRY *LPALCPROCESSCONTEXT)(ALCcontext *context);
+typedef void           (ALC_APIENTRY *LPALCSUSPENDCONTEXT)(ALCcontext *context);
+typedef void           (ALC_APIENTRY *LPALCDESTROYCONTEXT)(ALCcontext *context);
+typedef ALCcontext*    (ALC_APIENTRY *LPALCGETCURRENTCONTEXT)(void);
+typedef ALCdevice*     (ALC_APIENTRY *LPALCGETCONTEXTSDEVICE)(ALCcontext *context);
+typedef ALCdevice*     (ALC_APIENTRY *LPALCOPENDEVICE)(const ALCchar *devicename);
+typedef ALCboolean     (ALC_APIENTRY *LPALCCLOSEDEVICE)(ALCdevice *device);
+typedef ALCenum        (ALC_APIENTRY *LPALCGETERROR)(ALCdevice *device);
+typedef ALCboolean     (ALC_APIENTRY *LPALCISEXTENSIONPRESENT)(ALCdevice *device, const ALCchar *extname);
+typedef void*          (ALC_APIENTRY *LPALCGETPROCADDRESS)(ALCdevice *device, const ALCchar *funcname);
+typedef ALCenum        (ALC_APIENTRY *LPALCGETENUMVALUE)(ALCdevice *device, const ALCchar *enumname);
+typedef const ALCchar* (ALC_APIENTRY *LPALCGETSTRING)(ALCdevice *device, ALCenum param);
+typedef void           (ALC_APIENTRY *LPALCGETINTEGERV)(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values);
+typedef ALCdevice*     (ALC_APIENTRY *LPALCCAPTUREOPENDEVICE)(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize);
+typedef ALCboolean     (ALC_APIENTRY *LPALCCAPTURECLOSEDEVICE)(ALCdevice *device);
+typedef void           (ALC_APIENTRY *LPALCCAPTURESTART)(ALCdevice *device);
+typedef void           (ALC_APIENTRY *LPALCCAPTURESTOP)(ALCdevice *device);
+typedef void           (ALC_APIENTRY *LPALCCAPTURESAMPLES)(ALCdevice *device, ALCvoid *buffer, ALCsizei samples);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* AL_ALC_H */

+ 438 - 0
audio/al/AL/alext.h

@@ -0,0 +1,438 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2008 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#ifndef AL_ALEXT_H
+#define AL_ALEXT_H
+
+#include <stddef.h>
+/* Define int64_t and uint64_t types */
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+#include <inttypes.h>
+#elif defined(_WIN32) && defined(__GNUC__)
+#include <stdint.h>
+#elif defined(_WIN32)
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+#else
+/* Fallback if nothing above works */
+#include <inttypes.h>
+#endif
+
+#include "alc.h"
+#include "al.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef AL_LOKI_IMA_ADPCM_format
+#define AL_LOKI_IMA_ADPCM_format 1
+#define AL_FORMAT_IMA_ADPCM_MONO16_EXT           0x10000
+#define AL_FORMAT_IMA_ADPCM_STEREO16_EXT         0x10001
+#endif
+
+#ifndef AL_LOKI_WAVE_format
+#define AL_LOKI_WAVE_format 1
+#define AL_FORMAT_WAVE_EXT                       0x10002
+#endif
+
+#ifndef AL_EXT_vorbis
+#define AL_EXT_vorbis 1
+#define AL_FORMAT_VORBIS_EXT                     0x10003
+#endif
+
+#ifndef AL_LOKI_quadriphonic
+#define AL_LOKI_quadriphonic 1
+#define AL_FORMAT_QUAD8_LOKI                     0x10004
+#define AL_FORMAT_QUAD16_LOKI                    0x10005
+#endif
+
+#ifndef AL_EXT_float32
+#define AL_EXT_float32 1
+#define AL_FORMAT_MONO_FLOAT32                   0x10010
+#define AL_FORMAT_STEREO_FLOAT32                 0x10011
+#endif
+
+#ifndef AL_EXT_double
+#define AL_EXT_double 1
+#define AL_FORMAT_MONO_DOUBLE_EXT                0x10012
+#define AL_FORMAT_STEREO_DOUBLE_EXT              0x10013
+#endif
+
+#ifndef AL_EXT_MULAW
+#define AL_EXT_MULAW 1
+#define AL_FORMAT_MONO_MULAW_EXT                 0x10014
+#define AL_FORMAT_STEREO_MULAW_EXT               0x10015
+#endif
+
+#ifndef AL_EXT_ALAW
+#define AL_EXT_ALAW 1
+#define AL_FORMAT_MONO_ALAW_EXT                  0x10016
+#define AL_FORMAT_STEREO_ALAW_EXT                0x10017
+#endif
+
+#ifndef ALC_LOKI_audio_channel
+#define ALC_LOKI_audio_channel 1
+#define ALC_CHAN_MAIN_LOKI                       0x500001
+#define ALC_CHAN_PCM_LOKI                        0x500002
+#define ALC_CHAN_CD_LOKI                         0x500003
+#endif
+
+#ifndef AL_EXT_MCFORMATS
+#define AL_EXT_MCFORMATS 1
+#define AL_FORMAT_QUAD8                          0x1204
+#define AL_FORMAT_QUAD16                         0x1205
+#define AL_FORMAT_QUAD32                         0x1206
+#define AL_FORMAT_REAR8                          0x1207
+#define AL_FORMAT_REAR16                         0x1208
+#define AL_FORMAT_REAR32                         0x1209
+#define AL_FORMAT_51CHN8                         0x120A
+#define AL_FORMAT_51CHN16                        0x120B
+#define AL_FORMAT_51CHN32                        0x120C
+#define AL_FORMAT_61CHN8                         0x120D
+#define AL_FORMAT_61CHN16                        0x120E
+#define AL_FORMAT_61CHN32                        0x120F
+#define AL_FORMAT_71CHN8                         0x1210
+#define AL_FORMAT_71CHN16                        0x1211
+#define AL_FORMAT_71CHN32                        0x1212
+#endif
+
+#ifndef AL_EXT_MULAW_MCFORMATS
+#define AL_EXT_MULAW_MCFORMATS 1
+#define AL_FORMAT_MONO_MULAW                     0x10014
+#define AL_FORMAT_STEREO_MULAW                   0x10015
+#define AL_FORMAT_QUAD_MULAW                     0x10021
+#define AL_FORMAT_REAR_MULAW                     0x10022
+#define AL_FORMAT_51CHN_MULAW                    0x10023
+#define AL_FORMAT_61CHN_MULAW                    0x10024
+#define AL_FORMAT_71CHN_MULAW                    0x10025
+#endif
+
+#ifndef AL_EXT_IMA4
+#define AL_EXT_IMA4 1
+#define AL_FORMAT_MONO_IMA4                      0x1300
+#define AL_FORMAT_STEREO_IMA4                    0x1301
+#endif
+
+#ifndef AL_EXT_STATIC_BUFFER
+#define AL_EXT_STATIC_BUFFER 1
+typedef ALvoid (AL_APIENTRY*PFNALBUFFERDATASTATICPROC)(const ALint,ALenum,ALvoid*,ALsizei,ALsizei);
+#ifdef AL_ALEXT_PROTOTYPES
+AL_API ALvoid AL_APIENTRY alBufferDataStatic(const ALint buffer, ALenum format, ALvoid *data, ALsizei len, ALsizei freq);
+#endif
+#endif
+
+#ifndef ALC_EXT_EFX
+#define ALC_EXT_EFX 1
+#include "efx.h"
+#endif
+
+#ifndef ALC_EXT_disconnect
+#define ALC_EXT_disconnect 1
+#define ALC_CONNECTED                            0x313
+#endif
+
+#ifndef ALC_EXT_thread_local_context
+#define ALC_EXT_thread_local_context 1
+typedef ALCboolean  (ALC_APIENTRY*PFNALCSETTHREADCONTEXTPROC)(ALCcontext *context);
+typedef ALCcontext* (ALC_APIENTRY*PFNALCGETTHREADCONTEXTPROC)(void);
+#ifdef AL_ALEXT_PROTOTYPES
+ALC_API ALCboolean  ALC_APIENTRY alcSetThreadContext(ALCcontext *context);
+ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void);
+#endif
+#endif
+
+#ifndef AL_EXT_source_distance_model
+#define AL_EXT_source_distance_model 1
+#define AL_SOURCE_DISTANCE_MODEL                 0x200
+#endif
+
+#ifndef AL_SOFT_buffer_sub_data
+#define AL_SOFT_buffer_sub_data 1
+#define AL_BYTE_RW_OFFSETS_SOFT                  0x1031
+#define AL_SAMPLE_RW_OFFSETS_SOFT                0x1032
+typedef ALvoid (AL_APIENTRY*PFNALBUFFERSUBDATASOFTPROC)(ALuint,ALenum,const ALvoid*,ALsizei,ALsizei);
+#ifdef AL_ALEXT_PROTOTYPES
+AL_API ALvoid AL_APIENTRY alBufferSubDataSOFT(ALuint buffer,ALenum format,const ALvoid *data,ALsizei offset,ALsizei length);
+#endif
+#endif
+
+#ifndef AL_SOFT_loop_points
+#define AL_SOFT_loop_points 1
+#define AL_LOOP_POINTS_SOFT                      0x2015
+#endif
+
+#ifndef AL_EXT_FOLDBACK
+#define AL_EXT_FOLDBACK 1
+#define AL_EXT_FOLDBACK_NAME                     "AL_EXT_FOLDBACK"
+#define AL_FOLDBACK_EVENT_BLOCK                  0x4112
+#define AL_FOLDBACK_EVENT_START                  0x4111
+#define AL_FOLDBACK_EVENT_STOP                   0x4113
+#define AL_FOLDBACK_MODE_MONO                    0x4101
+#define AL_FOLDBACK_MODE_STEREO                  0x4102
+typedef void (AL_APIENTRY*LPALFOLDBACKCALLBACK)(ALenum,ALsizei);
+typedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTART)(ALenum,ALsizei,ALsizei,ALfloat*,LPALFOLDBACKCALLBACK);
+typedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTOP)(void);
+#ifdef AL_ALEXT_PROTOTYPES
+AL_API void AL_APIENTRY alRequestFoldbackStart(ALenum mode,ALsizei count,ALsizei length,ALfloat *mem,LPALFOLDBACKCALLBACK callback);
+AL_API void AL_APIENTRY alRequestFoldbackStop(void);
+#endif
+#endif
+
+#ifndef ALC_EXT_DEDICATED
+#define ALC_EXT_DEDICATED 1
+#define AL_DEDICATED_GAIN                        0x0001
+#define AL_EFFECT_DEDICATED_DIALOGUE             0x9001
+#define AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT 0x9000
+#endif
+
+#ifndef AL_SOFT_buffer_samples
+#define AL_SOFT_buffer_samples 1
+/* Channel configurations */
+#define AL_MONO_SOFT                             0x1500
+#define AL_STEREO_SOFT                           0x1501
+#define AL_REAR_SOFT                             0x1502
+#define AL_QUAD_SOFT                             0x1503
+#define AL_5POINT1_SOFT                          0x1504
+#define AL_6POINT1_SOFT                          0x1505
+#define AL_7POINT1_SOFT                          0x1506
+
+/* Sample types */
+#define AL_BYTE_SOFT                             0x1400
+#define AL_UNSIGNED_BYTE_SOFT                    0x1401
+#define AL_SHORT_SOFT                            0x1402
+#define AL_UNSIGNED_SHORT_SOFT                   0x1403
+#define AL_INT_SOFT                              0x1404
+#define AL_UNSIGNED_INT_SOFT                     0x1405
+#define AL_FLOAT_SOFT                            0x1406
+#define AL_DOUBLE_SOFT                           0x1407
+#define AL_BYTE3_SOFT                            0x1408
+#define AL_UNSIGNED_BYTE3_SOFT                   0x1409
+
+/* Storage formats */
+#define AL_MONO8_SOFT                            0x1100
+#define AL_MONO16_SOFT                           0x1101
+#define AL_MONO32F_SOFT                          0x10010
+#define AL_STEREO8_SOFT                          0x1102
+#define AL_STEREO16_SOFT                         0x1103
+#define AL_STEREO32F_SOFT                        0x10011
+#define AL_QUAD8_SOFT                            0x1204
+#define AL_QUAD16_SOFT                           0x1205
+#define AL_QUAD32F_SOFT                          0x1206
+#define AL_REAR8_SOFT                            0x1207
+#define AL_REAR16_SOFT                           0x1208
+#define AL_REAR32F_SOFT                          0x1209
+#define AL_5POINT1_8_SOFT                        0x120A
+#define AL_5POINT1_16_SOFT                       0x120B
+#define AL_5POINT1_32F_SOFT                      0x120C
+#define AL_6POINT1_8_SOFT                        0x120D
+#define AL_6POINT1_16_SOFT                       0x120E
+#define AL_6POINT1_32F_SOFT                      0x120F
+#define AL_7POINT1_8_SOFT                        0x1210
+#define AL_7POINT1_16_SOFT                       0x1211
+#define AL_7POINT1_32F_SOFT                      0x1212
+
+/* Buffer attributes */
+#define AL_INTERNAL_FORMAT_SOFT                  0x2008
+#define AL_BYTE_LENGTH_SOFT                      0x2009
+#define AL_SAMPLE_LENGTH_SOFT                    0x200A
+#define AL_SEC_LENGTH_SOFT                       0x200B
+
+typedef void (AL_APIENTRY*LPALBUFFERSAMPLESSOFT)(ALuint,ALuint,ALenum,ALsizei,ALenum,ALenum,const ALvoid*);
+typedef void (AL_APIENTRY*LPALBUFFERSUBSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,const ALvoid*);
+typedef void (AL_APIENTRY*LPALGETBUFFERSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,ALvoid*);
+typedef ALboolean (AL_APIENTRY*LPALISBUFFERFORMATSUPPORTEDSOFT)(ALenum);
+#ifdef AL_ALEXT_PROTOTYPES
+AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, ALuint samplerate, ALenum internalformat, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data);
+AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data);
+AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, ALvoid *data);
+AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum format);
+#endif
+#endif
+
+#ifndef AL_SOFT_direct_channels
+#define AL_SOFT_direct_channels 1
+#define AL_DIRECT_CHANNELS_SOFT                  0x1033
+#endif
+
+#ifndef ALC_SOFT_loopback
+#define ALC_SOFT_loopback 1
+#define ALC_FORMAT_CHANNELS_SOFT                 0x1990
+#define ALC_FORMAT_TYPE_SOFT                     0x1991
+
+/* Sample types */
+#define ALC_BYTE_SOFT                            0x1400
+#define ALC_UNSIGNED_BYTE_SOFT                   0x1401
+#define ALC_SHORT_SOFT                           0x1402
+#define ALC_UNSIGNED_SHORT_SOFT                  0x1403
+#define ALC_INT_SOFT                             0x1404
+#define ALC_UNSIGNED_INT_SOFT                    0x1405
+#define ALC_FLOAT_SOFT                           0x1406
+
+/* Channel configurations */
+#define ALC_MONO_SOFT                            0x1500
+#define ALC_STEREO_SOFT                          0x1501
+#define ALC_QUAD_SOFT                            0x1503
+#define ALC_5POINT1_SOFT                         0x1504
+#define ALC_6POINT1_SOFT                         0x1505
+#define ALC_7POINT1_SOFT                         0x1506
+
+typedef ALCdevice* (ALC_APIENTRY*LPALCLOOPBACKOPENDEVICESOFT)(const ALCchar*);
+typedef ALCboolean (ALC_APIENTRY*LPALCISRENDERFORMATSUPPORTEDSOFT)(ALCdevice*,ALCsizei,ALCenum,ALCenum);
+typedef void (ALC_APIENTRY*LPALCRENDERSAMPLESSOFT)(ALCdevice*,ALCvoid*,ALCsizei);
+#ifdef AL_ALEXT_PROTOTYPES
+ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceName);
+ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device, ALCsizei freq, ALCenum channels, ALCenum type);
+ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples);
+#endif
+#endif
+
+#ifndef AL_EXT_STEREO_ANGLES
+#define AL_EXT_STEREO_ANGLES 1
+#define AL_STEREO_ANGLES                         0x1030
+#endif
+
+#ifndef AL_EXT_SOURCE_RADIUS
+#define AL_EXT_SOURCE_RADIUS 1
+#define AL_SOURCE_RADIUS                         0x1031
+#endif
+
+#ifndef AL_SOFT_source_latency
+#define AL_SOFT_source_latency 1
+#define AL_SAMPLE_OFFSET_LATENCY_SOFT            0x1200
+#define AL_SEC_OFFSET_LATENCY_SOFT               0x1201
+typedef int64_t ALint64SOFT;
+typedef uint64_t ALuint64SOFT;
+typedef void (AL_APIENTRY*LPALSOURCEDSOFT)(ALuint,ALenum,ALdouble);
+typedef void (AL_APIENTRY*LPALSOURCE3DSOFT)(ALuint,ALenum,ALdouble,ALdouble,ALdouble);
+typedef void (AL_APIENTRY*LPALSOURCEDVSOFT)(ALuint,ALenum,const ALdouble*);
+typedef void (AL_APIENTRY*LPALGETSOURCEDSOFT)(ALuint,ALenum,ALdouble*);
+typedef void (AL_APIENTRY*LPALGETSOURCE3DSOFT)(ALuint,ALenum,ALdouble*,ALdouble*,ALdouble*);
+typedef void (AL_APIENTRY*LPALGETSOURCEDVSOFT)(ALuint,ALenum,ALdouble*);
+typedef void (AL_APIENTRY*LPALSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT);
+typedef void (AL_APIENTRY*LPALSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT,ALint64SOFT,ALint64SOFT);
+typedef void (AL_APIENTRY*LPALSOURCEI64VSOFT)(ALuint,ALenum,const ALint64SOFT*);
+typedef void (AL_APIENTRY*LPALGETSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT*);
+typedef void (AL_APIENTRY*LPALGETSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT*,ALint64SOFT*,ALint64SOFT*);
+typedef void (AL_APIENTRY*LPALGETSOURCEI64VSOFT)(ALuint,ALenum,ALint64SOFT*);
+#ifdef AL_ALEXT_PROTOTYPES
+AL_API void AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value);
+AL_API void AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3);
+AL_API void AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values);
+AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value);
+AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3);
+AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values);
+AL_API void AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value);
+AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3);
+AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values);
+AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value);
+AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3);
+AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values);
+#endif
+#endif
+
+#ifndef ALC_EXT_DEFAULT_FILTER_ORDER
+#define ALC_EXT_DEFAULT_FILTER_ORDER 1
+#define ALC_DEFAULT_FILTER_ORDER                 0x1100
+#endif
+
+#ifndef AL_SOFT_deferred_updates
+#define AL_SOFT_deferred_updates 1
+#define AL_DEFERRED_UPDATES_SOFT                 0xC002
+typedef ALvoid (AL_APIENTRY*LPALDEFERUPDATESSOFT)(void);
+typedef ALvoid (AL_APIENTRY*LPALPROCESSUPDATESSOFT)(void);
+#ifdef AL_ALEXT_PROTOTYPES
+AL_API ALvoid AL_APIENTRY alDeferUpdatesSOFT(void);
+AL_API ALvoid AL_APIENTRY alProcessUpdatesSOFT(void);
+#endif
+#endif
+
+#ifndef AL_SOFT_block_alignment
+#define AL_SOFT_block_alignment 1
+#define AL_UNPACK_BLOCK_ALIGNMENT_SOFT           0x200C
+#define AL_PACK_BLOCK_ALIGNMENT_SOFT             0x200D
+#endif
+
+#ifndef AL_SOFT_MSADPCM
+#define AL_SOFT_MSADPCM 1
+#define AL_FORMAT_MONO_MSADPCM_SOFT              0x1302
+#define AL_FORMAT_STEREO_MSADPCM_SOFT            0x1303
+#endif
+
+#ifndef AL_SOFT_source_length
+#define AL_SOFT_source_length 1
+/*#define AL_BYTE_LENGTH_SOFT                      0x2009*/
+/*#define AL_SAMPLE_LENGTH_SOFT                    0x200A*/
+/*#define AL_SEC_LENGTH_SOFT                       0x200B*/
+#endif
+
+#ifndef ALC_SOFT_pause_device
+#define ALC_SOFT_pause_device 1
+typedef void (ALC_APIENTRY*LPALCDEVICEPAUSESOFT)(ALCdevice *device);
+typedef void (ALC_APIENTRY*LPALCDEVICERESUMESOFT)(ALCdevice *device);
+#ifdef AL_ALEXT_PROTOTYPES
+ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device);
+ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device);
+#endif
+#endif
+
+#ifndef AL_EXT_BFORMAT
+#define AL_EXT_BFORMAT 1
+#define AL_FORMAT_BFORMAT2D_8                    0x20021
+#define AL_FORMAT_BFORMAT2D_16                   0x20022
+#define AL_FORMAT_BFORMAT2D_FLOAT32              0x20023
+#define AL_FORMAT_BFORMAT3D_8                    0x20031
+#define AL_FORMAT_BFORMAT3D_16                   0x20032
+#define AL_FORMAT_BFORMAT3D_FLOAT32              0x20033
+#endif
+
+#ifndef AL_EXT_MULAW_BFORMAT
+#define AL_EXT_MULAW_BFORMAT 1
+#define AL_FORMAT_BFORMAT2D_MULAW                0x10031
+#define AL_FORMAT_BFORMAT3D_MULAW                0x10032
+#endif
+
+#ifndef ALC_SOFT_HRTF
+#define ALC_SOFT_HRTF 1
+#define ALC_HRTF_SOFT                            0x1992
+#define ALC_DONT_CARE_SOFT                       0x0002
+#define ALC_HRTF_STATUS_SOFT                     0x1993
+#define ALC_HRTF_DISABLED_SOFT                   0x0000
+#define ALC_HRTF_ENABLED_SOFT                    0x0001
+#define ALC_HRTF_DENIED_SOFT                     0x0002
+#define ALC_HRTF_REQUIRED_SOFT                   0x0003
+#define ALC_HRTF_HEADPHONES_DETECTED_SOFT        0x0004
+#define ALC_HRTF_UNSUPPORTED_FORMAT_SOFT         0x0005
+#define ALC_NUM_HRTF_SPECIFIERS_SOFT             0x1994
+#define ALC_HRTF_SPECIFIER_SOFT                  0x1995
+#define ALC_HRTF_ID_SOFT                         0x1996
+typedef const ALCchar* (ALC_APIENTRY*LPALCGETSTRINGISOFT)(ALCdevice *device, ALCenum paramName, ALCsizei index);
+typedef ALCboolean (ALC_APIENTRY*LPALCRESETDEVICESOFT)(ALCdevice *device, const ALCint *attribs);
+#ifdef AL_ALEXT_PROTOTYPES
+ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum paramName, ALCsizei index);
+ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs);
+#endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 3 - 0
audio/al/AL/efx-creative.h

@@ -0,0 +1,3 @@
+/* The tokens that would be defined here are already defined in efx.h. This
+ * empty file is here to provide compatibility with Windows-based projects
+ * that would include it. */

+ 402 - 0
audio/al/AL/efx-presets.h

@@ -0,0 +1,402 @@
+/* Reverb presets for EFX */
+
+#ifndef EFX_PRESETS_H
+#define EFX_PRESETS_H
+
+#ifndef EFXEAXREVERBPROPERTIES_DEFINED
+#define EFXEAXREVERBPROPERTIES_DEFINED
+typedef struct {
+    float flDensity;
+    float flDiffusion;
+    float flGain;
+    float flGainHF;
+    float flGainLF;
+    float flDecayTime;
+    float flDecayHFRatio;
+    float flDecayLFRatio;
+    float flReflectionsGain;
+    float flReflectionsDelay;
+    float flReflectionsPan[3];
+    float flLateReverbGain;
+    float flLateReverbDelay;
+    float flLateReverbPan[3];
+    float flEchoTime;
+    float flEchoDepth;
+    float flModulationTime;
+    float flModulationDepth;
+    float flAirAbsorptionGainHF;
+    float flHFReference;
+    float flLFReference;
+    float flRoomRolloffFactor;
+    int   iDecayHFLimit;
+} EFXEAXREVERBPROPERTIES, *LPEFXEAXREVERBPROPERTIES;
+#endif
+
+/* Default Presets */
+
+#define EFX_REVERB_PRESET_GENERIC \
+    { 1.0000f, 1.0000f, 0.3162f, 0.8913f, 1.0000f, 1.4900f, 0.8300f, 1.0000f, 0.0500f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_PADDEDCELL \
+    { 0.1715f, 1.0000f, 0.3162f, 0.0010f, 1.0000f, 0.1700f, 0.1000f, 1.0000f, 0.2500f, 0.0010f, { 0.0000f, 0.0000f, 0.0000f }, 1.2691f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_ROOM \
+    { 0.4287f, 1.0000f, 0.3162f, 0.5929f, 1.0000f, 0.4000f, 0.8300f, 1.0000f, 0.1503f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 1.0629f, 0.0030f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_BATHROOM \
+    { 0.1715f, 1.0000f, 0.3162f, 0.2512f, 1.0000f, 1.4900f, 0.5400f, 1.0000f, 0.6531f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 3.2734f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_LIVINGROOM \
+    { 0.9766f, 1.0000f, 0.3162f, 0.0010f, 1.0000f, 0.5000f, 0.1000f, 1.0000f, 0.2051f, 0.0030f, { 0.0000f, 0.0000f, 0.0000f }, 0.2805f, 0.0040f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_STONEROOM \
+    { 1.0000f, 1.0000f, 0.3162f, 0.7079f, 1.0000f, 2.3100f, 0.6400f, 1.0000f, 0.4411f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.1003f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_AUDITORIUM \
+    { 1.0000f, 1.0000f, 0.3162f, 0.5781f, 1.0000f, 4.3200f, 0.5900f, 1.0000f, 0.4032f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.7170f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CONCERTHALL \
+    { 1.0000f, 1.0000f, 0.3162f, 0.5623f, 1.0000f, 3.9200f, 0.7000f, 1.0000f, 0.2427f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.9977f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CAVE \
+    { 1.0000f, 1.0000f, 0.3162f, 1.0000f, 1.0000f, 2.9100f, 1.3000f, 1.0000f, 0.5000f, 0.0150f, { 0.0000f, 0.0000f, 0.0000f }, 0.7063f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_ARENA \
+    { 1.0000f, 1.0000f, 0.3162f, 0.4477f, 1.0000f, 7.2400f, 0.3300f, 1.0000f, 0.2612f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.0186f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_HANGAR \
+    { 1.0000f, 1.0000f, 0.3162f, 0.3162f, 1.0000f, 10.0500f, 0.2300f, 1.0000f, 0.5000f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.2560f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CARPETEDHALLWAY \
+    { 0.4287f, 1.0000f, 0.3162f, 0.0100f, 1.0000f, 0.3000f, 0.1000f, 1.0000f, 0.1215f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 0.1531f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_HALLWAY \
+    { 0.3645f, 1.0000f, 0.3162f, 0.7079f, 1.0000f, 1.4900f, 0.5900f, 1.0000f, 0.2458f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.6615f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_STONECORRIDOR \
+    { 1.0000f, 1.0000f, 0.3162f, 0.7612f, 1.0000f, 2.7000f, 0.7900f, 1.0000f, 0.2472f, 0.0130f, { 0.0000f, 0.0000f, 0.0000f }, 1.5758f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_ALLEY \
+    { 1.0000f, 0.3000f, 0.3162f, 0.7328f, 1.0000f, 1.4900f, 0.8600f, 1.0000f, 0.2500f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.9954f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1250f, 0.9500f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_FOREST \
+    { 1.0000f, 0.3000f, 0.3162f, 0.0224f, 1.0000f, 1.4900f, 0.5400f, 1.0000f, 0.0525f, 0.1620f, { 0.0000f, 0.0000f, 0.0000f }, 0.7682f, 0.0880f, { 0.0000f, 0.0000f, 0.0000f }, 0.1250f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CITY \
+    { 1.0000f, 0.5000f, 0.3162f, 0.3981f, 1.0000f, 1.4900f, 0.6700f, 1.0000f, 0.0730f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.1427f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_MOUNTAINS \
+    { 1.0000f, 0.2700f, 0.3162f, 0.0562f, 1.0000f, 1.4900f, 0.2100f, 1.0000f, 0.0407f, 0.3000f, { 0.0000f, 0.0000f, 0.0000f }, 0.1919f, 0.1000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_QUARRY \
+    { 1.0000f, 1.0000f, 0.3162f, 0.3162f, 1.0000f, 1.4900f, 0.8300f, 1.0000f, 0.0000f, 0.0610f, { 0.0000f, 0.0000f, 0.0000f }, 1.7783f, 0.0250f, { 0.0000f, 0.0000f, 0.0000f }, 0.1250f, 0.7000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_PLAIN \
+    { 1.0000f, 0.2100f, 0.3162f, 0.1000f, 1.0000f, 1.4900f, 0.5000f, 1.0000f, 0.0585f, 0.1790f, { 0.0000f, 0.0000f, 0.0000f }, 0.1089f, 0.1000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_PARKINGLOT \
+    { 1.0000f, 1.0000f, 0.3162f, 1.0000f, 1.0000f, 1.6500f, 1.5000f, 1.0000f, 0.2082f, 0.0080f, { 0.0000f, 0.0000f, 0.0000f }, 0.2652f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_SEWERPIPE \
+    { 0.3071f, 0.8000f, 0.3162f, 0.3162f, 1.0000f, 2.8100f, 0.1400f, 1.0000f, 1.6387f, 0.0140f, { 0.0000f, 0.0000f, 0.0000f }, 3.2471f, 0.0210f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_UNDERWATER \
+    { 0.3645f, 1.0000f, 0.3162f, 0.0100f, 1.0000f, 1.4900f, 0.1000f, 1.0000f, 0.5963f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 7.0795f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 1.1800f, 0.3480f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_DRUGGED \
+    { 0.4287f, 0.5000f, 0.3162f, 1.0000f, 1.0000f, 8.3900f, 1.3900f, 1.0000f, 0.8760f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 3.1081f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 1.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_DIZZY \
+    { 0.3645f, 0.6000f, 0.3162f, 0.6310f, 1.0000f, 17.2300f, 0.5600f, 1.0000f, 0.1392f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.4937f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.8100f, 0.3100f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_PSYCHOTIC \
+    { 0.0625f, 0.5000f, 0.3162f, 0.8404f, 1.0000f, 7.5600f, 0.9100f, 1.0000f, 0.4864f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 2.4378f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 4.0000f, 1.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+
+/* Castle Presets */
+
+#define EFX_REVERB_PRESET_CASTLE_SMALLROOM \
+    { 1.0000f, 0.8900f, 0.3162f, 0.3981f, 0.1000f, 1.2200f, 0.8300f, 0.3100f, 0.8913f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 1.9953f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CASTLE_SHORTPASSAGE \
+    { 1.0000f, 0.8900f, 0.3162f, 0.3162f, 0.1000f, 2.3200f, 0.8300f, 0.3100f, 0.8913f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CASTLE_MEDIUMROOM \
+    { 1.0000f, 0.9300f, 0.3162f, 0.2818f, 0.1000f, 2.0400f, 0.8300f, 0.4600f, 0.6310f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 1.5849f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1550f, 0.0300f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CASTLE_LARGEROOM \
+    { 1.0000f, 0.8200f, 0.3162f, 0.2818f, 0.1259f, 2.5300f, 0.8300f, 0.5000f, 0.4467f, 0.0340f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.1850f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CASTLE_LONGPASSAGE \
+    { 1.0000f, 0.8900f, 0.3162f, 0.3981f, 0.1000f, 3.4200f, 0.8300f, 0.3100f, 0.8913f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CASTLE_HALL \
+    { 1.0000f, 0.8100f, 0.3162f, 0.2818f, 0.1778f, 3.1400f, 0.7900f, 0.6200f, 0.1778f, 0.0560f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CASTLE_CUPBOARD \
+    { 1.0000f, 0.8900f, 0.3162f, 0.2818f, 0.1000f, 0.6700f, 0.8700f, 0.3100f, 1.4125f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 3.5481f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CASTLE_COURTYARD \
+    { 1.0000f, 0.4200f, 0.3162f, 0.4467f, 0.1995f, 2.1300f, 0.6100f, 0.2300f, 0.2239f, 0.1600f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0360f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.3700f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_CASTLE_ALCOVE \
+    { 1.0000f, 0.8900f, 0.3162f, 0.5012f, 0.1000f, 1.6400f, 0.8700f, 0.3100f, 1.0000f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0340f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 }
+
+/* Factory Presets */
+
+#define EFX_REVERB_PRESET_FACTORY_SMALLROOM \
+    { 0.3645f, 0.8200f, 0.3162f, 0.7943f, 0.5012f, 1.7200f, 0.6500f, 1.3100f, 0.7079f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.7783f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.1190f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_FACTORY_SHORTPASSAGE \
+    { 0.3645f, 0.6400f, 0.2512f, 0.7943f, 0.5012f, 2.5300f, 0.6500f, 1.3100f, 1.0000f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0380f, { 0.0000f, 0.0000f, 0.0000f }, 0.1350f, 0.2300f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_FACTORY_MEDIUMROOM \
+    { 0.4287f, 0.8200f, 0.2512f, 0.7943f, 0.5012f, 2.7600f, 0.6500f, 1.3100f, 0.2818f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.1740f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_FACTORY_LARGEROOM \
+    { 0.4287f, 0.7500f, 0.2512f, 0.7079f, 0.6310f, 4.2400f, 0.5100f, 1.3100f, 0.1778f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.2310f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_FACTORY_LONGPASSAGE \
+    { 0.3645f, 0.6400f, 0.2512f, 0.7943f, 0.5012f, 4.0600f, 0.6500f, 1.3100f, 1.0000f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0370f, { 0.0000f, 0.0000f, 0.0000f }, 0.1350f, 0.2300f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_FACTORY_HALL \
+    { 0.4287f, 0.7500f, 0.3162f, 0.7079f, 0.6310f, 7.4300f, 0.5100f, 1.3100f, 0.0631f, 0.0730f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0270f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_FACTORY_CUPBOARD \
+    { 0.3071f, 0.6300f, 0.2512f, 0.7943f, 0.5012f, 0.4900f, 0.6500f, 1.3100f, 1.2589f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.9953f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.1070f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_FACTORY_COURTYARD \
+    { 0.3071f, 0.5700f, 0.3162f, 0.3162f, 0.6310f, 2.3200f, 0.2900f, 0.5600f, 0.2239f, 0.1400f, { 0.0000f, 0.0000f, 0.0000f }, 0.3981f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2900f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_FACTORY_ALCOVE \
+    { 0.3645f, 0.5900f, 0.2512f, 0.7943f, 0.5012f, 3.1400f, 0.6500f, 1.3100f, 1.4125f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.0000f, 0.0380f, { 0.0000f, 0.0000f, 0.0000f }, 0.1140f, 0.1000f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 }
+
+/* Ice Palace Presets */
+
+#define EFX_REVERB_PRESET_ICEPALACE_SMALLROOM \
+    { 1.0000f, 0.8400f, 0.3162f, 0.5623f, 0.2818f, 1.5100f, 1.5300f, 0.2700f, 0.8913f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1640f, 0.1400f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_ICEPALACE_SHORTPASSAGE \
+    { 1.0000f, 0.7500f, 0.3162f, 0.5623f, 0.2818f, 1.7900f, 1.4600f, 0.2800f, 0.5012f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0190f, { 0.0000f, 0.0000f, 0.0000f }, 0.1770f, 0.0900f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_ICEPALACE_MEDIUMROOM \
+    { 1.0000f, 0.8700f, 0.3162f, 0.5623f, 0.4467f, 2.2200f, 1.5300f, 0.3200f, 0.3981f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0270f, { 0.0000f, 0.0000f, 0.0000f }, 0.1860f, 0.1200f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_ICEPALACE_LARGEROOM \
+    { 1.0000f, 0.8100f, 0.3162f, 0.5623f, 0.4467f, 3.1400f, 1.5300f, 0.3200f, 0.2512f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 1.0000f, 0.0270f, { 0.0000f, 0.0000f, 0.0000f }, 0.2140f, 0.1100f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_ICEPALACE_LONGPASSAGE \
+    { 1.0000f, 0.7700f, 0.3162f, 0.5623f, 0.3981f, 3.0100f, 1.4600f, 0.2800f, 0.7943f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0250f, { 0.0000f, 0.0000f, 0.0000f }, 0.1860f, 0.0400f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_ICEPALACE_HALL \
+    { 1.0000f, 0.7600f, 0.3162f, 0.4467f, 0.5623f, 5.4900f, 1.5300f, 0.3800f, 0.1122f, 0.0540f, { 0.0000f, 0.0000f, 0.0000f }, 0.6310f, 0.0520f, { 0.0000f, 0.0000f, 0.0000f }, 0.2260f, 0.1100f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_ICEPALACE_CUPBOARD \
+    { 1.0000f, 0.8300f, 0.3162f, 0.5012f, 0.2239f, 0.7600f, 1.5300f, 0.2600f, 1.1220f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.9953f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.1430f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_ICEPALACE_COURTYARD \
+    { 1.0000f, 0.5900f, 0.3162f, 0.2818f, 0.3162f, 2.0400f, 1.2000f, 0.3800f, 0.3162f, 0.1730f, { 0.0000f, 0.0000f, 0.0000f }, 0.3162f, 0.0430f, { 0.0000f, 0.0000f, 0.0000f }, 0.2350f, 0.4800f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_ICEPALACE_ALCOVE \
+    { 1.0000f, 0.8400f, 0.3162f, 0.5623f, 0.2818f, 2.7600f, 1.4600f, 0.2800f, 1.1220f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1610f, 0.0900f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 }
+
+/* Space Station Presets */
+
+#define EFX_REVERB_PRESET_SPACESTATION_SMALLROOM \
+    { 0.2109f, 0.7000f, 0.3162f, 0.7079f, 0.8913f, 1.7200f, 0.8200f, 0.5500f, 0.7943f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0130f, { 0.0000f, 0.0000f, 0.0000f }, 0.1880f, 0.2600f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_SPACESTATION_SHORTPASSAGE \
+    { 0.2109f, 0.8700f, 0.3162f, 0.6310f, 0.8913f, 3.5700f, 0.5000f, 0.5500f, 1.0000f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.1720f, 0.2000f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_SPACESTATION_MEDIUMROOM \
+    { 0.2109f, 0.7500f, 0.3162f, 0.6310f, 0.8913f, 3.0100f, 0.5000f, 0.5500f, 0.3981f, 0.0340f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0350f, { 0.0000f, 0.0000f, 0.0000f }, 0.2090f, 0.3100f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_SPACESTATION_LARGEROOM \
+    { 0.3645f, 0.8100f, 0.3162f, 0.6310f, 0.8913f, 3.8900f, 0.3800f, 0.6100f, 0.3162f, 0.0560f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0350f, { 0.0000f, 0.0000f, 0.0000f }, 0.2330f, 0.2800f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_SPACESTATION_LONGPASSAGE \
+    { 0.4287f, 0.8200f, 0.3162f, 0.6310f, 0.8913f, 4.6200f, 0.6200f, 0.5500f, 1.0000f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0310f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2300f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_SPACESTATION_HALL \
+    { 0.4287f, 0.8700f, 0.3162f, 0.6310f, 0.8913f, 7.1100f, 0.3800f, 0.6100f, 0.1778f, 0.1000f, { 0.0000f, 0.0000f, 0.0000f }, 0.6310f, 0.0470f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2500f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_SPACESTATION_CUPBOARD \
+    { 0.1715f, 0.5600f, 0.3162f, 0.7079f, 0.8913f, 0.7900f, 0.8100f, 0.5500f, 1.4125f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.7783f, 0.0180f, { 0.0000f, 0.0000f, 0.0000f }, 0.1810f, 0.3100f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_SPACESTATION_ALCOVE \
+    { 0.2109f, 0.7800f, 0.3162f, 0.7079f, 0.8913f, 1.1600f, 0.8100f, 0.5500f, 1.4125f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.0000f, 0.0180f, { 0.0000f, 0.0000f, 0.0000f }, 0.1920f, 0.2100f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 }
+
+/* Wooden Galleon Presets */
+
+#define EFX_REVERB_PRESET_WOODEN_SMALLROOM \
+    { 1.0000f, 1.0000f, 0.3162f, 0.1122f, 0.3162f, 0.7900f, 0.3200f, 0.8700f, 1.0000f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_WOODEN_SHORTPASSAGE \
+    { 1.0000f, 1.0000f, 0.3162f, 0.1259f, 0.3162f, 1.7500f, 0.5000f, 0.8700f, 0.8913f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.6310f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_WOODEN_MEDIUMROOM \
+    { 1.0000f, 1.0000f, 0.3162f, 0.1000f, 0.2818f, 1.4700f, 0.4200f, 0.8200f, 0.8913f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_WOODEN_LARGEROOM \
+    { 1.0000f, 1.0000f, 0.3162f, 0.0891f, 0.2818f, 2.6500f, 0.3300f, 0.8200f, 0.8913f, 0.0660f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_WOODEN_LONGPASSAGE \
+    { 1.0000f, 1.0000f, 0.3162f, 0.1000f, 0.3162f, 1.9900f, 0.4000f, 0.7900f, 1.0000f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.4467f, 0.0360f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_WOODEN_HALL \
+    { 1.0000f, 1.0000f, 0.3162f, 0.0794f, 0.2818f, 3.4500f, 0.3000f, 0.8200f, 0.8913f, 0.0880f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0630f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_WOODEN_CUPBOARD \
+    { 1.0000f, 1.0000f, 0.3162f, 0.1413f, 0.3162f, 0.5600f, 0.4600f, 0.9100f, 1.1220f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0280f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_WOODEN_COURTYARD \
+    { 1.0000f, 0.6500f, 0.3162f, 0.0794f, 0.3162f, 1.7900f, 0.3500f, 0.7900f, 0.5623f, 0.1230f, { 0.0000f, 0.0000f, 0.0000f }, 0.1000f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_WOODEN_ALCOVE \
+    { 1.0000f, 1.0000f, 0.3162f, 0.1259f, 0.3162f, 1.2200f, 0.6200f, 0.9100f, 1.1220f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 }
+
+/* Sports Presets */
+
+#define EFX_REVERB_PRESET_SPORT_EMPTYSTADIUM \
+    { 1.0000f, 1.0000f, 0.3162f, 0.4467f, 0.7943f, 6.2600f, 0.5100f, 1.1000f, 0.0631f, 0.1830f, { 0.0000f, 0.0000f, 0.0000f }, 0.3981f, 0.0380f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_SPORT_SQUASHCOURT \
+    { 1.0000f, 0.7500f, 0.3162f, 0.3162f, 0.7943f, 2.2200f, 0.9100f, 1.1600f, 0.4467f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1260f, 0.1900f, 0.2500f, 0.0000f, 0.9943f, 7176.8999f, 211.2000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_SPORT_SMALLSWIMMINGPOOL \
+    { 1.0000f, 0.7000f, 0.3162f, 0.7943f, 0.8913f, 2.7600f, 1.2500f, 1.1400f, 0.6310f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1790f, 0.1500f, 0.8950f, 0.1900f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_SPORT_LARGESWIMMINGPOOL \
+    { 1.0000f, 0.8200f, 0.3162f, 0.7943f, 1.0000f, 5.4900f, 1.3100f, 1.1400f, 0.4467f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 0.5012f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2220f, 0.5500f, 1.1590f, 0.2100f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_SPORT_GYMNASIUM \
+    { 1.0000f, 0.8100f, 0.3162f, 0.4467f, 0.8913f, 3.1400f, 1.0600f, 1.3500f, 0.3981f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.5623f, 0.0450f, { 0.0000f, 0.0000f, 0.0000f }, 0.1460f, 0.1400f, 0.2500f, 0.0000f, 0.9943f, 7176.8999f, 211.2000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_SPORT_FULLSTADIUM \
+    { 1.0000f, 1.0000f, 0.3162f, 0.0708f, 0.7943f, 5.2500f, 0.1700f, 0.8000f, 0.1000f, 0.1880f, { 0.0000f, 0.0000f, 0.0000f }, 0.2818f, 0.0380f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_SPORT_STADIUMTANNOY \
+    { 1.0000f, 0.7800f, 0.3162f, 0.5623f, 0.5012f, 2.5300f, 0.8800f, 0.6800f, 0.2818f, 0.2300f, { 0.0000f, 0.0000f, 0.0000f }, 0.5012f, 0.0630f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+/* Prefab Presets */
+
+#define EFX_REVERB_PRESET_PREFAB_WORKSHOP \
+    { 0.4287f, 1.0000f, 0.3162f, 0.1413f, 0.3981f, 0.7600f, 1.0000f, 1.0000f, 1.0000f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_PREFAB_SCHOOLROOM \
+    { 0.4022f, 0.6900f, 0.3162f, 0.6310f, 0.5012f, 0.9800f, 0.4500f, 0.1800f, 1.4125f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0150f, { 0.0000f, 0.0000f, 0.0000f }, 0.0950f, 0.1400f, 0.2500f, 0.0000f, 0.9943f, 7176.8999f, 211.2000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_PREFAB_PRACTISEROOM \
+    { 0.4022f, 0.8700f, 0.3162f, 0.3981f, 0.5012f, 1.1200f, 0.5600f, 0.1800f, 1.2589f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.0950f, 0.1400f, 0.2500f, 0.0000f, 0.9943f, 7176.8999f, 211.2000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_PREFAB_OUTHOUSE \
+    { 1.0000f, 0.8200f, 0.3162f, 0.1122f, 0.1585f, 1.3800f, 0.3800f, 0.3500f, 0.8913f, 0.0240f, { 0.0000f, 0.0000f, -0.0000f }, 0.6310f, 0.0440f, { 0.0000f, 0.0000f, 0.0000f }, 0.1210f, 0.1700f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 107.5000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_PREFAB_CARAVAN \
+    { 1.0000f, 1.0000f, 0.3162f, 0.0891f, 0.1259f, 0.4300f, 1.5000f, 1.0000f, 1.0000f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.9953f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+
+/* Dome and Pipe Presets */
+
+#define EFX_REVERB_PRESET_DOME_TOMB \
+    { 1.0000f, 0.7900f, 0.3162f, 0.3548f, 0.2239f, 4.1800f, 0.2100f, 0.1000f, 0.3868f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 1.6788f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 0.1770f, 0.1900f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_PIPE_SMALL \
+    { 1.0000f, 1.0000f, 0.3162f, 0.3548f, 0.2239f, 5.0400f, 0.1000f, 0.1000f, 0.5012f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 2.5119f, 0.0150f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_DOME_SAINTPAULS \
+    { 1.0000f, 0.8700f, 0.3162f, 0.3548f, 0.2239f, 10.4800f, 0.1900f, 0.1000f, 0.1778f, 0.0900f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0420f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.1200f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_PIPE_LONGTHIN \
+    { 0.2560f, 0.9100f, 0.3162f, 0.4467f, 0.2818f, 9.2100f, 0.1800f, 0.1000f, 0.7079f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_PIPE_LARGE \
+    { 1.0000f, 1.0000f, 0.3162f, 0.3548f, 0.2239f, 8.4500f, 0.1000f, 0.1000f, 0.3981f, 0.0460f, { 0.0000f, 0.0000f, 0.0000f }, 1.5849f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_PIPE_RESONANT \
+    { 0.1373f, 0.9100f, 0.3162f, 0.4467f, 0.2818f, 6.8100f, 0.1800f, 0.1000f, 0.7079f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.0000f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x0 }
+
+/* Outdoors Presets */
+
+#define EFX_REVERB_PRESET_OUTDOORS_BACKYARD \
+    { 1.0000f, 0.4500f, 0.3162f, 0.2512f, 0.5012f, 1.1200f, 0.3400f, 0.4600f, 0.4467f, 0.0690f, { 0.0000f, 0.0000f, -0.0000f }, 0.7079f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.2180f, 0.3400f, 0.2500f, 0.0000f, 0.9943f, 4399.1001f, 242.9000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_OUTDOORS_ROLLINGPLAINS \
+    { 1.0000f, 0.0000f, 0.3162f, 0.0112f, 0.6310f, 2.1300f, 0.2100f, 0.4600f, 0.1778f, 0.3000f, { 0.0000f, 0.0000f, -0.0000f }, 0.4467f, 0.0190f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 4399.1001f, 242.9000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_OUTDOORS_DEEPCANYON \
+    { 1.0000f, 0.7400f, 0.3162f, 0.1778f, 0.6310f, 3.8900f, 0.2100f, 0.4600f, 0.3162f, 0.2230f, { 0.0000f, 0.0000f, -0.0000f }, 0.3548f, 0.0190f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 4399.1001f, 242.9000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_OUTDOORS_CREEK \
+    { 1.0000f, 0.3500f, 0.3162f, 0.1778f, 0.5012f, 2.1300f, 0.2100f, 0.4600f, 0.3981f, 0.1150f, { 0.0000f, 0.0000f, -0.0000f }, 0.1995f, 0.0310f, { 0.0000f, 0.0000f, 0.0000f }, 0.2180f, 0.3400f, 0.2500f, 0.0000f, 0.9943f, 4399.1001f, 242.9000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_OUTDOORS_VALLEY \
+    { 1.0000f, 0.2800f, 0.3162f, 0.0282f, 0.1585f, 2.8800f, 0.2600f, 0.3500f, 0.1413f, 0.2630f, { 0.0000f, 0.0000f, -0.0000f }, 0.3981f, 0.1000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.3400f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 107.5000f, 0.0000f, 0x0 }
+
+/* Mood Presets */
+
+#define EFX_REVERB_PRESET_MOOD_HEAVEN \
+    { 1.0000f, 0.9400f, 0.3162f, 0.7943f, 0.4467f, 5.0400f, 1.1200f, 0.5600f, 0.2427f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0800f, 2.7420f, 0.0500f, 0.9977f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_MOOD_HELL \
+    { 1.0000f, 0.5700f, 0.3162f, 0.3548f, 0.4467f, 3.5700f, 0.4900f, 2.0000f, 0.0000f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1100f, 0.0400f, 2.1090f, 0.5200f, 0.9943f, 5000.0000f, 139.5000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_MOOD_MEMORY \
+    { 1.0000f, 0.8500f, 0.3162f, 0.6310f, 0.3548f, 4.0600f, 0.8200f, 0.5600f, 0.0398f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.4740f, 0.4500f, 0.9886f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+
+/* Driving Presets */
+
+#define EFX_REVERB_PRESET_DRIVING_COMMENTATOR \
+    { 1.0000f, 0.0000f, 0.3162f, 0.5623f, 0.5012f, 2.4200f, 0.8800f, 0.6800f, 0.1995f, 0.0930f, { 0.0000f, 0.0000f, 0.0000f }, 0.2512f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9886f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_DRIVING_PITGARAGE \
+    { 0.4287f, 0.5900f, 0.3162f, 0.7079f, 0.5623f, 1.7200f, 0.9300f, 0.8700f, 0.5623f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.1100f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_DRIVING_INCAR_RACER \
+    { 0.0832f, 0.8000f, 0.3162f, 1.0000f, 0.7943f, 0.1700f, 2.0000f, 0.4100f, 1.7783f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0150f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10268.2002f, 251.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_DRIVING_INCAR_SPORTS \
+    { 0.0832f, 0.8000f, 0.3162f, 0.6310f, 1.0000f, 0.1700f, 0.7500f, 0.4100f, 1.0000f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 0.5623f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10268.2002f, 251.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_DRIVING_INCAR_LUXURY \
+    { 0.2560f, 1.0000f, 0.3162f, 0.1000f, 0.5012f, 0.1300f, 0.4100f, 0.4600f, 0.7943f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.5849f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10268.2002f, 251.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_DRIVING_FULLGRANDSTAND \
+    { 1.0000f, 1.0000f, 0.3162f, 0.2818f, 0.6310f, 3.0100f, 1.3700f, 1.2800f, 0.3548f, 0.0900f, { 0.0000f, 0.0000f, 0.0000f }, 0.1778f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10420.2002f, 250.0000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_DRIVING_EMPTYGRANDSTAND \
+    { 1.0000f, 1.0000f, 0.3162f, 1.0000f, 0.7943f, 4.6200f, 1.7500f, 1.4000f, 0.2082f, 0.0900f, { 0.0000f, 0.0000f, 0.0000f }, 0.2512f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10420.2002f, 250.0000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_DRIVING_TUNNEL \
+    { 1.0000f, 0.8100f, 0.3162f, 0.3981f, 0.8913f, 3.4200f, 0.9400f, 1.3100f, 0.7079f, 0.0510f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0470f, { 0.0000f, 0.0000f, 0.0000f }, 0.2140f, 0.0500f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 155.3000f, 0.0000f, 0x1 }
+
+/* City Presets */
+
+#define EFX_REVERB_PRESET_CITY_STREETS \
+    { 1.0000f, 0.7800f, 0.3162f, 0.7079f, 0.8913f, 1.7900f, 1.1200f, 0.9100f, 0.2818f, 0.0460f, { 0.0000f, 0.0000f, 0.0000f }, 0.1995f, 0.0280f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CITY_SUBWAY \
+    { 1.0000f, 0.7400f, 0.3162f, 0.7079f, 0.8913f, 3.0100f, 1.2300f, 0.9100f, 0.7079f, 0.0460f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0280f, { 0.0000f, 0.0000f, 0.0000f }, 0.1250f, 0.2100f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CITY_MUSEUM \
+    { 1.0000f, 0.8200f, 0.3162f, 0.1778f, 0.1778f, 3.2800f, 1.4000f, 0.5700f, 0.2512f, 0.0390f, { 0.0000f, 0.0000f, -0.0000f }, 0.8913f, 0.0340f, { 0.0000f, 0.0000f, 0.0000f }, 0.1300f, 0.1700f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 107.5000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_CITY_LIBRARY \
+    { 1.0000f, 0.8200f, 0.3162f, 0.2818f, 0.0891f, 2.7600f, 0.8900f, 0.4100f, 0.3548f, 0.0290f, { 0.0000f, 0.0000f, -0.0000f }, 0.8913f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.1300f, 0.1700f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 107.5000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_CITY_UNDERPASS \
+    { 1.0000f, 0.8200f, 0.3162f, 0.4467f, 0.8913f, 3.5700f, 1.1200f, 0.9100f, 0.3981f, 0.0590f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0370f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.1400f, 0.2500f, 0.0000f, 0.9920f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CITY_ABANDONED \
+    { 1.0000f, 0.6900f, 0.3162f, 0.7943f, 0.8913f, 3.2800f, 1.1700f, 0.9100f, 0.4467f, 0.0440f, { 0.0000f, 0.0000f, 0.0000f }, 0.2818f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2000f, 0.2500f, 0.0000f, 0.9966f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+/* Misc. Presets */
+
+#define EFX_REVERB_PRESET_DUSTYROOM \
+    { 0.3645f, 0.5600f, 0.3162f, 0.7943f, 0.7079f, 1.7900f, 0.3800f, 0.2100f, 0.5012f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0060f, { 0.0000f, 0.0000f, 0.0000f }, 0.2020f, 0.0500f, 0.2500f, 0.0000f, 0.9886f, 13046.0000f, 163.3000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CHAPEL \
+    { 1.0000f, 0.8400f, 0.3162f, 0.5623f, 1.0000f, 4.6200f, 0.6400f, 1.2300f, 0.4467f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.1100f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_SMALLWATERROOM \
+    { 1.0000f, 0.7000f, 0.3162f, 0.4477f, 1.0000f, 1.5100f, 1.2500f, 1.1400f, 0.8913f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1790f, 0.1500f, 0.8950f, 0.1900f, 0.9920f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+
+#endif /* EFX_PRESETS_H */

+ 761 - 0
audio/al/AL/efx.h

@@ -0,0 +1,761 @@
+#ifndef AL_EFX_H
+#define AL_EFX_H
+
+
+#include "alc.h"
+#include "al.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ALC_EXT_EFX_NAME                         "ALC_EXT_EFX"
+
+#define ALC_EFX_MAJOR_VERSION                    0x20001
+#define ALC_EFX_MINOR_VERSION                    0x20002
+#define ALC_MAX_AUXILIARY_SENDS                  0x20003
+
+
+/* Listener properties. */
+#define AL_METERS_PER_UNIT                       0x20004
+
+/* Source properties. */
+#define AL_DIRECT_FILTER                         0x20005
+#define AL_AUXILIARY_SEND_FILTER                 0x20006
+#define AL_AIR_ABSORPTION_FACTOR                 0x20007
+#define AL_ROOM_ROLLOFF_FACTOR                   0x20008
+#define AL_CONE_OUTER_GAINHF                     0x20009
+#define AL_DIRECT_FILTER_GAINHF_AUTO             0x2000A
+#define AL_AUXILIARY_SEND_FILTER_GAIN_AUTO       0x2000B
+#define AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO     0x2000C
+
+
+/* Effect properties. */
+
+/* Reverb effect parameters */
+#define AL_REVERB_DENSITY                        0x0001
+#define AL_REVERB_DIFFUSION                      0x0002
+#define AL_REVERB_GAIN                           0x0003
+#define AL_REVERB_GAINHF                         0x0004
+#define AL_REVERB_DECAY_TIME                     0x0005
+#define AL_REVERB_DECAY_HFRATIO                  0x0006
+#define AL_REVERB_REFLECTIONS_GAIN               0x0007
+#define AL_REVERB_REFLECTIONS_DELAY              0x0008
+#define AL_REVERB_LATE_REVERB_GAIN               0x0009
+#define AL_REVERB_LATE_REVERB_DELAY              0x000A
+#define AL_REVERB_AIR_ABSORPTION_GAINHF          0x000B
+#define AL_REVERB_ROOM_ROLLOFF_FACTOR            0x000C
+#define AL_REVERB_DECAY_HFLIMIT                  0x000D
+
+/* EAX Reverb effect parameters */
+#define AL_EAXREVERB_DENSITY                     0x0001
+#define AL_EAXREVERB_DIFFUSION                   0x0002
+#define AL_EAXREVERB_GAIN                        0x0003
+#define AL_EAXREVERB_GAINHF                      0x0004
+#define AL_EAXREVERB_GAINLF                      0x0005
+#define AL_EAXREVERB_DECAY_TIME                  0x0006
+#define AL_EAXREVERB_DECAY_HFRATIO               0x0007
+#define AL_EAXREVERB_DECAY_LFRATIO               0x0008
+#define AL_EAXREVERB_REFLECTIONS_GAIN            0x0009
+#define AL_EAXREVERB_REFLECTIONS_DELAY           0x000A
+#define AL_EAXREVERB_REFLECTIONS_PAN             0x000B
+#define AL_EAXREVERB_LATE_REVERB_GAIN            0x000C
+#define AL_EAXREVERB_LATE_REVERB_DELAY           0x000D
+#define AL_EAXREVERB_LATE_REVERB_PAN             0x000E
+#define AL_EAXREVERB_ECHO_TIME                   0x000F
+#define AL_EAXREVERB_ECHO_DEPTH                  0x0010
+#define AL_EAXREVERB_MODULATION_TIME             0x0011
+#define AL_EAXREVERB_MODULATION_DEPTH            0x0012
+#define AL_EAXREVERB_AIR_ABSORPTION_GAINHF       0x0013
+#define AL_EAXREVERB_HFREFERENCE                 0x0014
+#define AL_EAXREVERB_LFREFERENCE                 0x0015
+#define AL_EAXREVERB_ROOM_ROLLOFF_FACTOR         0x0016
+#define AL_EAXREVERB_DECAY_HFLIMIT               0x0017
+
+/* Chorus effect parameters */
+#define AL_CHORUS_WAVEFORM                       0x0001
+#define AL_CHORUS_PHASE                          0x0002
+#define AL_CHORUS_RATE                           0x0003
+#define AL_CHORUS_DEPTH                          0x0004
+#define AL_CHORUS_FEEDBACK                       0x0005
+#define AL_CHORUS_DELAY                          0x0006
+
+/* Distortion effect parameters */
+#define AL_DISTORTION_EDGE                       0x0001
+#define AL_DISTORTION_GAIN                       0x0002
+#define AL_DISTORTION_LOWPASS_CUTOFF             0x0003
+#define AL_DISTORTION_EQCENTER                   0x0004
+#define AL_DISTORTION_EQBANDWIDTH                0x0005
+
+/* Echo effect parameters */
+#define AL_ECHO_DELAY                            0x0001
+#define AL_ECHO_LRDELAY                          0x0002
+#define AL_ECHO_DAMPING                          0x0003
+#define AL_ECHO_FEEDBACK                         0x0004
+#define AL_ECHO_SPREAD                           0x0005
+
+/* Flanger effect parameters */
+#define AL_FLANGER_WAVEFORM                      0x0001
+#define AL_FLANGER_PHASE                         0x0002
+#define AL_FLANGER_RATE                          0x0003
+#define AL_FLANGER_DEPTH                         0x0004
+#define AL_FLANGER_FEEDBACK                      0x0005
+#define AL_FLANGER_DELAY                         0x0006
+
+/* Frequency shifter effect parameters */
+#define AL_FREQUENCY_SHIFTER_FREQUENCY           0x0001
+#define AL_FREQUENCY_SHIFTER_LEFT_DIRECTION      0x0002
+#define AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION     0x0003
+
+/* Vocal morpher effect parameters */
+#define AL_VOCAL_MORPHER_PHONEMEA                0x0001
+#define AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING  0x0002
+#define AL_VOCAL_MORPHER_PHONEMEB                0x0003
+#define AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING  0x0004
+#define AL_VOCAL_MORPHER_WAVEFORM                0x0005
+#define AL_VOCAL_MORPHER_RATE                    0x0006
+
+/* Pitchshifter effect parameters */
+#define AL_PITCH_SHIFTER_COARSE_TUNE             0x0001
+#define AL_PITCH_SHIFTER_FINE_TUNE               0x0002
+
+/* Ringmodulator effect parameters */
+#define AL_RING_MODULATOR_FREQUENCY              0x0001
+#define AL_RING_MODULATOR_HIGHPASS_CUTOFF        0x0002
+#define AL_RING_MODULATOR_WAVEFORM               0x0003
+
+/* Autowah effect parameters */
+#define AL_AUTOWAH_ATTACK_TIME                   0x0001
+#define AL_AUTOWAH_RELEASE_TIME                  0x0002
+#define AL_AUTOWAH_RESONANCE                     0x0003
+#define AL_AUTOWAH_PEAK_GAIN                     0x0004
+
+/* Compressor effect parameters */
+#define AL_COMPRESSOR_ONOFF                      0x0001
+
+/* Equalizer effect parameters */
+#define AL_EQUALIZER_LOW_GAIN                    0x0001
+#define AL_EQUALIZER_LOW_CUTOFF                  0x0002
+#define AL_EQUALIZER_MID1_GAIN                   0x0003
+#define AL_EQUALIZER_MID1_CENTER                 0x0004
+#define AL_EQUALIZER_MID1_WIDTH                  0x0005
+#define AL_EQUALIZER_MID2_GAIN                   0x0006
+#define AL_EQUALIZER_MID2_CENTER                 0x0007
+#define AL_EQUALIZER_MID2_WIDTH                  0x0008
+#define AL_EQUALIZER_HIGH_GAIN                   0x0009
+#define AL_EQUALIZER_HIGH_CUTOFF                 0x000A
+
+/* Effect type */
+#define AL_EFFECT_FIRST_PARAMETER                0x0000
+#define AL_EFFECT_LAST_PARAMETER                 0x8000
+#define AL_EFFECT_TYPE                           0x8001
+
+/* Effect types, used with the AL_EFFECT_TYPE property */
+#define AL_EFFECT_NULL                           0x0000
+#define AL_EFFECT_REVERB                         0x0001
+#define AL_EFFECT_CHORUS                         0x0002
+#define AL_EFFECT_DISTORTION                     0x0003
+#define AL_EFFECT_ECHO                           0x0004
+#define AL_EFFECT_FLANGER                        0x0005
+#define AL_EFFECT_FREQUENCY_SHIFTER              0x0006
+#define AL_EFFECT_VOCAL_MORPHER                  0x0007
+#define AL_EFFECT_PITCH_SHIFTER                  0x0008
+#define AL_EFFECT_RING_MODULATOR                 0x0009
+#define AL_EFFECT_AUTOWAH                        0x000A
+#define AL_EFFECT_COMPRESSOR                     0x000B
+#define AL_EFFECT_EQUALIZER                      0x000C
+#define AL_EFFECT_EAXREVERB                      0x8000
+
+/* Auxiliary Effect Slot properties. */
+#define AL_EFFECTSLOT_EFFECT                     0x0001
+#define AL_EFFECTSLOT_GAIN                       0x0002
+#define AL_EFFECTSLOT_AUXILIARY_SEND_AUTO        0x0003
+
+/* NULL Auxiliary Slot ID to disable a source send. */
+#define AL_EFFECTSLOT_NULL                       0x0000
+
+
+/* Filter properties. */
+
+/* Lowpass filter parameters */
+#define AL_LOWPASS_GAIN                          0x0001
+#define AL_LOWPASS_GAINHF                        0x0002
+
+/* Highpass filter parameters */
+#define AL_HIGHPASS_GAIN                         0x0001
+#define AL_HIGHPASS_GAINLF                       0x0002
+
+/* Bandpass filter parameters */
+#define AL_BANDPASS_GAIN                         0x0001
+#define AL_BANDPASS_GAINLF                       0x0002
+#define AL_BANDPASS_GAINHF                       0x0003
+
+/* Filter type */
+#define AL_FILTER_FIRST_PARAMETER                0x0000
+#define AL_FILTER_LAST_PARAMETER                 0x8000
+#define AL_FILTER_TYPE                           0x8001
+
+/* Filter types, used with the AL_FILTER_TYPE property */
+#define AL_FILTER_NULL                           0x0000
+#define AL_FILTER_LOWPASS                        0x0001
+#define AL_FILTER_HIGHPASS                       0x0002
+#define AL_FILTER_BANDPASS                       0x0003
+
+
+/* Effect object function types. */
+typedef void (AL_APIENTRY *LPALGENEFFECTS)(ALsizei, ALuint*);
+typedef void (AL_APIENTRY *LPALDELETEEFFECTS)(ALsizei, const ALuint*);
+typedef ALboolean (AL_APIENTRY *LPALISEFFECT)(ALuint);
+typedef void (AL_APIENTRY *LPALEFFECTI)(ALuint, ALenum, ALint);
+typedef void (AL_APIENTRY *LPALEFFECTIV)(ALuint, ALenum, const ALint*);
+typedef void (AL_APIENTRY *LPALEFFECTF)(ALuint, ALenum, ALfloat);
+typedef void (AL_APIENTRY *LPALEFFECTFV)(ALuint, ALenum, const ALfloat*);
+typedef void (AL_APIENTRY *LPALGETEFFECTI)(ALuint, ALenum, ALint*);
+typedef void (AL_APIENTRY *LPALGETEFFECTIV)(ALuint, ALenum, ALint*);
+typedef void (AL_APIENTRY *LPALGETEFFECTF)(ALuint, ALenum, ALfloat*);
+typedef void (AL_APIENTRY *LPALGETEFFECTFV)(ALuint, ALenum, ALfloat*);
+
+/* Filter object function types. */
+typedef void (AL_APIENTRY *LPALGENFILTERS)(ALsizei, ALuint*);
+typedef void (AL_APIENTRY *LPALDELETEFILTERS)(ALsizei, const ALuint*);
+typedef ALboolean (AL_APIENTRY *LPALISFILTER)(ALuint);
+typedef void (AL_APIENTRY *LPALFILTERI)(ALuint, ALenum, ALint);
+typedef void (AL_APIENTRY *LPALFILTERIV)(ALuint, ALenum, const ALint*);
+typedef void (AL_APIENTRY *LPALFILTERF)(ALuint, ALenum, ALfloat);
+typedef void (AL_APIENTRY *LPALFILTERFV)(ALuint, ALenum, const ALfloat*);
+typedef void (AL_APIENTRY *LPALGETFILTERI)(ALuint, ALenum, ALint*);
+typedef void (AL_APIENTRY *LPALGETFILTERIV)(ALuint, ALenum, ALint*);
+typedef void (AL_APIENTRY *LPALGETFILTERF)(ALuint, ALenum, ALfloat*);
+typedef void (AL_APIENTRY *LPALGETFILTERFV)(ALuint, ALenum, ALfloat*);
+
+/* Auxiliary Effect Slot object function types. */
+typedef void (AL_APIENTRY *LPALGENAUXILIARYEFFECTSLOTS)(ALsizei, ALuint*);
+typedef void (AL_APIENTRY *LPALDELETEAUXILIARYEFFECTSLOTS)(ALsizei, const ALuint*);
+typedef ALboolean (AL_APIENTRY *LPALISAUXILIARYEFFECTSLOT)(ALuint);
+typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint);
+typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, const ALint*);
+typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat);
+typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, const ALfloat*);
+typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint*);
+typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, ALint*);
+typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat*);
+typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, ALfloat*);
+
+#ifdef AL_ALEXT_PROTOTYPES
+AL_API ALvoid AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects);
+AL_API ALvoid AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects);
+AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect);
+AL_API ALvoid AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint iValue);
+AL_API ALvoid AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *piValues);
+AL_API ALvoid AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat flValue);
+AL_API ALvoid AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat *pflValues);
+AL_API ALvoid AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *piValue);
+AL_API ALvoid AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *piValues);
+AL_API ALvoid AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *pflValue);
+AL_API ALvoid AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *pflValues);
+
+AL_API ALvoid AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters);
+AL_API ALvoid AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters);
+AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter);
+AL_API ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint iValue);
+AL_API ALvoid AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *piValues);
+AL_API ALvoid AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat flValue);
+AL_API ALvoid AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat *pflValues);
+AL_API ALvoid AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *piValue);
+AL_API ALvoid AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *piValues);
+AL_API ALvoid AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *pflValue);
+AL_API ALvoid AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *pflValues);
+
+AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots);
+AL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *effectslots);
+AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot);
+AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint iValue);
+AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, const ALint *piValues);
+AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat flValue);
+AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const ALfloat *pflValues);
+AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *piValue);
+AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues);
+AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *pflValue);
+AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues);
+#endif
+
+/* Filter ranges and defaults. */
+
+/* Lowpass filter */
+#define AL_LOWPASS_MIN_GAIN                      (0.0f)
+#define AL_LOWPASS_MAX_GAIN                      (1.0f)
+#define AL_LOWPASS_DEFAULT_GAIN                  (1.0f)
+
+#define AL_LOWPASS_MIN_GAINHF                    (0.0f)
+#define AL_LOWPASS_MAX_GAINHF                    (1.0f)
+#define AL_LOWPASS_DEFAULT_GAINHF                (1.0f)
+
+/* Highpass filter */
+#define AL_HIGHPASS_MIN_GAIN                     (0.0f)
+#define AL_HIGHPASS_MAX_GAIN                     (1.0f)
+#define AL_HIGHPASS_DEFAULT_GAIN                 (1.0f)
+
+#define AL_HIGHPASS_MIN_GAINLF                   (0.0f)
+#define AL_HIGHPASS_MAX_GAINLF                   (1.0f)
+#define AL_HIGHPASS_DEFAULT_GAINLF               (1.0f)
+
+/* Bandpass filter */
+#define AL_BANDPASS_MIN_GAIN                     (0.0f)
+#define AL_BANDPASS_MAX_GAIN                     (1.0f)
+#define AL_BANDPASS_DEFAULT_GAIN                 (1.0f)
+
+#define AL_BANDPASS_MIN_GAINHF                   (0.0f)
+#define AL_BANDPASS_MAX_GAINHF                   (1.0f)
+#define AL_BANDPASS_DEFAULT_GAINHF               (1.0f)
+
+#define AL_BANDPASS_MIN_GAINLF                   (0.0f)
+#define AL_BANDPASS_MAX_GAINLF                   (1.0f)
+#define AL_BANDPASS_DEFAULT_GAINLF               (1.0f)
+
+
+/* Effect parameter ranges and defaults. */
+
+/* Standard reverb effect */
+#define AL_REVERB_MIN_DENSITY                    (0.0f)
+#define AL_REVERB_MAX_DENSITY                    (1.0f)
+#define AL_REVERB_DEFAULT_DENSITY                (1.0f)
+
+#define AL_REVERB_MIN_DIFFUSION                  (0.0f)
+#define AL_REVERB_MAX_DIFFUSION                  (1.0f)
+#define AL_REVERB_DEFAULT_DIFFUSION              (1.0f)
+
+#define AL_REVERB_MIN_GAIN                       (0.0f)
+#define AL_REVERB_MAX_GAIN                       (1.0f)
+#define AL_REVERB_DEFAULT_GAIN                   (0.32f)
+
+#define AL_REVERB_MIN_GAINHF                     (0.0f)
+#define AL_REVERB_MAX_GAINHF                     (1.0f)
+#define AL_REVERB_DEFAULT_GAINHF                 (0.89f)
+
+#define AL_REVERB_MIN_DECAY_TIME                 (0.1f)
+#define AL_REVERB_MAX_DECAY_TIME                 (20.0f)
+#define AL_REVERB_DEFAULT_DECAY_TIME             (1.49f)
+
+#define AL_REVERB_MIN_DECAY_HFRATIO              (0.1f)
+#define AL_REVERB_MAX_DECAY_HFRATIO              (2.0f)
+#define AL_REVERB_DEFAULT_DECAY_HFRATIO          (0.83f)
+
+#define AL_REVERB_MIN_REFLECTIONS_GAIN           (0.0f)
+#define AL_REVERB_MAX_REFLECTIONS_GAIN           (3.16f)
+#define AL_REVERB_DEFAULT_REFLECTIONS_GAIN       (0.05f)
+
+#define AL_REVERB_MIN_REFLECTIONS_DELAY          (0.0f)
+#define AL_REVERB_MAX_REFLECTIONS_DELAY          (0.3f)
+#define AL_REVERB_DEFAULT_REFLECTIONS_DELAY      (0.007f)
+
+#define AL_REVERB_MIN_LATE_REVERB_GAIN           (0.0f)
+#define AL_REVERB_MAX_LATE_REVERB_GAIN           (10.0f)
+#define AL_REVERB_DEFAULT_LATE_REVERB_GAIN       (1.26f)
+
+#define AL_REVERB_MIN_LATE_REVERB_DELAY          (0.0f)
+#define AL_REVERB_MAX_LATE_REVERB_DELAY          (0.1f)
+#define AL_REVERB_DEFAULT_LATE_REVERB_DELAY      (0.011f)
+
+#define AL_REVERB_MIN_AIR_ABSORPTION_GAINHF      (0.892f)
+#define AL_REVERB_MAX_AIR_ABSORPTION_GAINHF      (1.0f)
+#define AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF  (0.994f)
+
+#define AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR        (0.0f)
+#define AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR        (10.0f)
+#define AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR    (0.0f)
+
+#define AL_REVERB_MIN_DECAY_HFLIMIT              AL_FALSE
+#define AL_REVERB_MAX_DECAY_HFLIMIT              AL_TRUE
+#define AL_REVERB_DEFAULT_DECAY_HFLIMIT          AL_TRUE
+
+/* EAX reverb effect */
+#define AL_EAXREVERB_MIN_DENSITY                 (0.0f)
+#define AL_EAXREVERB_MAX_DENSITY                 (1.0f)
+#define AL_EAXREVERB_DEFAULT_DENSITY             (1.0f)
+
+#define AL_EAXREVERB_MIN_DIFFUSION               (0.0f)
+#define AL_EAXREVERB_MAX_DIFFUSION               (1.0f)
+#define AL_EAXREVERB_DEFAULT_DIFFUSION           (1.0f)
+
+#define AL_EAXREVERB_MIN_GAIN                    (0.0f)
+#define AL_EAXREVERB_MAX_GAIN                    (1.0f)
+#define AL_EAXREVERB_DEFAULT_GAIN                (0.32f)
+
+#define AL_EAXREVERB_MIN_GAINHF                  (0.0f)
+#define AL_EAXREVERB_MAX_GAINHF                  (1.0f)
+#define AL_EAXREVERB_DEFAULT_GAINHF              (0.89f)
+
+#define AL_EAXREVERB_MIN_GAINLF                  (0.0f)
+#define AL_EAXREVERB_MAX_GAINLF                  (1.0f)
+#define AL_EAXREVERB_DEFAULT_GAINLF              (1.0f)
+
+#define AL_EAXREVERB_MIN_DECAY_TIME              (0.1f)
+#define AL_EAXREVERB_MAX_DECAY_TIME              (20.0f)
+#define AL_EAXREVERB_DEFAULT_DECAY_TIME          (1.49f)
+
+#define AL_EAXREVERB_MIN_DECAY_HFRATIO           (0.1f)
+#define AL_EAXREVERB_MAX_DECAY_HFRATIO           (2.0f)
+#define AL_EAXREVERB_DEFAULT_DECAY_HFRATIO       (0.83f)
+
+#define AL_EAXREVERB_MIN_DECAY_LFRATIO           (0.1f)
+#define AL_EAXREVERB_MAX_DECAY_LFRATIO           (2.0f)
+#define AL_EAXREVERB_DEFAULT_DECAY_LFRATIO       (1.0f)
+
+#define AL_EAXREVERB_MIN_REFLECTIONS_GAIN        (0.0f)
+#define AL_EAXREVERB_MAX_REFLECTIONS_GAIN        (3.16f)
+#define AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN    (0.05f)
+
+#define AL_EAXREVERB_MIN_REFLECTIONS_DELAY       (0.0f)
+#define AL_EAXREVERB_MAX_REFLECTIONS_DELAY       (0.3f)
+#define AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY   (0.007f)
+
+#define AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ (0.0f)
+
+#define AL_EAXREVERB_MIN_LATE_REVERB_GAIN        (0.0f)
+#define AL_EAXREVERB_MAX_LATE_REVERB_GAIN        (10.0f)
+#define AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN    (1.26f)
+
+#define AL_EAXREVERB_MIN_LATE_REVERB_DELAY       (0.0f)
+#define AL_EAXREVERB_MAX_LATE_REVERB_DELAY       (0.1f)
+#define AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY   (0.011f)
+
+#define AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ (0.0f)
+
+#define AL_EAXREVERB_MIN_ECHO_TIME               (0.075f)
+#define AL_EAXREVERB_MAX_ECHO_TIME               (0.25f)
+#define AL_EAXREVERB_DEFAULT_ECHO_TIME           (0.25f)
+
+#define AL_EAXREVERB_MIN_ECHO_DEPTH              (0.0f)
+#define AL_EAXREVERB_MAX_ECHO_DEPTH              (1.0f)
+#define AL_EAXREVERB_DEFAULT_ECHO_DEPTH          (0.0f)
+
+#define AL_EAXREVERB_MIN_MODULATION_TIME         (0.04f)
+#define AL_EAXREVERB_MAX_MODULATION_TIME         (4.0f)
+#define AL_EAXREVERB_DEFAULT_MODULATION_TIME     (0.25f)
+
+#define AL_EAXREVERB_MIN_MODULATION_DEPTH        (0.0f)
+#define AL_EAXREVERB_MAX_MODULATION_DEPTH        (1.0f)
+#define AL_EAXREVERB_DEFAULT_MODULATION_DEPTH    (0.0f)
+
+#define AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF   (0.892f)
+#define AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF   (1.0f)
+#define AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF (0.994f)
+
+#define AL_EAXREVERB_MIN_HFREFERENCE             (1000.0f)
+#define AL_EAXREVERB_MAX_HFREFERENCE             (20000.0f)
+#define AL_EAXREVERB_DEFAULT_HFREFERENCE         (5000.0f)
+
+#define AL_EAXREVERB_MIN_LFREFERENCE             (20.0f)
+#define AL_EAXREVERB_MAX_LFREFERENCE             (1000.0f)
+#define AL_EAXREVERB_DEFAULT_LFREFERENCE         (250.0f)
+
+#define AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR     (0.0f)
+#define AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR     (10.0f)
+#define AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f)
+
+#define AL_EAXREVERB_MIN_DECAY_HFLIMIT           AL_FALSE
+#define AL_EAXREVERB_MAX_DECAY_HFLIMIT           AL_TRUE
+#define AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT       AL_TRUE
+
+/* Chorus effect */
+#define AL_CHORUS_WAVEFORM_SINUSOID              (0)
+#define AL_CHORUS_WAVEFORM_TRIANGLE              (1)
+
+#define AL_CHORUS_MIN_WAVEFORM                   (0)
+#define AL_CHORUS_MAX_WAVEFORM                   (1)
+#define AL_CHORUS_DEFAULT_WAVEFORM               (1)
+
+#define AL_CHORUS_MIN_PHASE                      (-180)
+#define AL_CHORUS_MAX_PHASE                      (180)
+#define AL_CHORUS_DEFAULT_PHASE                  (90)
+
+#define AL_CHORUS_MIN_RATE                       (0.0f)
+#define AL_CHORUS_MAX_RATE                       (10.0f)
+#define AL_CHORUS_DEFAULT_RATE                   (1.1f)
+
+#define AL_CHORUS_MIN_DEPTH                      (0.0f)
+#define AL_CHORUS_MAX_DEPTH                      (1.0f)
+#define AL_CHORUS_DEFAULT_DEPTH                  (0.1f)
+
+#define AL_CHORUS_MIN_FEEDBACK                   (-1.0f)
+#define AL_CHORUS_MAX_FEEDBACK                   (1.0f)
+#define AL_CHORUS_DEFAULT_FEEDBACK               (0.25f)
+
+#define AL_CHORUS_MIN_DELAY                      (0.0f)
+#define AL_CHORUS_MAX_DELAY                      (0.016f)
+#define AL_CHORUS_DEFAULT_DELAY                  (0.016f)
+
+/* Distortion effect */
+#define AL_DISTORTION_MIN_EDGE                   (0.0f)
+#define AL_DISTORTION_MAX_EDGE                   (1.0f)
+#define AL_DISTORTION_DEFAULT_EDGE               (0.2f)
+
+#define AL_DISTORTION_MIN_GAIN                   (0.01f)
+#define AL_DISTORTION_MAX_GAIN                   (1.0f)
+#define AL_DISTORTION_DEFAULT_GAIN               (0.05f)
+
+#define AL_DISTORTION_MIN_LOWPASS_CUTOFF         (80.0f)
+#define AL_DISTORTION_MAX_LOWPASS_CUTOFF         (24000.0f)
+#define AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF     (8000.0f)
+
+#define AL_DISTORTION_MIN_EQCENTER               (80.0f)
+#define AL_DISTORTION_MAX_EQCENTER               (24000.0f)
+#define AL_DISTORTION_DEFAULT_EQCENTER           (3600.0f)
+
+#define AL_DISTORTION_MIN_EQBANDWIDTH            (80.0f)
+#define AL_DISTORTION_MAX_EQBANDWIDTH            (24000.0f)
+#define AL_DISTORTION_DEFAULT_EQBANDWIDTH        (3600.0f)
+
+/* Echo effect */
+#define AL_ECHO_MIN_DELAY                        (0.0f)
+#define AL_ECHO_MAX_DELAY                        (0.207f)
+#define AL_ECHO_DEFAULT_DELAY                    (0.1f)
+
+#define AL_ECHO_MIN_LRDELAY                      (0.0f)
+#define AL_ECHO_MAX_LRDELAY                      (0.404f)
+#define AL_ECHO_DEFAULT_LRDELAY                  (0.1f)
+
+#define AL_ECHO_MIN_DAMPING                      (0.0f)
+#define AL_ECHO_MAX_DAMPING                      (0.99f)
+#define AL_ECHO_DEFAULT_DAMPING                  (0.5f)
+
+#define AL_ECHO_MIN_FEEDBACK                     (0.0f)
+#define AL_ECHO_MAX_FEEDBACK                     (1.0f)
+#define AL_ECHO_DEFAULT_FEEDBACK                 (0.5f)
+
+#define AL_ECHO_MIN_SPREAD                       (-1.0f)
+#define AL_ECHO_MAX_SPREAD                       (1.0f)
+#define AL_ECHO_DEFAULT_SPREAD                   (-1.0f)
+
+/* Flanger effect */
+#define AL_FLANGER_WAVEFORM_SINUSOID             (0)
+#define AL_FLANGER_WAVEFORM_TRIANGLE             (1)
+
+#define AL_FLANGER_MIN_WAVEFORM                  (0)
+#define AL_FLANGER_MAX_WAVEFORM                  (1)
+#define AL_FLANGER_DEFAULT_WAVEFORM              (1)
+
+#define AL_FLANGER_MIN_PHASE                     (-180)
+#define AL_FLANGER_MAX_PHASE                     (180)
+#define AL_FLANGER_DEFAULT_PHASE                 (0)
+
+#define AL_FLANGER_MIN_RATE                      (0.0f)
+#define AL_FLANGER_MAX_RATE                      (10.0f)
+#define AL_FLANGER_DEFAULT_RATE                  (0.27f)
+
+#define AL_FLANGER_MIN_DEPTH                     (0.0f)
+#define AL_FLANGER_MAX_DEPTH                     (1.0f)
+#define AL_FLANGER_DEFAULT_DEPTH                 (1.0f)
+
+#define AL_FLANGER_MIN_FEEDBACK                  (-1.0f)
+#define AL_FLANGER_MAX_FEEDBACK                  (1.0f)
+#define AL_FLANGER_DEFAULT_FEEDBACK              (-0.5f)
+
+#define AL_FLANGER_MIN_DELAY                     (0.0f)
+#define AL_FLANGER_MAX_DELAY                     (0.004f)
+#define AL_FLANGER_DEFAULT_DELAY                 (0.002f)
+
+/* Frequency shifter effect */
+#define AL_FREQUENCY_SHIFTER_MIN_FREQUENCY       (0.0f)
+#define AL_FREQUENCY_SHIFTER_MAX_FREQUENCY       (24000.0f)
+#define AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY   (0.0f)
+
+#define AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION  (0)
+#define AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION  (2)
+#define AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION (0)
+
+#define AL_FREQUENCY_SHIFTER_DIRECTION_DOWN      (0)
+#define AL_FREQUENCY_SHIFTER_DIRECTION_UP        (1)
+#define AL_FREQUENCY_SHIFTER_DIRECTION_OFF       (2)
+
+#define AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION (0)
+#define AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION (2)
+#define AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION (0)
+
+/* Vocal morpher effect */
+#define AL_VOCAL_MORPHER_MIN_PHONEMEA            (0)
+#define AL_VOCAL_MORPHER_MAX_PHONEMEA            (29)
+#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEA        (0)
+
+#define AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING (-24)
+#define AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING (24)
+#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING (0)
+
+#define AL_VOCAL_MORPHER_MIN_PHONEMEB            (0)
+#define AL_VOCAL_MORPHER_MAX_PHONEMEB            (29)
+#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEB        (10)
+
+#define AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING (-24)
+#define AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING (24)
+#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING (0)
+
+#define AL_VOCAL_MORPHER_PHONEME_A               (0)
+#define AL_VOCAL_MORPHER_PHONEME_E               (1)
+#define AL_VOCAL_MORPHER_PHONEME_I               (2)
+#define AL_VOCAL_MORPHER_PHONEME_O               (3)
+#define AL_VOCAL_MORPHER_PHONEME_U               (4)
+#define AL_VOCAL_MORPHER_PHONEME_AA              (5)
+#define AL_VOCAL_MORPHER_PHONEME_AE              (6)
+#define AL_VOCAL_MORPHER_PHONEME_AH              (7)
+#define AL_VOCAL_MORPHER_PHONEME_AO              (8)
+#define AL_VOCAL_MORPHER_PHONEME_EH              (9)
+#define AL_VOCAL_MORPHER_PHONEME_ER              (10)
+#define AL_VOCAL_MORPHER_PHONEME_IH              (11)
+#define AL_VOCAL_MORPHER_PHONEME_IY              (12)
+#define AL_VOCAL_MORPHER_PHONEME_UH              (13)
+#define AL_VOCAL_MORPHER_PHONEME_UW              (14)
+#define AL_VOCAL_MORPHER_PHONEME_B               (15)
+#define AL_VOCAL_MORPHER_PHONEME_D               (16)
+#define AL_VOCAL_MORPHER_PHONEME_F               (17)
+#define AL_VOCAL_MORPHER_PHONEME_G               (18)
+#define AL_VOCAL_MORPHER_PHONEME_J               (19)
+#define AL_VOCAL_MORPHER_PHONEME_K               (20)
+#define AL_VOCAL_MORPHER_PHONEME_L               (21)
+#define AL_VOCAL_MORPHER_PHONEME_M               (22)
+#define AL_VOCAL_MORPHER_PHONEME_N               (23)
+#define AL_VOCAL_MORPHER_PHONEME_P               (24)
+#define AL_VOCAL_MORPHER_PHONEME_R               (25)
+#define AL_VOCAL_MORPHER_PHONEME_S               (26)
+#define AL_VOCAL_MORPHER_PHONEME_T               (27)
+#define AL_VOCAL_MORPHER_PHONEME_V               (28)
+#define AL_VOCAL_MORPHER_PHONEME_Z               (29)
+
+#define AL_VOCAL_MORPHER_WAVEFORM_SINUSOID       (0)
+#define AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE       (1)
+#define AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH       (2)
+
+#define AL_VOCAL_MORPHER_MIN_WAVEFORM            (0)
+#define AL_VOCAL_MORPHER_MAX_WAVEFORM            (2)
+#define AL_VOCAL_MORPHER_DEFAULT_WAVEFORM        (0)
+
+#define AL_VOCAL_MORPHER_MIN_RATE                (0.0f)
+#define AL_VOCAL_MORPHER_MAX_RATE                (10.0f)
+#define AL_VOCAL_MORPHER_DEFAULT_RATE            (1.41f)
+
+/* Pitch shifter effect */
+#define AL_PITCH_SHIFTER_MIN_COARSE_TUNE         (-12)
+#define AL_PITCH_SHIFTER_MAX_COARSE_TUNE         (12)
+#define AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE     (12)
+
+#define AL_PITCH_SHIFTER_MIN_FINE_TUNE           (-50)
+#define AL_PITCH_SHIFTER_MAX_FINE_TUNE           (50)
+#define AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE       (0)
+
+/* Ring modulator effect */
+#define AL_RING_MODULATOR_MIN_FREQUENCY          (0.0f)
+#define AL_RING_MODULATOR_MAX_FREQUENCY          (8000.0f)
+#define AL_RING_MODULATOR_DEFAULT_FREQUENCY      (440.0f)
+
+#define AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF    (0.0f)
+#define AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF    (24000.0f)
+#define AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF (800.0f)
+
+#define AL_RING_MODULATOR_SINUSOID               (0)
+#define AL_RING_MODULATOR_SAWTOOTH               (1)
+#define AL_RING_MODULATOR_SQUARE                 (2)
+
+#define AL_RING_MODULATOR_MIN_WAVEFORM           (0)
+#define AL_RING_MODULATOR_MAX_WAVEFORM           (2)
+#define AL_RING_MODULATOR_DEFAULT_WAVEFORM       (0)
+
+/* Autowah effect */
+#define AL_AUTOWAH_MIN_ATTACK_TIME               (0.0001f)
+#define AL_AUTOWAH_MAX_ATTACK_TIME               (1.0f)
+#define AL_AUTOWAH_DEFAULT_ATTACK_TIME           (0.06f)
+
+#define AL_AUTOWAH_MIN_RELEASE_TIME              (0.0001f)
+#define AL_AUTOWAH_MAX_RELEASE_TIME              (1.0f)
+#define AL_AUTOWAH_DEFAULT_RELEASE_TIME          (0.06f)
+
+#define AL_AUTOWAH_MIN_RESONANCE                 (2.0f)
+#define AL_AUTOWAH_MAX_RESONANCE                 (1000.0f)
+#define AL_AUTOWAH_DEFAULT_RESONANCE             (1000.0f)
+
+#define AL_AUTOWAH_MIN_PEAK_GAIN                 (0.00003f)
+#define AL_AUTOWAH_MAX_PEAK_GAIN                 (31621.0f)
+#define AL_AUTOWAH_DEFAULT_PEAK_GAIN             (11.22f)
+
+/* Compressor effect */
+#define AL_COMPRESSOR_MIN_ONOFF                  (0)
+#define AL_COMPRESSOR_MAX_ONOFF                  (1)
+#define AL_COMPRESSOR_DEFAULT_ONOFF              (1)
+
+/* Equalizer effect */
+#define AL_EQUALIZER_MIN_LOW_GAIN                (0.126f)
+#define AL_EQUALIZER_MAX_LOW_GAIN                (7.943f)
+#define AL_EQUALIZER_DEFAULT_LOW_GAIN            (1.0f)
+
+#define AL_EQUALIZER_MIN_LOW_CUTOFF              (50.0f)
+#define AL_EQUALIZER_MAX_LOW_CUTOFF              (800.0f)
+#define AL_EQUALIZER_DEFAULT_LOW_CUTOFF          (200.0f)
+
+#define AL_EQUALIZER_MIN_MID1_GAIN               (0.126f)
+#define AL_EQUALIZER_MAX_MID1_GAIN               (7.943f)
+#define AL_EQUALIZER_DEFAULT_MID1_GAIN           (1.0f)
+
+#define AL_EQUALIZER_MIN_MID1_CENTER             (200.0f)
+#define AL_EQUALIZER_MAX_MID1_CENTER             (3000.0f)
+#define AL_EQUALIZER_DEFAULT_MID1_CENTER         (500.0f)
+
+#define AL_EQUALIZER_MIN_MID1_WIDTH              (0.01f)
+#define AL_EQUALIZER_MAX_MID1_WIDTH              (1.0f)
+#define AL_EQUALIZER_DEFAULT_MID1_WIDTH          (1.0f)
+
+#define AL_EQUALIZER_MIN_MID2_GAIN               (0.126f)
+#define AL_EQUALIZER_MAX_MID2_GAIN               (7.943f)
+#define AL_EQUALIZER_DEFAULT_MID2_GAIN           (1.0f)
+
+#define AL_EQUALIZER_MIN_MID2_CENTER             (1000.0f)
+#define AL_EQUALIZER_MAX_MID2_CENTER             (8000.0f)
+#define AL_EQUALIZER_DEFAULT_MID2_CENTER         (3000.0f)
+
+#define AL_EQUALIZER_MIN_MID2_WIDTH              (0.01f)
+#define AL_EQUALIZER_MAX_MID2_WIDTH              (1.0f)
+#define AL_EQUALIZER_DEFAULT_MID2_WIDTH          (1.0f)
+
+#define AL_EQUALIZER_MIN_HIGH_GAIN               (0.126f)
+#define AL_EQUALIZER_MAX_HIGH_GAIN               (7.943f)
+#define AL_EQUALIZER_DEFAULT_HIGH_GAIN           (1.0f)
+
+#define AL_EQUALIZER_MIN_HIGH_CUTOFF             (4000.0f)
+#define AL_EQUALIZER_MAX_HIGH_CUTOFF             (16000.0f)
+#define AL_EQUALIZER_DEFAULT_HIGH_CUTOFF         (6000.0f)
+
+
+/* Source parameter value ranges and defaults. */
+#define AL_MIN_AIR_ABSORPTION_FACTOR             (0.0f)
+#define AL_MAX_AIR_ABSORPTION_FACTOR             (10.0f)
+#define AL_DEFAULT_AIR_ABSORPTION_FACTOR         (0.0f)
+
+#define AL_MIN_ROOM_ROLLOFF_FACTOR               (0.0f)
+#define AL_MAX_ROOM_ROLLOFF_FACTOR               (10.0f)
+#define AL_DEFAULT_ROOM_ROLLOFF_FACTOR           (0.0f)
+
+#define AL_MIN_CONE_OUTER_GAINHF                 (0.0f)
+#define AL_MAX_CONE_OUTER_GAINHF                 (1.0f)
+#define AL_DEFAULT_CONE_OUTER_GAINHF             (1.0f)
+
+#define AL_MIN_DIRECT_FILTER_GAINHF_AUTO         AL_FALSE
+#define AL_MAX_DIRECT_FILTER_GAINHF_AUTO         AL_TRUE
+#define AL_DEFAULT_DIRECT_FILTER_GAINHF_AUTO     AL_TRUE
+
+#define AL_MIN_AUXILIARY_SEND_FILTER_GAIN_AUTO   AL_FALSE
+#define AL_MAX_AUXILIARY_SEND_FILTER_GAIN_AUTO   AL_TRUE
+#define AL_DEFAULT_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_TRUE
+
+#define AL_MIN_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_FALSE
+#define AL_MAX_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_TRUE
+#define AL_DEFAULT_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_TRUE
+
+
+/* Listener parameter value ranges and defaults. */
+#define AL_MIN_METERS_PER_UNIT                   FLT_MIN
+#define AL_MAX_METERS_PER_UNIT                   FLT_MAX
+#define AL_DEFAULT_METERS_PER_UNIT               (1.0f)
+
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif
+
+#endif /* AL_EFX_H */

+ 913 - 0
audio/al/al.go

@@ -0,0 +1,913 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+Package al implements the Go bindings of a subset of the functions of the OpenAL C library.
+
+It also implements a loader so the library can be dynamically loaded.
+The OpenAL documentation can be accessed at https://openal.org/documentation/
+
+*/
+package al
+
+/*
+#cgo darwin   CFLAGS:  -DGO_DARWIN
+#cgo linux    CFLAGS:  -DGO_LINUX   -I.
+#cgo windows  CFLAGS:  -DGO_WINDOWS -I.
+#cgo darwin   LDFLAGS:
+#cgo linux    LDFLAGS: -ldl
+#cgo windows  LDFLAGS:
+
+#ifdef GO_DARWIN
+#include <stdlib.h>
+#include "AL/al.h"
+#include "AL/alc.h"
+#include "AL/efx.h"
+#endif
+
+#ifdef GO_LINUX
+#include <stdlib.h>
+#include "AL/al.h"
+#include "AL/alc.h"
+#include "AL/efx.h"
+#endif
+
+#ifdef GO_WINDOWS
+#include <stdlib.h>
+#include "AL/al.h"
+#include "AL/alc.h"
+#include "AL/efx.h"
+#endif
+
+#include "loader.h"
+*/
+import "C"
+
+import (
+	"fmt"
+	"unsafe"
+)
+
+// AL constants
+const (
+	None                    = C.AL_NONE
+	False                   = C.AL_FALSE
+	True                    = C.AL_TRUE
+	SourceRelative          = C.AL_SOURCE_RELATIVE
+	ConeInnerAngle          = C.AL_CONE_INNER_ANGLE
+	ConeOuterAngle          = C.AL_CONE_OUTER_ANGLE
+	Pitch                   = C.AL_PITCH
+	Position                = C.AL_POSITION
+	Direction               = C.AL_DIRECTION
+	Velocity                = C.AL_VELOCITY
+	Looping                 = C.AL_LOOPING
+	Buffer                  = C.AL_BUFFER
+	Gain                    = C.AL_GAIN
+	MinGain                 = C.AL_MIN_GAIN
+	MaxGain                 = C.AL_MAX_GAIN
+	Orientation             = C.AL_ORIENTATION
+	SourceState             = C.AL_SOURCE_STATE
+	Initial                 = C.AL_INITIAL
+	Playing                 = C.AL_PLAYING
+	Paused                  = C.AL_PAUSED
+	Stopped                 = C.AL_STOPPED
+	BuffersQueued           = C.AL_BUFFERS_QUEUED
+	BuffersProcessed        = C.AL_BUFFERS_PROCESSED
+	ReferenceDistance       = C.AL_REFERENCE_DISTANCE
+	RolloffFactor           = C.AL_ROLLOFF_FACTOR
+	ConeOuterGain           = C.AL_CONE_OUTER_GAIN
+	MaxDistance             = C.AL_MAX_DISTANCE
+	SecOffset               = C.AL_SEC_OFFSET
+	SampleOffset            = C.AL_SAMPLE_OFFSET
+	ByteOffset              = C.AL_BYTE_OFFSET
+	SourceType              = C.AL_SOURCE_TYPE
+	Static                  = C.AL_STATIC
+	Streaming               = C.AL_STREAMING
+	Undetermined            = C.AL_UNDETERMINED
+	FormatMono8             = C.AL_FORMAT_MONO8
+	FormatMono16            = C.AL_FORMAT_MONO16
+	FormatStereo8           = C.AL_FORMAT_STEREO8
+	FormatStereo16          = C.AL_FORMAT_STEREO16
+	Frequency               = C.AL_FREQUENCY
+	Bits                    = C.AL_BITS
+	Channels                = C.AL_CHANNELS
+	Size                    = C.AL_SIZE
+	Unused                  = C.AL_UNUSED
+	Pending                 = C.AL_PENDING
+	Processed               = C.AL_PROCESSED
+	NoError                 = C.AL_NO_ERROR
+	InvalidName             = C.AL_INVALID_NAME
+	InvalidEnum             = C.AL_INVALID_ENUM
+	InvalidValue            = C.AL_INVALID_VALUE
+	InvalidOperation        = C.AL_INVALID_OPERATION
+	OutOfMemory             = C.AL_OUT_OF_MEMORY
+	Vendor                  = C.AL_VENDOR
+	Version                 = C.AL_VERSION
+	Renderer                = C.AL_RENDERER
+	Extensions              = C.AL_EXTENSIONS
+	DopplerFactor           = C.AL_DOPPLER_FACTOR
+	DopplerVelocity         = C.AL_DOPPLER_VELOCITY
+	SpeedOfSound            = C.AL_SPEED_OF_SOUND
+	DistanceModel           = C.AL_DISTANCE_MODEL
+	InverseDistance         = C.AL_INVERSE_DISTANCE
+	InverseDistanceClamped  = C.AL_INVERSE_DISTANCE_CLAMPED
+	LinearDistance          = C.AL_LINEAR_DISTANCE
+	LinearDistanceClamped   = C.AL_LINEAR_DISTANCE_CLAMPED
+	ExponentDistance        = C.AL_EXPONENT_DISTANCE
+	ExponentDistanceClamped = C.AL_EXPONENT_DISTANCE_CLAMPED
+)
+
+// ALC constants
+const (
+	AttributesSize                = C.ALC_ATTRIBUTES_SIZE
+	AllAttributes                 = C.ALC_ALL_ATTRIBUTES
+	DefaultDeviceSpecifier        = C.ALC_DEFAULT_DEVICE_SPECIFIER
+	DeviceSpecifier               = C.ALC_DEVICE_SPECIFIER
+	CtxExtensions                 = C.ALC_EXTENSIONS
+	ExtCapture                    = C.ALC_EXT_CAPTURE
+	CaptureDeviceSpecifier        = C.ALC_CAPTURE_DEVICE_SPECIFIER
+	CaptureDefaultDeviceSpecifier = C.ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER
+	CtxCaptureSamples             = C.ALC_CAPTURE_SAMPLES
+	EnumerateAllExt               = C.ALC_ENUMERATE_ALL_EXT
+	DefaultAllDevicesSpecifier    = C.ALC_DEFAULT_ALL_DEVICES_SPECIFIER
+	AllDevicesSpecifier           = C.ALC_ALL_DEVICES_SPECIFIER
+)
+
+// AL EFX extension constants
+const (
+	EFX_MAJOR_VERSION                       = C.ALC_EFX_MAJOR_VERSION
+	EFX_MINOR_VERSION                       = C.ALC_EFX_MINOR_VERSION
+	MAX_AUXILIARY_SENDS                     = C.ALC_MAX_AUXILIARY_SENDS
+	METERS_PER_UNIT                         = C.AL_METERS_PER_UNIT
+	AL_DIRECT_FILTER                        = C.AL_DIRECT_FILTER
+	AL_AUXILIARY_SEND_FILTER                = C.AL_AUXILIARY_SEND_FILTER
+	AL_AIR_ABSORPTION_FACTOR                = C.AL_AIR_ABSORPTION_FACTOR
+	AL_ROOM_ROLLOFF_FACTOR                  = C.AL_ROOM_ROLLOFF_FACTOR
+	AL_CONE_OUTER_GAINHF                    = C.AL_CONE_OUTER_GAINHF
+	AL_DIRECT_FILTER_GAINHF_AUTO            = C.AL_DIRECT_FILTER_GAINHF_AUTO
+	AL_AUXILIARY_SEND_FILTER_GAIN_AUTO      = C.AL_AUXILIARY_SEND_FILTER_GAIN_AUTO
+	AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO    = C.AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO
+	AL_REVERB_DENSITY                       = C.AL_REVERB_DENSITY
+	AL_REVERB_DIFFUSION                     = C.AL_REVERB_DIFFUSION
+	AL_REVERB_GAIN                          = C.AL_REVERB_GAIN
+	AL_REVERB_GAINHF                        = C.AL_REVERB_GAINHF
+	AL_REVERB_DECAY_TIME                    = C.AL_REVERB_DECAY_TIME
+	AL_REVERB_DECAY_HFRATIO                 = C.AL_REVERB_DECAY_HFRATIO
+	AL_REVERB_REFLECTIONS_GAIN              = C.AL_REVERB_REFLECTIONS_GAIN
+	AL_REVERB_REFLECTIONS_DELAY             = C.AL_REVERB_REFLECTIONS_DELAY
+	AL_REVERB_LATE_REVERB_GAIN              = C.AL_REVERB_LATE_REVERB_GAIN
+	AL_REVERB_LATE_REVERB_DELAY             = C.AL_REVERB_LATE_REVERB_DELAY
+	AL_REVERB_AIR_ABSORPTION_GAINHF         = C.AL_REVERB_AIR_ABSORPTION_GAINHF
+	AL_REVERB_ROOM_ROLLOFF_FACTOR           = C.AL_REVERB_ROOM_ROLLOFF_FACTOR
+	AL_REVERB_DECAY_HFLIMIT                 = C.AL_REVERB_DECAY_HFLIMIT
+	AL_EAXREVERB_DENSITY                    = C.AL_EAXREVERB_DENSITY
+	AL_EAXREVERB_DIFFUSION                  = C.AL_EAXREVERB_DIFFUSION
+	AL_EAXREVERB_GAIN                       = C.AL_EAXREVERB_GAIN
+	AL_EAXREVERB_GAINHF                     = C.AL_EAXREVERB_GAINHF
+	AL_EAXREVERB_GAINLF                     = C.AL_EAXREVERB_GAINLF
+	AL_EAXREVERB_DECAY_TIME                 = C.AL_EAXREVERB_DECAY_TIME
+	AL_EAXREVERB_DECAY_HFRATIO              = C.AL_EAXREVERB_DECAY_HFRATIO
+	AL_EAXREVERB_DECAY_LFRATIO              = C.AL_EAXREVERB_DECAY_LFRATIO
+	AL_EAXREVERB_REFLECTIONS_GAIN           = C.AL_EAXREVERB_REFLECTIONS_GAIN
+	AL_EAXREVERB_REFLECTIONS_DELAY          = C.AL_EAXREVERB_REFLECTIONS_DELAY
+	AL_EAXREVERB_REFLECTIONS_PAN            = C.AL_EAXREVERB_REFLECTIONS_PAN
+	AL_EAXREVERB_LATE_REVERB_GAIN           = C.AL_EAXREVERB_LATE_REVERB_GAIN
+	AL_EAXREVERB_LATE_REVERB_DELAY          = C.AL_EAXREVERB_LATE_REVERB_DELAY
+	AL_EAXREVERB_LATE_REVERB_PAN            = C.AL_EAXREVERB_LATE_REVERB_PAN
+	AL_EAXREVERB_ECHO_TIME                  = C.AL_EAXREVERB_ECHO_TIME
+	AL_EAXREVERB_ECHO_DEPTH                 = C.AL_EAXREVERB_ECHO_DEPTH
+	AL_EAXREVERB_MODULATION_TIME            = C.AL_EAXREVERB_MODULATION_TIME
+	AL_EAXREVERB_MODULATION_DEPTH           = C.AL_EAXREVERB_MODULATION_DEPTH
+	AL_EAXREVERB_AIR_ABSORPTION_GAINHF      = C.AL_EAXREVERB_AIR_ABSORPTION_GAINHF
+	AL_EAXREVERB_HFREFERENCE                = C.AL_EAXREVERB_HFREFERENCE
+	AL_EAXREVERB_LFREFERENCE                = C.AL_EAXREVERB_LFREFERENCE
+	AL_EAXREVERB_ROOM_ROLLOFF_FACTOR        = C.AL_EAXREVERB_ROOM_ROLLOFF_FACTOR
+	AL_EAXREVERB_DECAY_HFLIMIT              = C.AL_EAXREVERB_DECAY_HFLIMIT
+	AL_CHORUS_WAVEFORM                      = C.AL_CHORUS_WAVEFORM
+	AL_CHORUS_PHASE                         = C.AL_CHORUS_PHASE
+	AL_CHORUS_RATE                          = C.AL_CHORUS_RATE
+	AL_CHORUS_DEPTH                         = C.AL_CHORUS_DEPTH
+	AL_CHORUS_FEEDBACK                      = C.AL_CHORUS_FEEDBACK
+	AL_CHORUS_DELAY                         = C.AL_CHORUS_DELAY
+	AL_DISTORTION_EDGE                      = C.AL_DISTORTION_EDGE
+	AL_DISTORTION_GAIN                      = C.AL_DISTORTION_GAIN
+	AL_DISTORTION_LOWPASS_CUTOFF            = C.AL_DISTORTION_LOWPASS_CUTOFF
+	AL_DISTORTION_EQCENTER                  = C.AL_DISTORTION_EQCENTER
+	AL_DISTORTION_EQBANDWIDTH               = C.AL_DISTORTION_EQBANDWIDTH
+	AL_ECHO_DELAY                           = C.AL_ECHO_DELAY
+	AL_ECHO_LRDELAY                         = C.AL_ECHO_LRDELAY
+	AL_ECHO_DAMPING                         = C.AL_ECHO_DAMPING
+	AL_ECHO_FEEDBACK                        = C.AL_ECHO_FEEDBACK
+	AL_ECHO_SPREAD                          = C.AL_ECHO_SPREAD
+	AL_FLANGER_WAVEFORM                     = C.AL_FLANGER_WAVEFORM
+	AL_FLANGER_PHASE                        = C.AL_FLANGER_PHASE
+	AL_FLANGER_RATE                         = C.AL_FLANGER_RATE
+	AL_FLANGER_DEPTH                        = C.AL_FLANGER_DEPTH
+	AL_FLANGER_FEEDBACK                     = C.AL_FLANGER_FEEDBACK
+	AL_FLANGER_DELAY                        = C.AL_FLANGER_DELAY
+	AL_FREQUENCY_SHIFTER_FREQUENCY          = C.AL_FREQUENCY_SHIFTER_FREQUENCY
+	AL_FREQUENCY_SHIFTER_LEFT_DIRECTION     = C.AL_FREQUENCY_SHIFTER_LEFT_DIRECTION
+	AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION    = C.AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION
+	AL_VOCAL_MORPHER_PHONEMEA               = C.AL_VOCAL_MORPHER_PHONEMEA
+	AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING = C.AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING
+	AL_VOCAL_MORPHER_PHONEMEB               = C.AL_VOCAL_MORPHER_PHONEMEB
+	AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING = C.AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING
+	AL_VOCAL_MORPHER_WAVEFORM               = C.AL_VOCAL_MORPHER_WAVEFORM
+	AL_VOCAL_MORPHER_RATE                   = C.AL_VOCAL_MORPHER_RATE
+	AL_PITCH_SHIFTER_COARSE_TUNE            = C.AL_PITCH_SHIFTER_COARSE_TUNE
+	AL_PITCH_SHIFTER_FINE_TUNE              = C.AL_PITCH_SHIFTER_FINE_TUNE
+	AL_RING_MODULATOR_FREQUENCY             = C.AL_RING_MODULATOR_FREQUENCY
+	AL_RING_MODULATOR_HIGHPASS_CUTOFF       = C.AL_RING_MODULATOR_HIGHPASS_CUTOFF
+	AL_RING_MODULATOR_WAVEFORM              = C.AL_RING_MODULATOR_WAVEFORM
+	AL_AUTOWAH_ATTACK_TIME                  = C.AL_AUTOWAH_ATTACK_TIME
+	AL_AUTOWAH_RELEASE_TIME                 = C.AL_AUTOWAH_RELEASE_TIME
+	AL_AUTOWAH_RESONANCE                    = C.AL_AUTOWAH_RESONANCE
+	AL_AUTOWAH_PEAK_GAIN                    = C.AL_AUTOWAH_PEAK_GAIN
+	AL_COMPRESSOR_ONOFF                     = C.AL_COMPRESSOR_ONOFF
+	AL_EQUALIZER_LOW_GAIN                   = C.AL_EQUALIZER_LOW_GAIN
+	AL_EQUALIZER_LOW_CUTOFF                 = C.AL_EQUALIZER_LOW_CUTOFF
+	AL_EQUALIZER_MID1_GAIN                  = C.AL_EQUALIZER_MID1_GAIN
+	AL_EQUALIZER_MID1_CENTER                = C.AL_EQUALIZER_MID1_CENTER
+	AL_EQUALIZER_MID1_WIDTH                 = C.AL_EQUALIZER_MID1_WIDTH
+	AL_EQUALIZER_MID2_GAIN                  = C.AL_EQUALIZER_MID2_GAIN
+	AL_EQUALIZER_MID2_CENTER                = C.AL_EQUALIZER_MID2_CENTER
+	AL_EQUALIZER_MID2_WIDTH                 = C.AL_EQUALIZER_MID2_WIDTH
+	AL_EQUALIZER_HIGH_GAIN                  = C.AL_EQUALIZER_HIGH_GAIN
+	AL_EQUALIZER_HIGH_CUTOFF                = C.AL_EQUALIZER_HIGH_CUTOFF
+	AL_EFFECT_FIRST_PARAMETER               = C.AL_EFFECT_FIRST_PARAMETER
+	AL_EFFECT_LAST_PARAMETER                = C.AL_EFFECT_LAST_PARAMETER
+	AL_EFFECT_TYPE                          = C.AL_EFFECT_TYPE
+	AL_EFFECT_NULL                          = C.AL_EFFECT_NULL
+	AL_EFFECT_REVERB                        = C.AL_EFFECT_REVERB
+	AL_EFFECT_CHORUS                        = C.AL_EFFECT_CHORUS
+	AL_EFFECT_DISTORTION                    = C.AL_EFFECT_DISTORTION
+	AL_EFFECT_ECHO                          = C.AL_EFFECT_ECHO
+	AL_EFFECT_FLANGER                       = C.AL_EFFECT_FLANGER
+	AL_EFFECT_FREQUENCY_SHIFTER             = C.AL_EFFECT_FREQUENCY_SHIFTER
+	AL_EFFECT_VOCAL_MORPHER                 = C.AL_EFFECT_VOCAL_MORPHER
+	AL_EFFECT_PITCH_SHIFTER                 = C.AL_EFFECT_PITCH_SHIFTER
+	AL_EFFECT_RING_MODULATOR                = C.AL_EFFECT_RING_MODULATOR
+	AL_EFFECT_AUTOWAH                       = C.AL_EFFECT_AUTOWAH
+	AL_EFFECT_COMPRESSOR                    = C.AL_EFFECT_COMPRESSOR
+	AL_EFFECT_EQUALIZER                     = C.AL_EFFECT_EQUALIZER
+	AL_EFFECT_EAXREVERB                     = C.AL_EFFECT_EAXREVERB
+	AL_EFFECTSLOT_EFFECT                    = C.AL_EFFECTSLOT_EFFECT
+	AL_EFFECTSLOT_GAIN                      = C.AL_EFFECTSLOT_GAIN
+	AL_EFFECTSLOT_AUXILIARY_SEND_AUTO       = C.AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
+	AL_EFFECTSLOT_NULL                      = C.AL_EFFECTSLOT_NULL
+	AL_LOWPASS_GAIN                         = C.AL_LOWPASS_GAIN
+	AL_LOWPASS_GAINHF                       = C.AL_LOWPASS_GAINHF
+	AL_HIGHPASS_GAIN                        = C.AL_HIGHPASS_GAIN
+	AL_HIGHPASS_GAINLF                      = C.AL_HIGHPASS_GAINLF
+	AL_BANDPASS_GAIN                        = C.AL_BANDPASS_GAIN
+	AL_BANDPASS_GAINLF                      = C.AL_BANDPASS_GAINLF
+	AL_BANDPASS_GAINHF                      = C.AL_BANDPASS_GAINHF
+	AL_FILTER_FIRST_PARAMETER               = C.AL_FILTER_FIRST_PARAMETER
+	AL_FILTER_LAST_PARAMETER                = C.AL_FILTER_LAST_PARAMETER
+	AL_FILTER_TYPE                          = C.AL_FILTER_TYPE
+	AL_FILTER_NULL                          = C.AL_FILTER_NULL
+	AL_FILTER_LOWPASS                       = C.AL_FILTER_LOWPASS
+	AL_FILTER_HIGHPASS                      = C.AL_FILTER_HIGHPASS
+	AL_FILTER_BANDPASS                      = C.AL_FILTER_BANDPASS
+)
+
+var errCodes = map[uint]string{
+	C.AL_INVALID_NAME:      "AL_INVALID_NAME",
+	C.AL_INVALID_ENUM:      "AL_INVALID_ENUM",
+	C.AL_INVALID_VALUE:     "AL_INVALID_VALUE",
+	C.AL_INVALID_OPERATION: "AL_INVALID_OPERATION",
+	C.AL_OUT_OF_MEMORY:     "AL_OUT_OF_MEMORY",
+}
+
+type Device struct {
+	cdev *C.ALCdevice
+}
+
+type Context struct {
+	cctx *C.ALCcontext
+}
+
+// Statistics
+type Stats struct {
+	Sources  int   // Current number of sources
+	Buffers  int   // Current number of buffers
+	CgoCalls int64 // Accumulated cgo calls
+	Callocs  int   // Current number of C allocations
+}
+
+// Maps C pointer to device to Go pointer to Device
+var mapDevice = map[*C.ALCdevice]*Device{}
+
+// Global statistics structure
+var stats Stats
+
+//
+// Loads try to load dynamically the OpenAL library if available
+//
+func Load() error {
+
+	cres := C.al_load()
+	if cres == 0 {
+		return nil
+	}
+	return fmt.Errorf("Error trying to load OpenAL library")
+}
+
+// GetStats returns copy of the statistics structure
+func GetStats() Stats {
+
+	return stats
+}
+
+func CreateContext(dev *Device, attrlist []int) (*Context, error) {
+
+	var plist unsafe.Pointer
+	if len(attrlist) != 0 {
+		plist = (unsafe.Pointer)(&attrlist[0])
+	}
+	ctx := C.alcCreateContext(dev.cdev, (*C.ALCint)(plist))
+	if ctx != nil {
+		return &Context{ctx}, nil
+	}
+	return nil, fmt.Errorf("%s", errCodes[uint(C.alcGetError(dev.cdev))])
+}
+
+func MakeContextCurrent(ctx *Context) error {
+
+	cres := C.alcMakeContextCurrent(ctx.cctx)
+	if cres == C.ALC_TRUE {
+		return nil
+	}
+	return fmt.Errorf("%s", errCodes[uint(C.alGetError())])
+}
+
+func ProcessContext(ctx *Context) {
+
+	C.alcProcessContext(ctx.cctx)
+}
+
+func SuspendContext(ctx *Context) {
+
+	C.alcSuspendContext(ctx.cctx)
+}
+
+func DestroyContext(ctx *Context) {
+
+	C.alcDestroyContext(ctx.cctx)
+}
+
+func GetContextsDevice(ctx *Context) *Device {
+
+	cdev := C.alcGetContextsDevice(ctx.cctx)
+	if cdev == nil {
+		return nil
+	}
+	return mapDevice[cdev]
+}
+
+func OpenDevice(name string) (*Device, error) {
+
+	cstr := (*C.ALCchar)(C.CString(name))
+	defer C.free(unsafe.Pointer(cstr))
+	cdev := C.alcOpenDevice(cstr)
+	if cdev != nil {
+		dev := &Device{cdev}
+		mapDevice[cdev] = dev
+		return dev, nil
+	}
+	return nil, fmt.Errorf("%s", errCodes[uint(C.alGetError())])
+}
+
+func CloseDevice(dev *Device) error {
+
+	cres := C.alcCloseDevice(dev.cdev)
+	if cres == C.ALC_TRUE {
+		delete(mapDevice, dev.cdev)
+		return nil
+	}
+	return fmt.Errorf("%s", errCodes[uint(C.alGetError())])
+}
+
+func CtxGetError(dev *Device) error {
+
+	cerr := C.alcGetError(dev.cdev)
+	if cerr == C.AL_NONE {
+		return nil
+	}
+	return fmt.Errorf("%s", errCodes[uint(cerr)])
+}
+
+func CtxIsExtensionPresent(dev *Device, extname string) bool {
+
+	cname := (*C.ALCchar)(C.CString(extname))
+	defer C.free(unsafe.Pointer(cname))
+	cres := C.alcIsExtensionPresent(dev.cdev, cname)
+	if cres == C.AL_TRUE {
+		return true
+	}
+	return false
+}
+
+func CtxGetEnumValue(dev *Device, enumName string) uint32 {
+
+	cname := (*C.ALCchar)(C.CString(enumName))
+	defer C.free(unsafe.Pointer(cname))
+	cres := C.alcGetEnumValue(dev.cdev, cname)
+	return uint32(cres)
+}
+
+func CtxGetString(dev *Device, param uint) string {
+
+	cstr := C.alcGetString(dev.cdev, C.ALCenum(param))
+	return C.GoString((*C.char)(cstr))
+}
+
+func CtxGetIntegerv(dev *Device, param uint32, values []int32) {
+
+	C.alcGetIntegerv(dev.cdev, C.ALCenum(param), C.ALCsizei(len(values)), (*C.ALCint)(unsafe.Pointer(&values[0])))
+}
+
+func CaptureOpenDevice(devname string, frequency uint32, format uint32, buffersize uint32) (*Device, error) {
+
+	cstr := (*C.ALCchar)(C.CString(devname))
+	defer C.free(unsafe.Pointer(cstr))
+	cdev := C.alcCaptureOpenDevice(cstr, C.ALCuint(frequency), C.ALCenum(format), C.ALCsizei(buffersize))
+	if cdev != nil {
+		dev := &Device{cdev}
+		mapDevice[cdev] = dev
+		return dev, nil
+	}
+	return nil, fmt.Errorf("%s", errCodes[uint(C.alGetError())])
+}
+
+func CaptureCloseDevice(dev *Device) error {
+
+	cres := C.alcCaptureCloseDevice(dev.cdev)
+	if cres == C.AL_TRUE {
+		return nil
+	}
+	return fmt.Errorf("%s", errCodes[uint(C.alGetError())])
+}
+
+func CaptureStart(dev *Device) {
+
+	C.alcCaptureStart(dev.cdev)
+}
+
+func CaptureStop(dev *Device) {
+
+	C.alcCaptureStop(dev.cdev)
+}
+
+func CaptureSamples(dev *Device, buffer []byte) {
+
+	C.alcCaptureSamples(dev.cdev, unsafe.Pointer(&buffer[0]), C.ALCsizei(len(buffer)))
+}
+
+func Enable(capability uint) {
+
+	C.alEnable(C.ALenum(capability))
+}
+
+func Disable(capability uint) {
+
+	C.alDisable(C.ALenum(capability))
+}
+
+func IsEnabled(capability uint) bool {
+
+	cres := C.alIsEnabled(C.ALenum(capability))
+	if cres == C.AL_TRUE {
+		return true
+	}
+	return false
+}
+
+func GetString(param uint32) string {
+
+	cstr := C.alGetString(C.ALenum(param))
+	return C.GoString((*C.char)(cstr))
+}
+
+func GetBooleanv(param uint32, values []bool) {
+
+	cvals := make([]C.ALboolean, len(values))
+	C.alGetBooleanv(C.ALenum(param), &cvals[0])
+	for i := 0; i < len(cvals); i++ {
+		if cvals[i] == C.AL_TRUE {
+			values[i] = true
+		} else {
+			values[i] = false
+		}
+	}
+}
+
+func GetIntegerv(param uint32, values []int32) {
+
+	C.alGetIntegerv(C.ALenum(param), (*C.ALint)(unsafe.Pointer(&values[0])))
+}
+
+func GetFloatv(param uint32, values []float32) {
+
+	C.alGetFloatv(C.ALenum(param), (*C.ALfloat)(unsafe.Pointer(&values[0])))
+}
+
+func GetDoublev(param uint32, values []float64) {
+
+	C.alGetDoublev(C.ALenum(param), (*C.ALdouble)(unsafe.Pointer(&values[0])))
+}
+
+func GetBoolean(param uint32) bool {
+
+	cres := C.alGetBoolean(C.ALenum(param))
+	if cres == C.AL_TRUE {
+		return true
+	}
+	return false
+}
+
+func GetInteger(param uint32) int32 {
+
+	cres := C.alGetInteger(C.ALenum(param))
+	return int32(cres)
+}
+
+func GetFloat(param uint32) float32 {
+
+	cres := C.alGetFloat(C.ALenum(param))
+	return float32(cres)
+}
+
+func GetDouble(param uint32) float64 {
+
+	cres := C.alGetDouble(C.ALenum(param))
+	return float64(cres)
+}
+
+func GetError() error {
+
+	cerr := C.alGetError()
+	if cerr == C.AL_NONE {
+		return nil
+	}
+	return fmt.Errorf("%s", errCodes[uint(cerr)])
+}
+
+func IsExtensionPresent(extName string) bool {
+
+	cstr := (*C.ALchar)(C.CString(extName))
+	defer C.free(unsafe.Pointer(cstr))
+	cres := C.alIsExtensionPresent(cstr)
+	if cres == 0 {
+		return false
+	}
+	return true
+}
+
+func GetEnumValue(enam string) uint32 {
+
+	cenam := (*C.ALchar)(C.CString(enam))
+	defer C.free(unsafe.Pointer(cenam))
+	cres := C.alGetEnumValue(cenam)
+	return uint32(cres)
+}
+
+func Listenerf(param uint32, value float32) {
+
+	C.alListenerf(C.ALenum(param), C.ALfloat(value))
+}
+
+func Listener3f(param uint32, value1, value2, value3 float32) {
+
+	C.alListener3f(C.ALenum(param), C.ALfloat(value1), C.ALfloat(value2), C.ALfloat(value3))
+}
+
+func Listenerfv(param uint32, values []float32) {
+
+	C.alListenerfv(C.ALenum(param), (*C.ALfloat)(unsafe.Pointer(&values[0])))
+}
+
+func Listeneri(param uint32, value int32) {
+
+	C.alListeneri(C.ALenum(param), C.ALint(value))
+}
+
+func Listener3i(param uint32, value1, value2, value3 int32) {
+
+	C.alListener3i(C.ALenum(param), C.ALint(value1), C.ALint(value2), C.ALint(value3))
+}
+
+func Listeneriv(param uint32, values []int32) {
+
+	C.alListeneriv(C.ALenum(param), (*C.ALint)(unsafe.Pointer(&values[0])))
+}
+
+func GetListenerf(param uint32) float32 {
+
+	var cval C.ALfloat
+	C.alGetListenerf(C.ALenum(param), &cval)
+	return float32(cval)
+}
+
+func GetListener3f(param uint32) (float32, float32, float32) {
+
+	var cval1 C.ALfloat
+	var cval2 C.ALfloat
+	var cval3 C.ALfloat
+	C.alGetListener3f(C.ALenum(param), &cval1, &cval2, &cval3)
+	return float32(cval1), float32(cval2), float32(cval3)
+}
+
+func GetListenerfv(param uint32, values []uint32) {
+
+	C.alGetListenerfv(C.ALenum(param), (*C.ALfloat)(unsafe.Pointer(&values[0])))
+}
+
+func GetListeneri(param uint32) int32 {
+
+	var cval C.ALint
+	C.alGetListeneri(C.ALenum(param), &cval)
+	return int32(cval)
+}
+
+func GetListener3i(param uint32) (int32, int32, int32) {
+
+	var cval1 C.ALint
+	var cval2 C.ALint
+	var cval3 C.ALint
+	C.alGetListener3i(C.ALenum(param), &cval1, &cval2, &cval3)
+	return int32(cval1), int32(cval2), int32(cval3)
+}
+
+func GetListeneriv(param uint32, values []int32) {
+
+	if len(values) < 3 {
+		panic("Slice length less than minimum")
+	}
+	C.alGetListeneriv(C.ALenum(param), (*C.ALint)(unsafe.Pointer(&values[0])))
+}
+
+func GenSource() uint32 {
+
+	var csource C.ALuint
+	C.alGenSources(1, &csource)
+	stats.Sources++
+	return uint32(csource)
+}
+
+func GenSources(sources []uint32) {
+
+	C.alGenSources(C.ALsizei(len(sources)), (*C.ALuint)(unsafe.Pointer(&sources[0])))
+	stats.Sources += len(sources)
+}
+
+func DeleteSource(source uint32) {
+
+	C.alDeleteSources(1, (*C.ALuint)(unsafe.Pointer(&source)))
+	stats.Sources--
+}
+
+func DeleteSources(sources []uint32) {
+
+	C.alDeleteSources(C.ALsizei(len(sources)), (*C.ALuint)(unsafe.Pointer(&sources[0])))
+	stats.Sources -= len(sources)
+}
+
+func IsSource(source uint32) bool {
+
+	cres := C.alIsSource(C.ALuint(source))
+	if cres == C.AL_TRUE {
+		return true
+	}
+	return false
+}
+
+func Sourcef(source uint32, param uint32, value float32) {
+
+	C.alSourcef(C.ALuint(source), C.ALenum(param), C.ALfloat(value))
+}
+
+func Source3f(source uint32, param uint32, value1, value2, value3 float32) {
+
+	C.alSource3f(C.ALuint(source), C.ALenum(param), C.ALfloat(value1), C.ALfloat(value2), C.ALfloat(value3))
+}
+
+func Sourcefv(source uint32, param uint32, values []float32) {
+
+	if len(values) < 3 {
+		panic("Slice length less than minimum")
+	}
+	C.alSourcefv(C.ALuint(source), C.ALenum(param), (*C.ALfloat)(unsafe.Pointer(&values[0])))
+}
+
+func Sourcei(source uint32, param uint32, value int32) {
+
+	C.alSourcei(C.ALuint(source), C.ALenum(param), C.ALint(value))
+}
+
+func Source3i(source uint32, param uint32, value1, value2, value3 int32) {
+
+	C.alSource3i(C.ALuint(source), C.ALenum(param), C.ALint(value1), C.ALint(value2), C.ALint(value3))
+}
+
+func Sourceiv(source uint32, param uint32, values []int32) {
+
+	if len(values) < 3 {
+		panic("Slice length less than minimum")
+	}
+	C.alSourceiv(C.ALuint(source), C.ALenum(param), (*C.ALint)(unsafe.Pointer(&values[0])))
+}
+
+func GetSourcef(source uint32, param uint32) float32 {
+
+	var value C.ALfloat
+	C.alGetSourcef(C.ALuint(source), C.ALenum(param), &value)
+	return float32(value)
+}
+
+func GetSource3f(source uint32, param uint32) (float32, float32, float32) {
+
+	var cval1 C.ALfloat
+	var cval2 C.ALfloat
+	var cval3 C.ALfloat
+	C.alGetSource3f(C.ALuint(source), C.ALenum(param), &cval1, &cval2, &cval3)
+	return float32(cval1), float32(cval2), float32(cval3)
+}
+
+func GetSourcefv(source uint32, param uint32, values []float32) {
+
+	if len(values) < 3 {
+		panic("Slice length less than minimum")
+	}
+	C.alGetSourcefv(C.ALuint(source), C.ALenum(param), (*C.ALfloat)(unsafe.Pointer(&values[0])))
+}
+
+func GetSourcei(source uint32, param uint32) int32 {
+
+	var value C.ALint
+	C.alGetSourcei(C.ALuint(source), C.ALenum(param), &value)
+	return int32(value)
+}
+
+func GetSource3i(source uint32, param uint32) (int32, int32, int32) {
+
+	var cval1 C.ALint
+	var cval2 C.ALint
+	var cval3 C.ALint
+	C.alGetSource3i(C.ALuint(source), C.ALenum(param), &cval1, &cval2, &cval3)
+	return int32(cval1), int32(cval2), int32(cval3)
+}
+
+func GetSourceiv(source uint32, param uint32, values []int32) {
+
+	if len(values) < 3 {
+		panic("Slice length less than minimum")
+	}
+	C.alGetSourceiv(C.ALuint(source), C.ALenum(param), (*C.ALint)(unsafe.Pointer(&values[0])))
+}
+
+func SourcePlayv(sources []uint32) {
+
+	C.alSourcePlayv(C.ALsizei(len(sources)), (*C.ALuint)(unsafe.Pointer(&sources[0])))
+}
+
+func SourceStopv(sources []uint32) {
+
+	C.alSourceStopv(C.ALsizei(len(sources)), (*C.ALuint)(unsafe.Pointer(&sources[0])))
+}
+
+func SourceRewindv(sources []uint32) {
+
+	C.alSourceRewindv(C.ALsizei(len(sources)), (*C.ALuint)(unsafe.Pointer(&sources[0])))
+}
+
+func SourcePausev(sources []uint32) {
+
+	C.alSourcePausev(C.ALsizei(len(sources)), (*C.ALuint)(unsafe.Pointer(&sources[0])))
+}
+
+func SourcePlay(source uint32) {
+
+	C.alSourcePlay(C.ALuint(source))
+}
+
+func SourceStop(source uint32) {
+
+	C.alSourceStop(C.ALuint(source))
+}
+
+func SourceRewind(source uint32) {
+
+	C.alSourceRewind(C.ALuint(source))
+}
+
+func SourcePause(source uint32) {
+
+	C.alSourcePause(C.ALuint(source))
+}
+
+func SourceQueueBuffers(source uint32, buffers ...uint32) {
+
+	C.alSourceQueueBuffers(C.ALuint(source), C.ALsizei(len(buffers)), (*C.ALuint)(unsafe.Pointer(&buffers[0])))
+}
+
+func SourceUnqueueBuffers(source uint32, n uint32, buffers []uint32) {
+
+	removed := make([]C.ALuint, n)
+	C.alSourceUnqueueBuffers(C.ALuint(source), C.ALsizei(n), &removed[0])
+}
+
+func GenBuffers(n uint32) []uint32 {
+
+	buffers := make([]uint32, n)
+	C.alGenBuffers(C.ALsizei(len(buffers)), (*C.ALuint)(unsafe.Pointer(&buffers[0])))
+	return buffers
+}
+
+func DeleteBuffers(buffers []uint32) {
+
+	C.alDeleteBuffers(C.ALsizei(len(buffers)), (*C.ALuint)(unsafe.Pointer(&buffers[0])))
+}
+
+func IsBuffer(buffer uint32) bool {
+
+	cres := C.alIsBuffer(C.ALuint(buffer))
+	if cres == C.AL_TRUE {
+		return true
+	}
+	return false
+}
+
+func BufferData(buffer uint32, format uint32, data unsafe.Pointer, size uint32, freq uint32) {
+
+	C.alBufferData(C.ALuint(buffer), C.ALenum(format), data, C.ALsizei(size), C.ALsizei(freq))
+}
+
+func Bufferf(buffer uint32, param uint32, value float32) {
+
+	C.alBufferf(C.ALuint(buffer), C.ALenum(param), C.ALfloat(value))
+}
+
+func Buffer3f(buffer uint32, param uint32, value1, value2, value3 float32) {
+
+	C.alBuffer3f(C.ALuint(buffer), C.ALenum(param), C.ALfloat(value1), C.ALfloat(value2), C.ALfloat(value3))
+}
+
+func Bufferfv(buffer uint32, param uint32, values []float32) {
+
+	C.alBufferfv(C.ALuint(buffer), C.ALenum(param), (*C.ALfloat)(unsafe.Pointer(&values[0])))
+}
+
+func Bufferi(buffer uint32, param uint32, value int32) {
+
+	C.alBufferi(C.ALuint(buffer), C.ALenum(param), C.ALint(value))
+}
+
+func Buffer3i(buffer uint32, param uint32, value1, value2, value3 int32) {
+
+	C.alBuffer3i(C.ALuint(buffer), C.ALenum(param), C.ALint(value1), C.ALint(value2), C.ALint(value3))
+}
+
+func Bufferiv(buffer uint32, param uint32, values []int32) {
+
+	C.alBufferiv(C.ALuint(buffer), C.ALenum(param), (*C.ALint)(unsafe.Pointer(&values[0])))
+}
+
+func GetBufferf(buffer uint32, param uint32) float32 {
+
+	var value C.ALfloat
+	C.alGetBufferf(C.ALuint(buffer), C.ALenum(param), &value)
+	return float32(value)
+}
+
+func GetBuffer3f(buffer uint32, param uint32) (v1 float32, v2 float32, v3 float32) {
+
+	var value1, value2, value3 C.ALfloat
+	C.alGetBuffer3f(C.ALuint(buffer), C.ALenum(param), &value1, &value2, &value3)
+	return float32(value1), float32(value2), float32(value3)
+}
+
+func GetBufferfv(buffer uint32, param uint32, values []float32) {
+
+	C.alGetBufferfv(C.ALuint(buffer), C.ALenum(param), (*C.ALfloat)(unsafe.Pointer(&values[0])))
+}
+
+func GetBufferi(buffer uint32, param uint32) int32 {
+
+	var value C.ALint
+	C.alGetBufferi(C.ALuint(buffer), C.ALenum(param), &value)
+	return int32(value)
+}
+
+func GetBuffer3i(buffer uint32, param uint32) (int32, int32, int32) {
+
+	var value1, value2, value3 C.ALint
+	C.alGetBuffer3i(C.ALuint(buffer), C.ALenum(param), &value1, &value2, &value3)
+	return int32(value1), int32(value2), int32(value3)
+}
+
+func GetBufferiv(buffer uint32, param uint32, values []int32) {
+
+	C.alGetBufferiv(C.ALuint(buffer), C.ALenum(param), (*C.ALint)(unsafe.Pointer(&values[0])))
+}

+ 752 - 0
audio/al/loader.c

@@ -0,0 +1,752 @@
+#include "loader.h"
+
+typedef void (*alProc)(void);
+
+//
+// Windows --------------------------------------------------------------------
+//
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN 1
+#include <windows.h>
+
+static HMODULE libal;
+
+static int open_libal(void) {
+
+	libal = LoadLibraryA("OpenAL32.dll");
+    if (libal == NULL) {
+        return -1;
+    }
+    return 0;
+}
+
+static void close_libal(void) {
+	FreeLibrary(libal);
+}
+
+static alProc get_proc(const char *proc) {
+    return (alProc) GetProcAddress(libal, proc);
+}
+//
+// Mac --------------------------------------------------------------------
+//
+#elif defined(__APPLE__) || defined(__APPLE_CC__)
+#include <Carbon/Carbon.h>
+
+CFBundleRef bundle;
+CFURLRef bundleURL;
+
+static void open_libal(void) {
+	bundleURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
+		CFSTR("/System/Library/Frameworks/OpenAL.framework"),
+		kCFURLPOSIXPathStyle, true);
+	bundle = CFBundleCreate(kCFAllocatorDefault, bundleURL);
+	assert(bundle != NULL);
+}
+
+static void close_libal(void) {
+	CFRelease(bundle);
+	CFRelease(bundleURL);
+}
+
+static alProc get_proc(const char *proc) {
+	GL3WglProc res;
+	CFStringRef procname = CFStringCreateWithCString(kCFAllocatorDefault, proc,
+		kCFStringEncodingASCII);
+	res = (GL3WglProc) CFBundleGetFunctionPointerForName(bundle, procname);
+	CFRelease(procname);
+	return res;
+}
+//
+// Linux --------------------------------------------------------------------
+//
+#else
+#include <dlfcn.h>
+
+static void *libal;
+
+static char* lib_names[] = {
+    "libopenal.so",
+    "libopenal.so.1",
+    NULL
+};
+
+static int open_libal(void) {
+
+    int i = 0;
+    while (lib_names[i] != NULL) {
+	    libal = dlopen(lib_names[i], RTLD_LAZY | RTLD_GLOBAL);
+        if (libal != NULL) {
+            dlerror(); // clear errors
+            return 0;
+        }
+        i++;
+    }
+    return -1;
+}
+
+static void close_libal(void) {
+	dlclose(libal);
+}
+
+static alProc get_proc(const char *proc) {
+    return dlsym(libal, proc);
+}
+#endif
+
+// Prototypes of local functions
+static void load_procs(void);
+static void load_efx_procs(void);
+
+
+// Pointers to functions loaded from shared library
+LPALENABLE                  palEnable;
+LPALDISABLE                 palDisable;
+LPALISENABLED               palIsEnabled;
+LPALGETSTRING               palGetString;
+LPALGETBOOLEANV             palGetBooleanv;
+LPALGETINTEGERV             palGetIntegerv;
+LPALGETFLOATV               palGetFloatv;
+LPALGETDOUBLEV              palGetDoublev;
+LPALGETBOOLEAN              palGetBoolean;
+LPALGETINTEGER              palGetInteger;
+LPALGETFLOAT                palGetFloat;
+LPALGETDOUBLE               palGetDouble;
+LPALGETERROR                palGetError;
+LPALISEXTENSIONPRESENT      palIsExtensionPresent;
+LPALGETPROCADDRESS          palGetProcAddress;
+LPALGETENUMVALUE            palGetEnumValue;
+LPALLISTENERF               palListenerf;
+LPALLISTENER3F              palListener3f;
+LPALLISTENERFV              palListenerfv;
+LPALLISTENERI               palListeneri;
+LPALLISTENER3I              palListener3i;
+LPALLISTENERIV              palListeneriv;
+LPALGETLISTENERF            palGetListenerf;
+LPALGETLISTENER3F           palGetListener3f;
+LPALGETLISTENERFV           palGetListenerfv;
+LPALGETLISTENERI            palGetListeneri;
+LPALGETLISTENER3I           palGetListener3i;
+LPALGETLISTENERIV           palGetListeneriv;
+LPALGENSOURCES              palGenSources;
+LPALDELETESOURCES           palDeleteSources;
+LPALISSOURCE                palIsSource;
+LPALSOURCEF                 palSourcef;
+LPALSOURCE3F                palSource3f;
+LPALSOURCEFV                palSourcefv;
+LPALSOURCEI                 palSourcei;
+LPALSOURCE3I                palSource3i;
+LPALSOURCEIV                palSourceiv;
+LPALGETSOURCEF              palGetSourcef;
+LPALGETSOURCE3F             palGetSource3f;
+LPALGETSOURCEFV             palGetSourcefv;
+LPALGETSOURCEI              palGetSourcei;
+LPALGETSOURCE3I             palGetSource3i;
+LPALGETSOURCEIV             palGetSourceiv;
+LPALSOURCEPLAYV             palSourcePlayv;
+LPALSOURCESTOPV             palSourceStopv;
+LPALSOURCEREWINDV           palSourceRewindv;
+LPALSOURCEPAUSEV            palSourcePausev;
+LPALSOURCEPLAY              palSourcePlay;
+LPALSOURCESTOP              palSourceStop;
+LPALSOURCEREWIND            palSourceRewind;
+LPALSOURCEPAUSE             palSourcePause;
+LPALSOURCEQUEUEBUFFERS      palSourceQueueBuffers;
+LPALSOURCEUNQUEUEBUFFERS    palSourceUnqueueBuffers;
+LPALGENBUFFERS              palGenBuffers;
+LPALDELETEBUFFERS           palDeleteBuffers;
+LPALISBUFFER                palIsBuffer;
+LPALBUFFERDATA              palBufferData;
+LPALBUFFERF                 palBufferf;
+LPALBUFFER3F                palBuffer3f;
+LPALBUFFERFV                palBufferfv;
+LPALBUFFERI                 palBufferi;
+LPALBUFFER3I                palBuffer3i;
+LPALBUFFERIV                palBufferiv;
+LPALGETBUFFERF              palGetBufferf;
+LPALGETBUFFER3F             palGetBuffer3f;
+LPALGETBUFFERFV             palGetBufferfv;
+LPALGETBUFFERI              palGetBufferi;
+LPALGETBUFFER3I             palGetBuffer3i;
+LPALGETBUFFERIV             palGetBufferiv;
+LPALDOPPLERFACTOR           palDopplerFactor;
+LPALDOPPLERVELOCITY         palDopplerVelocity;
+LPALSPEEDOFSOUND            palSpeedOfSound;
+LPALDISTANCEMODEL           palDistanceModel;
+
+LPALCCREATECONTEXT          palcCreateContext;
+LPALCMAKECONTEXTCURRENT     palcMakeContextCurrent;     
+LPALCPROCESSCONTEXT         palcProcessContext;
+LPALCSUSPENDCONTEXT         palcSuspendContext;
+LPALCDESTROYCONTEXT         palcDestroyContext;
+LPALCGETCURRENTCONTEXT      palcGetCurrentContext;
+LPALCGETCONTEXTSDEVICE      palcGetContextsDevice;
+LPALCOPENDEVICE             palcOpenDevice;
+LPALCCLOSEDEVICE            palcCloseDevice;
+LPALCGETERROR               palcGetError;
+LPALCISEXTENSIONPRESENT     palcIsExtensionPresent;
+LPALCGETPROCADDRESS         palcGetProcAddress;
+LPALCGETENUMVALUE           palcGetEnumValue;
+LPALCGETSTRING              palcGetString;
+LPALCGETINTEGERV            palcGetIntegerv;
+LPALCCAPTUREOPENDEVICE      palcCaptureOpenDevice;
+LPALCCAPTURECLOSEDEVICE     palcCaptureCloseDevice;
+LPALCCAPTURESTART           palcCaptureStart;
+LPALCCAPTURESTOP            palcCaptureStop;
+LPALCCAPTURESAMPLES         palcCaptureSamples;
+
+// Pointers to EFX extension functions
+LPALGENEFFECTS                   palGenEffects;
+LPALDELETEEFFECTS                palDeleteEffects;
+LPALISEFFECT                     palIsEffect;
+LPALEFFECTI                      palEffecti;
+LPALEFFECTIV                     palEffectiv;
+LPALEFFECTF                      palEffectf;
+LPALEFFECTFV                     palEffectfv;
+LPALGETEFFECTI                   palGetEffecti;
+LPALGETEFFECTIV                  palGetEffectiv;
+LPALGETEFFECTF                   palGetEffectf;
+LPALGETEFFECTFV                  palGetEffectfv;
+
+LPALGENFILTERS                   palGenFilters;
+LPALDELETEFILTERS                palDeleteFilters;
+LPALISFILTER                     palIsFilter;
+LPALFILTERI                      palFilteri;
+LPALFILTERIV                     palFilteriv;
+LPALFILTERF                      palFilterf;
+LPALFILTERFV                     palFilterfv;
+LPALGETFILTERI                   palGetFilteri;
+LPALGETFILTERIV                  palGetFilteriv;
+LPALGETFILTERF                   palGetFilterf;
+LPALGETFILTERFV                  palGetFilterfv;
+
+LPALGENAUXILIARYEFFECTSLOTS      palGenAuxiliaryEffectsSlots;
+LPALDELETEAUXILIARYEFFECTSLOTS   palDeleteAuxiliaryEffectsSlots;
+LPALISAUXILIARYEFFECTSLOT        palIsAuxiliaryEffectSlot;
+LPALAUXILIARYEFFECTSLOTI         palAuxiliaryEffectSloti;
+LPALAUXILIARYEFFECTSLOTIV        palAuxiliaryEffectSlotiv;
+LPALAUXILIARYEFFECTSLOTF         palAuxiliaryEffectSlotf;
+LPALAUXILIARYEFFECTSLOTFV        palAuxiliaryEffectSlotfv;
+LPALGETAUXILIARYEFFECTSLOTI      palGetAuxiliaryEffectSloti;
+LPALGETAUXILIARYEFFECTSLOTIV     palGetAuxiliaryEffectSlotif;
+LPALGETAUXILIARYEFFECTSLOTF      palGetAuxiliaryEffectSlotf;
+LPALGETAUXILIARYEFFECTSLOTFV     palGetAuxiliaryEffectSlotfv;
+
+
+int al_load() {
+
+    int res = open_libal();
+    if (res) {
+        return res;
+    }
+    load_procs();
+    load_efx_procs();
+    return 0;
+}
+
+static void load_procs(void) {
+    palEnable               = (LPALENABLE)get_proc("alEnable");
+    palDisable              = (LPALDISABLE)get_proc("alDisable");
+    palIsEnabled            = (LPALISENABLED)get_proc("alIsEnabled");
+    palGetString            = (LPALGETSTRING)get_proc("alGetString");
+    palGetBooleanv          = (LPALGETBOOLEANV)get_proc("alGetBooleanv");
+    palGetIntegerv          = (LPALGETINTEGERV)get_proc("alGetIntegerv");
+    palGetFloatv            = (LPALGETFLOATV)get_proc("alGetFloatv");
+    palGetDoublev           = (LPALGETDOUBLEV)get_proc("alGetDoublev");
+    palGetBoolean           = (LPALGETBOOLEAN)get_proc("alGetBoolean");
+    palGetInteger           = (LPALGETINTEGER)get_proc("alGetInteger");
+    palGetFloat             = (LPALGETFLOAT)get_proc("alGetFloat");
+    palGetDouble            = (LPALGETDOUBLE)get_proc("alGetDouble");
+    palGetError             = (LPALGETERROR)get_proc("alGetError");
+    palIsExtensionPresent   = (LPALISEXTENSIONPRESENT)get_proc("alIsExtensionPresent");
+    palGetProcAddress       = (LPALGETPROCADDRESS)get_proc("alGetProcAddress");
+    palGetEnumValue         = (LPALGETENUMVALUE)get_proc("alGetEnumValue");
+    palListenerf            = (LPALLISTENERF)get_proc("alListeners");
+    palListener3f           = (LPALLISTENER3F)get_proc("alListener3f");
+    palListenerfv           = (LPALLISTENERFV)get_proc("alListenerfv");
+    palListeneri            = (LPALLISTENERI)get_proc("alListeneri");
+    palListener3i           = (LPALLISTENER3I)get_proc("alListener3i");
+    palListeneriv           = (LPALLISTENERIV)get_proc("alListeneriv");
+    palGetListenerf         = (LPALGETLISTENERF)get_proc("alGetListenerf");
+    palGetListener3f        = (LPALGETLISTENER3F)get_proc("alGetListener3f");
+    palGetListenerfv        = (LPALGETLISTENERFV)get_proc("alGetListenerfv");
+    palGetListeneri         = (LPALGETLISTENERI)get_proc("alGetListeneri");
+    palGetListener3i        = (LPALGETLISTENER3I)get_proc("alGetListener3i");
+    palGetListeneriv        = (LPALGETLISTENERIV)get_proc("alGetListeneriv");
+    palGenSources           = (LPALGENSOURCES)get_proc("alGenSources");
+    palDeleteSources        = (LPALDELETESOURCES)get_proc("alDeleteSources");
+    palIsSource             = (LPALISSOURCE)get_proc("alIsSource");
+    palSourcef              = (LPALSOURCEF)get_proc("alSourcef");
+    palSource3f             = (LPALSOURCE3F)get_proc("alSource3f");
+    palSourcefv             = (LPALSOURCEFV)get_proc("alSourcefv");
+    palSourcei              = (LPALSOURCEI)get_proc("alSourcei");
+    palSource3i             = (LPALSOURCE3I)get_proc("alSource3i");
+    palSourceiv             = (LPALSOURCEIV)get_proc(" alSourceiv");
+    palGetSourcef           = (LPALGETSOURCEF)get_proc("alGetSourcef");
+    palGetSource3f          = (LPALGETSOURCE3F)get_proc("alGetSource3f");
+    palGetSourcefv          = (LPALGETSOURCEFV)get_proc("alGetSourcefv");
+    palGetSourcei           = (LPALGETSOURCEI)get_proc("alGetSourcei");
+    palGetSource3i          = (LPALGETSOURCE3I)get_proc("alGetSource3i");
+    palGetSourceiv          = (LPALGETSOURCEIV)get_proc("alGetSourceiv");
+    palSourcePlayv          = (LPALSOURCEPLAYV)get_proc("alSourcePlayv");
+    palSourceStopv          = (LPALSOURCESTOPV)get_proc("alSourceStopv");
+    palSourceRewindv        = (LPALSOURCEREWINDV)get_proc("alSourceRewindv");
+    palSourcePausev         = (LPALSOURCEPAUSEV)get_proc("alSourcePausev");
+    palSourcePlay           = (LPALSOURCEPLAY)get_proc("alSourcePlay");
+    palSourceStop           = (LPALSOURCESTOP)get_proc("alSourceStop");
+    palSourceRewind         = (LPALSOURCEREWIND)get_proc("alSourceRewind");
+    palSourcePause          = (LPALSOURCEPAUSE)get_proc("alSourcePause");
+    palSourceQueueBuffers   = (LPALSOURCEQUEUEBUFFERS)get_proc("alSourceQueueBuffers");
+    palSourceUnqueueBuffers = (LPALSOURCEUNQUEUEBUFFERS)get_proc("alSourceUnqueueBuffers");
+    palGenBuffers           = (LPALGENBUFFERS)get_proc("alGenBuffers");
+    palDeleteBuffers        = (LPALDELETEBUFFERS)get_proc("alDeleteBuffers");
+    palIsBuffer             = (LPALISBUFFER)get_proc("alIsBuffer");
+    palBufferData           = (LPALBUFFERDATA)get_proc("alBufferData");
+    palBufferf              = (LPALBUFFERF)get_proc("alBufferf");
+    palBuffer3f             = (LPALBUFFER3F)get_proc("alBuffer3f");
+    palBufferfv             = (LPALBUFFERFV)get_proc("alBufferfv");
+    palBufferi              = (LPALBUFFERI)get_proc("alBufferi");
+    palBuffer3i             = (LPALBUFFER3I)get_proc("alBuffer3i");
+    palBufferiv             = (LPALBUFFERIV)get_proc("alBufferiv");
+    palGetBufferf           = (LPALGETBUFFERF)get_proc("alGetBufferf");
+    palGetBuffer3f          = (LPALGETBUFFER3F)get_proc("alGetBuffer3f");
+    palGetBufferfv          = (LPALGETBUFFERFV)get_proc("alGetBufferfv");
+    palGetBufferi           = (LPALGETBUFFERI)get_proc("alGetBufferi");
+    palGetBuffer3i          = (LPALGETBUFFER3I)get_proc("alGetBuffer3i");
+    palGetBufferiv          = (LPALGETBUFFERIV)get_proc("alGetBufferiv");
+    palDopplerFactor        = (LPALDOPPLERFACTOR)get_proc("alDopplerFactor");
+    palDopplerVelocity      = (LPALDOPPLERVELOCITY)get_proc("alDopplerVelocity");
+    palSpeedOfSound         = (LPALSPEEDOFSOUND)get_proc("alSpeedOfSound");
+    palDistanceModel        = (LPALDISTANCEMODEL)get_proc("alDistanceModel");
+
+    palcCreateContext       = (LPALCCREATECONTEXT)get_proc("alcCreateContext");
+    palcMakeContextCurrent  = (LPALCMAKECONTEXTCURRENT)get_proc("alcMakeContextCurrent");     
+    palcProcessContext      = (LPALCPROCESSCONTEXT)get_proc("alcProcessContext");
+    palcSuspendContext      = (LPALCSUSPENDCONTEXT)get_proc("alcSuspendContext");
+    palcDestroyContext      = (LPALCDESTROYCONTEXT)get_proc("alcDestroyContext");
+    palcGetCurrentContext   = (LPALCGETCURRENTCONTEXT)get_proc("alcGetCurrentContext");
+    palcGetContextsDevice   = (LPALCGETCONTEXTSDEVICE)get_proc("alcGetContextsDevice");
+    palcOpenDevice          = (LPALCOPENDEVICE)get_proc("alcOpenDevice");
+    palcCloseDevice         = (LPALCCLOSEDEVICE)get_proc("alcCloseDevice");
+    palcGetError            = (LPALCGETERROR)get_proc("alcGetError");
+    palcIsExtensionPresent  = (LPALCISEXTENSIONPRESENT)get_proc("alcIsExtensionPresent");
+    palcGetProcAddress      = (LPALCGETPROCADDRESS)get_proc("alcGetProcAddress");
+    palcGetEnumValue        = (LPALCGETENUMVALUE)get_proc("alcGetEnumValue");
+    palcGetString           = (LPALCGETSTRING)get_proc("alcGetString");
+    palcGetIntegerv         = (LPALCGETINTEGERV)get_proc("alcGetIntegerv");
+    palcCaptureOpenDevice   = (LPALCCAPTUREOPENDEVICE)get_proc("alcCaptureOpenDevice");
+    palcCaptureCloseDevice  = (LPALCCAPTURECLOSEDEVICE)get_proc("alcCaptureCloseDevice");
+    palcCaptureStart        = (LPALCCAPTURESTART)get_proc("alcCaptureStart");
+    palcCaptureStop         = (LPALCCAPTURESTOP)get_proc("alcCaptureStop");
+    palcCaptureSamples      = (LPALCCAPTURESAMPLES)get_proc("alcCaptureSamples");
+}
+
+static void load_efx_procs(void) {
+
+    palGenEffects       = palGetProcAddress("alGenEffects");
+    palDeleteEffects    = palGetProcAddress("alDeleteEffects");
+    palIsEffect         = palGetProcAddress("alIsEffect");
+    palEffecti          = palGetProcAddress("alEffecti");
+    palEffectiv         = palGetProcAddress("alEffectiv");
+    palEffectf          = palGetProcAddress("alEffectf");
+    palEffectfv         = palGetProcAddress("alEffectfv");
+    palGetEffecti       = palGetProcAddress("alGetEffectiv");
+    palGetEffectiv      = palGetProcAddress("alGetEffectiv");
+    palGetEffectf       = palGetProcAddress("alGetEffectf");
+    palGetEffectfv      = palGetProcAddress("alGetEffectfv");
+
+    palGenFilters       = palGetProcAddress("alGenFilters");
+    palDeleteFilters    = palGetProcAddress("alDeleteFilters");
+    palIsFilter         = palGetProcAddress("alIsFilter");
+    palFilteri          = palGetProcAddress("alFilteri");
+    palFilteriv         = palGetProcAddress("alFilteriv");
+    palFilterf          = palGetProcAddress("alFilterf");
+    palFilterfv         = palGetProcAddress("alFilterfv");
+    palGetFilteri       = palGetProcAddress("GetFilteri");
+    palGetFilteriv      = palGetProcAddress("GetFilteriv");
+    palGetFilterf       = palGetProcAddress("GetFilterf");
+    palGetFilterfv      = palGetProcAddress("GetFilterfv");
+
+    palGenAuxiliaryEffectsSlots     = palGetProcAddress("alGenAuxiliaryEffectSlots");
+    palDeleteAuxiliaryEffectsSlots  = palGetProcAddress("alDeleteAuxiliaryEffectsSlots");
+    palIsAuxiliaryEffectSlot        = palGetProcAddress("alIsAuxiliaryEffectSlot");
+    palAuxiliaryEffectSloti         = palGetProcAddress("alAuxiliaryEffectSloti");
+    palAuxiliaryEffectSlotiv        = palGetProcAddress("alAuxiliaryEffectSlotiv");
+    palAuxiliaryEffectSlotf         = palGetProcAddress("alAuxiliaryEffectSlotf");
+    palAuxiliaryEffectSlotfv        = palGetProcAddress("alAuxiliaryEffectSlotfv");
+    palGetAuxiliaryEffectSloti      = palGetProcAddress("alGetAuxiliaryEffectSloti");
+    palGetAuxiliaryEffectSlotif     = palGetProcAddress("alGetAuxiliaryEffectSlotif");
+    palGetAuxiliaryEffectSlotf      = palGetProcAddress("alGetAuxiliaryEffectSlotf");
+    palGetAuxiliaryEffectSlotfv     = palGetProcAddress("alGetAuxiliaryEffectSlotfv");
+}
+
+//
+// Go code cannot call C function pointers directly
+// The following C functions call the corresponding function pointers and can be
+// called by Go code.
+//
+
+//
+// alc.h
+//
+
+ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint* attrlist) {
+    return palcCreateContext(device, attrlist);
+}
+
+ALC_API ALCboolean  ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context) {
+    return palcMakeContextCurrent(context);
+}
+
+ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context) {
+        palcProcessContext(context);
+}
+
+ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context) {
+    palcSuspendContext(context);
+}
+
+ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context) {
+    palcDestroyContext(context);
+}
+
+ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void) {
+    return palcGetCurrentContext();
+}
+
+ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *context) {
+    return palcGetContextsDevice(context);
+}
+
+ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *devicename) {
+    return palcOpenDevice(devicename);
+}
+
+ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device) {
+    return palcCloseDevice(device);
+}
+
+ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device) {
+    return palcGetError(device);
+}
+
+ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extname) {
+    return palcIsExtensionPresent(device, extname);
+}
+
+ALC_API void* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcname) {
+    return palcGetProcAddress(device, funcname);
+}
+
+ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumname) {
+    return palcGetEnumValue(device, enumname);
+}
+
+ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *device, ALCenum param) {
+    return palcGetString(device, param);
+}
+
+ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values) {
+    return palcGetIntegerv(device, param, size, values);
+}
+
+ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize) {
+    return palcCaptureOpenDevice(devicename, frequency, format, buffersize);
+}
+
+ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device) {
+    return palcCaptureCloseDevice(device);
+}
+
+ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device) {
+    palcCaptureStart(device);
+}
+
+ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device) {
+    palcCaptureStop(device);
+}
+
+ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) {
+    palcCaptureSamples(device, buffer, samples);
+}
+
+//
+// al.h
+//
+
+AL_API void AL_APIENTRY alEnable(ALenum capability) {
+    palEnable(capability);
+}
+
+AL_API void AL_APIENTRY alDisable(ALenum capability) {
+    palDisable(capability);
+}
+
+AL_API ALboolean AL_APIENTRY alIsEnabled(ALenum capability) {
+    return palIsEnabled(capability);
+}
+
+AL_API const ALchar* AL_APIENTRY alGetString(ALenum param) {
+    return palGetString(param);
+}
+
+AL_API void AL_APIENTRY alGetBooleanv(ALenum param, ALboolean *values) {
+    palGetBooleanv(param, values);
+}
+
+AL_API void AL_APIENTRY alGetIntegerv(ALenum param, ALint *values) {
+    palGetIntegerv(param, values);
+}
+
+AL_API void AL_APIENTRY alGetFloatv(ALenum param, ALfloat *values) {
+    palGetFloatv(param, values);
+}
+
+AL_API void AL_APIENTRY alGetDoublev(ALenum param, ALdouble *values) {
+    palGetDoublev(param, values);
+}
+
+AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum param) {
+    return palGetBoolean(param);
+}
+
+AL_API ALint AL_APIENTRY alGetInteger(ALenum param) {
+    return palGetInteger(param);
+}
+
+AL_API ALfloat AL_APIENTRY alGetFloat(ALenum param) {
+    return palGetFloat(param);
+}
+
+AL_API ALdouble AL_APIENTRY alGetDouble(ALenum param) {
+    return palGetDouble(param);
+}
+
+AL_API ALenum AL_APIENTRY alGetError(void) {
+    return palGetError();
+}
+
+AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extname) {
+    return palIsExtensionPresent(extname);
+}
+
+AL_API void* AL_APIENTRY alGetProcAddress(const ALchar *fname) {
+    return palGetProcAddress(fname);
+}
+
+AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *ename) {
+    return palGetEnumValue(ename);
+}
+
+AL_API void AL_APIENTRY alListenerf(ALenum param, ALfloat value) {
+    palListenerf(param, value);
+}
+
+AL_API void AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) {
+    palListener3f(param, value1, value2, value3);
+}
+
+AL_API void AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values) {
+    palListenerfv(param, values);
+}
+
+AL_API void AL_APIENTRY alListeneri(ALenum param, ALint value) {
+    palListeneri(param, value);
+}
+
+AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3) {
+    palListener3i(param, value1, value2, value3);
+}
+
+AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values) {
+    palListeneriv(param, values);
+}
+
+AL_API void AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value) {
+    palGetListenerf(param, value);
+}
+
+AL_API void AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) {
+    palGetListener3f(param, value1, value2, value3);
+}
+
+AL_API void AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values) {
+    palGetListenerfv(param, values);
+}
+
+AL_API void AL_APIENTRY alGetListeneri(ALenum param, ALint *value) {
+    palGetListeneri(param, value);
+}
+
+AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3) {
+    palGetListener3i(param, value1, value2, value3);
+}
+
+AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint *values) {
+    palGetListeneriv(param, values);
+}
+
+AL_API void AL_APIENTRY alGenSources(ALsizei n, ALuint *sources) {
+    palGenSources(n, sources);
+}
+
+AL_API void AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources) {
+    palDeleteSources(n, sources);
+}
+
+AL_API ALboolean AL_APIENTRY alIsSource(ALuint source) {
+    return palIsSource(source);
+}
+
+AL_API void AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value) {
+    palSourcef(source, param, value);
+}
+
+AL_API void AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) {
+    palSource3f(source, param, value1, value2, value3);
+}
+
+AL_API void AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values) {
+    palSourcefv(source, param, values);
+}
+
+AL_API void AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value) {
+    palSourcei(source, param, value);
+}
+
+AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3) {
+    palSource3i(source, param, value1, value2, value3);
+}
+
+AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values) {
+    palSourceiv(source, param, values);
+}
+
+AL_API void AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value) {
+    palGetSourcef(source, param, value);
+}
+
+AL_API void AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) {
+    palGetSource3f(source, param, value1, value2, value3);
+}
+
+AL_API void AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values) {
+    palGetSourcefv(source, param, values);
+}
+
+AL_API void AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value) {
+    palGetSourcei(source, param, value);
+}
+
+AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3) {
+    palGetSource3i(source, param, value1, value2, value3);
+}
+
+AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values) {
+    palGetSourceiv(source, param, values);
+}
+
+AL_API void AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources) {
+    palSourcePlayv(n, sources);
+}
+
+AL_API void AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources) {
+    palSourceStopv(n, sources);
+}
+
+AL_API void AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources) {
+    palSourceRewindv(n, sources);
+}
+
+AL_API void AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources) {
+    palSourcePausev(n, sources);
+}
+
+AL_API void AL_APIENTRY alSourcePlay(ALuint source) {
+    palSourcePlay(source);
+}
+
+AL_API void AL_APIENTRY alSourceStop(ALuint source) {
+    palSourceStop(source);
+}
+
+AL_API void AL_APIENTRY alSourceRewind(ALuint source) {
+    palSourceRewind(source);
+}
+
+AL_API void AL_APIENTRY alSourcePause(ALuint source) {
+    palSourcePause(source);
+}
+
+AL_API void AL_APIENTRY alSourceQueueBuffers(ALuint source, ALsizei nb, const ALuint *buffers) {
+    palSourceQueueBuffers(source, nb, buffers);
+}
+
+AL_API void AL_APIENTRY alSourceUnqueueBuffers(ALuint source, ALsizei nb, ALuint *buffers) {
+    palSourceUnqueueBuffers(source, nb, buffers);
+}
+
+AL_API void AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers) {
+    palGenBuffers(n, buffers);
+}
+
+AL_API void AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers) {
+    palDeleteBuffers(n, buffers);
+}
+
+AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer) {
+    return palIsBuffer(buffer);
+}
+
+AL_API void AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq) {
+    palBufferData(buffer, format, data, size, freq);
+}
+
+AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat value) {
+    palBufferf(buffer, param, value);
+}
+
+AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) {
+    palBuffer3f(buffer, param, value1, value2, value3);
+}
+
+AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *values) {
+    palBufferfv(buffer, param, values);
+}
+
+AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value) {
+    palBufferi(buffer, param, value);
+}
+
+AL_API void AL_APIENTRY alBuffer3i(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3) {
+    palBuffer3i(buffer, param, value1, value2, value3);
+}
+
+AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *values) {
+    palBufferiv(buffer, param, values);
+}
+
+AL_API void AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *value) {
+    palGetBufferf(buffer, param, value);
+}
+
+AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) {
+    palGetBuffer3f(buffer, param, value1, value2, value3);
+}
+
+AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *values) {
+    palGetBufferfv(buffer, param, values);
+}
+
+AL_API void AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value) {
+    palGetBufferi(buffer, param, value);
+}
+
+AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3) {
+    palGetBuffer3i(buffer, param, value1, value2, value3);
+}
+
+AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values) {
+    palGetBufferiv(buffer, param, values);
+}
+

+ 160 - 0
audio/al/loader.h

@@ -0,0 +1,160 @@
+#ifndef LOADER_H
+#define LOADER_H
+
+
+#ifdef _WIN32
+#include <stdlib.h>
+#include <stdio.h>
+#include "AL/al.h"
+#include "AL/alc.h"
+#include "AL/efx.h"
+#elif defined(__APPLE__) || defined(__APPLE_CC__)
+#include <stdlib.h>
+#include "AL/al.h"
+#include "AL/alc.h"
+#include "AL/efx.h"
+#else
+#include <stdlib.h>
+#include <stdio.h>
+#include "AL/al.h"
+#include "AL/alc.h"
+#include "AL/efx.h"
+#endif
+
+
+int al_load();
+
+extern LPALENABLE                  palEnable;
+extern LPALDISABLE                 palDisable;
+extern LPALISENABLED               palIsEnabled;
+extern LPALGETSTRING               palGetString;
+extern LPALGETBOOLEANV             palGetBooleanv;
+extern LPALGETINTEGERV             palGetIntegerv;
+extern LPALGETFLOATV               palGetFloatv;
+extern LPALGETDOUBLEV              palGetDoublev;
+extern LPALGETBOOLEAN              palGetBoolean;
+extern LPALGETINTEGER              palGetInteger;
+extern LPALGETFLOAT                palGetFloat;
+extern LPALGETDOUBLE               palGetDouble;
+extern LPALGETERROR                palGetError;
+extern LPALISEXTENSIONPRESENT      palIsExtensionPresent;
+extern LPALGETPROCADDRESS          palGetProcAddress;
+extern LPALGETENUMVALUE            palGetEnumValue;
+extern LPALLISTENERF               palListenerf;
+extern LPALLISTENER3F              palListener3f;
+extern LPALLISTENERFV              palListenerfv;
+extern LPALLISTENERI               palListeneri;
+extern LPALLISTENER3I              palListener3i;
+extern LPALLISTENERIV              palListeneriv;
+extern LPALGETLISTENERF            palGetListenerf;
+extern LPALGETLISTENER3F           palGetListener3f;
+extern LPALGETLISTENERFV           palGetListenerfv;
+extern LPALGETLISTENERI            palGetListeneri;
+extern LPALGETLISTENER3I           palGetListener3i;
+extern LPALGETLISTENERIV           palGetListeneriv;
+extern LPALGENSOURCES              palGenSources;
+extern LPALDELETESOURCES           palDeleteSources;
+extern LPALISSOURCE                palIsSource;
+extern LPALSOURCEF                 palSourcef;
+extern LPALSOURCE3F                palSource3f;
+extern LPALSOURCEFV                palSourcefv;
+extern LPALSOURCEI                 palSourcei;
+extern LPALSOURCE3I                palSource3i;
+extern LPALSOURCEIV                palSourceiv;
+extern LPALGETSOURCEF              palGetSourcef;
+extern LPALGETSOURCE3F             palGetSource3f;
+extern LPALGETSOURCEFV             palGetSourcefv;
+extern LPALGETSOURCEI              palGetSourcei;
+extern LPALGETSOURCE3I             palGetSource3i;
+extern LPALGETSOURCEIV             palGetSourceiv;
+extern LPALSOURCEPLAYV             palSourcePlayv;
+extern LPALSOURCESTOPV             palSourceStopv;
+extern LPALSOURCEREWINDV           palSourceRewindv;
+extern LPALSOURCEPAUSEV            palSourcePausev;
+extern LPALSOURCEPLAY              palSourcePlay;
+extern LPALSOURCESTOP              palSourceStop;
+extern LPALSOURCEREWIND            palSourceRewind;
+extern LPALSOURCEPAUSE             palSourcePause;
+extern LPALSOURCEQUEUEBUFFERS      palSourceQueueBuffers;
+extern LPALSOURCEUNQUEUEBUFFERS    palSourceUnqueueBuffers;
+extern LPALGENBUFFERS              palGenBuffers;
+extern LPALDELETEBUFFERS           palDeleteBuffers;
+extern LPALISBUFFER                palIsBuffer;
+extern LPALBUFFERDATA              palBufferData;
+extern LPALBUFFERF                 palBufferf;
+extern LPALBUFFER3F                palBuffer3f;
+extern LPALBUFFERFV                palBufferfv;
+extern LPALBUFFERI                 palBufferi;
+extern LPALBUFFER3I                palBuffer3i;
+extern LPALBUFFERIV                palBufferiv;
+extern LPALGETBUFFERF              palGetBufferf;
+extern LPALGETBUFFER3F             palGetBuffer3f;
+extern LPALGETBUFFERFV             palGetBufferfv;
+extern LPALGETBUFFERI              palGetBufferi;
+extern LPALGETBUFFER3I             palGetBuffer3i;
+extern LPALGETBUFFERIV             palGetBufferiv;
+extern LPALDOPPLERFACTOR           palDopplerFactor;
+extern LPALDOPPLERVELOCITY         palDopplerVelocity;
+extern LPALSPEEDOFSOUND            palSpeedOfSound;
+extern LPALDISTANCEMODEL           palDistanceModel;
+
+extern LPALCCREATECONTEXT          palcCreateContext;
+extern LPALCMAKECONTEXTCURRENT     palcMakeContextCurrent;     
+extern LPALCPROCESSCONTEXT         palcProcessContext;
+extern LPALCSUSPENDCONTEXT         palcSuspendContext;
+extern LPALCDESTROYCONTEXT         palcDestroyContext;
+extern LPALCGETCURRENTCONTEXT      palcGetCurrentContext;
+extern LPALCGETCONTEXTSDEVICE      palcGetContextsDevice;
+extern LPALCOPENDEVICE             palcOpenDevice;
+extern LPALCCLOSEDEVICE            palcCloseDevice;
+extern LPALCGETERROR               palcGetError;
+extern LPALCISEXTENSIONPRESENT     palcIsExtensionPresent;
+extern LPALCGETPROCADDRESS         palcGetProcAddress;
+extern LPALCGETENUMVALUE           palcGetEnumValue;
+extern LPALCGETSTRING              palcGetString;
+extern LPALCGETINTEGERV            palcGetIntegerv;
+extern LPALCCAPTUREOPENDEVICE      palcCaptureOpenDevice;
+extern LPALCCAPTURECLOSEDEVICE     palcCaptureCloseDevice;
+extern LPALCCAPTURESTART           palcCaptureStart;
+extern LPALCCAPTURESTOP            palcCaptureStop;
+extern LPALCCAPTURESAMPLES         palcCaptureSamples;
+
+// EFX extension
+extern LPALGENEFFECTS              palGenEffects;
+extern LPALDELETEEFFECTS           palDeleteEffects;
+extern LPALISEFFECT                palIsEffect;
+extern LPALEFFECTI                 palEffecti;
+extern LPALEFFECTIV                palEffectiv;
+extern LPALEFFECTF                 palEffectf;
+extern LPALEFFECTFV                palEffectfv;
+extern LPALGETEFFECTI              palGetEffecti;
+extern LPALGETEFFECTIV             palGetEffectiv;
+extern LPALGETEFFECTF              palGetEffectf;
+extern LPALGETEFFECTFV             palGetEffectfv;
+
+extern LPALGENFILTERS              palGenFilters;
+extern LPALDELETEFILTERS           palDeleteFilters;
+extern LPALISFILTER                palIsFilter;
+extern LPALFILTERI                 palFilteri;
+extern LPALFILTERIV                palFilteriv;
+extern LPALFILTERF                 palFilterf;
+extern LPALFILTERFV                palFilterfv;
+extern LPALGETFILTERI              palGetFilteri;
+extern LPALGETFILTERIV             palGetFilteriv;
+extern LPALGETFILTERF              palGetFilterf;
+extern LPALGETFILTERFV             palGetFilterfv;
+
+extern LPALGENAUXILIARYEFFECTSLOTS      palGenAuxiliaryEffectSlos;
+extern LPALDELETEAUXILIARYEFFECTSLOTS   palDeleteAuxiliaryEffectSlots;
+extern LPALISAUXILIARYEFFECTSLOT        palIsAuxiliaryEffectSlot;
+extern LPALAUXILIARYEFFECTSLOTI         palAuxiliaryEffectSloti;
+extern LPALAUXILIARYEFFECTSLOTIV        palAuxiliaryEffectSlotiv;
+extern LPALAUXILIARYEFFECTSLOTF         palAuxiliaryEffectSlotf;
+extern LPALAUXILIARYEFFECTSLOTFV        palAuxiliaryEffectSlotfv;
+extern LPALGETAUXILIARYEFFECTSLOTI      palGetAuxiliaryEffectSloti;
+extern LPALGETAUXILIARYEFFECTSLOTIV     palGetAuxiliaryEffectSlotif;
+extern LPALGETAUXILIARYEFFECTSLOTF      palGetAuxiliaryEffectSlotf;
+extern LPALGETAUXILIARYEFFECTSLOTFV     palGetAuxiliaryEffectSlotfv;
+
+#endif
+

+ 292 - 0
audio/audio_file.go

@@ -0,0 +1,292 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package audio
+
+import (
+	"fmt"
+	"github.com/g3n/engine/audio/al"
+	"github.com/g3n/engine/audio/ov"
+	"io"
+	"os"
+	"unsafe"
+)
+
+type AudioInfo struct {
+	Format     int     // OpenAl Format
+	Channels   int     // Number of channels
+	SampleRate int     // Sample rate in hz
+	BitsSample int     // Number of bits per sample (8 or 16)
+	DataSize   int     // Total data size in bytes
+	BytesSec   int     // Bytes per second
+	TotalTime  float64 // Total time in seconds
+}
+
+type AudioFile struct {
+	wavef   *os.File  // Pointer to wave file opened filed (nil for vorbis)
+	vorbisf *ov.File  // Pointer to vorbis file structure (nil for wave)
+	info    AudioInfo // Audio information structure
+	looping bool      // Looping flag
+}
+
+// NewAudioFile creates and returns a pointer to a new audio file object and an error
+func NewAudioFile(filename string) (*AudioFile, error) {
+
+	// Checks if file exists
+	_, err := os.Stat(filename)
+	if err != nil {
+		return nil, err
+	}
+
+	af := new(AudioFile)
+
+	// Try to open as a wave file
+	if af.openWave(filename) == nil {
+		return af, nil
+	}
+
+	// Try to open as an ogg vorbis file
+	if af.openVorbis(filename) == nil {
+		return af, nil
+	}
+
+	return nil, fmt.Errorf("Unsuported file type")
+}
+
+// Close closes the audiofile
+func (af *AudioFile) Close() error {
+
+	if af.wavef != nil {
+		return af.wavef.Close()
+	}
+	return ov.Clear(af.vorbisf)
+}
+
+// Read reads decoded data from the audio file
+func (af *AudioFile) Read(pdata unsafe.Pointer, nbytes int) (int, error) {
+
+	// Slice to access buffer
+	bs := (*[1 << 30]byte)(pdata)[0:nbytes:nbytes]
+
+	// Reads wave file directly
+	if af.wavef != nil {
+		n, err := af.wavef.Read(bs)
+		if err != nil {
+			return 0, err
+		}
+		if !af.looping {
+			return n, nil
+		}
+		if n == nbytes {
+			return n, nil
+		}
+		// EOF reached. Position file at the beginning
+		_, err = af.wavef.Seek(int64(waveHeaderSize), 0)
+		if err != nil {
+			return 0, nil
+		}
+		// Reads next data into the remaining buffer space
+		n2, err := af.wavef.Read(bs[n:])
+		if err != nil {
+			return 0, err
+		}
+		return n + n2, err
+	}
+
+	// Decodes Ogg vorbis
+	decoded := 0
+	for decoded < nbytes {
+		n, _, err := ov.Read(af.vorbisf, unsafe.Pointer(&bs[decoded]), nbytes-decoded, false, 2, true)
+		// Error
+		if err != nil {
+			return 0, err
+		}
+		// EOF
+		if n == 0 {
+			if !af.looping {
+				break
+			}
+			// Position file at the beginning
+			err = ov.PcmSeek(af.vorbisf, 0)
+			if err != nil {
+				return 0, err
+			}
+		}
+		decoded += n
+	}
+	if nbytes > 0 && decoded == 0 {
+		return 0, io.EOF
+	}
+	return decoded, nil
+}
+
+// Seek sets the file reading position relative to the origin
+func (af *AudioFile) Seek(pos uint) error {
+
+	if af.wavef != nil {
+		_, err := af.wavef.Seek(int64(waveHeaderSize+pos), 0)
+		return err
+	}
+	return ov.PcmSeek(af.vorbisf, int64(pos))
+}
+
+// AudioInfo returns the audio info structure for this audio file
+func (af *AudioFile) Info() AudioInfo {
+
+	return af.info
+}
+
+// CurrenTime returns the current time in seconds for the current
+// file read position
+func (af *AudioFile) CurrentTime() float64 {
+
+	if af.vorbisf != nil {
+		pos, _ := ov.TimeTell(af.vorbisf)
+		return pos
+	} else {
+		pos, err := af.wavef.Seek(0, 1)
+		if err != nil {
+			return 0
+		}
+		return float64(pos) / float64(af.info.BytesSec)
+	}
+}
+
+// Looping returns the current looping state of this audio file
+func (af *AudioFile) Looping() bool {
+
+	return af.looping
+}
+
+// SetLooping sets the looping state of this audio file
+func (af *AudioFile) SetLooping(looping bool) {
+
+	af.looping = looping
+}
+
+// openWave tries to open the specified file as a wave file
+// and if succesfull, sets the file pointer positioned after the header.
+func (af *AudioFile) openWave(filename string) error {
+
+	// Open file
+	osf, err := os.Open(filename)
+	if err != nil {
+		return err
+	}
+
+	// Reads header
+	header := make([]uint8, waveHeaderSize)
+	n, err := osf.Read(header)
+	if err != nil {
+		osf.Close()
+		return err
+	}
+	if n < waveHeaderSize {
+		osf.Close()
+		return fmt.Errorf("File size less than header")
+	}
+	// Checks file marks
+	if string(header[0:4]) != fileMark {
+		osf.Close()
+		return fmt.Errorf("'RIFF' mark not found")
+	}
+	if string(header[8:12]) != fileHead {
+		osf.Close()
+		return fmt.Errorf("'WAVE' mark not found")
+	}
+
+	// Decodes header fields
+	af.info.Format = -1
+	af.info.Channels = int(header[22]) + int(header[23])<<8
+	af.info.SampleRate = int(header[24]) + int(header[25])<<8 + int(header[26])<<16 + int(header[27])<<24
+	af.info.BitsSample = int(header[34]) + int(header[35])<<8
+	af.info.DataSize = int(header[40]) + int(header[41])<<8 + int(header[42])<<16 + int(header[43])<<24
+
+	// Sets OpenAL format field if possible
+	if af.info.Channels == 1 {
+		if af.info.BitsSample == 8 {
+			af.info.Format = al.FormatMono8
+		} else if af.info.BitsSample == 16 {
+			af.info.Format = al.FormatMono16
+		}
+	} else if af.info.Channels == 2 {
+		if af.info.BitsSample == 8 {
+			af.info.Format = al.FormatStereo8
+		} else if af.info.BitsSample == 16 {
+			af.info.Format = al.FormatStereo16
+		}
+	}
+	if af.info.Format == -1 {
+		osf.Close()
+		return fmt.Errorf("Unsupported OpenAL format")
+	}
+
+	// Calculates bytes/sec and total time
+	var bytesChannel int
+	if af.info.BitsSample == 8 {
+		bytesChannel = 1
+	} else {
+		bytesChannel = 2
+	}
+	af.info.BytesSec = af.info.SampleRate * af.info.Channels * bytesChannel
+	af.info.TotalTime = float64(af.info.DataSize) / float64(af.info.BytesSec)
+
+	// Seeks after the header
+	_, err = osf.Seek(waveHeaderSize, 0)
+	if err != nil {
+		osf.Close()
+		return err
+	}
+
+	af.wavef = osf
+	return nil
+}
+
+// openVorbis tries to open the specified file as an ogg vorbis file
+// and if succesfull, sets up the player for playing this file
+func (af *AudioFile) openVorbis(filename string) error {
+
+	// Checks for Ogg Vorbis support
+	if !ov.IsLoaded() {
+		return fmt.Errorf("Unsupported file type")
+	}
+
+	// Try to open file as ogg vorbis
+	vf, err := ov.Fopen(filename)
+	if err != nil {
+		return err
+	}
+
+	// Get info for opened vorbis file
+	var info ov.VorbisInfo
+	err = ov.Info(vf, -1, &info)
+	if err != nil {
+		return err
+	}
+	if info.Channels == 1 {
+		af.info.Format = al.FormatMono16
+	} else if info.Channels == 2 {
+		af.info.Format = al.FormatStereo16
+	} else {
+		return fmt.Errorf("Unsupported number of channels")
+	}
+	totalSamples, err := ov.PcmTotal(vf, -1)
+	if err != nil {
+		ov.Clear(vf)
+		return nil
+	}
+	timeTotal, err := ov.TimeTotal(vf, -1)
+	if err != nil {
+		ov.Clear(vf)
+		return nil
+	}
+
+	af.vorbisf = vf
+	af.info.SampleRate = info.Rate
+	af.info.BitsSample = 16
+	af.info.Channels = info.Channels
+	af.info.DataSize = int(totalSamples) * info.Channels * 2
+	af.info.TotalTime = timeTotal
+	return nil
+}

+ 8 - 0
audio/doc.go

@@ -0,0 +1,8 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+ Package audio ...
+*/
+package audio

+ 79 - 0
audio/listener.go

@@ -0,0 +1,79 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package audio
+
+import (
+	"github.com/g3n/engine/audio/al"
+	"github.com/g3n/engine/core"
+	"github.com/g3n/engine/gls"
+	"github.com/g3n/engine/math32"
+)
+
+// Listener embeds a core.Node and
+type Listener struct {
+	core.Node
+}
+
+func NewListener() *Listener {
+
+	l := new(Listener)
+	l.Node.Init()
+	return l
+}
+
+func (l *Listener) SetVelocity(vx, vy, vz float32) {
+
+	al.Listener3f(al.Velocity, vx, vy, vz)
+}
+
+func (l *Listener) SetVelocityVec(v *math32.Vector3) {
+
+	al.Listener3f(al.Velocity, v.X, v.Y, v.Z)
+}
+
+func (l *Listener) Velocity() (float32, float32, float32) {
+
+	return al.GetListener3f(al.Velocity)
+}
+
+func (l *Listener) VelocityVec() math32.Vector3 {
+
+	vx, vy, vz := al.GetListener3f(al.Velocity)
+	return math32.Vector3{vx, vy, vz}
+}
+
+func (l *Listener) SetGain(gain float32) {
+
+	al.Listenerf(al.Gain, gain)
+}
+
+func (l *Listener) Gain() float32 {
+
+	return al.GetListenerf(al.Gain)
+}
+
+// Render is called by the renderer at each frame
+// Updates the OpenAL position and orientation of this listener
+func (l *Listener) Render(gl *gls.GLS) {
+
+	// Sets the listener source world position
+	var wpos math32.Vector3
+	l.WorldPosition(&wpos)
+	al.Listener3f(al.Position, wpos.X, wpos.Y, wpos.Z)
+
+	// Get listener current world direction
+	var vdir math32.Vector3
+	l.WorldDirection(&vdir)
+
+	// Assumes initial UP vector and recalculates current up vector
+	vup := math32.Vector3{0, 1, 0}
+	var vright math32.Vector3
+	vright.CrossVectors(&vdir, &vup)
+	vup.CrossVectors(&vright, &vdir)
+
+	// Sets the listener orientation
+	orientation := []float32{vdir.X, vdir.Y, vdir.Z, vup.X, vup.Y, vup.Z}
+	al.Listenerfv(al.Orientation, orientation)
+}

+ 9 - 0
audio/ov/build.go

@@ -0,0 +1,9 @@
+package ov
+
+// #cgo darwin   CFLAGS:  -DGO_DARWIN
+// #cgo linux    CFLAGS:  -DGO_LINUX   -I.
+// #cgo windows  CFLAGS:  -DGO_WINDOWS -I.
+// #cgo darwin   LDFLAGS:
+// #cgo linux    LDFLAGS: -ldl
+// #cgo windows  LDFLAGS:
+import "C"

+ 346 - 0
audio/ov/loader.c

@@ -0,0 +1,346 @@
+//
+// Dynamically loads the vorbis file shared library / dll 
+//
+#include "loader.h"
+
+
+typedef void (*alProc)(void);
+
+//
+// Windows --------------------------------------------------------------------
+//
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN 1
+#include <windows.h>
+
+static HMODULE libvbf;
+
+static int open_libvbf(void) {
+
+	libvbf = LoadLibraryA("libvorbisfile.dll");
+    if (libvbf == NULL) {
+        return -1;
+    }
+    return 0;
+}
+
+static void close_libvbf(void) {
+	FreeLibrary(libvbf);
+}
+
+static alProc get_proc(const char *proc) {
+    return (alProc) GetProcAddress(libvbf, proc);
+}
+//
+// Mac --------------------------------------------------------------------
+//
+#elif defined(__APPLE__) || defined(__APPLE_CC__)
+#include <Carbon/Carbon.h>
+
+CFBundleRef bundle;
+CFURLRef bundleURL;
+
+static void open_libvbf(void) {
+	bundleURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
+		CFSTR("/System/Library/Frameworks/OpenAL.framework"),
+		kCFURLPOSIXPathStyle, true);
+	bundle = CFBundleCreate(kCFAllocatorDefault, bundleURL);
+	assert(bundle != NULL);
+}
+
+static void close_libvbf(void) {
+	CFRelease(bundle);
+	CFRelease(bundleURL);
+}
+
+static alProc get_proc(const char *proc) {
+	GL3WglProc res;
+	CFStringRef procname = CFStringCreateWithCString(kCFAllocatorDefault, proc,
+		kCFStringEncodingASCII);
+	res = (GL3WglProc) CFBundleGetFunctionPointerForName(bundle, procname);
+	CFRelease(procname);
+	return res;
+}
+//
+// Linux --------------------------------------------------------------------
+//
+#else
+#include <dlfcn.h>
+
+static void *libvbf;
+
+static char* lib_names[] = {
+    "libvorbisfile.so.3",
+    "libvorbisfile.so",
+    NULL
+};
+
+static int open_libvbf(void) {
+
+    int i = 0;
+    while (lib_names[i] != NULL) {
+	    libvbf = dlopen(lib_names[i], RTLD_LAZY | RTLD_GLOBAL);
+        if (libvbf != NULL) {
+            dlerror(); // clear errors
+            return 0;
+        }
+        i++;
+    }
+    return -1;
+}
+
+static void close_libvbf(void) {
+	dlclose(libvbf);
+}
+
+static alProc get_proc(const char *proc) {
+    return dlsym(libvbf, proc);
+}
+#endif
+
+// Prototypes of local functions
+static void load_procs(void);
+
+
+// Pointers to functions loaded from shared library
+LPOVCLEAR           p_ov_clear;
+LPOVFOPEN           p_ov_fopen;
+LPOVOPEN            p_ov_open;
+LPOVOPENCALLBACKS   p_ov_open_callbacks;
+LPOVTEST            p_ov_test;
+LPOVTESTCALLBACKS   p_ov_test_callbacks;
+LPOVTESTOPEN        p_ov_test_open;
+LPOVBITRATE         p_ov_bitrate;
+LPOVBITRATEINSTANT  p_ov_bitrate_instant;
+LPOVSTREAMS         p_ov_streams;
+LPOVSEEKABLE        p_ov_seekable;
+LPOVSERIALNUMBER    p_ov_serialnumber;
+LPOVRAWTOTAL        p_ov_raw_total;
+LPOVPCMTOTAL        p_ov_pcm_total;
+LPOVTIMETOTAL       p_ov_time_total;
+LPOVRAWSEEK         p_ov_raw_seek;
+LPOVPCMSEEK         p_ov_pcm_seek;
+LPOVPCMSEEKPAGE     p_ov_pcm_seek_page;
+LPOVTIMESEEK        p_ov_time_seek;
+LPOVTIMESEEKPAGE    p_ov_time_seek_page;
+LPOVRAWSEEKLAP      p_ov_raw_seek_lap;
+LPOVPCMSEEKLAP      p_ov_pcm_seek_lap;
+LPOVPCMSEEKPAGELAP  p_ov_pcm_seek_page_lap;
+LPOVTIMESEEKLAP     p_ov_time_seek_lap;
+LPOVTIMESEEKPAGELAP p_ov_time_seek_page_lap;
+LPOVRAWTELL         p_ov_raw_tell;
+LPOVPCMTELL         p_ov_pcm_tell;
+LPOVTIMETELL        p_ov_time_tell;
+LPOVINFO            p_ov_info;
+LPOVCOMMENT         p_ov_comment;
+LPOVREADFLOAT       p_ov_read_float;
+LPOVREADFILTER      p_ov_read_filter;
+LPOVREAD            p_ov_read;
+LPOVCROSSLAP        p_ov_crosslap;
+LPOVHALFRATE        p_ov_halfrate;
+LPOVHALFRATEP       p_ov_halfrate_p;
+
+
+// Load functions from shared library
+int vorbisfile_load() {
+
+    int res = open_libvbf();
+    if (res) {
+        return res;
+    }
+    load_procs();
+    return 0;
+}
+
+// Loads function addresses and store in the pointers
+static void load_procs(void) {
+    p_ov_clear              = (LPOVCLEAR)get_proc("ov_clear");
+    p_ov_fopen              = (LPOVFOPEN)get_proc("ov_fopen");
+    p_ov_open               = (LPOVOPEN)get_proc("ov_open");
+    p_ov_open_callbacks     = (LPOVOPENCALLBACKS)get_proc("ov_open_callbacks");
+    p_ov_test               = (LPOVTEST)get_proc("ov_test");
+    p_ov_test_callbacks     = (LPOVTESTCALLBACKS)get_proc("ov_test_callbacks");
+    p_ov_test_open          = (LPOVTESTOPEN)get_proc("ov_test_open");
+    p_ov_bitrate            = (LPOVBITRATE)get_proc("ov_bitrate");
+    p_ov_bitrate_instant    = (LPOVBITRATEINSTANT)get_proc("ov_bitrate_instant");
+    p_ov_streams            = (LPOVSTREAMS)get_proc("ov_streams");
+    p_ov_seekable           = (LPOVSEEKABLE)get_proc("ov_seekable");
+    p_ov_serialnumber       = (LPOVSERIALNUMBER)get_proc("ov_serialnumber");
+    p_ov_raw_total          = (LPOVRAWTOTAL)get_proc("ov_raw_total");
+    p_ov_pcm_total          = (LPOVPCMTOTAL)get_proc("ov_pcm_total");
+    p_ov_time_total         = (LPOVTIMETOTAL)get_proc("ov_time_total");
+    p_ov_raw_seek           = (LPOVRAWSEEK)get_proc("ov_raw_seek");
+    p_ov_pcm_seek           = (LPOVPCMSEEK)get_proc("ov_pcm_seek");
+    p_ov_pcm_seek_page      = (LPOVPCMSEEKPAGE)get_proc("ov_pcm_seek_page");
+    p_ov_time_seek          = (LPOVTIMESEEK)get_proc("ov_time_seek");
+    p_ov_time_seek_page     = (LPOVTIMESEEKPAGE)get_proc("ov_time_seek_page");
+    p_ov_raw_seek_lap       = (LPOVRAWSEEKLAP)get_proc("ov_raw_seek_lap");
+    p_ov_pcm_seek_lap       = (LPOVPCMSEEKLAP)get_proc("ov_pcm_seek_lap");
+    p_ov_pcm_seek_page_lap  = (LPOVPCMSEEKPAGELAP)get_proc("ov_pcm_seek_page_lap");
+    p_ov_time_seek_lap      = (LPOVTIMESEEKLAP)get_proc("ov_time_seek_lap");
+    p_ov_time_seek_page_lap = (LPOVTIMESEEKPAGELAP)get_proc("ov_time_seek_page_lap");
+    p_ov_raw_tell           = (LPOVRAWTELL)get_proc("ov_raw_tell");
+    p_ov_pcm_tell           = (LPOVPCMTELL)get_proc("ov_pcm_tell");
+    p_ov_time_tell          = (LPOVTIMETELL)get_proc("ov_time_tell");
+    p_ov_info               = (LPOVINFO)get_proc("ov_info");
+    p_ov_comment            = (LPOVCOMMENT)get_proc("ov_comment");
+    p_ov_read_float         = (LPOVREADFLOAT)get_proc("ov_read_float");
+    p_ov_read_filter        = (LPOVREADFILTER)get_proc("ov_read_filter");
+    p_ov_read               = (LPOVREAD)get_proc("ov_read");
+    p_ov_crosslap           = (LPOVCROSSLAP)get_proc("ov_crosslap");
+    p_ov_halfrate           = (LPOVHALFRATE)get_proc("ov_halfrate");
+    p_ov_halfrate_p         = (LPOVHALFRATEP)get_proc("ov_halfrate_p");
+}
+
+//
+// Go code cannot directly call the vorbis file function pointers loaded dynamically
+// The following C functions call the corresponding function pointers and can be
+// called by Go code.
+//
+
+int ov_clear(OggVorbis_File *vf) {
+    return p_ov_clear(vf);
+}
+
+int ov_fopen(const char *path,OggVorbis_File *vf) {
+    return p_ov_fopen(path, vf);
+}
+
+int ov_open(FILE *f,OggVorbis_File *vf,const char *initial,long ibytes) {
+    return ov_open(f, vf, initial, ibytes);
+}
+
+int ov_open_callbacks(void *datasource, OggVorbis_File *vf, const char *initial, long ibytes, ov_callbacks callbacks) {
+    return p_ov_open_callbacks(datasource, vf, initial, ibytes, callbacks);
+}
+
+int ov_test(FILE *f,OggVorbis_File *vf,const char *initial,long ibytes) {
+    return p_ov_test(f, vf, initial, ibytes);
+}
+
+int ov_test_callbacks(void *datasource, OggVorbis_File *vf, const char *initial, long ibytes, ov_callbacks callbacks) {
+    return p_ov_test_callbacks(datasource, vf, initial, ibytes, callbacks);
+}
+
+int ov_test_open(OggVorbis_File *vf) {
+    return p_ov_test_open(vf);
+}
+
+long ov_bitrate(OggVorbis_File *vf,int i) {
+    return p_ov_bitrate(vf, i);
+}
+
+long ov_bitrate_instant(OggVorbis_File *vf) {
+    return p_ov_bitrate_instant(vf);
+}
+
+long ov_streams(OggVorbis_File *vf) {
+    return p_ov_streams(vf);
+}
+
+long ov_seekable(OggVorbis_File *vf) {
+    return p_ov_seekable(vf);
+}
+
+long ov_serialnumber(OggVorbis_File *vf,int i) {
+    return p_ov_serialnumber(vf, i);
+}
+
+ogg_int64_t ov_raw_total(OggVorbis_File *vf,int i) {
+    return p_ov_raw_total(vf, i);
+}
+
+ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i) {
+    return p_ov_pcm_total(vf, i);
+}
+
+double ov_time_total(OggVorbis_File *vf,int i) {
+    return p_ov_time_total(vf, i);
+}
+
+int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos) {
+    return p_ov_raw_seek(vf, pos);
+}
+
+int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos) {
+    return p_ov_pcm_seek(vf, pos);
+}
+
+int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos) {
+    return p_ov_pcm_seek_page(vf, pos);
+}
+
+int ov_time_seek(OggVorbis_File *vf,double pos) {
+    return p_ov_time_seek(vf, pos);
+}
+
+int ov_time_seek_page(OggVorbis_File *vf,double pos) {
+    return p_ov_time_seek(vf, pos);
+}
+
+int ov_raw_seek_lap(OggVorbis_File *vf,ogg_int64_t pos) {
+    return p_ov_raw_seek_lap(vf, pos);
+}
+
+int ov_pcm_seek_lap(OggVorbis_File *vf,ogg_int64_t pos) {
+    return p_ov_pcm_seek(vf, pos);
+}
+
+int ov_pcm_seek_page_lap(OggVorbis_File *vf,ogg_int64_t pos) {
+    return p_ov_pcm_seek_page_lap(vf, pos);
+}
+
+int ov_time_seek_lap(OggVorbis_File *vf,double pos) {
+    return p_ov_time_seek_lap(vf, pos);
+}
+
+int ov_time_seek_page_lap(OggVorbis_File *vf,double pos) {
+    return p_ov_time_seek_page_lap(vf, pos);
+}
+
+ogg_int64_t ov_raw_tell(OggVorbis_File *vf) {
+    return p_ov_raw_tell(vf);
+}
+
+ogg_int64_t ov_pcm_tell(OggVorbis_File *vf) {
+    return p_ov_pcm_tell(vf);
+}
+
+double ov_time_tell(OggVorbis_File *vf) {
+    return p_ov_time_tell(vf);
+}
+
+vorbis_info *ov_info(OggVorbis_File *vf,int link) {
+    return p_ov_info(vf, link);
+}
+
+vorbis_comment *ov_comment(OggVorbis_File *vf,int link) {
+    return p_ov_comment(vf, link);
+}
+
+long ov_read_float(OggVorbis_File *vf,float ***pcm_channels,int samples, int *bitstream) {
+    return p_ov_read_float(vf, pcm_channels, samples, bitstream);
+}
+
+long ov_read_filter(OggVorbis_File *vf,char *buffer,int length, int bigendianp,int word,int sgned,int *bitstream,
+                          void (*filter)(float **pcm,long channels,long samples,void *filter_param),void *filter_param) {
+    return p_ov_read_filter(vf, buffer, length, bigendianp, word, sgned, bitstream, filter, filter_param);
+}
+
+long ov_read(OggVorbis_File *vf,char *buffer,int length, int bigendianp,int word,int sgned,int *bitstream) {
+    return p_ov_read(vf, buffer, length, bigendianp, word, sgned, bitstream);
+}
+
+int ov_crosslap(OggVorbis_File *vf1,OggVorbis_File *vf2) {
+    return p_ov_crosslap(vf1, vf2);
+}
+
+int ov_halfrate(OggVorbis_File *vf,int flag) {
+    return p_ov_halfrate(vf, flag);
+}
+
+int ov_halfrate_p(OggVorbis_File *vf) {
+    return p_ov_halfrate_p(vf);
+}
+
+

+ 103 - 0
audio/ov/loader.h

@@ -0,0 +1,103 @@
+#ifndef VBF_LOADER_H
+#define VBF_LOADER_H
+
+#include "vorbis/vorbisfile.h"
+
+#if defined(_WIN32)
+ #define VBF_APIENTRY __cdecl
+#else
+ #define VBF_APIENTRY
+#endif
+
+
+// API function pointers type definitions
+typedef int (VBF_APIENTRY *LPOVCLEAR)(OggVorbis_File *vf);
+typedef int (VBF_APIENTRY *LPOVFOPEN)(const char *path,OggVorbis_File *vf);
+typedef int (VBF_APIENTRY *LPOVOPEN)(FILE *f,OggVorbis_File *vf,const char *initial,long ibytes);
+typedef int (VBF_APIENTRY *LPOVOPENCALLBACKS)(void *datasource, OggVorbis_File *vf, const char *initial, long ibytes, ov_callbacks callbacks);
+
+typedef int (VBF_APIENTRY *LPOVTEST)(FILE *f,OggVorbis_File *vf,const char *initial,long ibytes);
+typedef int (VBF_APIENTRY *LPOVTESTCALLBACKS)(void *datasource, OggVorbis_File *vf, const char *initial, long ibytes, ov_callbacks callbacks);
+typedef int (VBF_APIENTRY *LPOVTESTOPEN)(OggVorbis_File *vf);
+
+typedef long (VBF_APIENTRY *LPOVBITRATE)(OggVorbis_File *vf,int i);
+typedef long (VBF_APIENTRY *LPOVBITRATEINSTANT)(OggVorbis_File *vf);
+typedef int (VBF_APIENTRY *LPOVSTREAMS)(OggVorbis_File *vf);
+typedef int (VBF_APIENTRY *LPOVSEEKABLE)(OggVorbis_File *vf);
+typedef int (VBF_APIENTRY *LPOVSERIALNUMBER)(OggVorbis_File *vf,int i);
+
+typedef ogg_int64_t (VBF_APIENTRY *LPOVRAWTOTAL)(OggVorbis_File *vf,int i);
+typedef ogg_int64_t (VBF_APIENTRY *LPOVPCMTOTAL)(OggVorbis_File *vf,int i);
+typedef double (VBF_APIENTRY *LPOVTIMETOTAL)(OggVorbis_File *vf,int i);
+
+typedef int (VBF_APIENTRY *LPOVRAWSEEK)(OggVorbis_File *vf,ogg_int64_t pos);
+typedef int (VBF_APIENTRY *LPOVPCMSEEK)(OggVorbis_File *vf,ogg_int64_t pos);
+typedef int (VBF_APIENTRY *LPOVPCMSEEKPAGE)(OggVorbis_File *vf,ogg_int64_t pos);
+typedef int (VBF_APIENTRY *LPOVTIMESEEK)(OggVorbis_File *vf,double pos);
+typedef int (VBF_APIENTRY *LPOVTIMESEEKPAGE)(OggVorbis_File *vf,double pos);
+
+typedef int (VBF_APIENTRY *LPOVRAWSEEKLAP)(OggVorbis_File *vf,ogg_int64_t pos);
+typedef int (VBF_APIENTRY *LPOVPCMSEEKLAP)(OggVorbis_File *vf,ogg_int64_t pos);
+typedef int (VBF_APIENTRY *LPOVPCMSEEKPAGELAP)(OggVorbis_File *vf,ogg_int64_t pos);
+typedef int (VBF_APIENTRY *LPOVTIMESEEKLAP)(OggVorbis_File *vf,double pos);
+typedef int (VBF_APIENTRY *LPOVTIMESEEKPAGELAP)(OggVorbis_File *vf,double pos);
+
+typedef ogg_int64_t (VBF_APIENTRY *LPOVRAWTELL)(OggVorbis_File *vf);
+typedef ogg_int64_t (VBF_APIENTRY *LPOVPCMTELL)(OggVorbis_File *vf);
+typedef double (VBF_APIENTRY *LPOVTIMETELL)(OggVorbis_File *vf);
+
+typedef vorbis_info* (VBF_APIENTRY *LPOVINFO)(OggVorbis_File *vf,int link);
+typedef vorbis_comment* (VBF_APIENTRY *LPOVCOMMENT)(OggVorbis_File *vf,int link);
+
+typedef long (VBF_APIENTRY *LPOVREADFLOAT)(OggVorbis_File *vf,float ***pcm_channels,int samples, int *bitstream);
+typedef long (VBF_APIENTRY *LPOVREADFILTER)(OggVorbis_File *vf,char *buffer,int length, int bigendianp,int word,int sgned,int *bitstream, void (*filter)(float **pcm,long channels,long samples,void *filter_param),void *filter_param); 
+typedef long (VBF_APIENTRY *LPOVREAD)(OggVorbis_File *vf,char *buffer,int length, int bigendianp,int word,int sgned,int *bitstream);
+typedef int (VBF_APIENTRY *LPOVCROSSLAP)(OggVorbis_File *vf1,OggVorbis_File *vf2);
+typedef int (VBF_APIENTRY *LPOVHALFRATE)(OggVorbis_File *vf,int flag);
+typedef int (VBF_APIENTRY *LPOVHALFRATEP)(OggVorbis_File *vf);
+
+
+int vorbisfile_load();
+
+
+extern LPOVCLEAR           p_ov_clear;
+extern LPOVFOPEN           p_ov_fopen;
+extern LPOVOPEN            p_ov_open;
+extern LPOVOPENCALLBACKS   p_ov_open_callbacks;
+extern LPOVTEST            p_ov_test;
+extern LPOVTESTCALLBACKS   p_ov_test_callbacks;
+extern LPOVTESTOPEN        p_ov_test_open;
+extern LPOVBITRATE         p_ov_bitrate;
+extern LPOVBITRATEINSTANT  p_ov_bitrate_instant;
+extern LPOVSTREAMS         p_ov_streams;
+extern LPOVSEEKABLE        p_ov_seekable;
+extern LPOVSERIALNUMBER    p_ov_serialnumber;
+extern LPOVRAWTOTAL        p_ov_raw_total;
+extern LPOVPCMTOTAL        p_ov_pcm_total;
+extern LPOVTIMETOTAL       p_ov_time_total;
+extern LPOVRAWSEEK         p_ov_raw_seek;
+extern LPOVPCMSEEK         p_ov_pcm_seek;
+extern LPOVPCMSEEKPAGE     p_ov_pcm_seek_page;
+extern LPOVTIMESEEK        p_ov_time_seek;
+extern LPOVTIMESEEKPAGE    p_ov_time_seek_page;
+extern LPOVRAWSEEKLAP      p_ov_raw_seek_lap;
+extern LPOVPCMSEEKLAP      p_ov_pcm_seek_lap;
+extern LPOVPCMSEEKPAGELAP  p_ov_pcm_seek_page_lap;
+extern LPOVTIMESEEKLAP     p_ov_time_seek_lap;
+extern LPOVTIMESEEKPAGELAP p_ov_time_seek_page_lap;
+extern LPOVRAWTELL         p_ov_raw_tell;
+extern LPOVPCMTELL         p_ov_pcm_tell;
+extern LPOVTIMETELL        p_ov_time_tell;
+extern LPOVINFO            p_ov_info;
+extern LPOVCOMMENT         p_ov_comment;
+extern LPOVREADFLOAT       p_ov_read_float;
+extern LPOVREADFILTER      p_ov_read_filter;
+extern LPOVREAD            p_ov_read;
+extern LPOVCROSSLAP        p_ov_crosslap;
+extern LPOVHALFRATE        p_ov_halfrate;
+extern LPOVHALFRATEP       p_ov_halfrate_p;
+
+
+
+#endif
+

+ 210 - 0
audio/ov/ogg/ogg.h

@@ -0,0 +1,210 @@
+/********************************************************************
+ *                                                                  *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.   *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
+ *                                                                  *
+ * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007             *
+ * by the Xiph.Org Foundation http://www.xiph.org/                  *
+ *                                                                  *
+ ********************************************************************
+
+ function: toplevel libogg include
+ last mod: $Id: ogg.h 18044 2011-08-01 17:55:20Z gmaxwell $
+
+ ********************************************************************/
+#ifndef _OGG_H
+#define _OGG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+#include <ogg/os_types.h>
+
+typedef struct {
+  void *iov_base;
+  size_t iov_len;
+} ogg_iovec_t;
+
+typedef struct {
+  long endbyte;
+  int  endbit;
+
+  unsigned char *buffer;
+  unsigned char *ptr;
+  long storage;
+} oggpack_buffer;
+
+/* ogg_page is used to encapsulate the data in one Ogg bitstream page *****/
+
+typedef struct {
+  unsigned char *header;
+  long header_len;
+  unsigned char *body;
+  long body_len;
+} ogg_page;
+
+/* ogg_stream_state contains the current encode/decode state of a logical
+   Ogg bitstream **********************************************************/
+
+typedef struct {
+  unsigned char   *body_data;    /* bytes from packet bodies */
+  long    body_storage;          /* storage elements allocated */
+  long    body_fill;             /* elements stored; fill mark */
+  long    body_returned;         /* elements of fill returned */
+
+
+  int     *lacing_vals;      /* The values that will go to the segment table */
+  ogg_int64_t *granule_vals; /* granulepos values for headers. Not compact
+                                this way, but it is simple coupled to the
+                                lacing fifo */
+  long    lacing_storage;
+  long    lacing_fill;
+  long    lacing_packet;
+  long    lacing_returned;
+
+  unsigned char    header[282];      /* working space for header encode */
+  int              header_fill;
+
+  int     e_o_s;          /* set when we have buffered the last packet in the
+                             logical bitstream */
+  int     b_o_s;          /* set after we've written the initial page
+                             of a logical bitstream */
+  long    serialno;
+  long    pageno;
+  ogg_int64_t  packetno;  /* sequence number for decode; the framing
+                             knows where there's a hole in the data,
+                             but we need coupling so that the codec
+                             (which is in a separate abstraction
+                             layer) also knows about the gap */
+  ogg_int64_t   granulepos;
+
+} ogg_stream_state;
+
+/* ogg_packet is used to encapsulate the data and metadata belonging
+   to a single raw Ogg/Vorbis packet *************************************/
+
+typedef struct {
+  unsigned char *packet;
+  long  bytes;
+  long  b_o_s;
+  long  e_o_s;
+
+  ogg_int64_t  granulepos;
+
+  ogg_int64_t  packetno;     /* sequence number for decode; the framing
+                                knows where there's a hole in the data,
+                                but we need coupling so that the codec
+                                (which is in a separate abstraction
+                                layer) also knows about the gap */
+} ogg_packet;
+
+typedef struct {
+  unsigned char *data;
+  int storage;
+  int fill;
+  int returned;
+
+  int unsynced;
+  int headerbytes;
+  int bodybytes;
+} ogg_sync_state;
+
+/* Ogg BITSTREAM PRIMITIVES: bitstream ************************/
+
+extern void  oggpack_writeinit(oggpack_buffer *b);
+extern int   oggpack_writecheck(oggpack_buffer *b);
+extern void  oggpack_writetrunc(oggpack_buffer *b,long bits);
+extern void  oggpack_writealign(oggpack_buffer *b);
+extern void  oggpack_writecopy(oggpack_buffer *b,void *source,long bits);
+extern void  oggpack_reset(oggpack_buffer *b);
+extern void  oggpack_writeclear(oggpack_buffer *b);
+extern void  oggpack_readinit(oggpack_buffer *b,unsigned char *buf,int bytes);
+extern void  oggpack_write(oggpack_buffer *b,unsigned long value,int bits);
+extern long  oggpack_look(oggpack_buffer *b,int bits);
+extern long  oggpack_look1(oggpack_buffer *b);
+extern void  oggpack_adv(oggpack_buffer *b,int bits);
+extern void  oggpack_adv1(oggpack_buffer *b);
+extern long  oggpack_read(oggpack_buffer *b,int bits);
+extern long  oggpack_read1(oggpack_buffer *b);
+extern long  oggpack_bytes(oggpack_buffer *b);
+extern long  oggpack_bits(oggpack_buffer *b);
+extern unsigned char *oggpack_get_buffer(oggpack_buffer *b);
+
+extern void  oggpackB_writeinit(oggpack_buffer *b);
+extern int   oggpackB_writecheck(oggpack_buffer *b);
+extern void  oggpackB_writetrunc(oggpack_buffer *b,long bits);
+extern void  oggpackB_writealign(oggpack_buffer *b);
+extern void  oggpackB_writecopy(oggpack_buffer *b,void *source,long bits);
+extern void  oggpackB_reset(oggpack_buffer *b);
+extern void  oggpackB_writeclear(oggpack_buffer *b);
+extern void  oggpackB_readinit(oggpack_buffer *b,unsigned char *buf,int bytes);
+extern void  oggpackB_write(oggpack_buffer *b,unsigned long value,int bits);
+extern long  oggpackB_look(oggpack_buffer *b,int bits);
+extern long  oggpackB_look1(oggpack_buffer *b);
+extern void  oggpackB_adv(oggpack_buffer *b,int bits);
+extern void  oggpackB_adv1(oggpack_buffer *b);
+extern long  oggpackB_read(oggpack_buffer *b,int bits);
+extern long  oggpackB_read1(oggpack_buffer *b);
+extern long  oggpackB_bytes(oggpack_buffer *b);
+extern long  oggpackB_bits(oggpack_buffer *b);
+extern unsigned char *oggpackB_get_buffer(oggpack_buffer *b);
+
+/* Ogg BITSTREAM PRIMITIVES: encoding **************************/
+
+extern int      ogg_stream_packetin(ogg_stream_state *os, ogg_packet *op);
+extern int      ogg_stream_iovecin(ogg_stream_state *os, ogg_iovec_t *iov,
+                                   int count, long e_o_s, ogg_int64_t granulepos);
+extern int      ogg_stream_pageout(ogg_stream_state *os, ogg_page *og);
+extern int      ogg_stream_pageout_fill(ogg_stream_state *os, ogg_page *og, int nfill);
+extern int      ogg_stream_flush(ogg_stream_state *os, ogg_page *og);
+extern int      ogg_stream_flush_fill(ogg_stream_state *os, ogg_page *og, int nfill);
+
+/* Ogg BITSTREAM PRIMITIVES: decoding **************************/
+
+extern int      ogg_sync_init(ogg_sync_state *oy);
+extern int      ogg_sync_clear(ogg_sync_state *oy);
+extern int      ogg_sync_reset(ogg_sync_state *oy);
+extern int      ogg_sync_destroy(ogg_sync_state *oy);
+extern int      ogg_sync_check(ogg_sync_state *oy);
+
+extern char    *ogg_sync_buffer(ogg_sync_state *oy, long size);
+extern int      ogg_sync_wrote(ogg_sync_state *oy, long bytes);
+extern long     ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og);
+extern int      ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og);
+extern int      ogg_stream_pagein(ogg_stream_state *os, ogg_page *og);
+extern int      ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op);
+extern int      ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op);
+
+/* Ogg BITSTREAM PRIMITIVES: general ***************************/
+
+extern int      ogg_stream_init(ogg_stream_state *os,int serialno);
+extern int      ogg_stream_clear(ogg_stream_state *os);
+extern int      ogg_stream_reset(ogg_stream_state *os);
+extern int      ogg_stream_reset_serialno(ogg_stream_state *os,int serialno);
+extern int      ogg_stream_destroy(ogg_stream_state *os);
+extern int      ogg_stream_check(ogg_stream_state *os);
+extern int      ogg_stream_eos(ogg_stream_state *os);
+
+extern void     ogg_page_checksum_set(ogg_page *og);
+
+extern int      ogg_page_version(const ogg_page *og);
+extern int      ogg_page_continued(const ogg_page *og);
+extern int      ogg_page_bos(const ogg_page *og);
+extern int      ogg_page_eos(const ogg_page *og);
+extern ogg_int64_t  ogg_page_granulepos(const ogg_page *og);
+extern int      ogg_page_serialno(const ogg_page *og);
+extern long     ogg_page_pageno(const ogg_page *og);
+extern int      ogg_page_packets(const ogg_page *og);
+
+extern void     ogg_packet_clear(ogg_packet *op);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* _OGG_H */

+ 147 - 0
audio/ov/ogg/os_types.h

@@ -0,0 +1,147 @@
+/********************************************************************
+ *                                                                  *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.   *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
+ *                                                                  *
+ * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002             *
+ * by the Xiph.Org Foundation http://www.xiph.org/                  *
+ *                                                                  *
+ ********************************************************************
+
+ function: #ifdef jail to whip a few platforms into the UNIX ideal.
+ last mod: $Id: os_types.h 19098 2014-02-26 19:06:45Z giles $
+
+ ********************************************************************/
+#ifndef _OS_TYPES_H
+#define _OS_TYPES_H
+
+/* make it easy on the folks that want to compile the libs with a
+   different malloc than stdlib */
+#define _ogg_malloc  malloc
+#define _ogg_calloc  calloc
+#define _ogg_realloc realloc
+#define _ogg_free    free
+
+#if defined(_WIN32)
+
+#  if defined(__CYGWIN__)
+#    include <stdint.h>
+     typedef int16_t ogg_int16_t;
+     typedef uint16_t ogg_uint16_t;
+     typedef int32_t ogg_int32_t;
+     typedef uint32_t ogg_uint32_t;
+     typedef int64_t ogg_int64_t;
+     typedef uint64_t ogg_uint64_t;
+#  elif defined(__MINGW32__)
+#    include <sys/types.h>
+     typedef short ogg_int16_t;
+     typedef unsigned short ogg_uint16_t;
+     typedef int ogg_int32_t;
+     typedef unsigned int ogg_uint32_t;
+     typedef long long ogg_int64_t;
+     typedef unsigned long long ogg_uint64_t;
+#  elif defined(__MWERKS__)
+     typedef long long ogg_int64_t;
+     typedef int ogg_int32_t;
+     typedef unsigned int ogg_uint32_t;
+     typedef short ogg_int16_t;
+     typedef unsigned short ogg_uint16_t;
+#  else
+     /* MSVC/Borland */
+     typedef __int64 ogg_int64_t;
+     typedef __int32 ogg_int32_t;
+     typedef unsigned __int32 ogg_uint32_t;
+     typedef __int16 ogg_int16_t;
+     typedef unsigned __int16 ogg_uint16_t;
+#  endif
+
+#elif defined(__MACOS__)
+
+#  include <sys/types.h>
+   typedef SInt16 ogg_int16_t;
+   typedef UInt16 ogg_uint16_t;
+   typedef SInt32 ogg_int32_t;
+   typedef UInt32 ogg_uint32_t;
+   typedef SInt64 ogg_int64_t;
+
+#elif (defined(__APPLE__) && defined(__MACH__)) /* MacOS X Framework build */
+
+#  include <inttypes.h>
+   typedef int16_t ogg_int16_t;
+   typedef uint16_t ogg_uint16_t;
+   typedef int32_t ogg_int32_t;
+   typedef uint32_t ogg_uint32_t;
+   typedef int64_t ogg_int64_t;
+
+#elif defined(__HAIKU__)
+
+  /* Haiku */
+#  include <sys/types.h>
+   typedef short ogg_int16_t;
+   typedef unsigned short ogg_uint16_t;
+   typedef int ogg_int32_t;
+   typedef unsigned int ogg_uint32_t;
+   typedef long long ogg_int64_t;
+
+#elif defined(__BEOS__)
+
+   /* Be */
+#  include <inttypes.h>
+   typedef int16_t ogg_int16_t;
+   typedef uint16_t ogg_uint16_t;
+   typedef int32_t ogg_int32_t;
+   typedef uint32_t ogg_uint32_t;
+   typedef int64_t ogg_int64_t;
+
+#elif defined (__EMX__)
+
+   /* OS/2 GCC */
+   typedef short ogg_int16_t;
+   typedef unsigned short ogg_uint16_t;
+   typedef int ogg_int32_t;
+   typedef unsigned int ogg_uint32_t;
+   typedef long long ogg_int64_t;
+
+#elif defined (DJGPP)
+
+   /* DJGPP */
+   typedef short ogg_int16_t;
+   typedef int ogg_int32_t;
+   typedef unsigned int ogg_uint32_t;
+   typedef long long ogg_int64_t;
+
+#elif defined(R5900)
+
+   /* PS2 EE */
+   typedef long ogg_int64_t;
+   typedef int ogg_int32_t;
+   typedef unsigned ogg_uint32_t;
+   typedef short ogg_int16_t;
+
+#elif defined(__SYMBIAN32__)
+
+   /* Symbian GCC */
+   typedef signed short ogg_int16_t;
+   typedef unsigned short ogg_uint16_t;
+   typedef signed int ogg_int32_t;
+   typedef unsigned int ogg_uint32_t;
+   typedef long long int ogg_int64_t;
+
+#elif defined(__TMS320C6X__)
+
+   /* TI C64x compiler */
+   typedef signed short ogg_int16_t;
+   typedef unsigned short ogg_uint16_t;
+   typedef signed int ogg_int32_t;
+   typedef unsigned int ogg_uint32_t;
+   typedef long long int ogg_int64_t;
+
+#else
+
+#  include <ogg/config_types.h>
+
+#endif
+
+#endif  /* _OS_TYPES_H */

+ 243 - 0
audio/ov/vorbis/codec.h

@@ -0,0 +1,243 @@
+/********************************************************************
+ *                                                                  *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.   *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
+ *                                                                  *
+ * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001             *
+ * by the Xiph.Org Foundation http://www.xiph.org/                  *
+
+ ********************************************************************
+
+ function: libvorbis codec headers
+ last mod: $Id: codec.h 17021 2010-03-24 09:29:41Z xiphmont $
+
+ ********************************************************************/
+
+#ifndef _vorbis_codec_h_
+#define _vorbis_codec_h_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+#include <ogg/ogg.h>
+
+typedef struct vorbis_info{
+  int version;
+  int channels;
+  long rate;
+
+  /* The below bitrate declarations are *hints*.
+     Combinations of the three values carry the following implications:
+
+     all three set to the same value:
+       implies a fixed rate bitstream
+     only nominal set:
+       implies a VBR stream that averages the nominal bitrate.  No hard
+       upper/lower limit
+     upper and or lower set:
+       implies a VBR bitstream that obeys the bitrate limits. nominal
+       may also be set to give a nominal rate.
+     none set:
+       the coder does not care to speculate.
+  */
+
+  long bitrate_upper;
+  long bitrate_nominal;
+  long bitrate_lower;
+  long bitrate_window;
+
+  void *codec_setup;
+} vorbis_info;
+
+/* vorbis_dsp_state buffers the current vorbis audio
+   analysis/synthesis state.  The DSP state belongs to a specific
+   logical bitstream ****************************************************/
+typedef struct vorbis_dsp_state{
+  int analysisp;
+  vorbis_info *vi;
+
+  float **pcm;
+  float **pcmret;
+  int      pcm_storage;
+  int      pcm_current;
+  int      pcm_returned;
+
+  int  preextrapolate;
+  int  eofflag;
+
+  long lW;
+  long W;
+  long nW;
+  long centerW;
+
+  ogg_int64_t granulepos;
+  ogg_int64_t sequence;
+
+  ogg_int64_t glue_bits;
+  ogg_int64_t time_bits;
+  ogg_int64_t floor_bits;
+  ogg_int64_t res_bits;
+
+  void       *backend_state;
+} vorbis_dsp_state;
+
+typedef struct vorbis_block{
+  /* necessary stream state for linking to the framing abstraction */
+  float  **pcm;       /* this is a pointer into local storage */
+  oggpack_buffer opb;
+
+  long  lW;
+  long  W;
+  long  nW;
+  int   pcmend;
+  int   mode;
+
+  int         eofflag;
+  ogg_int64_t granulepos;
+  ogg_int64_t sequence;
+  vorbis_dsp_state *vd; /* For read-only access of configuration */
+
+  /* local storage to avoid remallocing; it's up to the mapping to
+     structure it */
+  void               *localstore;
+  long                localtop;
+  long                localalloc;
+  long                totaluse;
+  struct alloc_chain *reap;
+
+  /* bitmetrics for the frame */
+  long glue_bits;
+  long time_bits;
+  long floor_bits;
+  long res_bits;
+
+  void *internal;
+
+} vorbis_block;
+
+/* vorbis_block is a single block of data to be processed as part of
+the analysis/synthesis stream; it belongs to a specific logical
+bitstream, but is independent from other vorbis_blocks belonging to
+that logical bitstream. *************************************************/
+
+struct alloc_chain{
+  void *ptr;
+  struct alloc_chain *next;
+};
+
+/* vorbis_info contains all the setup information specific to the
+   specific compression/decompression mode in progress (eg,
+   psychoacoustic settings, channel setup, options, codebook
+   etc). vorbis_info and substructures are in backends.h.
+*********************************************************************/
+
+/* the comments are not part of vorbis_info so that vorbis_info can be
+   static storage */
+typedef struct vorbis_comment{
+  /* unlimited user comment fields.  libvorbis writes 'libvorbis'
+     whatever vendor is set to in encode */
+  char **user_comments;
+  int   *comment_lengths;
+  int    comments;
+  char  *vendor;
+
+} vorbis_comment;
+
+
+/* libvorbis encodes in two abstraction layers; first we perform DSP
+   and produce a packet (see docs/analysis.txt).  The packet is then
+   coded into a framed OggSquish bitstream by the second layer (see
+   docs/framing.txt).  Decode is the reverse process; we sync/frame
+   the bitstream and extract individual packets, then decode the
+   packet back into PCM audio.
+
+   The extra framing/packetizing is used in streaming formats, such as
+   files.  Over the net (such as with UDP), the framing and
+   packetization aren't necessary as they're provided by the transport
+   and the streaming layer is not used */
+
+/* Vorbis PRIMITIVES: general ***************************************/
+
+extern void     vorbis_info_init(vorbis_info *vi);
+extern void     vorbis_info_clear(vorbis_info *vi);
+extern int      vorbis_info_blocksize(vorbis_info *vi,int zo);
+extern void     vorbis_comment_init(vorbis_comment *vc);
+extern void     vorbis_comment_add(vorbis_comment *vc, const char *comment);
+extern void     vorbis_comment_add_tag(vorbis_comment *vc,
+                                       const char *tag, const char *contents);
+extern char    *vorbis_comment_query(vorbis_comment *vc, const char *tag, int count);
+extern int      vorbis_comment_query_count(vorbis_comment *vc, const char *tag);
+extern void     vorbis_comment_clear(vorbis_comment *vc);
+
+extern int      vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb);
+extern int      vorbis_block_clear(vorbis_block *vb);
+extern void     vorbis_dsp_clear(vorbis_dsp_state *v);
+extern double   vorbis_granule_time(vorbis_dsp_state *v,
+                                    ogg_int64_t granulepos);
+
+extern const char *vorbis_version_string(void);
+
+/* Vorbis PRIMITIVES: analysis/DSP layer ****************************/
+
+extern int      vorbis_analysis_init(vorbis_dsp_state *v,vorbis_info *vi);
+extern int      vorbis_commentheader_out(vorbis_comment *vc, ogg_packet *op);
+extern int      vorbis_analysis_headerout(vorbis_dsp_state *v,
+                                          vorbis_comment *vc,
+                                          ogg_packet *op,
+                                          ogg_packet *op_comm,
+                                          ogg_packet *op_code);
+extern float  **vorbis_analysis_buffer(vorbis_dsp_state *v,int vals);
+extern int      vorbis_analysis_wrote(vorbis_dsp_state *v,int vals);
+extern int      vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb);
+extern int      vorbis_analysis(vorbis_block *vb,ogg_packet *op);
+
+extern int      vorbis_bitrate_addblock(vorbis_block *vb);
+extern int      vorbis_bitrate_flushpacket(vorbis_dsp_state *vd,
+                                           ogg_packet *op);
+
+/* Vorbis PRIMITIVES: synthesis layer *******************************/
+extern int      vorbis_synthesis_idheader(ogg_packet *op);
+extern int      vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc,
+                                          ogg_packet *op);
+
+extern int      vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi);
+extern int      vorbis_synthesis_restart(vorbis_dsp_state *v);
+extern int      vorbis_synthesis(vorbis_block *vb,ogg_packet *op);
+extern int      vorbis_synthesis_trackonly(vorbis_block *vb,ogg_packet *op);
+extern int      vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb);
+extern int      vorbis_synthesis_pcmout(vorbis_dsp_state *v,float ***pcm);
+extern int      vorbis_synthesis_lapout(vorbis_dsp_state *v,float ***pcm);
+extern int      vorbis_synthesis_read(vorbis_dsp_state *v,int samples);
+extern long     vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op);
+
+extern int      vorbis_synthesis_halfrate(vorbis_info *v,int flag);
+extern int      vorbis_synthesis_halfrate_p(vorbis_info *v);
+
+/* Vorbis ERRORS and return codes ***********************************/
+
+#define OV_FALSE      -1
+#define OV_EOF        -2
+#define OV_HOLE       -3
+
+#define OV_EREAD      -128
+#define OV_EFAULT     -129
+#define OV_EIMPL      -130
+#define OV_EINVAL     -131
+#define OV_ENOTVORBIS -132
+#define OV_EBADHEADER -133
+#define OV_EVERSION   -134
+#define OV_ENOTAUDIO  -135
+#define OV_EBADPACKET -136
+#define OV_EBADLINK   -137
+#define OV_ENOSEEK    -138
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
+

+ 436 - 0
audio/ov/vorbis/vorbisenc.h

@@ -0,0 +1,436 @@
+/********************************************************************
+ *                                                                  *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.   *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
+ *                                                                  *
+ * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001             *
+ * by the Xiph.Org Foundation http://www.xiph.org/                  *
+ *                                                                  *
+ ********************************************************************
+
+ function: vorbis encode-engine setup
+ last mod: $Id: vorbisenc.h 17021 2010-03-24 09:29:41Z xiphmont $
+
+ ********************************************************************/
+
+/** \file
+ * Libvorbisenc is a convenient API for setting up an encoding
+ * environment using libvorbis. Libvorbisenc encapsulates the
+ * actions needed to set up the encoder properly.
+ */
+
+#ifndef _OV_ENC_H_
+#define _OV_ENC_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+#include "codec.h"
+
+/**
+ * This is the primary function within libvorbisenc for setting up managed
+ * bitrate modes.
+ *
+ * Before this function is called, the \ref vorbis_info
+ * struct should be initialized by using vorbis_info_init() from the libvorbis
+ * API.  After encoding, vorbis_info_clear() should be called.
+ *
+ * The max_bitrate, nominal_bitrate, and min_bitrate settings are used to set
+ * constraints for the encoded file.  This function uses these settings to
+ * select the appropriate encoding mode and set it up.
+ *
+ * \param vi               Pointer to an initialized \ref vorbis_info struct.
+ * \param channels         The number of channels to be encoded.
+ * \param rate             The sampling rate of the source audio.
+ * \param max_bitrate      Desired maximum bitrate (limit). -1 indicates unset.
+ * \param nominal_bitrate  Desired average, or central, bitrate. -1 indicates unset.
+ * \param min_bitrate      Desired minimum bitrate. -1 indicates unset.
+ *
+ * \return Zero for success, and negative values for failure.
+ *
+ * \retval 0          Success.
+ * \retval OV_EFAULT  Internal logic fault; indicates a bug or heap/stack corruption.
+ * \retval OV_EINVAL  Invalid setup request, eg, out of range argument.
+ * \retval OV_EIMPL   Unimplemented mode; unable to comply with bitrate request.
+ */
+extern int vorbis_encode_init(vorbis_info *vi,
+                              long channels,
+                              long rate,
+
+                              long max_bitrate,
+                              long nominal_bitrate,
+                              long min_bitrate);
+
+/**
+ * This function performs step-one of a three-step bitrate-managed encode
+ * setup.  It functions similarly to the one-step setup performed by \ref
+ * vorbis_encode_init but allows an application to make further encode setup
+ * tweaks using \ref vorbis_encode_ctl before finally calling \ref
+ * vorbis_encode_setup_init to complete the setup process.
+ *
+ * Before this function is called, the \ref vorbis_info struct should be
+ * initialized by using vorbis_info_init() from the libvorbis API.  After
+ * encoding, vorbis_info_clear() should be called.
+ *
+ * The max_bitrate, nominal_bitrate, and min_bitrate settings are used to set
+ * constraints for the encoded file.  This function uses these settings to
+ * select the appropriate encoding mode and set it up.
+ *
+ * \param vi                Pointer to an initialized vorbis_info struct.
+ * \param channels          The number of channels to be encoded.
+ * \param rate              The sampling rate of the source audio.
+ * \param max_bitrate       Desired maximum bitrate (limit). -1 indicates unset.
+ * \param nominal_bitrate   Desired average, or central, bitrate. -1 indicates unset.
+ * \param min_bitrate       Desired minimum bitrate. -1 indicates unset.
+ *
+ * \return Zero for success, and negative for failure.
+ *
+ * \retval 0           Success
+ * \retval OV_EFAULT   Internal logic fault; indicates a bug or heap/stack corruption.
+ * \retval OV_EINVAL   Invalid setup request, eg, out of range argument.
+ * \retval OV_EIMPL    Unimplemented mode; unable to comply with bitrate request.
+ */
+extern int vorbis_encode_setup_managed(vorbis_info *vi,
+                                       long channels,
+                                       long rate,
+
+                                       long max_bitrate,
+                                       long nominal_bitrate,
+                                       long min_bitrate);
+
+/**
+ * This function performs step-one of a three-step variable bitrate
+ * (quality-based) encode setup.  It functions similarly to the one-step setup
+ * performed by \ref vorbis_encode_init_vbr() but allows an application to
+ * make further encode setup tweaks using \ref vorbis_encode_ctl() before
+ * finally calling \ref vorbis_encode_setup_init to complete the setup
+ * process.
+ *
+ * Before this function is called, the \ref vorbis_info struct should be
+ * initialized by using \ref vorbis_info_init() from the libvorbis API.  After
+ * encoding, vorbis_info_clear() should be called.
+ *
+ * \param vi        Pointer to an initialized vorbis_info struct.
+ * \param channels  The number of channels to be encoded.
+ * \param rate      The sampling rate of the source audio.
+ * \param quality   Desired quality level, currently from -0.1 to 1.0 (lo to hi).
+ *
+ * \return Zero for success, and negative values for failure.
+ *
+ * \retval  0          Success
+ * \retval  OV_EFAULT  Internal logic fault; indicates a bug or heap/stack corruption.
+ * \retval  OV_EINVAL  Invalid setup request, eg, out of range argument.
+ * \retval  OV_EIMPL   Unimplemented mode; unable to comply with quality level request.
+ */
+extern int vorbis_encode_setup_vbr(vorbis_info *vi,
+                                  long channels,
+                                  long rate,
+
+                                  float quality
+                                  );
+
+/**
+ * This is the primary function within libvorbisenc for setting up variable
+ * bitrate ("quality" based) modes.
+ *
+ *
+ * Before this function is called, the vorbis_info struct should be
+ * initialized by using vorbis_info_init() from the libvorbis API. After
+ * encoding, vorbis_info_clear() should be called.
+ *
+ * \param vi           Pointer to an initialized vorbis_info struct.
+ * \param channels     The number of channels to be encoded.
+ * \param rate         The sampling rate of the source audio.
+ * \param base_quality Desired quality level, currently from -0.1 to 1.0 (lo to hi).
+ *
+ *
+ * \return Zero for success, or a negative number for failure.
+ *
+ * \retval 0           Success
+ * \retval OV_EFAULT   Internal logic fault; indicates a bug or heap/stack corruption.
+ * \retval OV_EINVAL   Invalid setup request, eg, out of range argument.
+ * \retval OV_EIMPL    Unimplemented mode; unable to comply with quality level request.
+ */
+extern int vorbis_encode_init_vbr(vorbis_info *vi,
+                                  long channels,
+                                  long rate,
+
+                                  float base_quality
+                                  );
+
+/**
+ * This function performs the last stage of three-step encoding setup, as
+ * described in the API overview under managed bitrate modes.
+ *
+ * Before this function is called, the \ref vorbis_info struct should be
+ * initialized by using vorbis_info_init() from the libvorbis API, one of
+ * \ref vorbis_encode_setup_managed() or \ref vorbis_encode_setup_vbr() called to
+ * initialize the high-level encoding setup, and \ref vorbis_encode_ctl()
+ * called if necessary to make encoding setup changes.
+ * vorbis_encode_setup_init() finalizes the highlevel encoding structure into
+ * a complete encoding setup after which the application may make no further
+ * setup changes.
+ *
+ * After encoding, vorbis_info_clear() should be called.
+ *
+ * \param vi Pointer to an initialized \ref vorbis_info struct.
+ *
+ * \return Zero for success, and negative values for failure.
+ *
+ * \retval  0           Success.
+ * \retval  OV_EFAULT  Internal logic fault; indicates a bug or heap/stack corruption.
+ *
+ * \retval OV_EINVAL   Attempt to use vorbis_encode_setup_init() without first
+ * calling one of vorbis_encode_setup_managed() or vorbis_encode_setup_vbr() to
+ * initialize the high-level encoding setup
+ *
+ */
+extern int vorbis_encode_setup_init(vorbis_info *vi);
+
+/**
+ * This function implements a generic interface to miscellaneous encoder
+ * settings similar to the classic UNIX 'ioctl()' system call.  Applications
+ * may use vorbis_encode_ctl() to query or set bitrate management or quality
+ * mode details by using one of several \e request arguments detailed below.
+ * vorbis_encode_ctl() must be called after one of
+ * vorbis_encode_setup_managed() or vorbis_encode_setup_vbr().  When used
+ * to modify settings, \ref vorbis_encode_ctl() must be called before \ref
+ * vorbis_encode_setup_init().
+ *
+ * \param vi      Pointer to an initialized vorbis_info struct.
+ *
+ * \param number Specifies the desired action; See \ref encctlcodes "the list
+ * of available requests".
+ *
+ * \param arg void * pointing to a data structure matching the request
+ * argument.
+ *
+ * \retval 0          Success. Any further return information (such as the result of a
+ * query) is placed into the storage pointed to by *arg.
+ *
+ * \retval OV_EINVAL  Invalid argument, or an attempt to modify a setting after
+ * calling vorbis_encode_setup_init().
+ *
+ * \retval OV_EIMPL   Unimplemented or unknown request
+ */
+extern int vorbis_encode_ctl(vorbis_info *vi,int number,void *arg);
+
+/**
+ * \deprecated This is a deprecated interface. Please use vorbis_encode_ctl()
+ * with the \ref ovectl_ratemanage2_arg struct and \ref
+ * OV_ECTL_RATEMANAGE2_GET and \ref OV_ECTL_RATEMANAGE2_SET calls in new code.
+ *
+ * The \ref ovectl_ratemanage_arg structure is used with vorbis_encode_ctl()
+ * and the \ref OV_ECTL_RATEMANAGE_GET, \ref OV_ECTL_RATEMANAGE_SET, \ref
+ * OV_ECTL_RATEMANAGE_AVG, \ref OV_ECTL_RATEMANAGE_HARD calls in order to
+ * query and modify specifics of the encoder's bitrate management
+ * configuration.
+*/
+struct ovectl_ratemanage_arg {
+  int    management_active; /**< nonzero if bitrate management is active*/
+/** hard lower limit (in kilobits per second) below which the stream bitrate
+    will never be allowed for any given bitrate_hard_window seconds of time.*/
+  long   bitrate_hard_min;
+/** hard upper limit (in kilobits per second) above which the stream bitrate
+    will never be allowed for any given bitrate_hard_window seconds of time.*/
+  long   bitrate_hard_max;
+/** the window period (in seconds) used to regulate the hard bitrate minimum
+    and maximum*/
+  double bitrate_hard_window;
+/** soft lower limit (in kilobits per second) below which the average bitrate
+    tracker will start nudging the bitrate higher.*/
+  long   bitrate_av_lo;
+/** soft upper limit (in kilobits per second) above which the average bitrate
+    tracker will start nudging the bitrate lower.*/
+  long   bitrate_av_hi;
+/** the window period (in seconds) used to regulate the average bitrate
+    minimum and maximum.*/
+  double bitrate_av_window;
+/** Regulates the relative centering of the average and hard windows; in
+    libvorbis 1.0 and 1.0.1, the hard window regulation overlapped but
+    followed the average window regulation. In libvorbis 1.1 a bit-reservoir
+    interface replaces the old windowing interface; the older windowing
+    interface is simulated and this field has no effect.*/
+  double bitrate_av_window_center;
+};
+
+/**
+ * \name struct ovectl_ratemanage2_arg
+ *
+ * The ovectl_ratemanage2_arg structure is used with vorbis_encode_ctl() and
+ * the OV_ECTL_RATEMANAGE2_GET and OV_ECTL_RATEMANAGE2_SET calls in order to
+ * query and modify specifics of the encoder's bitrate management
+ * configuration.
+ *
+*/
+struct ovectl_ratemanage2_arg {
+  int    management_active; /**< nonzero if bitrate management is active */
+/** Lower allowed bitrate limit in kilobits per second */
+  long   bitrate_limit_min_kbps;
+/** Upper allowed bitrate limit in kilobits per second */
+  long   bitrate_limit_max_kbps;
+  long   bitrate_limit_reservoir_bits; /**<Size of the bitrate reservoir in bits */
+/** Regulates the bitrate reservoir's preferred fill level in a range from 0.0
+ * to 1.0; 0.0 tries to bank bits to buffer against future bitrate spikes, 1.0
+ * buffers against future sudden drops in instantaneous bitrate. Default is
+ * 0.1
+ */
+  double bitrate_limit_reservoir_bias;
+/** Average bitrate setting in kilobits per second */
+  long   bitrate_average_kbps;
+/** Slew rate limit setting for average bitrate adjustment; sets the minimum
+ *  time in seconds the bitrate tracker may swing from one extreme to the
+ *  other when boosting or damping average bitrate.
+ */
+  double bitrate_average_damping;
+};
+
+
+/**
+ * \name vorbis_encode_ctl() codes
+ *
+ * \anchor encctlcodes
+ *
+ * These values are passed as the \c number parameter of vorbis_encode_ctl().
+ * The type of the referent of that function's \c arg pointer depends on these
+ * codes.
+ */
+/*@{*/
+
+/**
+ * Query the current encoder bitrate management setting.
+ *
+ *Argument: <tt>struct ovectl_ratemanage2_arg *</tt>
+ *
+ * Used to query the current encoder bitrate management setting. Also used to
+ * initialize fields of an ovectl_ratemanage2_arg structure for use with
+ * \ref OV_ECTL_RATEMANAGE2_SET.
+ */
+#define OV_ECTL_RATEMANAGE2_GET      0x14
+
+/**
+ * Set the current encoder bitrate management settings.
+ *
+ * Argument: <tt>struct ovectl_ratemanage2_arg *</tt>
+ *
+ * Used to set the current encoder bitrate management settings to the values
+ * listed in the ovectl_ratemanage2_arg. Passing a NULL pointer will disable
+ * bitrate management.
+*/
+#define OV_ECTL_RATEMANAGE2_SET      0x15
+
+/**
+ * Returns the current encoder hard-lowpass setting (kHz) in the double
+ * pointed to by arg.
+ *
+ * Argument: <tt>double *</tt>
+*/
+#define OV_ECTL_LOWPASS_GET          0x20
+
+/**
+ *  Sets the encoder hard-lowpass to the value (kHz) pointed to by arg. Valid
+ *  lowpass settings range from 2 to 99.
+ *
+ * Argument: <tt>double *</tt>
+*/
+#define OV_ECTL_LOWPASS_SET          0x21
+
+/**
+ *  Returns the current encoder impulse block setting in the double pointed
+ *  to by arg.
+ *
+ * Argument: <tt>double *</tt>
+*/
+#define OV_ECTL_IBLOCK_GET           0x30
+
+/**
+ *  Sets the impulse block bias to the the value pointed to by arg.
+ *
+ * Argument: <tt>double *</tt>
+ *
+ *  Valid range is -15.0 to 0.0 [default]. A negative impulse block bias will
+ *  direct to encoder to use more bits when incoding short blocks that contain
+ *  strong impulses, thus improving the accuracy of impulse encoding.
+ */
+#define OV_ECTL_IBLOCK_SET           0x31
+
+/**
+ *  Returns the current encoder coupling setting in the int pointed
+ *  to by arg.
+ *
+ * Argument: <tt>int *</tt>
+*/
+#define OV_ECTL_COUPLING_GET         0x40
+
+/**
+ *  Enables/disables channel coupling in multichannel encoding according to arg.
+ *
+ * Argument: <tt>int *</tt>
+ *
+ *  Zero disables channel coupling for multichannel inputs, nonzer enables
+ *  channel coupling.  Setting has no effect on monophonic encoding or
+ *  multichannel counts that do not offer coupling.  At present, coupling is
+ *  available for stereo and 5.1 encoding.
+ */
+#define OV_ECTL_COUPLING_SET         0x41
+
+  /* deprecated rate management supported only for compatibility */
+
+/**
+ * Old interface to querying bitrate management settings.
+ *
+ * Deprecated after move to bit-reservoir style management in 1.1 rendered
+ * this interface partially obsolete.
+
+ * \deprecated Please use \ref OV_ECTL_RATEMANAGE2_GET instead.
+ *
+ * Argument: <tt>struct ovectl_ratemanage_arg *</tt>
+ */
+#define OV_ECTL_RATEMANAGE_GET       0x10
+/**
+ * Old interface to modifying bitrate management settings.
+ *
+ *  deprecated after move to bit-reservoir style management in 1.1 rendered
+ *  this interface partially obsolete.
+ *
+ * \deprecated Please use \ref OV_ECTL_RATEMANAGE2_SET instead.
+ *
+ * Argument: <tt>struct ovectl_ratemanage_arg *</tt>
+ */
+#define OV_ECTL_RATEMANAGE_SET       0x11
+/**
+ * Old interface to setting average-bitrate encoding mode.
+ *
+ * Deprecated after move to bit-reservoir style management in 1.1 rendered
+ * this interface partially obsolete.
+ *
+ *  \deprecated Please use \ref OV_ECTL_RATEMANAGE2_SET instead.
+ *
+ * Argument: <tt>struct ovectl_ratemanage_arg *</tt>
+ */
+#define OV_ECTL_RATEMANAGE_AVG       0x12
+/**
+ * Old interface to setting bounded-bitrate encoding modes.
+ *
+ * deprecated after move to bit-reservoir style management in 1.1 rendered
+ * this interface partially obsolete.
+ *
+ *  \deprecated Please use \ref OV_ECTL_RATEMANAGE2_SET instead.
+ *
+ * Argument: <tt>struct ovectl_ratemanage_arg *</tt>
+ */
+#define OV_ECTL_RATEMANAGE_HARD      0x13
+
+/*@}*/
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif

+ 206 - 0
audio/ov/vorbis/vorbisfile.h

@@ -0,0 +1,206 @@
+/********************************************************************
+ *                                                                  *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.   *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
+ *                                                                  *
+ * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007             *
+ * by the Xiph.Org Foundation http://www.xiph.org/                  *
+ *                                                                  *
+ ********************************************************************
+
+ function: stdio-based convenience library for opening/seeking/decoding
+ last mod: $Id: vorbisfile.h 17182 2010-04-29 03:48:32Z xiphmont $
+
+ ********************************************************************/
+
+#ifndef _OV_FILE_H_
+#define _OV_FILE_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+#include <stdio.h>
+#include "codec.h"
+
+/* The function prototypes for the callbacks are basically the same as for
+ * the stdio functions fread, fseek, fclose, ftell.
+ * The one difference is that the FILE * arguments have been replaced with
+ * a void * - this is to be used as a pointer to whatever internal data these
+ * functions might need. In the stdio case, it's just a FILE * cast to a void *
+ *
+ * If you use other functions, check the docs for these functions and return
+ * the right values. For seek_func(), you *MUST* return -1 if the stream is
+ * unseekable
+ */
+typedef struct {
+  size_t (*read_func)  (void *ptr, size_t size, size_t nmemb, void *datasource);
+  int    (*seek_func)  (void *datasource, ogg_int64_t offset, int whence);
+  int    (*close_func) (void *datasource);
+  long   (*tell_func)  (void *datasource);
+} ov_callbacks;
+
+#ifndef OV_EXCLUDE_STATIC_CALLBACKS
+
+/* a few sets of convenient callbacks, especially for use under
+ * Windows where ov_open_callbacks() should always be used instead of
+ * ov_open() to avoid problems with incompatible crt.o version linking
+ * issues. */
+
+static int _ov_header_fseek_wrap(FILE *f,ogg_int64_t off,int whence){
+  if(f==NULL)return(-1);
+
+#ifdef __MINGW32__
+  return fseeko64(f,off,whence);
+#elif defined (_WIN32)
+  return _fseeki64(f,off,whence);
+#else
+  return fseek(f,off,whence);
+#endif
+}
+
+/* These structs below (OV_CALLBACKS_DEFAULT etc) are defined here as
+ * static data. That means that every file which includes this header
+ * will get its own copy of these structs whether it uses them or
+ * not unless it #defines OV_EXCLUDE_STATIC_CALLBACKS.
+ * These static symbols are essential on platforms such as Windows on
+ * which several different versions of stdio support may be linked to
+ * by different DLLs, and we need to be certain we know which one
+ * we're using (the same one as the main application).
+ */
+
+static ov_callbacks OV_CALLBACKS_DEFAULT = {
+  (size_t (*)(void *, size_t, size_t, void *))  fread,
+  (int (*)(void *, ogg_int64_t, int))           _ov_header_fseek_wrap,
+  (int (*)(void *))                             fclose,
+  (long (*)(void *))                            ftell
+};
+
+static ov_callbacks OV_CALLBACKS_NOCLOSE = {
+  (size_t (*)(void *, size_t, size_t, void *))  fread,
+  (int (*)(void *, ogg_int64_t, int))           _ov_header_fseek_wrap,
+  (int (*)(void *))                             NULL,
+  (long (*)(void *))                            ftell
+};
+
+static ov_callbacks OV_CALLBACKS_STREAMONLY = {
+  (size_t (*)(void *, size_t, size_t, void *))  fread,
+  (int (*)(void *, ogg_int64_t, int))           NULL,
+  (int (*)(void *))                             fclose,
+  (long (*)(void *))                            NULL
+};
+
+static ov_callbacks OV_CALLBACKS_STREAMONLY_NOCLOSE = {
+  (size_t (*)(void *, size_t, size_t, void *))  fread,
+  (int (*)(void *, ogg_int64_t, int))           NULL,
+  (int (*)(void *))                             NULL,
+  (long (*)(void *))                            NULL
+};
+
+#endif
+
+#define  NOTOPEN   0
+#define  PARTOPEN  1
+#define  OPENED    2
+#define  STREAMSET 3
+#define  INITSET   4
+
+typedef struct OggVorbis_File {
+  void            *datasource; /* Pointer to a FILE *, etc. */
+  int              seekable;
+  ogg_int64_t      offset;
+  ogg_int64_t      end;
+  ogg_sync_state   oy;
+
+  /* If the FILE handle isn't seekable (eg, a pipe), only the current
+     stream appears */
+  int              links;
+  ogg_int64_t     *offsets;
+  ogg_int64_t     *dataoffsets;
+  long            *serialnos;
+  ogg_int64_t     *pcmlengths; /* overloaded to maintain binary
+                                  compatibility; x2 size, stores both
+                                  beginning and end values */
+  vorbis_info     *vi;
+  vorbis_comment  *vc;
+
+  /* Decoding working state local storage */
+  ogg_int64_t      pcm_offset;
+  int              ready_state;
+  long             current_serialno;
+  int              current_link;
+
+  double           bittrack;
+  double           samptrack;
+
+  ogg_stream_state os; /* take physical pages, weld into a logical
+                          stream of packets */
+  vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
+  vorbis_block     vb; /* local working space for packet->PCM decode */
+
+  ov_callbacks callbacks;
+
+} OggVorbis_File;
+
+
+extern int ov_clear(OggVorbis_File *vf);
+extern int ov_fopen(const char *path,OggVorbis_File *vf);
+extern int ov_open(FILE *f,OggVorbis_File *vf,const char *initial,long ibytes);
+extern int ov_open_callbacks(void *datasource, OggVorbis_File *vf,
+                const char *initial, long ibytes, ov_callbacks callbacks);
+
+extern int ov_test(FILE *f,OggVorbis_File *vf,const char *initial,long ibytes);
+extern int ov_test_callbacks(void *datasource, OggVorbis_File *vf,
+                const char *initial, long ibytes, ov_callbacks callbacks);
+extern int ov_test_open(OggVorbis_File *vf);
+
+extern long ov_bitrate(OggVorbis_File *vf,int i);
+extern long ov_bitrate_instant(OggVorbis_File *vf);
+extern long ov_streams(OggVorbis_File *vf);
+extern long ov_seekable(OggVorbis_File *vf);
+extern long ov_serialnumber(OggVorbis_File *vf,int i);
+
+extern ogg_int64_t ov_raw_total(OggVorbis_File *vf,int i);
+extern ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i);
+extern double ov_time_total(OggVorbis_File *vf,int i);
+
+extern int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos);
+extern int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos);
+extern int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos);
+extern int ov_time_seek(OggVorbis_File *vf,double pos);
+extern int ov_time_seek_page(OggVorbis_File *vf,double pos);
+
+extern int ov_raw_seek_lap(OggVorbis_File *vf,ogg_int64_t pos);
+extern int ov_pcm_seek_lap(OggVorbis_File *vf,ogg_int64_t pos);
+extern int ov_pcm_seek_page_lap(OggVorbis_File *vf,ogg_int64_t pos);
+extern int ov_time_seek_lap(OggVorbis_File *vf,double pos);
+extern int ov_time_seek_page_lap(OggVorbis_File *vf,double pos);
+
+extern ogg_int64_t ov_raw_tell(OggVorbis_File *vf);
+extern ogg_int64_t ov_pcm_tell(OggVorbis_File *vf);
+extern double ov_time_tell(OggVorbis_File *vf);
+
+extern vorbis_info *ov_info(OggVorbis_File *vf,int link);
+extern vorbis_comment *ov_comment(OggVorbis_File *vf,int link);
+
+extern long ov_read_float(OggVorbis_File *vf,float ***pcm_channels,int samples,
+                          int *bitstream);
+extern long ov_read_filter(OggVorbis_File *vf,char *buffer,int length,
+                          int bigendianp,int word,int sgned,int *bitstream,
+                          void (*filter)(float **pcm,long channels,long samples,void *filter_param),void *filter_param);
+extern long ov_read(OggVorbis_File *vf,char *buffer,int length,
+                    int bigendianp,int word,int sgned,int *bitstream);
+extern int ov_crosslap(OggVorbis_File *vf1,OggVorbis_File *vf2);
+
+extern int ov_halfrate(OggVorbis_File *vf,int flag);
+extern int ov_halfrate_p(OggVorbis_File *vf);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
+

+ 232 - 0
audio/ov/vorbisfile.go

@@ -0,0 +1,232 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+Package ov implements the Go bindings of a subset of the functions of the Ogg Vorbis File C library.
+
+It also implements a loader so the library can be dynamically loaded.
+The libvorbisfile C API reference is at: https://xiph.org/vorbis/doc/vorbisfile/reference.html
+
+*/
+package ov
+
+// #include <stdlib.h>
+// #include "vorbis/vorbisfile.h"
+// #include "loader.h"
+import "C"
+
+import (
+	"fmt"
+	"unsafe"
+)
+
+// File type encapsulates a pointer to C allocated OggVorbis_File structure
+type File struct {
+	vf *C.OggVorbis_File
+}
+
+type VorbisInfo struct {
+	Version        int
+	Channels       int
+	Rate           int
+	BitrateUpper   int
+	BitrateNominal int
+	BitrateLower   int
+	BitrateWindow  int
+}
+
+const (
+	Eread      = C.OV_EREAD
+	Efault     = C.OV_EFAULT
+	Eimpl      = C.OV_EIMPL
+	Einval     = C.OV_EINVAL
+	EnotVorbis = C.OV_ENOTVORBIS
+	EbadHeader = C.OV_EBADHEADER
+	Eversion   = C.OV_EVERSION
+	EnotAudio  = C.OV_ENOTAUDIO
+	EbadPacket = C.OV_EBADPACKET
+	EbadLink   = C.OV_EBADLINK
+	EnoSeek    = C.OV_ENOSEEK
+)
+
+// Maps ogg vorbis error codes to string
+var errCodes = map[C.int]string{
+	C.OV_EREAD:      "Eread",
+	C.OV_EFAULT:     "Efault",
+	C.OV_EIMPL:      "Eimpl",
+	C.OV_EINVAL:     "Einval",
+	C.OV_ENOTVORBIS: "EnotVorbis",
+	C.OV_EVERSION:   "Eversion",
+	C.OV_ENOTAUDIO:  "EnotAudio",
+	C.OV_EBADPACKET: "EbadPacket",
+	C.OV_EBADLINK:   "EbadLink",
+	C.OV_ENOSEEK:    "EnoSeek",
+}
+
+// Flag indicating if library has been loaded
+var loaded = false
+
+// Load tries to load dinamically the libvorbisfile shared library/dll.
+// Most of the functions of this package can only be called only
+// after the library was successfully loaded.
+func Load() error {
+
+	// Checks if already loaded
+	if loaded {
+		return nil
+	}
+
+	// Loads libvorbisfile
+	cres := C.vorbisfile_load()
+	if cres == 0 {
+		loaded = true
+		return nil
+	}
+	return fmt.Errorf("Error loading libvorbisfile shared library/dll")
+}
+
+// IsLoaded returns if library has been loaded succesfully
+func IsLoaded() bool {
+
+	return loaded
+}
+
+// Fopen opens an ogg vorbis file for decoding
+// Returns an opaque pointer to the internal decode structure and an error
+func Fopen(path string) (*File, error) {
+
+	checkLoaded()
+	// Allocates pointer to vorbisfile structure using C memory
+	var f File
+	f.vf = (*C.OggVorbis_File)(C.malloc(C.size_t(unsafe.Sizeof(C.OggVorbis_File{}))))
+	fmt.Printf("SIZE:%v\n", unsafe.Sizeof(C.OggVorbis_File{}))
+
+	cpath := C.CString(path)
+	defer C.free(unsafe.Pointer(cpath))
+	cerr := C.ov_fopen(cpath, f.vf)
+	if cerr == 0 {
+		return &f, nil
+	}
+	return nil, fmt.Errorf("Error:%s from Fopen", errCodes[cerr])
+}
+
+// Clear clears the decoded buffers and closes the file
+func Clear(f *File) error {
+
+	checkLoaded()
+	cerr := C.ov_clear(f.vf)
+	if cerr == 0 {
+		C.free(unsafe.Pointer(f.vf))
+		f.vf = nil
+		return nil
+	}
+	return fmt.Errorf("Error:%s from Clear", errCodes[cerr])
+}
+
+// Read decodes next data from the file updating the specified buffer contents and
+// returns the number of bytes read, the number of current logical bitstream and an error
+func Read(f *File, buffer unsafe.Pointer, length int, bigendianp bool, word int, sgned bool) (int, int, error) {
+
+	checkLoaded()
+	var cbigendianp C.int = 0
+	var csgned C.int = 0
+	var bitstream C.int
+
+	if bigendianp {
+		cbigendianp = 1
+	}
+	if sgned {
+		csgned = 1
+	}
+	cres := C.ov_read(f.vf, (*C.char)(buffer), C.int(length), cbigendianp, C.int(word), csgned, &bitstream)
+	if cres < 0 {
+		return 0, 0, fmt.Errorf("Error:%s from Read()", errCodes[C.int(cres)])
+	}
+	return int(cres), int(bitstream), nil
+}
+
+// Info updates the specified VorbisInfo structure with contains basic
+// information about the audio in a vorbis stream
+func Info(f *File, link int, info *VorbisInfo) error {
+
+	checkLoaded()
+	vi := C.ov_info(f.vf, C.int(link))
+	if vi == nil {
+		return fmt.Errorf("Error returned from 'ov_info'")
+	}
+	info.Version = int(vi.version)
+	info.Channels = int(vi.channels)
+	info.Rate = int(vi.rate)
+	info.BitrateUpper = int(vi.bitrate_upper)
+	info.BitrateNominal = int(vi.bitrate_nominal)
+	info.BitrateLower = int(vi.bitrate_lower)
+	info.BitrateWindow = int(vi.bitrate_window)
+	return nil
+}
+
+// Seekable returns indication whether or not the bitstream is seekable
+func Seekable(f *File) bool {
+
+	checkLoaded()
+	cres := C.ov_seekable(f.vf)
+	if cres == 0 {
+		return false
+	}
+	return true
+}
+
+// Seek seeks to the offset specified (in number pcm samples) within the physical bitstream.
+// This function only works for seekable streams.
+// Updates everything needed within the decoder, so you can immediately call Read()
+// and get data from the newly seeked to position.
+func PcmSeek(f *File, pos int64) error {
+
+	checkLoaded()
+	cres := C.ov_pcm_seek(f.vf, C.ogg_int64_t(pos))
+	if cres == 0 {
+		return nil
+	}
+	return fmt.Errorf("Error:%s from 'ov_pcm_seek()'", errCodes[C.int(cres)])
+}
+
+// PcmTotal returns the total number of pcm samples of the physical bitstream or a specified logical bit stream.
+// To retrieve the total pcm samples for the entire physical bitstream, the 'link' parameter should be set to -1
+func PcmTotal(f *File, i int) (int64, error) {
+
+	checkLoaded()
+	cres := C.ov_pcm_total(f.vf, C.int(i))
+	if cres < 0 {
+		return 0, fmt.Errorf("Error:%s from 'ov_pcm_total()'", errCodes[C.int(cres)])
+	}
+	return int64(cres), nil
+}
+
+// TimeTotal returns the total time in seconds of the physical bitstream or a specified logical bitstream
+// To retrieve the time total for the entire physical bitstream, 'i' should be set to -1.
+func TimeTotal(f *File, i int) (float64, error) {
+
+	checkLoaded()
+	cres := C.ov_time_total(f.vf, C.int(i))
+	if cres < 0 {
+		return 0, fmt.Errorf("Error:%s from 'ov_time_total()'", errCodes[C.int(cres)])
+	}
+	return float64(cres), nil
+}
+
+// TimeTell returns the current decoding offset in seconds.
+func TimeTell(f *File) (float64, error) {
+
+	checkLoaded()
+	cres := C.ov_time_tell(f.vf)
+	if cres < 0 {
+		return 0, fmt.Errorf("Error:%s from 'ov_time_total()'", errCodes[C.int(cres)])
+	}
+	return float64(cres), nil
+}
+
+func checkLoaded() {
+	if !loaded {
+		panic("libvorbisfile shared library/dll was not loaded")
+	}
+}

+ 365 - 0
audio/player.go

@@ -0,0 +1,365 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package audio
+
+// #include <stdlib.h>
+import "C"
+
+import (
+	"github.com/g3n/engine/audio/al"
+	"github.com/g3n/engine/core"
+	"github.com/g3n/engine/gls"
+	"github.com/g3n/engine/math32"
+	"io"
+	"time"
+	"unsafe"
+)
+
+const (
+	playerBufferCount = 2
+	playerBufferSize  = 16 * 1024
+)
+
+// Player is a 3D (spatial) audio file player
+// It embeds a core.Node so it can be inserted as a child in any other 3D object.
+type Player struct {
+	core.Node                // Embedded node
+	af        *AudioFile     // Pointer to media audio file
+	buffers   []uint32       // OpenAL buffer names
+	source    uint32         // OpenAL source name
+	nextBuf   int            // Index of next buffer to fill
+	pdata     unsafe.Pointer // Pointer to C allocated storage
+	disposed  bool           // Disposed flag
+	gchan     chan (string)  // Channel for informing of goroutine end
+}
+
+// NewPlayer creates and returns a pointer to a new audio player object
+// which will play the audio encoded in the specified file.
+// Currently it supports wave and Ogg Vorbis formats.
+func NewPlayer(filename string) (*Player, error) {
+
+	// Try to open audio file
+	af, err := NewAudioFile(filename)
+	if err != nil {
+		return nil, err
+	}
+
+	// Creates player
+	p := new(Player)
+	p.Node.Init()
+	p.af = af
+
+	// Generate buffers names
+	p.buffers = al.GenBuffers(playerBufferCount)
+
+	// Generate source name
+	p.source = al.GenSource()
+
+	// Allocates C memory buffer
+	p.pdata = C.malloc(playerBufferSize)
+
+	// Initialize channel for communication with internal goroutine
+	p.gchan = make(chan string, 1)
+	return p, nil
+}
+
+// Dispose disposes of this player resources
+func (p *Player) Dispose() {
+
+	p.Stop()
+
+	// Close file
+	p.af.Close()
+
+	// Release OpenAL resources
+	al.DeleteSource(p.source)
+	al.DeleteBuffers(p.buffers)
+
+	// Release C memory
+	C.free(p.pdata)
+	p.pdata = nil
+	p.disposed = true
+}
+
+// State returns the current state of this player
+func (p *Player) State() int {
+
+	return int(al.GetSourcei(p.source, al.SourceState))
+}
+
+// Play starts playing this player
+func (p *Player) Play() error {
+
+	state := p.State()
+	// Already playing, nothing to do
+	if state == al.Playing {
+		return nil
+	}
+
+	// If paused, goroutine should be running, just starts playing
+	if state == al.Paused {
+		al.SourcePlay(p.source)
+		return nil
+	}
+
+	// Inactive or Stopped state
+	if state == al.Initial || state == al.Stopped {
+
+		// Sets file pointer to the beginning
+		err := p.af.Seek(0)
+		if err != nil {
+			return err
+		}
+
+		// Fill buffers with decoded data
+		for i := 0; i < playerBufferCount; i++ {
+			err = p.fillBuffer(p.buffers[i])
+			if err != nil {
+				if err != io.EOF {
+					return err
+				}
+				break
+			}
+		}
+		p.nextBuf = 0
+
+		// Clear previous goroutine response channel
+		select {
+		case _ = <-p.gchan:
+		default:
+		}
+		// Starts playing and starts goroutine to fill buffers
+		al.SourcePlay(p.source)
+		go p.run()
+		return nil
+	}
+	return nil
+}
+
+// Pause sets the player in the pause state
+func (p *Player) Pause() {
+
+	if p.State() == al.Paused {
+		return
+	}
+	al.SourcePause(p.source)
+}
+
+// Stop stops the player
+func (p *Player) Stop() {
+
+	state := p.State()
+	if state == al.Stopped || state == al.Initial {
+		return
+	}
+	al.SourceStop(p.source)
+	// Waits for goroutine to finish
+	<-p.gchan
+}
+
+// CurrentTime returns the current time in seconds spent in the stream
+func (p *Player) CurrentTime() float64 {
+
+	return p.af.CurrentTime()
+}
+
+// TotalTime returns the total time in seconds to play this stream
+func (p *Player) TotalTime() float64 {
+
+	return p.af.info.TotalTime
+}
+
+// Gain returns the current gain (volume) of this player
+func (p *Player) Gain() float32 {
+
+	return al.GetSourcef(p.source, al.Gain)
+}
+
+// SetGain sets the gain (volume) of this player
+func (p *Player) SetGain(gain float32) {
+
+	al.Sourcef(p.source, al.Gain, gain)
+}
+
+// MinGain returns the current minimum gain of this player
+func (p *Player) MinGain() float32 {
+
+	return al.GetSourcef(p.source, al.MinGain)
+}
+
+// SetMinGain sets the minimum gain (volume) of this player
+func (p *Player) SetMinGain(gain float32) {
+
+	al.Sourcef(p.source, al.MinGain, gain)
+}
+
+// MinGain returns the current maximum gain of this player
+func (p *Player) MaxGain() float32 {
+
+	return al.GetSourcef(p.source, al.MaxGain)
+}
+
+// SetMaxGain sets the maximum gain (volume) of this player
+func (p *Player) SetMaxGain(gain float32) {
+
+	al.Sourcef(p.source, al.MaxGain, gain)
+}
+
+// Pitch returns the current pitch factor of this player
+func (p *Player) Pitch() float32 {
+
+	return al.GetSourcef(p.source, al.Pitch)
+}
+
+// SetPitch sets the pitch factor of this player
+func (p *Player) SetPitch(pitch float32) {
+
+	al.Sourcef(p.source, al.Pitch, pitch)
+}
+
+// Looping returns the current looping state of this player
+func (p *Player) Looping() bool {
+
+	return p.af.Looping()
+}
+
+// SetLooping sets the looping state of this player
+func (p *Player) SetLooping(looping bool) {
+
+	p.af.SetLooping(looping)
+}
+
+// InnerCone returns the inner cone angle in degrees
+func (p *Player) InnerCone() float32 {
+
+	return al.GetSourcef(p.source, al.ConeInnerAngle)
+}
+
+// SetInnerCone sets the inner cone angle in degrees
+func (p *Player) SetInnerCone(inner float32) {
+
+	al.Sourcef(p.source, al.ConeInnerAngle, inner)
+}
+
+// OuterCone returns the outer cone angle in degrees
+func (p *Player) OuterCone() float32 {
+
+	return al.GetSourcef(p.source, al.ConeOuterAngle)
+}
+
+// SetOuterCone sets the outer cone angle in degrees
+func (p *Player) SetOuterCone(outer float32) {
+
+	al.Sourcef(p.source, al.ConeOuterAngle, outer)
+}
+
+// SetVelocity sets the velocity of this player
+// It is used to calculate Doppler effects
+func (p *Player) SetVelocity(vx, vy, vz float32) {
+
+	al.Source3f(p.source, al.Velocity, vx, vy, vz)
+}
+
+// SetVelocityVec sets the velocity of this player from the specified vector
+// It is used to calculate Doppler effects
+func (p Player) SetVelocityVec(v *math32.Vector3) {
+
+	al.Source3f(p.source, al.Velocity, v.X, v.Y, v.Z)
+}
+
+// Velocity returns this player velocity
+func (p *Player) Velocity() (float32, float32, float32) {
+
+	return al.GetSource3f(p.source, al.Velocity)
+}
+
+// VelocityVec returns this player velocity vector
+func (p *Player) VelocityVec() math32.Vector3 {
+
+	vx, vy, vz := al.GetSource3f(p.source, al.Velocity)
+	return math32.Vector3{vx, vy, vz}
+}
+
+// SetRollofFactor sets this player rolloff factor user to calculate
+// the gain attenuation by distance
+func (p *Player) SetRolloffFactor(rfactor float32) {
+
+	al.Sourcef(p.source, al.RolloffFactor, rfactor)
+}
+
+// Render satisfies the INode interface.
+// It is called by renderer at every frame and is used to
+// update the audio source position and direction
+func (p *Player) Render(gl *gls.GLS) {
+
+	// Sets the player source world position
+	var wpos math32.Vector3
+	p.WorldPosition(&wpos)
+	al.Source3f(p.source, al.Position, wpos.X, wpos.Y, wpos.Z)
+
+	// Sets the player source world direction
+	var wdir math32.Vector3
+	p.WorldDirection(&wdir)
+	al.Source3f(p.source, al.Direction, wdir.X, wdir.Y, wdir.Z)
+}
+
+// Goroutine to fill PCM buffers with decoded data for OpenAL
+func (p *Player) run() {
+
+	for {
+		// Get current state of player source
+		state := al.GetSourcei(p.source, al.SourceState)
+		processed := al.GetSourcei(p.source, al.BuffersProcessed)
+		queued := al.GetSourcei(p.source, al.BuffersQueued)
+		//log.Debug("state:%x processed:%v queued:%v", state, processed, queued)
+
+		// If stopped, unqueues all buffer before exiting
+		if state == al.Stopped {
+			if queued == 0 {
+				break
+			}
+			// Unqueue buffers
+			if processed > 0 {
+				al.SourceUnqueueBuffers(p.source, uint32(processed), nil)
+			}
+			continue
+		}
+
+		// If no buffers processed, sleeps and try again
+		if processed == 0 {
+			time.Sleep(20 * time.Millisecond)
+			continue
+		}
+
+		// Remove processed buffers from the queue
+		al.SourceUnqueueBuffers(p.source, uint32(processed), nil)
+		// Fill and enqueue buffers with new data
+		for i := 0; i < int(processed); i++ {
+			err := p.fillBuffer(p.buffers[p.nextBuf])
+			if err != nil {
+				break
+			}
+			p.nextBuf = (p.nextBuf + 1) % playerBufferCount
+		}
+	}
+	// Sends indication of goroutine end
+	p.gchan <- "end"
+}
+
+// fillBuffer fills the specified OpenAL buffer with next decoded data
+// and queues the buffer to this player source
+func (p *Player) fillBuffer(buf uint32) error {
+
+	// Reads next decoded data
+	n, err := p.af.Read(p.pdata, playerBufferSize)
+	if err != nil {
+		return err
+	}
+	// Sends data to buffer
+	//log.Debug("BufferData:%v format:%x n:%v rate:%v", buf, p.af.info.Format, n, p.af.info.SampleRate)
+	al.BufferData(buf, uint32(p.af.info.Format), p.pdata, uint32(n), uint32(p.af.info.SampleRate))
+	al.SourceQueueBuffers(p.source, buf)
+	return nil
+}

+ 9 - 0
audio/vorbis/build.go

@@ -0,0 +1,9 @@
+package vorbis
+
+// #cgo darwin   CFLAGS:  -DGO_DARWIN
+// #cgo linux    CFLAGS:  -DGO_LINUX   -I.
+// #cgo windows  CFLAGS:  -DGO_WINDOWS -I.
+// #cgo darwin   LDFLAGS:
+// #cgo linux    LDFLAGS: -ldl
+// #cgo windows  LDFLAGS:
+import "C"

+ 111 - 0
audio/vorbis/loader.c

@@ -0,0 +1,111 @@
+//
+// Dynamically loads the vorbis shared library / dll
+// Currently only get the pointer to the function to get the library version
+//
+#include "loader.h"
+
+
+typedef void (*alProc)(void);
+
+//
+// Windows --------------------------------------------------------------------
+//
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN 1
+#include <windows.h>
+
+static HMODULE libvb;
+
+static int open_libvb(void) {
+
+	libvb = LoadLibraryA("libvorbis.dll");
+    if (libvb == NULL) {
+        return -1;
+    }
+    return 0;
+}
+
+static void close_libvb(void) {
+	FreeLibrary(libvb);
+}
+
+static alProc get_proc(const char *proc) {
+    return (alProc) GetProcAddress(libvb, proc);
+}
+//
+// Mac --------------------------------------------------------------------
+//
+#elif defined(__APPLE__) || defined(__APPLE_CC__)
+
+
+
+//
+// Linux --------------------------------------------------------------------
+//
+#else
+#include <dlfcn.h>
+
+static void *libvb;
+
+static char* lib_names[] = {
+    "libvorbis.so",
+    "libvorbis.so.0",
+    NULL
+};
+
+static int open_libvb(void) {
+
+    int i = 0;
+    while (lib_names[i] != NULL) {
+	    libvb = dlopen(lib_names[i], RTLD_LAZY | RTLD_GLOBAL);
+        if (libvb != NULL) {
+            dlerror(); // clear errors
+            return 0;
+        }
+        i++;
+    }
+    return -1;
+}
+
+static void close_libvb(void) {
+	dlclose(libvb);
+}
+
+static alProc get_proc(const char *proc) {
+    return dlsym(libvb, proc);
+}
+#endif
+
+// Prototypes of local functions
+static void load_procs(void);
+
+
+// Pointers to functions loaded from shared library
+LPVORBISVERSIONSTRING   p_vorbis_version_string;
+
+
+// Load functions from shared library
+int vorbis_load() {
+
+    int res = open_libvb();
+    if (res) {
+        return res;
+    }
+    load_procs();
+    return 0;
+}
+
+static void load_procs(void) {
+    p_vorbis_version_string = (LPVORBISVERSIONSTRING)get_proc("vorbis_version_string");
+}
+
+//
+// Go code cannot directly call the vorbis file function pointers loaded dynamically
+// The following C functions call the corresponding function pointers and can be
+// called by Go code.
+//
+const char *vorbis_version_string(void) {
+
+    return p_vorbis_version_string();
+}
+

+ 28 - 0
audio/vorbis/loader.h

@@ -0,0 +1,28 @@
+#ifndef VB_LOADER_H
+#define VB_LOADER_H
+
+#include "vorbis/vorbisenc.h"
+
+#if defined(_WIN32)
+ #define VB_APIENTRY __cdecl
+#else
+ #define VB_APIENTRY
+#endif
+
+
+// API function pointers type definitions
+typedef const char* (VB_APIENTRY *LPVORBISVERSIONSTRING)(void);
+
+
+// Loader
+int vorbis_load();
+
+
+// Pointers to functions
+extern LPVORBISVERSIONSTRING   p_vorbis_version_string;
+
+
+
+#endif
+
+

+ 35 - 0
audio/vorbis/vorbis.go

@@ -0,0 +1,35 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+ Package vorbis implements the Go bindings of a subset (only one function) of the functions of the libvorbis library
+ It also implements a loader so the library can be dynamically loaded
+ See API reference at: https://xiph.org/vorbis/doc/libvorbis/reference.html
+*/
+package vorbis
+
+// #include "loader.h"
+import "C"
+
+import (
+	"fmt"
+)
+
+// Load tries to load dinamically libvorbis share library/dll
+func Load() error {
+
+	// Loads libvorbis
+	cres := C.vorbis_load()
+	if cres != 0 {
+		return fmt.Errorf("Error loading libvorbis shared library/dll")
+	}
+	return nil
+}
+
+// VersionString returns a string giving version information for libvorbis
+func VersionString() string {
+
+	cstr := C.vorbis_version_string()
+	return C.GoString(cstr)
+}

+ 94 - 0
audio/wave.go

@@ -0,0 +1,94 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package audio
+
+import (
+	"fmt"
+	"github.com/g3n/engine/audio/al"
+	"os"
+)
+
+// WaveSpecs describes the characterists of the audio encoded in a wave file.
+type WaveSpecs struct {
+	Format     int     // OpenAl Format
+	Type       int     // Type field from wave header
+	Channels   int     // Number of channels
+	SampleRate int     // Sample rate in hz
+	BitsSample int     // Number of bits per sample (8 or 16)
+	DataSize   int     // Total data size in bytes
+	BytesSec   int     // Bytes per second
+	TotalTime  float64 // Total time in seconds
+}
+
+const (
+	waveHeaderSize = 44
+	fileMark       = "RIFF"
+	fileHead       = "WAVE"
+)
+
+// WaveCheck checks if the specified filepath corresponds to a an audio wave file.
+// If the file is a valid wave file, return a pointer to WaveSpec structure
+// with information about the encoded audio data.
+func WaveCheck(filepath string) (*WaveSpecs, error) {
+
+	// Open file
+	f, err := os.Open(filepath)
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+
+	// Reads header
+	header := make([]uint8, waveHeaderSize)
+	n, err := f.Read(header)
+	if err != nil {
+		return nil, err
+	}
+	if n < waveHeaderSize {
+		return nil, fmt.Errorf("File size less than header")
+	}
+	// Checks file marks
+	if string(header[0:4]) != fileMark {
+		return nil, fmt.Errorf("'RIFF' mark not found")
+	}
+	if string(header[8:12]) != fileHead {
+		return nil, fmt.Errorf("'WAVE' mark not found")
+	}
+
+	// Decodes header fields
+	var ws WaveSpecs
+	ws.Format = -1
+	ws.Type = int(header[20]) + int(header[21])<<8
+	ws.Channels = int(header[22]) + int(header[23])<<8
+	ws.SampleRate = int(header[24]) + int(header[25])<<8 + int(header[26])<<16 + int(header[27])<<24
+	ws.BitsSample = int(header[34]) + int(header[35])<<8
+	ws.DataSize = int(header[40]) + int(header[41])<<8 + int(header[42])<<16 + int(header[43])<<24
+
+	// Sets OpenAL format field if possible
+	if ws.Channels == 1 {
+		if ws.BitsSample == 8 {
+			ws.Format = al.FormatMono8
+		} else if ws.BitsSample == 16 {
+			ws.Format = al.FormatMono16
+		}
+	} else if ws.Channels == 2 {
+		if ws.BitsSample == 8 {
+			ws.Format = al.FormatStereo8
+		} else if ws.BitsSample == 16 {
+			ws.Format = al.FormatStereo16
+		}
+	}
+
+	// Calculates bytes/sec and total time
+	var bytesChannel int
+	if ws.BitsSample == 8 {
+		bytesChannel = 1
+	} else {
+		bytesChannel = 2
+	}
+	ws.BytesSec = ws.SampleRate * ws.Channels * bytesChannel
+	ws.TotalTime = float64(ws.DataSize) / float64(ws.BytesSec)
+	return &ws, nil
+}

+ 143 - 0
camera/camera.go

@@ -0,0 +1,143 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package camera contain common camera types used for rendering 3D scenes.
+package camera
+
+import (
+	"github.com/g3n/engine/core"
+	"github.com/g3n/engine/math32"
+)
+
+// ICamera is interface for all camera types
+type ICamera interface {
+	GetCamera() *Camera
+	ViewMatrix(*math32.Matrix4)
+	ProjMatrix(*math32.Matrix4)
+	Project(*math32.Vector3) *math32.Vector3
+	Unproject(*math32.Vector3) *math32.Vector3
+	SetRaycaster(rc *core.Raycaster, x, y float32)
+}
+
+// Camera is the base camera which is normally embedded in other camera types
+type Camera struct {
+	core.Node                 // Embedded Node
+	target     math32.Vector3 // camera target in world coordinates
+	up         math32.Vector3 // camera Up vector
+	viewMatrix math32.Matrix4 // last calculated view matrix
+}
+
+// Initialize initializes the base camera.
+// It is used by other camera types which embed this base camera
+func (cam *Camera) Initialize() {
+
+	cam.Node.Init()
+	cam.target.Set(0, 0, 0)
+	cam.up.Set(0, 1, 0)
+	cam.SetDirection(0, 0, -1)
+	cam.updateQuaternion()
+}
+
+// WorldDirection updates the specified vector with the
+// current world direction the camera is pointed
+func (cam *Camera) WorldDirection(result *math32.Vector3) {
+
+	var wpos math32.Vector3
+	cam.WorldPosition(&wpos)
+	*result = cam.target
+	result.Sub(&wpos).Normalize()
+}
+
+// LookAt sets the camera target position
+func (cam *Camera) LookAt(target *math32.Vector3) {
+
+	cam.target = *target
+	cam.updateQuaternion()
+}
+
+// GetCamera satisfies the ICamera interface
+func (cam *Camera) GetCamera() *Camera {
+
+	return cam
+}
+
+// Target get the current target position
+func (cam *Camera) Target() math32.Vector3 {
+
+	return cam.target
+}
+
+// Up get the current up position
+func (cam *Camera) Up() math32.Vector3 {
+
+	return cam.up
+}
+
+// SetUp sets the camera up vector
+func (cam *Camera) SetUp(up *math32.Vector3) {
+
+	cam.up = *up
+}
+
+// SetPosition sets this camera world position
+// This method overrides the Node method to update
+// the camera quaternion, because changing the camera position
+// may change its rotation
+func (cam *Camera) SetPosition(x, y, z float32) {
+
+	cam.Node.SetPosition(x, y, z)
+	cam.updateQuaternion()
+}
+
+// SetPositionVec sets this node position from the specified vector pointer
+// This method overrides the Node method to update
+// the camera quaternion, because changing the camera position
+// may change its rotation
+func (cam *Camera) SetPositionVec(vpos *math32.Vector3) {
+
+	cam.Node.SetPositionVec(vpos)
+	cam.updateQuaternion()
+}
+
+// ViewMatrix returns the current view matrix of this camera
+func (cam *Camera) ViewMatrix(m *math32.Matrix4) {
+
+	var wpos math32.Vector3
+	cam.WorldPosition(&wpos)
+	cam.viewMatrix.LookAt(&wpos, &cam.target, &cam.up)
+	*m = cam.viewMatrix
+}
+
+// updateQuaternion must be called when the camera position or target
+// is changed to update its quaternion.
+// This is important if the camera has children, such as an audio listener
+func (cam *Camera) updateQuaternion() {
+
+	var wdir math32.Vector3
+	cam.WorldDirection(&wdir)
+	var q math32.Quaternion
+	q.SetFromUnitVectors(&math32.Vector3{0, 0, -1}, &wdir)
+	cam.SetQuaternionQuat(&q)
+}
+
+// Project satisfies the ICamera interface and must
+// be implemented for specific camera types.
+func (cam *Camera) Project(v *math32.Vector3) *math32.Vector3 {
+
+	panic("Not implemented")
+}
+
+// Unproject satisfies the ICamera interface and must
+// be implemented for specific camera types.
+func (cam *Camera) Unproject(v *math32.Vector3) *math32.Vector3 {
+
+	panic("Not implemented")
+}
+
+// SetRaycaster satisfies the ICamera interface and must
+// be implemented for specific camera types.
+func (cam *Camera) SetRaycaster(rc *core.Raycaster, x, y float32) {
+
+	panic("Not implemented")
+}

+ 510 - 0
camera/control/orbit_control.go

@@ -0,0 +1,510 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package control
+
+import (
+	"fmt"
+	"github.com/g3n/engine/camera"
+	"github.com/g3n/engine/math32"
+	"github.com/g3n/engine/util/logger"
+	"github.com/g3n/engine/window"
+	"math"
+)
+
+type OrbitControl struct {
+	Enabled         bool    // Control enabled state
+	EnableRotate    bool    // Rotate enabled state
+	EnableZoom      bool    // Zoom enabled state
+	EnablePan       bool    // Pan enabled state
+	EnableKeys      bool    // Enable keys state
+	ZoomSpeed       float32 // Zoom speed factor. Default is 0.1
+	RotateSpeed     float32 // Rotate speed factor. Default is 1.0
+	MinDistance     float32 // Minimum distance from target. Default is 0.01
+	MaxDistance     float32 // Maximum distance from target. Default is infinity
+	MinPolarAngle   float32 // Minimum polar angle for rotatiom
+	MaxPolarAngle   float32
+	MinAzimuthAngle float32
+	MaxAzimuthAngle float32
+	KeyRotateSpeed  float32
+	KeyPanSpeed     float32
+	// Internal
+	icam        camera.ICamera
+	cam         *camera.Camera
+	camPersp    *camera.Perspective
+	camOrtho    *camera.Orthographic
+	win         window.IWindow
+	position0   math32.Vector3 // Initial camera position
+	target0     math32.Vector3 // Initial camera target position
+	state       int            // current active state
+	phiDelta    float32        // rotation delta in the XZ plane
+	thetaDelta  float32        // rotation delta in the YX plane
+	rotateStart math32.Vector2
+	rotateEnd   math32.Vector2
+	rotateDelta math32.Vector2
+	panStart    math32.Vector2 // initial pan screen coordinates
+	panEnd      math32.Vector2 // final pan scren coordinates
+	panDelta    math32.Vector2
+	panOffset   math32.Vector2
+	zoomStart   float32
+	zoomEnd     float32
+	zoomDelta   float32
+	subsEvents  int // Address of this field is used as events subscription id
+	subsPos     int // Address of this field is used as cursor pos events subscription id
+}
+
+const (
+	stateNone = iota
+	stateRotate
+	stateZoom
+	statePan
+)
+
+// Package logger
+var log = logger.New("ORBIT", logger.Default)
+
+// NewOrbitControl creates and returns a pointer to a new orbito control for
+// the specified camera and window
+func NewOrbitControl(icam camera.ICamera, win window.IWindow) *OrbitControl {
+
+	oc := new(OrbitControl)
+	oc.icam = icam
+	oc.win = win
+
+	oc.cam = icam.GetCamera()
+	if persp, ok := icam.(*camera.Perspective); ok {
+		oc.camPersp = persp
+	} else if ortho, ok := icam.(*camera.Orthographic); ok {
+		oc.camOrtho = ortho
+	} else {
+		panic("Invalid camera type")
+	}
+
+	// Set defaults
+	oc.Enabled = true
+	oc.EnableRotate = true
+	oc.EnableZoom = true
+	oc.EnablePan = true
+	oc.EnableKeys = true
+	oc.ZoomSpeed = 1.0
+	oc.RotateSpeed = 1.0
+	oc.MinDistance = 0.01
+	oc.MaxDistance = float32(math.Inf(1))
+	oc.MinPolarAngle = 0
+	oc.MaxPolarAngle = math32.Pi
+	oc.MinAzimuthAngle = float32(math.Inf(-1))
+	oc.MaxAzimuthAngle = float32(math.Inf(1))
+	oc.KeyPanSpeed = 5.0
+	oc.KeyRotateSpeed = 0.02
+
+	// Saves initial camera parameters
+	oc.position0 = oc.cam.Position()
+	oc.target0 = oc.cam.Target()
+
+	// Subscribe to events
+	oc.win.SubscribeID(window.OnMouseUp, &oc.subsEvents, oc.onMouse)
+	oc.win.SubscribeID(window.OnMouseDown, &oc.subsEvents, oc.onMouse)
+	oc.win.SubscribeID(window.OnScroll, &oc.subsEvents, oc.onScroll)
+	oc.win.SubscribeID(window.OnKeyDown, &oc.subsEvents, oc.onKey)
+	return oc
+}
+
+func (oc *OrbitControl) Dispose() {
+
+	// Unsubscribe to event handlers
+	oc.win.UnsubscribeID(window.OnMouseUp, &oc.subsEvents)
+	oc.win.UnsubscribeID(window.OnMouseDown, &oc.subsEvents)
+	oc.win.UnsubscribeID(window.OnScroll, &oc.subsEvents)
+	oc.win.UnsubscribeID(window.OnKeyDown, &oc.subsEvents)
+	oc.win.UnsubscribeID(window.OnCursor, &oc.subsPos)
+}
+
+// Reset to initial camera position
+func (oc *OrbitControl) Reset() {
+
+	oc.state = stateNone
+	oc.cam.SetPositionVec(&oc.position0)
+	oc.cam.LookAt(&oc.target0)
+}
+
+// Pan the camera and target by the specified deltas
+func (oc *OrbitControl) Pan(deltaX, deltaY float32) {
+
+	width, height := oc.win.GetSize()
+	oc.pan(deltaX, deltaY, width, height)
+	oc.updatePan()
+}
+
+// Zoom in or out
+func (oc *OrbitControl) Zoom(delta float32) {
+
+	oc.zoomDelta = delta
+	oc.updateZoom()
+}
+
+// Rotate camera left by specified angle
+func (oc *OrbitControl) RotateLeft(angle float32) {
+
+	oc.thetaDelta -= angle
+	oc.updateRotate()
+}
+
+// Rotate camera up by specified angle
+func (oc *OrbitControl) RotateUp(angle float32) {
+
+	oc.phiDelta -= angle
+	oc.updateRotate()
+}
+
+// Updates camera rotation from tethaDelta and phiDelta
+func (oc *OrbitControl) updateRotate() {
+
+	const EPS = 0.01
+
+	// Get camera parameters
+	position := oc.cam.Position()
+	target := oc.cam.Target()
+	up := oc.cam.Up()
+
+	// Camera UP is the orbit axis
+	var quat math32.Quaternion
+	quat.SetFromUnitVectors(&up, &math32.Vector3{0, 1, 0})
+	quatInverse := quat
+	quatInverse.Inverse()
+
+	// Calculates direction vector from camera position to target
+	vdir := position
+	vdir.Sub(&target)
+	vdir.ApplyQuaternion(&quat)
+
+	// Calculate angles from current camera position
+	radius := vdir.Length()
+	theta := math32.Atan2(vdir.X, vdir.Z)
+	phi := math32.Acos(vdir.Y / radius)
+
+	// Add deltas to the angles
+	theta += oc.thetaDelta
+	phi += oc.phiDelta
+
+	// Restrict phi (elevation) to be between desired limits
+	phi = math32.Max(oc.MinPolarAngle, math32.Min(oc.MaxPolarAngle, phi))
+	phi = math32.Max(EPS, math32.Min(math32.Pi-EPS, phi))
+	// Restrict theta to be between desired limits
+	theta = math32.Max(oc.MinAzimuthAngle, math32.Min(oc.MaxAzimuthAngle, theta))
+
+	// Calculate new cartesian coordinates
+	vdir.X = radius * math32.Sin(phi) * math32.Sin(theta)
+	vdir.Y = radius * math32.Cos(phi)
+	vdir.Z = radius * math32.Sin(phi) * math32.Cos(theta)
+
+	// Rotate offset back to "camera-up-vector-is-up" space
+	vdir.ApplyQuaternion(&quatInverse)
+
+	position = target
+	position.Add(&vdir)
+	oc.cam.SetPositionVec(&position)
+
+	// Reset deltas
+	oc.thetaDelta = 0
+	oc.phiDelta = 0
+}
+
+// Updates camera rotation from tethaDelta and phiDelta
+// ALTERNATIVE rotation algorithm
+func (oc *OrbitControl) updateRotate2() {
+
+	const EPS = 0.01
+
+	// Get camera parameters
+	position := oc.cam.Position()
+	target := oc.cam.Target()
+	up := oc.cam.Up()
+
+	// Calculates direction vector from target to camera
+	vdir := position
+	vdir.Sub(&target)
+
+	// Calculates right and up vectors
+	var vright math32.Vector3
+	vright.CrossVectors(&up, &vdir)
+	vright.Normalize()
+	var vup math32.Vector3
+	vup.CrossVectors(&vdir, &vright)
+	vup.Normalize()
+
+	phi := vdir.AngleTo(&math32.Vector3{0, 1, 0})
+	newphi := phi + oc.phiDelta
+	if newphi < EPS || newphi > math32.Pi-EPS {
+		oc.phiDelta = 0
+	} else if newphi < oc.MinPolarAngle || newphi > oc.MaxPolarAngle {
+		oc.phiDelta = 0
+	}
+
+	// Rotates position around the two vectors
+	vdir.ApplyAxisAngle(&vup, oc.thetaDelta)
+	vdir.ApplyAxisAngle(&vright, oc.phiDelta)
+
+	// Adds target back get final position
+	position = target
+	position.Add(&vdir)
+	log.Debug("orbit set position")
+	oc.cam.SetPositionVec(&position)
+
+	// Reset deltas
+	oc.thetaDelta = 0
+	oc.phiDelta = 0
+}
+
+// Updates camera pan from panOffset
+func (oc *OrbitControl) updatePan() {
+
+	// Get camera parameters
+	position := oc.cam.Position()
+	target := oc.cam.Target()
+	up := oc.cam.Up()
+
+	// Calculates direction vector from camera position to target
+	vdir := target
+	vdir.Sub(&position)
+	vdir.Normalize()
+
+	// Calculates vector perpendicular to direction and up (side vector)
+	var vpanx math32.Vector3
+	vpanx.CrossVectors(&up, &vdir)
+	vpanx.Normalize()
+
+	// Calculates vector perpendicular to direction and vpanx
+	var vpany math32.Vector3
+	vpany.CrossVectors(&vdir, &vpanx)
+	vpany.Normalize()
+
+	// Adds pan offsets
+	vpanx.MultiplyScalar(oc.panOffset.X)
+	vpany.MultiplyScalar(oc.panOffset.Y)
+	var vpan math32.Vector3
+	vpan.AddVectors(&vpanx, &vpany)
+
+	// Adds offsets to camera position and target
+	position.Add(&vpan)
+	target.Add(&vpan)
+
+	// Sets new camera parameters
+	oc.cam.SetPositionVec(&position)
+	oc.cam.LookAt(&target)
+
+	// Reset deltas
+	oc.panOffset.Set(0, 0)
+}
+
+// Updates camera zoom from zoomDelta
+func (oc *OrbitControl) updateZoom() {
+
+	if oc.camOrtho != nil {
+		zoom := oc.camOrtho.Zoom() - 0.01*oc.zoomDelta
+		oc.camOrtho.SetZoom(zoom)
+		// Reset delta
+		oc.zoomDelta = 0
+		return
+	}
+
+	// Get camera and target positions
+	position := oc.cam.Position()
+	target := oc.cam.Target()
+
+	// Calculates direction vector from target to camera position
+	vdir := position
+	vdir.Sub(&target)
+
+	// Calculates new distance from target and applies limits
+	dist := vdir.Length() * (1.0 + oc.zoomDelta*oc.ZoomSpeed/10.0)
+	dist = math32.Max(oc.MinDistance, math32.Min(oc.MaxDistance, dist))
+	vdir.SetLength(dist)
+
+	// Adds new distance to target to get new camera position
+	target.Add(&vdir)
+	oc.cam.SetPositionVec(&target)
+
+	// Reset delta
+	oc.zoomDelta = 0
+}
+
+// Called when mouse button event is received
+func (oc *OrbitControl) onMouse(evname string, ev interface{}) {
+
+	fmt.Printf("mev:%+v\n", ev.(*window.MouseEvent))
+
+	// If control not enabled ignore event
+	if !oc.Enabled {
+		return
+	}
+
+	mev := ev.(*window.MouseEvent)
+	// Mouse button pressed
+	if mev.Action == window.Press {
+		// Left button pressed sets Rotate state
+		if mev.Button == window.MouseButtonLeft {
+			if !oc.EnableRotate {
+				return
+			}
+			oc.state = stateRotate
+			oc.rotateStart.Set(float32(mev.Xpos), float32(mev.Ypos))
+		} else
+		// Middle button pressed sets Zoom state
+		if mev.Button == window.MouseButtonMiddle {
+			if !oc.EnableZoom {
+				return
+			}
+			oc.state = stateZoom
+			oc.zoomStart = float32(mev.Ypos)
+		} else
+		// Right button pressed sets Pan state
+		if mev.Button == window.MouseButtonRight {
+			if !oc.EnablePan {
+				return
+			}
+			oc.state = statePan
+			oc.panStart.Set(float32(mev.Xpos), float32(mev.Ypos))
+		}
+		// If a valid state is set requests mouse position events
+		if oc.state != stateNone {
+			oc.win.SubscribeID(window.OnCursor, &oc.subsPos, oc.onCursorPos)
+		}
+		return
+	}
+
+	// Mouse button released
+	if mev.Action == window.Release {
+		oc.win.UnsubscribeID(window.OnCursor, &oc.subsPos)
+		oc.state = stateNone
+	}
+}
+
+// Called when cursor position event is received
+func (oc *OrbitControl) onCursorPos(evname string, ev interface{}) {
+
+	// If control not enabled ignore event
+	if !oc.Enabled {
+		return
+	}
+
+	mev := ev.(*window.CursorEvent)
+	// Rotation
+	if oc.state == stateRotate {
+		oc.rotateEnd.Set(float32(mev.Xpos), float32(mev.Ypos))
+		oc.rotateDelta.SubVectors(&oc.rotateEnd, &oc.rotateStart)
+		oc.rotateStart = oc.rotateEnd
+		// rotating across whole screen goes 360 degrees around
+		width, height := oc.win.GetSize()
+		oc.RotateLeft(2 * math32.Pi * oc.rotateDelta.X / float32(width) * oc.RotateSpeed)
+		// rotating up and down along whole screen attempts to go 360, but limited to 180
+		oc.RotateUp(2 * math32.Pi * oc.rotateDelta.Y / float32(height) * oc.RotateSpeed)
+		return
+	}
+
+	// Panning
+	if oc.state == statePan {
+		oc.panEnd.Set(float32(mev.Xpos), float32(mev.Ypos))
+		oc.panDelta.SubVectors(&oc.panEnd, &oc.panStart)
+		oc.panStart = oc.panEnd
+		oc.Pan(oc.panDelta.X, oc.panDelta.Y)
+		return
+	}
+
+	// Zooming
+	if oc.state == stateZoom {
+		oc.zoomEnd = float32(mev.Ypos)
+		oc.zoomDelta = oc.zoomEnd - oc.zoomStart
+		oc.zoomStart = oc.zoomEnd
+		oc.Zoom(oc.zoomDelta)
+	}
+}
+
+// Called when mouse button scroll event is received
+func (oc *OrbitControl) onScroll(evname string, ev interface{}) {
+
+	if !oc.Enabled || !oc.EnableZoom || oc.state != stateNone {
+		return
+	}
+	sev := ev.(*window.ScrollEvent)
+	oc.Zoom(float32(-sev.Yoffset))
+}
+
+// Called when key is pressed, released or repeats.
+func (oc *OrbitControl) onKey(evname string, ev interface{}) {
+
+	if !oc.Enabled || !oc.EnableKeys {
+		return
+	}
+
+	kev := ev.(*window.KeyEvent)
+	if kev.Action == window.Release {
+		return
+	}
+
+	if oc.EnablePan && kev.Mods == 0 {
+		switch kev.Keycode {
+		case window.KeyUp:
+			oc.Pan(0, oc.KeyPanSpeed)
+		case window.KeyDown:
+			oc.Pan(0, -oc.KeyPanSpeed)
+		case window.KeyLeft:
+			oc.Pan(oc.KeyPanSpeed, 0)
+		case window.KeyRight:
+			oc.Pan(-oc.KeyPanSpeed, 0)
+		}
+	}
+
+	if oc.EnableRotate && kev.Mods == window.ModShift {
+		switch kev.Keycode {
+		case window.KeyUp:
+			oc.RotateUp(oc.KeyRotateSpeed)
+		case window.KeyDown:
+			oc.RotateUp(-oc.KeyRotateSpeed)
+		case window.KeyLeft:
+			oc.RotateLeft(-oc.KeyRotateSpeed)
+		case window.KeyRight:
+			oc.RotateLeft(oc.KeyRotateSpeed)
+		}
+	}
+
+	if oc.EnableZoom && kev.Mods == window.ModControl {
+		switch kev.Keycode {
+		case window.KeyUp:
+			oc.Zoom(-1.0)
+		case window.KeyDown:
+			oc.Zoom(1.0)
+		}
+	}
+}
+
+func (oc *OrbitControl) pan(deltaX, deltaY float32, swidth, sheight int) {
+
+	// Perspective camera
+	if oc.camPersp != nil {
+		position := oc.cam.Position()
+		target := oc.cam.Target()
+		offset := position.Clone().Sub(&target)
+		targetDistance := offset.Length()
+		// Half the FOV is center to top of screen
+		targetDistance += math32.Tan((oc.camPersp.Fov() / 2.0) * math32.Pi / 180.0)
+		// we actually don't use screenWidth, since perspective camera is fixed to screen height
+		oc.panLeft(2 * deltaX * targetDistance / float32(sheight))
+		oc.panUp(2 * deltaY * targetDistance / float32(sheight))
+		return
+	}
+	// Orthographic camera
+	left, right, top, bottom, _, _ := oc.camOrtho.Planes()
+	oc.panLeft(deltaX * (right - left) / float32(swidth))
+	oc.panUp(deltaY * (top - bottom) / float32(sheight))
+}
+
+func (oc *OrbitControl) panLeft(distance float32) {
+
+	oc.panOffset.X += distance
+}
+
+func (oc *OrbitControl) panUp(distance float32) {
+
+	oc.panOffset.Y += distance
+}

+ 12 - 0
camera/logger.go

@@ -0,0 +1,12 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package camera
+
+import (
+	"github.com/g3n/engine/util/logger"
+)
+
+// Package logger
+var log = logger.New("CAMERA", logger.Default)

+ 68 - 0
camera/orthographic.go

@@ -0,0 +1,68 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package camera
+
+import (
+	"github.com/g3n/engine/math32"
+)
+
+// Orthographic is
+type Orthographic struct {
+	Camera              // Embedded camera
+	left        float32 // left plane x coordinate
+	right       float32 // right plane x coordinate
+	top         float32 // top plane y coordinate
+	bottom      float32 // bottom plane y coordinate
+	near        float32 // near plane z coordinate
+	far         float32 // far plane z coordinate
+	zoom        float32
+	projChanged bool           // camera projection parameters changed (needs to recalculates projection matrix)
+	projMatrix  math32.Matrix4 // last calculated projection matrix
+}
+
+// NewOrthographic creates and returns a pointer to a new orthographic camera with the specified parameters.
+func NewOrthographic(left, right, top, bottom, near, far float32) *Orthographic {
+
+	cam := new(Orthographic)
+	cam.Camera.Initialize()
+	cam.left = left
+	cam.right = right
+	cam.top = top
+	cam.bottom = bottom
+	cam.near = near
+	cam.far = far
+	cam.zoom = 1.0
+	cam.projChanged = true
+	return cam
+}
+
+// SetZoom sets the zoom factor of the camera
+func (cam *Orthographic) SetZoom(zoom float32) {
+
+	cam.zoom = math32.Abs(zoom)
+	cam.projChanged = true
+}
+
+// Zoom returns the zoom factor of the camera
+func (cam *Orthographic) Zoom() float32 {
+
+	return cam.zoom
+}
+
+// Planes returns the coordinates of the camera planes
+func (cam *Orthographic) Planes() (left, right, top, bottom, near, far float32) {
+
+	return cam.left, cam.right, cam.top, cam.bottom, cam.near, cam.far
+}
+
+// ProjMatrix satisfies the ICamera interface
+func (cam *Orthographic) ProjMatrix(m *math32.Matrix4) {
+
+	if cam.projChanged {
+		cam.projMatrix.MakeOrthographic(cam.left/cam.zoom, cam.right/cam.zoom, cam.top/cam.zoom, cam.bottom/cam.zoom, cam.near, cam.far)
+		cam.projChanged = false
+	}
+	*m = cam.projMatrix
+}

+ 136 - 0
camera/perspective.go

@@ -0,0 +1,136 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package camera
+
+import (
+	"github.com/g3n/engine/core"
+	"github.com/g3n/engine/math32"
+)
+
+type Perspective struct {
+	Camera                     // Embedded camera
+	fov         float32        // field of view in degrees
+	aspect      float32        // aspect ratio (width/height)
+	near        float32        // near plane z coordinate
+	far         float32        // far plane z coordinate
+	projChanged bool           // camera projection parameters changed (needs to recalculates projection matrix)
+	projMatrix  math32.Matrix4 // last calculated projection matrix
+}
+
+// NewPerspective creates and returns a pointer to a new perspective camera with the
+// specified parameters.
+func NewPerspective(fov, aspect, near, far float32) *Perspective {
+
+	cam := new(Perspective)
+	cam.Camera.Initialize()
+	cam.fov = fov
+	cam.aspect = aspect
+	cam.near = near
+	cam.far = far
+	cam.projChanged = true
+	return cam
+}
+
+// SetFov sets the camera field of view in degrees
+func (cam *Perspective) SetFov(fov float32) {
+
+	cam.fov = fov
+	cam.projChanged = true
+}
+
+// SetAspect sets the camera aspect ratio (width/height)
+func (cam *Perspective) SetAspect(aspect float32) {
+
+	cam.aspect = aspect
+	cam.projChanged = true
+}
+
+// Fov returns the current camera FOV (field of view) in degrees
+func (cam *Perspective) Fov() float32 {
+
+	return cam.fov
+}
+
+// Aspect returns the current camera aspect ratio
+func (cam Perspective) Aspect() float32 {
+
+	return cam.aspect
+}
+
+// Near returns the current camera near plane Z coordinate
+func (cam *Perspective) Near() float32 {
+
+	return cam.near
+}
+
+// Far returns the current camera far plane Z coordinate
+func (cam *Perspective) Far() float32 {
+
+	return cam.far
+}
+
+// ProjMatrix satisfies the ICamera interface
+func (cam *Perspective) ProjMatrix(m *math32.Matrix4) {
+
+	cam.updateProjMatrix()
+	*m = cam.projMatrix
+}
+
+// Project transforms the specified position from world coordinates to this camera projected coordinates.
+func (cam *Perspective) Project(v *math32.Vector3) *math32.Vector3 {
+
+	cam.updateProjMatrix()
+	var matrix math32.Matrix4
+	matrixWorld := cam.MatrixWorld()
+	matrix.MultiplyMatrices(&cam.projMatrix, matrix.GetInverse(&matrixWorld, true))
+	v.ApplyProjection(&matrix)
+	return v
+}
+
+// Unproject transforms the specified position from camera projected coordinates to world coordinates.
+func (cam *Perspective) Unproject(v *math32.Vector3) *math32.Vector3 {
+
+	// Get inverted camera view matrix
+	var viewMatrix math32.Matrix4
+	cam.ViewMatrix(&viewMatrix)
+	var invertedViewMatrix math32.Matrix4
+	invertedViewMatrix.GetInverse(&viewMatrix, true)
+
+	// Get inverted camera projection matrix
+	cam.updateProjMatrix()
+	var invertedProjMatrix math32.Matrix4
+	invertedProjMatrix.GetInverse(&cam.projMatrix, true)
+
+	// Multiply invertedViewMatrix by invertedProjMatrix
+	// to get transformation from camera projected coordinates to world coordinates
+	// and project vector using this transformation
+	var matrix math32.Matrix4
+	matrix.MultiplyMatrices(&invertedViewMatrix, &invertedProjMatrix)
+	v.ApplyProjection(&matrix)
+	return v
+}
+
+// SetRaycaster sets the specified raycaster with this camera position in world coordinates
+// pointing to the direction defined by the specified coordinates unprojected using this camera.
+func (cam *Perspective) SetRaycaster(rc *core.Raycaster, sx, sy float32) {
+
+	var origin, direction math32.Vector3
+	matrixWorld := cam.MatrixWorld()
+	origin.SetFromMatrixPosition(&matrixWorld)
+	direction.Set(sx, sy, 0.5)
+	cam.Unproject(&direction).Sub(&origin).Normalize()
+	rc.Set(&origin, &direction)
+	// Updates the view matrix of the raycaster
+	cam.ViewMatrix(&rc.ViewMatrix)
+}
+
+// updateProjMatrix updates this camera projection matrix if necessary
+func (cam *Perspective) updateProjMatrix() {
+
+	if cam.projChanged {
+		cam.projMatrix.MakePerspective(cam.fov, cam.aspect, cam.near, cam.far)
+		cam.projChanged = false
+	}
+}

+ 14 - 0
core/RenderInfo.go

@@ -0,0 +1,14 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package core
+
+import (
+	"github.com/g3n/engine/math32"
+)
+
+type RenderInfo struct {
+	ViewMatrix math32.Matrix4 // Current camera view matrix
+	ProjMatrix math32.Matrix4 // Current camera projection matrix
+}

+ 137 - 0
core/dispatcher.go

@@ -0,0 +1,137 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package core
+
+import ()
+
+type Dispatcher struct {
+	evmap  map[string][]subscription // maps event name to subcriptions list
+	cancel bool                      // flag informing cancelled dispatch
+}
+
+type IDispatcher interface {
+	Subscribe(evname string, cb Callback)
+	SubscribeID(evname string, id interface{}, cb Callback)
+	UnsubscribeID(evname string, id interface{}) int
+	Dispatch(evname string, ev interface{}) bool
+	ClearSubscriptions()
+	CancelDispatch()
+}
+
+type Callback func(string, interface{})
+
+type subscription struct {
+	id interface{}
+	cb func(string, interface{})
+}
+
+// NewEventDispatcher creates and returns a pointer to an Event Dispatcher
+func NewDispatcher() *Dispatcher {
+
+	ed := new(Dispatcher)
+	ed.Initialize()
+	return ed
+}
+
+// Initialize initializes this event dispatcher.
+// It is normally used by other types which embed an event dispatcher
+func (ed *Dispatcher) Initialize() {
+
+	ed.evmap = make(map[string][]subscription)
+}
+
+// Subscribe subscribes to receive events with the given name.
+// If it is necessary to unsubscribe the event, the function SubscribeID
+// should be used.
+func (ed *Dispatcher) Subscribe(evname string, cb Callback) {
+
+	ed.SubscribeID(evname, nil, cb)
+}
+
+// Subscribe subscribes to receive events with the given name.
+// The function accepts a unique id to be use to unsubscribe this event
+func (ed *Dispatcher) SubscribeID(evname string, id interface{}, cb Callback) {
+
+	//log.Debug("Dispatcher(%p).SubscribeID:%s (%v)", ed, evname, id)
+	ed.evmap[evname] = append(ed.evmap[evname], subscription{id, cb})
+}
+
+// Unsubscribe unsubscribes from the specified event and subscription id
+// Returns the number of subscriptions found.
+func (ed *Dispatcher) UnsubscribeID(evname string, id interface{}) int {
+
+	// Get list of subscribers for this event
+	// If not found, nothing to do
+	subs, ok := ed.evmap[evname]
+	if !ok {
+		return 0
+	}
+
+	// Remove all subscribers with the specified id for this event
+	found := 0
+	pos := 0
+	for pos < len(subs) {
+		if subs[pos].id == id {
+			copy(subs[pos:], subs[pos+1:])
+			subs[len(subs)-1] = subscription{}
+			subs = subs[:len(subs)-1]
+			found++
+		} else {
+			pos++
+		}
+	}
+	//log.Debug("Dispatcher(%p).UnsubscribeID:%s (%p): %v",ed, evname, id, found)
+	ed.evmap[evname] = subs
+	return found
+}
+
+// Dispatch dispatch the specified event and data to all registered subscribers.
+// The function returns true if the propagation was cancelled by a subscriber.
+func (ed *Dispatcher) Dispatch(evname string, ev interface{}) bool {
+
+	// Get list of subscribers for this event
+	subs := ed.evmap[evname]
+	if subs == nil {
+		return false
+	}
+
+	// Dispatch to all subscribers
+	//log.Debug("Dispatcher(%p).Dispatch:%s", ed, evname)
+	ed.cancel = false
+	for i := 0; i < len(subs); i++ {
+		subs[i].cb(evname, ev)
+		if ed.cancel {
+			break
+		}
+	}
+	return ed.cancel
+}
+
+// ClearSubscriptions clear all subscriptions from this dispatcher
+func (ed *Dispatcher) ClearSubscriptions() {
+
+	ed.evmap = make(map[string][]subscription)
+	//log.Debug("Dispatcher(%p).ClearSubscriptions: %d", ed, len(ed.evmap))
+}
+
+// CancelDispatch cancels the propagation of the current event.
+// No more subscribers will be called for this event dispatch.
+func (ed *Dispatcher) CancelDispatch() {
+
+	ed.cancel = true
+}
+
+//// LogSubscriptions is used for debugging to log the current
+//// subscriptions of this dispatcher
+//func (ed *Dispatcher) LogSubscriptions() {
+//
+//    for evname, subs := range ed.evmap {
+//        log.Debug("event:%s", evname)
+//        for _, sub := range subs {
+//            log.Debug("   subscription:%v", sub)
+//        }
+//    }
+//    log.Debug("")
+//}

+ 7 - 0
core/doc.go

@@ -0,0 +1,7 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package core implements some basic types used by other packages.
+package core
+

+ 9 - 0
core/logger.go

@@ -0,0 +1,9 @@
+package core
+
+import (
+    "github.com/g3n/engine/util/logger"
+)
+
+// Package logger
+var log = logger.New("CORE", logger.Default)
+

+ 497 - 0
core/node.go

@@ -0,0 +1,497 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package core
+
+import (
+	"github.com/g3n/engine/gls"
+	"github.com/g3n/engine/math32"
+)
+
+// Interface for all node types
+type INode interface {
+	GetNode() *Node
+	UpdateMatrixWorld()
+	Raycast(*Raycaster, *[]Intersect)
+	Render(gs *gls.GLS)
+	Dispose()
+}
+
+type Node struct {
+	Dispatcher                    // Embedded event dispatcher
+	loaderID    string            // ID used by loader
+	name        string            // Optional node name
+	position    math32.Vector3    // Node position, specified as a Vector3
+	rotation    math32.Vector3    // Node rotation, specified in Euler angles.
+	quaternion  math32.Quaternion // Node rotation, specified as a Quaternion.
+	scale       math32.Vector3    // Node scale as a Vector3
+	direction   math32.Vector3    // Initial direction
+	matrix      math32.Matrix4    // Transform matrix relative to this node parent.
+	matrixWorld math32.Matrix4    // Transform world matrix
+	visible     bool              // Visible flag
+	parent      INode             // Parent node
+	children    []INode           // Array with node children
+	userData    interface{}       // Generic user data
+}
+
+// NewNode creates and returns a pointer to a new Node
+func NewNode() *Node {
+
+	n := new(Node)
+	n.Init()
+	return n
+}
+
+// Init initializes this Node
+// It is normally use by other types which embed a Node
+func (n *Node) Init() {
+
+	n.Dispatcher.Initialize()
+	n.position.Set(0, 0, 0)
+	n.rotation.Set(0, 0, 0)
+	n.quaternion.Set(0, 0, 0, 1)
+	n.scale.Set(1, 1, 1)
+	n.direction.Set(0, 0, 1)
+	n.matrix.Identity()
+	n.matrixWorld.Identity()
+	n.children = make([]INode, 0)
+	n.visible = true
+}
+
+// GetNode satisfies the INode interface and returns
+// a pointer to the embedded Node
+func (n *Node) GetNode() *Node {
+
+	return n
+}
+
+// Raycast satisfies the INode interface
+func (n *Node) Raycast(rc *Raycaster, intersects *[]Intersect) {
+}
+
+// Render satisfies the INode interface
+func (n *Node) Render(gs *gls.GLS) {
+}
+
+// Dispose satisfies the INode interface
+func (n *Node) Dispose() {
+}
+
+// SetLoaderID is normally used by external loaders, such as Collada,
+// to assign an ID to the node with the ID value in the node description
+// Can be used to find other loaded nodes.
+func (n *Node) SetLoaderID(id string) {
+
+	n.loaderID = id
+}
+
+// LoaderID returns an optional ID set when this node was
+// created by an external loader such as Collada
+func (n *Node) LoaderID() string {
+
+	return n.loaderID
+}
+
+// FindLoaderID looks in the specified node and all its children
+// for a node with the specifid loaderID and if found returns it.
+// Returns nil if not found
+func (n *Node) FindLoaderID(id string) INode {
+
+	var finder func(parent INode, id string) INode
+	finder = func(parent INode, id string) INode {
+		pnode := parent.GetNode()
+		if pnode.loaderID == id {
+			return parent
+		}
+		for _, child := range pnode.children {
+			found := finder(child, id)
+			if found != nil {
+				return found
+			}
+		}
+		return nil
+	}
+	return finder(n, id)
+}
+
+// SetName set an option name for the node.
+// This name can be used for debugging or other purposes.
+func (n *Node) SetName(name string) {
+
+	n.name = name
+}
+
+// Name returns current optional name for this node
+func (n *Node) Name() string {
+
+	return n.name
+}
+
+// SetPosition sets this node world position
+func (n *Node) SetPosition(x, y, z float32) {
+
+	n.position.Set(x, y, z)
+}
+
+// SetPositionVec sets this node position from the specified vector pointer
+func (n *Node) SetPositionVec(vpos *math32.Vector3) {
+
+	n.position = *vpos
+}
+
+// SetPositionX sets the x coordinate of this node position
+func (n *Node) SetPositionX(x float32) {
+
+	n.position.X = x
+}
+
+// SetPositionY sets the y coordinate of this node position
+func (n *Node) SetPositionY(y float32) {
+
+	n.position.Y = y
+}
+
+// SetPositionZ sets the z coordinate of this node position
+func (n *Node) SetPositionZ(z float32) {
+
+	n.position.Z = z
+}
+
+// Position returns the current node position as a vector
+func (n *Node) Position() math32.Vector3 {
+
+	return n.position
+}
+
+// SetRotation sets the three fields of the node rotation in radians
+// The node quaternion is updated
+func (n *Node) SetRotation(x, y, z float32) {
+
+	n.rotation.Set(x, y, z)
+	n.quaternion.SetFromEuler(&n.rotation)
+}
+
+// SetRotationX sets the x rotation angle in radians
+// The node quaternion is updated
+func (n *Node) SetRotationX(x float32) {
+
+	n.rotation.X = x
+	n.quaternion.SetFromEuler(&n.rotation)
+}
+
+// SetRotationY sets the y rotation angle in radians
+// The node quaternion is updated
+func (n *Node) SetRotationY(y float32) {
+
+	n.rotation.Y = y
+	n.quaternion.SetFromEuler(&n.rotation)
+}
+
+// SetRotationZ sets the z rotation angle in radians
+// The node quaternion is updated
+func (n *Node) SetRotationZ(z float32) {
+
+	n.rotation.Z = z
+	n.quaternion.SetFromEuler(&n.rotation)
+}
+
+// AddRotationX adds to the current rotation x coordinate in radians
+// The node quaternion is updated
+func (n *Node) AddRotationX(x float32) {
+
+	n.rotation.X += x
+	n.quaternion.SetFromEuler(&n.rotation)
+}
+
+// AddRotationY adds to the current rotation y coordinate in radians
+// The node quaternion is updated
+func (n *Node) AddRotationY(y float32) {
+
+	n.rotation.Y += y
+	n.quaternion.SetFromEuler(&n.rotation)
+}
+
+// AddRotationZ adds to the current rotation z coordinate in radians
+// The node quaternion is updated
+func (n *Node) AddRotationZ(z float32) {
+
+	n.rotation.Z += z
+	n.quaternion.SetFromEuler(&n.rotation)
+}
+
+// Rotation returns the current rotation
+func (n *Node) Rotation() math32.Vector3 {
+
+	return n.rotation
+}
+
+// SetQuaternion sets this node  quaternion with the specified fields
+func (n *Node) SetQuaternion(x, y, z, w float32) {
+
+	n.quaternion.Set(x, y, z, w)
+}
+
+// SetQuaternionQuat sets this node quaternion from the specified quaternion pointer
+func (n *Node) SetQuaternionQuat(q *math32.Quaternion) {
+
+	n.quaternion = *q
+}
+
+// QuaternionMult multiplies the quaternion by the specified quaternion
+func (n *Node) QuaternionMult(q *math32.Quaternion) {
+
+	n.quaternion.Multiply(q)
+}
+
+// Quaternion returns the current quaternion
+func (n *Node) Quaternion() math32.Quaternion {
+
+	return n.quaternion
+}
+
+// SetScale sets this node scale fields
+func (n *Node) SetScale(x, y, z float32) {
+
+	n.scale.Set(x, y, z)
+}
+
+// SetScaleVec sets this node scale from a pointer to a Vector3
+func (n *Node) SetScaleVec(scale *math32.Vector3) {
+
+	n.scale = *scale
+}
+
+// SetScaleX sets the X scale of this node
+func (n *Node) SetScaleX(sx float32) {
+
+	n.scale.X = sx
+}
+
+// SetScaleY sets the Y scale of this node
+func (n *Node) SetScaleY(sy float32) {
+
+	n.scale.Y = sy
+}
+
+// SetScaleZ sets the Z scale of this node
+func (n *Node) SetScaleZ(sz float32) {
+
+	n.scale.Z = sz
+}
+
+// Scale returns the current scale
+func (n *Node) Scale() math32.Vector3 {
+
+	return n.scale
+}
+
+// SetDirection sets this node initial direction vector
+func (n *Node) SetDirection(x, y, z float32) {
+
+	n.direction.Set(x, y, z)
+}
+
+// SetDirection sets this node initial direction vector
+func (n *Node) SetDirectionv(vdir *math32.Vector3) {
+
+	n.direction = *vdir
+}
+
+// Direction returns this node initial direction
+func (n *Node) Direction() math32.Vector3 {
+
+	return n.direction
+}
+
+// SetMatrix sets this node local transformation matrix
+func (n *Node) SetMatrix(m *math32.Matrix4) {
+
+	n.matrix = *m
+}
+
+// Matrix returns a copy of this node local transformation matrix
+func (n *Node) Matrix() math32.Matrix4 {
+
+	return n.matrix
+}
+
+// SetVisible sets the node visibility state
+func (n *Node) SetVisible(state bool) {
+
+	n.visible = state
+}
+
+// Visible returns the node visibility state
+func (n *Node) Visible() bool {
+
+	return n.visible
+}
+
+// WorldPosition updates this node world matrix and gets
+// the current world position vector.
+func (n *Node) WorldPosition(result *math32.Vector3) {
+
+	n.UpdateMatrixWorld()
+	result.SetFromMatrixPosition(&n.matrixWorld)
+}
+
+// WorldQuaternion sets the specified result quaternion with
+// this node current world quaternion
+func (n *Node) WorldQuaternion(result *math32.Quaternion) {
+
+	var position math32.Vector3
+	var scale math32.Vector3
+	n.UpdateMatrixWorld()
+	n.matrixWorld.Decompose(&position, result, &scale)
+}
+
+// WorldRotation sets the specified result vector with
+// current world rotation of this node in Euler angles.
+func (n *Node) WorldRotation(result *math32.Vector3) {
+
+	var quaternion math32.Quaternion
+	n.WorldQuaternion(&quaternion)
+	result.SetFromQuaternion(&quaternion)
+}
+
+// WorldScale sets the specified result vector with
+// the current world scale of this node
+func (n *Node) WorldScale(result *math32.Vector3) {
+
+	var position math32.Vector3
+	var quaternion math32.Quaternion
+	n.UpdateMatrixWorld()
+	n.matrixWorld.Decompose(&position, &quaternion, result)
+}
+
+// WorldDirection updates this object world matrix and sets
+// the current world direction.
+func (n *Node) WorldDirection(result *math32.Vector3) {
+
+	var quaternion math32.Quaternion
+	n.WorldQuaternion(&quaternion)
+	*result = n.direction
+	result.ApplyQuaternion(&quaternion)
+}
+
+// MatrixWorld returns a copy of this node matrix world
+func (n *Node) MatrixWorld() math32.Matrix4 {
+
+	return n.matrixWorld
+}
+
+// UpdateMatrix updates this node local matrix transform from its
+// current position, quaternion and scale.
+func (n *Node) UpdateMatrix() {
+
+	n.matrix.Compose(&n.position, &n.quaternion, &n.scale)
+}
+
+// UpdateMatrixWorld updates this node world transform matrix and of all its children
+func (n *Node) UpdateMatrixWorld() {
+
+	n.UpdateMatrix()
+	if n.parent == nil {
+		n.matrixWorld = n.matrix
+	} else {
+		parent := n.parent.GetNode()
+		n.matrixWorld.MultiplyMatrices(&parent.matrixWorld, &n.matrix)
+	}
+	// Update this Node children matrices
+	for _, ichild := range n.children {
+		ichild.UpdateMatrixWorld()
+	}
+}
+
+// SetParent sets this node parent
+func (n *Node) SetParent(iparent INode) {
+
+	n.parent = iparent
+}
+
+// Parent returns this node parent
+func (n *Node) Parent() INode {
+
+	return n.parent
+}
+
+// Children returns the list of this node children
+func (n *Node) Children() []INode {
+
+	return n.children
+}
+
+// Add adds the specified INode to this node list of children
+func (n *Node) Add(ichild INode) *Node {
+
+	child := ichild.GetNode()
+	if n == child {
+		panic("Node.Add: object can't be added as a child of itself")
+		return nil
+	}
+	// If this child already has a parent,
+	// removes it from this parent children list
+	if child.parent != nil {
+		child.parent.GetNode().Remove(ichild)
+	}
+	child.parent = n
+	n.children = append(n.children, ichild)
+	return n
+}
+
+// Remove removes the specified INode from this node list of children
+// Returns true if found or false otherwise
+func (n *Node) Remove(ichild INode) bool {
+
+	for pos, current := range n.children {
+		if current == ichild {
+			copy(n.children[pos:], n.children[pos+1:])
+			n.children[len(n.children)-1] = nil
+			n.children = n.children[:len(n.children)-1]
+			ichild.GetNode().parent = nil
+			return true
+		}
+	}
+	return false
+}
+
+// RemoveAll removes all children from this node
+func (n *Node) RemoveAll(recurs bool) {
+
+	for pos, ichild := range n.children {
+		n.children[pos] = nil
+		ichild.GetNode().parent = nil
+		if recurs {
+			ichild.GetNode().RemoveAll(recurs)
+		}
+	}
+	n.children = n.children[0:0]
+}
+
+// DisposeChildren removes and disposes all children of this
+// node and if 'recurs' is true for each of its children recursively.
+func (n *Node) DisposeChildren(recurs bool) {
+
+	for pos, ichild := range n.children {
+		n.children[pos] = nil
+		ichild.GetNode().parent = nil
+		if recurs {
+			ichild.GetNode().DisposeChildren(true)
+		}
+		ichild.Dispose()
+	}
+	n.children = n.children[0:0]
+}
+
+// SetUserData sets this node associated generic user data
+func (n *Node) SetUserData(data interface{}) {
+
+	n.userData = data
+}
+
+// UserData returns this node associated generic user data
+func (n *Node) UserData() interface{} {
+
+	return n.userData
+}

+ 116 - 0
core/raycaster.go

@@ -0,0 +1,116 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package core
+
+import (
+	"github.com/g3n/engine/math32"
+	"sort"
+)
+
+type Raycaster struct {
+	// The distance from the ray origin to the intersected points
+	// must be greater than the value of this field to be considered.
+	// The defaul value is 0.0
+	Near float32
+	// The distance from the ray origin to the intersected points
+	// must be less than the value of this field to be considered.
+	// The defaul value is +Infinity.
+	Far float32
+	// Minimum distance in world coordinates between the ray and
+	// a line segment when checking intersects with lines.
+	// The default value is 0.1
+	LinePrecision float32
+	// Minimum distance in world coordinates between the ray and
+	// a point when checking intersects with points.
+	// The default value is 0.1
+	PointPrecision float32
+	// This field must be set with the camera view matrix used
+	// when checking for sprite intersections.
+	// It is set automatically when using camera.SetRaycaster
+	ViewMatrix math32.Matrix4
+	// Embedded ray
+	math32.Ray
+}
+
+// Intersect describes the intersection between a ray and an object
+type Intersect struct {
+	// Distance between the origin of the ray and the intersect
+	Distance float32
+	// Point of intersection in world coordinates
+	Point math32.Vector3
+	// Intersected node
+	Object INode
+	// If the geometry has indices, this field is the
+	// index in the Indices buffer of the vertex intersected
+	// or the first vertex of the intersected face.
+	// If the geometry doesn't have indices, this field is the
+	// index in the positions buffer of the vertex intersected
+	// or the first vertex of the insersected face.
+	Index uint32
+}
+
+// New creates and returns a pointer to a new raycaster object
+// with the specified origin and direction.
+func NewRaycaster(origin, direction *math32.Vector3) *Raycaster {
+
+	rc := new(Raycaster)
+	rc.Ray.Set(origin, direction)
+	rc.Near = 0
+	rc.Far = math32.Inf(1)
+	rc.LinePrecision = 0.1
+	rc.PointPrecision = 0.1
+	return rc
+}
+
+// IntersectObject checks intersections between this raycaster and
+// and the specified node. If recursive is true, it also checks
+// the intersection with the node's children.
+// Intersections are returned sorted by distance, closest first.
+func (rc *Raycaster) IntersectObject(inode INode, recursive bool) []Intersect {
+
+	intersects := []Intersect{}
+	rc.intersectObject(inode, &intersects, recursive)
+	sort.Sort(Intersects(intersects))
+	return intersects
+}
+
+// IntersectObjects checks intersections between this raycaster and
+// the specified array of scene nodes. If recursive is true, it also checks
+// the intersection with each nodes' children.
+// Intersections are returned sorted by distance, closest first.
+func (rc *Raycaster) IntersectObjects(inodes []INode, recursive bool) []Intersect {
+
+	intersects := []Intersect{}
+	for _, inode := range inodes {
+		rc.intersectObject(inode, &intersects, recursive)
+	}
+	sort.Sort(Intersects(intersects))
+	return intersects
+}
+
+func (rc *Raycaster) intersectObject(inode INode, intersects *[]Intersect, recursive bool) {
+
+	node := inode.GetNode()
+	if !node.Visible() {
+		return
+	}
+	inode.Raycast(rc, intersects)
+	if recursive {
+		for _, child := range node.Children() {
+			rc.intersectObject(child, intersects, true)
+		}
+	}
+	return
+}
+
+// For sorting Intersects by distance
+type Intersects []Intersect
+
+func (is Intersects) Len() int      { return len(is) }
+func (is Intersects) Swap(i, j int) { is[i], is[j] = is[j], is[i] }
+func (is Intersects) Less(i, j int) bool {
+
+	return is[i].Distance < is[j].Distance
+}

+ 121 - 0
core/timer.go

@@ -0,0 +1,121 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package core
+
+import (
+	"time"
+)
+
+type TimerManager struct {
+	nextID int       // next timer id
+	timers []timeout // list of timeouts
+}
+
+// Type for timer callback functions
+type TimerCallback func(interface{})
+
+// Internal structure for each active timer
+type timeout struct {
+	id     int           // timeout id
+	expire time.Time     // expiration time
+	period time.Duration // period time
+	cb     TimerCallback // callback function
+	arg    interface{}   // callback function argument
+}
+
+// NewTimerManager creates and returns a new timer manager
+func NewTimerManager() *TimerManager {
+
+	tm := new(TimerManager)
+	tm.Initialize()
+	return tm
+}
+
+// Initialize initializes the timer manager.
+// It is normally used when the TimerManager is embedded in another type.
+func (tm *TimerManager) Initialize() {
+
+	tm.nextID = 1
+	tm.timers = make([]timeout, 0)
+}
+
+// SetTimeout sets a timeout with the specified duration and callback
+// The function returns the timeout id which can be used to cancel the timeout
+func (tm *TimerManager) SetTimeout(td time.Duration, arg interface{}, cb TimerCallback) int {
+
+	return tm.setTimer(td, false, arg, cb)
+}
+
+// SetInterval sets a periodic timeout with the specified duration and callback
+// The function returns the timeout id which can be used to cancel the timeout
+func (tm *TimerManager) SetInterval(td time.Duration, arg interface{}, cb TimerCallback) int {
+
+	return tm.setTimer(td, true, arg, cb)
+}
+
+// ClearTimeout clears the timeout specified by the id.
+// Returns true if the timeout is found.
+func (tm *TimerManager) ClearTimeout(id int) bool {
+
+	for pos, t := range tm.timers {
+		if t.id == id {
+			copy(tm.timers[pos:], tm.timers[pos+1:])
+			tm.timers[len(tm.timers)-1] = timeout{}
+			tm.timers = tm.timers[:len(tm.timers)-1]
+			return true
+		}
+	}
+	return false
+}
+
+// ProcessTimers should be called periodically to process the timers
+func (tm *TimerManager) ProcessTimers() {
+
+	now := time.Now()
+	for pos, t := range tm.timers {
+		// If empty entry, ignore
+		if t.id == 0 {
+			continue
+		}
+		// Checks if entry expired
+		if now.After(t.expire) {
+			if t.period == 0 {
+				tm.timers[pos] = timeout{}
+			} else {
+				tm.timers[pos].expire = now.Add(t.period)
+			}
+			t.cb(t.arg)
+		}
+	}
+}
+
+// setTimer sets a new timer with the specified duration
+func (tm *TimerManager) setTimer(td time.Duration, periodic bool, arg interface{}, cb TimerCallback) int {
+
+	// Creates timeout entry
+	t := timeout{
+		id:     tm.nextID,
+		expire: time.Now().Add(td),
+		cb:     cb,
+		arg:    arg,
+		period: 0,
+	}
+	if periodic {
+		t.period = td
+	}
+	tm.nextID++
+
+	// Look for empty entry
+	for pos, ct := range tm.timers {
+		if ct.id == 0 {
+			tm.timers[pos] = t
+			return t.id
+		}
+	}
+
+	// If no empty entry found, add to end of array
+	tm.timers = append(tm.timers, t)
+	return t.id
+}

+ 120 - 0
geometry/box.go

@@ -0,0 +1,120 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package geometry
+
+import (
+	"github.com/g3n/engine/gls"
+	"github.com/g3n/engine/math32"
+)
+
+type Box struct {
+	Geometry
+	Width          float64
+	Height         float64
+	Depth          float64
+	WidthSegments  int
+	HeightSegments int
+	DepthSegments  int
+}
+
+// NewBox creates and returns a pointer to a new Box geometry object.
+// The geometry is defined by its width, height, depth and the number of
+// segments of each dimension (minimum = 1).
+func NewBox(width, height, depth float64, widthSegments, heightSegments, depthSegments int) *Box {
+
+	box := new(Box)
+	box.Geometry.Init()
+
+	box.Width = width
+	box.Height = height
+	box.Depth = depth
+	box.WidthSegments = widthSegments
+	box.HeightSegments = heightSegments
+	box.DepthSegments = depthSegments
+
+	// Create buffers
+	positions := math32.NewArrayF32(0, 16)
+	normals := math32.NewArrayF32(0, 16)
+	uvs := math32.NewArrayF32(0, 16)
+	indices := math32.NewArrayU32(0, 16)
+
+	width_half := width / 2
+	height_half := height / 2
+	depth_half := depth / 2
+
+	// Internal function to build each box plane
+	buildPlane := func(u, v string, udir, vdir int, width, height, depth float64, materialIndex uint) {
+
+		gridX := widthSegments
+		gridY := heightSegments
+		width_half := width / 2
+		height_half := height / 2
+		offset := positions.Len() / 3
+		var w string
+
+		if (u == "x" && v == "y") || (u == "y" && v == "x") {
+			w = "z"
+		} else if (u == "x" && v == "z") || (u == "z" && v == "x") {
+			w = "y"
+			gridY = depthSegments
+		} else if (u == "z" && v == "y") || (u == "y" && v == "z") {
+			w = "x"
+			gridX = depthSegments
+		}
+
+		gridX1 := gridX + 1
+		gridY1 := gridY + 1
+		segment_width := width / float64(gridX)
+		segment_height := height / float64(gridY)
+		var normal math32.Vector3
+		if depth > 0 {
+			normal.SetByName(w, 1)
+		} else {
+			normal.SetByName(w, -1)
+		}
+
+		// Generates the plane vertices, normals and uv coordinates.
+		for iy := 0; iy < gridY1; iy++ {
+			for ix := 0; ix < gridX1; ix++ {
+				var vector math32.Vector3
+				vector.SetByName(u, float32((float64(ix)*segment_width-width_half)*float64(udir)))
+				vector.SetByName(v, float32((float64(iy)*segment_height-height_half)*float64(vdir)))
+				vector.SetByName(w, float32(depth))
+				positions.AppendVector3(&vector)
+				normals.AppendVector3(&normal)
+				uvs.Append(float32(float64(ix)/float64(gridX)), float32(float64(1)-(float64(iy)/float64(gridY))))
+			}
+		}
+
+		gstart := indices.Size()
+		matIndex := materialIndex
+		// Generates the indices for the vertices, normals and uvs
+		for iy := 0; iy < gridY; iy++ {
+			for ix := 0; ix < gridX; ix++ {
+				a := ix + gridX1*iy
+				b := ix + gridX1*(iy+1)
+				c := (ix + 1) + gridX1*(iy+1)
+				d := (ix + 1) + gridX1*iy
+				indices.Append(uint32(a+offset), uint32(b+offset), uint32(d+offset), uint32(b+offset), uint32(c+offset), uint32(d+offset))
+			}
+		}
+		gcount := indices.Size() - gstart
+		box.AddGroup(gstart, gcount, int(matIndex))
+	}
+
+	buildPlane("z", "y", -1, -1, depth, height, width_half, 0)  // px
+	buildPlane("z", "y", 1, -1, depth, height, -width_half, 1)  // nx
+	buildPlane("x", "z", 1, 1, width, depth, height_half, 2)    // py
+	buildPlane("x", "z", 1, -1, width, depth, -height_half, 3)  // ny
+	buildPlane("x", "y", 1, -1, width, height, depth_half, 4)   // pz
+	buildPlane("x", "y", -1, -1, width, height, -depth_half, 5) // nz
+
+	box.SetIndices(indices)
+	box.AddVBO(gls.NewVBO().AddAttrib("VertexPosition", 3).SetBuffer(positions))
+	box.AddVBO(gls.NewVBO().AddAttrib("VertexNormal", 3).SetBuffer(normals))
+	box.AddVBO(gls.NewVBO().AddAttrib("VertexTexcoord", 2).SetBuffer(uvs))
+
+	return box
+}

+ 82 - 0
geometry/circle.go

@@ -0,0 +1,82 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package geometry
+
+import (
+	"github.com/g3n/engine/gls"
+	"github.com/g3n/engine/math32"
+	"math"
+)
+
+type Circle struct {
+	Geometry
+	Radius      float64
+	Segments    int
+	ThetaStart  float64
+	ThetaLength float64
+}
+
+// NewCircle creates and returns a pointer to a new Circle geometry object.
+// The geometry is defined by its radius, the number of segments (triangles), minimum = 3,
+// the start angle in radians for the first segment (thetaStart) and
+// the central angle in radians (thetaLength) of the circular sector.
+func NewCircle(radius float64, segments int, thetaStart, thetaLength float64) *Circle {
+
+	circ := new(Circle)
+	circ.Geometry.Init()
+
+	circ.Radius = radius
+	circ.Segments = segments
+	circ.ThetaStart = thetaStart
+	circ.ThetaLength = thetaLength
+
+	if segments < 3 {
+		segments = 3
+	}
+
+	// Create buffers
+	positions := math32.NewArrayF32(0, 16)
+	normals := math32.NewArrayF32(0, 16)
+	uvs := math32.NewArrayF32(0, 16)
+	indices := math32.NewArrayU32(0, 16)
+
+	// Append circle center position
+	center := math32.NewVector3(0, 0, 0)
+	positions.AppendVector3(center)
+
+	// Append circle center normal
+	var normal math32.Vector3
+	normal.Z = 1
+	normals.AppendVector3(&normal)
+
+	// Append circle center uv coord
+	centerUV := math32.NewVector2(0.5, 0.5)
+	uvs.AppendVector2(centerUV)
+
+	for i := 0; i <= segments; i++ {
+		segment := thetaStart + float64(i)/float64(segments)*thetaLength
+
+		vx := float32(radius * math.Cos(segment))
+		vy := float32(radius * math.Sin(segment))
+
+		// Appends vertex position, normal and uv coordinates
+		positions.Append(vx, vy, 0)
+		normals.AppendVector3(&normal)
+		uvs.Append((vx/float32(radius)+1)/2, (vy/float32(radius)+1)/2)
+	}
+
+	for i := 1; i <= segments; i++ {
+		indices.Append(uint32(i), uint32(i)+1, 0)
+	}
+
+	circ.SetIndices(indices)
+	circ.AddVBO(gls.NewVBO().AddAttrib("VertexPosition", 3).SetBuffer(positions))
+	circ.AddVBO(gls.NewVBO().AddAttrib("VertexNormal", 3).SetBuffer(normals))
+	circ.AddVBO(gls.NewVBO().AddAttrib("VertexTexcoord", 2).SetBuffer(uvs))
+
+	//circ.BoundingSphere = math32.NewSphere(math32.NewVector3(0,0,0), float32(radius))
+
+	return circ
+}

+ 260 - 0
geometry/cylinder.go

@@ -0,0 +1,260 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package geometry
+
+import (
+	"github.com/g3n/engine/gls"
+	"github.com/g3n/engine/math32"
+	"math"
+)
+
+type Cylinder struct {
+	Geometry
+	RadiusTop      float64
+	RadiusBottom   float64
+	Height         float64
+	RadialSegments int
+	HeightSegments int
+	ThetaStart     float64
+	ThetaLength    float64
+	Top            bool
+	Bottom         bool
+}
+
+// NewCylinder creates and returns a pointer to a new Cylinder geometry object.
+func NewCylinder(radiusTop, radiusBottom, height float64,
+	radialSegments, heightSegments int,
+	thetaStart, thetaLength float64, top, bottom bool) *Cylinder {
+
+	c := new(Cylinder)
+	c.Geometry.Init()
+
+	c.RadiusTop = radiusTop
+	c.RadiusBottom = radiusBottom
+	c.Height = height
+	c.RadialSegments = radialSegments
+	c.HeightSegments = heightSegments
+	c.ThetaStart = thetaStart
+	c.ThetaLength = thetaLength
+	c.Top = top
+	c.Bottom = bottom
+
+	heightHalf := height / 2
+	vertices := [][]int{}
+	uvs := [][]math32.Vector2{}
+
+	// Create buffer for vertex positions
+	Positions := math32.NewArrayF32(0, 0)
+
+	for y := 0; y <= heightSegments; y++ {
+		var verticesRow = []int{}
+		var uvsRow = []math32.Vector2{}
+		v := float64(y) / float64(heightSegments)
+		radius := v*(radiusBottom-radiusTop) + radiusTop
+		for x := 0; x <= radialSegments; x++ {
+			u := float64(x) / float64(radialSegments)
+			var vertex math32.Vector3
+			vertex.X = float32(radius * math.Sin(u*thetaLength+thetaStart))
+			vertex.Y = float32(-v*height + heightHalf)
+			vertex.Z = float32(radius * math.Cos(u*thetaLength+thetaStart))
+			Positions.AppendVector3(&vertex)
+			verticesRow = append(verticesRow, Positions.Size()/3-1)
+			uvsRow = append(uvsRow, math32.Vector2{float32(u), 1.0 - float32(v)})
+		}
+		vertices = append(vertices, verticesRow)
+		uvs = append(uvs, uvsRow)
+	}
+
+	tanTheta := (radiusBottom - radiusTop) / height
+	var na, nb math32.Vector3
+
+	// Create preallocated buffers for normals and uvs and buffer for indices
+	npos := Positions.Size()
+	Normals := math32.NewArrayF32(npos, npos)
+	Uvs := math32.NewArrayF32(2*npos/3, 2*npos/3)
+	Indices := math32.NewArrayU32(0, 0)
+
+	for x := 0; x < radialSegments; x++ {
+		if radiusTop != 0 {
+			Positions.GetVector3(3*vertices[0][x], &na)
+			Positions.GetVector3(3*vertices[0][x+1], &nb)
+		} else {
+			Positions.GetVector3(3*vertices[1][x], &na)
+			Positions.GetVector3(3*vertices[1][x+1], &nb)
+		}
+
+		na.SetY(float32(math.Sqrt(float64(na.X*na.X+na.Z*na.Z)) * tanTheta)).Normalize()
+		nb.SetY(float32(math.Sqrt(float64(nb.X*nb.X+nb.Z*nb.Z)) * tanTheta)).Normalize()
+
+		for y := 0; y < heightSegments; y++ {
+			v1 := vertices[y][x]
+			v2 := vertices[y+1][x]
+			v3 := vertices[y+1][x+1]
+			v4 := vertices[y][x+1]
+
+			n1 := na
+			n2 := na
+			n3 := nb
+			n4 := nb
+
+			uv1 := uvs[y][x]
+			uv2 := uvs[y+1][x]
+			uv3 := uvs[y+1][x+1]
+			uv4 := uvs[y][x+1]
+
+			Indices.Append(uint32(v1), uint32(v2), uint32(v4))
+			Normals.SetVector3(3*v1, &n1)
+			Normals.SetVector3(3*v2, &n2)
+			Normals.SetVector3(3*v4, &n4)
+
+			Indices.Append(uint32(v2), uint32(v3), uint32(v4))
+			Normals.SetVector3(3*v2, &n2)
+			Normals.SetVector3(3*v3, &n3)
+			Normals.SetVector3(3*v4, &n4)
+
+			Uvs.SetVector2(2*v1, &uv1)
+			Uvs.SetVector2(2*v2, &uv2)
+			Uvs.SetVector2(2*v3, &uv3)
+			Uvs.SetVector2(2*v4, &uv4)
+		}
+	}
+	// First group is the body of the cylinder
+	// without the caps
+	c.AddGroup(0, Indices.Size(), 0)
+	nextGroup := Indices.Size()
+
+	// Top cap
+	if top && radiusTop > 0 {
+
+		// Array of vertex indices to build used to build the faces.
+		indices := []uint32{}
+		nextidx := Positions.Size() / 3
+
+		// Appends top segments vertices and builds array of its indices
+		var uv1, uv2, uv3 math32.Vector2
+		for x := 0; x < radialSegments; x++ {
+			uv1 = uvs[0][x]
+			uv2 = uvs[0][x+1]
+			uv3 = math32.Vector2{uv2.X, 0}
+			// Appends CENTER with its own UV.
+			Positions.Append(0, float32(heightHalf), 0)
+			Normals.Append(0, 1, 0)
+			Uvs.AppendVector2(&uv3)
+			indices = append(indices, uint32(nextidx))
+			nextidx++
+			// Appends vertex
+			v := math32.Vector3{}
+			vi := vertices[0][x]
+			Positions.GetVector3(3*vi, &v)
+			Positions.AppendVector3(&v)
+			Normals.Append(0, 1, 0)
+			Uvs.AppendVector2(&uv1)
+			indices = append(indices, uint32(nextidx))
+			nextidx++
+		}
+		// Appends copy of first vertex (center)
+		var vertex, normal math32.Vector3
+		var uv math32.Vector2
+		Positions.GetVector3(3*int(indices[0]), &vertex)
+		Normals.GetVector3(3*int(indices[0]), &normal)
+		Uvs.GetVector2(2*int(indices[0]), &uv)
+		Positions.AppendVector3(&vertex)
+		Normals.AppendVector3(&normal)
+		Uvs.AppendVector2(&uv)
+		indices = append(indices, uint32(nextidx))
+		nextidx++
+
+		// Appends copy of second vertex (v1) USING LAST UV2
+		Positions.GetVector3(3*int(indices[1]), &vertex)
+		Normals.GetVector3(3*int(indices[1]), &normal)
+		Positions.AppendVector3(&vertex)
+		Normals.AppendVector3(&normal)
+		Uvs.AppendVector2(&uv2)
+		indices = append(indices, uint32(nextidx))
+		nextidx++
+
+		// Append faces indices
+		for x := 0; x < radialSegments; x++ {
+			pos := 2 * x
+			i1 := indices[pos]
+			i2 := indices[pos+1]
+			i3 := indices[pos+3]
+			Indices.Append(uint32(i1), uint32(i2), uint32(i3))
+		}
+		// Second group is optional top cap of the cylinder
+		c.AddGroup(nextGroup, Indices.Size()-nextGroup, 1)
+		nextGroup = Indices.Size()
+	}
+
+	// Bottom cap
+	if bottom && radiusBottom > 0 {
+
+		// Array of vertex indices to build used to build the faces.
+		indices := []uint32{}
+		nextidx := Positions.Size() / 3
+
+		// Appends top segments vertices and builds array of its indices
+		var uv1, uv2, uv3 math32.Vector2
+		for x := 0; x < radialSegments; x++ {
+			uv1 = uvs[heightSegments][x]
+			uv2 = uvs[heightSegments][x+1]
+			uv3 = math32.Vector2{uv2.X, 1}
+			// Appends CENTER with its own UV.
+			Positions.Append(0, float32(-heightHalf), 0)
+			Normals.Append(0, -1, 0)
+			Uvs.AppendVector2(&uv3)
+			indices = append(indices, uint32(nextidx))
+			nextidx++
+			// Appends vertex
+			v := math32.Vector3{}
+			vi := vertices[heightSegments][x]
+			Positions.GetVector3(3*vi, &v)
+			Positions.AppendVector3(&v)
+			Normals.Append(0, -1, 0)
+			Uvs.AppendVector2(&uv1)
+			indices = append(indices, uint32(nextidx))
+			nextidx++
+		}
+
+		// Appends copy of first vertex (center)
+		var vertex, normal math32.Vector3
+		var uv math32.Vector2
+		Positions.GetVector3(3*int(indices[0]), &vertex)
+		Normals.GetVector3(3*int(indices[0]), &normal)
+		Uvs.GetVector2(2*int(indices[0]), &uv)
+		Positions.AppendVector3(&vertex)
+		Normals.AppendVector3(&normal)
+		Uvs.AppendVector2(&uv)
+		indices = append(indices, uint32(nextidx))
+		nextidx++
+
+		// Appends copy of second vertex (v1) USING LAST UV2
+		Positions.GetVector3(3*int(indices[1]), &vertex)
+		Normals.GetVector3(3*int(indices[1]), &normal)
+		Positions.AppendVector3(&vertex)
+		Normals.AppendVector3(&normal)
+		Uvs.AppendVector2(&uv2)
+		indices = append(indices, uint32(nextidx))
+		nextidx++
+
+		// Appends faces indices
+		for x := 0; x < radialSegments; x++ {
+			pos := 2 * x
+			i1 := indices[pos]
+			i2 := indices[pos+3]
+			i3 := indices[pos+1]
+			Indices.Append(uint32(i1), uint32(i2), uint32(i3))
+		}
+		// Third group is optional bottom cap of the cylinder
+		c.AddGroup(nextGroup, Indices.Size()-nextGroup, 2)
+	}
+
+	c.SetIndices(Indices)
+	c.AddVBO(gls.NewVBO().AddAttrib("VertexPosition", 3).SetBuffer(Positions))
+	c.AddVBO(gls.NewVBO().AddAttrib("VertexNormal", 3).SetBuffer(Normals))
+	c.AddVBO(gls.NewVBO().AddAttrib("VertexTexcoord", 2).SetBuffer(Uvs))
+
+	return c
+}

+ 301 - 0
geometry/geometry.go

@@ -0,0 +1,301 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package geometry allows the generation of geometries...
+package geometry
+
+import (
+	"github.com/g3n/engine/gls"
+	"github.com/g3n/engine/math32"
+)
+
+// Interface for all geometries
+type IGeometry interface {
+	GetGeometry() *Geometry
+	RenderSetup(gs *gls.GLS)
+	Dispose()
+}
+
+type Geometry struct {
+	refcount            int             // Current number of references
+	vbos                []*gls.VBO      // Array of VBOs
+	groups              []Group         // Array geometry groups
+	indices             math32.ArrayU32 // Buffer with indices
+	gs                  *gls.GLS        // Pointer to gl context. Valid after first render setup
+	handleVAO           uint32          // Handle to OpenGL VAO
+	handleIndices       uint32          // Handle to OpenGL buffer for indices
+	updateIndices       bool            // Flag to indicate that indices must be transferred
+	boundingBox         math32.Box3     // Last calculated bounding box
+	boundingBoxValid    bool            // Indicates if last calculated bounding box is valid
+	boundingSphere      math32.Sphere   // Last calculated bounding sphere
+	boundingSphereValid bool            // Indicates if last calculated bounding sphere is valid
+}
+
+// Geometry group object
+type Group struct {
+	Start    int    // Index of first element of the group
+	Count    int    // Number of elements in the group
+	Matindex int    // Material index for this group
+	Matid    string // Material id used when loading external models
+}
+
+func NewGeometry() *Geometry {
+
+	g := new(Geometry)
+	g.Init()
+	return g
+}
+
+// Init initializes the geometry
+func (g *Geometry) Init() {
+
+	g.refcount = 1
+	g.vbos = make([]*gls.VBO, 0)
+	g.groups = make([]Group, 0)
+	g.gs = nil
+	g.handleVAO = 0
+	g.handleIndices = 0
+	g.updateIndices = true
+}
+
+// Incref increments the reference count for this geometry
+// and returns a pointer to the geometry.
+// It should be used when this geometry is shared by another
+// Graphic object.
+func (g *Geometry) Incref() *Geometry {
+
+	g.refcount++
+	return g
+}
+
+// Dispose decrements this geometry reference count and
+// if necessary releases OpenGL resources, C memory
+// and VBOs associated with this geometry.
+func (g *Geometry) Dispose() {
+
+	if g.refcount > 1 {
+		g.refcount--
+		return
+	}
+
+	if g.gs != nil {
+		g.gs.DeleteVertexArrays(g.handleVAO)
+		g.gs.DeleteBuffers(g.handleIndices)
+	}
+	g.Init()
+}
+
+func (g *Geometry) GetGeometry() *Geometry {
+
+	return g
+}
+
+// AddGroup adds a geometry group (for multimaterial)
+func (g *Geometry) AddGroup(start, count, matIndex int) *Group {
+
+	g.groups = append(g.groups, Group{start, count, matIndex, ""})
+	return &g.groups[len(g.groups)-1]
+}
+
+// AddGroupList adds the specified list of groups to this geometry
+func (g *Geometry) AddGroupList(groups []Group) {
+
+	for _, group := range groups {
+		g.groups = append(g.groups, group)
+	}
+}
+
+// GroupCount returns the number of geometry groups (for multimaterial)
+func (g *Geometry) GroupCount() int {
+
+	return len(g.groups)
+}
+
+// GroupAt returns pointer to geometry group at the specified index
+func (g *Geometry) GroupAt(idx int) *Group {
+
+	return &g.groups[idx]
+}
+
+// SetIndices sets the indices array for this geometry
+func (g *Geometry) SetIndices(indices math32.ArrayU32) {
+
+	g.indices = indices
+	g.boundingBoxValid = false
+	g.boundingSphereValid = false
+}
+
+// Indices returns this geometry indices array
+func (g *Geometry) Indices() math32.ArrayU32 {
+
+	return g.indices
+}
+
+// AddVBO adds a Vertex Buffer Object for this geometry
+func (g *Geometry) AddVBO(vbo *gls.VBO) {
+
+	g.vbos = append(g.vbos, vbo)
+}
+
+// VBO returns a pointer to this geometry VBO for the specified attribute.
+// Returns nil if the VBO is not found.
+func (g *Geometry) VBO(attrib string) *gls.VBO {
+
+	for _, vbo := range g.vbos {
+		if vbo.Attrib(attrib) != nil {
+			return vbo
+		}
+	}
+	return nil
+}
+
+// Returns the number of items in the first VBO
+// (The number of items should be same for all VBOs)
+// An item is a complete vertex position (3 floats) for example
+func (g *Geometry) Items() int {
+
+	if len(g.vbos) == 0 {
+		return 0
+	}
+	vbo := g.vbos[0]
+	if vbo.AttribCount() == 0 {
+		return 0
+	}
+	attrib := vbo.AttribAt(0)
+	return vbo.Buffer().Size() / int(attrib.ItemSize)
+}
+
+// BoundingBox computes the bounding box of the geometry if necessary
+// and returns is value
+func (g *Geometry) BoundingBox() math32.Box3 {
+
+	// If valid, returns its value
+	if g.boundingBoxValid {
+		return g.boundingBox
+	}
+
+	// Get buffer with position vertices
+	vbPos := g.VBO("VertexPosition")
+	if vbPos == nil {
+		return g.boundingBox
+	}
+	positions := vbPos.Buffer()
+
+	// Calculates bounding box
+	var vertex math32.Vector3
+	g.boundingBox.Min.Set(0, 0, 0)
+	g.boundingBox.Max.Set(0, 0, 0)
+	for i := 0; i < positions.Size(); i += 3 {
+		positions.GetVector3(i, &vertex)
+		g.boundingBox.ExpandByPoint(&vertex)
+	}
+	g.boundingBoxValid = true
+	return g.boundingBox
+}
+
+// BoundingSphere computes the bounding sphere of this geometry
+// if necessary and returns its value.
+func (g *Geometry) BoundingSphere() math32.Sphere {
+
+	// if valid, returns its value
+	if g.boundingSphereValid {
+		return g.boundingSphere
+	}
+
+	// Get buffer with position vertices
+	vbPos := g.VBO("VertexPosition")
+	if vbPos == nil {
+		return g.boundingSphere
+	}
+	positions := vbPos.Buffer()
+
+	// Get/calculates the bounding box
+	box := g.BoundingBox()
+
+	// Sets the center of the bounding sphere the same as the center of the bounding box.
+	box.Center(&g.boundingSphere.Center)
+	center := g.boundingSphere.Center
+
+	// Find the radius of the bounding sphere
+	maxRadiusSq := float32(0.0)
+	for i := 0; i < positions.Size(); i += 3 {
+		var vertex math32.Vector3
+		positions.GetVector3(i, &vertex)
+		maxRadiusSq = math32.Max(maxRadiusSq, center.DistanceToSquared(&vertex))
+	}
+	radius := math32.Sqrt(maxRadiusSq)
+	if math32.IsNaN(radius) {
+		panic("geometry.BoundingSphere: computed radius is NaN")
+	}
+	g.boundingSphere.Radius = float32(radius)
+	g.boundingSphereValid = true
+	return g.boundingSphere
+}
+
+// ApplyMatrix multiplies each of the geometry position vertices
+// by the specified matrix and apply the correspondent normal
+// transform matrix to the geometry normal vectors.
+// The geometry's bounding box and sphere are recomputed if needed.
+func (g *Geometry) ApplyMatrix(m *math32.Matrix4) {
+
+	// Get positions buffer
+	vboPos := g.VBO("VertexPosition")
+	if vboPos == nil {
+		return
+	}
+	positions := vboPos.Buffer()
+	// Apply matrix to all position vertices
+	for i := 0; i < positions.Size(); i += 3 {
+		var vertex math32.Vector3
+		positions.GetVector3(i, &vertex)
+		vertex.ApplyMatrix4(m)
+		positions.SetVector3(i, &vertex)
+	}
+	vboPos.Update()
+
+	// Get normals buffer
+	vboNormals := g.VBO("VertexNormal")
+	if vboNormals == nil {
+		return
+	}
+	normals := vboNormals.Buffer()
+	// Apply normal matrix to all normal vectors
+	var normalMatrix math32.Matrix3
+	normalMatrix.GetNormalMatrix(m)
+	for i := 0; i < normals.Size(); i += 3 {
+		var vertex math32.Vector3
+		normals.GetVector3(i, &vertex)
+		vertex.ApplyMatrix3(&normalMatrix).Normalize()
+		normals.SetVector3(i, &vertex)
+	}
+	vboNormals.Update()
+}
+
+// RenderSetup is called by the renderer before drawing the geometry
+func (g *Geometry) RenderSetup(gs *gls.GLS) {
+
+	// First time initialization
+	if g.gs == nil {
+		// Generates VAO and binds it
+		g.handleVAO = gs.GenVertexArray()
+		gs.BindVertexArray(g.handleVAO)
+		// Generates VBO for indices
+		g.handleIndices = gs.GenBuffer()
+		// Saves pointer to gl indicating initialization was done.
+		g.gs = gs
+	}
+
+	// Update VBOs
+	gs.BindVertexArray(g.handleVAO)
+	for _, vbo := range g.vbos {
+		vbo.Transfer(gs)
+	}
+
+	// Updates Indices buffer if necessary
+	if g.indices.Size() > 0 && g.updateIndices {
+		gs.BindBuffer(gls.ELEMENT_ARRAY_BUFFER, g.handleIndices)
+		gs.BufferData(gls.ELEMENT_ARRAY_BUFFER, g.indices.Bytes(), g.indices, gls.STATIC_DRAW)
+		g.updateIndices = false
+	}
+}

+ 12 - 0
geometry/logger.go

@@ -0,0 +1,12 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package geometry
+
+import (
+	"github.com/g3n/engine/util/logger"
+)
+
+// Package logger
+var log = logger.New("GEOMETRY", logger.Default)

+ 78 - 0
geometry/plane.go

@@ -0,0 +1,78 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package geometry
+
+import (
+	"github.com/g3n/engine/gls"
+	"github.com/g3n/engine/math32"
+)
+
+type Plane struct {
+	Geometry
+	Width          float32
+	Height         float32
+	WidthSegments  int
+	HeightSegments int
+}
+
+// NewPlane creates and returns a pointer to a Plane Geometry.
+// The plane is defined by its width, height and the number of width and height segments.
+// The minimum number of segments for the width and/or the height is 1.
+// The plane is generated centered in the XY plane with Z=0.
+func NewPlane(width, height float32, widthSegments, heightSegments int) *Plane {
+
+	plane := new(Plane)
+	plane.Geometry.Init()
+
+	plane.Width = width
+	plane.Height = height
+	plane.WidthSegments = widthSegments
+	plane.HeightSegments = heightSegments
+
+	width_half := width / 2
+	height_half := height / 2
+	gridX := widthSegments
+	gridY := heightSegments
+	gridX1 := gridX + 1
+	gridY1 := gridY + 1
+	segment_width := width / float32(gridX)
+	segment_height := height / float32(gridY)
+
+	// Create buffers
+	positions := math32.NewArrayF32(0, 16)
+	normals := math32.NewArrayF32(0, 16)
+	uvs := math32.NewArrayF32(0, 16)
+	indices := math32.NewArrayU32(0, 16)
+
+	// Generate plane vertices, vertices normals and vertices texture mappings.
+	for iy := 0; iy < gridY1; iy++ {
+		y := float32(iy)*segment_height - height_half
+		for ix := 0; ix < gridX1; ix++ {
+			x := float32(ix)*segment_width - width_half
+			positions.Append(float32(x), float32(-y), 0)
+			normals.Append(0, 0, 1)
+			uvs.Append(float32(float64(ix)/float64(gridX)), float32(float64(1)-(float64(iy)/float64(gridY))))
+		}
+	}
+
+	// Generate plane vertices indices for the faces
+	for iy := 0; iy < gridY; iy++ {
+		for ix := 0; ix < gridX; ix++ {
+			a := ix + gridX1*iy
+			b := ix + gridX1*(iy+1)
+			c := (ix + 1) + gridX1*(iy+1)
+			d := (ix + 1) + gridX1*iy
+			indices.Append(uint32(a), uint32(b), uint32(d))
+			indices.Append(uint32(b), uint32(c), uint32(d))
+		}
+	}
+
+	plane.SetIndices(indices)
+	plane.AddVBO(gls.NewVBO().AddAttrib("VertexPosition", 3).SetBuffer(positions))
+	plane.AddVBO(gls.NewVBO().AddAttrib("VertexNormal", 3).SetBuffer(normals))
+	plane.AddVBO(gls.NewVBO().AddAttrib("VertexTexcoord", 2).SetBuffer(uvs))
+
+	return plane
+}

+ 90 - 0
geometry/sphere.go

@@ -0,0 +1,90 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package geometry
+
+import (
+	"github.com/g3n/engine/gls"
+	"github.com/g3n/engine/math32"
+	"math"
+)
+
+type Sphere struct {
+	Geometry
+	Radius         float64
+	WidthSegments  int
+	HeightSegments int
+	PhiStart       float64
+	PhiLength      float64
+	ThetaStart     float64
+	ThetaLength    float64
+}
+
+func NewSphere(radius float64, widthSegments, heightSegments int, phiStart, phiLength, thetaStart, thetaLength float64) *Sphere {
+
+	s := new(Sphere)
+	s.Geometry.Init()
+
+	s.Radius = radius
+	s.WidthSegments = widthSegments
+	s.HeightSegments = heightSegments
+	s.PhiStart = phiStart
+	s.PhiLength = phiLength
+	s.ThetaStart = thetaStart
+
+	thetaEnd := thetaStart + thetaLength
+	vertexCount := (widthSegments + 1) * (heightSegments + 1)
+
+	// Create buffers
+	positions := math32.NewArrayF32(vertexCount*3, vertexCount*3)
+	normals := math32.NewArrayF32(vertexCount*3, vertexCount*3)
+	uvs := math32.NewArrayF32(vertexCount*2, vertexCount*2)
+	indices := math32.NewArrayU32(0, vertexCount)
+
+	index := 0
+	vertices := make([][]uint32, 0)
+	var normal math32.Vector3
+
+	for y := 0; y <= heightSegments; y++ {
+		verticesRow := make([]uint32, 0)
+		v := float64(y) / float64(heightSegments)
+		for x := 0; x <= widthSegments; x++ {
+			u := float64(x) / float64(widthSegments)
+			px := -radius * math.Cos(phiStart+u*phiLength) * math.Sin(thetaStart+v*thetaLength)
+			py := radius * math.Cos(thetaStart+v*thetaLength)
+			pz := radius * math.Sin(phiStart+u*phiLength) * math.Sin(thetaStart+v*thetaLength)
+			normal.Set(float32(px), float32(py), float32(pz)).Normalize()
+
+			positions.Set(index*3, float32(px), float32(py), float32(pz))
+			normals.SetVector3(index*3, &normal)
+			uvs.Set(index*2, float32(u), float32(v))
+			verticesRow = append(verticesRow, uint32(index))
+			index++
+		}
+		vertices = append(vertices, verticesRow)
+	}
+
+	for y := 0; y < heightSegments; y++ {
+		for x := 0; x < widthSegments; x++ {
+			v1 := vertices[y][x+1]
+			v2 := vertices[y][x]
+			v3 := vertices[y+1][x]
+			v4 := vertices[y+1][x+1]
+			if y != 0 || thetaStart > 0 {
+				indices.Append(v1, v2, v4)
+			}
+			if y != heightSegments-1 || thetaEnd < math.Pi {
+				indices.Append(v2, v3, v4)
+			}
+		}
+	}
+
+	s.SetIndices(indices)
+	s.AddVBO(gls.NewVBO().AddAttrib("VertexPosition", 3).SetBuffer(positions))
+	s.AddVBO(gls.NewVBO().AddAttrib("VertexNormal", 3).SetBuffer(normals))
+	s.AddVBO(gls.NewVBO().AddAttrib("VertexTexcoord", 2).SetBuffer(uvs))
+
+	//s.BoundingSphere = math32.NewSphere(math32.NewVector3(0, 0, 0), float32(radius))
+	return s
+}

+ 75 - 0
geometry/torus.go

@@ -0,0 +1,75 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package geometry
+
+import (
+	"github.com/g3n/engine/gls"
+	"github.com/g3n/engine/math32"
+	"math"
+)
+
+type Torus struct {
+	Geometry                // embedded geometry
+	Radius          float64 // Torus radius
+	Tube            float64 // Diameter of the torus tube
+	RadialSegments  int     // Number of radial segments
+	TubularSegments int     // Number of tubular segments
+	Arc             float64 // Central angle
+}
+
+func NewTorus(radius, tube float64, radialSegments, tubularSegments int, arc float64) *Torus {
+
+	t := new(Torus)
+	t.Geometry.Init()
+
+	t.Radius = radius
+	t.Tube = tube
+	t.RadialSegments = radialSegments
+	t.TubularSegments = tubularSegments
+	t.Arc = arc
+
+	// Create buffers
+	positions := math32.NewArrayF32(0, 0)
+	normals := math32.NewArrayF32(0, 0)
+	uvs := math32.NewArrayF32(0, 0)
+	indices := math32.NewArrayU32(0, 0)
+
+	var center math32.Vector3
+	for j := 0; j <= radialSegments; j++ {
+		for i := 0; i <= tubularSegments; i++ {
+			u := float64(i) / float64(tubularSegments) * arc
+			v := float64(j) / float64(radialSegments) * math.Pi * 2
+
+			center.X = float32(radius * math.Cos(u))
+			center.Y = float32(radius * math.Sin(u))
+
+			var vertex math32.Vector3
+			vertex.X = float32((radius + tube*math.Cos(v)) * math.Cos(u))
+			vertex.Y = float32((radius + tube*math.Cos(v)) * math.Sin(u))
+			vertex.Z = float32(tube * math.Sin(v))
+			positions.AppendVector3(&vertex)
+
+			uvs.Append(float32(float64(i)/float64(tubularSegments)), float32(float64(j)/float64(radialSegments)))
+			normals.AppendVector3(vertex.Sub(&center).Normalize())
+		}
+	}
+
+	for j := 1; j <= radialSegments; j++ {
+		for i := 1; i <= tubularSegments; i++ {
+			a := (tubularSegments+1)*j + i - 1
+			b := (tubularSegments+1)*(j-1) + i - 1
+			c := (tubularSegments+1)*(j-1) + i
+			d := (tubularSegments+1)*j + i
+			indices.Append(uint32(a), uint32(b), uint32(d), uint32(b), uint32(c), uint32(d))
+		}
+	}
+
+	t.SetIndices(indices)
+	t.AddVBO(gls.NewVBO().AddAttrib("VertexPosition", 3).SetBuffer(positions))
+	t.AddVBO(gls.NewVBO().AddAttrib("VertexNormal", 3).SetBuffer(normals))
+	t.AddVBO(gls.NewVBO().AddAttrib("VertexTexcoord", 2).SetBuffer(uvs))
+
+	return t
+}

File diff suppressed because it is too large
+ 1927 - 0
gls/consts.go


+ 529 - 0
gls/gls.go

@@ -0,0 +1,529 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Encapsulates the raw OpenGL in a more friendly API and
+// keeps some state to minimize function calling
+//
+package gls
+
+import (
+	"github.com/g3n/engine/util/logger"
+	"github.com/go-gl/gl/v3.3-core/gl"
+)
+
+type GLS struct {
+	// Check openGL API errors flags
+	CheckErrors bool
+	// Current active program
+	Prog *Program
+	// Programs cache
+	Programs map[*Program]bool
+	// Statistics
+	Stats struct {
+		Vaos     int
+		Vbos     int
+		Textures int
+	}
+	// Current view port
+	viewportX      int32
+	viewportY      int32
+	viewportWidth  int32
+	viewportHeight int32
+	// Current clear color
+	clearColorR  float32
+	clearColorG  float32
+	clearColorB  float32
+	clearColorA  float32
+	lineWidth    float32
+	sideView     int
+	depthFunc    uint32
+	depthMask    int
+	capabilities map[int]int
+	// Current blending state
+	blendEquation      uint32
+	blendSrc           uint32
+	blendDst           uint32
+	blendEquationRGB   uint32
+	blendEquationAlpha uint32
+	blendSrcRGB        uint32
+	blendSrcAlpha      uint32
+	blendDstRGB        uint32
+	blendDstAlpha      uint32
+}
+
+// Internal capability enable/disabled state
+const (
+	capUndef    = 0
+	capDisabled = 1
+	capEnabled  = 2
+)
+const (
+	intUndef = -1
+	intFalse = 0
+	intTrue  = 1
+)
+
+// Polygon side view.
+const (
+	FrontSide = iota + 1
+	BackSide
+	DoubleSide
+)
+
+// Package logger
+var log = logger.New("GL", logger.Default)
+
+//
+// New creates and returns a new instance of an GLS object
+// which encapsulates the state of an OpenGL context
+//
+func New() (*GLS, error) {
+
+	gs := new(GLS)
+	gs.Reset()
+
+	// Initialize GL
+	err := gl.Init()
+	if err != nil {
+		return nil, err
+	}
+	gs.CheckErrors = true
+	return gs, nil
+}
+
+//
+// Reset resets the internal state kept of the OpenGL
+//
+func (gs *GLS) Reset() {
+
+	gs.lineWidth = 0.0
+	gs.sideView = intUndef
+	gs.depthFunc = 0
+	gs.depthMask = intUndef
+	gs.capabilities = make(map[int]int)
+	gs.Programs = make(map[*Program]bool)
+
+	//gs.blendEquation = intUndef
+	//gs.blendSrc = intUndef
+	//gs.blendDst = intUndef
+	//gs.blendEquationRGB = 0
+	//gs.blendEquationAlpha = 0
+	//gs.blendSrcRGB = intUndef
+	//gs.blendSrcAlpha = intUndef
+	//gs.blendDstRGB = intUndef
+	//gs.blendDstAlpha = intUndef
+}
+
+func (gs *GLS) SetDefaultState() {
+
+	gl.ClearColor(0, 0, 0, 1)
+	gl.ClearDepth(1)
+	gl.ClearStencil(0)
+
+	gs.Enable(gl.DEPTH_TEST)
+	gs.DepthFunc(gl.LEQUAL)
+	gl.FrontFace(gl.CCW)
+	gl.CullFace(gl.BACK)
+	gs.Enable(gl.CULL_FACE)
+	gs.Enable(gl.BLEND)
+	gs.BlendEquation(gl.FUNC_ADD)
+	gs.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
+	gs.Enable(gl.VERTEX_PROGRAM_POINT_SIZE)
+	gs.Enable(gl.PROGRAM_POINT_SIZE)
+	gs.Enable(gl.MULTISAMPLE)
+
+	//gl.Viewport(gs.viewportX, gs.viewportY, gs.viewportWidth, gs.viewportHeight)
+	//gl.ClearColor(g.ClearColor.R, g.ClearColor.G, g.ClearColor.B, 1.0)
+}
+
+func (gs *GLS) ActiveTexture(texture uint32) {
+
+	gl.ActiveTexture(texture)
+	gs.checkError("ActiveTexture")
+}
+
+func (gs *GLS) BindBuffer(target int, vbo uint32) {
+
+	gl.BindBuffer(uint32(target), vbo)
+	gs.checkError("BindBuffer")
+}
+
+// BindTexture
+func (gs *GLS) BindTexture(target int, tex uint32) {
+
+	gl.BindTexture(uint32(target), tex)
+	gs.checkError("BindTexture")
+}
+
+// Bind Vertex Array Object
+func (gs *GLS) BindVertexArray(vao uint32) {
+
+	gl.BindVertexArray(vao)
+	gs.checkError("BindVertexArray")
+}
+
+// BlendEquation set the equation for blending pixels.
+func (gs *GLS) BlendEquation(mode uint32) {
+
+	if gs.blendEquation == mode {
+		return
+	}
+	gl.BlendEquation(mode)
+	gs.checkError("BlendEquation")
+	gs.blendEquation = mode
+}
+
+func (gs *GLS) BlendEquationSeparate(modeRGB uint32, modeAlpha uint32) {
+
+	if gs.blendEquationRGB == modeRGB && gs.blendEquationAlpha == modeAlpha {
+		return
+	}
+	gl.BlendEquationSeparate(uint32(modeRGB), uint32(modeAlpha))
+	gs.checkError("BlendEquationSeparate")
+	gs.blendEquationRGB = modeRGB
+	gs.blendEquationAlpha = modeAlpha
+}
+
+func (gs *GLS) BlendFunc(sfactor, dfactor uint32) {
+
+	if gs.blendSrc == sfactor && gs.blendDst == dfactor {
+		return
+	}
+	gl.BlendFunc(sfactor, dfactor)
+	gs.checkError("BlendFunc")
+	gs.blendSrc = sfactor
+	gs.blendDst = dfactor
+}
+
+func (gs *GLS) BlendFuncSeparate(srcRGB uint32, dstRGB uint32, srcAlpha uint32, dstAlpha uint32) {
+
+	if gs.blendSrcRGB == srcRGB && gs.blendDstRGB == dstRGB &&
+		gs.blendSrcAlpha == srcAlpha && gs.blendDstAlpha == dstAlpha {
+		return
+	}
+	gl.BlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha)
+	gs.checkError("BlendFuncSeparate")
+	gs.blendSrcRGB = srcRGB
+	gs.blendDstRGB = dstRGB
+	gs.blendSrcAlpha = srcAlpha
+	gs.blendDstAlpha = dstAlpha
+}
+
+func (gs *GLS) BufferData(target uint32, size int, data interface{}, usage uint32) {
+
+	gl.BufferData(target, size, gl.Ptr(data), usage)
+	gs.checkError("BufferData")
+}
+
+func (gs *GLS) ClearColor(r, g, b, a float32) {
+
+	if gs.clearColorR == a && gs.clearColorG == g && gs.clearColorB == b && gs.clearColorA == a {
+		return
+	}
+	gl.ClearColor(r, g, b, a)
+	gs.clearColorR = r
+	gs.clearColorG = g
+	gs.clearColorB = b
+	gs.clearColorA = a
+}
+
+func (gs *GLS) Clear(mask int) {
+
+	gl.Clear(uint32(mask))
+}
+
+func (gs *GLS) DeleteBuffers(vbos ...uint32) {
+
+	gl.DeleteBuffers(int32(len(vbos)), &vbos[0])
+	gs.checkError("DeleteBuffers")
+}
+
+func (gs *GLS) DeleteTextures(tex ...uint32) {
+
+	gl.DeleteTextures(int32(len(tex)), &tex[0])
+	gs.checkError("DeleteTextures")
+	gs.Stats.Textures -= len(tex)
+}
+
+func (gs *GLS) DeleteVertexArrays(vaos ...uint32) {
+
+	gl.DeleteVertexArrays(int32(len(vaos)), &vaos[0])
+	gs.checkError("DeleteVertexArrays")
+}
+
+func (gs *GLS) DepthFunc(mode uint32) {
+
+	if gs.depthFunc == mode {
+		return
+	}
+	gl.DepthFunc(mode)
+	gs.checkError("DepthFunc")
+	gs.depthFunc = mode
+}
+
+func (gs *GLS) DepthMask(flag bool) {
+
+	if gs.depthMask == intTrue && flag {
+		return
+	}
+	if gs.depthMask == intFalse && !flag {
+		return
+	}
+	gl.DepthMask(flag)
+	gs.checkError("DepthMask")
+	if flag {
+		gs.depthMask = intTrue
+	} else {
+		gs.depthMask = intFalse
+	}
+}
+
+func (gs *GLS) DrawArrays(mode uint32, first int32, count int32) {
+
+	gl.DrawArrays(mode, first, count)
+	gs.checkError("DrawArrays")
+}
+
+func (gs *GLS) DrawElements(mode uint32, count int32, itype uint32, start uint32) {
+
+	gl.DrawElements(mode, int32(count), itype, gl.PtrOffset(int(start)))
+	gs.checkError("DrawElements")
+}
+
+func (gs *GLS) Enable(cap int) {
+
+	if gs.capabilities[cap] == capEnabled {
+		return
+	}
+	gl.Enable(uint32(cap))
+	gs.checkError("Enable")
+	gs.capabilities[cap] = capEnabled
+}
+
+func (gs *GLS) EnableVertexAttribArray(index uint32) {
+
+	gl.EnableVertexAttribArray(index)
+	gs.checkError("EnableVertexAttribArray")
+}
+
+func (gs *GLS) Disable(cap int) {
+
+	if gs.capabilities[cap] == capDisabled {
+		return
+	}
+	gl.Disable(uint32(cap))
+	gs.checkError("Disable")
+	gs.capabilities[cap] = capDisabled
+}
+
+func (gs *GLS) FrontFace(mode uint32) {
+
+	gl.FrontFace(mode)
+	gs.checkError("FrontFace")
+}
+
+// GenBuffer generates and returns one Vertex Buffer Object name
+func (gs *GLS) GenBuffer() uint32 {
+
+	var buf uint32
+	gl.GenBuffers(1, &buf)
+	gs.checkError("GenBuffers")
+	gs.Stats.Vbos++
+	return buf
+}
+
+// GenerateMipmap generates mipmaps for the specified texture object.
+func (gs *GLS) GenerateMipmap(target uint32) {
+
+	gl.GenerateMipmap(target)
+	gs.checkError("GenerateMipmap")
+}
+
+// GenTexture generates and returns one Texture Object name
+func (gs *GLS) GenTexture() uint32 {
+
+	var tex uint32
+	gl.GenTextures(1, &tex)
+	gs.checkError("GenTextures")
+	gs.Stats.Textures++
+	return tex
+}
+
+func (gs *GLS) GenVertexArray() uint32 {
+
+	var vao uint32
+	gl.GenVertexArrays(1, &vao)
+	gs.checkError("GenVertexArrays")
+	gs.Stats.Vaos++
+	return vao
+}
+
+func (gs *GLS) GetString(name uint32) string {
+
+	cstr := gl.GetString(name)
+	return gl.GoStr(cstr)
+}
+
+func (gs *GLS) GetViewport() (x, y, width, height int32) {
+
+	return gs.viewportX, gs.viewportY, gs.viewportWidth, gs.viewportHeight
+}
+
+func (gs *GLS) LineWidth(width float32) {
+
+	if gs.lineWidth == width {
+		return
+	}
+	gl.LineWidth(width)
+	gs.checkError("LineWidth")
+	gs.lineWidth = width
+}
+
+func (gs *GLS) SetDepthTest(mode bool) {
+
+	if mode {
+		gs.Enable(gl.DEPTH_TEST)
+	} else {
+		gs.Disable(gl.DEPTH_TEST)
+	}
+}
+
+func (gs *GLS) SetSideView(mode int) {
+
+	if gs.sideView == mode {
+		return
+	}
+	switch mode {
+	// Default: show only the front size
+	case FrontSide:
+		gs.Enable(gl.CULL_FACE)
+		gl.FrontFace(gl.CCW)
+	// Show only the back side
+	case BackSide:
+		gs.Enable(gl.CULL_FACE)
+		gl.FrontFace(gl.CW)
+	// Show both sides
+	case DoubleSide:
+		gs.Disable(gl.CULL_FACE)
+	default:
+		panic("SetSideView() invalid mode")
+	}
+	gs.sideView = mode
+}
+
+func (gs *GLS) TexImage2D(target uint32, level int32, iformat int32, width int32, height int32, border int32, format uint32, itype uint32, data interface{}) {
+
+	gl.TexImage2D(uint32(target), int32(level), int32(iformat), int32(width), int32(height), int32(border), uint32(format), uint32(itype), gl.Ptr(data))
+	gs.checkError("TexImage2D")
+}
+
+func (gs *GLS) TexStorage2D(target int, levels int, iformat int, width, height int) {
+
+	gl.TexStorage2D(uint32(target), int32(levels), uint32(iformat), int32(width), int32(height))
+	gs.checkError("TexStorage2D")
+}
+
+func (gs *GLS) TexParameteri(target uint32, pname uint32, param int32) {
+
+	gl.TexParameteri(target, pname, param)
+	gs.checkError("TexParameteri")
+}
+
+func (gs *GLS) PolygonMode(face, mode int) {
+
+	gl.PolygonMode(uint32(face), uint32(mode))
+	gs.checkError("PolygonMode")
+}
+
+func (gs *GLS) PolygonOffset(factor float32, units float32) {
+
+	gl.PolygonOffset(factor, units)
+	gs.checkError("PolygonOffset")
+}
+
+func (gs *GLS) Uniform1i(location int32, v0 int32) {
+
+	gl.Uniform1i(location, v0)
+	gs.checkError("Uniform1i")
+}
+
+func (gs *GLS) Uniform1f(location int32, v0 float32) {
+
+	gl.Uniform1f(location, v0)
+	gs.checkError("Uniform1f")
+}
+
+func (gs *GLS) Uniform2f(location int32, v0, v1 float32) {
+
+	gl.Uniform2f(location, v0, v1)
+	gs.checkError("Uniform2f")
+}
+
+func (gs *GLS) Uniform3f(location int32, v0, v1, v2 float32) {
+
+	gl.Uniform3f(location, v0, v1, v2)
+	gs.checkError("Uniform3f")
+}
+
+func (gs *GLS) Uniform4f(location int32, v0, v1, v2, v3 float32) {
+
+	gl.Uniform4f(location, v0, v1, v2, v3)
+	gs.checkError("Uniform4f")
+}
+
+func (gs *GLS) UniformMatrix3fv(location int32, count int32, transpose bool, v []float32) {
+
+	gl.UniformMatrix3fv(location, count, transpose, &v[0])
+	gs.checkError("UniformMatrix3fv")
+}
+
+func (gs *GLS) UniformMatrix4fv(location int32, count int32, transpose bool, v []float32) {
+
+	gl.UniformMatrix4fv(location, count, transpose, &v[0])
+	gs.checkError("UniformMatrix4fv")
+}
+
+// Use set this program as the current program.
+func (gs *GLS) UseProgram(prog *Program) {
+
+	if prog.handle == 0 {
+		panic("Invalid program")
+	}
+	gl.UseProgram(prog.handle)
+	gs.checkError("UseProgram")
+	gs.Prog = prog
+
+	// Inserts program in cache if not already there.
+	if !gs.Programs[prog] {
+		gs.Programs[prog] = true
+		log.Warn("New Program activated. Total: %d", len(gs.Programs))
+	}
+}
+
+func (gs *GLS) VertexAttribPointer(index uint32, size int32, xtype uint32, normalized bool, stride int32, offset uint32) {
+
+	gl.VertexAttribPointer(index, size, xtype, normalized, stride, gl.PtrOffset(int(offset)))
+	gs.checkError("VertexAttribPointer")
+}
+
+func (gs *GLS) Viewport(x, y, width, height int32) {
+
+	gl.Viewport(x, y, width, height)
+	gs.checkError("Viewport")
+	gs.viewportX = x
+	gs.viewportY = y
+	gs.viewportWidth = width
+	gs.viewportHeight = height
+}
+
+// checkError checks the error code of the previously called OpenGL function
+func (gls *GLS) checkError(fname string) {
+
+	if gls.CheckErrors {
+		ecode := gl.GetError()
+		if ecode != 0 {
+			log.Fatal("Error:%d calling:%s()", ecode, fname)
+		}
+	}
+}

+ 539 - 0
gls/program.go

@@ -0,0 +1,539 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gls
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"github.com/g3n/engine/math32"
+	"github.com/go-gl/gl/v3.3-core/gl"
+	"io"
+	"strconv"
+	"strings"
+)
+
+// Shader Program Object
+type Program struct {
+	// Shows source code in error messages
+	ShowSource bool
+	gs         *GLS
+	handle     uint32
+	shaders    []shaderInfo
+	uniforms   map[string]int32
+	Specs      interface{}
+}
+
+type shaderInfo struct {
+	stype   uint32
+	source  string
+	defines map[string]interface{}
+	handle  uint32
+}
+
+// Map shader types to names
+var shaderNames = map[uint32]string{
+	gl.VERTEX_SHADER:   "Vertex Shader",
+	gl.FRAGMENT_SHADER: "Fragment Shader",
+}
+
+// NewProgram creates a new empty shader program object.
+// Use this type methods to add shaders and build the final program.
+func (gs *GLS) NewProgram() *Program {
+
+	prog := new(Program)
+	prog.gs = gs
+
+	prog.shaders = make([]shaderInfo, 0)
+	prog.uniforms = make(map[string]int32)
+	prog.ShowSource = true
+	return prog
+}
+
+// AddShaders adds a shader to this program.
+// This must be done before the program is built.
+func (prog *Program) AddShader(stype uint32, source string, defines map[string]interface{}) {
+
+	if prog.handle != 0 {
+		log.Fatal("Program already built")
+	}
+	prog.shaders = append(prog.shaders, shaderInfo{stype, source, defines, 0})
+}
+
+// Build builds the program compiling and linking the previously supplied shaders.
+func (prog *Program) Build() error {
+
+	if prog.handle != 0 {
+		return fmt.Errorf("Program already built")
+	}
+
+	// Checks if shaders were provided
+	if len(prog.shaders) == 0 {
+		return fmt.Errorf("No shaders supplied")
+	}
+
+	// Create program
+	prog.handle = gl.CreateProgram()
+	if prog.handle == 0 {
+		return fmt.Errorf("Error creating program")
+	}
+
+	// Clean unused GL allocated resources
+	defer func() {
+		for _, sinfo := range prog.shaders {
+			if sinfo.handle != 0 {
+				gl.DeleteShader(sinfo.handle)
+				sinfo.handle = 0
+			}
+		}
+	}()
+
+	// Compiles and attach each shader
+	for _, sinfo := range prog.shaders {
+		// Creates string with defines from specified parameters
+		deflines := make([]string, 0)
+		if sinfo.defines != nil {
+			for pname, pval := range sinfo.defines {
+				line := "#define " + pname + " "
+				switch val := pval.(type) {
+				case bool:
+					if val {
+						deflines = append(deflines, line)
+					}
+				case float32:
+					line += strconv.FormatFloat(float64(val), 'f', -1, 32)
+					deflines = append(deflines, line)
+				default:
+					panic("Parameter type not supported")
+				}
+			}
+		}
+		deftext := strings.Join(deflines, "\n")
+		// Compile shader
+		shader, err := CompileShader(sinfo.stype, sinfo.source+deftext)
+		if err != nil {
+			gl.DeleteProgram(prog.handle)
+			prog.handle = 0
+			msg := fmt.Sprintf("Error compiling %s: %s", shaderNames[sinfo.stype], err)
+			if prog.ShowSource {
+				source := FormatSource(sinfo.source + deftext)
+				msg += source
+			}
+			return errors.New(msg)
+		}
+		sinfo.handle = shader
+		gl.AttachShader(prog.handle, shader)
+	}
+
+	// Link program and checks for errors
+	gl.LinkProgram(prog.handle)
+	var status int32
+	gl.GetProgramiv(prog.handle, gl.LINK_STATUS, &status)
+	if status == gl.FALSE {
+		var logLength int32
+		gl.GetProgramiv(prog.handle, gl.INFO_LOG_LENGTH, &logLength)
+		log := strings.Repeat("\x00", int(logLength+1))
+		gl.GetProgramInfoLog(prog.handle, logLength, nil, gl.Str(log))
+		prog.handle = 0
+		return fmt.Errorf("Error linking program: %v", log)
+	}
+
+	return nil
+}
+
+// Handle returns the handle of this program
+func (prog *Program) Handle() uint32 {
+
+	return prog.handle
+}
+
+// GetActiveUniformBlockSize returns the minimum number of bytes
+// to contain the data for the uniform block specified by its index.
+func (prog *Program) GetActiveUniformBlockSize(ubindex uint32) int32 {
+
+	var uboSize int32
+	gl.GetActiveUniformBlockiv(prog.handle, ubindex, gl.UNIFORM_BLOCK_DATA_SIZE, &uboSize)
+	if prog.gs.CheckErrors {
+		ecode := gl.GetError()
+		if ecode != 0 {
+			log.Fatal("GetUniformBlockSize(%v) error: %d", ubindex, ecode)
+		}
+	}
+	return uboSize
+}
+
+// GetActiveUniformsiv returns information about the specified uniforms
+// specified by its indices
+func (prog *Program) GetActiveUniformsiv(indices []uint32, pname uint32) []int32 {
+
+	data := make([]int32, len(indices))
+	gl.GetActiveUniformsiv(prog.handle, int32(len(indices)), &indices[0], pname, &data[0])
+	if prog.gs.CheckErrors {
+		ecode := gl.GetError()
+		if ecode != 0 {
+			log.Fatal("GetActiveUniformsiv() error: %d", ecode)
+		}
+	}
+	return data
+}
+
+// GetAttributeLocation returns the location of the specified attribute
+// in this program. This location is internally cached.
+func (prog *Program) GetAttribLocation(name string) int32 {
+
+	loc := gl.GetAttribLocation(prog.handle, gl.Str(name+"\x00"))
+	prog.gs.checkError("GetAttribLocation")
+	return loc
+}
+
+// GetUniformBlockIndex returns the index of the named uniform block.
+// If the supplied name is not valid, the function returns gl.INVALID_INDEX
+func (prog *Program) GetUniformBlockIndex(name string) uint32 {
+
+	index := gl.GetUniformBlockIndex(prog.handle, gl.Str(name+"\x00"))
+	if prog.gs.CheckErrors {
+		ecode := gl.GetError()
+		if ecode != 0 {
+			log.Fatal("GetUniformBlockIndex(%s) error", name)
+		}
+	}
+	return index
+}
+
+// GetUniformIndices returns the indices for each specified named
+// uniform. If an specified name is not valid the corresponding
+// index value will be gl.INVALID_INDEX
+func (prog *Program) GetUniformIndices(names []string) []uint32 {
+
+	// Add C terminators to uniform names
+	for _, s := range names {
+		s += "\x00"
+	}
+	unames, freefunc := gl.Strs(names...)
+
+	indices := make([]uint32, len(names))
+	gl.GetUniformIndices(prog.handle, int32(len(names)), unames, &indices[0])
+	if prog.gs.CheckErrors {
+		ecode := gl.GetError()
+		if ecode != 0 {
+			log.Fatal("GetUniformIndices() error: %d", ecode)
+		}
+	}
+	freefunc()
+	return indices
+}
+
+// GetUniformLocation returns the location of the specified uniform in this program.
+// This location is internally cached.
+func (prog *Program) GetUniformLocation(name string) int32 {
+
+	// Try to get from the cache
+	loc, ok := prog.uniforms[name]
+	if ok {
+		return loc
+	}
+	// Get location from GL
+	loc = gl.GetUniformLocation(prog.handle, gl.Str(name+"\x00"))
+	if prog.gs.CheckErrors {
+		ecode := gl.GetError()
+		if ecode != 0 {
+			log.Fatal("GetUniformLocation(%s) error: %d", name, ecode)
+		}
+	}
+	// Cache result
+	prog.uniforms[name] = loc
+	if loc < 0 {
+		log.Warn("GetUniformLocation(%s) NOT FOUND", name)
+	}
+	return loc
+}
+
+// SetUniformInt sets this program uniform variable specified by
+// its location to the the value of the specified int
+func (prog *Program) SetUniformInt(loc int32, v int) {
+
+	gl.Uniform1i(loc, int32(v))
+	if prog.gs.CheckErrors {
+		ecode := gl.GetError()
+		if ecode != 0 {
+			log.Fatal("SetUniformInt() error: %d", ecode)
+		}
+	}
+}
+
+// SetUniformFloat sets this program uniform variable specified by
+// its location to the the value of the specified float
+func (prog *Program) SetUniformFloat(loc int32, v float32) {
+
+	gl.Uniform1f(loc, v)
+	if prog.gs.CheckErrors {
+		ecode := gl.GetError()
+		if ecode != 0 {
+			log.Fatal("SetUniformFloat() error: %d", ecode)
+		}
+	}
+}
+
+// SetUniformVector2 sets this program uniform variable specified by
+// its location to the the value of the specified Vector2
+func (prog *Program) SetUniformVector2(loc int32, v *math32.Vector2) {
+
+	gl.Uniform2f(loc, v.X, v.Y)
+	if prog.gs.CheckErrors {
+		ecode := gl.GetError()
+		if ecode != 0 {
+			log.Fatal("SetUniformVector2() error: %d", ecode)
+		}
+	}
+}
+
+// SetUniformVector3 sets this program uniform variable specified by
+// its location to the the value of the specified Vector3
+func (prog *Program) SetUniformVector3(loc int32, v *math32.Vector3) {
+
+	gl.Uniform3f(loc, v.X, v.Y, v.Z)
+	if prog.gs.CheckErrors {
+		ecode := gl.GetError()
+		if ecode != 0 {
+			log.Fatal("SetUniformVector3() error: %d", ecode)
+		}
+	}
+}
+
+// SetUniformVector4 sets this program uniform variable specified by
+// its location to the the value of the specified Vector4
+func (prog *Program) SetUniformVector4(loc int32, v *math32.Vector4) {
+
+	gl.Uniform4f(loc, v.X, v.Y, v.Z, v.W)
+	if prog.gs.CheckErrors {
+		ecode := gl.GetError()
+		if ecode != 0 {
+			log.Fatal("SetUniformVector4() error: %d", ecode)
+		}
+	}
+}
+
+// SetUniformMatrix3 sets this program uniform variable specified by
+// its location with the values from the specified Matrix3.
+func (prog *Program) SetUniformMatrix3(loc int32, m *math32.Matrix3) {
+
+	gl.UniformMatrix3fv(loc, 1, false, &m[0])
+	if prog.gs.CheckErrors {
+		ecode := gl.GetError()
+		if ecode != 0 {
+			log.Fatal("SetUniformMatrix3() error: %d", ecode)
+		}
+	}
+}
+
+// SetUniformMatrix4 sets this program uniform variable specified by
+// its location with the values from the specified Matrix4.
+func (prog *Program) SetUniformMatrix4(loc int32, m *math32.Matrix4) {
+
+	gl.UniformMatrix4fv(loc, 1, false, &m[0])
+	if prog.gs.CheckErrors {
+		ecode := gl.GetError()
+		if ecode != 0 {
+			log.Fatal("SetUniformMatrix4() error: %d", ecode)
+		}
+	}
+}
+
+// SetUniformIntByName sets this program uniform variable specified by
+// its name to the value of the specified int.
+// The specified name location is cached internally.
+func (prog *Program) SetUniformIntByName(name string, v int) {
+
+	gl.Uniform1i(prog.GetUniformLocation(name), int32(v))
+	if prog.gs.CheckErrors {
+		ecode := gl.GetError()
+		if ecode != 0 {
+			log.Fatal("GetUniformIntByName(%s) error: %d", name, ecode)
+		}
+	}
+}
+
+// SetUniformFloatByName sets this program uniform variable specified by
+// its name to the value of the specified float32.
+// The specified name location is cached internally.
+func (prog *Program) SetUniformFloatByName(name string, v float32) {
+
+	gl.Uniform1f(prog.GetUniformLocation(name), v)
+	if prog.gs.CheckErrors {
+		ecode := gl.GetError()
+		if ecode != 0 {
+			log.Fatal("SetUniformFloatByName(%s) error: %d", name, ecode)
+		}
+	}
+}
+
+// SetUniformVector2ByName sets this program uniform variable specified by
+// its name to the values from the specified Vector2.
+// The specified name location is cached internally.
+func (prog *Program) SetUniformVector2ByName(name string, v *math32.Vector2) {
+
+	gl.Uniform2f(prog.GetUniformLocation(name), v.X, v.Y)
+	if prog.gs.CheckErrors {
+		ecode := gl.GetError()
+		if ecode != 0 {
+			log.Fatal("SetUniformVector2ByName(%s) error: %d", name, ecode)
+		}
+	}
+}
+
+// SetUniformVector3ByName sets this program uniform variable specified by
+// its name to the values from the specified Vector3.
+// The specified name location is cached internally.
+func (prog *Program) SetUniformVector3ByName(name string, v *math32.Vector3) {
+
+	gl.Uniform3f(prog.GetUniformLocation(name), v.X, v.Y, v.Z)
+	if prog.gs.CheckErrors {
+		ecode := gl.GetError()
+		if ecode != 0 {
+			log.Fatal("SetUniformVector3ByName(%s) error: %d", name, ecode)
+		}
+	}
+}
+
+// SetUniformVector4ByName sets this program uniform variable specified by
+// its name to the values from the specified Vector4.
+// The specified name location is cached internally.
+func (prog *Program) SetUniformVector4ByName(name string, v *math32.Vector4) {
+
+	gl.Uniform4f(prog.GetUniformLocation(name), v.X, v.Y, v.Z, v.W)
+	if prog.gs.CheckErrors {
+		ecode := gl.GetError()
+		if ecode != 0 {
+			log.Fatal("SetUniformVector4ByName(%s) error: %d", name, ecode)
+		}
+	}
+}
+
+// SetUniformMatrix3ByName sets this program uniform variable specified by
+// its name with the values from the specified Matrix3.
+// The specified name location is cached internally.
+func (prog *Program) SetUniformMatrix3ByName(name string, m *math32.Matrix3) {
+
+	gl.UniformMatrix3fv(prog.GetUniformLocation(name), 1, false, &m[0])
+	if prog.gs.CheckErrors {
+		ecode := gl.GetError()
+		if ecode != 0 {
+			log.Fatal("SetUniformMatrix3ByName(%s) error: %d", name, ecode)
+		}
+	}
+}
+
+// SetUniformMatrix4ByName sets this program uniform variable specified by
+// its name with the values from the specified Matrix4.
+// The location of the name is cached internally.
+func (prog *Program) SetUniformMatrix4ByName(name string, m *math32.Matrix4) {
+
+	gl.UniformMatrix4fv(prog.GetUniformLocation(name), 1, false, &m[0])
+	if prog.gs.CheckErrors {
+		ecode := gl.GetError()
+		if ecode != 0 {
+			log.Fatal("SetUniformMatrix4ByName(%s) error: %d", name, ecode)
+		}
+	}
+}
+
+// SetUniformColorByName set this program uniform variable specified by
+// its name to the values from the specified Color
+// The specified name location is cached internally.
+func (prog *Program) SetUniformColorByName(name string, c *math32.Color) {
+
+	gl.Uniform3f(prog.GetUniformLocation(name), c.R, c.G, c.B)
+	if prog.gs.CheckErrors {
+		ecode := gl.GetError()
+		if ecode != 0 {
+			log.Fatal("SetUniformColorByName(%s) error: %d", name, ecode)
+		}
+	}
+}
+
+// SetUniformColor4ByName set this program uniform variable specified by
+// its name to the values from the specified Color4
+// The specified name location is cached internally.
+func (prog *Program) SetUniformColor4ByName(name string, c *math32.Color4) {
+
+	gl.Uniform4f(prog.GetUniformLocation(name), c.R, c.G, c.B, c.A)
+	if prog.gs.CheckErrors {
+		ecode := gl.GetError()
+		if ecode != 0 {
+			log.Fatal("SetUniformColor4ByName(%s) error: %d", name, ecode)
+		}
+	}
+}
+
+// CompileShader creates and compiles a shader of the specified type and with
+// the specified source code and returns a non-zero value by which
+// it can be referenced.
+func CompileShader(stype uint32, source string) (uint32, error) {
+
+	shader := gl.CreateShader(stype)
+	if shader == 0 {
+		return 0, fmt.Errorf("Error creating shader")
+	}
+
+	// Allocates C string to store the source
+	csource, freeSource := gl.Strs(source + "\x00")
+	defer freeSource()
+
+	// Set shader source and compile it
+	gl.ShaderSource(shader, 1, csource, nil)
+	gl.CompileShader(shader)
+
+	// Get the shader compiler log
+	var logLength int32
+	gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, &logLength)
+	slog := strings.Repeat("\x00", int(logLength+1))
+	gl.GetShaderInfoLog(shader, logLength, nil, gl.Str(slog))
+
+	// Get the shader compile status
+	var status int32
+	gl.GetShaderiv(shader, gl.COMPILE_STATUS, &status)
+	if status == gl.FALSE {
+		return shader, fmt.Errorf("%s", slog)
+	}
+
+	// Even if the shader compiled OK, if the log has data,
+	// return error to see warnings
+	if len(slog) > 2 {
+		return shader, fmt.Errorf("%s", slog)
+	}
+	return shader, nil
+}
+
+// FormatSource returns the supplied program source code with
+// line numbers prepended.
+func FormatSource(source string) string {
+
+	// Reads all lines from the source string
+	lines := make([]string, 0)
+	buf := bytes.NewBuffer([]byte(source))
+	for {
+		line, err := buf.ReadBytes('\n')
+		if err != nil {
+			if err == io.EOF {
+				break
+			}
+			panic(err)
+		}
+		lines = append(lines, string(line[:len(line)-1]))
+	}
+	// Adds a final line terminator
+	lines = append(lines, "\n")
+
+	// Prepends the line number for each line
+	ndigits := len(strconv.Itoa(len(lines)))
+	format := "%0" + strconv.Itoa(ndigits) + "d:%s"
+	formatted := make([]string, 0)
+	for pos, l := range lines {
+		fline := fmt.Sprintf(format, pos+1, l)
+		formatted = append(formatted, fline)
+	}
+
+	return strings.Join(formatted, "\n")
+}

+ 401 - 0
gls/uniform.go

@@ -0,0 +1,401 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gls
+
+import (
+	"fmt"
+	"github.com/g3n/engine/math32"
+)
+
+//
+// Type Uniform is the type for all uniforms
+//
+type Uniform struct {
+	name    string // original name
+	nameidx string // cached indexed name
+	idx     int    // index value of indexed name
+}
+
+// Location returns the current location of the uniform
+// for the current active program
+func (uni *Uniform) Location(gs *GLS) int32 {
+
+	loc := gs.Prog.GetUniformLocation(uni.name)
+	return loc
+}
+
+// Location returns the current location of the uniform
+// for the current active program and index
+func (uni *Uniform) LocationIdx(gs *GLS, idx int) int32 {
+
+	// Rebuilds uniform indexed name if necessary
+	if uni.nameidx == "" || uni.idx != idx {
+		uni.nameidx = fmt.Sprintf("%s[%d]", uni.name, idx)
+		uni.idx = idx
+	}
+	//log.Debug("Location(%s, %d)", uni.name, idx)
+	loc := gs.Prog.GetUniformLocation(uni.nameidx)
+	return loc
+}
+
+//
+// Type Uniform1i is a Uniform containing one int value
+//
+type Uniform1i struct {
+	Uniform
+	v0 int32
+}
+
+func NewUniform1i(name string) *Uniform1i {
+
+	uni := new(Uniform1i)
+	uni.name = name
+	return uni
+}
+
+func (uni *Uniform1i) Init(name string) {
+
+	uni.name = name
+}
+
+func (uni *Uniform1i) Set(v int32) {
+
+	uni.v0 = v
+}
+
+func (uni *Uniform1i) Get() int32 {
+
+	return uni.v0
+}
+
+func (uni *Uniform1i) Transfer(gs *GLS) {
+
+	gs.Uniform1i(uni.Location(gs), uni.v0)
+}
+
+func (uni *Uniform1i) TransferIdx(gs *GLS, idx int) {
+
+	gs.Uniform1i(uni.LocationIdx(gs, idx), uni.v0)
+}
+
+//
+// Type Uniform1f is a Uniform containing one float32 value
+//
+type Uniform1f struct {
+	Uniform
+	v0 float32
+}
+
+func NewUniform1f(name string) *Uniform1f {
+
+	uni := new(Uniform1f)
+	uni.Init(name)
+	return uni
+}
+
+func (uni *Uniform1f) Init(name string) {
+
+	uni.name = name
+}
+
+func (uni *Uniform1f) Set(v float32) {
+
+	uni.v0 = v
+}
+
+func (uni *Uniform1f) Get() float32 {
+
+	return uni.v0
+}
+
+func (uni *Uniform1f) Transfer(gs *GLS) {
+
+	gs.Uniform1f(uni.Location(gs), uni.v0)
+}
+
+func (uni *Uniform1f) TransferIdx(gs *GLS, idx int) {
+
+	gs.Uniform1f(uni.LocationIdx(gs, idx), uni.v0)
+}
+
+//
+// Type Uniform2f is a Uniform containing two float32 values
+//
+type Uniform2f struct {
+	Uniform
+	v0 float32
+	v1 float32
+}
+
+func NewUniform2f(name string) *Uniform2f {
+
+	uni := new(Uniform2f)
+	uni.Init(name)
+	return uni
+}
+
+func (uni *Uniform2f) Init(name string) {
+
+	uni.name = name
+}
+
+func (uni *Uniform2f) Set(v0, v1 float32) {
+
+	uni.v0 = v0
+	uni.v1 = v1
+}
+
+func (uni *Uniform2f) Get() (float32, float32) {
+
+	return uni.v0, uni.v1
+}
+
+func (uni *Uniform2f) SetVector2(v *math32.Vector2) {
+
+	uni.v0 = v.X
+	uni.v1 = v.Y
+}
+
+func (uni *Uniform2f) GetVector2() math32.Vector2 {
+
+	return math32.Vector2{uni.v0, uni.v1}
+}
+
+func (uni *Uniform2f) Transfer(gs *GLS) {
+
+	gs.Uniform2f(uni.Location(gs), uni.v0, uni.v1)
+}
+
+func (uni *Uniform2f) TransferIdx(gs *GLS, idx int) {
+
+	gs.Uniform2f(uni.LocationIdx(gs, idx), uni.v0, uni.v1)
+}
+
+//
+// Type Uniform3f is a Uniform containing three float32 values
+//
+type Uniform3f struct {
+	Uniform
+	v0 float32
+	v1 float32
+	v2 float32
+}
+
+func NewUniform3f(name string) *Uniform3f {
+
+	uni := new(Uniform3f)
+	uni.Init(name)
+	return uni
+}
+
+func (uni *Uniform3f) Init(name string) {
+
+	uni.name = name
+}
+
+func (uni *Uniform3f) Set(v0, v1, v2 float32) {
+
+	uni.v0 = v0
+	uni.v1 = v1
+	uni.v2 = v2
+}
+
+func (uni *Uniform3f) Get() (float32, float32, float32) {
+
+	return uni.v0, uni.v1, uni.v2
+}
+
+func (uni *Uniform3f) SetVector3(v *math32.Vector3) {
+
+	uni.v0 = v.X
+	uni.v1 = v.Y
+	uni.v2 = v.Z
+}
+
+func (uni *Uniform3f) GetVector3() math32.Vector3 {
+
+	return math32.Vector3{uni.v0, uni.v1, uni.v2}
+}
+
+func (uni *Uniform3f) SetColor(color *math32.Color) {
+
+	uni.v0 = color.R
+	uni.v1 = color.G
+	uni.v2 = color.B
+}
+
+func (uni *Uniform3f) GetColor() math32.Color {
+
+	return math32.Color{uni.v0, uni.v1, uni.v2}
+}
+
+func (uni *Uniform3f) Transfer(gl *GLS) {
+
+	loc := uni.Location(gl)
+	gl.Uniform3f(loc, uni.v0, uni.v1, uni.v2)
+	//log.Debug("Uniform3f: %s (%v) -> %v,%v,%v", uni.name, loc, uni.v0, uni.v1, uni.v2)
+}
+
+func (uni *Uniform3f) TransferIdx(gl *GLS, idx int) {
+
+	loc := uni.LocationIdx(gl, idx)
+	gl.Uniform3f(loc, uni.v0, uni.v1, uni.v2)
+	//log.Debug("Uniform3f: %s -> %v,%v,%v", uni.nameidx, uni.v0, uni.v1, uni.v2)
+}
+
+//
+// Type Uniform4f is a Uniform containing four float32 values
+//
+type Uniform4f struct {
+	Uniform
+	v0 float32
+	v1 float32
+	v2 float32
+	v3 float32
+}
+
+func NewUniform4f(name string) *Uniform4f {
+
+	uni := new(Uniform4f)
+	uni.Init(name)
+	return uni
+}
+
+func (uni *Uniform4f) Init(name string) {
+
+	uni.name = name
+}
+
+func (uni *Uniform4f) Set(v0, v1, v2, v3 float32) {
+
+	uni.v0 = v0
+	uni.v1 = v1
+	uni.v2 = v2
+	uni.v3 = v3
+}
+
+func (uni *Uniform4f) Get() (float32, float32, float32, float32) {
+
+	return uni.v0, uni.v1, uni.v2, uni.v3
+}
+
+func (uni *Uniform4f) SetVector4(v *math32.Vector4) {
+
+	uni.v0 = v.X
+	uni.v1 = v.Y
+	uni.v2 = v.Z
+	uni.v3 = v.W
+}
+
+func (uni *Uniform4f) GetVector4() math32.Vector4 {
+
+	return math32.Vector4{uni.v0, uni.v1, uni.v2, uni.v3}
+}
+
+func (uni *Uniform4f) SetColor4(c *math32.Color4) {
+
+	uni.v0 = c.R
+	uni.v1 = c.G
+	uni.v2 = c.B
+	uni.v3 = c.A
+}
+
+func (uni *Uniform4f) GetColor4() math32.Color4 {
+
+	return math32.Color4{uni.v0, uni.v1, uni.v2, uni.v3}
+}
+
+func (uni *Uniform4f) Transfer(gl *GLS) {
+
+	//log.Debug("Uniform4f.Transfer: %s %d", uni.name, uni.Location(gl))
+	gl.Uniform4f(uni.Location(gl), uni.v0, uni.v1, uni.v2, uni.v3)
+}
+
+func (uni *Uniform4f) TransferIdx(gl *GLS, idx int) {
+
+	gl.Uniform4f(uni.LocationIdx(gl, idx), uni.v0, uni.v1, uni.v2, uni.v3)
+}
+
+//
+// Type UniformMatrix3f is a Uniform containing nine float32 values
+// organized as 3x3 matrix
+//
+type UniformMatrix3f struct {
+	Uniform
+	v [9]float32
+}
+
+func NewUniformMatrix3f(name string) *UniformMatrix3f {
+
+	uni := new(UniformMatrix3f)
+	uni.Init(name)
+	return uni
+}
+
+func (uni *UniformMatrix3f) Init(name string) {
+
+	uni.name = name
+}
+
+func (uni *UniformMatrix3f) SetMatrix3(m *math32.Matrix3) {
+
+	uni.v = *m
+}
+
+func (uni *UniformMatrix3f) GetMatrix3() math32.Matrix3 {
+
+	return uni.v
+}
+
+func (uni *UniformMatrix3f) Transfer(gl *GLS) {
+
+	gl.UniformMatrix3fv(uni.Location(gl), 1, false, uni.v[0:9])
+}
+
+func (uni *UniformMatrix3f) TransferIdx(gl *GLS, idx int) {
+
+	gl.UniformMatrix3fv(uni.LocationIdx(gl, idx), 1, false, uni.v[0:9])
+}
+
+//
+// Type UniformMatrix4f is a Uniform containing sixteen float32 values
+// organized as 4x4 matrix
+//
+type UniformMatrix4f struct {
+	Uniform
+	v [16]float32
+}
+
+func NewUniformMatrix4f(name string) *UniformMatrix4f {
+
+	uni := new(UniformMatrix4f)
+	uni.Init(name)
+	return uni
+}
+
+func (uni *UniformMatrix4f) Init(name string) {
+
+	uni.name = name
+}
+
+func (uni *UniformMatrix4f) SetMatrix4(m *math32.Matrix4) {
+
+	uni.v = *m
+}
+
+func (uni *UniformMatrix4f) GetMatrix4() math32.Matrix4 {
+
+	return uni.v
+}
+
+func (uni *UniformMatrix4f) Transfer(gl *GLS) {
+
+	gl.UniformMatrix4fv(uni.Location(gl), 1, false, uni.v[0:16])
+}
+
+func (uni *UniformMatrix4f) TransferIdx(gl *GLS, idx int) {
+
+	gl.UniformMatrix4fv(uni.LocationIdx(gl, idx), 1, false, uni.v[0:16])
+}

+ 148 - 0
gls/vbo.go

@@ -0,0 +1,148 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gls
+
+import (
+	"github.com/g3n/engine/math32"
+	"unsafe"
+)
+
+// VBO abstracts an OpenGL Vertex Buffer Object
+type VBO struct {
+	gs      *GLS
+	handle  uint32          // OpenGL handle for this VBO
+	usage   uint32          // Expected usage patter of the buffer
+	update  bool            // Update flag
+	buffer  math32.ArrayF32 // Data buffer
+	attribs []VBOattrib     // List of attributes
+}
+
+// VBOattrib describes one attribute of an OpenGL Vertex Buffer Object
+type VBOattrib struct {
+	Name     string // Name of of the attribute
+	ItemSize int32  // Number of elements for each item
+}
+
+// NewVBO creates and returns a pointer to a new OpenGL Vertex Buffer Object
+func NewVBO() *VBO {
+
+	vbo := new(VBO)
+	vbo.init()
+	return vbo
+}
+
+// init initializes this VBO
+func (vbo *VBO) init() {
+
+	vbo.gs = nil
+	vbo.handle = 0
+	vbo.usage = STATIC_DRAW
+	vbo.update = true
+	vbo.attribs = make([]VBOattrib, 0)
+}
+
+// AddAttrib adds a new attribute to this VBO
+func (vbo *VBO) AddAttrib(name string, itemSize int32) *VBO {
+
+	vbo.attribs = append(vbo.attribs, VBOattrib{
+		Name:     name,
+		ItemSize: itemSize,
+	})
+	return vbo
+}
+
+// Attrib finds and returns pointer the attribute with the specified name
+// or nil if not found
+func (vbo *VBO) Attrib(name string) *VBOattrib {
+
+	for _, attr := range vbo.attribs {
+		if attr.Name == name {
+			return &attr
+		}
+	}
+	return nil
+}
+
+// AttribAt returns pointer to the VBO attribute at the specified index
+func (vbo *VBO) AttribAt(idx int) *VBOattrib {
+
+	return &vbo.attribs[idx]
+}
+
+// AttribCount returns the current number of attributes for this VBO
+func (vbo *VBO) AttribCount() int {
+
+	return len(vbo.attribs)
+}
+
+// Sets the VBO buffer
+func (vbo *VBO) SetBuffer(buffer math32.ArrayF32) *VBO {
+
+	vbo.buffer = buffer
+	return vbo
+}
+
+// Sets the expected usage pattern of the buffer.
+// The default value is GL_STATIC_DRAW.
+func (vbo *VBO) SetUsage(usage uint32) {
+
+	vbo.usage = usage
+}
+
+// Buffer returns pointer to the VBO buffer
+func (vbo *VBO) Buffer() *math32.ArrayF32 {
+
+	return &vbo.buffer
+}
+
+// Updates sets the update flag to force the VBO update
+func (vbo *VBO) Update() {
+
+	vbo.update = true
+}
+
+// Transfer is called internally and transfer the data in the VBO buffer to OpenGL if necessary
+func (vbo *VBO) Transfer(gs *GLS) {
+
+	// If the VBO buffer is empty, ignore
+	if vbo.buffer.Bytes() == 0 {
+		return
+	}
+
+	// First time initialization
+	if vbo.gs == nil {
+		vbo.handle = gs.GenBuffer()
+		gs.BindBuffer(ARRAY_BUFFER, vbo.handle)
+		// Calculates stride
+		elsize := int32(unsafe.Sizeof(float32(0)))
+		var stride int32 = 0
+		for _, attrib := range vbo.attribs {
+			stride += elsize * attrib.ItemSize
+		}
+		// For each attribute
+		var items uint32 = 0
+		var offset uint32 = 0
+		for _, attrib := range vbo.attribs {
+			// Get attribute location in the current program
+			loc := gs.Prog.GetAttribLocation(attrib.Name)
+			if loc < 0 {
+				continue
+			}
+			// Enables attribute and sets its stride and offset in the buffer
+			gs.EnableVertexAttribArray(uint32(loc))
+			gs.VertexAttribPointer(uint32(loc), attrib.ItemSize, FLOAT, false, stride, offset)
+			items += uint32(attrib.ItemSize)
+			offset = uint32(elsize) * items
+		}
+		vbo.gs = gs // this indicates that the vbo was initialized
+	}
+	if !vbo.update {
+		return
+	}
+	// Transfer the VBO data to OpenGL
+	gs.BindBuffer(ARRAY_BUFFER, vbo.handle)
+	gs.BufferData(ARRAY_BUFFER, vbo.buffer.Bytes(), &vbo.buffer[0], vbo.usage)
+	vbo.update = false
+}

+ 47 - 0
graphic/axis_helper.go

@@ -0,0 +1,47 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package graphic
+
+import (
+	"github.com/g3n/engine/geometry"
+	"github.com/g3n/engine/gls"
+	"github.com/g3n/engine/material"
+	"github.com/g3n/engine/math32"
+)
+
+type AxisHelper struct {
+	Lines
+}
+
+func NewAxisHelper(size float32) *AxisHelper {
+
+	axis := new(AxisHelper)
+
+	// Creates geometry with three orthogonal lines
+	// starting at the origin
+	geom := geometry.NewGeometry()
+	positions := math32.NewArrayF32(0, 18)
+	positions.Append(
+		0, 0, 0, size, 0, 0,
+		0, 0, 0, 0, size, 0,
+		0, 0, 0, 0, 0, size,
+	)
+	colors := math32.NewArrayF32(0, 18)
+	colors.Append(
+		1, 0, 0, 1, 0.6, 0,
+		0, 1, 0, 0.6, 1, 0,
+		0, 0, 1, 0, 0.6, 1,
+	)
+	geom.AddVBO(gls.NewVBO().AddAttrib("VertexPosition", 3).SetBuffer(positions))
+	geom.AddVBO(gls.NewVBO().AddAttrib("VertexColor", 3).SetBuffer(colors))
+
+	// Creates line material
+	mat := material.NewBasic()
+	mat.SetLineWidth(2.0)
+
+	// Initialize lines with the specified geometry and material
+	axis.Lines.Init(geom, mat)
+	return axis
+}

+ 177 - 0
graphic/graphic.go

@@ -0,0 +1,177 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package graphic ...
+package graphic
+
+import (
+	"github.com/g3n/engine/core"
+	"github.com/g3n/engine/geometry"
+	"github.com/g3n/engine/gls"
+	"github.com/g3n/engine/material"
+)
+
+// Graphic is a Node which has a visible representation in the scene.
+// It has an associated geometry and one or more materials.
+// It is the base type used by other graphics such as lines, line_strip,
+// points and meshes.
+type Graphic struct {
+	core.Node                     // Embedded Node
+	igeom      geometry.IGeometry // Associated IGeometry
+	materials  []GraphicMaterial  // Materials
+	mode       uint32             // OpenGL primitive
+	renderable bool               // Renderable flag
+}
+
+// GraphicMaterial specifies the material to be used for
+// a subset of vertices from the Graphic geometry
+// A Graphic object has at least one GraphicMaterial
+type GraphicMaterial struct {
+	imat     material.IMaterial // Associated material
+	start    int                // Index of first element in the geometry
+	count    int                // Number of elements
+	igraphic IGraphic           // Graphic which contains this GraphicMaterial
+}
+
+// Interface for all Graphics
+type IGraphic interface {
+	core.INode
+	GetGraphic() *Graphic
+	GetGeometry() *geometry.Geometry
+	Renderable() bool
+	SetRenderable(bool)
+	RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo)
+}
+
+// Init initializes a Graphic type embedded in another type
+// with the specified geometry and OpenGL mode.
+func (gr *Graphic) Init(igeom geometry.IGeometry, mode uint32) *Graphic {
+
+	gr.Node.Init()
+	gr.igeom = igeom
+	gr.mode = mode
+	gr.materials = make([]GraphicMaterial, 0)
+	gr.renderable = true
+	return gr
+}
+
+// GetGraphic satisfies the IGraphic interface and
+// returns pointer to the base Graphic
+func (gr *Graphic) GetGraphic() *Graphic {
+
+	return gr
+}
+
+// GetGeometry satisfies the IGraphic interface and returns
+// a pointer to the geometry associated with this graphic
+func (gr *Graphic) GetGeometry() *geometry.Geometry {
+
+	return gr.igeom.GetGeometry()
+}
+
+// Dispose overrides the embedded Node Dispose method
+func (gr *Graphic) Dispose() {
+
+    gr.igeom.Dispose()
+    for i := 0; i < len(gr.materials); i++ {
+        gr.materials[i].imat.Dispose()
+    }
+}
+
+// SetRenderable satisfies the IGraphic interface and
+// sets the renderable state of this Graphic (default = true)
+func (gr *Graphic) SetRenderable(state bool) {
+
+	gr.renderable = state
+}
+
+// Renderable satisfies the IGraphic interface and
+// returns the renderable state of this graphic
+func (gr *Graphic) Renderable() bool {
+
+	return gr.renderable
+}
+
+// Add material for the specified subset of vertices.
+// If the material applies to all vertices, start and count must be 0.
+func (gr *Graphic) AddMaterial(igr IGraphic, imat material.IMaterial, start, count int) {
+
+	gmat := GraphicMaterial{
+		imat:     imat,
+		start:    start,
+		count:    count,
+		igraphic: igr,
+	}
+	gr.materials = append(gr.materials, gmat)
+}
+
+// Add group material
+func (gr *Graphic) AddGroupMaterial(igr IGraphic, imat material.IMaterial, gindex int) {
+
+	geom := gr.igeom.GetGeometry()
+	if gindex < 0 || gindex >= geom.GroupCount() {
+		panic("Invalid group index")
+	}
+	group := geom.GroupAt(gindex)
+	gr.AddMaterial(igr, imat, group.Start, group.Count)
+}
+
+// Materials returns slice with this graphic materials
+func (gr *Graphic) Materials() []GraphicMaterial {
+
+	return gr.materials
+}
+
+// GetMaterial returns the  material associated with the specified vertex position
+func (gr *Graphic) GetMaterial(vpos int) material.IMaterial {
+
+	for _, gmat := range gr.materials {
+		// Case for unimaterial
+		if gmat.count == 0 {
+			return gmat.imat
+		}
+		if gmat.start >= vpos && gmat.start+gmat.count <= vpos {
+			return gmat.imat
+		}
+	}
+	return nil
+}
+
+func (grmat *GraphicMaterial) GetMaterial() material.IMaterial {
+
+	return grmat.imat
+}
+
+// Render is called by the renderer to render this graphic material
+func (grmat *GraphicMaterial) Render(gs *gls.GLS, rinfo *core.RenderInfo) {
+
+	// Setup the associated material (set states and transfer material uniforms and textures)
+	grmat.imat.RenderSetup(gs)
+
+	// Setup the associated geometry (set VAO and transfer VBOS)
+	gr := grmat.igraphic.GetGraphic()
+	gr.igeom.RenderSetup(gs)
+
+	// Setup current graphic (transfer matrices)
+	grmat.igraphic.RenderSetup(gs, rinfo)
+
+	// Get the number of vertices for the current material
+	count := grmat.count
+
+	geom := gr.igeom.GetGeometry()
+	indices := geom.Indices()
+	// Indexed geometry
+	if indices.Size() > 0 {
+		if count == 0 {
+			count = indices.Size()
+		}
+		gs.DrawElements(gr.mode, int32(count), gls.UNSIGNED_INT, 4 * uint32(grmat.start))
+		// Non indexed geometry
+	} else {
+		if count == 0 {
+			count = geom.Items()
+		}
+		gs.DrawArrays(gr.mode, int32(grmat.start), int32(count))
+	}
+}

+ 51 - 0
graphic/grid_helper.go

@@ -0,0 +1,51 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package graphic
+
+import (
+	"github.com/g3n/engine/geometry"
+	"github.com/g3n/engine/gls"
+	"github.com/g3n/engine/material"
+	"github.com/g3n/engine/math32"
+)
+
+type GridHelper struct {
+	Lines
+}
+
+// NewGridHelper creates and returns a pointer to a new grid help object
+// with the specified size and step
+func NewGridHelper(size, step float32, color *math32.Color) *GridHelper {
+
+	grid := new(GridHelper)
+
+	half_size := size / 2
+	positions := math32.NewArrayF32(0, 0)
+	for i := -half_size; i <= half_size; i += step {
+		positions.Append(
+			-half_size, 0, i, color.R, color.G, color.B,
+			half_size, 0, i, color.R, color.G, color.B,
+			i, 0, -half_size, color.R, color.G, color.B,
+			i, 0, half_size, color.R, color.G, color.B,
+		)
+	}
+
+	// Creates geometry
+	geom := geometry.NewGeometry()
+	geom.AddVBO(
+		gls.NewVBO().
+			AddAttrib("VertexPosition", 3).
+			AddAttrib("VertexColor", 3).
+			SetBuffer(positions),
+	)
+
+	// Creates material
+	mat := material.NewBasic()
+	mat.SetLineWidth(1.0)
+
+	// Initialize lines with the specified geometry and material
+	grid.Lines.Init(geom, mat)
+	return grid
+}

+ 48 - 0
graphic/line_strip.go

@@ -0,0 +1,48 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package graphic
+
+import (
+	"github.com/g3n/engine/core"
+	"github.com/g3n/engine/geometry"
+	"github.com/g3n/engine/gls"
+	"github.com/g3n/engine/material"
+	"github.com/g3n/engine/math32"
+)
+
+type LineStrip struct {
+	Graphic
+	mvpm gls.UniformMatrix4f // Model view projection matrix uniform
+}
+
+// NewLineStrip creates and returns a pointer to a new LineStrip graphic
+// with the specified geometry and material
+func NewLineStrip(igeom geometry.IGeometry, imat material.IMaterial) *LineStrip {
+
+	l := new(LineStrip)
+	l.Graphic.Init(igeom, gls.LINE_STRIP)
+	l.AddMaterial(l, imat, 0, 0)
+	l.mvpm.Init("MVP")
+	return l
+}
+
+// RenderSetup is called by the engine before drawing this geometry
+func (l *LineStrip) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
+
+	// Calculates model view projection matrix and updates uniform
+	mw := l.MatrixWorld()
+	var mvpm math32.Matrix4
+	mvpm.MultiplyMatrices(&rinfo.ViewMatrix, &mw)
+	mvpm.MultiplyMatrices(&rinfo.ProjMatrix, &mvpm)
+	l.mvpm.SetMatrix4(&mvpm)
+	l.mvpm.Transfer(gs)
+}
+
+// Raycast satisfies the INode interface and checks the intersections
+// of this geometry with the specified raycaster
+func (l *LineStrip) Raycast(rc *core.Raycaster, intersects *[]core.Intersect) {
+
+	lineRaycast(l, rc, intersects, 1)
+}

+ 148 - 0
graphic/lines.go

@@ -0,0 +1,148 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package graphic
+
+import (
+	"github.com/g3n/engine/core"
+	"github.com/g3n/engine/geometry"
+	"github.com/g3n/engine/gls"
+	"github.com/g3n/engine/material"
+	"github.com/g3n/engine/math32"
+)
+
+// Lines is a Graphic which is rendered as a collection of independent lines
+type Lines struct {
+	Graphic
+	mvpm gls.UniformMatrix4f // Model view projection matrix uniform
+}
+
+func (l *Lines) Init(igeom geometry.IGeometry, imat material.IMaterial) {
+
+	l.Graphic.Init(igeom, gls.LINES)
+	l.AddMaterial(l, imat, 0, 0)
+	l.mvpm.Init("MVP")
+}
+
+func NewLines(igeom geometry.IGeometry, imat material.IMaterial) *Lines {
+
+	l := new(Lines)
+	l.Init(igeom, imat)
+	return l
+}
+
+// RenderSetup is called by the engine before drawing this geometry
+func (l *Lines) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
+
+	// Calculates model view projection matrix and updates uniform
+	mw := l.MatrixWorld()
+	var mvpm math32.Matrix4
+	mvpm.MultiplyMatrices(&rinfo.ViewMatrix, &mw)
+	mvpm.MultiplyMatrices(&rinfo.ProjMatrix, &mvpm)
+	l.mvpm.SetMatrix4(&mvpm)
+	l.mvpm.Transfer(gs)
+}
+
+// Raycast satisfies the INode interface and checks the intersections
+// of this geometry with the specified raycaster
+func (l *Lines) Raycast(rc *core.Raycaster, intersects *[]core.Intersect) {
+
+	lineRaycast(l, rc, intersects, 2)
+}
+
+// Internal function used by raycasting for Lines and LineStrip
+func lineRaycast(igr IGraphic, rc *core.Raycaster, intersects *[]core.Intersect, step int) {
+
+	// Get the bounding sphere
+	gr := igr.GetGraphic()
+	geom := igr.GetGeometry()
+	sphere := geom.BoundingSphere()
+
+	// Transform bounding sphere from model to world coordinates and
+	// checks intersection with raycaster
+	matrixWorld := gr.MatrixWorld()
+	sphere.ApplyMatrix4(&matrixWorld)
+	if !rc.IsIntersectionSphere(&sphere) {
+		return
+	}
+
+	// Copy ray and transform to model coordinates
+	// This ray will will also be used to check intersects with
+	// the geometry, as is much less expensive to transform the
+	// ray to model coordinates than the geometry to world coordinates.
+	var inverseMatrix math32.Matrix4
+	var ray math32.Ray
+	inverseMatrix.GetInverse(&matrixWorld, true)
+	ray.Copy(&rc.Ray).ApplyMatrix4(&inverseMatrix)
+
+	var vstart math32.Vector3
+	var vend math32.Vector3
+	var interSegment math32.Vector3
+	var interRay math32.Vector3
+
+	// Get geometry positions and indices buffers
+	vboPos := geom.VBO("VertexPosition")
+	if vboPos == nil {
+		return
+	}
+	positions := vboPos.Buffer()
+	indices := geom.Indices()
+	precisionSq := rc.LinePrecision * rc.LinePrecision
+
+	// Checks intersection with individual lines for indexed geometry
+	if indices.Size() > 0 {
+		for i := 0; i < indices.Size()-1; i += step {
+			// Calculates distance from ray to this line segment
+			a := indices[i]
+			b := indices[i+1]
+			positions.GetVector3(int(3*a), &vstart)
+			positions.GetVector3(int(3*b), &vend)
+			distSq := ray.DistanceSqToSegment(&vstart, &vend, &interRay, &interSegment)
+			if distSq > precisionSq {
+				continue
+			}
+			// Move back to world coordinates for distance calculation
+			interRay.ApplyMatrix4(&matrixWorld)
+			origin := rc.Ray.Origin()
+			distance := origin.DistanceTo(&interRay)
+			if distance < rc.Near || distance > rc.Far {
+				continue
+			}
+
+			interSegment.ApplyMatrix4(&matrixWorld)
+			*intersects = append(*intersects, core.Intersect{
+				Distance: distance,
+				Point:    interSegment,
+				Index:    uint32(i),
+				Object:   igr,
+			})
+		}
+		// Checks intersection with individual lines for NON indexed geometry
+	} else {
+		for i := 0; i < positions.Size()/3-1; i += step {
+			positions.GetVector3(int(3*i), &vstart)
+			positions.GetVector3(int(3*i+3), &vend)
+			distSq := ray.DistanceSqToSegment(&vstart, &vend, &interRay, &interSegment)
+			if distSq > precisionSq {
+				continue
+			}
+
+			// Move back to world coordinates for distance calculation
+			interRay.ApplyMatrix4(&matrixWorld)
+			origin := rc.Ray.Origin()
+			distance := origin.DistanceTo(&interRay)
+			if distance < rc.Near || distance > rc.Far {
+				continue
+			}
+
+			interSegment.ApplyMatrix4(&matrixWorld)
+			*intersects = append(*intersects, core.Intersect{
+				Distance: distance,
+				Point:    interSegment,
+				Index:    uint32(i),
+				Object:   igr,
+			})
+		}
+	}
+}

+ 12 - 0
graphic/logger.go

@@ -0,0 +1,12 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package graphic
+
+import (
+	"github.com/g3n/engine/util/logger"
+)
+
+// Package logger
+var log = logger.New("GRAPHIC", logger.Default)

+ 200 - 0
graphic/mesh.go

@@ -0,0 +1,200 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package graphic
+
+import (
+	"github.com/g3n/engine/core"
+	"github.com/g3n/engine/geometry"
+	"github.com/g3n/engine/gls"
+	"github.com/g3n/engine/material"
+	"github.com/g3n/engine/math32"
+)
+
+type Mesh struct {
+	Graphic                     // Embedded graphic
+	mvm     gls.UniformMatrix4f // Model view matrix uniform
+	mvpm    gls.UniformMatrix4f // Model view projection matrix uniform
+	nm      gls.UniformMatrix3f // Normal matrix uniform
+}
+
+// NewMesh creates and returns a pointer to a mesh with the specified geometry and material
+// If the mesh has multi materials, the material specified here must be nil and the
+// individual materials must be add using "AddMateria" or AddGroupMaterial"
+func NewMesh(igeom geometry.IGeometry, imat material.IMaterial) *Mesh {
+
+	m := new(Mesh)
+	m.Init(igeom, imat)
+	return m
+}
+
+func (m *Mesh) Init(igeom geometry.IGeometry, imat material.IMaterial) {
+
+	m.Graphic.Init(igeom, gls.TRIANGLES)
+
+	// Initialize uniforms
+	m.mvm.Init("ModelViewMatrix")
+	m.mvpm.Init("MVP")
+	m.nm.Init("NormalMatrix")
+
+	// Adds single material if not nil
+	if imat != nil {
+		m.AddMaterial(imat, 0, 0)
+	}
+}
+
+func (m *Mesh) AddMaterial(imat material.IMaterial, start, count int) {
+
+	m.Graphic.AddMaterial(m, imat, start, count)
+}
+
+// Add group material
+func (m *Mesh) AddGroupMaterial(imat material.IMaterial, gindex int) {
+
+	m.Graphic.AddGroupMaterial(m, imat, gindex)
+}
+
+// RenderSetup is called by the engine before drawing the mesh geometry
+// It is responsible to updating the current shader uniforms with
+// the model matrices.
+func (m *Mesh) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
+
+	// Calculates model view matrix and updates uniform
+	mw := m.MatrixWorld()
+	var mvm math32.Matrix4
+	mvm.MultiplyMatrices(&rinfo.ViewMatrix, &mw)
+	m.mvm.SetMatrix4(&mvm)
+	m.mvm.Transfer(gs)
+
+	// Calculates model view projection matrix and updates uniform
+	var mvpm math32.Matrix4
+	mvpm.MultiplyMatrices(&rinfo.ProjMatrix, &mvm)
+	m.mvpm.SetMatrix4(&mvpm)
+	m.mvpm.Transfer(gs)
+
+	// Calculates normal matrix and updates uniform
+	var nm math32.Matrix3
+	nm.GetNormalMatrix(&mvm)
+	m.nm.SetMatrix3(&nm)
+	m.nm.Transfer(gs)
+}
+
+// Raycast checks intersections between this geometry and the specified raycaster
+// and if any found appends it to the specified intersects array.
+func (m *Mesh) Raycast(rc *core.Raycaster, intersects *[]core.Intersect) {
+
+	// Transform this mesh geometry bounding sphere from model
+	// to world coordinates and checks intersection with raycaster
+	geom := m.GetGeometry()
+	sphere := geom.BoundingSphere()
+	matrixWorld := m.MatrixWorld()
+	sphere.ApplyMatrix4(&matrixWorld)
+	if !rc.IsIntersectionSphere(&sphere) {
+		return
+	}
+
+	// Copy ray and transform to model coordinates
+	// This ray will will also be used to check intersects with
+	// the geometry, as is much less expensive to transform the
+	// ray to model coordinates than the geometry to world coordinates.
+	var inverseMatrix math32.Matrix4
+	inverseMatrix.GetInverse(&matrixWorld, true)
+	var ray math32.Ray
+	ray.Copy(&rc.Ray).ApplyMatrix4(&inverseMatrix)
+	bbox := geom.BoundingBox()
+	if !ray.IsIntersectionBox(&bbox) {
+		return
+	}
+
+	// Local function to check the intersection of the ray from the raycaster with
+	// the specified face defined by three poins.
+	checkIntersection := func(mat *material.Material, pA, pB, pC, point *math32.Vector3) *core.Intersect {
+
+		var intersect bool
+		switch mat.Side() {
+		case material.SideBack:
+			intersect = ray.IntersectTriangle(pC, pB, pA, true, point)
+		case material.SideFront:
+			intersect = ray.IntersectTriangle(pA, pB, pC, true, point)
+		case material.SideDouble:
+			intersect = ray.IntersectTriangle(pA, pB, pC, false, point)
+		}
+		if !intersect {
+			return nil
+		}
+
+		// Transform intersection point from model to world coordinates
+		var intersectionPointWorld = *point
+		intersectionPointWorld.ApplyMatrix4(&matrixWorld)
+
+		// Calculates the distance from the ray origin to intersection point
+		origin := rc.Ray.Origin()
+		distance := origin.DistanceTo(&intersectionPointWorld)
+
+		// Checks if distance is between the bounds of the raycaster
+		if distance < rc.Near || distance > rc.Far {
+			return nil
+		}
+
+		return &core.Intersect{
+			Distance: distance,
+			Point:    intersectionPointWorld,
+			Object:   m,
+		}
+	}
+
+	// Get buffer with position vertices
+	vboPos := geom.VBO("VertexPosition")
+	if vboPos == nil {
+		panic("mesh.Raycast(): VertexPosition VBO not found")
+	}
+	positions := vboPos.Buffer()
+	indices := geom.Indices()
+
+	var vA math32.Vector3
+	var vB math32.Vector3
+	var vC math32.Vector3
+
+	// Geometry has indexed vertices
+	if indices.Size() > 0 {
+		for i := 0; i < indices.Size(); i += 3 {
+			// Get face indices
+			a := indices[i]
+			b := indices[i+1]
+			c := indices[i+2]
+			// Get face position vectors
+			positions.GetVector3(int(3*a), &vA)
+			positions.GetVector3(int(3*b), &vB)
+			positions.GetVector3(int(3*c), &vC)
+			// Checks intersection of the ray with this face
+			mat := m.GetMaterial(i).GetMaterial()
+			var point math32.Vector3
+			intersect := checkIntersection(mat, &vA, &vB, &vC, &point)
+			if intersect != nil {
+				intersect.Index = uint32(i)
+				*intersects = append(*intersects, *intersect)
+			}
+		}
+		// Geometry has NO indexed vertices
+	} else {
+		for i := 0; i < positions.Size(); i += 9 {
+			// Get face indices
+			a := i / 3
+			b := a + 1
+			c := a + 2
+			// Set face position vectors
+			positions.GetVector3(int(3*a), &vA)
+			positions.GetVector3(int(3*b), &vB)
+			positions.GetVector3(int(3*c), &vC)
+			// Checks intersection of the ray with this face
+			mat := m.GetMaterial(i).GetMaterial()
+			var point math32.Vector3
+			intersect := checkIntersection(mat, &vA, &vB, &vC, &point)
+			if intersect != nil {
+				intersect.Index = uint32(a)
+				*intersects = append(*intersects, *intersect)
+			}
+		}
+	}
+}

+ 96 - 0
graphic/normals_helper.go

@@ -0,0 +1,96 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package graphic
+
+import (
+	"github.com/g3n/engine/core"
+	"github.com/g3n/engine/geometry"
+	"github.com/g3n/engine/gls"
+	"github.com/g3n/engine/material"
+	"github.com/g3n/engine/math32"
+)
+
+type NormalsHelper struct {
+	Lines
+	size   float32
+	target *core.Node
+	tgeom  *geometry.Geometry
+}
+
+// NewNormalsHelper creates, initializes and returns a pointer to Normals helper object.
+// This helper shows the normal vectors of the specified object.
+func NewNormalsHelper(ig IGraphic, size float32, color *math32.Color, lineWidth float32) *NormalsHelper {
+
+	// Creates new Normals helper
+	nh := new(NormalsHelper)
+	nh.size = size
+
+	// Saves the object to show the normals
+	nh.target = ig.GetNode()
+
+	// Get the geometry of the target object
+	nh.tgeom = ig.GetGeometry()
+
+	// Get the number of target vertex positions
+	vertices := nh.tgeom.VBO("VertexPosition")
+	n := vertices.Buffer().Size() * 2
+
+	// Creates this helper geometry
+	geom := geometry.NewGeometry()
+	positions := math32.NewArrayF32(n, n)
+	geom.AddVBO(gls.NewVBO().AddAttrib("VertexPosition", 3).SetBuffer(positions))
+
+	// Creates this helper material
+	mat := material.NewStandard(color)
+	mat.SetLineWidth(lineWidth)
+
+	// Initialize graphic
+	nh.Lines.Init(geom, mat)
+
+	nh.Update()
+	return nh
+}
+
+// Update should be called in the render loop to update the normals from the
+// target object
+func (nh *NormalsHelper) Update() {
+
+	var v1 math32.Vector3
+	var v2 math32.Vector3
+	var normalMatrix math32.Matrix3
+
+	// Updates the target object matrix and get its normal matrix
+	matrixWorld := nh.target.MatrixWorld()
+	normalMatrix.GetNormalMatrix(&matrixWorld)
+
+	// Get the target positions and normals buffers
+	tposvbo := nh.tgeom.VBO("VertexPosition")
+	tpositions := tposvbo.Buffer()
+	tnormvbo := nh.tgeom.VBO("VertexNormal")
+	tnormals := tnormvbo.Buffer()
+
+	// Get this object positions buffer
+	geom := nh.GetGeometry()
+	posvbo := geom.VBO("VertexPosition")
+	positions := posvbo.Buffer()
+
+	// For each target object vertex position:
+	for pos := 0; pos < tpositions.Size(); pos += 3 {
+		// Get the target vertex position and apply the current world matrix transform
+		// to get the base for this normal line segment.
+		tpositions.GetVector3(pos, &v1)
+		v1.ApplyMatrix4(&matrixWorld)
+
+		// Calculates the end position of the normal line segment
+		tnormals.GetVector3(pos, &v2)
+		v2.ApplyMatrix3(&normalMatrix).Normalize().MultiplyScalar(nh.size).Add(&v1)
+
+		// Sets the line segment representing the normal of the current target position
+		// at this helper VBO
+		positions.SetVector3(2*pos, &v1)
+		positions.SetVector3(2*pos+3, &v2)
+	}
+	posvbo.Update()
+}

+ 117 - 0
graphic/points.go

@@ -0,0 +1,117 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package graphic
+
+import (
+	"github.com/g3n/engine/core"
+	"github.com/g3n/engine/geometry"
+	"github.com/g3n/engine/gls"
+	"github.com/g3n/engine/material"
+	"github.com/g3n/engine/math32"
+)
+
+type Points struct {
+	Graphic                     // Embedded graphic
+	mvpm    gls.UniformMatrix4f // Model view projection matrix uniform
+}
+
+// NewPoints creates and returns a graphic points object with the specified
+// geometry and material.
+func NewPoints(igeom geometry.IGeometry, imat material.IMaterial) *Points {
+
+	p := new(Points)
+	p.Graphic.Init(igeom, gls.POINTS)
+	if imat != nil {
+		p.AddMaterial(p, imat, 0, 0)
+	}
+	p.mvpm.Init("MVP")
+	return p
+}
+
+// RenderSetup is called by the engine before rendering this graphic
+func (p *Points) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
+
+	// Calculates model view projection matrix and updates uniform
+	mw := p.MatrixWorld()
+	var mvpm math32.Matrix4
+	mvpm.MultiplyMatrices(&rinfo.ViewMatrix, &mw)
+	mvpm.MultiplyMatrices(&rinfo.ProjMatrix, &mvpm)
+	p.mvpm.SetMatrix4(&mvpm)
+	p.mvpm.Transfer(gs)
+}
+
+// Raycast satisfies the INode interface and checks the intersections
+// of this geometry with the specified raycaster
+func (p *Points) Raycast(rc *core.Raycaster, intersects *[]core.Intersect) {
+
+	// Checks intersection with the bounding sphere transformed to world coordinates
+	geom := p.GetGeometry()
+	sphere := geom.BoundingSphere()
+	matrixWorld := p.MatrixWorld()
+	sphere.ApplyMatrix4(&matrixWorld)
+	if !rc.IsIntersectionSphere(&sphere) {
+		return
+	}
+
+	// Copy ray and transforms to model coordinates
+	var inverseMatrix math32.Matrix4
+	var ray math32.Ray
+	inverseMatrix.GetInverse(&matrixWorld, true)
+	ray.Copy(&rc.Ray).ApplyMatrix4(&inverseMatrix)
+
+	// Checks intersection with all points
+	scale := p.Scale()
+	localThreshold := rc.PointPrecision / ((scale.X + scale.Y + scale.Z) / 3)
+	localThresholdSq := localThreshold * localThreshold
+
+	// internal function to check intersection with a point
+	testPoint := func(point *math32.Vector3, index int) {
+
+		// Get distance from ray to point and if greater than threshold,
+		// nothing to do.
+		rayPointDistanceSq := ray.DistanceSqToPoint(point)
+		if rayPointDistanceSq >= localThresholdSq {
+			return
+		}
+		var intersectPoint math32.Vector3
+		ray.ClosestPointToPoint(point, &intersectPoint)
+		intersectPoint.ApplyMatrix4(&matrixWorld)
+		origin := rc.Ray.Origin()
+		distance := origin.DistanceTo(&intersectPoint)
+		if distance < rc.Near || distance > rc.Far {
+			return
+		}
+		// Appends intersection of raycaster with this point
+		*intersects = append(*intersects, core.Intersect{
+			Distance: distance,
+			Point:    intersectPoint,
+			Index:    uint32(index),
+			Object:   p,
+		})
+	}
+
+	// Get buffer with position vertices
+	vbPos := geom.VBO("VertexPosition")
+	if vbPos == nil {
+		panic("points.Raycast(): VertexPosition VBO not found")
+	}
+	positions := vbPos.Buffer()
+
+	var point math32.Vector3
+	indices := geom.Indices()
+	// Geometry has indexed vertices
+	if indices.Size() > 0 {
+		for i := 0; i < indices.Size(); i++ {
+			a := indices[i]
+			positions.GetVector3(int(a*3), &point)
+			testPoint(&point, i)
+		}
+	} else {
+		for i := 0; i < positions.Size()/3; i++ {
+			positions.GetVector3(i*3, &point)
+			testPoint(&point, i)
+		}
+	}
+}

+ 90 - 0
graphic/skybox.go

@@ -0,0 +1,90 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package graphic
+
+import (
+	"github.com/g3n/engine/core"
+	"github.com/g3n/engine/geometry"
+	"github.com/g3n/engine/gls"
+	"github.com/g3n/engine/material"
+	"github.com/g3n/engine/math32"
+	"github.com/g3n/engine/texture"
+)
+
+type SkyboxData struct {
+	DirAndPrefix string
+	Extension    string
+	Suffixes     [6]string
+}
+
+type Skybox struct {
+	Graphic                     // embedded graphic object
+	mvm     gls.UniformMatrix4f // model view matrix uniform
+	mvpm    gls.UniformMatrix4f // model view projection matrix uniform
+	nm      gls.UniformMatrix3f // normal matrix uniform
+}
+
+// NewSkybox creates and returns a pointer to a skybox with the specified textures
+func NewSkybox(data SkyboxData) (*Skybox, error) {
+
+	skybox := new(Skybox)
+
+	geom := geometry.NewBox(50, 50, 50, 1, 1, 1)
+	skybox.Graphic.Init(geom, gls.TRIANGLES)
+
+	for i := 0; i < 6; i++ {
+		tex, err := texture.NewTexture2DFromImage(data.DirAndPrefix + data.Suffixes[i] + "." + data.Extension)
+		if err != nil {
+			return nil, err
+		}
+		matFace := material.NewStandard(math32.NewColor(1, 1, 1))
+		matFace.AddTexture(tex)
+		matFace.SetSide(material.SideBack)
+		matFace.SetUseLights(material.UseLightAmbient)
+		skybox.AddGroupMaterial(skybox, matFace, i)
+	}
+
+	// Creates uniforms
+	skybox.mvm.Init("ModelViewMatrix")
+	skybox.mvpm.Init("MVP")
+	skybox.nm.Init("NormalMatrix")
+
+	return skybox, nil
+}
+
+// RenderSetup is called by the engine before drawing the skybox geometry
+// It is responsible to updating the current shader uniforms with
+// the model matrices.
+func (skybox *Skybox) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
+
+	// TODO
+	// Disable writes to the depth buffer (call glDepthMask(GL_FALSE)).
+	// This will cause every other object to draw over the skybox, making it always appear "behind" everything else.
+	// Since writes to the depth buffer are off, it doesn't matter how small the skybox is as long as it's larger than the camera's near clip plane.
+
+	var mvm math32.Matrix4
+	mvm.Copy(&rinfo.ViewMatrix)
+
+	// Clear translation
+	mvm[12] = 0
+	mvm[13] = 0
+	mvm[14] = 0
+	// mvm.ExtractRotation(&rinfo.ViewMatrix) // TODO <- ExtractRotation does not work as expected?
+	skybox.mvm.SetMatrix4(&mvm)
+	skybox.mvm.Transfer(gs)
+
+	// Calculates model view projection matrix and updates uniform
+	var mvpm math32.Matrix4
+	mvpm.MultiplyMatrices(&rinfo.ProjMatrix, &mvm)
+	skybox.mvpm.SetMatrix4(&mvpm)
+	skybox.mvpm.Transfer(gs)
+
+	// Calculates normal matrix and updates uniform
+	var nm math32.Matrix3
+	nm.GetNormalMatrix(&mvm)
+	skybox.nm.SetMatrix3(&nm)
+	skybox.nm.Transfer(gs)
+
+}

+ 161 - 0
graphic/sprite.go

@@ -0,0 +1,161 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package graphic
+
+import (
+	"github.com/g3n/engine/core"
+	"github.com/g3n/engine/geometry"
+	"github.com/g3n/engine/gls"
+	"github.com/g3n/engine/material"
+	"github.com/g3n/engine/math32"
+)
+
+type Sprite struct {
+	Graphic                     // Embedded graphic
+	mvpm    gls.UniformMatrix4f // Model view projection matrix uniform
+}
+
+// NewSprite creates and returns a pointer to a sprite with the specified dimensions and material
+func NewSprite(width, height float32, imat material.IMaterial) *Sprite {
+
+	s := new(Sprite)
+
+	// Creates geometry
+	geom := geometry.NewGeometry()
+	w := width / 2
+	h := height / 2
+
+	// Builds array with vertex positions and texture coordinates
+	positions := math32.NewArrayF32(0, 12)
+	positions.Append(
+		-w, -h, 0, 0, 0,
+		w, -h, 0, 1, 0,
+		w, h, 0, 1, 1,
+		-w, h, 0, 0, 1,
+	)
+	// Builds array of indices
+	indices := math32.NewArrayU32(0, 6)
+	indices.Append(0, 1, 2, 0, 2, 3)
+
+	// Set geometry buffers
+	geom.SetIndices(indices)
+	geom.AddVBO(
+		gls.NewVBO().
+			AddAttrib("VertexPosition", 3).
+			AddAttrib("VertexTexcoord", 2).
+			SetBuffer(positions),
+	)
+
+	s.Graphic.Init(geom, gls.TRIANGLES)
+	s.AddMaterial(s, imat, 0, 0)
+
+	s.mvpm.Init("MVP")
+	return s
+}
+
+func (s *Sprite) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
+
+	// Calculates model view matrix
+	mw := s.MatrixWorld()
+	var mvm math32.Matrix4
+	mvm.MultiplyMatrices(&rinfo.ViewMatrix, &mw)
+
+	// Decomposes model view matrix
+	var position math32.Vector3
+	var quaternion math32.Quaternion
+	var scale math32.Vector3
+	mvm.Decompose(&position, &quaternion, &scale)
+
+	// Removes any rotation in X and Y axes and compose new model view matrix
+	rotation := s.Rotation()
+	rotation.X = 0
+	rotation.Y = 0
+	quaternion.SetFromEuler(&rotation)
+	var mvm_new math32.Matrix4
+	mvm_new.Compose(&position, &quaternion, &scale)
+
+	// Calculates final MVP and updates uniform
+	var mvpm math32.Matrix4
+	mvpm.MultiplyMatrices(&rinfo.ProjMatrix, &mvm_new)
+	s.mvpm.SetMatrix4(&mvpm)
+	s.mvpm.Transfer(gs)
+}
+
+// Raycast checks intersections between this geometry and the specified raycaster
+// and if any found appends it to the specified intersects array.
+func (s *Sprite) Raycast(rc *core.Raycaster, intersects *[]core.Intersect) {
+
+	// Copy and convert ray to camera coordinates
+	var ray math32.Ray
+	ray.Copy(&rc.Ray).ApplyMatrix4(&rc.ViewMatrix)
+
+	// Calculates ViewMatrix * MatrixWorld
+	var mv math32.Matrix4
+	matrixWorld := s.MatrixWorld()
+	mv.MultiplyMatrices(&rc.ViewMatrix, &matrixWorld)
+
+	// Decompose transformation matrix in its components
+	var position math32.Vector3
+	var quaternion math32.Quaternion
+	var scale math32.Vector3
+	mv.Decompose(&position, &quaternion, &scale)
+
+	// Remove any rotation in X and Y axis and
+	// compose new transformation matrix
+	rotation := s.Rotation()
+	rotation.X = 0
+	rotation.Y = 0
+	quaternion.SetFromEuler(&rotation)
+	mv.Compose(&position, &quaternion, &scale)
+
+	// Get buffer with vertices and uvs
+	geom := s.GetGeometry()
+	vboPos := geom.VBO("VertexPosition")
+	if vboPos == nil {
+		panic("sprite.Raycast(): VertexPosition VBO not found")
+	}
+	// Get vertex positions, transform to camera coordinates and
+	// checks intersection with ray
+	buffer := vboPos.Buffer()
+	indices := geom.Indices()
+	var v1 math32.Vector3
+	var v2 math32.Vector3
+	var v3 math32.Vector3
+	var point math32.Vector3
+	intersect := false
+	for i := 0; i < indices.Size(); i += 3 {
+		pos := indices[i]
+		buffer.GetVector3(int(pos*5), &v1)
+		v1.ApplyMatrix4(&mv)
+		pos = indices[i+1]
+		buffer.GetVector3(int(pos*5), &v2)
+		v2.ApplyMatrix4(&mv)
+		pos = indices[i+2]
+		buffer.GetVector3(int(pos*5), &v3)
+		v3.ApplyMatrix4(&mv)
+		if ray.IntersectTriangle(&v1, &v2, &v3, false, &point) {
+			intersect = true
+			break
+		}
+	}
+	if !intersect {
+		return
+	}
+	// Get distance from intersection point
+	origin := ray.Origin()
+	distance := origin.DistanceTo(&point)
+
+	// Checks if distance is between the bounds of the raycaster
+	if distance < rc.Near || distance > rc.Far {
+		return
+	}
+
+	// Appends intersection to received parameter.
+	*intersects = append(*intersects, core.Intersect{
+		Distance: distance,
+		Point:    point,
+		Object:   s,
+	})
+}

+ 18 - 0
gui/align.go

@@ -0,0 +1,18 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+type Align int
+
+const (
+	AlignNone   = Align(iota) // No aligh
+	AlignLeft                 // Align horizontally at left
+	AlignRight                // Align horizontally at right
+	AlignWidth                // Align horizontally using all width
+	AlignTop                  // Align vertically at the top
+	AlignBottom               // Align vertically at the cnter
+	AlignHeight               // Align vertically using all height
+	AlignCenter               // Align horizontally/vertically at the center
+)

File diff suppressed because it is too large
+ 306 - 0
gui/assets/data.go


+ 899 - 0
gui/assets/icodes.go

@@ -0,0 +1,899 @@
+//
+// This file was generated from the original
+// 'codepoints' file from the material icon fonts.
+//
+package assets
+
+const (
+	Rotation3d                            = 0xe84d
+	AcUnit                                = 0xeb3b
+	AccessAlarm                           = 0xe190
+	AccessAlarms                          = 0xe191
+	AccessTime                            = 0xe192
+	Accessibility                         = 0xe84e
+	Accessible                            = 0xe914
+	AccountBalance                        = 0xe84f
+	AccountBalanceWallet                  = 0xe850
+	AccountBox                            = 0xe851
+	AccountCircle                         = 0xe853
+	Adb                                   = 0xe60e
+	Add                                   = 0xe145
+	AddAPhoto                             = 0xe439
+	AddAlarm                              = 0xe193
+	AddAlert                              = 0xe003
+	AddBox                                = 0xe146
+	AddCircle                             = 0xe147
+	AddCircleOutline                      = 0xe148
+	AddLocation                           = 0xe567
+	AddShoppingCart                       = 0xe854
+	AddToPhotos                           = 0xe39d
+	AddToQueue                            = 0xe05c
+	Adjust                                = 0xe39e
+	AirlineSeatFlat                       = 0xe630
+	AirlineSeatFlatAngled                 = 0xe631
+	AirlineSeatIndividualSuite            = 0xe632
+	AirlineSeatLegroomExtra               = 0xe633
+	AirlineSeatLegroomNormal              = 0xe634
+	AirlineSeatLegroomReduced             = 0xe635
+	AirlineSeatReclineExtra               = 0xe636
+	AirlineSeatReclineNormal              = 0xe637
+	AirplanemodeActive                    = 0xe195
+	AirplanemodeInactive                  = 0xe194
+	Airplay                               = 0xe055
+	AirportShuttle                        = 0xeb3c
+	Alarm                                 = 0xe855
+	AlarmAdd                              = 0xe856
+	AlarmOff                              = 0xe857
+	AlarmOn                               = 0xe858
+	Album                                 = 0xe019
+	AllInclusive                          = 0xeb3d
+	AllOut                                = 0xe90b
+	Android                               = 0xe859
+	Announcement                          = 0xe85a
+	Apps                                  = 0xe5c3
+	Archive                               = 0xe149
+	ArrowBack                             = 0xe5c4
+	ArrowDownward                         = 0xe5db
+	ArrowDropDown                         = 0xe5c5
+	ArrowDropDownCircle                   = 0xe5c6
+	ArrowDropUp                           = 0xe5c7
+	ArrowForward                          = 0xe5c8
+	ArrowUpward                           = 0xe5d8
+	ArtTrack                              = 0xe060
+	AspectRatio                           = 0xe85b
+	Assessment                            = 0xe85c
+	Assignment                            = 0xe85d
+	AssignmentInd                         = 0xe85e
+	AssignmentLate                        = 0xe85f
+	AssignmentReturn                      = 0xe860
+	AssignmentReturned                    = 0xe861
+	AssignmentTurnedIn                    = 0xe862
+	Assistant                             = 0xe39f
+	AssistantPhoto                        = 0xe3a0
+	AttachFile                            = 0xe226
+	AttachMoney                           = 0xe227
+	Attachment                            = 0xe2bc
+	Audiotrack                            = 0xe3a1
+	Autorenew                             = 0xe863
+	AvTimer                               = 0xe01b
+	Backspace                             = 0xe14a
+	Backup                                = 0xe864
+	BatteryAlert                          = 0xe19c
+	BatteryChargingFull                   = 0xe1a3
+	BatteryFull                           = 0xe1a4
+	BatteryStd                            = 0xe1a5
+	BatteryUnknown                        = 0xe1a6
+	BeachAccess                           = 0xeb3e
+	Beenhere                              = 0xe52d
+	Block                                 = 0xe14b
+	Bluetooth                             = 0xe1a7
+	BluetoothAudio                        = 0xe60f
+	BluetoothConnected                    = 0xe1a8
+	BluetoothDisabled                     = 0xe1a9
+	BluetoothSearching                    = 0xe1aa
+	BlurCircular                          = 0xe3a2
+	BlurLinear                            = 0xe3a3
+	BlurOff                               = 0xe3a4
+	BlurOn                                = 0xe3a5
+	Book                                  = 0xe865
+	Bookmark                              = 0xe866
+	BookmarkBorder                        = 0xe867
+	BorderAll                             = 0xe228
+	BorderBottom                          = 0xe229
+	BorderClear                           = 0xe22a
+	BorderColor                           = 0xe22b
+	BorderHorizontal                      = 0xe22c
+	BorderInner                           = 0xe22d
+	BorderLeft                            = 0xe22e
+	BorderOuter                           = 0xe22f
+	BorderRight                           = 0xe230
+	BorderStyle                           = 0xe231
+	BorderTop                             = 0xe232
+	BorderVertical                        = 0xe233
+	Brightness1                           = 0xe3a6
+	Brightness2                           = 0xe3a7
+	Brightness3                           = 0xe3a8
+	Brightness4                           = 0xe3a9
+	Brightness5                           = 0xe3aa
+	Brightness6                           = 0xe3ab
+	Brightness7                           = 0xe3ac
+	BrightnessAuto                        = 0xe1ab
+	BrightnessHigh                        = 0xe1ac
+	BrightnessLow                         = 0xe1ad
+	BrightnessMedium                      = 0xe1ae
+	BrokenImage                           = 0xe3ad
+	Brush                                 = 0xe3ae
+	BugReport                             = 0xe868
+	Build                                 = 0xe869
+	Business                              = 0xe0af
+	BusinessCenter                        = 0xeb3f
+	Cached                                = 0xe86a
+	Cake                                  = 0xe7e9
+	Call                                  = 0xe0b0
+	CallEnd                               = 0xe0b1
+	CallMade                              = 0xe0b2
+	CallMerge                             = 0xe0b3
+	CallMissed                            = 0xe0b4
+	CallMissedOutgoing                    = 0xe0e4
+	CallReceived                          = 0xe0b5
+	CallSplit                             = 0xe0b6
+	Camera                                = 0xe3af
+	CameraAlt                             = 0xe3b0
+	CameraEnhance                         = 0xe8fc
+	CameraFront                           = 0xe3b1
+	CameraRear                            = 0xe3b2
+	CameraRoll                            = 0xe3b3
+	Cancel                                = 0xe5c9
+	CardGiftcard                          = 0xe8f6
+	CardMembership                        = 0xe8f7
+	CardTravel                            = 0xe8f8
+	Casino                                = 0xeb40
+	Cast                                  = 0xe307
+	CastConnected                         = 0xe308
+	CenterFocusStrong                     = 0xe3b4
+	CenterFocusWeak                       = 0xe3b5
+	ChangeHistory                         = 0xe86b
+	Chat                                  = 0xe0b7
+	ChatBubble                            = 0xe0ca
+	ChatBubbleOutline                     = 0xe0cb
+	Check                                 = 0xe5ca
+	CheckBox                              = 0xe834
+	CheckBoxOutlineBlank                  = 0xe835
+	CheckCircle                           = 0xe86c
+	ChevronLeft                           = 0xe5cb
+	ChevronRight                          = 0xe5cc
+	ChildCare                             = 0xeb41
+	ChildFriendly                         = 0xeb42
+	ChromeReaderMode                      = 0xe86d
+	Class                                 = 0xe86e
+	Clear                                 = 0xe14c
+	ClearAll                              = 0xe0b8
+	Close                                 = 0xe5cd
+	ClosedCaption                         = 0xe01c
+	Cloud                                 = 0xe2bd
+	CloudCircle                           = 0xe2be
+	CloudDone                             = 0xe2bf
+	CloudDownload                         = 0xe2c0
+	CloudOff                              = 0xe2c1
+	CloudQueue                            = 0xe2c2
+	CloudUpload                           = 0xe2c3
+	Code                                  = 0xe86f
+	Collections                           = 0xe3b6
+	CollectionsBookmark                   = 0xe431
+	ColorLens                             = 0xe3b7
+	Colorize                              = 0xe3b8
+	Comment                               = 0xe0b9
+	Compare                               = 0xe3b9
+	CompareArrows                         = 0xe915
+	Computer                              = 0xe30a
+	ConfirmationNumber                    = 0xe638
+	ContactMail                           = 0xe0d0
+	ContactPhone                          = 0xe0cf
+	Contacts                              = 0xe0ba
+	ContentCopy                           = 0xe14d
+	ContentCut                            = 0xe14e
+	ContentPaste                          = 0xe14f
+	ControlPoint                          = 0xe3ba
+	ControlPointDuplicate                 = 0xe3bb
+	Copyright                             = 0xe90c
+	Create                                = 0xe150
+	CreateNewFolder                       = 0xe2cc
+	CreditCard                            = 0xe870
+	Crop                                  = 0xe3be
+	Crop169                               = 0xe3bc
+	Crop32                                = 0xe3bd
+	Crop54                                = 0xe3bf
+	Crop75                                = 0xe3c0
+	CropDin                               = 0xe3c1
+	CropFree                              = 0xe3c2
+	CropLandscape                         = 0xe3c3
+	CropOriginal                          = 0xe3c4
+	CropPortrait                          = 0xe3c5
+	CropRotate                            = 0xe437
+	CropSquare                            = 0xe3c6
+	Dashboard                             = 0xe871
+	DataUsage                             = 0xe1af
+	DateRange                             = 0xe916
+	Dehaze                                = 0xe3c7
+	Delete                                = 0xe872
+	Description                           = 0xe873
+	DesktopMac                            = 0xe30b
+	DesktopWindows                        = 0xe30c
+	Details                               = 0xe3c8
+	DeveloperBoard                        = 0xe30d
+	DeveloperMode                         = 0xe1b0
+	DeviceHub                             = 0xe335
+	Devices                               = 0xe1b1
+	DevicesOther                          = 0xe337
+	DialerSip                             = 0xe0bb
+	Dialpad                               = 0xe0bc
+	Directions                            = 0xe52e
+	DirectionsBike                        = 0xe52f
+	DirectionsBoat                        = 0xe532
+	DirectionsBus                         = 0xe530
+	DirectionsCar                         = 0xe531
+	DirectionsRailway                     = 0xe534
+	DirectionsRun                         = 0xe566
+	DirectionsSubway                      = 0xe533
+	DirectionsTransit                     = 0xe535
+	DirectionsWalk                        = 0xe536
+	DiscFull                              = 0xe610
+	Dns                                   = 0xe875
+	DoNotDisturb                          = 0xe612
+	DoNotDisturbAlt                       = 0xe611
+	Dock                                  = 0xe30e
+	Domain                                = 0xe7ee
+	Done                                  = 0xe876
+	DoneAll                               = 0xe877
+	DonutLarge                            = 0xe917
+	DonutSmall                            = 0xe918
+	Drafts                                = 0xe151
+	DragHandle                            = 0xe25d
+	DriveEta                              = 0xe613
+	Dvr                                   = 0xe1b2
+	Edit                                  = 0xe3c9
+	EditLocation                          = 0xe568
+	Eject                                 = 0xe8fb
+	Email                                 = 0xe0be
+	EnhancedEncryption                    = 0xe63f
+	Equalizer                             = 0xe01d
+	Error                                 = 0xe000
+	ErrorOutline                          = 0xe001
+	Event                                 = 0xe878
+	EventAvailable                        = 0xe614
+	EventBusy                             = 0xe615
+	EventNote                             = 0xe616
+	EventSeat                             = 0xe903
+	ExitToApp                             = 0xe879
+	ExpandLess                            = 0xe5ce
+	ExpandMore                            = 0xe5cf
+	Explicit                              = 0xe01e
+	Explore                               = 0xe87a
+	Exposure                              = 0xe3ca
+	ExposureNeg1                          = 0xe3cb
+	ExposureNeg2                          = 0xe3cc
+	ExposurePlus1                         = 0xe3cd
+	ExposurePlus2                         = 0xe3ce
+	ExposureZero                          = 0xe3cf
+	Extension                             = 0xe87b
+	Face                                  = 0xe87c
+	FastForward                           = 0xe01f
+	FastRewind                            = 0xe020
+	Favorite                              = 0xe87d
+	FavoriteBorder                        = 0xe87e
+	Feedback                              = 0xe87f
+	FiberDvr                              = 0xe05d
+	FiberManualRecord                     = 0xe061
+	FiberNew                              = 0xe05e
+	FiberPin                              = 0xe06a
+	FiberSmartRecord                      = 0xe062
+	FileDownload                          = 0xe2c4
+	FileUpload                            = 0xe2c6
+	Filter                                = 0xe3d3
+	Filter1                               = 0xe3d0
+	Filter2                               = 0xe3d1
+	Filter3                               = 0xe3d2
+	Filter4                               = 0xe3d4
+	Filter5                               = 0xe3d5
+	Filter6                               = 0xe3d6
+	Filter7                               = 0xe3d7
+	Filter8                               = 0xe3d8
+	Filter9                               = 0xe3d9
+	Filter9Plus                           = 0xe3da
+	FilterBAndW                           = 0xe3db
+	FilterCenterFocus                     = 0xe3dc
+	FilterDrama                           = 0xe3dd
+	FilterFrames                          = 0xe3de
+	FilterHdr                             = 0xe3df
+	FilterList                            = 0xe152
+	FilterNone                            = 0xe3e0
+	FilterTiltShift                       = 0xe3e2
+	FilterVintage                         = 0xe3e3
+	FindInPage                            = 0xe880
+	FindReplace                           = 0xe881
+	Fingerprint                           = 0xe90d
+	FitnessCenter                         = 0xeb43
+	Flag                                  = 0xe153
+	Flare                                 = 0xe3e4
+	FlashAuto                             = 0xe3e5
+	FlashOff                              = 0xe3e6
+	FlashOn                               = 0xe3e7
+	Flight                                = 0xe539
+	FlightLand                            = 0xe904
+	FlightTakeoff                         = 0xe905
+	Flip                                  = 0xe3e8
+	FlipToBack                            = 0xe882
+	FlipToFront                           = 0xe883
+	Folder                                = 0xe2c7
+	FolderOpen                            = 0xe2c8
+	FolderShared                          = 0xe2c9
+	FolderSpecial                         = 0xe617
+	FontDownload                          = 0xe167
+	FormatAlignCenter                     = 0xe234
+	FormatAlignJustify                    = 0xe235
+	FormatAlignLeft                       = 0xe236
+	FormatAlignRight                      = 0xe237
+	FormatBold                            = 0xe238
+	FormatClear                           = 0xe239
+	FormatColorFill                       = 0xe23a
+	FormatColorReset                      = 0xe23b
+	FormatColorText                       = 0xe23c
+	FormatIndentDecrease                  = 0xe23d
+	FormatIndentIncrease                  = 0xe23e
+	FormatItalic                          = 0xe23f
+	FormatLineSpacing                     = 0xe240
+	FormatListBulleted                    = 0xe241
+	FormatListNumbered                    = 0xe242
+	FormatPaint                           = 0xe243
+	FormatQuote                           = 0xe244
+	FormatShapes                          = 0xe25e
+	FormatSize                            = 0xe245
+	FormatStrikethrough                   = 0xe246
+	FormatTextdirectionLToR               = 0xe247
+	FormatTextdirectionRToL               = 0xe248
+	FormatUnderlined                      = 0xe249
+	Forum                                 = 0xe0bf
+	Forward                               = 0xe154
+	Forward10                             = 0xe056
+	Forward30                             = 0xe057
+	Forward5                              = 0xe058
+	FreeBreakfast                         = 0xeb44
+	Fullscreen                            = 0xe5d0
+	FullscreenExit                        = 0xe5d1
+	Functions                             = 0xe24a
+	Gamepad                               = 0xe30f
+	Games                                 = 0xe021
+	Gavel                                 = 0xe90e
+	Gesture                               = 0xe155
+	GetApp                                = 0xe884
+	Gif                                   = 0xe908
+	GolfCourse                            = 0xeb45
+	GpsFixed                              = 0xe1b3
+	GpsNotFixed                           = 0xe1b4
+	GpsOff                                = 0xe1b5
+	Grade                                 = 0xe885
+	Gradient                              = 0xe3e9
+	Grain                                 = 0xe3ea
+	GraphicEq                             = 0xe1b8
+	GridOff                               = 0xe3eb
+	GridOn                                = 0xe3ec
+	Group                                 = 0xe7ef
+	GroupAdd                              = 0xe7f0
+	GroupWork                             = 0xe886
+	Hd                                    = 0xe052
+	HdrOff                                = 0xe3ed
+	HdrOn                                 = 0xe3ee
+	HdrStrong                             = 0xe3f1
+	HdrWeak                               = 0xe3f2
+	Headset                               = 0xe310
+	HeadsetMic                            = 0xe311
+	Healing                               = 0xe3f3
+	Hearing                               = 0xe023
+	Help                                  = 0xe887
+	HelpOutline                           = 0xe8fd
+	HighQuality                           = 0xe024
+	Highlight                             = 0xe25f
+	HighlightOff                          = 0xe888
+	History                               = 0xe889
+	Home                                  = 0xe88a
+	HotTub                                = 0xeb46
+	Hotel                                 = 0xe53a
+	HourglassEmpty                        = 0xe88b
+	HourglassFull                         = 0xe88c
+	Http                                  = 0xe902
+	Https                                 = 0xe88d
+	Image                                 = 0xe3f4
+	ImageAspectRatio                      = 0xe3f5
+	ImportContacts                        = 0xe0e0
+	ImportExport                          = 0xe0c3
+	ImportantDevices                      = 0xe912
+	Inbox                                 = 0xe156
+	IndeterminateCheckBox                 = 0xe909
+	Info                                  = 0xe88e
+	InfoOutline                           = 0xe88f
+	Input                                 = 0xe890
+	InsertChart                           = 0xe24b
+	InsertComment                         = 0xe24c
+	InsertDriveFile                       = 0xe24d
+	InsertEmoticon                        = 0xe24e
+	InsertInvitation                      = 0xe24f
+	InsertLink                            = 0xe250
+	InsertPhoto                           = 0xe251
+	InvertColors                          = 0xe891
+	InvertColorsOff                       = 0xe0c4
+	Iso                                   = 0xe3f6
+	Keyboard                              = 0xe312
+	KeyboardArrowDown                     = 0xe313
+	KeyboardArrowLeft                     = 0xe314
+	KeyboardArrowRight                    = 0xe315
+	KeyboardArrowUp                       = 0xe316
+	KeyboardBackspace                     = 0xe317
+	KeyboardCapslock                      = 0xe318
+	KeyboardHide                          = 0xe31a
+	KeyboardReturn                        = 0xe31b
+	KeyboardTab                           = 0xe31c
+	KeyboardVoice                         = 0xe31d
+	Kitchen                               = 0xeb47
+	Label                                 = 0xe892
+	LabelOutline                          = 0xe893
+	Landscape                             = 0xe3f7
+	Language                              = 0xe894
+	Laptop                                = 0xe31e
+	LaptopChromebook                      = 0xe31f
+	LaptopMac                             = 0xe320
+	LaptopWindows                         = 0xe321
+	Launch                                = 0xe895
+	Layers                                = 0xe53b
+	LayersClear                           = 0xe53c
+	LeakAdd                               = 0xe3f8
+	LeakRemove                            = 0xe3f9
+	Lens                                  = 0xe3fa
+	LibraryAdd                            = 0xe02e
+	LibraryBooks                          = 0xe02f
+	LibraryMusic                          = 0xe030
+	LightbulbOutline                      = 0xe90f
+	LineStyle                             = 0xe919
+	LineWeight                            = 0xe91a
+	LinearScale                           = 0xe260
+	Link                                  = 0xe157
+	LinkedCamera                          = 0xe438
+	List                                  = 0xe896
+	LiveHelp                              = 0xe0c6
+	LiveTv                                = 0xe639
+	LocalActivity                         = 0xe53f
+	LocalAirport                          = 0xe53d
+	LocalAtm                              = 0xe53e
+	LocalBar                              = 0xe540
+	LocalCafe                             = 0xe541
+	LocalCarWash                          = 0xe542
+	LocalConvenienceStore                 = 0xe543
+	LocalDining                           = 0xe556
+	LocalDrink                            = 0xe544
+	LocalFlorist                          = 0xe545
+	LocalGasStation                       = 0xe546
+	LocalGroceryStore                     = 0xe547
+	LocalHospital                         = 0xe548
+	LocalHotel                            = 0xe549
+	LocalLaundryService                   = 0xe54a
+	LocalLibrary                          = 0xe54b
+	LocalMall                             = 0xe54c
+	LocalMovies                           = 0xe54d
+	LocalOffer                            = 0xe54e
+	LocalParking                          = 0xe54f
+	LocalPharmacy                         = 0xe550
+	LocalPhone                            = 0xe551
+	LocalPizza                            = 0xe552
+	LocalPlay                             = 0xe553
+	LocalPostOffice                       = 0xe554
+	LocalPrintshop                        = 0xe555
+	LocalSee                              = 0xe557
+	LocalShipping                         = 0xe558
+	LocalTaxi                             = 0xe559
+	LocationCity                          = 0xe7f1
+	LocationDisabled                      = 0xe1b6
+	LocationOff                           = 0xe0c7
+	LocationOn                            = 0xe0c8
+	LocationSearching                     = 0xe1b7
+	Lock                                  = 0xe897
+	LockOpen                              = 0xe898
+	LockOutline                           = 0xe899
+	Looks                                 = 0xe3fc
+	Looks3                                = 0xe3fb
+	Looks4                                = 0xe3fd
+	Looks5                                = 0xe3fe
+	Looks6                                = 0xe3ff
+	LooksOne                              = 0xe400
+	LooksTwo                              = 0xe401
+	Loop                                  = 0xe028
+	Loupe                                 = 0xe402
+	Loyalty                               = 0xe89a
+	Mail                                  = 0xe158
+	MailOutline                           = 0xe0e1
+	Map                                   = 0xe55b
+	Markunread                            = 0xe159
+	MarkunreadMailbox                     = 0xe89b
+	Memory                                = 0xe322
+	Menu                                  = 0xe5d2
+	MergeType                             = 0xe252
+	Message                               = 0xe0c9
+	Mic                                   = 0xe029
+	MicNone                               = 0xe02a
+	MicOff                                = 0xe02b
+	Mms                                   = 0xe618
+	ModeComment                           = 0xe253
+	ModeEdit                              = 0xe254
+	MoneyOff                              = 0xe25c
+	MonochromePhotos                      = 0xe403
+	Mood                                  = 0xe7f2
+	MoodBad                               = 0xe7f3
+	More                                  = 0xe619
+	MoreHoriz                             = 0xe5d3
+	MoreVert                              = 0xe5d4
+	Motorcycle                            = 0xe91b
+	Mouse                                 = 0xe323
+	MoveToInbox                           = 0xe168
+	Movie                                 = 0xe02c
+	MovieCreation                         = 0xe404
+	MovieFilter                           = 0xe43a
+	MusicNote                             = 0xe405
+	MusicVideo                            = 0xe063
+	MyLocation                            = 0xe55c
+	Nature                                = 0xe406
+	NaturePeople                          = 0xe407
+	NavigateBefore                        = 0xe408
+	NavigateNext                          = 0xe409
+	Navigation                            = 0xe55d
+	NearMe                                = 0xe569
+	NetworkCell                           = 0xe1b9
+	NetworkCheck                          = 0xe640
+	NetworkLocked                         = 0xe61a
+	NetworkWifi                           = 0xe1ba
+	NewReleases                           = 0xe031
+	NextWeek                              = 0xe16a
+	Nfc                                   = 0xe1bb
+	NoEncryption                          = 0xe641
+	NoSim                                 = 0xe0cc
+	NotInterested                         = 0xe033
+	NoteAdd                               = 0xe89c
+	Notifications                         = 0xe7f4
+	NotificationsActive                   = 0xe7f7
+	NotificationsNone                     = 0xe7f5
+	NotificationsOff                      = 0xe7f6
+	NotificationsPaused                   = 0xe7f8
+	OfflinePin                            = 0xe90a
+	OndemandVideo                         = 0xe63a
+	Opacity                               = 0xe91c
+	OpenInBrowser                         = 0xe89d
+	OpenInNew                             = 0xe89e
+	OpenWith                              = 0xe89f
+	Pages                                 = 0xe7f9
+	Pageview                              = 0xe8a0
+	Palette                               = 0xe40a
+	PanTool                               = 0xe925
+	Panorama                              = 0xe40b
+	PanoramaFishEye                       = 0xe40c
+	PanoramaHorizontal                    = 0xe40d
+	PanoramaVertical                      = 0xe40e
+	PanoramaWideAngle                     = 0xe40f
+	PartyMode                             = 0xe7fa
+	Pause                                 = 0xe034
+	PauseCircleFilled                     = 0xe035
+	PauseCircleOutline                    = 0xe036
+	Payment                               = 0xe8a1
+	People                                = 0xe7fb
+	PeopleOutline                         = 0xe7fc
+	PermCameraMic                         = 0xe8a2
+	PermContactCalendar                   = 0xe8a3
+	PermDataSetting                       = 0xe8a4
+	PermDeviceInformation                 = 0xe8a5
+	PermIdentity                          = 0xe8a6
+	PermMedia                             = 0xe8a7
+	PermPhoneMsg                          = 0xe8a8
+	PermScanWifi                          = 0xe8a9
+	Person                                = 0xe7fd
+	PersonAdd                             = 0xe7fe
+	PersonOutline                         = 0xe7ff
+	PersonPin                             = 0xe55a
+	PersonPinCircle                       = 0xe56a
+	PersonalVideo                         = 0xe63b
+	Pets                                  = 0xe91d
+	Phone                                 = 0xe0cd
+	PhoneAndroid                          = 0xe324
+	PhoneBluetoothSpeaker                 = 0xe61b
+	PhoneForwarded                        = 0xe61c
+	PhoneInTalk                           = 0xe61d
+	PhoneIphone                           = 0xe325
+	PhoneLocked                           = 0xe61e
+	PhoneMissed                           = 0xe61f
+	PhonePaused                           = 0xe620
+	Phonelink                             = 0xe326
+	PhonelinkErase                        = 0xe0db
+	PhonelinkLock                         = 0xe0dc
+	PhonelinkOff                          = 0xe327
+	PhonelinkRing                         = 0xe0dd
+	PhonelinkSetup                        = 0xe0de
+	Photo                                 = 0xe410
+	PhotoAlbum                            = 0xe411
+	PhotoCamera                           = 0xe412
+	PhotoFilter                           = 0xe43b
+	PhotoLibrary                          = 0xe413
+	PhotoSizeSelectActual                 = 0xe432
+	PhotoSizeSelectLarge                  = 0xe433
+	PhotoSizeSelectSmall                  = 0xe434
+	PictureAsPdf                          = 0xe415
+	PictureInPicture                      = 0xe8aa
+	PictureInPictureAlt                   = 0xe911
+	PinDrop                               = 0xe55e
+	Place                                 = 0xe55f
+	PlayArrow                             = 0xe037
+	PlayCircleFilled                      = 0xe038
+	PlayCircleOutline                     = 0xe039
+	PlayForWork                           = 0xe906
+	PlaylistAdd                           = 0xe03b
+	PlaylistAddCheck                      = 0xe065
+	PlaylistPlay                          = 0xe05f
+	PlusOne                               = 0xe800
+	Poll                                  = 0xe801
+	Polymer                               = 0xe8ab
+	Pool                                  = 0xeb48
+	PortableWifiOff                       = 0xe0ce
+	Portrait                              = 0xe416
+	Power                                 = 0xe63c
+	PowerInput                            = 0xe336
+	PowerSettingsNew                      = 0xe8ac
+	PregnantWoman                         = 0xe91e
+	PresentToAll                          = 0xe0df
+	Print                                 = 0xe8ad
+	Public                                = 0xe80b
+	Publish                               = 0xe255
+	QueryBuilder                          = 0xe8ae
+	QuestionAnswer                        = 0xe8af
+	Queue                                 = 0xe03c
+	QueueMusic                            = 0xe03d
+	QueuePlayNext                         = 0xe066
+	Radio                                 = 0xe03e
+	RadioButtonChecked                    = 0xe837
+	RadioButtonUnchecked                  = 0xe836
+	RateReview                            = 0xe560
+	Receipt                               = 0xe8b0
+	RecentActors                          = 0xe03f
+	RecordVoiceOver                       = 0xe91f
+	Redeem                                = 0xe8b1
+	Redo                                  = 0xe15a
+	Refresh                               = 0xe5d5
+	Remove                                = 0xe15b
+	RemoveCircle                          = 0xe15c
+	RemoveCircleOutline                   = 0xe15d
+	RemoveFromQueue                       = 0xe067
+	RemoveRedEye                          = 0xe417
+	Reorder                               = 0xe8fe
+	Repeat                                = 0xe040
+	RepeatOne                             = 0xe041
+	Replay                                = 0xe042
+	Replay10                              = 0xe059
+	Replay30                              = 0xe05a
+	Replay5                               = 0xe05b
+	Reply                                 = 0xe15e
+	ReplyAll                              = 0xe15f
+	Report                                = 0xe160
+	ReportProblem                         = 0xe8b2
+	RestaurantMenu                        = 0xe561
+	Restore                               = 0xe8b3
+	RingVolume                            = 0xe0d1
+	Room                                  = 0xe8b4
+	RoomService                           = 0xeb49
+	Rotate90DegreesCcw                    = 0xe418
+	RotateLeft                            = 0xe419
+	RotateRight                           = 0xe41a
+	RoundedCorner                         = 0xe920
+	Router                                = 0xe328
+	Rowing                                = 0xe921
+	RvHookup                              = 0xe642
+	Satellite                             = 0xe562
+	Save                                  = 0xe161
+	Scanner                               = 0xe329
+	Schedule                              = 0xe8b5
+	School                                = 0xe80c
+	ScreenLockLandscape                   = 0xe1be
+	ScreenLockPortrait                    = 0xe1bf
+	ScreenLockRotation                    = 0xe1c0
+	ScreenRotation                        = 0xe1c1
+	ScreenShare                           = 0xe0e2
+	SdCard                                = 0xe623
+	SdStorage                             = 0xe1c2
+	Search                                = 0xe8b6
+	Security                              = 0xe32a
+	SelectAll                             = 0xe162
+	Send                                  = 0xe163
+	Settings                              = 0xe8b8
+	SettingsApplications                  = 0xe8b9
+	SettingsBackupRestore                 = 0xe8ba
+	SettingsBluetooth                     = 0xe8bb
+	SettingsBrightness                    = 0xe8bd
+	SettingsCell                          = 0xe8bc
+	SettingsEthernet                      = 0xe8be
+	SettingsInputAntenna                  = 0xe8bf
+	SettingsInputComponent                = 0xe8c0
+	SettingsInputComposite                = 0xe8c1
+	SettingsInputHdmi                     = 0xe8c2
+	SettingsInputSvideo                   = 0xe8c3
+	SettingsOverscan                      = 0xe8c4
+	SettingsPhone                         = 0xe8c5
+	SettingsPower                         = 0xe8c6
+	SettingsRemote                        = 0xe8c7
+	SettingsSystemDaydream                = 0xe1c3
+	SettingsVoice                         = 0xe8c8
+	Share                                 = 0xe80d
+	Shop                                  = 0xe8c9
+	ShopTwo                               = 0xe8ca
+	ShoppingBasket                        = 0xe8cb
+	ShoppingCart                          = 0xe8cc
+	ShortText                             = 0xe261
+	Shuffle                               = 0xe043
+	SignalCellular4Bar                    = 0xe1c8
+	SignalCellularConnectedNoInternet4Bar = 0xe1cd
+	SignalCellularNoSim                   = 0xe1ce
+	SignalCellularNull                    = 0xe1cf
+	SignalCellularOff                     = 0xe1d0
+	SignalWifi4Bar                        = 0xe1d8
+	SignalWifi4BarLock                    = 0xe1d9
+	SignalWifiOff                         = 0xe1da
+	SimCard                               = 0xe32b
+	SimCardAlert                          = 0xe624
+	SkipNext                              = 0xe044
+	SkipPrevious                          = 0xe045
+	Slideshow                             = 0xe41b
+	SlowMotionVideo                       = 0xe068
+	Smartphone                            = 0xe32c
+	SmokeFree                             = 0xeb4a
+	SmokingRooms                          = 0xeb4b
+	Sms                                   = 0xe625
+	SmsFailed                             = 0xe626
+	Snooze                                = 0xe046
+	Sort                                  = 0xe164
+	SortByAlpha                           = 0xe053
+	Spa                                   = 0xeb4c
+	SpaceBar                              = 0xe256
+	Speaker                               = 0xe32d
+	SpeakerGroup                          = 0xe32e
+	SpeakerNotes                          = 0xe8cd
+	SpeakerPhone                          = 0xe0d2
+	Spellcheck                            = 0xe8ce
+	Star                                  = 0xe838
+	StarBorder                            = 0xe83a
+	StarHalf                              = 0xe839
+	Stars                                 = 0xe8d0
+	StayCurrentLandscape                  = 0xe0d3
+	StayCurrentPortrait                   = 0xe0d4
+	StayPrimaryLandscape                  = 0xe0d5
+	StayPrimaryPortrait                   = 0xe0d6
+	Stop                                  = 0xe047
+	StopScreenShare                       = 0xe0e3
+	Storage                               = 0xe1db
+	Store                                 = 0xe8d1
+	StoreMallDirectory                    = 0xe563
+	Straighten                            = 0xe41c
+	StrikethroughS                        = 0xe257
+	Style                                 = 0xe41d
+	SubdirectoryArrowLeft                 = 0xe5d9
+	SubdirectoryArrowRight                = 0xe5da
+	Subject                               = 0xe8d2
+	Subscriptions                         = 0xe064
+	Subtitles                             = 0xe048
+	SupervisorAccount                     = 0xe8d3
+	SurroundSound                         = 0xe049
+	SwapCalls                             = 0xe0d7
+	SwapHoriz                             = 0xe8d4
+	SwapVert                              = 0xe8d5
+	SwapVerticalCircle                    = 0xe8d6
+	SwitchCamera                          = 0xe41e
+	SwitchVideo                           = 0xe41f
+	Sync                                  = 0xe627
+	SyncDisabled                          = 0xe628
+	SyncProblem                           = 0xe629
+	SystemUpdate                          = 0xe62a
+	SystemUpdateAlt                       = 0xe8d7
+	Tab                                   = 0xe8d8
+	TabUnselected                         = 0xe8d9
+	Tablet                                = 0xe32f
+	TabletAndroid                         = 0xe330
+	TabletMac                             = 0xe331
+	TagFaces                              = 0xe420
+	TapAndPlay                            = 0xe62b
+	Terrain                               = 0xe564
+	TextFields                            = 0xe262
+	TextFormat                            = 0xe165
+	Textsms                               = 0xe0d8
+	Texture                               = 0xe421
+	Theaters                              = 0xe8da
+	ThumbDown                             = 0xe8db
+	ThumbUp                               = 0xe8dc
+	ThumbsUpDown                          = 0xe8dd
+	TimeToLeave                           = 0xe62c
+	Timelapse                             = 0xe422
+	Timeline                              = 0xe922
+	Timer                                 = 0xe425
+	Timer10                               = 0xe423
+	Timer3                                = 0xe424
+	TimerOff                              = 0xe426
+	Toc                                   = 0xe8de
+	Today                                 = 0xe8df
+	Toll                                  = 0xe8e0
+	Tonality                              = 0xe427
+	TouchApp                              = 0xe913
+	Toys                                  = 0xe332
+	TrackChanges                          = 0xe8e1
+	Traffic                               = 0xe565
+	Transform                             = 0xe428
+	Translate                             = 0xe8e2
+	TrendingDown                          = 0xe8e3
+	TrendingFlat                          = 0xe8e4
+	TrendingUp                            = 0xe8e5
+	Tune                                  = 0xe429
+	TurnedIn                              = 0xe8e6
+	TurnedInNot                           = 0xe8e7
+	Tv                                    = 0xe333
+	Unarchive                             = 0xe169
+	Undo                                  = 0xe166
+	UnfoldLess                            = 0xe5d6
+	UnfoldMore                            = 0xe5d7
+	Update                                = 0xe923
+	Usb                                   = 0xe1e0
+	VerifiedUser                          = 0xe8e8
+	VerticalAlignBottom                   = 0xe258
+	VerticalAlignCenter                   = 0xe259
+	VerticalAlignTop                      = 0xe25a
+	Vibration                             = 0xe62d
+	VideoLibrary                          = 0xe04a
+	Videocam                              = 0xe04b
+	VideocamOff                           = 0xe04c
+	VideogameAsset                        = 0xe338
+	ViewAgenda                            = 0xe8e9
+	ViewArray                             = 0xe8ea
+	ViewCarousel                          = 0xe8eb
+	ViewColumn                            = 0xe8ec
+	ViewComfy                             = 0xe42a
+	ViewCompact                           = 0xe42b
+	ViewDay                               = 0xe8ed
+	ViewHeadline                          = 0xe8ee
+	ViewList                              = 0xe8ef
+	ViewModule                            = 0xe8f0
+	ViewQuilt                             = 0xe8f1
+	ViewStream                            = 0xe8f2
+	ViewWeek                              = 0xe8f3
+	Vignette                              = 0xe435
+	Visibility                            = 0xe8f4
+	VisibilityOff                         = 0xe8f5
+	VoiceChat                             = 0xe62e
+	Voicemail                             = 0xe0d9
+	VolumeDown                            = 0xe04d
+	VolumeMute                            = 0xe04e
+	VolumeOff                             = 0xe04f
+	VolumeUp                              = 0xe050
+	VpnKey                                = 0xe0da
+	VpnLock                               = 0xe62f
+	Wallpaper                             = 0xe1bc
+	Warning                               = 0xe002
+	Watch                                 = 0xe334
+	WatchLater                            = 0xe924
+	WbAuto                                = 0xe42c
+	WbCloudy                              = 0xe42d
+	WbIncandescent                        = 0xe42e
+	WbIridescent                          = 0xe436
+	WbSunny                               = 0xe430
+	Wc                                    = 0xe63d
+	Web                                   = 0xe051
+	WebAsset                              = 0xe069
+	Weekend                               = 0xe16b
+	Whatshot                              = 0xe80e
+	Widgets                               = 0xe1bd
+	Wifi                                  = 0xe63e
+	WifiLock                              = 0xe1e1
+	WifiTethering                         = 0xe1e2
+	Work                                  = 0xe8f9
+	WrapText                              = 0xe25b
+	YoutubeSearchedFor                    = 0xe8fa
+	ZoomIn                                = 0xe8ff
+	ZoomOut                               = 0xe900
+	ZoomOutMap                            = 0xe56b
+)

+ 259 - 0
gui/button.go

@@ -0,0 +1,259 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+	"github.com/g3n/engine/math32"
+	"github.com/g3n/engine/window"
+)
+
+/***************************************
+
+ Button Panel
+ +-------------------------------+
+ |  Image/Icon      Label        |
+ |  +----------+   +----------+  |
+ |  |          |   |          |  |
+ |  |          |   |          |  |
+ |  +----------+   +----------+  |
+ +-------------------------------+
+
+****************************************/
+
+type Button struct {
+	*Panel                  // Embedded Panel
+	Label     *Label        // Label panel
+	image     *Image        // pointer to button image (may be nil)
+	icon      *Label        // pointer to button icon (may be nil
+	styles    *ButtonStyles // pointer to current button styles
+	mouseOver bool          // true if mouse is over button
+	pressed   bool          // true if button is pressed
+}
+
+// Button style
+type ButtonStyle struct {
+	Border      BorderSizes
+	Paddings    BorderSizes
+	BorderColor math32.Color4
+	BgColor     math32.Color
+	FgColor     math32.Color
+}
+
+// All Button styles
+type ButtonStyles struct {
+	Normal   ButtonStyle
+	Over     ButtonStyle
+	Focus    ButtonStyle
+	Pressed  ButtonStyle
+	Disabled ButtonStyle
+}
+
+// NewButton creates and returns a pointer to a new button widget
+// with the specified text for the button label.
+func NewButton(text string) *Button {
+
+	b := new(Button)
+	b.styles = &StyleDefault.Button
+
+	// Initializes the button panel
+	b.Panel = NewPanel(0, 0)
+
+	// Subscribe to panel events
+	b.Panel.Subscribe(OnKeyDown, b.onKey)
+	b.Panel.Subscribe(OnKeyUp, b.onKey)
+	b.Panel.Subscribe(OnMouseUp, b.onMouse)
+	b.Panel.Subscribe(OnMouseDown, b.onMouse)
+	b.Panel.Subscribe(OnCursorEnter, b.onCursor)
+	b.Panel.Subscribe(OnCursorLeave, b.onCursor)
+	b.Panel.Subscribe(OnEnable, func(name string, ev interface{}) { b.update() })
+	b.Panel.Subscribe(OnResize, func(name string, ev interface{}) { b.recalc() })
+
+	// Creates label
+	b.Label = NewLabel(text)
+	b.Label.Subscribe(OnResize, func(name string, ev interface{}) { b.recalc() })
+	b.Panel.Add(b.Label)
+
+	b.recalc() // recalc first then update!
+	b.update()
+	return b
+}
+
+// SetIcon sets the button icon from the default Icon font.
+// If there is currently a selected image, it is removed
+func (b *Button) SetIcon(icode int) {
+
+	ico := NewIconLabel(string(icode))
+	if b.image != nil {
+		b.Panel.Remove(b.image)
+		b.image = nil
+	}
+	if b.icon != nil {
+		b.Panel.Remove(b.icon)
+	}
+	b.icon = ico
+	b.icon.SetFontSize(b.Label.FontSize() * 1.4)
+	b.Panel.Add(b.icon)
+
+	b.recalc()
+	b.update()
+}
+
+// SetImage sets the button left image from the specified filename
+// If there is currently a selected icon, it is removed
+func (b *Button) SetImage(imgfile string) error {
+
+	img, err := NewImage(imgfile)
+	if err != nil {
+		return err
+	}
+	if b.image != nil {
+		b.Panel.Remove(b.image)
+	}
+	b.image = img
+	b.Panel.Add(b.image)
+	b.recalc()
+	return nil
+}
+
+// SetStyles set the button styles overriding the default style
+func (b *Button) SetStyles(bs *ButtonStyles) {
+
+	b.styles = bs
+	b.update()
+}
+
+// onCursor process subscribed cursor events
+func (b *Button) onCursor(evname string, ev interface{}) {
+
+	switch evname {
+	case OnCursorEnter:
+		b.mouseOver = true
+		b.update()
+		b.root.StopPropagation(Stop3D)
+	case OnCursorLeave:
+		b.pressed = false
+		b.mouseOver = false
+		b.update()
+	default:
+		return
+	}
+}
+
+// onMouseEvent process subscribed mouse events
+func (b *Button) onMouse(evname string, ev interface{}) {
+
+	switch evname {
+	case OnMouseDown:
+		b.root.SetKeyFocus(b)
+		b.pressed = true
+		b.update()
+		b.Dispatch(OnClick, nil)
+	case OnMouseUp:
+		b.pressed = false
+		b.update()
+	default:
+		return
+	}
+	b.root.StopPropagation(Stop3D)
+}
+
+// onKey processes subscribed key events
+func (b *Button) onKey(evname string, ev interface{}) {
+
+	kev := ev.(*window.KeyEvent)
+	if evname == OnKeyDown && kev.Keycode == window.KeyEnter {
+		b.pressed = true
+		b.update()
+		b.Dispatch(OnClick, nil)
+		b.root.StopPropagation(Stop3D)
+		return
+	}
+	if evname == OnKeyUp && kev.Keycode == window.KeyEnter {
+		b.pressed = false
+		b.update()
+		b.root.StopPropagation(Stop3D)
+		return
+	}
+	return
+}
+
+// update updates the button visual state
+func (b *Button) update() {
+
+	if !b.Enabled() {
+		b.applyStyle(&b.styles.Disabled)
+		return
+	}
+	if b.pressed {
+		b.applyStyle(&b.styles.Pressed)
+		return
+	}
+	if b.mouseOver {
+		b.applyStyle(&b.styles.Over)
+		return
+	}
+	b.applyStyle(&b.styles.Normal)
+}
+
+// applyStyle applies the specified button style
+func (b *Button) applyStyle(bs *ButtonStyle) {
+
+	b.SetBordersColor4(&bs.BorderColor)
+	b.SetBordersFrom(&bs.Border)
+	b.SetPaddingsFrom(&bs.Paddings)
+	b.SetColor(&bs.BgColor)
+	if b.icon != nil {
+		b.icon.SetColor(&bs.FgColor)
+	}
+	//b.Label.SetColor(&bs.FgColor)
+}
+
+// recalc recalculates all dimensions and position from inside out
+func (b *Button) recalc() {
+
+	// Current width and height of button content area
+	width := b.Panel.ContentWidth()
+	height := b.Panel.ContentHeight()
+
+	// Image or icon width
+	imgWidth := float32(0)
+	if b.image != nil {
+		imgWidth = b.image.Width()
+	} else if b.icon != nil {
+		imgWidth = b.icon.Width()
+	}
+
+	// Sets new content width and height if necessary
+	spacing := float32(4)
+	minWidth := imgWidth + spacing + b.Label.Width()
+	minHeight := b.Label.Height()
+	resize := false
+	if width < minWidth {
+		width = minWidth
+		resize = true
+	}
+	if height < minHeight {
+		height = minHeight
+		resize = true
+	}
+	if resize {
+		b.SetContentSize(width, height)
+	}
+
+	// Centralize horizontally
+	px := (width - minWidth) / 2
+
+	// Set label position
+	ly := (height - b.Label.Height()) / 2
+	b.Label.SetPosition(px+imgWidth+spacing, ly)
+
+	// Image/icon position
+	if b.image != nil {
+		iy := (height - b.image.height) / 2
+		b.image.SetPosition(px, iy)
+	} else if b.icon != nil {
+		b.icon.SetPosition(px, ly)
+	}
+}

+ 276 - 0
gui/checkradio.go

@@ -0,0 +1,276 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+	"github.com/g3n/engine/gui/assets"
+	"github.com/g3n/engine/math32"
+	"github.com/g3n/engine/window"
+)
+
+const (
+	checkON  = string(assets.CheckBox)
+	checkOFF = string(assets.CheckBoxOutlineBlank)
+	radioON  = string(assets.RadioButtonChecked)
+	radioOFF = string(assets.RadioButtonUnchecked)
+)
+
+type CheckRadio struct {
+	Panel             // Embedded panel
+	Label      *Label // Text label
+	icon       *Label
+	styles     *CheckRadioStyles
+	check      bool
+	group      string
+	cursorOver bool
+	state      bool
+	codeON     string
+	codeOFF    string
+}
+
+type CheckRadioStyle struct {
+	Border      BorderSizes
+	Paddings    BorderSizes
+	BorderColor math32.Color4
+	BgColor     math32.Color4
+	FgColor     math32.Color
+}
+
+type CheckRadioStyles struct {
+	Normal   CheckRadioStyle
+	Over     CheckRadioStyle
+	Focus    CheckRadioStyle
+	Disabled CheckRadioStyle
+}
+
+// NewCheckBox creates and returns a pointer to a new CheckBox widget
+// with the specified text
+func NewCheckBox(text string) *CheckRadio {
+
+	return newCheckRadio(true, text)
+}
+
+// NewRadioButton creates and returns a pointer to a new RadioButton widget
+// with the specified text
+func NewRadioButton(text string) *CheckRadio {
+
+	return newCheckRadio(false, text)
+}
+
+// newCheckRadio creates and returns a pointer to a new CheckRadio widget
+// with the specified type and text
+func newCheckRadio(check bool, text string) *CheckRadio {
+
+	cb := new(CheckRadio)
+	cb.styles = &StyleDefault.CheckRadio
+
+	// Adapts to specified type: CheckBox or RadioButton
+	cb.check = check
+	cb.state = false
+	if cb.check {
+		cb.codeON = checkON
+		cb.codeOFF = checkOFF
+	} else {
+		cb.codeON = radioON
+		cb.codeOFF = radioOFF
+	}
+
+	// Initialize panel
+	cb.Panel.Initialize(0, 0)
+
+	// Subscribe to events
+	cb.Panel.Subscribe(OnKeyDown, cb.onKey)
+	cb.Panel.Subscribe(OnCursorEnter, cb.onCursor)
+	cb.Panel.Subscribe(OnCursorLeave, cb.onCursor)
+	cb.Panel.Subscribe(OnMouseDown, cb.onMouse)
+	cb.Panel.Subscribe(OnEnable, func(evname string, ev interface{}) { cb.update() })
+
+	// Creates label
+	cb.Label = NewLabel(text)
+	cb.Label.Subscribe(OnResize, func(evname string, ev interface{}) { cb.recalc() })
+	cb.Panel.Add(cb.Label)
+
+	// Creates icon label
+	cb.icon = NewIconLabel(" ")
+	cb.Panel.Add(cb.icon)
+
+	cb.recalc()
+	cb.update()
+	return cb
+}
+
+// SetRoot overrides the IPanel.SetRoot method
+func (cb *CheckRadio) SetRoot(root *Root) {
+
+	if cb.root == root {
+		return
+	}
+	cb.root = root
+	// Subscribes once to this root panel OnRadioGroup events
+	root.Subscribe(OnRadioGroup, func(name string, ev interface{}) {
+		cb.onRadioGroup(ev.(*CheckRadio))
+	})
+}
+
+// Value returns the current state of the checkbox
+func (cb *CheckRadio) Value() bool {
+
+	return cb.state
+}
+
+// SetValue sets the current state of the checkbox
+func (cb *CheckRadio) SetValue(state bool) *CheckRadio {
+
+	if state == cb.state {
+		return cb
+	}
+	cb.state = state
+	cb.update()
+	return cb
+}
+
+// Group returns the name of the radio group
+func (cb *CheckRadio) Group() string {
+
+	return cb.group
+}
+
+// SetGroup sets the name of the radio group
+func (cb *CheckRadio) SetGroup(group string) {
+
+	cb.group = group
+}
+
+// SetStyles set the button styles overriding the default style
+func (cb *CheckRadio) SetStyles(bs *CheckRadioStyles) {
+
+	cb.styles = bs
+	cb.update()
+}
+
+// toggleState toggles the current state of the checkbox/radiobutton
+func (cb *CheckRadio) toggleState() {
+
+	if cb.check {
+		cb.state = !cb.state
+	} else {
+		if len(cb.group) == 0 {
+			cb.state = !cb.state
+		} else {
+			if cb.state {
+				return
+			}
+			cb.state = !cb.state
+		}
+	}
+	cb.update()
+	cb.Dispatch(OnChange, nil)
+	if !cb.check && len(cb.group) > 0 {
+		cb.root.Dispatch(OnRadioGroup, cb)
+	}
+}
+
+// onMouse process OnMouseDown events
+func (cb *CheckRadio) onMouse(evname string, ev interface{}) {
+
+	cb.root.SetKeyFocus(cb)
+	cb.root.StopPropagation(Stop3D)
+	cb.toggleState()
+	// Dispatch OnClick for left mouse button down
+	if evname == OnMouseDown {
+		mev := ev.(*window.MouseEvent)
+		if mev.Button == window.MouseButtonLeft {
+			cb.Dispatch(OnClick, nil)
+		}
+	}
+}
+
+// onCursor process OnCursor* events
+func (cb *CheckRadio) onCursor(evname string, ev interface{}) {
+
+	if evname == OnCursorEnter {
+		cb.cursorOver = true
+	} else {
+		cb.cursorOver = false
+	}
+	cb.update()
+}
+
+// onKey receives subscribed key events
+func (cb *CheckRadio) onKey(evname string, ev interface{}) {
+
+	kev := ev.(*window.KeyEvent)
+	if evname == OnKeyDown && kev.Keycode == window.KeyEnter {
+		cb.toggleState()
+		cb.update()
+		cb.Dispatch(OnClick, nil)
+		cb.root.StopPropagation(Stop3D)
+		return
+	}
+	return
+}
+
+// onRadioGroup receives subscriber OnRadioGroup events
+func (cb *CheckRadio) onRadioGroup(other *CheckRadio) {
+
+	// If event is for this button, ignore
+	if cb == other {
+		return
+	}
+	// If other radio group is not the group of this button, ignore
+	if cb.group != other.group {
+		return
+	}
+	// Toggle this button state
+	cb.SetValue(!other.Value())
+}
+
+// update updates the visual appearance of the checkbox
+func (cb *CheckRadio) update() {
+
+	if cb.state {
+		cb.icon.SetText(cb.codeON)
+	} else {
+		cb.icon.SetText(cb.codeOFF)
+	}
+
+	if !cb.Enabled() {
+		cb.applyStyle(&cb.styles.Disabled)
+		return
+	}
+	if cb.cursorOver {
+		cb.applyStyle(&cb.styles.Over)
+		return
+	}
+	cb.applyStyle(&cb.styles.Normal)
+}
+
+// setStyle sets the specified checkradio style
+func (cb *CheckRadio) applyStyle(s *CheckRadioStyle) {
+
+	cb.Panel.SetBordersColor4(&s.BorderColor)
+	cb.Panel.SetBordersFrom(&s.Border)
+	cb.Panel.SetPaddingsFrom(&s.Paddings)
+	cb.Panel.SetColor4(&s.BgColor)
+
+	cb.icon.SetColor(&s.FgColor)
+	cb.Label.SetColor(&s.FgColor)
+}
+
+// recalc recalculates dimensions and position from inside out
+func (cb *CheckRadio) recalc() {
+
+	// Sets icon position
+	cb.icon.SetFontSize(cb.Label.FontSize() * 1.3)
+	cb.icon.SetPosition(0, 0)
+
+	// Label position
+	spacing := float32(4)
+	cb.Label.SetPosition(cb.icon.Width()+spacing, 0)
+
+	// Content width
+	width := cb.icon.Width() + spacing + cb.Label.Width()
+	cb.SetContentSize(width, cb.Label.Height())
+}

+ 119 - 0
gui/control_folder.go

@@ -0,0 +1,119 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+	"fmt"
+)
+
+type ControlFolder struct {
+	Folder                       // Embedded folder
+	tree    Tree                 // control tree
+	styles  *ControlFolderStyles // Pointer to styles
+	current interface{}
+}
+
+type ControlFolderStyles struct {
+	Folder *FolderStyles
+	Tree   *TreeStyles
+}
+
+type ControlFolderGroup struct {
+	control *ControlFolder
+	node    *TreeNode
+}
+
+// NewControlFolder creates and returns a pointer to a new control folder widget
+// with the specified text and initial width
+func NewControlFolder(text string, width float32) *ControlFolder {
+
+	f := new(ControlFolder)
+	f.Initialize(text, width)
+	return f
+}
+
+// Initialize initializes the control folder with the specified text and initial width
+// It is normally used when the control folder is embedded in another object
+func (f *ControlFolder) Initialize(text string, width float32) {
+
+	f.styles = &StyleDefault.ControlFolder
+	f.tree.Initialize(width, width)
+	f.tree.SetStyles(f.styles.Tree)
+	f.tree.SetAutoHeight(600)
+	f.tree.SetAutoWidth(400)
+
+	f.Folder.Initialize(text, width, &f.tree)
+	f.Folder.SetStyles(f.styles.Folder)
+	f.Folder.SetAlignRight(false)
+}
+
+func (f *ControlFolder) Clear() {
+
+	f.tree.Clear()
+}
+
+func (f *ControlFolder) AddCheckBox(text string) *CheckRadio {
+
+	cb := NewCheckBox(text)
+	f.tree.Add(cb)
+	return cb
+}
+
+func (f *ControlFolder) AddSlider(text string, sf, v float32) *Slider {
+
+	cont, slider := f.newSlider(text, sf, v)
+	f.tree.Add(cont)
+	return slider
+}
+
+func (f *ControlFolder) AddGroup(text string) *ControlFolderGroup {
+
+	g := new(ControlFolderGroup)
+	g.control = f
+	g.node = f.tree.AddNode(text)
+	return g
+}
+
+func (g *ControlFolderGroup) AddCheckBox(text string) *CheckRadio {
+
+	cb := NewCheckBox(text)
+	g.node.Add(cb)
+	return cb
+}
+
+func (g *ControlFolderGroup) AddSlider(text string, sf, v float32) *Slider {
+
+	cont, slider := g.control.newSlider(text, sf, v)
+	g.node.Add(cont)
+	return slider
+}
+
+func (f *ControlFolder) newSlider(text string, sf, value float32) (IPanel, *Slider) {
+
+	// Creates container panel for the label and slider
+	cont := NewPanel(200, 32)
+	hbox := NewHBoxLayout()
+	hbox.spacing = 4
+	cont.SetLayout(hbox)
+
+	// Adds label
+	l := NewImageLabel(text)
+	l.SetLayoutParams(&HBoxLayoutParams{AlignV: AlignCenter})
+	cont.Add(l)
+
+	// Adds slider
+	s := NewHSlider(100, l.Height())
+	s.SetScaleFactor(sf)
+	s.SetScaleFactor(sf)
+	s.SetValue(value)
+	s.SetText(fmt.Sprintf("%1.1f", value))
+	s.Subscribe(OnChange, func(evname string, ev interface{}) {
+		s.SetText(fmt.Sprintf("%1.1f", s.Value()))
+	})
+	s.SetLayoutParams(&HBoxLayoutParams{AlignV: AlignCenter, Expand: 1})
+	cont.Add(s)
+
+	return cont, s
+}

+ 7 - 0
gui/doc.go

@@ -0,0 +1,7 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package gui ...
+package gui
+

+ 89 - 0
gui/docklayout.go

@@ -0,0 +1,89 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+type DockLayout struct {
+}
+
+type DockLayoutParams struct {
+	Edge int
+}
+
+const (
+	DockTop = iota + 1
+	DockRight
+	DockBottom
+	DockLeft
+	DockCenter
+)
+
+func NewDockLayout() *DockLayout {
+
+	return new(DockLayout)
+}
+
+func (dl *DockLayout) Recalc(ipan IPanel) {
+
+	pan := ipan.GetPanel()
+	width := pan.Width()
+	topY := float32(0)
+	bottomY := pan.Height()
+	leftX := float32(0)
+	rightX := width
+
+	// Top and bottom first
+	for _, iobj := range pan.Children() {
+		child := iobj.(IPanel).GetPanel()
+		if child.layoutParams == nil {
+			continue
+		}
+		params := child.layoutParams.(*DockLayoutParams)
+		if params.Edge == DockTop {
+			child.SetPosition(0, topY)
+			topY += child.Height()
+			child.SetWidth(width)
+			continue
+		}
+		if params.Edge == DockBottom {
+			child.SetPosition(0, bottomY-child.Height())
+			bottomY -= child.Height()
+			child.SetWidth(width)
+			continue
+		}
+	}
+	// Left and right
+	for _, iobj := range pan.Children() {
+		child := iobj.(IPanel).GetPanel()
+		if child.layoutParams == nil {
+			continue
+		}
+		params := child.layoutParams.(*DockLayoutParams)
+		if params.Edge == DockLeft {
+			child.SetPosition(leftX, topY)
+			leftX += child.Width()
+			child.SetHeight(bottomY - topY)
+			continue
+		}
+		if params.Edge == DockRight {
+			child.SetPosition(rightX-child.Width(), topY)
+			rightX -= child.Width()
+			child.SetHeight(bottomY - topY)
+			continue
+		}
+	}
+	// Center (only the first found)
+	for _, iobj := range pan.Children() {
+		child := iobj.(IPanel).GetPanel()
+		if child.layoutParams == nil {
+			continue
+		}
+		params := child.layoutParams.(*DockLayoutParams)
+		if params.Edge == DockCenter {
+			child.SetPosition(leftX, topY)
+			child.SetSize(rightX-leftX, bottomY-topY)
+			break
+		}
+	}
+}

+ 280 - 0
gui/dropdown.go

@@ -0,0 +1,280 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+	"github.com/g3n/engine/gui/assets"
+	"github.com/g3n/engine/math32"
+	"github.com/g3n/engine/window"
+)
+
+type DropDown struct {
+	Panel                        // Embedded panel
+	icon         *Label          // internal label with icon
+	list         *List           // internal list
+	styles       *DropDownStyles // pointer to dropdown styles
+	litem        *ImageLabel     // Item shown in drop box (copy of selected)
+	selItem      *ImageLabel     // selected item from list
+	overDropdown bool
+	overList     bool
+	focus        bool
+	clickOut     bool
+}
+
+// DropDown list style
+type DropDownStyle struct {
+	Border      BorderSizes
+	Paddings    BorderSizes
+	BorderColor math32.Color4
+	BgColor     math32.Color
+	FgColor     math32.Color
+}
+
+// DropDown list styles
+type DropDownStyles struct {
+	Normal   *DropDownStyle
+	Over     *DropDownStyle
+	Focus    *DropDownStyle
+	Disabled *DropDownStyle
+}
+
+// NewDropDown creates and returns a pointer to a new drop down widget with the specified width.
+func NewDropDown(width float32, item *ImageLabel) *DropDown {
+
+	dd := new(DropDown)
+	dd.styles = &StyleDefault.DropDown
+	dd.litem = item
+
+	dd.Panel.Initialize(width, 0)
+	dd.Panel.Subscribe(OnKeyDown, dd.onKeyEvent)
+	dd.Panel.Subscribe(OnMouseDown, dd.onMouse)
+	dd.Panel.Subscribe(OnCursorEnter, dd.onCursor)
+	dd.Panel.Subscribe(OnCursorLeave, dd.onCursor)
+
+	// ListItem
+	dd.Panel.Add(dd.litem)
+
+	// Create icon
+	dd.icon = NewIconLabel(" ")
+	dd.icon.SetFontSize(StyleDefault.Font.Size() * 1.3)
+	dd.icon.SetText(string(assets.ArrowDropDown))
+	dd.Panel.Add(dd.icon)
+
+	/// Create list
+	dd.list = NewVList(0, 0)
+	dd.list.bounded = false
+	dd.list.dropdown = true
+	dd.list.SetVisible(false)
+
+	dd.list.Subscribe(OnMouseDown, dd.onListMouse)
+	dd.list.Subscribe(OnMouseOut, dd.onListMouse)
+	dd.list.Subscribe(OnChange, dd.onListChangeEvent)
+	dd.Panel.Add(dd.list)
+
+	dd.update()
+	dd.recalc()
+	return dd
+}
+
+// Add add a list item at the end of the list
+func (dd *DropDown) Add(item *ImageLabel) {
+
+	dd.list.Add(item)
+}
+
+// InsertAt inserts a list item at the specified position
+// Returs true if the item was successfuly inserted
+func (dd *DropDown) InsertAt(pos int, item *ImageLabel) {
+
+	dd.list.InsertAt(pos, item)
+}
+
+// RemoveAt removes the list item from the specified position
+// Returs true if the item was successfuly removed
+func (dd *DropDown) RemoveAt(pos int) {
+
+	dd.list.RemoveAt(pos)
+}
+
+// ItemAt returns the list item at the specified position
+func (dd *DropDown) ItemAt(pos int) *ImageLabel {
+
+	return dd.list.ItemAt(pos).(*ImageLabel)
+}
+
+// Returns the currently selected item or nil if not item
+// was selected
+func (dd *DropDown) Selected() *ImageLabel {
+
+	return dd.selItem
+}
+
+// SetSelected sets the selected item
+func (dd *DropDown) SetSelected(item *ImageLabel) {
+
+	dd.list.SetSelected(item, true)
+}
+
+// SelectPos selects the item at the specified position
+func (dd *DropDown) SelectPos(pos int) {
+
+	dd.list.SelectPos(pos, true)
+}
+
+// onKeyEvent is called when key event is received when this dropdown has the key focus.
+func (dd *DropDown) onKeyEvent(evname string, ev interface{}) {
+
+	kev := ev.(*window.KeyEvent)
+	switch kev.Keycode {
+	case window.KeyF1:
+		if dd.list.Visible() {
+			dd.list.SetVisible(false)
+		}
+	default:
+		return
+	}
+}
+
+// onMouse receives subscribed mouse events over the dropdown
+func (dd *DropDown) onMouse(evname string, ev interface{}) {
+
+	if evname == OnMouseDown {
+		// If clickOut list already closed
+		if dd.clickOut {
+			dd.clickOut = false
+			return
+		}
+		dd.list.SetVisible(true)
+		dd.root.SetKeyFocus(dd.list)
+		return
+	}
+}
+
+// onCursor receives subscribed cursor events over the dropdown
+func (dd *DropDown) onCursor(evname string, ev interface{}) {
+
+	if evname == OnCursorEnter {
+		dd.overDropdown = true
+		dd.update()
+		return
+	}
+	if evname == OnCursorLeave {
+		dd.overDropdown = false
+		dd.update()
+		return
+	}
+}
+
+// onListMouseEvent receives mouse events over the list
+func (dd *DropDown) onListMouse(evname string, ev interface{}) {
+
+	mev := ev.(*window.MouseEvent)
+	// List was clicked
+	if evname == OnMouseDown {
+		// If click occurred inside the list scrollbar ignore it
+		if dd.list.vscroll != nil {
+			if dd.list.vscroll.ContainsPosition(mev.Xpos, mev.Ypos) {
+				return
+			}
+		}
+		// Otherwise, closes the list
+		dd.list.SetVisible(false)
+		//dd.copySelected()
+		dd.overList = false
+		dd.update()
+		return
+	}
+	// Hide list when clicked out
+	if evname == OnMouseOut {
+		if dd.list.Visible() {
+			dd.list.SetVisible(false)
+		}
+		// If list clickout occurred inside the dropdown, set 'clickOut' to
+		// indicate that the list was already closed
+		if dd.Panel.ContainsPosition(mev.Xpos, mev.Ypos) {
+			dd.clickOut = true
+		}
+	}
+}
+
+// onListCursor receives subscribed events over the list
+func (dd *DropDown) onListCursor(evname string, ev interface{}) {
+
+	if evname == OnCursorEnter {
+		dd.overList = true
+		dd.update()
+		return
+	}
+	if evname == OnCursorLeave {
+		dd.overList = false
+		dd.update()
+		return
+	}
+
+}
+
+// copySelected copy to the dropdown panel the selected item
+// from the list.
+func (dd *DropDown) copySelected() {
+
+	dd.selItem = dd.list.Selected()[0].(*ImageLabel)
+	dd.litem.CopyFields(dd.selItem)
+	dd.recalc()
+	dd.Dispatch(OnChange, nil)
+}
+
+// onListChangeEvent is called when an item in the list is selected
+func (dd *DropDown) onListChangeEvent(evname string, ev interface{}) {
+
+	dd.copySelected()
+}
+
+// recalc recalculates the dimensions and positions of the dropdown
+// panel, children and list
+func (dd *DropDown) recalc() {
+
+	// Dropdown icon position
+	posx := dd.Panel.ContentWidth() - dd.icon.Width()
+	dd.icon.SetPosition(posx, 0)
+
+	// List item position and width
+	ipan := dd.litem.GetPanel()
+	ipan.SetPosition(0, 0)
+	//ipan.SetWidth(posx)
+	height := ipan.Height()
+	dd.Panel.SetContentHeight(height)
+
+	// List position
+	dd.list.SetWidth(dd.Panel.Width())
+	dd.list.SetHeight(6*height + 1)
+	dd.list.SetPositionX(0)
+	dd.list.SetPositionY(dd.Panel.Height())
+}
+
+// update updates the visual state
+func (dd *DropDown) update() {
+
+	if dd.overDropdown || dd.overList {
+		dd.applyStyle(dd.styles.Over)
+		dd.list.ApplyStyle(OverStyle)
+		return
+	}
+	if dd.focus {
+		dd.applyStyle(dd.styles.Focus)
+		dd.list.ApplyStyle(FocusStyle)
+		return
+	}
+	dd.applyStyle(dd.styles.Normal)
+	dd.list.ApplyStyle(NormalStyle)
+}
+
+// applyStyle applies the specified style
+func (dd *DropDown) applyStyle(s *DropDownStyle) {
+
+	dd.SetBordersFrom(&s.Border)
+	dd.SetBordersColor4(&s.BorderColor)
+	dd.SetPaddingsFrom(&s.Paddings)
+	dd.SetColor(&s.BgColor)
+}

+ 340 - 0
gui/edit.go

@@ -0,0 +1,340 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+	"github.com/g3n/engine/math32"
+	"github.com/g3n/engine/text"
+	"github.com/g3n/engine/window"
+	"strings"
+	"time"
+)
+
+type Edit struct {
+	Label              // Embedded label
+	MaxLength   int    // Maximum number of characters
+	width       int    // edit width in pixels
+	placeHolder string // place holder string
+	text        string // current edit text
+	col         int    // current column
+	focus       bool   // key focus flag
+	cursorOver  bool
+	blinkID     int
+	caretOn     bool
+	styles      *EditStyles
+}
+
+type EditStyle struct {
+	Border      BorderSizes
+	Paddings    BorderSizes
+	BorderColor math32.Color4
+	BgColor     math32.Color
+	BgAlpha     float32
+	FgColor     math32.Color
+	HolderColor math32.Color
+}
+
+type EditStyles struct {
+	Normal   EditStyle
+	Over     EditStyle
+	Focus    EditStyle
+	Disabled EditStyle
+}
+
+const (
+	editMarginX = 4
+	blinkTime   = 1000
+)
+
+// NewEdit creates and returns a pointer to a new edit widget
+func NewEdit(width int, placeHolder string) *Edit {
+
+	ed := new(Edit)
+	ed.width = width
+	ed.placeHolder = placeHolder
+
+	ed.styles = &StyleDefault.Edit
+	ed.text = ""
+	ed.MaxLength = 80
+	ed.col = 0
+	ed.focus = false
+
+	ed.Label.initialize("", StyleDefault.Font)
+	ed.Label.Subscribe(OnKeyDown, ed.onKey)
+	ed.Label.Subscribe(OnChar, ed.onChar)
+	ed.Label.Subscribe(OnMouseDown, ed.onMouse)
+	ed.Label.Subscribe(OnCursorEnter, ed.onCursor)
+	ed.Label.Subscribe(OnCursorLeave, ed.onCursor)
+	ed.Label.Subscribe(OnEnable, func(evname string, ev interface{}) { ed.update() })
+
+	ed.update()
+	return ed
+}
+
+// SetText sets this edit text
+func (ed *Edit) SetText(text string) *Edit {
+
+	// Remove new lines from text
+	ed.text = strings.Replace(text, "\n", "", -1)
+	ed.update()
+	return ed
+}
+
+// Text returns the current edited text
+func (ed *Edit) Text() string {
+
+	return ed.text
+}
+
+// SetFontSize sets label font size (overrides Label.SetFontSize)
+func (ed *Edit) SetFontSize(size float64) *Edit {
+
+	ed.Label.fontSize = size
+	ed.redraw(ed.focus)
+	return ed
+}
+
+// SetStyles set the button styles overriding the default style
+func (ed *Edit) SetStyles(es *EditStyles) {
+
+	ed.styles = es
+	ed.update()
+}
+
+// LostKeyFocus satisfies the IPanel interface and is called by gui root
+// container when the panel loses the key focus
+func (ed *Edit) LostKeyFocus() {
+
+	ed.focus = false
+	ed.update()
+	ed.root.ClearTimeout(ed.blinkID)
+}
+
+// CursorPos sets the position of the cursor at the
+// specified  column if possible
+func (ed *Edit) CursorPos(col int) {
+
+	if col <= text.StrCount(ed.text) {
+		ed.col = col
+		ed.redraw(ed.focus)
+	}
+}
+
+// CursorLeft moves the edit cursor one character left if possible
+func (ed *Edit) CursorLeft() {
+
+	if ed.col > 0 {
+		ed.col--
+		ed.redraw(ed.focus)
+	}
+}
+
+// CursorRight moves the edit cursor one character right if possible
+func (ed *Edit) CursorRight() {
+
+	if ed.col < text.StrCount(ed.text) {
+		ed.col++
+		ed.redraw(ed.focus)
+	}
+}
+
+// CursorBack deletes the character at left of the cursor if possible
+func (ed *Edit) CursorBack() {
+
+	if ed.col > 0 {
+		ed.col--
+		ed.text = text.StrRemove(ed.text, ed.col)
+		ed.redraw(ed.focus)
+		ed.Dispatch(OnChange, nil)
+	}
+}
+
+// CursorHome moves the edit cursor to the beginning of the text
+func (ed *Edit) CursorHome() {
+
+	ed.col = 0
+	ed.redraw(ed.focus)
+}
+
+// CursorEnd moves the edit cursor to the end of the text
+func (ed *Edit) CursorEnd() {
+
+	ed.col = text.StrCount(ed.text)
+	ed.redraw(ed.focus)
+}
+
+// CursorDelete deletes the character at the right of the cursor if possible
+func (ed *Edit) CursorDelete() {
+
+	if ed.col < text.StrCount(ed.text) {
+		ed.text = text.StrRemove(ed.text, ed.col)
+		ed.redraw(ed.focus)
+		ed.Dispatch(OnChange, nil)
+	}
+}
+
+// CursorInput inserts the specified string at the current cursor position
+func (ed *Edit) CursorInput(s string) {
+
+	if text.StrCount(ed.text) >= ed.MaxLength {
+		return
+	}
+
+	// Set new text with included input
+	var newText string
+	if ed.col < text.StrCount(ed.text) {
+		newText = text.StrInsert(ed.text, s, ed.col)
+	} else {
+		newText = ed.text + s
+	}
+
+	// Checks if new text exceeds edit width
+	width, _ := ed.Label.font.MeasureText(newText)
+	if float32(width)+editMarginX+float32(1) >= ed.Label.ContentWidth() {
+		return
+	}
+
+	ed.text = newText
+	ed.col++
+
+	ed.Dispatch(OnChange, nil)
+	ed.redraw(ed.focus)
+}
+
+// redraw redraws the text showing the caret if specified
+func (ed *Edit) redraw(caret bool) {
+
+	line := 0
+	if !caret {
+		line = -1
+	}
+	ed.Label.setTextCaret(ed.text, editMarginX, ed.width, line, ed.col)
+}
+
+// onKey receives subscribed key events
+func (ed *Edit) onKey(evname string, ev interface{}) {
+
+	kev := ev.(*window.KeyEvent)
+	switch kev.Keycode {
+	case window.KeyLeft:
+		ed.CursorLeft()
+	case window.KeyRight:
+		ed.CursorRight()
+	case window.KeyHome:
+		ed.CursorHome()
+	case window.KeyEnd:
+		ed.CursorEnd()
+	case window.KeyBackspace:
+		ed.CursorBack()
+	case window.KeyDelete:
+		ed.CursorDelete()
+	default:
+		return
+	}
+	ed.root.StopPropagation(Stop3D)
+}
+
+// onChar receives subscribed char events
+func (ed *Edit) onChar(evname string, ev interface{}) {
+
+	cev := ev.(*window.CharEvent)
+	ed.CursorInput(string(cev.Char))
+}
+
+// onMouseEvent receives subscribed mouse down events
+func (ed *Edit) onMouse(evname string, ev interface{}) {
+
+	e := ev.(*window.MouseEvent)
+	if e.Button != window.MouseButtonLeft {
+		return
+	}
+
+	// Set key focus to this panel
+	ed.root.SetKeyFocus(ed)
+
+	// Find clicked column
+	var nchars int
+	for nchars = 1; nchars <= text.StrCount(ed.text); nchars++ {
+		width, _ := ed.Label.font.MeasureText(text.StrPrefix(ed.text, nchars))
+		posx := e.Xpos - ed.pospix.X
+		if posx < editMarginX+float32(width) {
+			break
+		}
+	}
+	if !ed.focus {
+		ed.focus = true
+		ed.blinkID = ed.root.SetInterval(750*time.Millisecond, nil, ed.blink)
+	}
+	ed.CursorPos(nchars - 1)
+	ed.root.StopPropagation(Stop3D)
+}
+
+// onCursor receives subscribed cursor events
+func (ed *Edit) onCursor(evname string, ev interface{}) {
+
+	if evname == OnCursorEnter {
+		ed.cursorOver = true
+		ed.update()
+		ed.root.StopPropagation(Stop3D)
+		return
+	}
+	if evname == OnCursorLeave {
+		ed.cursorOver = false
+		ed.update()
+		ed.root.StopPropagation(Stop3D)
+		return
+	}
+}
+
+// blink blinks the caret
+func (ed *Edit) blink(arg interface{}) {
+
+	if !ed.focus {
+		return
+	}
+	if !ed.caretOn {
+		ed.caretOn = true
+	} else {
+		ed.caretOn = false
+	}
+	ed.redraw(ed.caretOn)
+}
+
+// update updates the visual state
+func (ed *Edit) update() {
+
+	if !ed.Enabled() {
+		ed.applyStyle(&ed.styles.Disabled)
+		return
+	}
+	if ed.cursorOver {
+		ed.applyStyle(&ed.styles.Over)
+		return
+	}
+	if ed.focus {
+		ed.applyStyle(&ed.styles.Focus)
+		return
+	}
+	ed.applyStyle(&ed.styles.Normal)
+}
+
+// applyStyle applies the specified style
+func (ed *Edit) applyStyle(s *EditStyle) {
+
+	ed.SetBordersFrom(&s.Border)
+	ed.SetBordersColor4(&s.BorderColor)
+	ed.SetPaddingsFrom(&s.Paddings)
+	ed.Label.SetColor(&s.FgColor)
+	ed.Label.SetBgColor(&s.BgColor)
+	//ed.Label.SetBgAlpha(s.BgAlpha)
+
+	if !ed.focus && len(ed.text) == 0 && len(ed.placeHolder) > 0 {
+		ed.Label.SetColor(&s.HolderColor)
+		ed.Label.setTextCaret(ed.placeHolder, editMarginX, ed.width, -1, ed.col)
+	} else {
+		ed.Label.SetColor(&s.FgColor)
+		ed.redraw(ed.focus)
+	}
+}

+ 29 - 0
gui/events.go

@@ -0,0 +1,29 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+	"github.com/g3n/engine/window"
+)
+
+// Events
+const (
+	OnClick       = "gui.OnClick"       // Widget clicked by mouse or key
+	OnCursor      = window.OnCursor     // cursor (mouse) position events
+	OnCursorEnter = "gui.OnCursorEnter" // cursor enters the panel area
+	OnCursorLeave = "gui.OnCursorLeave" // cursor leaves the panel area
+	OnMouseDown   = window.OnMouseDown  // any mouse button is pressed
+	OnMouseUp     = window.OnMouseUp    // any mouse button is released
+	OnMouseOut    = "gui.OnMouseOut"    // mouse button pressed outside of the panel
+	OnKeyDown     = window.OnKeyDown    // key is pressed
+	OnKeyUp       = window.OnKeyUp      // key is released
+	OnChar        = window.OnChar       // key is pressed and has unicode
+	OnResize      = "gui.OnResize"      // panel size changed (no parameters)
+	OnEnable      = "gui.OnEnable"      // panel enabled state changed (no parameters)
+	OnChange      = "gui.OnChange"      // onChange is emitted by List, DropDownList, CheckBox and Edit
+	OnScroll      = window.OnScroll     // scroll event
+	OnChild       = "gui.OnChild"       // child added to or removed from panel
+	OnRadioGroup  = "gui.OnRadioGroup"  // radio button from a group changed state
+)

+ 37 - 0
gui/filllayout.go

@@ -0,0 +1,37 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+type FillLayout struct {
+	width  bool
+	height bool
+}
+
+// NewFillLayout creates and returns a pointer of a new fill layout
+func NewFillLayout(width, height bool) *FillLayout {
+
+	f := new(FillLayout)
+	f.width = width
+	f.height = height
+	return f
+}
+
+// Recalc is called by the panel which has this layout
+func (f *FillLayout) Recalc(ipan IPanel) {
+
+	parent := ipan.GetPanel()
+	children := parent.Children()
+	if len(children) == 0 {
+		return
+	}
+	child := children[0].(IPanel).GetPanel()
+
+	if f.width {
+		child.SetWidth(parent.ContentWidth())
+	}
+	if f.height {
+		child.SetHeight(parent.ContentHeight())
+	}
+}

+ 182 - 0
gui/folder.go

@@ -0,0 +1,182 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+	"github.com/g3n/engine/math32"
+)
+
+type Folder struct {
+	Panel               // Embedded panel
+	label        Label  // Folder label
+	icon         Label  // Folder icon
+	contentPanel IPanel // Content panel
+	styles       *FolderStyles
+	cursorOver   bool
+	alignRight   bool
+}
+
+type FolderStyle struct {
+	Margins     BorderSizes
+	Border      BorderSizes
+	Paddings    BorderSizes
+	BorderColor math32.Color4
+	BgColor     math32.Color
+	FgColor     math32.Color
+	Icons       [2]int
+}
+
+type FolderStyles struct {
+	Normal   *FolderStyle
+	Over     *FolderStyle
+	Focus    *FolderStyle
+	Disabled *FolderStyle
+}
+
+// NewFolder creates and returns a pointer to a new folder widget
+// with the specified text and initial width
+func NewFolder(text string, width float32, contentPanel IPanel) *Folder {
+
+	f := new(Folder)
+	f.Initialize(text, width, contentPanel)
+	return f
+}
+
+// Initialize initializes the Folder with the specified text and initial width
+// It is normally used when the folder is embedded in another object
+func (f *Folder) Initialize(text string, width float32, contentPanel IPanel) {
+
+	f.Panel.Initialize(width, 0)
+	f.styles = &StyleDefault.Folder
+
+	// Initialize label
+	f.label.initialize(text, StyleDefault.Font)
+	f.Panel.Add(&f.label)
+
+	// Create icon
+	f.icon.initialize("", StyleDefault.FontIcon)
+	f.icon.SetFontSize(f.label.FontSize() * 1.3)
+	f.Panel.Add(&f.icon)
+
+	// Setup content panel
+	f.contentPanel = contentPanel
+	contentPanel.GetPanel().bounded = false
+	contentPanel.GetPanel().SetVisible(false)
+	f.Panel.Add(f.contentPanel)
+
+	// Set event callbacks
+	f.Panel.Subscribe(OnMouseDown, f.onMouse)
+	f.Panel.Subscribe(OnCursorEnter, f.onCursor)
+	f.Panel.Subscribe(OnCursorLeave, f.onCursor)
+
+	f.alignRight = true
+	f.update()
+	f.recalc()
+}
+
+// SetStyles set the folder styles overriding the default style
+func (f *Folder) SetStyles(fs *FolderStyles) {
+
+	f.styles = fs
+	f.update()
+}
+
+// SetAlignRight sets the side of the alignment of the content panel
+// in relation to the folder
+func (f *Folder) SetAlignRight(state bool) {
+
+	f.alignRight = state
+	f.recalc()
+}
+
+// TotalHeight returns this folder total height
+// considering the contents panel, if visible
+func (f *Folder) TotalHeight() float32 {
+
+	height := f.Height()
+	if f.contentPanel.GetPanel().Visible() {
+		height += f.contentPanel.GetPanel().Height()
+	}
+	return height
+}
+
+// onMouse receives mouse button events over the folder panel
+func (f *Folder) onMouse(evname string, ev interface{}) {
+
+	switch evname {
+	case OnMouseDown:
+		cont := f.contentPanel.GetPanel()
+		if !cont.Visible() {
+			cont.SetVisible(true)
+		} else {
+			cont.SetVisible(false)
+		}
+		f.update()
+		f.recalc()
+	default:
+		return
+	}
+}
+
+// onCursor receives cursor events over the folder panel
+func (f *Folder) onCursor(evname string, ev interface{}) {
+
+	switch evname {
+	case OnCursorEnter:
+		f.cursorOver = true
+		f.update()
+	case OnCursorLeave:
+		f.cursorOver = false
+		f.update()
+	default:
+		return
+	}
+}
+
+// update updates the folder visual state
+func (f *Folder) update() {
+
+	if f.cursorOver {
+		f.applyStyle(f.styles.Over)
+		return
+	}
+	f.applyStyle(f.styles.Normal)
+}
+
+// applyStyle applies the specified style
+func (f *Folder) applyStyle(s *FolderStyle) {
+
+	f.SetMarginsFrom(&s.Margins)
+	f.SetBordersColor4(&s.BorderColor)
+	f.SetBordersFrom(&s.Border)
+	f.SetPaddingsFrom(&s.Paddings)
+	f.SetColor(&s.BgColor)
+
+	icode := 0
+	if f.contentPanel.GetPanel().Visible() {
+		icode = 1
+	}
+	f.icon.SetText(string(s.Icons[icode]))
+	f.label.SetBgColor(&s.BgColor)
+}
+
+func (f *Folder) recalc() {
+
+	// icon position
+	f.icon.SetPosition(0, 0)
+
+	// Label position and width
+	f.label.SetPosition(f.icon.Width()+4, 0)
+	f.Panel.SetContentHeight(f.label.Height())
+
+	// Sets position of the base folder scroller panel
+	cont := f.contentPanel.GetPanel()
+	if f.alignRight {
+		cont.SetPosition(0, f.Panel.Height())
+	} else {
+		dx := cont.Width() - f.Panel.Width()
+		cont.SetPosition(-dx, f.Panel.Height())
+	}
+}

+ 135 - 0
gui/gridlayout.go

@@ -0,0 +1,135 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+type GridLayout struct {
+	columns int
+}
+
+type GridLayoutParams struct {
+	Row     int   // grid layout row number from 0
+	Col     int   // grid layout column number from 0
+	ColSpan int   // number of additional columns to ocuppy to the right
+	AlignH  Align // vertical alignment
+	AlignV  Align // horizontal alignment
+}
+
+// NewGridLayout creates and returns a pointer of a new grid layout
+func NewGridLayout() *GridLayout {
+
+	g := new(GridLayout)
+	return g
+}
+
+func (g *GridLayout) Recalc(ipan IPanel) {
+
+	type element struct {
+		panel  *Panel
+		params *GridLayoutParams
+	}
+	rows := 0
+	cols := 0
+	items := []element{}
+
+	pan := ipan.GetPanel()
+	for _, obj := range pan.Children() {
+		// Get child panel
+		child := obj.(IPanel).GetPanel()
+		// Ignore if not visible
+		if !child.Visible() {
+			continue
+		}
+		// Ignore if no layout params
+		if child.layoutParams == nil {
+			continue
+		}
+		// Checks layout params
+		params, ok := child.layoutParams.(*GridLayoutParams)
+		if !ok {
+			panic("layoutParams is not GridLayoutParams")
+		}
+		if params.Row >= rows {
+			rows = params.Row + 1
+		}
+		if params.Col >= cols {
+			cols = params.Col + 1
+		}
+		items = append(items, element{child, params})
+	}
+	// Check limits
+	if rows > 100 {
+		panic("Element row outsize limits")
+	}
+	if cols > 100 {
+		panic("Element column outsize limits")
+	}
+
+	// Determine row and column maximum sizes
+	colSizes := make([]int, cols)
+	rowSizes := make([]int, rows)
+	for _, el := range items {
+		width := el.panel.Width()
+		height := el.panel.Height()
+		if int(width) > colSizes[el.params.Col] {
+			colSizes[el.params.Col] = int(width)
+		}
+		if int(height) > rowSizes[el.params.Row] {
+			rowSizes[el.params.Row] = int(height)
+		}
+	}
+
+	// Determine row and column starting positions
+	colStart := make([]int, cols)
+	rowStart := make([]int, rows)
+	for i := 1; i < len(colSizes); i++ {
+		colStart[i] = colStart[i-1] + colSizes[i-1]
+	}
+	for i := 1; i < len(rowSizes); i++ {
+		rowStart[i] = rowStart[i-1] + rowSizes[i-1]
+	}
+
+	// Position the elements
+	for _, el := range items {
+		row := el.params.Row
+		col := el.params.Col
+		cellHeight := rowSizes[row]
+		// Current cell width
+		cellWidth := 0
+		for c := 0; c <= el.params.ColSpan; c++ {
+			pos := col + c
+			if pos >= len(colSizes) {
+				break
+			}
+			cellWidth += colSizes[pos]
+		}
+		rstart := float32(rowStart[row])
+		cstart := float32(colStart[col])
+		// Horizontal alignment
+		var dx float32 = 0
+		switch el.params.AlignH {
+		case AlignNone:
+		case AlignLeft:
+		case AlignRight:
+			dx = float32(cellWidth) - el.panel.width
+		case AlignCenter:
+			dx = (float32(cellWidth) - el.panel.width) / 2
+		default:
+			panic("Invalid horizontal alignment")
+		}
+		// Vertical alignment
+		var dy float32 = 0
+		switch el.params.AlignV {
+		case AlignNone:
+		case AlignTop:
+		case AlignBottom:
+			dy = float32(cellHeight) - el.panel.height
+		case AlignCenter:
+			dy = (float32(cellHeight) - el.panel.height) / 2
+		default:
+			panic("Invalid vertical alignment")
+		}
+		el.panel.SetPosition(cstart+dx, rstart+dy)
+	}
+}

+ 185 - 0
gui/hboxlayout.go

@@ -0,0 +1,185 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+type HBoxLayout struct {
+	pan     IPanel
+	spacing float32 // horizontal spacing between the children in pixels.
+	alignH  Align   // horizontal alignment of the whole block of children
+}
+
+// Parameters for individual children
+type HBoxLayoutParams struct {
+	Expand float32 // item expand horizontally factor (0 - no expand)
+	AlignV Align   // item vertical alignment
+}
+
+// NewHBoxLayout creates and returns a pointer to a new horizontal box layout
+func NewHBoxLayout() *HBoxLayout {
+
+	bl := new(HBoxLayout)
+	bl.spacing = 0
+	bl.alignH = AlignLeft
+	return bl
+}
+
+// SetSpacing sets the horizontal spacing between the items in pixels
+// and updates the layout if possible
+func (bl *HBoxLayout) SetSpacing(spacing float32) {
+
+	bl.spacing = spacing
+	bl.Recalc(bl.pan)
+}
+
+// SetAlignH sets the horizontal alignment of the whole group of items
+// inside the parent panel and updates the layout if possible.
+// This only has any effect if there are no expanded items.
+func (bl *HBoxLayout) SetAlignH(align Align) {
+
+	bl.alignH = align
+	bl.Recalc(bl.pan)
+}
+
+// Recalc recalculates and sets the position and sizes of all children
+func (bl *HBoxLayout) Recalc(ipan IPanel) {
+
+	// Saves the received panel
+	bl.pan = ipan
+	if bl.pan == nil {
+		return
+	}
+	parent := ipan.GetPanel()
+	if len(parent.Children()) == 0 {
+		return
+	}
+
+	// Calculates the total width, expanded width, fixed width and
+	// the sum of the expand factor for all items.
+	var twidth float32 = 0
+	var ewidth float32 = 0
+	var fwidth float32 = 0
+	var texpand float32 = 0
+	ecount := 0
+	paramsDef := HBoxLayoutParams{Expand: 0, AlignV: AlignTop}
+	for pos, obj := range parent.Children() {
+		pan := obj.(IPanel).GetPanel()
+		// Get item layout parameters or use default
+		params := paramsDef
+		if pan.layoutParams != nil {
+			params = *pan.layoutParams.(*HBoxLayoutParams)
+		}
+		// Calculate total width
+		twidth += pan.Width()
+		if pos > 0 {
+			twidth += bl.spacing
+		}
+		// Calculate width of expanded items
+		if params.Expand > 0 {
+			texpand += params.Expand
+			ewidth += pan.Width()
+			if pos > 0 {
+				ewidth += bl.spacing
+			}
+			ecount++
+			// Calculate width of fixed items
+		} else {
+			fwidth += pan.Width()
+			if pos > 0 {
+				fwidth += bl.spacing
+			}
+		}
+	}
+
+	// If there is at least on expanded item, all free space will be occupied
+	spaceMiddle := bl.spacing
+	var posX float32 = 0
+	if texpand > 0 {
+		// If there is free space, distribute space between expanded items
+		totalSpace := parent.ContentWidth() - twidth
+		if totalSpace > 0 {
+			for _, obj := range parent.Children() {
+				pan := obj.(IPanel).GetPanel()
+				// Get item layout parameters or use default
+				params := paramsDef
+				if pan.layoutParams != nil {
+					params = *pan.layoutParams.(*HBoxLayoutParams)
+				}
+				if params.Expand > 0 {
+					iwidth := totalSpace * params.Expand / texpand
+					pan.SetWidth(pan.Width() + iwidth)
+				}
+			}
+			// No free space: distribute expanded items widths
+		} else {
+			for _, obj := range parent.Children() {
+				pan := obj.(IPanel).GetPanel()
+				// Get item layout parameters or use default
+				params := paramsDef
+				if pan.layoutParams != nil {
+					params = *pan.layoutParams.(*HBoxLayoutParams)
+				}
+				if params.Expand > 0 {
+					spacing := bl.spacing * (float32(ecount) - 1)
+					iwidth := (parent.ContentWidth() - spacing - fwidth - bl.spacing) * params.Expand / texpand
+					pan.SetWidth(iwidth)
+				}
+			}
+		}
+		// No expanded items: checks block horizontal alignment
+	} else {
+		// Calculates initial x position which depends
+		// on the current horizontal alignment.
+		switch bl.alignH {
+		case AlignLeft:
+			posX = 0
+		case AlignCenter:
+			posX = (parent.ContentWidth() - twidth) / 2
+		case AlignRight:
+			posX = parent.ContentWidth() - twidth
+		case AlignWidth:
+			space := parent.ContentWidth() - twidth + bl.spacing*float32(len(parent.Children())-1)
+			if space < 0 {
+				space = bl.spacing * float32(len(parent.Children())-1)
+			}
+			spaceMiddle = space / float32(len(parent.Children())+1)
+			posX = spaceMiddle
+		default:
+			log.Fatal("HBoxLayout: invalid global horizontal alignment")
+		}
+	}
+
+	// Calculates the Y position of each item considering its vertical alignment
+	var posY float32
+	height := parent.ContentHeight()
+	for pos, obj := range parent.Children() {
+		pan := obj.(IPanel).GetPanel()
+		// Get item layout parameters or use default
+		params := paramsDef
+		if pan.layoutParams != nil {
+			params = *pan.layoutParams.(*HBoxLayoutParams)
+		}
+		cheight := pan.Height()
+		switch params.AlignV {
+		case AlignTop:
+			posY = 0
+		case AlignCenter:
+			posY = (height - cheight) / 2
+		case AlignBottom:
+			posY = height - cheight
+		case AlignHeight:
+			posY = 0
+			pan.SetHeight(height)
+		default:
+			log.Fatal("HBoxLayout: invalid item vertical alignment")
+		}
+		// Sets the child position
+		pan.SetPosition(posX, posY)
+		// Calculates next position
+		posX += pan.Width()
+		if pos < len(parent.Children())-1 {
+			posX += spaceMiddle
+		}
+	}
+}

+ 9 - 0
gui/ilayout.go

@@ -0,0 +1,9 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+type ILayout interface {
+	Recalc(ipan IPanel)
+}

+ 51 - 0
gui/image.go

@@ -0,0 +1,51 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+	"github.com/g3n/engine/texture"
+	"image"
+)
+
+type Image struct {
+	Panel                    // Embedded panel
+	tex   *texture.Texture2D // pointer to image texture
+}
+
+// NewImage creates and returns an image panel with the image
+// from the specified image used as a texture.
+// Initially the size of the panel content area is the exact size of the image.
+func NewImage(imgfile string) (image *Image, err error) {
+
+	tex, err := texture.NewTexture2DFromImage(imgfile)
+	if err != nil {
+		return nil, err
+	}
+	return NewImageFromTex(tex), nil
+}
+
+// NewImageFromRGBA creates and returns an image panel from the
+// specified image
+func NewImageFromRGBA(rgba *image.RGBA) *Image {
+
+	tex := texture.NewTexture2DFromRGBA(rgba)
+	return NewImageFromTex(tex)
+}
+
+// NewImageFromTex creates and returns an image panel from the specified texture2D
+func NewImageFromTex(tex *texture.Texture2D) *Image {
+
+	i := new(Image)
+	i.Panel.Initialize(0, 0)
+	i.tex = tex
+	i.Panel.SetContentSize(float32(i.tex.Width()), float32(i.tex.Height()))
+	i.Material().AddTexture(i.tex)
+	return i
+}
+
+//func (i *Image) Clone() *Image {
+//
+//	return NewImageFromTex(i.tex.Clone())
+//}

+ 236 - 0
gui/imagelabel.go

@@ -0,0 +1,236 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+	"github.com/g3n/engine/math32"
+)
+
+/***************************************
+
+ ImageLabel
+ +--------------------------------+
+ |  Image or Icon   Label         |
+ |  +-----------+   +----------+  |
+ |  |           |   |          |  |
+ |  |           |   |          |  |
+ |  +-----------+   +----------+  |
+ +--------------------------------+
+
+****************************************/
+
+type ImageLabel struct {
+	Panel        // Embedded panel
+	label Label  // internal label
+	image *Image // optional internal image
+	icon  *Label // optional internal icon label
+	icode int    // icon code (if icon is set)
+}
+
+// ImageLabel style
+type ImageLabelStyle struct {
+	Border      BorderSizes
+	Paddings    BorderSizes
+	BorderColor math32.Color4
+	BgColor     math32.Color4
+	FgColor     math32.Color4
+}
+
+// NewImageLabel creates and returns a pointer to a new image label widget
+// with the specified text for the label and no image/icon
+func NewImageLabel(text string) *ImageLabel {
+
+	il := new(ImageLabel)
+
+	// Initializes the panel
+	il.Panel.Initialize(0, 0)
+	il.Panel.Subscribe(OnResize, func(evname string, ev interface{}) { il.recalc() })
+
+	// Initializes the label
+	il.label.initialize(text, StyleDefault.Font)
+	il.label.Subscribe(OnResize, func(evname string, ev interface{}) { il.recalc() })
+	il.Panel.Add(&il.label)
+
+	il.recalc()
+	return il
+}
+
+// SetText sets the text of the image label
+func (il *ImageLabel) SetText(text string) {
+
+	il.label.SetText(text)
+}
+
+// Text returns the current label text
+func (il *ImageLabel) Text() string {
+
+	return il.label.Text()
+}
+
+// SetIcon sets the image label icon from the default Icon font.
+// If there is currently a selected image, it is removed
+func (il *ImageLabel) SetIcon(icode int) {
+
+	if il.image != nil {
+		il.Panel.Remove(il.image)
+		il.image = nil
+	}
+	if il.icon == nil {
+		il.icon = NewIconLabel(string(icode))
+		il.icon.SetFontSize(il.label.FontSize() * 1.4)
+		il.Panel.Add(il.icon)
+	}
+	il.icon.SetText(string(icode))
+	il.icode = icode
+	il.recalc()
+}
+
+// SetImage sets the image label image
+func (il *ImageLabel) SetImage(img *Image) {
+
+	if il.icon != nil {
+		il.Panel.Remove(il.icon)
+		il.icon = nil
+	}
+	if il.image != nil {
+		il.Panel.Remove(il.image)
+	}
+	il.image = img
+	il.Panel.Add(il.image)
+	il.recalc()
+}
+
+// SetImageFromFile sets the image label image from the specified filename
+// If there is currently a selected icon, it is removed
+func (il *ImageLabel) SetImageFromFile(imgfile string) error {
+
+	img, err := NewImage(imgfile)
+	if err != nil {
+		return err
+	}
+	il.SetImage(img)
+	return nil
+}
+
+// SetColor sets the color of the label and icon text
+func (il *ImageLabel) SetColor(color *math32.Color) {
+
+	il.label.SetColor(color)
+	if il.icon != nil {
+		il.icon.SetColor(color)
+	}
+}
+
+// SetColor4 sets the color4 of the label and icon
+func (il *ImageLabel) SetColor4(color *math32.Color4) {
+
+	il.label.SetColor4(color)
+	if il.icon != nil {
+		il.icon.SetColor4(color)
+	}
+}
+
+// SetBgColor sets the color of the image label background
+// The color alpha is set to 1.0
+func (il *ImageLabel) SetBgColor(color *math32.Color) {
+
+	il.Panel.SetColor(color)
+	if il.icon != nil {
+		il.icon.SetColor(color)
+	}
+	il.label.SetBgColor(color)
+}
+
+// SetBgColor4 sets the color4 of the image label background
+func (il *ImageLabel) SetBgColor4(color *math32.Color4) {
+
+	il.Panel.SetColor4(color)
+	if il.icon != nil {
+		il.icon.SetColor4(color)
+	}
+	il.label.SetBgColor4(color)
+}
+
+// SetFontSize sets the size of the image label font size
+func (il *ImageLabel) SetFontSize(size float64) {
+
+	il.label.SetFontSize(size)
+}
+
+// CopyFields copies another image label icon/image and text to this one
+func (il *ImageLabel) CopyFields(other *ImageLabel) {
+
+	il.label.SetText(other.label.Text())
+	if other.icon != nil {
+		il.SetIcon(other.icode)
+	}
+	if other.image != nil {
+		// TODO li.SetImage(other.image.Clone())
+	}
+	il.recalc()
+}
+
+// applyStyle applies the specified image label style
+func (il *ImageLabel) applyStyle(s *ImageLabelStyle) {
+
+	il.SetBordersColor4(&s.BorderColor)
+	il.SetBordersFrom(&s.Border)
+	il.SetPaddingsFrom(&s.Paddings)
+	il.SetColor4(&s.BgColor)
+	if il.icon != nil {
+		il.icon.SetColor4(&s.FgColor)
+	}
+	il.label.SetColor4(&s.FgColor)
+}
+
+// recalc recalculates dimensions and positions from inside out
+func (il *ImageLabel) recalc() {
+
+	// Current width and height the content area
+	width := il.Panel.ContentWidth()
+	height := il.Panel.ContentHeight()
+
+	// Image or icon width
+	var imgWidth float32 = 0
+	var spacing float32
+	if il.image != nil {
+		imgWidth = il.image.Width()
+		spacing = 4
+	} else if il.icon != nil {
+		imgWidth = il.icon.Width()
+		spacing = 4
+	}
+
+	// Sets new content width and height if necessary
+	minWidth := imgWidth + spacing + il.label.Width()
+	minHeight := il.label.Height()
+	resize := false
+	if width < minWidth {
+		width = minWidth
+		resize = true
+	}
+	if height < minHeight {
+		height = minHeight
+		resize = true
+	}
+	if resize {
+		il.Panel.SetContentSize(width, height)
+	}
+
+	// Centralize horizontally
+	px := (width - minWidth) / 2
+
+	// Set label position
+	ly := (height - il.label.Height()) / 2
+	il.label.SetPosition(px+imgWidth+spacing, ly)
+
+	// Image/icon position
+	if il.image != nil {
+		iy := (height - il.image.height) / 2
+		il.image.SetPosition(px, iy)
+	} else if il.icon != nil {
+		il.icon.SetPosition(px, ly)
+	}
+}

+ 197 - 0
gui/label.go

@@ -0,0 +1,197 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+	"github.com/g3n/engine/gls"
+	"github.com/g3n/engine/math32"
+	"github.com/g3n/engine/text"
+	"github.com/g3n/engine/texture"
+)
+
+type Label struct {
+	Panel       // Embedded panel
+	fontSize    float64
+	fontDPI     float64
+	lineSpacing float64
+	bgColor     math32.Color4
+	fgColor     math32.Color4
+	font        *text.Font
+	tex         *texture.Texture2D // Pointer to texture with drawed text
+	currentText string
+}
+
+// NewLabel creates and returns a label panel with the specified text
+// drawn using the current default font.
+func NewLabel(msg string) *Label {
+
+	l := new(Label)
+	l.initialize(msg, StyleDefault.Font)
+	return l
+}
+
+// NewIconLabel creates and returns a label panel using the specified text
+// drawn using the default icon font.
+func NewIconLabel(msg string) *Label {
+
+	l := new(Label)
+	l.initialize(msg, StyleDefault.FontIcon)
+	return l
+}
+
+// initialize initializes this label and is normally used by other
+// gui types which contains a label.
+func (l *Label) initialize(msg string, font *text.Font) {
+
+	l.font = font
+	l.Panel.Initialize(0, 0)
+	l.fontSize = 14
+	l.fontDPI = 72
+	l.lineSpacing = 1.0
+	l.bgColor = math32.Color4{0, 0, 0, 0}
+	l.fgColor = math32.Black4
+	l.SetText(msg)
+}
+
+// SetText draws the label text using the current font
+func (l *Label) SetText(msg string) {
+
+	// Do not allow empty labels
+	str := msg
+	if len(msg) == 0 {
+		str = " "
+	}
+
+	// Set font properties
+	l.font.SetSize(l.fontSize)
+	l.font.SetDPI(l.fontDPI)
+	l.font.SetLineSpacing(l.lineSpacing)
+	l.font.SetBgColor4(&l.bgColor)
+	l.font.SetFgColor4(&l.fgColor)
+
+	// Measure text
+	width, height := l.font.MeasureText(str)
+	// Create image canvas with the exact size of the texture
+	// and draw the text.
+	canvas := text.NewCanvas(width, height, &l.bgColor)
+	canvas.DrawText(0, 0, str, l.font)
+
+	// Creates texture if if doesnt exist.
+	if l.tex == nil {
+		l.tex = texture.NewTexture2DFromRGBA(canvas.RGBA)
+		l.tex.SetMagFilter(gls.NEAREST)
+		l.tex.SetMinFilter(gls.NEAREST)
+		l.Panel.Material().AddTexture(l.tex)
+		// Otherwise update texture with new image
+	} else {
+		l.tex.SetFromRGBA(canvas.RGBA)
+	}
+
+	// Updates label panel dimensions
+	l.Panel.SetContentSize(float32(width), float32(height))
+	l.currentText = str
+}
+
+// Text returns the current label text
+func (l *Label) Text() string {
+
+	return l.currentText
+}
+
+// SetColor sets the color of the label text
+// The color alpha is set to 1.0
+func (l *Label) SetColor(color *math32.Color) *Label {
+
+	l.fgColor.FromColor(color, 1.0)
+	l.SetText(l.currentText)
+	return l
+}
+
+// SetColor4 sets the color4 of the label text
+func (l *Label) SetColor4(color4 *math32.Color4) *Label {
+
+	l.fgColor = *color4
+	l.SetText(l.currentText)
+	return l
+}
+
+// Color returns the current color of the label text
+func (l *Label) Color() math32.Color4 {
+
+	return l.fgColor
+}
+
+// SetBgColor sets the color of the label background
+// The color alpha is set to 1.0
+func (l *Label) SetBgColor(color *math32.Color) *Label {
+
+	l.bgColor.FromColor(color, 1.0)
+	l.Panel.SetColor4(&l.bgColor)
+	l.SetText(l.currentText)
+	return l
+}
+
+// SetBgColor4 sets the color4 of the label background
+func (l *Label) SetBgColor4(color *math32.Color4) *Label {
+
+	l.bgColor = *color
+	l.Panel.SetColor4(&l.bgColor)
+	l.SetText(l.currentText)
+	return l
+}
+
+// BgColor returns the current color the label background
+func (l *Label) BgColor() math32.Color4 {
+
+	return l.bgColor
+}
+
+// SetFontSize sets label font size
+func (l *Label) SetFontSize(size float64) *Label {
+
+	l.fontSize = size
+	l.SetText(l.currentText)
+	return l
+}
+
+// FontSize returns the current label font size
+func (l *Label) FontSize() float64 {
+
+	return l.fontSize
+}
+
+// setTextCaret sets the label text and draws a caret at the
+// specified line and column.
+// It is normally used by the Edit widget.
+func (l *Label) setTextCaret(msg string, mx, width, line, col int) {
+
+	// Set font properties
+	l.font.SetSize(l.fontSize)
+	l.font.SetDPI(l.fontDPI)
+	l.font.SetLineSpacing(l.lineSpacing)
+	l.font.SetBgColor4(&l.bgColor)
+	l.font.SetFgColor4(&l.fgColor)
+
+	// Create canvas and draw text
+	_, height := l.font.MeasureText(msg)
+	canvas := text.NewCanvas(width, height, &l.bgColor)
+	canvas.DrawTextCaret(mx, 0, msg, l.font, line, col)
+
+	// Creates texture if if doesnt exist.
+	if l.tex == nil {
+		l.tex = texture.NewTexture2DFromRGBA(canvas.RGBA)
+		l.Panel.Material().AddTexture(l.tex)
+		// Otherwise update texture with new image
+	} else {
+		l.tex.SetFromRGBA(canvas.RGBA)
+	}
+	// Set texture filtering parameters for text
+	l.tex.SetMagFilter(gls.NEAREST)
+	l.tex.SetMinFilter(gls.NEAREST)
+
+	// Updates label panel dimensions
+	l.Panel.SetContentSize(float32(width), float32(height))
+	l.currentText = msg
+}

+ 531 - 0
gui/list.go

@@ -0,0 +1,531 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+	"github.com/g3n/engine/math32"
+	"github.com/g3n/engine/window"
+)
+
+type List struct {
+	Scroller             // Embedded scroller
+	styles   *ListStyles // Pointer to styles
+	single   bool        // Single selection flag (default is true)
+	focus    bool        // has keyboard focus
+	dropdown bool        // this is used as dropdown
+	keyNext  window.Key  // Code of key to select next item
+	keyPrev  window.Key  // Code of key to select previous item
+}
+
+// All items inserted into the list are
+// encapsulated inside a ListItem
+type ListItem struct {
+	Panel               // Container panel
+	item        IPanel  // Original item
+	selected    bool    // Item selected flag
+	highlighted bool    // Item highlighted flag
+	padLeft     float32 // Additional left padding
+	list        *List   // Pointer to list
+}
+
+type ListStyles struct {
+	Scroller *ScrollerStyles
+	Item     *ListItemStyles
+}
+
+type ListItemStyles struct {
+	Normal      ListItemStyle
+	Over        ListItemStyle
+	Selected    ListItemStyle
+	Highlighted ListItemStyle
+	SelHigh     ListItemStyle
+}
+
+type ListItemStyle struct {
+	Border      BorderSizes
+	Paddings    BorderSizes
+	BorderColor math32.Color4
+	BgColor     math32.Color4
+	FgColor     math32.Color
+}
+
+// NewVList creates and returns a pointer to a new vertical list panel
+// with the specified dimensions
+func NewVList(width, height float32) *List {
+
+	return newList(true, width, height)
+}
+
+// NewHList creates and returns a pointer to a new horizontal list panel
+// with the specified dimensions
+func NewHList(width, height float32) *List {
+
+	return newList(false, width, height)
+}
+
+// newList creates and returns a pointer to a new list panel
+// with the specified orientation and dimensions
+func newList(vert bool, width, height float32) *List {
+
+	li := new(List)
+	li.initialize(vert, width, height)
+	return li
+}
+
+func (li *List) initialize(vert bool, width, height float32) {
+
+	li.styles = &StyleDefault.List
+	li.single = true
+
+	li.Scroller.initialize(vert, width, height)
+	li.Scroller.SetStyles(li.styles.Scroller)
+	li.Scroller.adjustItem = true
+	li.Scroller.Subscribe(OnMouseDown, li.onMouseEvent)
+	li.Scroller.Subscribe(OnKeyDown, li.onKeyEvent)
+
+	if vert {
+		li.keyNext = window.KeyDown
+		li.keyPrev = window.KeyUp
+	} else {
+		li.keyNext = window.KeyRight
+		li.keyPrev = window.KeyLeft
+	}
+
+	li.update()
+}
+
+// SetSingle sets the single/multiple selection flag of the list
+func (li *List) SetSingle(state bool) {
+
+	li.single = state
+}
+
+// Single returns the current state of the single/multiple selection flag
+func (li *List) Single() bool {
+
+	return li.single
+}
+
+// SetStyles set the listr styles overriding the default style
+func (li *List) SetStyles(s *ListStyles) {
+
+	li.styles = s
+	li.Scroller.SetStyles(li.styles.Scroller)
+	li.update()
+}
+
+// Add add a list item at the end of the list
+func (li *List) Add(item IPanel) {
+
+	li.InsertAt(len(li.items), item)
+}
+
+// InsertAt inserts a list item at the specified position
+// Returs true if the item was successfuly inserted
+func (li *List) InsertAt(pos int, item IPanel) {
+
+	litem := newListItem(li, item)
+	li.Scroller.InsertAt(pos, litem)
+	litem.Panel.Subscribe(OnMouseDown, litem.onMouse)
+	litem.Panel.Subscribe(OnCursorEnter, litem.onCursor)
+}
+
+// RemoveAt removes the list item from the specified position
+// Returns true if the item was successfuly removed
+func (li *List) RemoveAt(pos int) {
+
+	li.Scroller.RemoveAt(pos)
+}
+
+// Remove removes the specified item from the list
+func (li *List) Remove(item IPanel) {
+
+	for p, curr := range li.items {
+		if curr.(*ListItem).item == item {
+			li.RemoveAt(p)
+			return
+		}
+	}
+}
+
+// ItemAt returns the list item at the specified position
+func (li *List) ItemAt(pos int) IPanel {
+
+	item := li.Scroller.ItemAt(pos)
+	if item == nil {
+		return nil
+	}
+	litem := item.(*ListItem)
+	return litem.item
+}
+
+// ItemPosition returns the position of the specified item in
+// the list or -1 if not found
+func (li *List) ItemPosition(item IPanel) int {
+
+	for pos := 0; pos < len(li.items); pos++ {
+		if li.items[pos].(*ListItem).item == item {
+			return pos
+		}
+	}
+	return -1
+}
+
+// Selected returns list with the currently selected items
+func (li *List) Selected() []IPanel {
+
+	sel := []IPanel{}
+	for _, item := range li.items {
+		litem := item.(*ListItem)
+		if litem.selected {
+			sel = append(sel, litem.item)
+		}
+	}
+	return sel
+}
+
+// Select selects or unselects the specified item
+func (li *List) SetSelected(item IPanel, state bool) {
+
+	for _, curr := range li.items {
+		litem := curr.(*ListItem)
+		if litem.item == item {
+			litem.SetSelected(state)
+			li.update()
+			li.Dispatch(OnChange, nil)
+			return
+		}
+	}
+}
+
+// SelectPos selects or unselects the item at the specified position
+func (li *List) SelectPos(pos int, state bool) {
+
+	if pos < 0 || pos >= len(li.items) {
+		return
+	}
+	litem := li.items[pos].(*ListItem)
+	if litem.selected == state {
+		return
+	}
+	litem.SetSelected(state)
+	li.update()
+	li.Dispatch(OnChange, nil)
+}
+
+// SetItemPadLeftAt sets the additional left padding for this item
+// It is used mainly by the tree control
+func (li *List) SetItemPadLeftAt(pos int, pad float32) {
+
+	if pos < 0 || pos >= len(li.items) {
+		return
+	}
+	litem := li.items[pos].(*ListItem)
+	litem.padLeft = pad
+	litem.update()
+}
+
+// selNext selects or highlights the next item, if possible
+func (li *List) selNext(sel bool, update bool) *ListItem {
+
+	// Checks for empty list
+	if len(li.items) == 0 {
+		return nil
+	}
+	// Find currently selected item
+	var pos int
+	if sel {
+		pos = li.selected()
+	} else {
+		pos = li.highlighted()
+	}
+
+	var newItem *ListItem
+	newSel := true
+	// If no item found, returns first.
+	if pos < 0 {
+		newItem = li.items[0].(*ListItem)
+		if sel {
+			newItem.SetSelected(true)
+		} else {
+			newItem.SetHighlighted(true)
+		}
+	} else {
+		item := li.items[pos].(*ListItem)
+		// Item is not the last, get next
+		if pos < len(li.items)-1 {
+			newItem = li.items[pos+1].(*ListItem)
+			if sel {
+				item.SetSelected(false)
+				newItem.SetSelected(true)
+			} else {
+				item.SetHighlighted(false)
+				newItem.SetHighlighted(true)
+			}
+			if !li.ItemVisible(pos + 1) {
+				li.ScrollDown()
+			}
+			// Item is the last, don't change
+		} else {
+			newItem = item
+			newSel = false
+		}
+	}
+
+	if update {
+		li.update()
+	}
+	if sel && newSel {
+		li.Dispatch(OnChange, nil)
+	}
+	return newItem
+}
+
+// selPrev selects or highlights the next item, if possible
+func (li *List) selPrev(sel bool, update bool) *ListItem {
+
+	// Check for empty list
+	if len(li.items) == 0 {
+		return nil
+	}
+
+	// Find first selected item
+	var pos int
+	if sel {
+		pos = li.selected()
+	} else {
+		pos = li.highlighted()
+	}
+
+	var newItem *ListItem
+	newSel := true
+	// If no item found, returns first.
+	if pos < 0 {
+		newItem = li.items[0].(*ListItem)
+		if sel {
+			newItem.SetSelected(true)
+		} else {
+			newItem.SetHighlighted(true)
+		}
+	} else {
+		item := li.items[pos].(*ListItem)
+		if pos == 0 {
+			newItem = item
+			newSel = false
+		} else {
+			newItem = li.items[pos-1].(*ListItem)
+			if sel {
+				item.SetSelected(false)
+				newItem.SetSelected(true)
+			} else {
+				item.SetHighlighted(false)
+				newItem.SetHighlighted(true)
+			}
+			if (pos - 1) < li.first {
+				li.ScrollUp()
+			}
+		}
+	}
+	if update {
+		li.update()
+	}
+	if sel && newSel {
+		li.Dispatch(OnChange, nil)
+	}
+	return newItem
+}
+
+// selected returns the position of first selected item
+func (li *List) selected() (pos int) {
+
+	for pos, item := range li.items {
+		if item.(*ListItem).selected {
+			return pos
+		}
+	}
+	return -1
+}
+
+// highlighted returns the position of first highlighted item
+func (li *List) highlighted() (pos int) {
+
+	for pos, item := range li.items {
+		if item.(*ListItem).highlighted {
+			return pos
+		}
+	}
+	return -1
+}
+
+// onMouseEvent receives subscribed mouse events for the list
+func (li *List) onMouseEvent(evname string, ev interface{}) {
+
+	li.root.SetKeyFocus(li)
+}
+
+// onKeyEvent receives subscribed key events for the list
+func (li *List) onKeyEvent(evname string, ev interface{}) {
+
+	kev := ev.(*window.KeyEvent)
+	// Dropdown mode
+	if li.dropdown {
+		switch kev.Keycode {
+		case li.keyNext:
+			li.selNext(true, true)
+		case li.keyPrev:
+			li.selPrev(true, true)
+		case window.KeyEnter:
+			li.SetVisible(false)
+		default:
+			return
+		}
+		li.root.StopPropagation(Stop3D)
+		return
+	}
+
+	// Listbox mode single selection
+	if li.single {
+		switch kev.Keycode {
+		case li.keyNext:
+			li.selNext(true, true)
+		case li.keyPrev:
+			li.selPrev(true, true)
+		default:
+			return
+		}
+		li.root.StopPropagation(Stop3D)
+		return
+	}
+
+	// Listbox mode multiple selection
+	switch kev.Keycode {
+	case li.keyNext:
+		li.selNext(false, true)
+	case li.keyPrev:
+		li.selPrev(false, true)
+	case window.KeySpace:
+		pos := li.highlighted()
+		if pos >= 0 {
+			litem := li.items[pos].(*ListItem)
+			li.setSelection(litem, !litem.selected, true, true)
+		}
+	default:
+		return
+	}
+	li.root.StopPropagation(Stop3D)
+}
+
+// setSelection sets the selected state of the specified item
+// updating the visual appearance of the list if necessary
+func (li *List) setSelection(litem *ListItem, state bool, force bool, dispatch bool) {
+
+	// If already at this state, nothing to do
+	if litem.selected == state && !force {
+		return
+	}
+	litem.SetSelected(state)
+
+	// If single selection, deselects all other items
+	if li.single {
+		for _, curr := range li.items {
+			if curr.(*ListItem) != litem {
+				curr.(*ListItem).SetSelected(false)
+			}
+		}
+	}
+	li.update()
+	if dispatch {
+		li.Dispatch(OnChange, nil)
+	}
+}
+
+// update updates the visual state the list and its items
+func (li *List) update() {
+
+	// Update the list items styles
+	for _, item := range li.items {
+		item.(*ListItem).update()
+	}
+}
+
+//
+// ListItem methods
+//
+
+func newListItem(list *List, item IPanel) *ListItem {
+
+	litem := new(ListItem)
+	litem.Panel.Initialize(0, 0)
+	litem.item = item
+	litem.list = list
+	litem.Panel.Add(item)
+	litem.SetContentWidth(item.GetPanel().Width())
+	litem.SetContentHeight(item.GetPanel().Height())
+	litem.update()
+	return litem
+}
+
+// onMouse receives mouse button events over the list item
+func (litem *ListItem) onMouse(evname string, ev interface{}) {
+
+	if litem.list.single {
+		litem.list.setSelection(litem, true, true, true)
+	} else {
+		litem.list.setSelection(litem, !litem.selected, true, true)
+	}
+}
+
+// onCursor receives cursor enter events over the list item
+func (litem *ListItem) onCursor(evname string, ev interface{}) {
+
+	if litem.list.dropdown {
+		litem.list.setSelection(litem, true, true, false)
+		return
+	}
+}
+
+// setSelected sets this item selected state
+func (litem *ListItem) SetSelected(state bool) {
+
+	litem.selected = state
+	//litem.item.SetSelected2(state)
+}
+
+// setHighlighted sets this item selected state
+func (litem *ListItem) SetHighlighted(state bool) {
+
+	litem.highlighted = state
+	//litem.item.SetHighlighted2(state)
+}
+
+// updates the list item visual style accordingly to its current state
+func (litem *ListItem) update() {
+
+	list := litem.list
+	if litem.selected && !litem.highlighted {
+		litem.applyStyle(&list.styles.Item.Selected)
+		return
+	}
+	if !litem.selected && litem.highlighted {
+		litem.applyStyle(&list.styles.Item.Highlighted)
+		return
+	}
+	if litem.selected && litem.highlighted {
+		litem.applyStyle(&list.styles.Item.SelHigh)
+		return
+	}
+	litem.applyStyle(&list.styles.Item.Normal)
+}
+
+// applyStyle applies the specified style to this ListItem
+func (litem *ListItem) applyStyle(s *ListItemStyle) {
+
+	litem.SetBordersFrom(&s.Border)
+	litem.SetBordersColor4(&s.BorderColor)
+	pads := s.Paddings
+	pads.Left += litem.padLeft
+	litem.SetPaddingsFrom(&pads)
+	litem.SetColor4(&s.BgColor)
+}

+ 12 - 0
gui/logger.go

@@ -0,0 +1,12 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+	"github.com/g3n/engine/util/logger"
+)
+
+// Package logger
+var log = logger.New("GUI", logger.Default)

+ 806 - 0
gui/panel.go

@@ -0,0 +1,806 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+	"github.com/g3n/engine/core"
+	"github.com/g3n/engine/geometry"
+	"github.com/g3n/engine/gls"
+	"github.com/g3n/engine/graphic"
+	"github.com/g3n/engine/material"
+	"github.com/g3n/engine/math32"
+)
+
+/*********************************************
+
+ Panel areas:
+ +------------------------------------------+
+ |  Margin area                             |
+ |  +------------------------------------+  |
+ |  |  Border area                       |  |
+ |  |  +------------------------------+  |  |
+ |  |  | Padding area                 |  |  |
+ |  |  |  +------------------------+  |  |  |
+ |  |  |  | Content area           |  |  |  |
+ |  |  |  |                        |  |  |  |
+ |  |  |  |                        |  |  |  |
+ |  |  |  +------------------------+  |  |  |
+ |  |  |                              |  |  |
+ |  |  +------------------------------+  |  |
+ |  |                                    |  |
+ |  +------------------------------------+  |
+ |                                          |
+ +------------------------------------------+
+
+*********************************************/
+
+// IPanel is the interface for all panel types
+type IPanel interface {
+	graphic.IGraphic
+	GetPanel() *Panel
+	SetRoot(*Root)
+	LostKeyFocus()
+	TotalHeight() float32
+}
+
+type Panel struct {
+	graphic.Graphic                     // Embedded graphic
+	root            *Root               // pointer to root container
+	width           float32             // external width in pixels
+	height          float32             // external height in pixels
+	mat             *material.Material  // panel material
+	marginSizes     BorderSizes         // external margin sizes in pixel coordinates
+	borderSizes     BorderSizes         // border sizes in pixel coordinates
+	paddingSizes    BorderSizes         // padding sizes in pixel coordinates
+	content         Rect                // current content rectangle in pixel coordinates
+	modelMatrixUni  gls.UniformMatrix4f // pointer to model matrix uniform
+	borderColorUni  gls.Uniform4f       // pointer to border color uniform
+	paddingColorUni gls.Uniform4f       // pointer to padding color uniform
+	contentColorUni gls.Uniform4f       // pointer to content color uniform
+	boundsUni       gls.Uniform4f       // pointer to bounds uniform (texture coordinates)
+	borderUni       gls.Uniform4f       // pointer to border uniform (texture coordinates)
+	paddingUni      gls.Uniform4f       // pointer to padding uniform (texture coordinates)
+	contentUni      gls.Uniform4f       // pointer to content uniform (texture coordinates)
+	pospix          math32.Vector3      // absolute position in pixels
+	xmin            float32             // minimum absolute x this panel can use
+	xmax            float32             // maximum absolute x this panel can use
+	ymin            float32             // minimum absolute y this panel can use
+	ymax            float32             // maximum absolute y this panel can use
+	nextChildZ      float32             // Z coordinate of next child added
+	bounded         bool                // panel is bounded by its parent
+	enabled         bool                // enable event processing
+	cursorEnter     bool                // mouse enter dispatched
+	layout          ILayout             // current layout for children
+	layoutParams    interface{}         // current layout parameters used by container panel
+}
+
+const (
+	deltaZ = -0.00001
+)
+
+// NewPanel creates and returns a pointer to a new panel with the
+// specified dimensions in pixels
+func NewPanel(width, height float32) *Panel {
+
+	p := new(Panel)
+	p.Initialize(width, height)
+	return p
+}
+
+// Initialize initializes this panel and is normally used by other types which embed a panel.
+func (p *Panel) Initialize(width, height float32) {
+
+	p.width = width
+	p.height = height
+	p.nextChildZ = deltaZ
+
+	// Builds array with vertex positions and texture coordinates
+	positions := math32.NewArrayF32(0, 20)
+	positions.Append(
+		0, 0, 0, 0, 1,
+		0, -1, 0, 0, 0,
+		1, -1, 0, 1, 0,
+		1, 0, 0, 1, 1,
+	)
+	// Builds array of indices
+	indices := math32.NewArrayU32(0, 6)
+	indices.Append(0, 1, 2, 0, 2, 3)
+
+	// Creates geometry
+	geom := geometry.NewGeometry()
+	geom.SetIndices(indices)
+	geom.AddVBO(gls.NewVBO().
+		AddAttrib("VertexPosition", 3).
+		AddAttrib("VertexTexcoord", 2).
+		SetBuffer(positions),
+	)
+
+	// Initialize material
+	p.mat = material.NewMaterial()
+	p.mat.SetShader("shaderPanel")
+
+	// Initialize graphic
+	p.Graphic.Init(geom, gls.TRIANGLES)
+	p.AddMaterial(p, p.mat, 0, 0)
+
+	// Creates and adds uniform
+	p.modelMatrixUni.Init("ModelMatrix")
+	p.borderColorUni.Init("BorderColor")
+	p.paddingColorUni.Init("PaddingColor")
+	p.contentColorUni.Init("ContentColor")
+	p.boundsUni.Init("Bounds")
+	p.borderUni.Init("Border")
+	p.paddingUni.Init("Padding")
+	p.contentUni.Init("Content")
+
+	// Set defaults
+	p.borderColorUni.Set(0, 0, 0, 1)
+	p.bounded = true
+	p.enabled = true
+
+	p.resize(width, height)
+}
+
+// GetPanel satisfies the IPanel interface and
+// returns pointer to this panel
+func (pan *Panel) GetPanel() *Panel {
+
+	return pan
+}
+
+// SetRoot satisfies the IPanel interface
+// and sets the pointer to this panel root container
+func (pan *Panel) SetRoot(root *Root) {
+
+	pan.root = root
+}
+
+// LostKeyFocus satisfies the IPanel interface and is called by gui root
+// container when the panel loses the key focus
+func (p *Panel) LostKeyFocus() {
+
+}
+
+// TotalHeight satisfies the IPanel interface and returns the total
+// height of this panel considering visible not bounded children
+func (p *Panel) TotalHeight() float32 {
+
+	return p.Height()
+}
+
+// SetSelected satisfies the IPanel interface and is normally called
+// by a list container to change the panel visual appearance
+func (p *Panel) SetSelected2(state bool) {
+
+}
+
+// SetHighlighted satisfies the IPanel interface and is normally called
+// by a list container to change the panel visual appearance
+func (p *Panel) SetHighlighted2(state bool) {
+
+}
+
+// Material returns a pointer for this panel core.Material
+func (p *Panel) Material() *material.Material {
+
+	return p.mat
+}
+
+// SetTopChild sets the Z coordinate of the specified panel to
+// be on top of all other children of this panel.
+// The function does not check if the specified panel is a
+// child of this one.
+func (p *Panel) SetTopChild(ipan IPanel) {
+
+	ipan.GetPanel().SetPositionZ(p.nextChildZ)
+}
+
+// SetTopChild sets this panel to be on the foreground
+// in relation to all its siblings.
+func (p *Panel) SetForeground() {
+
+	// internal function to calculate the total minimum Z
+	// for a panel hierarchy
+	var getTopZ func(*Panel) float32
+	getTopZ = func(pan *Panel) float32 {
+		topZ := pan.Position().Z
+		for _, iobj := range pan.Children() {
+			child := iobj.(IPanel).GetPanel()
+			cz := pan.Position().Z + getTopZ(child)
+			if cz < topZ {
+				topZ = cz
+			}
+		}
+		return topZ
+	}
+
+	// Find the child of this panel with the minimum Z
+	par := p.Parent().(IPanel).GetPanel()
+	topZ := float32(10)
+	for _, iobj := range par.Children() {
+		child := iobj.(IPanel).GetPanel()
+		cz := getTopZ(child)
+		if cz < topZ {
+			topZ = cz
+		}
+	}
+	if p.Position().Z > topZ {
+		p.SetPositionZ(topZ + deltaZ)
+	}
+}
+
+// SetPosition sets this panel absolute position in pixel coordinates
+// from left to right and from top to bottom of the screen.
+func (p *Panel) SetPosition(x, y float32) {
+
+	p.Node.SetPositionX(math32.Round(x))
+	p.Node.SetPositionY(math32.Round(y))
+}
+
+// SetSize sets this panel external width and height in pixels.
+func (p *Panel) SetSize(width, height float32) {
+
+	if width < 0 {
+		log.Warn("Invalid panel width:%v", width)
+		width = 0
+	}
+	if height < 0 {
+		log.Warn("Invalid panel height:%v", height)
+		height = 0
+	}
+	p.resize(width, height)
+}
+
+// SetWidth sets this panel external width in pixels.
+// The internal panel areas and positions are recalculated
+func (p *Panel) SetWidth(width float32) {
+
+	p.SetSize(width, p.height)
+}
+
+// SetHeight sets this panel external height in pixels.
+// The internal panel areas and positions are recalculated
+func (p *Panel) SetHeight(height float32) {
+
+	p.SetSize(p.width, height)
+}
+
+// SetContentAspectWidth sets the width of the content area of the panel
+// to the specified value and adjusts its height to keep the same aspect radio.
+func (p *Panel) SetContentAspectWidth(width float32) {
+
+	aspect := p.content.Width / p.content.Height
+	height := width / aspect
+	p.SetContentSize(width, height)
+}
+
+// SetContentAspectHeight sets the height of the content area of the panel
+// to the specified value and adjusts its width to keep the same aspect ratio.
+func (p *Panel) SetContentAspectHeight(height float32) {
+
+	aspect := p.content.Width / p.content.Height
+	width := height / aspect
+	p.SetContentSize(width, height)
+}
+
+// Width returns the current panel external width in pixels
+func (p *Panel) Width() float32 {
+
+	return p.width
+}
+
+// Height returns the current panel external height in pixels
+func (p *Panel) Height() float32 {
+
+	return p.height
+}
+
+// ContentWidth returns the current width of the content area in pixels
+func (p *Panel) ContentWidth() float32 {
+
+	return p.content.Width
+}
+
+// ContentHeight returns the current height of the content area in pixels
+func (p *Panel) ContentHeight() float32 {
+
+	return p.content.Height
+}
+
+// SetMargins set this panel margin sizes in pixels
+// and recalculates the panel external size
+func (p *Panel) SetMargins(top, right, bottom, left float32) {
+
+	p.marginSizes.Set(top, right, bottom, left)
+	p.resize(p.calcWidth(), p.calcHeight())
+}
+
+// SetMarginsFrom sets this panel margins sizes from the specified
+// BorderSizes pointer and recalculates the panel external size
+func (p *Panel) SetMarginsFrom(src *BorderSizes) {
+
+	p.marginSizes = *src
+	p.resize(p.calcWidth(), p.calcHeight())
+}
+
+// Margins returns the current margin sizes in pixels
+func (p *Panel) Margins() BorderSizes {
+
+	return p.marginSizes
+}
+
+// SetBorders sets this panel border sizes in pixels
+// and recalculates the panel external size
+func (p *Panel) SetBorders(top, right, bottom, left float32) {
+
+	p.borderSizes.Set(top, right, bottom, left)
+	p.resize(p.calcWidth(), p.calcHeight())
+}
+
+// SetBordersFrom sets this panel border sizes from the specified
+// BorderSizes pointer and recalculates the panel size
+func (p *Panel) SetBordersFrom(src *BorderSizes) {
+
+	p.borderSizes = *src
+	p.resize(p.calcWidth(), p.calcHeight())
+}
+
+// Borders returns this panel current border sizes
+func (p *Panel) Borders() BorderSizes {
+
+	return p.borderSizes
+}
+
+// SetPaddings sets the panel padding sizes in pixels
+func (p *Panel) SetPaddings(top, right, bottom, left float32) {
+
+	p.paddingSizes.Set(top, right, bottom, left)
+	p.resize(p.calcWidth(), p.calcHeight())
+}
+
+// SetPaddingsFrom sets this panel padding sizes from the specified
+// BorderSizes pointer and recalculates the panel size
+func (p *Panel) SetPaddingsFrom(src *BorderSizes) {
+
+	p.paddingSizes = *src
+	p.resize(p.calcWidth(), p.calcHeight())
+}
+
+// Paddings returns this panel padding sizes in pixels
+func (p *Panel) Paddings() BorderSizes {
+
+	return p.paddingSizes
+}
+
+// SetBordersColor sets the color of this panel borders
+// The borders opacity is set to 1.0 (full opaque)
+func (p *Panel) SetBordersColor(color *math32.Color) {
+
+	p.borderColorUni.Set(color.R, color.G, color.B, 1)
+}
+
+// SetBordersColor4 sets the color and opacity of this panel borders
+func (p *Panel) SetBordersColor4(color *math32.Color4) {
+
+	p.borderColorUni.Set(color.R, color.G, color.B, color.A)
+}
+
+// BorderColor4 returns current border color
+func (p *Panel) BordersColor4() math32.Color4 {
+
+	var color math32.Color4
+	p.borderColorUni.SetColor4(&color)
+	return color
+}
+
+// SetPaddingsColor sets the color of this panel paddings.
+func (p *Panel) SetPaddingsColor(color *math32.Color) {
+
+	p.paddingColorUni.Set(color.R, color.G, color.B, 1.0)
+}
+
+// SetColor sets the color of the panel paddings and content area
+func (p *Panel) SetColor(color *math32.Color) *Panel {
+
+	p.paddingColorUni.Set(color.R, color.G, color.B, 1.0)
+	p.contentColorUni.Set(color.R, color.G, color.B, 1.0)
+	return p
+}
+
+// SetColor4 sets the color of the panel paddings and content area
+func (p *Panel) SetColor4(color *math32.Color4) *Panel {
+
+	p.paddingColorUni.Set(color.R, color.G, color.B, color.A)
+	p.contentColorUni.Set(color.R, color.G, color.B, color.A)
+	return p
+}
+
+// Color4 returns the current color of the panel content area
+func (p *Panel) Color4() math32.Color4 {
+
+	return p.contentColorUni.GetColor4()
+}
+
+// SetContentSize sets this panel content size to the specified dimensions.
+// The external size of the panel may increase or decrease to acomodate
+// the new content size.
+func (p *Panel) SetContentSize(width, height float32) {
+
+	// Calculates the new desired external width and height
+	eWidth := width +
+		p.paddingSizes.Left + p.paddingSizes.Right +
+		p.borderSizes.Left + p.borderSizes.Right +
+		p.marginSizes.Left + p.marginSizes.Right
+	eHeight := height +
+		p.paddingSizes.Top + p.paddingSizes.Bottom +
+		p.borderSizes.Top + p.borderSizes.Bottom +
+		p.marginSizes.Top + p.marginSizes.Bottom
+	p.resize(eWidth, eHeight)
+}
+
+// SetContentWidth sets this panel content width to the specified dimension in pixels.
+// The external size of the panel may increase or decrease to acomodate the new width
+func (p *Panel) SetContentWidth(width float32) {
+
+	p.SetContentSize(width, p.content.Height)
+}
+
+// SetContentHeight sets this panel content height to the specified dimension in pixels.
+// The external size of the panel may increase or decrease to acomodate the new width
+func (p *Panel) SetContentHeight(height float32) {
+
+	p.SetContentSize(p.content.Width, height)
+}
+
+// MinWidth returns the minimum width of this panel (ContentWidth = 0)
+func (p *Panel) MinWidth() float32 {
+
+	return p.paddingSizes.Left + p.paddingSizes.Right +
+		p.borderSizes.Left + p.borderSizes.Right +
+		p.marginSizes.Left + p.marginSizes.Right
+}
+
+// MinHeight returns the minimum height of this panel (ContentHeight = 0)
+func (p *Panel) MinHeight() float32 {
+
+	return p.paddingSizes.Top + p.paddingSizes.Bottom +
+		p.borderSizes.Top + p.borderSizes.Bottom +
+		p.marginSizes.Top + p.marginSizes.Bottom
+}
+
+// Add adds a child panel to this one
+func (p *Panel) Add(ichild IPanel) *Panel {
+
+	p.Node.Add(ichild)
+	node := ichild.GetPanel()
+	node.SetParent(p)
+	node.SetPositionZ(p.nextChildZ)
+	p.nextChildZ += deltaZ
+	if p.layout != nil {
+		p.layout.Recalc(p)
+	}
+	p.Dispatch(OnChild, nil)
+	return p
+}
+
+// Remove removes the specified child from this panel
+func (p *Panel) Remove(ichild IPanel) bool {
+
+	res := p.Node.Remove(ichild)
+	if res {
+		if p.layout != nil {
+			p.layout.Recalc(p)
+		}
+		p.Dispatch(OnChild, nil)
+	}
+	return res
+}
+
+// Bounded returns this panel bounded state
+func (p *Panel) Bounded() bool {
+
+	return p.bounded
+}
+
+// SetBounded sets this panel bounded state
+func (p *Panel) SetBounded(bounded bool) {
+
+	p.bounded = bounded
+}
+
+// UpdateMatrixWorld overrides the standard Node version which is called by
+// the Engine before rendering the frame.
+func (p *Panel) UpdateMatrixWorld() {
+
+	par := p.Parent()
+	if par == nil {
+		p.updateBounds(nil)
+	} else {
+		par, ok := par.(*Panel)
+		if ok {
+			p.updateBounds(par)
+		} else {
+			p.updateBounds(nil)
+		}
+	}
+	// Update this panel children
+	for _, ichild := range p.Children() {
+		ichild.UpdateMatrixWorld()
+	}
+}
+
+// ContainsPosition returns indication if this panel contains
+// the specified screen position in pixels.
+func (p *Panel) ContainsPosition(x, y float32) bool {
+
+	if x < p.pospix.X || x >= (p.pospix.X+p.width) {
+		return false
+	}
+	if y < p.pospix.Y || y >= (p.pospix.Y+p.height) {
+		return false
+	}
+	return true
+}
+
+//// CancelMouse is called by the gui root panel when a mouse event occurs
+//// outside of this panel and the panel is not in focus.
+//func (p *Panel) CancelMouse(m *Root, mev MouseEvent) {
+//
+//	if !p.enabled {
+//		return
+//	}
+//	// If OnMouseEnter previously sent, sends OnMouseLeave
+//	if p.cursorEnter {
+//		p.DispatchPrefix(OnMouseLeave, &mev)
+//		p.cursorEnter = false
+//	}
+//	// If click outside the panel
+//	if mev.Button >= 0 {
+//		if mev.Action == core.ActionPress {
+//			p.DispatchPrefix(OnMouseButtonOut, &mev)
+//		}
+//	}
+//}
+
+// SetEnabled sets the panel enabled state
+// A disabled panel do not process key or mouse events.
+func (p *Panel) SetEnabled(state bool) {
+
+	p.enabled = state
+	p.Dispatch(OnEnable, nil)
+}
+
+// Enabled returns the current enabled state of this panel
+func (p *Panel) Enabled() bool {
+
+	return p.enabled
+}
+
+// SetLayout sets the layout to use to position the children of this panel
+// To remove the layout, call this function passing nil as parameter.
+func (p *Panel) SetLayout(ilayout ILayout) {
+
+	p.layout = ilayout
+	if p.layout != nil {
+		p.layout.Recalc(p)
+	}
+}
+
+// SetLayoutParams sets the layout parameters for this panel
+func (p *Panel) SetLayoutParams(params interface{}) {
+
+	p.layoutParams = params
+}
+
+// updateBounds is called by UpdateMatrixWorld() and calculates this panel
+// bounds considering the bounds of its parent
+func (p *Panel) updateBounds(par *Panel) {
+
+	if par == nil {
+		p.pospix = p.Position()
+		p.xmin = p.pospix.X
+		p.ymin = p.pospix.Y
+		p.xmax = p.pospix.X + p.width
+		p.ymax = p.pospix.Y + p.height
+		p.boundsUni.Set(0, 0, 1, 1)
+		return
+	}
+	// If this panel is bounded to its parent, its coordinates are relative
+	// to the parent internal content rectangle.
+	if p.bounded {
+		p.pospix.X = p.Position().X + par.pospix.X + float32(par.marginSizes.Left+par.borderSizes.Left+par.paddingSizes.Left)
+		p.pospix.Y = p.Position().Y + par.pospix.Y + float32(par.marginSizes.Top+par.borderSizes.Top+par.paddingSizes.Top)
+		p.pospix.Z = p.Position().Z + par.pospix.Z
+		// Otherwise its coordinates are relative to the parent outer coordinates.
+	} else {
+		p.pospix.X = p.Position().X + par.pospix.X
+		p.pospix.Y = p.Position().Y + par.pospix.Y
+		p.pospix.Z = p.Position().Z + par.pospix.Z
+	}
+	// Maximum x,y coordinates for this panel
+	p.xmin = p.pospix.X
+	p.ymin = p.pospix.Y
+	p.xmax = p.pospix.X + p.width
+	p.ymax = p.pospix.Y + p.height
+	if p.bounded {
+		// Get the parent minimum X and Y absolute coordinates in pixels
+		pxmin := par.xmin + par.marginSizes.Right + par.borderSizes.Right + par.paddingSizes.Right
+		pymin := par.ymin + par.marginSizes.Bottom + par.borderSizes.Bottom + par.paddingSizes.Bottom
+		// Get the parent maximum X and Y absolute coordinates in pixels
+		pxmax := par.xmax - (par.marginSizes.Right + par.borderSizes.Right + par.paddingSizes.Right)
+		pymax := par.ymax - (par.marginSizes.Bottom + par.borderSizes.Bottom + par.paddingSizes.Bottom)
+		// Update this panel minimum x and y coordinates.
+		if p.xmin < pxmin {
+			p.xmin = pxmin
+		}
+		if p.ymin < pymin {
+			p.ymin = pymin
+		}
+		// Update this panel maximum x and y coordinates.
+		if p.xmax > pxmax {
+			p.xmax = pxmax
+		}
+		if p.ymax > pymax {
+			p.ymax = pymax
+		}
+	}
+	// Set default values for bounds in texture coordinates
+	xmintex := float32(0.0)
+	ymintex := float32(0.0)
+	xmaxtex := float32(1.0)
+	ymaxtex := float32(1.0)
+	// If this panel is bounded to its parent, calculates the bounds
+	// for clipping in texture coordinates
+	if p.bounded {
+		if p.pospix.X < p.xmin {
+			xmintex = (p.xmin - p.pospix.X) / p.width
+		}
+		if p.pospix.Y < p.ymin {
+			ymintex = (p.ymin - p.pospix.Y) / p.height
+		}
+		if p.pospix.X+p.width > p.xmax {
+			xmaxtex = (p.xmax - p.pospix.X) / p.width
+		}
+		if p.pospix.Y+p.height > p.ymax {
+			ymaxtex = (p.ymax - p.pospix.Y) / p.height
+		}
+	}
+	// Sets bounds uniform
+	p.boundsUni.Set(xmintex, ymintex, xmaxtex, ymaxtex)
+}
+
+// calcWidth calculates the panel external width in pixels
+func (p *Panel) calcWidth() float32 {
+
+	return p.content.Width +
+		p.paddingSizes.Left + p.paddingSizes.Right +
+		p.borderSizes.Left + p.borderSizes.Right +
+		p.marginSizes.Left + p.marginSizes.Right
+}
+
+// calcHeight calculates the panel external height in pixels
+func (p *Panel) calcHeight() float32 {
+
+	return p.content.Height +
+		p.paddingSizes.Top + p.paddingSizes.Bottom +
+		p.borderSizes.Top + p.borderSizes.Bottom +
+		p.marginSizes.Top + p.marginSizes.Bottom
+}
+
+// resize tries to set the external size of the panel to the specified
+// dimensions and recalculates the size and positions of the internal areas.
+// The margins, borders and padding sizes are kept and the content
+// area size is adjusted. So if the panel is decreased, its minimum
+// size is determined by the margins, borders and paddings.
+func (p *Panel) resize(width, height float32) {
+
+	var padding Rect
+	var border Rect
+
+	width = math32.Round(width)
+	height = math32.Round(height)
+	// Adjusts content width
+	p.content.Width = width -
+		p.marginSizes.Left - p.marginSizes.Right -
+		p.borderSizes.Left - p.borderSizes.Right -
+		p.paddingSizes.Left - p.paddingSizes.Right
+	if p.content.Width < 0 {
+		p.content.Width = 0
+	}
+	// Adjust other area widths
+	padding.Width = p.paddingSizes.Left + p.content.Width + p.paddingSizes.Right
+	border.Width = p.borderSizes.Left + padding.Width + p.borderSizes.Right
+
+	// Adjusts content height
+	p.content.Height = height -
+		p.marginSizes.Top - p.marginSizes.Bottom -
+		p.borderSizes.Top - p.borderSizes.Bottom -
+		p.paddingSizes.Top - p.paddingSizes.Bottom
+	if p.content.Height < 0 {
+		p.content.Height = 0
+	}
+	// Adjust other area heights
+	padding.Height = p.paddingSizes.Top + p.content.Height + p.paddingSizes.Bottom
+	border.Height = p.borderSizes.Top + padding.Height + p.borderSizes.Bottom
+
+	// Sets area positions
+	border.X = p.marginSizes.Left
+	border.Y = p.marginSizes.Top
+	padding.X = border.X + p.borderSizes.Left
+	padding.Y = border.Y + p.borderSizes.Top
+	p.content.X = padding.X + p.paddingSizes.Left
+	p.content.Y = padding.Y + p.paddingSizes.Top
+
+	// Sets final panel dimensions (may be different from requested dimensions)
+	p.width = p.marginSizes.Left + border.Width + p.marginSizes.Right
+	p.height = p.marginSizes.Top + border.Height + p.marginSizes.Bottom
+
+	// Updates border uniform in texture coordinates (0,0 -> 1,1)
+	p.borderUni.Set(
+		float32(border.X)/float32(p.width),
+		float32(border.Y)/float32(p.height),
+		float32(border.Width)/float32(p.width),
+		float32(border.Height)/float32(p.height),
+	)
+	// Updates padding uniform in texture coordinates (0,0 -> 1,1)
+	p.paddingUni.Set(
+		float32(padding.X)/float32(p.width),
+		float32(padding.Y)/float32(p.height),
+		float32(padding.Width)/float32(p.width),
+		float32(padding.Height)/float32(p.height),
+	)
+	// Updates content uniform in texture coordinates (0,0 -> 1,1)
+	p.contentUni.Set(
+		float32(p.content.X)/float32(p.width),
+		float32(p.content.Y)/float32(p.height),
+		float32(p.content.Width)/float32(p.width),
+		float32(p.content.Height)/float32(p.height),
+	)
+	// Update layout and dispatch event
+	if p.layout != nil {
+		p.layout.Recalc(p)
+	}
+	p.Dispatch(OnResize, nil)
+}
+
+// RenderSetup is called by the Engine before drawing the object
+func (p *Panel) RenderSetup(gl *gls.GLS, rinfo *core.RenderInfo) {
+
+	// Get the current viewport width and height
+	_, _, width, height := gl.GetViewport()
+	fwidth := float32(width)
+	fheight := float32(height)
+
+	// Scale the quad for the viewport so it has fixed dimensions in pixels.
+	fw := float32(p.width) / fwidth
+	fh := float32(p.height) / fheight
+	var scale math32.Vector3
+	scale.Set(2*fw, 2*fh, 1)
+
+	// Convert absolute position in pixel coordinates from the top/left to
+	// standard OpenGL clip coordinates of the quad center
+	var posclip math32.Vector3
+	posclip.X = (p.pospix.X - fwidth/2) / (fwidth / 2)
+	posclip.Y = -(p.pospix.Y - fheight/2) / (fheight / 2)
+	posclip.Z = p.pospix.Z
+	//log.Debug("panel posclip:%v\n", posclip)
+
+	// Sets the model matrix uniform
+	var mm math32.Matrix4
+	var quat math32.Quaternion
+	quat.SetIdentity()
+	mm.Compose(&posclip, &quat, &scale)
+	p.modelMatrixUni.SetMatrix4(&mm)
+
+	// Transfer uniforms
+	p.borderColorUni.Transfer(gl)
+	p.paddingColorUni.Transfer(gl)
+	p.contentColorUni.Transfer(gl)
+	p.boundsUni.Transfer(gl)
+	p.borderUni.Transfer(gl)
+	p.paddingUni.Transfer(gl)
+	p.contentUni.Transfer(gl)
+	p.modelMatrixUni.Transfer(gl)
+}

+ 350 - 0
gui/root.go

@@ -0,0 +1,350 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+	"github.com/g3n/engine/core"
+	"github.com/g3n/engine/gls"
+	"github.com/g3n/engine/window"
+	"sort"
+)
+
+type Root struct {
+	Panel                            // embedded panel
+	core.TimerManager                // embedded TimerManager
+	gs                *gls.GLS       // OpenGL state
+	win               window.IWindow // Window
+	stopPropagation   int            // stop event propagation bitmask
+	keyFocus          IPanel         // current child panel with key focus
+	mouseFocus        IPanel         // current child panel with mouse focus
+	scrollFocus       IPanel         // current child panel with scroll focus
+	targets           listPanelZ     // preallocated list of target panels
+}
+
+const (
+	StopGUI = 0x01             // Stop event propagation to GUI
+	Stop3D  = 0x02             // Stop event propagation to 3D
+	StopAll = StopGUI | Stop3D // Stop event propagation
+)
+
+// NewRoot creates and returns a pointer to a gui root panel for the specified window
+func NewRoot(gs *gls.GLS, win window.IWindow) *Root {
+
+	r := new(Root)
+	r.gs = gs
+	r.win = win
+	r.Panel.Initialize(0, 0)
+	r.TimerManager.Initialize()
+	// for optimization, sets this root panel as not renderable as in most cases
+	// it is used only as a container
+	r.SetRenderable(false)
+	// Subscribe to window events
+	r.SubscribeWin()
+	r.targets = []IPanel{}
+	return r
+}
+
+// SubscribeWin subscribes this root panel to window events
+func (r *Root) SubscribeWin() {
+
+	r.win.Subscribe(window.OnKeyUp, r.onKey)
+	r.win.Subscribe(window.OnKeyDown, r.onKey)
+	r.win.Subscribe(window.OnChar, r.onChar)
+	r.win.Subscribe(window.OnMouseUp, r.onMouse)
+	r.win.Subscribe(window.OnMouseDown, r.onMouse)
+	r.win.Subscribe(window.OnCursor, r.onCursor)
+	r.win.Subscribe(window.OnScroll, r.onScroll)
+	r.win.Subscribe(window.OnWindowSize, r.onWindowSize)
+	r.win.Subscribe(window.OnFrame, r.onFrame)
+}
+
+// Add adds the specified panel to the root container list of children
+func (r *Root) Add(ipan IPanel) {
+
+	r.Panel.Add(ipan)
+	ipan.GetNode().SetParent(r)
+}
+
+// SetKeyFocus sets the panel which will receive all keyboard events
+// Passing nil will remove the focus (if any)
+func (r *Root) SetKeyFocus(ipan IPanel) {
+
+	if r.keyFocus != nil {
+		// If this panel is already in focus, nothing to do
+		if ipan != nil {
+			if r.keyFocus.GetPanel() == ipan.GetPanel() {
+				return
+			}
+		}
+		r.keyFocus.LostKeyFocus()
+	}
+	r.keyFocus = ipan
+}
+
+// ClearKeyFocus clears the key focus panel (if any) without
+// calling LostKeyFocus() for previous focused panel
+func (r *Root) ClearKeyFocus() {
+
+	r.keyFocus = nil
+}
+
+// SetMouseFocus sets the panel which will receive all mouse events
+// Passing nil will restore the default event processing
+func (r *Root) SetMouseFocus(ipan IPanel) {
+
+	r.mouseFocus = ipan
+}
+
+// SetScrollFocus sets the panel which will receive all scroll events
+// Passing nil will restore the default event processing
+func (r *Root) SetScrollFocus(ipan IPanel) {
+
+	r.scrollFocus = ipan
+}
+
+// HasKeyFocus checks if the specified panel has the key focus
+func (r *Root) HasKeyFocus(ipan IPanel) bool {
+
+	if r.keyFocus == nil {
+		return false
+	}
+	if r.keyFocus.GetPanel() == ipan.GetPanel() {
+		return true
+	}
+	return false
+}
+
+// HasMouseFocus checks if the specified panel has the mouse focus
+func (r *Root) HasMouseFocus(ipan IPanel) bool {
+
+	if r.mouseFocus == nil {
+		return false
+	}
+	if r.mouseFocus.GetPanel() == ipan.GetPanel() {
+		return true
+	}
+	return false
+}
+
+// StopPropagation stops the propagation of the current event
+// to outside the root panel (for example the 3D camera)
+func (r *Root) StopPropagation(events int) {
+
+	r.stopPropagation |= events
+}
+
+// SetCursorNormal sets the cursor of the associated window to
+// standard type
+func (r *Root) SetCursorNormal() {
+
+	r.win.SetStandardCursor(window.ArrowCursor)
+}
+
+// SetCursorDrag sets the cursor of the associated window to
+// drag type
+func (r *Root) SetCursorDrag() {
+
+	r.win.SetStandardCursor(window.HandCursor)
+}
+
+// SetCursorHResize sets the cursor of the associated window to
+// horizontal resize type
+func (r *Root) SetCursorHResize() {
+
+	r.win.SetStandardCursor(window.HResizeCursor)
+}
+
+// SetCursorVResize sets the cursor of the associated window to
+// vertical resize type
+func (r *Root) SetCursorVResize() {
+
+	r.win.SetStandardCursor(window.VResizeCursor)
+}
+
+// onKey is called when key events are received
+func (r *Root) onKey(evname string, ev interface{}) {
+
+	// If no panel has the key focus, nothing to do
+	if r.keyFocus == nil {
+		return
+	}
+	// Dispatch window.KeyEvent to focused panel subscribers
+	r.stopPropagation = 0
+	r.keyFocus.GetPanel().Dispatch(evname, ev)
+	// If requested, stopj propagation of event outside the root gui
+	if (r.stopPropagation & Stop3D) != 0 {
+		r.win.CancelDispatch()
+	}
+}
+
+// onChar is called when char events are received
+func (r *Root) onChar(evname string, ev interface{}) {
+
+	// If no panel has the key focus, nothing to do
+	if r.keyFocus == nil {
+		return
+	}
+	// Dispatch window.CharEvent to focused panel subscribers
+	r.stopPropagation = 0
+	r.keyFocus.GetPanel().Dispatch(evname, ev)
+	// If requested, stopj propagation of event outside the root gui
+	if (r.stopPropagation & Stop3D) != 0 {
+		r.win.CancelDispatch()
+	}
+}
+
+// onMouse is called when mouse button events are received
+func (r *Root) onMouse(evname string, ev interface{}) {
+
+	mev := ev.(*window.MouseEvent)
+	r.sendPanels(mev.Xpos, mev.Ypos, evname, ev)
+}
+
+// onCursor is called when (mouse) cursor events are received
+func (r *Root) onCursor(evname string, ev interface{}) {
+
+	cev := ev.(*window.CursorEvent)
+	r.sendPanels(cev.Xpos, cev.Ypos, evname, ev)
+}
+
+// sendPanel sends mouse or cursor event to focused panel or panels
+// which contains the specified screen position
+func (r *Root) sendPanels(x, y float32, evname string, ev interface{}) {
+
+	// If there is panel with MouseFocus send only to this panel
+	if r.mouseFocus != nil {
+		r.mouseFocus.GetPanel().Dispatch(evname, ev)
+		if (r.stopPropagation & Stop3D) != 0 {
+			r.win.CancelDispatch()
+		}
+		return
+	}
+
+	// Clear list of panels which contains the mouse position
+	r.targets = r.targets[0:0]
+
+	// checkPanel checks recursively if the specified panel and
+	// any of its child contains the mouse position
+	var checkPanel func(ipan IPanel)
+	checkPanel = func(ipan IPanel) {
+		pan := ipan.GetPanel()
+		// If panel not visible or not enabled, ignore
+		if !pan.Visible() || !pan.Enabled() {
+			return
+		}
+		// Checks if this panel contains the mouse position
+		found := pan.ContainsPosition(x, y)
+		if found {
+			r.targets = append(r.targets, ipan)
+		} else {
+			// If OnCursorEnter previously sent, sends OnMouseLeave
+			if pan.cursorEnter {
+				pan.Dispatch(OnCursorLeave, ev)
+				pan.cursorEnter = false
+			}
+			// If mouse button was pressed, sends event informing mouse down outside of the panel
+			if evname == OnMouseDown {
+				pan.Dispatch(OnMouseOut, ev)
+			}
+		}
+		// Checks if any of its children also contains the position
+		for _, child := range pan.Children() {
+			ipan := child.(IPanel)
+			checkPanel(ipan)
+		}
+	}
+
+	// Checks all children of this root node
+	for _, iobj := range r.Node.Children() {
+		ipan, ok := iobj.(IPanel)
+		if !ok {
+			continue
+		}
+		checkPanel(ipan)
+	}
+
+	// No panels found
+	if len(r.targets) == 0 {
+		// If event is mouse click, removes the keyboard focus
+		if evname == OnMouseDown {
+			r.SetKeyFocus(nil)
+		}
+		return
+	}
+
+	// Sorts panels by absolute Z with the most foreground panels first
+	// and sends event to all panels or until a stop is requested
+	sort.Sort(r.targets)
+	r.stopPropagation = 0
+
+	// Send events to panels
+	for _, ipan := range r.targets {
+		ipan.SetRoot(r)
+		pan := ipan.GetPanel()
+		// Cursor position event
+		if evname == OnCursor {
+			pan.Dispatch(evname, ev)
+			if !pan.cursorEnter {
+				pan.Dispatch(OnCursorEnter, ev)
+				pan.cursorEnter = true
+			}
+			// Mouse button event
+		} else {
+			pan.Dispatch(evname, ev)
+		}
+		if (r.stopPropagation & StopGUI) != 0 {
+			break
+		}
+	}
+
+	// Stops propagation of event outside the root gui
+	if (r.stopPropagation & Stop3D) != 0 {
+		r.win.CancelDispatch()
+	}
+}
+
+// onScroll is called when scroll events are received and
+// is responsible to dispatch them to child panels.
+func (r *Root) onScroll(evname string, ev interface{}) {
+
+	// If no panel with the scroll focus, nothing to do
+	if r.scrollFocus == nil {
+		return
+	}
+	// Dispatch event to panel with scroll focus
+	r.scrollFocus.GetPanel().Dispatch(evname, ev)
+
+	// Stops propagation of event outside the root gui
+	if (r.stopPropagation & Stop3D) != 0 {
+		r.win.CancelDispatch()
+	}
+}
+
+// onSize is called when window size events are received
+func (r *Root) onWindowSize(evname string, ev interface{}) {
+
+	// Sends event only to immediate children
+	for _, ipan := range r.Children() {
+		ipan.(IPanel).GetPanel().Dispatch(evname, ev)
+	}
+}
+
+// onFrame is called when window finished swapping frame buffers
+func (r *Root) onFrame(evname string, ev interface{}) {
+
+	r.TimerManager.ProcessTimers()
+}
+
+// For sorting panels by Z coordinate
+type listPanelZ []IPanel
+
+func (p listPanelZ) Len() int      { return len(p) }
+func (p listPanelZ) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
+func (p listPanelZ) Less(i, j int) bool {
+
+	iz := p[i].GetPanel().pospix.Z
+	jz := p[j].GetPanel().pospix.Z
+	return iz < jz
+}

+ 209 - 0
gui/scrollbar.go

@@ -0,0 +1,209 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+	"github.com/g3n/engine/math32"
+	"github.com/g3n/engine/window"
+)
+
+/***************************************
+
+ ScrollBar Panel
+ +--------------------------------+
+ |         scroll button          |
+ |        +--------------+        |
+ |        |              |        |
+ |        |              |        |
+ |        +--------------+        |
+ +--------------------------------+
+
+**/
+
+type ScrollBar struct {
+	Panel                    // Embedded panel
+	style    *ScrollBarStyle // pointer to current style
+	vertical bool            // type of scrollbar
+	button   scrollBarButton // scrollbar button
+}
+
+type scrollBarButton struct {
+	Panel              // Embedded panel
+	sb      *ScrollBar // pointer to parent scroll bar
+	pressed bool       // mouse button pressed flag
+	mouseX  float32    // last mouse click x position
+	mouseY  float32    // last mouse click y position
+}
+
+type ScrollBarStyle struct {
+	Paddings     BorderSizes
+	Borders      BorderSizes
+	BordersColor math32.Color4
+	Color        math32.Color
+	Button       ScrollBarButtonStyle
+}
+
+type ScrollBarButtonStyle struct {
+	Borders      BorderSizes
+	BordersColor math32.Color4
+	Color        math32.Color
+	Size         float32
+}
+
+// NewVScrollBar creates and returns a pointer to a new vertical scroll bar
+// with the specified dimensions.
+func NewVScrollBar(width, height float32) *ScrollBar {
+
+	return newScrollBar(width, height, true)
+}
+
+// NewHScrollBar creates and returns a pointer to a new horizontal scroll bar
+// with the specified dimensions.
+func NewHScrollBar(width, height float32) *ScrollBar {
+
+	return newScrollBar(width, height, false)
+}
+
+// newScrollBar creates and returns a pointer to a new scroll bar panel
+// with the specified width, height, orientation and target.
+func newScrollBar(width, height float32, vertical bool) *ScrollBar {
+
+	sb := new(ScrollBar)
+	sb.initialize(width, height, vertical)
+	return sb
+}
+
+// initialize initializes this scrollbar
+func (sb *ScrollBar) initialize(width, height float32, vertical bool) {
+
+	sb.style = &StyleDefault.ScrollBar
+	sb.vertical = vertical
+	sb.Panel.Initialize(width, height)
+	sb.Panel.Subscribe(OnMouseDown, sb.onMouse)
+
+	// Initialize scrollbar button
+	sb.button.Panel.Initialize(0, 0)
+	sb.button.Panel.Subscribe(OnMouseDown, sb.button.onMouse)
+	sb.button.Panel.Subscribe(OnMouseUp, sb.button.onMouse)
+	sb.button.Panel.Subscribe(OnCursor, sb.button.onCursor)
+	sb.button.SetMargins(1, 1, 1, 1)
+	sb.button.sb = sb
+	sb.Add(&sb.button)
+
+	sb.update()
+	sb.recalc()
+}
+
+// Value returns the current position of the button in the scrollbar
+// The returned value is between 0.0 and 1.0
+func (sb *ScrollBar) Value() float64 {
+
+	if sb.vertical {
+		return float64(sb.button.Position().Y) / (float64(sb.content.Height) - float64(sb.button.height))
+	} else {
+		return float64(sb.button.Position().X) / (float64(sb.content.Width) - float64(sb.button.width))
+	}
+}
+
+// SetValue sets the position of the button of the scrollbar
+// from 0.0 (minimum) to 1.0 (maximum).
+func (sb *ScrollBar) SetValue(v float32) {
+
+	v = math32.Clamp(v, 0.0, 1.0)
+	if sb.vertical {
+		pos := v * (float32(sb.content.Height) - float32(sb.button.height))
+		sb.button.SetPositionY(pos)
+	} else {
+		pos := v * (float32(sb.content.Width) - float32(sb.button.width))
+		sb.button.SetPositionX(pos)
+	}
+}
+
+// onMouse receives subscribed mouse events over the scrollbar outer panel
+func (sb *ScrollBar) onMouse(evname string, ev interface{}) {
+
+	e := ev.(*window.MouseEvent)
+	if e.Button != window.MouseButtonLeft {
+		return
+	}
+	if sb.vertical {
+		posy := e.Ypos - sb.pospix.Y
+		newY := math32.Clamp(posy-(sb.button.height/2), 0, sb.content.Height-sb.button.height)
+		sb.button.SetPositionY(newY)
+	} else {
+		posx := e.Xpos - sb.pospix.X
+		newX := math32.Clamp(posx-(sb.button.width/2), 0, sb.content.Width-sb.button.width)
+		sb.button.SetPositionX(newX)
+	}
+	sb.root.StopPropagation(Stop3D)
+	sb.Dispatch(OnChange, nil)
+}
+
+// recalc recalculates sizes and positions
+func (sb *ScrollBar) recalc() {
+
+	if sb.vertical {
+		sb.button.SetSize(sb.content.Width, sb.style.Button.Size)
+	} else {
+		sb.button.SetSize(sb.style.Button.Size, sb.content.Height)
+	}
+}
+
+// update updates border sizes and colors
+func (sb *ScrollBar) update() {
+
+	sb.SetBordersFrom(&sb.style.Borders)
+	sb.SetPaddingsFrom(&sb.style.Paddings)
+	sb.SetBordersColor4(&sb.style.BordersColor)
+	sb.SetColor(&sb.style.Color)
+
+	sb.button.SetBordersFrom(&sb.style.Button.Borders)
+	sb.button.SetBordersColor4(&sb.style.Button.BordersColor)
+	sb.button.SetColor(&sb.style.Button.Color)
+}
+
+// onMouse receives subscribed mouse events for the scroll bar button
+func (button *scrollBarButton) onMouse(evname string, ev interface{}) {
+
+	e := ev.(*window.MouseEvent)
+	if e.Button != window.MouseButtonLeft {
+		return
+	}
+	switch evname {
+	case OnMouseDown:
+		button.pressed = true
+		button.mouseX = e.Xpos
+		button.mouseY = e.Ypos
+		button.sb.root.SetMouseFocus(button)
+	case OnMouseUp:
+		button.pressed = false
+		button.sb.root.SetMouseFocus(nil)
+	default:
+		return
+	}
+	button.sb.root.StopPropagation(Stop3D)
+}
+
+// onCursor receives subscribed cursor events for the scroll bar button
+func (button *scrollBarButton) onCursor(evname string, ev interface{}) {
+
+	e := ev.(*window.CursorEvent)
+	if !button.pressed {
+		return
+	}
+	if button.sb.vertical {
+		dy := button.mouseY - e.Ypos
+		py := button.Position().Y
+		button.SetPositionY(math32.Clamp(py-dy, 0, button.sb.content.Height-button.sb.style.Button.Size))
+	} else {
+		dx := button.mouseX - e.Xpos
+		px := button.Position().X
+		button.SetPositionX(math32.Clamp(px-dx, 0, button.sb.content.Width-button.sb.style.Button.Size))
+	}
+	button.mouseX = e.Xpos
+	button.mouseY = e.Ypos
+	button.sb.Dispatch(OnChange, nil)
+	button.sb.root.StopPropagation(Stop3D)
+}

+ 611 - 0
gui/scroller.go

@@ -0,0 +1,611 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+	"github.com/g3n/engine/math32"
+	"github.com/g3n/engine/window"
+	"math"
+)
+
+type Scroller struct {
+	Panel                          // Embedded panel
+	vert           bool            // vertical/horizonta scroller flag
+	styles         *ScrollerStyles // pointer to current styles
+	items          []IPanel        // list of panels in the scroller
+	hscroll        *ScrollBar      // horizontal scroll bar
+	vscroll        *ScrollBar      // vertical scroll bar
+	maxAutoWidth   float32         // maximum auto width (if 0, auto width disabled)
+	maxAutoHeight  float32         // maximum auto height (if 0, auto width disabled)
+	first          int             // first visible item position
+	adjustItem     bool            // adjust item to width or height
+	focus          bool            // has keyboard focus
+	cursorOver     bool            // mouse is over the list
+	scrollBarEvent bool
+}
+
+type ScrollerStyle struct {
+	Border      BorderSizes
+	Paddings    BorderSizes
+	BorderColor math32.Color4
+	BgColor     math32.Color
+	FgColor     math32.Color
+}
+
+type ScrollerStyles struct {
+	Normal   ScrollerStyle
+	Over     ScrollerStyle
+	Focus    ScrollerStyle
+	Disabled ScrollerStyle
+}
+
+// NewVScroller creates and returns a pointer to a new vertical scroller panel
+// with the specified dimensions.
+func NewVScroller(width, height float32) *Scroller {
+
+	return newScroller(true, width, height)
+}
+
+// NewHScroller creates and returns a pointer to a new horizontal scroller panel
+// with the specified dimensions.
+func NewHScroller(width, height float32) *Scroller {
+
+	return newScroller(false, width, height)
+}
+
+// newScroller creates and returns a pointer to a new Scroller panel
+// with the specified layout orientation and initial dimensions
+func newScroller(vert bool, width, height float32) *Scroller {
+
+	s := new(Scroller)
+	s.initialize(vert, width, height)
+	return s
+}
+
+// Clear removes and disposes of all the scroller children
+func (s *Scroller) Clear() {
+
+	s.Panel.DisposeChildren(true)
+	s.hscroll = nil
+	s.vscroll = nil
+	s.items = s.items[0:0]
+}
+
+// Len return the number of items in the scroller
+func (s *Scroller) Len() int {
+
+	return len(s.items)
+}
+
+// Add appends the specified item to the end of the scroller
+func (s *Scroller) Add(item IPanel) {
+
+	s.InsertAt(len(s.items), item)
+}
+
+// InsertAt inserts an item at the specified position
+func (s *Scroller) InsertAt(pos int, item IPanel) {
+
+	// Validates position
+	if pos < 0 || pos > len(s.items) {
+		panic("Scroller.InsertAt(): Invalid position")
+	}
+	item.GetPanel().SetVisible(false)
+
+	// Insert item in the items array
+	s.items = append(s.items, nil)
+	copy(s.items[pos+1:], s.items[pos:])
+	s.items[pos] = item
+
+	// Insert item in the scroller
+	s.Panel.Add(item)
+	s.autoSize()
+	s.recalc()
+
+	// Scroll bar should be on the foreground,
+	// in relation of all the other child panels.
+	if s.vscroll != nil {
+		s.Panel.SetTopChild(s.vscroll)
+	}
+	if s.hscroll != nil {
+		s.Panel.SetTopChild(s.hscroll)
+	}
+}
+
+// RemoveAt removes item from the specified position
+func (s *Scroller) RemoveAt(pos int) {
+
+	// Validates position
+	if pos < 0 || pos >= len(s.items) {
+		panic("Scroller.RemoveAt(): Invalid position")
+	}
+
+	// Remove event listener
+	item := s.items[pos]
+
+	// Remove item from the items array
+	copy(s.items[pos:], s.items[pos+1:])
+	s.items[len(s.items)-1] = nil
+	s.items = s.items[:len(s.items)-1]
+
+	// Remove item from the scroller children
+	s.Panel.Remove(item)
+	s.autoSize()
+	s.recalc()
+}
+
+// Remove removes the specified item from the Scroller
+func (s *Scroller) Remove(item IPanel) {
+
+	for p, curr := range s.items {
+		if curr == item {
+			s.RemoveAt(p)
+			return
+		}
+	}
+}
+
+// GetItem returns the item at the specified position.
+// Returns nil if the position is invalid.
+func (s *Scroller) ItemAt(pos int) IPanel {
+
+	if pos < 0 || pos >= len(s.items) {
+		return nil
+	}
+	return s.items[pos]
+}
+
+// ItemPosition returns the position of the specified item in
+// the scroller of -1 if not found
+func (s *Scroller) ItemPosition(item IPanel) int {
+
+	for pos := 0; pos < len(s.items); pos++ {
+		if s.items[pos] == item {
+			return pos
+		}
+	}
+	return -1
+}
+
+// First returns the position of the first visible item
+func (s *Scroller) First() int {
+
+	return s.first
+}
+
+// SetFirst set the position of first visible if possible
+func (s *Scroller) SetFirst(pos int) {
+
+	if pos >= 0 && pos <= s.maxFirst() {
+		s.first = pos
+		s.recalc()
+	}
+}
+
+// ScrollDown scrolls the list down one item if possible
+func (s *Scroller) ScrollDown() {
+
+	max := s.maxFirst()
+	if s.first >= max {
+		return
+	}
+	s.first++
+	s.recalc()
+}
+
+// ScrollUp scrolls the list up one item if possible
+func (s *Scroller) ScrollUp() {
+
+	if s.first == 0 {
+		return
+	}
+	s.first--
+	s.recalc()
+}
+
+// ItemVisible returns indication if the item at the specified
+// position is completely visible or not
+func (s *Scroller) ItemVisible(pos int) bool {
+
+	if pos < s.first {
+		return false
+	}
+
+	// Vertical scroller
+	if s.vert {
+		var height float32 = 0
+		for i := s.first; i < len(s.items); i++ {
+			item := s.items[pos]
+			height += item.GetPanel().height
+			if height > s.height {
+				return false
+			}
+			if pos == i {
+				return true
+			}
+		}
+		return false
+		// Horizontal scroller
+	} else {
+		var width float32 = 0
+		for i := s.first; i < len(s.items); i++ {
+			item := s.items[pos]
+			width += item.GetPanel().width
+			if width > s.width {
+				return false
+			}
+			if pos == i {
+				return true
+			}
+		}
+		return false
+	}
+}
+
+// SetStyles set the scroller styles overriding the default style
+func (s *Scroller) SetStyles(ss *ScrollerStyles) {
+
+	s.styles = ss
+	s.update()
+}
+
+func (s *Scroller) ApplyStyle(style int) {
+
+	switch style {
+	case OverStyle:
+		s.applyStyle(&s.styles.Over)
+	case FocusStyle:
+		s.applyStyle(&s.styles.Focus)
+	case NormalStyle:
+		s.applyStyle(&s.styles.Normal)
+	case DefaultStyle:
+		s.update()
+	}
+}
+
+func (s *Scroller) SetAutoWidth(maxWidth float32) {
+
+	s.maxAutoWidth = maxWidth
+}
+
+func (s *Scroller) SetAutoHeight(maxHeight float32) {
+
+	s.maxAutoHeight = maxHeight
+}
+
+// initialize initializes this scroller and is normally used by other types which contains a scroller
+func (s *Scroller) initialize(vert bool, width, height float32) {
+
+	s.vert = vert
+	s.Panel.Initialize(width, height)
+	s.styles = &StyleDefault.Scroller
+
+	s.Panel.Subscribe(OnCursorEnter, s.onCursor)
+	s.Panel.Subscribe(OnCursorLeave, s.onCursor)
+	s.Panel.Subscribe(OnScroll, s.onScroll)
+	s.Panel.Subscribe(OnResize, s.onResize)
+
+	s.update()
+	s.recalc()
+}
+
+// onCursor receives subscribed cursor events over the panel
+func (s *Scroller) onCursor(evname string, ev interface{}) {
+
+	switch evname {
+	case OnCursorEnter:
+		s.root.SetScrollFocus(s)
+		s.cursorOver = true
+		s.update()
+	case OnCursorLeave:
+		s.root.SetScrollFocus(nil)
+		s.cursorOver = false
+		s.update()
+	}
+	s.root.StopPropagation(Stop3D)
+}
+
+// onScroll receives subscriber mouse scroll events when this scroller has
+// the scroll focus (set by OnMouseEnter)
+func (s *Scroller) onScroll(evname string, ev interface{}) {
+
+	sev := ev.(*window.ScrollEvent)
+	if sev.Yoffset > 0 {
+		s.ScrollUp()
+	} else if sev.Yoffset < 0 {
+		s.ScrollDown()
+	}
+	s.root.StopPropagation(Stop3D)
+}
+
+// onScroll receives resize events
+func (s *Scroller) onResize(evname string, ev interface{}) {
+
+	s.recalc()
+}
+
+// autoSize resizes the scroller if necessary
+func (s *Scroller) autoSize() {
+
+	if s.maxAutoWidth == 0 && s.maxAutoHeight == 0 {
+		return
+	}
+
+	var width float32 = 0
+	var height float32 = 0
+	for _, item := range s.items {
+		panel := item.GetPanel()
+		if panel.Width() > width {
+			width = panel.Width()
+		}
+		height += panel.TotalHeight()
+	}
+
+	// If auto maximum width enabled
+	if s.maxAutoWidth > 0 {
+		if width <= s.maxAutoWidth {
+			s.SetContentWidth(width)
+		}
+	}
+	// If auto maximum height enabled
+	if s.maxAutoHeight > 0 {
+		if height <= s.maxAutoHeight {
+			s.SetContentHeight(height)
+		}
+	}
+}
+
+// recalc recalculates the positions and visibilities of all the items
+func (s *Scroller) recalc() {
+
+	if s.vert {
+		s.vRecalc()
+	} else {
+		s.hRecalc()
+	}
+}
+
+// vRecalc recalculates for the vertical scroller
+func (s *Scroller) vRecalc() {
+
+	// Checks if scroll bar should be visible or not
+	scroll := false
+	if s.first > 0 {
+		scroll = true
+	} else {
+		var posY float32 = 0
+		for _, item := range s.items[s.first:] {
+			posY += item.TotalHeight()
+			if posY >= s.height {
+				scroll = true
+				break
+			}
+		}
+	}
+	s.setVScrollBar(scroll)
+	// Items width
+	width := s.ContentWidth()
+	if scroll {
+		width -= s.vscroll.Width()
+	}
+
+	var posY float32 = 0
+	// Sets positions of all items
+	for pos, ipan := range s.items {
+		item := ipan.GetPanel()
+		if pos < s.first {
+			item.SetVisible(false)
+			continue
+		}
+		// If item is after last visible, sets not visible
+		if posY > s.height {
+			item.SetVisible(false)
+			continue
+		}
+		// Sets item position
+		item.SetVisible(true)
+		item.SetPosition(0, posY)
+		if s.adjustItem {
+			item.SetWidth(width)
+		}
+		posY += ipan.TotalHeight()
+	}
+
+	// Set scroll bar value if recalc was not due by scroll event
+	if scroll && !s.scrollBarEvent {
+		s.vscroll.SetValue(float32(s.first) / float32(s.maxFirst()))
+	}
+	s.scrollBarEvent = false
+}
+
+// hRecalc recalculates for the horizontal scroller
+func (s *Scroller) hRecalc() {
+
+	// Checks if scroll bar should be visible or not
+	scroll := false
+	if s.first > 0 {
+		scroll = true
+	} else {
+		var posX float32 = 0
+		for _, item := range s.items[s.first:] {
+			posX += item.GetPanel().Width()
+			if posX >= s.width {
+				scroll = true
+				break
+			}
+		}
+	}
+	s.setHScrollBar(scroll)
+	// Items height
+	height := s.ContentHeight()
+	if scroll {
+		height -= s.hscroll.Height()
+	}
+
+	var posX float32 = 0
+	// Sets positions of all items
+	for pos, ipan := range s.items {
+		item := ipan.GetPanel()
+		// If item is before first visible, sets not visible
+		if pos < s.first {
+			item.SetVisible(false)
+			continue
+		}
+		// If item is after last visible, sets not visible
+		if posX > s.width {
+			item.SetVisible(false)
+			continue
+		}
+		// Sets item position
+		item.SetVisible(true)
+		item.SetPosition(posX, 0)
+		if s.adjustItem {
+			item.SetHeight(height)
+		}
+		posX += item.Width()
+	}
+
+	// Set scroll bar value if recalc was not due by scroll event
+	if scroll && !s.scrollBarEvent {
+		s.hscroll.SetValue(float32(s.first) / float32(s.maxFirst()))
+	}
+	s.scrollBarEvent = false
+}
+
+// maxFirst returns the maximum position of the first visible item
+func (s *Scroller) maxFirst() int {
+
+	// Vertical scroller
+	if s.vert {
+		var height float32 = 0
+		pos := len(s.items) - 1
+		if pos < 0 {
+			return 0
+		}
+		for {
+			item := s.items[pos]
+			height += item.GetPanel().Height()
+			if height > s.Height() {
+				break
+			}
+			pos--
+			if pos < 0 {
+				break
+			}
+		}
+		return pos + 1
+		// Horizontal scroller
+	} else {
+		var width float32 = 0
+		pos := len(s.items) - 1
+		if pos < 0 {
+			return 0
+		}
+		for {
+			item := s.items[pos]
+			width += item.GetPanel().Width()
+			if width > s.Width() {
+				break
+			}
+			pos--
+			if pos < 0 {
+				break
+			}
+		}
+		return pos + 1
+	}
+}
+
+// setVScrollBar sets the visibility state of the vertical scrollbar
+func (s *Scroller) setVScrollBar(state bool) {
+
+	// Visible
+	if state {
+		var scrollWidth float32 = 20
+		if s.vscroll == nil {
+			s.vscroll = NewVScrollBar(0, 0)
+			s.vscroll.SetBorders(0, 0, 0, 1)
+			s.vscroll.Subscribe(OnChange, s.onScrollBarEvent)
+			s.Panel.Add(s.vscroll)
+		}
+		s.vscroll.SetSize(scrollWidth, s.ContentHeight())
+		s.vscroll.SetPositionX(s.ContentWidth() - scrollWidth)
+		s.vscroll.SetPositionY(0)
+		s.vscroll.recalc()
+		s.vscroll.SetVisible(true)
+		// Not visible
+	} else {
+		if s.vscroll != nil {
+			s.vscroll.SetVisible(false)
+		}
+	}
+}
+
+// setHScrollBar sets the visibility state of the horizontal scrollbar
+func (s *Scroller) setHScrollBar(state bool) {
+
+	// Visible
+	if state {
+		var scrollHeight float32 = 20
+		if s.hscroll == nil {
+			s.hscroll = NewHScrollBar(0, 0)
+			s.hscroll.SetBorders(1, 0, 0, 0)
+			s.hscroll.Subscribe(OnChange, s.onScrollBarEvent)
+			s.Panel.Add(s.hscroll)
+		}
+		s.hscroll.SetSize(s.ContentWidth(), scrollHeight)
+		s.hscroll.SetPositionX(0)
+		s.hscroll.SetPositionY(s.ContentHeight() - scrollHeight)
+		s.hscroll.recalc()
+		s.hscroll.SetVisible(true)
+		// Not visible
+	} else {
+		if s.hscroll != nil {
+			s.hscroll.SetVisible(false)
+		}
+	}
+}
+
+// onScrollEvent is called when the list scrollbar value changes
+func (s *Scroller) onScrollBarEvent(evname string, ev interface{}) {
+
+	var pos float64
+	if s.vert {
+		pos = s.vscroll.Value()
+	} else {
+		pos = s.hscroll.Value()
+	}
+
+	first := int(math.Floor((float64(s.maxFirst()) * pos) + 0.5))
+	if first == s.first {
+		return
+	}
+	s.scrollBarEvent = true
+	s.first = first
+	s.recalc()
+}
+
+// update updates the visual state the list and its items
+func (s *Scroller) update() {
+
+	if s.cursorOver {
+		s.applyStyle(&s.styles.Over)
+		return
+	}
+	if s.focus {
+		s.applyStyle(&s.styles.Focus)
+		return
+	}
+	s.applyStyle(&s.styles.Normal)
+}
+
+// applyStyle sets the specified style
+func (s *Scroller) applyStyle(st *ScrollerStyle) {
+
+	s.SetBordersFrom(&st.Border)
+	s.SetBordersColor4(&st.BorderColor)
+	s.SetPaddingsFrom(&st.Paddings)
+	s.SetColor(&st.BgColor)
+}

+ 327 - 0
gui/slider.go

@@ -0,0 +1,327 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+	"github.com/g3n/engine/math32"
+	"github.com/g3n/engine/window"
+)
+
+/***************************************
+
+ Slider
+ +--------------------------------+
+ |  +--------------------------+  |
+ |  |      +----------+        |  |
+ |  |      |          |        |  |
+ |  |      |          |        |  |
+ |  |      +----------+        |  |
+ |  +--------------------------+  |
+ +--------------------------------+
+
+**/
+
+type Slider struct {
+	Panel                     // Embedded panel
+	slider      Panel         // embedded slider panel
+	label       *Label        // optional label
+	horiz       bool          // orientation
+	styles      *SliderStyles // pointer to styles
+	pos         float32       // current slider position
+	posLast     float32       // last position of the mouse cursor when dragging
+	pressed     bool          // mouse button is pressed and dragging
+	cursorOver  bool          // mouse is over slider
+	scaleFactor float32       // scale factor (default = 1.0)
+}
+
+// SliderStyle
+type SliderStyle struct {
+	Border      BorderSizes   // outer panel border sizes
+	BorderColor math32.Color4 // outer panel border colors
+	Paddings    BorderSizes   // outer panel padding sizes
+	BgColor     math32.Color4 // outer panel color
+	FgColor     math32.Color4 // slider panel color
+}
+
+// All Slider styles
+type SliderStyles struct {
+	Normal   SliderStyle
+	Over     SliderStyle
+	Focus    SliderStyle
+	Disabled SliderStyle
+}
+
+// NewHSlider creates and returns a pointer to a new horizontal slider
+// with the specified initial dimensions.
+func NewHSlider(width, height float32) *Slider {
+
+	return newSlider(true, width, height)
+}
+
+// NewVSlider creates and returns a pointer to a new vertical slider
+// with the specified initial dimensions.
+func NewVSlider(width, height float32) *Slider {
+
+	return newSlider(false, width, height)
+}
+
+// NewSlider creates and returns a pointer to a new slider with the
+// specified initial dimensions.
+func newSlider(horiz bool, width, height float32) *Slider {
+
+	s := new(Slider)
+	s.horiz = horiz
+	s.styles = &StyleDefault.Slider
+	s.scaleFactor = 1.0
+
+	// Initialize main panel
+	s.Panel.Initialize(width, height)
+	s.Panel.Subscribe(OnMouseDown, s.onMouse)
+	s.Panel.Subscribe(OnMouseUp, s.onMouse)
+	s.Panel.Subscribe(OnCursor, s.onCursor)
+	s.Panel.Subscribe(OnCursorEnter, s.onCursor)
+	s.Panel.Subscribe(OnCursorLeave, s.onCursor)
+	s.Panel.Subscribe(OnScroll, s.onScroll)
+	s.Panel.Subscribe(OnKeyDown, s.onKey)
+	s.Panel.Subscribe(OnResize, s.onResize)
+
+	// Initialize slider panel
+	s.slider.Initialize(0, 0)
+	s.Panel.Add(&s.slider)
+
+	s.recalc()
+	s.update()
+	return s
+}
+
+// SetStyles set the slider styles overriding the default style
+func (s *Slider) SetStyles(ss *SliderStyles) {
+
+	s.styles = ss
+	s.update()
+}
+
+// SetText sets the text of the slider optional label
+func (s *Slider) SetText(text string) {
+
+	if s.label == nil {
+		s.label = NewLabel(text)
+		s.Panel.Add(s.label)
+	} else {
+		s.label.SetText(text)
+	}
+	s.update()
+	s.recalc()
+}
+
+// SetValue sets the value of the slider considering the current scale factor
+// and updates its visual appearance.
+func (s *Slider) SetValue(value float32) {
+
+	pos := value / s.scaleFactor
+	s.setPos(pos)
+}
+
+// Value returns the current value of the slider considering the current scale factor
+func (s *Slider) Value() float32 {
+
+	return s.pos * s.scaleFactor
+}
+
+// SetScaleFactor set the slider scale factor (default = 1.0)
+func (s *Slider) SetScaleFactor(factor float32) {
+
+	s.scaleFactor = factor
+}
+
+// ScaleFactor returns  the slider current scale factor (default = 1.0)
+func (s *Slider) ScaleFactor() float32 {
+
+	return s.scaleFactor
+}
+
+// setPos sets the slider position from 0.0 to 1.0
+// and updates its visual appearance.
+func (s *Slider) setPos(pos float32) {
+
+	const eps = 0.01
+	if pos < 0 {
+		pos = 0
+	} else if pos > 1.0 {
+		pos = 1
+	}
+	if pos > (s.pos+eps) && pos < (s.pos+eps) {
+		return
+	}
+	s.pos = pos
+	s.recalc()
+	s.Dispatch(OnChange, nil)
+}
+
+// onMouse process subscribed mouse events over the outer panel
+func (s *Slider) onMouse(evname string, ev interface{}) {
+
+	mev := ev.(*window.MouseEvent)
+	switch evname {
+	case OnMouseDown:
+		s.pressed = true
+		if s.horiz {
+			s.posLast = mev.Xpos
+		} else {
+			s.posLast = mev.Ypos
+		}
+		s.root.SetMouseFocus(s)
+		s.root.SetKeyFocus(s)
+	case OnMouseUp:
+		s.pressed = false
+		if !s.cursorOver {
+			s.root.SetCursorNormal()
+		}
+		s.root.SetMouseFocus(nil)
+	default:
+		return
+	}
+	s.root.StopPropagation(Stop3D)
+}
+
+// onCursor process subscribed cursor events
+func (s *Slider) onCursor(evname string, ev interface{}) {
+
+	cev := ev.(*window.CursorEvent)
+	switch evname {
+	case OnCursorEnter:
+		s.root.SetScrollFocus(s)
+		if s.horiz {
+			s.root.SetCursorHResize()
+		} else {
+			s.root.SetCursorVResize()
+		}
+		s.cursorOver = true
+		s.update()
+	case OnCursorLeave:
+		s.root.SetScrollFocus(nil)
+		s.root.SetCursorNormal()
+		s.cursorOver = false
+		s.update()
+	case OnCursor:
+		if !s.pressed {
+			return
+		}
+		var pos float32
+		if s.horiz {
+			delta := cev.Xpos - s.posLast
+			s.posLast = cev.Xpos
+			newpos := s.slider.Width() + delta
+			pos = newpos / s.Panel.ContentWidth()
+		} else {
+			delta := cev.Ypos - s.posLast
+			s.posLast = cev.Ypos
+			newpos := s.slider.Height() - delta
+			pos = newpos / s.Panel.ContentHeight()
+		}
+		s.setPos(pos)
+	default:
+		return
+	}
+	s.root.StopPropagation(Stop3D)
+}
+
+// onScroll process subscribed scroll events
+func (s *Slider) onScroll(evname string, ev interface{}) {
+
+	sev := ev.(*window.ScrollEvent)
+	v := s.pos
+	v += sev.Yoffset * 0.01
+	s.setPos(v)
+	s.root.StopPropagation(Stop3D)
+}
+
+// onKey process subscribed key events
+func (s *Slider) onKey(evname string, ev interface{}) {
+
+	kev := ev.(*window.KeyEvent)
+	delta := float32(0.01)
+	// Horizontal slider
+	if s.horiz {
+		switch kev.Keycode {
+		case window.KeyLeft:
+			s.setPos(s.pos - delta)
+		case window.KeyRight:
+			s.setPos(s.pos + delta)
+		default:
+			return
+		}
+		// Vertical slider
+	} else {
+		switch kev.Keycode {
+		case window.KeyDown:
+			s.setPos(s.pos - delta)
+		case window.KeyUp:
+			s.setPos(s.pos + delta)
+		default:
+			return
+		}
+	}
+	s.root.StopPropagation(Stop3D)
+}
+
+// onResize process subscribed resize events
+func (s *Slider) onResize(evname string, ev interface{}) {
+
+	s.recalc()
+}
+
+// update updates the slider visual state
+func (s *Slider) update() {
+
+	if !s.Enabled() {
+		s.applyStyle(&s.styles.Disabled)
+		return
+	}
+	if s.cursorOver {
+		s.applyStyle(&s.styles.Over)
+		return
+	}
+	s.applyStyle(&s.styles.Normal)
+}
+
+// applyStyle applies the specified slider style
+func (s *Slider) applyStyle(ss *SliderStyle) {
+
+	s.SetBordersColor4(&ss.BorderColor)
+	s.SetBordersFrom(&ss.Border)
+	s.SetPaddingsFrom(&ss.Paddings)
+	s.Panel.SetColor4(&ss.BgColor)
+	s.slider.SetColor4(&ss.FgColor)
+}
+
+// recalc recalculates the dimensions and positions of the internal panels.
+func (s *Slider) recalc() {
+
+	if s.horiz {
+		if s.label != nil {
+			lx := (s.Panel.ContentWidth() - s.label.Width()) / 2
+			if s.Panel.ContentHeight() < s.label.Height() {
+				s.Panel.SetContentHeight(s.label.Height())
+			}
+			ly := (s.Panel.ContentHeight() - s.label.Height()) / 2
+			s.label.SetPosition(lx, ly)
+		}
+		width := s.Panel.ContentWidth() * s.pos
+		s.slider.SetSize(width, s.Panel.ContentHeight())
+	} else {
+		if s.label != nil {
+			if s.Panel.ContentWidth() < s.label.Width() {
+				s.Panel.SetContentWidth(s.label.Width())
+			}
+			lx := (s.Panel.ContentWidth() - s.label.Width()) / 2
+			ly := (s.Panel.ContentHeight() - s.label.Height()) / 2
+			s.label.SetPosition(lx, ly)
+		}
+		height := s.Panel.ContentHeight() * s.pos
+		s.slider.SetPositionY(s.Panel.ContentHeight() - height)
+		s.slider.SetSize(s.Panel.ContentWidth(), height)
+	}
+}

+ 252 - 0
gui/splitter.go

@@ -0,0 +1,252 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+	"github.com/g3n/engine/math32"
+	"github.com/g3n/engine/window"
+)
+
+type Splitter struct {
+	Panel                     // Embedded panel
+	P0        Panel           // Left/Top panel
+	P1        Panel           // Right/Bottom panel
+	styles    *SplitterStyles // pointer to current styles
+	spacer    Panel           // spacer panel
+	horiz     bool            // horizontal or vertical splitter
+	pos       float32         // central position of the spacer panel bar in pixels
+	posLast   float32         // last position of the mouse cursor when dragging
+	pressed   bool            // mouse button is pressed and dragging
+	mouseOver bool            // mouse is over the spacer panel
+}
+
+type SplitterStyle struct {
+	SpacerBorderColor math32.Color4
+	SpacerColor       math32.Color
+	SpacerSize        float32
+}
+
+type SplitterStyles struct {
+	Normal SplitterStyle
+	Over   SplitterStyle
+	Drag   SplitterStyle
+}
+
+// NewHSplitter creates and returns a pointer to a new horizontal splitter
+// widget with the specified initial dimensions
+func NewHSplitter(width, height float32) *Splitter {
+
+	return newSplitter(true, width, height)
+}
+
+// NewVSplitter creates and returns a pointer to a new vertical splitter
+// widget with the specified initial dimensions
+func NewVSplitter(width, height float32) *Splitter {
+
+	return newSplitter(false, width, height)
+}
+
+// newSpliter creates and returns a pointer of a new splitter with
+// the specified orientation and initial dimensions.
+func newSplitter(horiz bool, width, height float32) *Splitter {
+
+	s := new(Splitter)
+	s.horiz = horiz
+	s.styles = &StyleDefault.Splitter
+	s.Panel.Initialize(width, height)
+
+	// Initialize left/top panel
+	s.P0.Initialize(0, 0)
+	s.Panel.Add(&s.P0)
+
+	// Initialize right/bottom panel
+	s.P1.Initialize(0, 0)
+	s.Panel.Add(&s.P1)
+
+	// Initialize spacer panel
+	s.spacer.Initialize(0, 0)
+	s.Panel.Add(&s.spacer)
+
+	if horiz {
+		s.spacer.SetBorders(0, 1, 0, 1)
+		s.spacer.SetWidth(6)
+		s.pos = width / 2
+	} else {
+		s.spacer.SetBorders(1, 0, 1, 0)
+		s.spacer.SetHeight(6)
+		s.pos = height / 2
+	}
+
+	s.spacer.Subscribe(OnMouseDown, s.onMouse)
+	s.spacer.Subscribe(OnMouseUp, s.onMouse)
+	s.spacer.Subscribe(OnCursor, s.onCursor)
+	s.spacer.Subscribe(OnCursorEnter, s.onCursor)
+	s.spacer.Subscribe(OnCursorLeave, s.onCursor)
+	s.update()
+	s.recalc()
+	return s
+}
+
+// SetSplit sets the position of the splitter bar.
+// It accepts a value from 0.0 to 1.0
+func (s *Splitter) SetSplit(pos float32) {
+
+	if s.horiz {
+		s.setSplit(pos * s.Width())
+	} else {
+		s.setSplit(pos * s.Height())
+	}
+	s.recalc()
+}
+
+// Split returns the current position of the splitter bar.
+// It returns a value from 0.0 to 1.0
+func (s *Splitter) Split() float32 {
+
+	var pos float32
+	if s.horiz {
+		pos = s.pos / s.Width()
+	} else {
+		pos = s.pos / s.Height()
+	}
+	return pos
+}
+
+// onMouse receives subscribed mouse events over the spacer panel
+func (s *Splitter) onMouse(evname string, ev interface{}) {
+
+	mev := ev.(*window.MouseEvent)
+	switch evname {
+	case OnMouseDown:
+		s.pressed = true
+		if s.horiz {
+			s.posLast = mev.Xpos
+		} else {
+			s.posLast = mev.Ypos
+		}
+		s.root.SetMouseFocus(&s.spacer)
+	case OnMouseUp:
+		s.pressed = false
+		s.root.SetCursorNormal()
+		s.root.SetMouseFocus(nil)
+	default:
+	}
+	s.root.StopPropagation(Stop3D)
+}
+
+// onCursor receives subscribed cursor events over the spacer panel
+func (s *Splitter) onCursor(evname string, ev interface{}) {
+
+	cev := ev.(*window.CursorEvent)
+	switch evname {
+	case OnCursorEnter:
+		if s.horiz {
+			s.root.SetCursorHResize()
+		} else {
+			s.root.SetCursorVResize()
+		}
+		s.mouseOver = true
+		s.update()
+	case OnCursorLeave:
+		s.root.SetCursorNormal()
+		s.mouseOver = false
+		s.update()
+	case OnCursor:
+		if !s.pressed {
+			return
+		}
+		var delta float32
+		if s.horiz {
+			delta = cev.Xpos - s.posLast
+			s.posLast = cev.Xpos
+		} else {
+			delta = cev.Ypos - s.posLast
+			s.posLast = cev.Ypos
+		}
+		s.setSplit(s.pos + delta)
+		s.recalc()
+	default:
+		return
+	}
+	s.root.StopPropagation(Stop3D)
+}
+
+// setSplit sets the validated and clamped split position from the received value.
+func (s *Splitter) setSplit(pos float32) {
+
+	var max float32
+	var halfspace float32
+	if s.horiz {
+		halfspace = s.spacer.Width() / 2
+		max = s.Width()
+	} else {
+		halfspace = s.spacer.Height() / 2
+		max = s.Height()
+	}
+
+	if pos > max-halfspace {
+		s.pos = max - halfspace
+	} else if pos <= halfspace {
+		s.pos = halfspace
+	} else {
+		s.pos = pos
+	}
+}
+
+// update updates the splitter visual state
+func (s *Splitter) update() {
+
+	if s.pressed {
+		s.applyStyle(&s.styles.Drag)
+		return
+	}
+	if s.mouseOver {
+		s.applyStyle(&s.styles.Over)
+		return
+	}
+	s.applyStyle(&s.styles.Normal)
+}
+
+// applyStyle applies the specified splitter style
+func (s *Splitter) applyStyle(ss *SplitterStyle) {
+
+	s.spacer.SetBordersColor4(&ss.SpacerBorderColor)
+	s.spacer.SetColor(&ss.SpacerColor)
+	if s.horiz {
+		s.spacer.SetWidth(ss.SpacerSize)
+	} else {
+		s.spacer.SetHeight(ss.SpacerSize)
+	}
+}
+
+// recalc relcalculates the position and sizes of the internal panels
+func (s *Splitter) recalc() {
+
+	width := s.Width()
+	height := s.Height()
+	if s.horiz {
+		halfspace := s.spacer.Width() / 2
+		// First panel
+		s.P0.SetPosition(0, 0)
+		s.P0.SetSize(s.pos-halfspace, height)
+		// Spacer panel
+		s.spacer.SetPosition(s.pos-halfspace, 0)
+		s.spacer.SetHeight(height)
+		// Second panel
+		s.P1.SetPosition(s.pos+halfspace, 0)
+		s.P1.SetSize(width-s.pos-halfspace, height)
+	} else {
+		halfspace := s.spacer.Height() / 2
+		// First panel
+		s.P0.SetPosition(0, 0)
+		s.P0.SetSize(width, s.pos-halfspace)
+		// Spacer panel
+		s.spacer.SetPosition(0, s.pos-halfspace)
+		s.spacer.SetWidth(width)
+		// Second panel
+		s.P1.SetPosition(0, s.pos+halfspace)
+		s.P1.SetSize(width, height-s.pos-halfspace)
+	}
+}

+ 710 - 0
gui/style.go

@@ -0,0 +1,710 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+	"github.com/g3n/engine/gui/assets"
+	"github.com/g3n/engine/math32"
+	"github.com/g3n/engine/text"
+)
+
+func init() {
+
+	setupDefaultStyle()
+}
+
+// Pointer to default style
+var StyleDefault *Style
+
+// All styles
+type Style struct {
+	Font          *text.Font
+	FontIcon      *text.Font
+	Button        ButtonStyles
+	CheckRadio    CheckRadioStyles
+	Edit          EditStyles
+	ScrollBar     ScrollBarStyle
+	Slider        SliderStyles
+	Splitter      SplitterStyles
+	Window        WindowStyles
+	Scroller      ScrollerStyles
+	List          ListStyles
+	DropDown      DropDownStyles
+	Folder        FolderStyles
+	Tree          TreeStyles
+	ControlFolder ControlFolderStyles
+}
+
+const (
+	defaultFont     = "fonts/FreeSans.ttf"
+	defaultFontBold = "fonts/FreeSansBold.ttf"
+	defaultFontIcon = "fonts/MaterialIcons-Regular.ttf"
+)
+
+const (
+	OverStyle = iota + 1
+	FocusStyle
+	DisabledStyle
+	NormalStyle
+	DefaultStyle
+)
+
+// setupDefaultStyle initializes the default Gui global styles
+func setupDefaultStyle() {
+
+	StyleDefault = &Style{}
+
+	// Creates Default Font
+	fontData := assets.MustAsset(defaultFont)
+	font, err := text.NewFontFromData(fontData)
+	if err != nil {
+		panic(err)
+	}
+	font.SetLineSpacing(1.0)
+	font.SetSize(14)
+	font.SetDPI(72)
+	font.SetFgColor4(&math32.Color4{0, 0, 0, 1})
+	font.SetBgColor4(&math32.Color4{1, 1, 1, 0})
+	StyleDefault.Font = font
+
+	// Creates Icon Font
+	fontIconData := assets.MustAsset(defaultFontIcon)
+	fontIcon, err := text.NewFontFromData(fontIconData)
+	if err != nil {
+		panic(err)
+	}
+	fontIcon.SetLineSpacing(1.0)
+	fontIcon.SetSize(14)
+	fontIcon.SetDPI(72)
+	fontIcon.SetFgColor4(&math32.Color4{0, 0, 0, 1})
+	fontIcon.SetBgColor4(&math32.Color4{1, 1, 1, 1})
+	StyleDefault.FontIcon = fontIcon
+
+	borderSizes := BorderSizes{1, 1, 1, 1}
+	borderColor := math32.Color4{0, 0, 0, 1}
+	borderColorDis := math32.Color4{0.4, 0.4, 0.4, 1}
+
+	bgColor := math32.Color{0.8, 0.8, 0.8}
+	bgColor4 := math32.Color4{0, 0, 0, 0}
+	bgColorOver := math32.Color{1, 1, 1}
+	bgColor4Over := math32.Color4{1, 1, 1, 0.5}
+	bgColor4Sel := math32.Color4{0.6, 0.6, 0.6, 1}
+
+	fgColor := math32.Color{0, 0, 0}
+	fgColorSel := math32.Color{0, 0, 0}
+	fgColorDis := math32.Color{0.3, 0.3, 0.3}
+
+	// Button styles
+	StyleDefault.Button = ButtonStyles{
+		Normal: ButtonStyle{
+			Border:      borderSizes,
+			Paddings:    BorderSizes{2, 4, 2, 4},
+			BorderColor: borderColor,
+			BgColor:     bgColor,
+			FgColor:     fgColor,
+		},
+		Over: ButtonStyle{
+			Border:      borderSizes,
+			Paddings:    BorderSizes{2, 4, 2, 4},
+			BorderColor: borderColor,
+			BgColor:     bgColorOver,
+			FgColor:     fgColor,
+		},
+		Focus: ButtonStyle{
+			Border:      borderSizes,
+			Paddings:    BorderSizes{2, 4, 2, 4},
+			BorderColor: borderColor,
+			BgColor:     bgColorOver,
+			FgColor:     fgColor,
+		},
+		Pressed: ButtonStyle{
+			Border:      BorderSizes{2, 2, 2, 2},
+			Paddings:    BorderSizes{2, 4, 2, 4},
+			BorderColor: borderColor,
+			BgColor:     bgColorOver,
+			FgColor:     fgColor,
+		},
+		Disabled: ButtonStyle{
+			Border:      borderSizes,
+			Paddings:    BorderSizes{2, 4, 2, 4},
+			BorderColor: borderColorDis,
+			BgColor:     bgColor,
+			FgColor:     fgColorDis,
+		},
+	}
+
+	// CheckRadio styles
+	StyleDefault.CheckRadio = CheckRadioStyles{
+		Normal: CheckRadioStyle{
+			Border:      BorderSizes{0, 0, 0, 0},
+			Paddings:    BorderSizes{0, 0, 0, 0},
+			BorderColor: borderColor,
+			BgColor:     bgColor4,
+			FgColor:     fgColor,
+		},
+		Over: CheckRadioStyle{
+			Border:      BorderSizes{0, 0, 0, 0},
+			Paddings:    BorderSizes{0, 0, 0, 0},
+			BorderColor: borderColor,
+			BgColor:     bgColor4Over,
+			FgColor:     fgColor,
+		},
+		Focus: CheckRadioStyle{
+			Border:      BorderSizes{0, 0, 0, 0},
+			Paddings:    BorderSizes{0, 0, 0, 0},
+			BorderColor: borderColor,
+			BgColor:     bgColor4Over,
+			FgColor:     fgColor,
+		},
+		Disabled: CheckRadioStyle{
+			Border:      BorderSizes{0, 0, 0, 0},
+			Paddings:    BorderSizes{0, 0, 0, 0},
+			BorderColor: borderColor,
+			BgColor:     bgColor4,
+			FgColor:     fgColorDis,
+		},
+	}
+
+	// Edit styles
+	StyleDefault.Edit = EditStyles{
+		Normal: EditStyle{
+			Border:      BorderSizes{1, 1, 1, 1},
+			Paddings:    BorderSizes{0, 0, 0, 0},
+			BorderColor: borderColor,
+			BgColor:     bgColor,
+			BgAlpha:     1.0,
+			FgColor:     fgColor,
+			HolderColor: math32.Color{0.4, 0.4, 0.4},
+		},
+		Over: EditStyle{
+			Border:      BorderSizes{1, 1, 1, 1},
+			Paddings:    BorderSizes{0, 0, 0, 0},
+			BorderColor: borderColor,
+			BgColor:     bgColorOver,
+			BgAlpha:     1.0,
+			FgColor:     fgColor,
+			HolderColor: math32.Color{0.4, 0.4, 0.4},
+		},
+		Focus: EditStyle{
+			Border:      BorderSizes{1, 1, 1, 1},
+			Paddings:    BorderSizes{0, 0, 0, 0},
+			BorderColor: borderColor,
+			BgColor:     bgColorOver,
+			BgAlpha:     1.0,
+			FgColor:     fgColor,
+			HolderColor: math32.Color{0.4, 0.4, 0.4},
+		},
+		Disabled: EditStyle{
+			Border:      BorderSizes{1, 1, 1, 1},
+			Paddings:    BorderSizes{0, 0, 0, 0},
+			BorderColor: borderColor,
+			BgColor:     bgColor,
+			BgAlpha:     1.0,
+			FgColor:     fgColorDis,
+			HolderColor: math32.Color{0.4, 0.4, 0.4},
+		},
+	}
+
+	// ScrollBar style
+	StyleDefault.ScrollBar = ScrollBarStyle{
+		Paddings:     BorderSizes{1, 1, 1, 1},
+		Borders:      BorderSizes{1, 1, 1, 1},
+		BordersColor: borderColor,
+		Color:        math32.Color{0.8, 0.8, 0.8},
+		Button: ScrollBarButtonStyle{
+			Borders:      BorderSizes{1, 1, 1, 1},
+			BordersColor: borderColor,
+			Color:        math32.Color{0.5, 0.5, 0.5},
+			Size:         30,
+		},
+	}
+
+	// Slider styles
+	StyleDefault.Slider = SliderStyles{
+		Normal: SliderStyle{
+			Border:      borderSizes,
+			BorderColor: borderColor,
+			Paddings:    BorderSizes{0, 0, 0, 0},
+			BgColor:     math32.Color4{0.8, 0.8, 0.8, 1},
+			FgColor:     math32.Color4{0, 0.8, 0, 1},
+		},
+		Over: SliderStyle{
+			Border:      borderSizes,
+			BorderColor: borderColor,
+			Paddings:    BorderSizes{0, 0, 0, 0},
+			BgColor:     math32.Color4{1, 1, 1, 1},
+			FgColor:     math32.Color4{0, 1, 0, 1},
+		},
+		Focus: SliderStyle{
+			Border:      borderSizes,
+			BorderColor: borderColor,
+			Paddings:    BorderSizes{0, 0, 0, 0},
+			BgColor:     math32.Color4{1, 1, 1, 1},
+			FgColor:     math32.Color4{0, 1, 0, 1},
+		},
+		Disabled: SliderStyle{
+			Border:      borderSizes,
+			BorderColor: borderColor,
+			Paddings:    BorderSizes{0, 0, 0, 0},
+			BgColor:     math32.Color4{0.8, 0.8, 0.8, 1},
+			FgColor:     math32.Color4{0, 0.8, 0, 1},
+		},
+	}
+
+	// Splitter styles
+	StyleDefault.Splitter = SplitterStyles{
+		Normal: SplitterStyle{
+			SpacerBorderColor: borderColor,
+			SpacerColor:       bgColor,
+			SpacerSize:        6,
+		},
+		Over: SplitterStyle{
+			SpacerBorderColor: borderColor,
+			SpacerColor:       bgColorOver,
+			SpacerSize:        6,
+		},
+		Drag: SplitterStyle{
+			SpacerBorderColor: borderColor,
+			SpacerColor:       bgColorOver,
+			SpacerSize:        6,
+		},
+	}
+
+	StyleDefault.Window = WindowStyles{
+		Normal: WindowStyle{
+			Border:           BorderSizes{4, 4, 4, 4},
+			Paddings:         BorderSizes{0, 0, 0, 0},
+			BorderColor:      math32.Color4{0.2, 0.2, 0.2, 1},
+			TitleBorders:     BorderSizes{0, 0, 1, 0},
+			TitleBorderColor: math32.Color4{0, 0, 0, 1},
+			TitleBgColor:     math32.Color4{0, 1, 0, 1},
+			TitleFgColor:     math32.Color4{0, 0, 0, 1},
+		},
+		Over: WindowStyle{
+			Border:           BorderSizes{4, 4, 4, 4},
+			Paddings:         BorderSizes{0, 0, 0, 0},
+			BorderColor:      math32.Color4{0.2, 0.2, 0.2, 1},
+			TitleBorders:     BorderSizes{0, 0, 1, 0},
+			TitleBorderColor: math32.Color4{0, 0, 0, 1},
+			TitleBgColor:     math32.Color4{0, 1, 0, 1},
+			TitleFgColor:     math32.Color4{0, 0, 0, 1},
+		},
+		Focus: WindowStyle{
+			Border:           BorderSizes{4, 4, 4, 4},
+			Paddings:         BorderSizes{0, 0, 0, 0},
+			BorderColor:      math32.Color4{0.2, 0.2, 0.2, 1},
+			TitleBorders:     BorderSizes{0, 0, 1, 0},
+			TitleBorderColor: math32.Color4{0, 0, 0, 1},
+			TitleBgColor:     math32.Color4{0, 1, 0, 1},
+			TitleFgColor:     math32.Color4{0, 0, 0, 1},
+		},
+		Disabled: WindowStyle{
+			Border:           BorderSizes{4, 4, 4, 4},
+			Paddings:         BorderSizes{0, 0, 0, 0},
+			BorderColor:      math32.Color4{0.2, 0.2, 0.2, 1},
+			TitleBorders:     BorderSizes{0, 0, 1, 0},
+			TitleBorderColor: math32.Color4{0, 0, 0, 1},
+			TitleBgColor:     math32.Color4{0, 1, 0, 1},
+			TitleFgColor:     math32.Color4{0, 0, 0, 1},
+		},
+	}
+
+	// Scroller styles
+	StyleDefault.Scroller = ScrollerStyles{
+		Normal: ScrollerStyle{
+			Border:      BorderSizes{1, 1, 1, 1},
+			Paddings:    BorderSizes{0, 0, 0, 0},
+			BorderColor: borderColor,
+			BgColor:     bgColor,
+			FgColor:     fgColor,
+		},
+		Over: ScrollerStyle{
+			Border:      BorderSizes{1, 1, 1, 1},
+			Paddings:    BorderSizes{0, 0, 0, 0},
+			BorderColor: borderColor,
+			BgColor:     bgColorOver,
+			FgColor:     fgColor,
+		},
+		Focus: ScrollerStyle{
+			Border:      BorderSizes{1, 1, 1, 1},
+			Paddings:    BorderSizes{0, 0, 0, 0},
+			BorderColor: borderColor,
+			BgColor:     bgColorOver,
+			FgColor:     fgColor,
+		},
+		Disabled: ScrollerStyle{
+			Border:      BorderSizes{1, 1, 1, 1},
+			Paddings:    BorderSizes{0, 0, 0, 0},
+			BorderColor: borderColor,
+			BgColor:     bgColor,
+			FgColor:     fgColor,
+		},
+	}
+
+	// List styles
+	StyleDefault.List = ListStyles{
+		Scroller: &ScrollerStyles{
+			Normal: ScrollerStyle{
+				Border:      BorderSizes{1, 1, 1, 1},
+				Paddings:    BorderSizes{0, 0, 0, 0},
+				BorderColor: borderColor,
+				BgColor:     bgColor,
+				FgColor:     fgColor,
+			},
+			Over: ScrollerStyle{
+				Border:      BorderSizes{1, 1, 1, 1},
+				Paddings:    BorderSizes{0, 0, 0, 0},
+				BorderColor: borderColor,
+				BgColor:     bgColorOver,
+				FgColor:     fgColor,
+			},
+			Focus: ScrollerStyle{
+				Border:      BorderSizes{1, 1, 1, 1},
+				Paddings:    BorderSizes{0, 0, 0, 0},
+				BorderColor: borderColor,
+				BgColor:     bgColorOver,
+				FgColor:     fgColor,
+			},
+			Disabled: ScrollerStyle{
+				Border:      BorderSizes{1, 1, 1, 1},
+				Paddings:    BorderSizes{0, 0, 0, 0},
+				BorderColor: borderColor,
+				BgColor:     bgColor,
+				FgColor:     fgColor,
+			},
+		},
+		Item: &ListItemStyles{
+			Normal: ListItemStyle{
+				Border:      BorderSizes{1, 0, 1, 0},
+				Paddings:    BorderSizes{0, 0, 0, 2},
+				BorderColor: math32.Color4{0, 0, 0, 0},
+				BgColor:     bgColor4,
+				FgColor:     fgColor,
+			},
+			Selected: ListItemStyle{
+				Border:      BorderSizes{1, 0, 1, 0},
+				Paddings:    BorderSizes{0, 0, 0, 2},
+				BorderColor: math32.Color4{0, 0, 0, 0},
+				BgColor:     bgColor4Sel,
+				FgColor:     fgColorSel,
+			},
+			Highlighted: ListItemStyle{
+				Border:      BorderSizes{1, 0, 1, 0},
+				Paddings:    BorderSizes{0, 0, 0, 2},
+				BorderColor: math32.Color4{0, 0, 0, 1},
+				BgColor:     bgColor4Over,
+				FgColor:     fgColor,
+			},
+			SelHigh: ListItemStyle{
+				Border:      BorderSizes{1, 0, 1, 0},
+				Paddings:    BorderSizes{0, 0, 0, 2},
+				BorderColor: math32.Color4{0, 0, 0, 1},
+				BgColor:     bgColor4Sel,
+				FgColor:     fgColorSel,
+			},
+		},
+	}
+
+	//	StyleDefault.ListItem = ListItemStyles{
+	//		Normal: ListItemStyle{
+	//			Border:      BorderSizes{1, 0, 1, 0},
+	//			Paddings:    BorderSizes{0, 0, 0, 2},
+	//			BorderColor: math32.Color4{0, 0, 0, 0},
+	//			BgColor:     bgColor4,
+	//			FgColor:     fgColor,
+	//		},
+	//		Over: ListItemStyle{
+	//			Border:      BorderSizes{1, 0, 1, 0},
+	//			Paddings:    BorderSizes{0, 0, 0, 2},
+	//			BorderColor: math32.Color4{0, 0, 0, 0},
+	//			BgColor:     bgColor4Over,
+	//			FgColor:     fgColor,
+	//		},
+	//		Selected: ListItemStyle{
+	//			Border:      BorderSizes{1, 0, 1, 0},
+	//			Paddings:    BorderSizes{0, 0, 0, 2},
+	//			BorderColor: math32.Color4{0, 0, 0, 0},
+	//			BgColor:     bgColor4Sel,
+	//			FgColor:     fgColorSel,
+	//		},
+	//		Highlighted: ListItemStyle{
+	//			Border:      BorderSizes{1, 0, 1, 0},
+	//			Paddings:    BorderSizes{0, 0, 0, 2},
+	//			BorderColor: math32.Color4{0, 0, 0, 1},
+	//			BgColor:     bgColor4Over,
+	//			FgColor:     fgColor,
+	//		},
+	//		SelHigh: ListItemStyle{
+	//			Border:      BorderSizes{1, 0, 1, 0},
+	//			Paddings:    BorderSizes{0, 0, 0, 2},
+	//			BorderColor: math32.Color4{0, 0, 0, 1},
+	//			BgColor:     bgColor4Sel,
+	//			FgColor:     fgColorSel,
+	//		},
+	//	}
+	//
+	StyleDefault.DropDown = DropDownStyles{
+		Normal: &DropDownStyle{
+			Border:      BorderSizes{1, 1, 1, 1},
+			Paddings:    BorderSizes{0, 0, 0, 2},
+			BorderColor: borderColor,
+			BgColor:     bgColor,
+			FgColor:     fgColor,
+		},
+		Over: &DropDownStyle{
+			Border:      BorderSizes{1, 1, 1, 1},
+			Paddings:    BorderSizes{0, 0, 0, 2},
+			BorderColor: borderColor,
+			BgColor:     bgColorOver,
+			FgColor:     fgColor,
+		},
+		Focus: &DropDownStyle{
+			Border:      BorderSizes{1, 1, 1, 1},
+			Paddings:    BorderSizes{0, 0, 0, 2},
+			BorderColor: borderColor,
+			BgColor:     bgColorOver,
+			FgColor:     fgColor,
+		},
+		Disabled: &DropDownStyle{
+			Border:      BorderSizes{1, 1, 1, 1},
+			Paddings:    BorderSizes{0, 0, 0, 2},
+			BorderColor: borderColor,
+			BgColor:     bgColor,
+			FgColor:     fgColor,
+		},
+	}
+
+	StyleDefault.Folder = FolderStyles{
+		Normal: &FolderStyle{
+			Margins:     BorderSizes{0, 0, 0, 0},
+			Border:      BorderSizes{1, 1, 1, 1},
+			Paddings:    BorderSizes{2, 0, 2, 2},
+			BorderColor: borderColor,
+			BgColor:     bgColor,
+			FgColor:     fgColor,
+			Icons:       [2]int{assets.ExpandMore, assets.ExpandLess},
+		},
+		Over: &FolderStyle{
+			Margins:     BorderSizes{0, 0, 0, 0},
+			Border:      BorderSizes{1, 1, 1, 1},
+			Paddings:    BorderSizes{2, 0, 2, 2},
+			BorderColor: borderColor,
+			BgColor:     bgColorOver,
+			FgColor:     fgColor,
+			Icons:       [2]int{assets.ExpandMore, assets.ExpandLess},
+		},
+		Focus: &FolderStyle{
+			Margins:     BorderSizes{0, 0, 0, 0},
+			Border:      BorderSizes{1, 1, 1, 1},
+			Paddings:    BorderSizes{2, 2, 2, 2},
+			BorderColor: borderColor,
+			BgColor:     bgColorOver,
+			FgColor:     fgColor,
+			Icons:       [2]int{assets.ExpandMore, assets.ExpandLess},
+		},
+		Disabled: &FolderStyle{
+			Margins:     BorderSizes{0, 0, 0, 0},
+			Border:      BorderSizes{1, 1, 1, 1},
+			Paddings:    BorderSizes{2, 2, 2, 2},
+			BorderColor: borderColor,
+			BgColor:     bgColorOver,
+			FgColor:     fgColor,
+			Icons:       [2]int{assets.ExpandMore, assets.ExpandLess},
+		},
+	}
+
+	StyleDefault.Tree = TreeStyles{
+		List: &ListStyles{
+			Scroller: &ScrollerStyles{
+				Normal: ScrollerStyle{
+					Border:      BorderSizes{1, 1, 1, 1},
+					Paddings:    BorderSizes{0, 0, 0, 0},
+					BorderColor: borderColor,
+					BgColor:     bgColor,
+					FgColor:     fgColor,
+				},
+				Over: ScrollerStyle{
+					Border:      BorderSizes{1, 1, 1, 1},
+					Paddings:    BorderSizes{0, 0, 0, 0},
+					BorderColor: borderColor,
+					BgColor:     bgColorOver,
+					FgColor:     fgColor,
+				},
+				Focus: ScrollerStyle{
+					Border:      BorderSizes{1, 1, 1, 1},
+					Paddings:    BorderSizes{0, 0, 0, 0},
+					BorderColor: borderColor,
+					BgColor:     bgColorOver,
+					FgColor:     fgColor,
+				},
+				Disabled: ScrollerStyle{
+					Border:      BorderSizes{1, 1, 1, 1},
+					Paddings:    BorderSizes{0, 0, 0, 0},
+					BorderColor: borderColor,
+					BgColor:     bgColor,
+					FgColor:     fgColor,
+				},
+			},
+			Item: &ListItemStyles{
+				Normal: ListItemStyle{
+					Border:      BorderSizes{1, 0, 1, 0},
+					Paddings:    BorderSizes{0, 0, 0, 2},
+					BorderColor: math32.Color4{0, 0, 0, 0},
+					BgColor:     bgColor4,
+					FgColor:     fgColor,
+				},
+				Selected: ListItemStyle{
+					Border:      BorderSizes{1, 0, 1, 0},
+					Paddings:    BorderSizes{0, 0, 0, 2},
+					BorderColor: math32.Color4{0, 0, 0, 0},
+					BgColor:     bgColor4Sel,
+					FgColor:     fgColorSel,
+				},
+				Highlighted: ListItemStyle{
+					Border:      BorderSizes{1, 0, 1, 0},
+					Paddings:    BorderSizes{0, 0, 0, 2},
+					BorderColor: math32.Color4{0, 0, 0, 1},
+					BgColor:     bgColor4Over,
+					FgColor:     fgColor,
+				},
+				SelHigh: ListItemStyle{
+					Border:      BorderSizes{1, 0, 1, 0},
+					Paddings:    BorderSizes{0, 0, 0, 2},
+					BorderColor: math32.Color4{0, 0, 0, 1},
+					BgColor:     bgColor4Sel,
+					FgColor:     fgColorSel,
+				},
+			},
+		},
+		Node: &TreeNodeStyles{
+			Normal: TreeNodeStyle{
+				Border:      BorderSizes{0, 0, 0, 0},
+				Paddings:    BorderSizes{0, 0, 0, 0},
+				BorderColor: borderColor,
+				BgColor:     bgColor,
+				FgColor:     fgColor,
+				Icons:       [2]int{assets.ExpandMore, assets.ExpandLess},
+			},
+		},
+		Padlevel: 16.0,
+	}
+
+	StyleDefault.ControlFolder = ControlFolderStyles{
+		Folder: &FolderStyles{
+			Normal: &FolderStyle{
+				Margins:     BorderSizes{0, 0, 0, 0},
+				Border:      BorderSizes{1, 1, 1, 1},
+				Paddings:    BorderSizes{2, 0, 2, 2},
+				BorderColor: math32.Color4{0, 0, 0, 0},
+				BgColor:     math32.Color{0, 0.5, 1},
+				FgColor:     fgColor,
+				Icons:       [2]int{assets.ExpandMore, assets.ExpandLess},
+			},
+			Over: &FolderStyle{
+				Margins:     BorderSizes{0, 0, 0, 0},
+				Border:      BorderSizes{1, 1, 1, 1},
+				Paddings:    BorderSizes{2, 0, 2, 2},
+				BorderColor: math32.Color4{0, 0, 0, 0},
+				BgColor:     math32.Color{0, 0.5, 1},
+				FgColor:     fgColor,
+				Icons:       [2]int{assets.ExpandMore, assets.ExpandLess},
+			},
+			Focus: &FolderStyle{
+				Margins:     BorderSizes{0, 0, 0, 0},
+				Border:      BorderSizes{1, 1, 1, 1},
+				Paddings:    BorderSizes{2, 2, 2, 2},
+				BorderColor: math32.Color4{0, 0, 0, 0},
+				BgColor:     math32.Color{0, 0.5, 1},
+				FgColor:     fgColor,
+				Icons:       [2]int{assets.ExpandMore, assets.ExpandLess},
+			},
+			Disabled: &FolderStyle{
+				Margins:     BorderSizes{0, 0, 0, 0},
+				Border:      BorderSizes{1, 1, 1, 1},
+				Paddings:    BorderSizes{2, 2, 2, 2},
+				BorderColor: math32.Color4{0, 0, 0, 0},
+				BgColor:     math32.Color{0, 0.5, 1},
+				FgColor:     fgColor,
+				Icons:       [2]int{assets.ExpandMore, assets.ExpandLess},
+			},
+		},
+		Tree: &TreeStyles{
+			List: &ListStyles{
+				Scroller: &ScrollerStyles{
+					Normal: ScrollerStyle{
+						Border:      BorderSizes{1, 1, 1, 1},
+						Paddings:    BorderSizes{0, 2, 0, 0},
+						BorderColor: borderColor,
+						BgColor:     bgColor,
+						FgColor:     fgColor,
+					},
+					Over: ScrollerStyle{
+						Border:      BorderSizes{1, 1, 1, 1},
+						Paddings:    BorderSizes{0, 2, 0, 0},
+						BorderColor: borderColor,
+						BgColor:     bgColorOver,
+						FgColor:     fgColor,
+					},
+					Focus: ScrollerStyle{
+						Border:      BorderSizes{1, 1, 1, 1},
+						Paddings:    BorderSizes{0, 2, 0, 0},
+						BorderColor: borderColor,
+						BgColor:     bgColorOver,
+						FgColor:     fgColor,
+					},
+					Disabled: ScrollerStyle{
+						Border:      BorderSizes{1, 1, 1, 1},
+						Paddings:    BorderSizes{0, 2, 0, 0},
+						BorderColor: borderColor,
+						BgColor:     bgColor,
+						FgColor:     fgColor,
+					},
+				},
+				Item: &ListItemStyles{
+					Normal: ListItemStyle{
+						Border:      BorderSizes{1, 0, 1, 0},
+						Paddings:    BorderSizes{0, 0, 0, 2},
+						BorderColor: math32.Color4{0, 0, 0, 0},
+						BgColor:     bgColor4,
+						FgColor:     fgColor,
+					},
+					Selected: ListItemStyle{
+						Border:      BorderSizes{1, 0, 1, 0},
+						Paddings:    BorderSizes{0, 0, 0, 2},
+						BorderColor: math32.Color4{0, 0, 0, 0},
+						BgColor:     bgColor4,
+						FgColor:     fgColor,
+					},
+					Highlighted: ListItemStyle{
+						Border:      BorderSizes{1, 0, 1, 0},
+						Paddings:    BorderSizes{0, 0, 0, 2},
+						BorderColor: math32.Color4{0, 0, 0, 1},
+						BgColor:     bgColor4Over,
+						FgColor:     fgColor,
+					},
+					SelHigh: ListItemStyle{
+						Border:      BorderSizes{1, 0, 1, 0},
+						Paddings:    BorderSizes{0, 0, 0, 2},
+						BorderColor: math32.Color4{0, 0, 0, 1},
+						BgColor:     bgColor4Sel,
+						FgColor:     fgColorSel,
+					},
+				},
+			},
+			Node: &TreeNodeStyles{
+				Normal: TreeNodeStyle{
+					Border:      BorderSizes{0, 0, 0, 0},
+					Paddings:    BorderSizes{0, 0, 0, 0},
+					BorderColor: borderColor,
+					BgColor:     bgColor,
+					FgColor:     fgColor,
+					Icons:       [2]int{assets.ExpandMore, assets.ExpandLess},
+				},
+			},
+			Padlevel: 2.0,
+		},
+	}
+
+}

+ 467 - 0
gui/tree.go

@@ -0,0 +1,467 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+	"github.com/g3n/engine/math32"
+	"github.com/g3n/engine/window"
+)
+
+type Tree struct {
+	List               // Embedded list panel
+	styles *TreeStyles // Pointer to styles
+}
+
+type TreeStyles struct {
+	List     *ListStyles     // Styles for the embedded list
+	Node     *TreeNodeStyles // Styles for the node panel
+	Padlevel float32         // Left padding indentation
+}
+
+type TreeNodeStyles struct {
+	Normal TreeNodeStyle
+}
+
+type TreeNodeStyle struct {
+	Margins     BorderSizes
+	Border      BorderSizes
+	Paddings    BorderSizes
+	BorderColor math32.Color4
+	BgColor     math32.Color
+	FgColor     math32.Color
+	Icons       [2]int
+}
+
+type TreeNode struct {
+	Panel              // Embedded panel
+	label    Label     // Node label
+	icon     Label     // Node icon
+	tree     *Tree     // Parent tree
+	parNode  *TreeNode // Parent node
+	items    []IPanel  // List of node items
+	expanded bool      // Node expanded flag
+}
+
+// NewTree creates and returns a pointer to a new tree widget
+func NewTree(width, height float32) *Tree {
+
+	t := new(Tree)
+	t.Initialize(width, height)
+	return t
+}
+
+// Initialize initializes the tree with the specified initial width and height
+// It is normally used when the folder is embedded in another object
+func (t *Tree) Initialize(width, height float32) {
+
+	t.List.initialize(true, width, height)
+	t.SetStyles(&StyleDefault.Tree)
+	t.List.Subscribe(OnKeyDown, t.onKey)
+	t.List.Subscribe(OnKeyUp, t.onKey)
+}
+
+// SetStyles set the tree styles overriding the default style
+func (t *Tree) SetStyles(s *TreeStyles) {
+
+	t.styles = s
+	t.List.SetStyles(t.styles.List)
+	t.update()
+}
+
+// InsertAt inserts a child panel at the specified position in the tree
+func (t *Tree) InsertAt(pos int, child IPanel) {
+
+	t.List.InsertAt(pos, child)
+}
+
+// Add child panel to the end tree
+func (t *Tree) Add(ichild IPanel) {
+
+	t.List.Add(ichild)
+}
+
+// InsertNodeAt inserts at the specified position a new tree node
+// with the specified text at the end of this tree
+// and returns pointer to the new node
+func (t *Tree) InsertNodeAt(pos int, text string) *TreeNode {
+
+	n := newTreeNode(text, t, nil)
+	n.update()
+	n.recalc()
+	t.List.InsertAt(pos, n)
+	return n
+}
+
+// Add adds a new tree node with the specified text
+// at the end of this tree and returns pointer to the new node
+func (t *Tree) AddNode(text string) *TreeNode {
+
+	n := newTreeNode(text, t, nil)
+	n.update()
+	n.recalc()
+	t.List.Add(n)
+	return n
+}
+
+// Remove removes the specified child from the tree or any
+// of its children nodes.
+func (t *Tree) Remove(child IPanel) {
+
+	for idx := 0; idx < t.List.Len(); idx++ {
+		curr := t.List.ItemAt(idx)
+		if curr == child {
+			node, ok := curr.(*TreeNode)
+			if ok {
+				node.remove()
+			} else {
+				t.List.Remove(child)
+			}
+			return
+		}
+		node, ok := curr.(*TreeNode)
+		if ok {
+			node.Remove(child)
+		}
+	}
+}
+
+//// Clear removes all items from the tree
+//func (t *Tree) Clear() {
+//
+//	for t.List.Len() > 0 {
+//		curr := t.List.ItemAt(0)
+//		node, ok := curr.(*TreeNode)
+//		if ok {
+//			node.remove()
+//		} else {
+//			t.List.Remove(curr)
+//		}
+//	}
+//}
+
+// Selected returns the currently selected element or nil
+func (t *Tree) Selected() IPanel {
+
+	sel := t.List.Selected()
+	if len(sel) == 0 {
+		return nil
+	}
+	return sel[0]
+}
+
+// FindChild searches for the specified child in the tree and
+// all its children. If found, returns the parent node and
+// its position relative to the parent.
+// If the parent is the tree returns nil as the parent
+// If not found returns nil and -1
+func (t *Tree) FindChild(child IPanel) (*TreeNode, int) {
+
+	for idx := 0; idx < t.List.Len(); idx++ {
+		curr := t.List.ItemAt(idx)
+		if curr == child {
+			return nil, idx
+		}
+		node, ok := curr.(*TreeNode)
+		if ok {
+			par, pos := node.FindChild(child)
+			if pos >= 0 {
+				return par, pos
+			}
+		}
+	}
+	return nil, -1
+}
+
+// onKey receives key down events for the embedded list
+func (t *Tree) onKey(evname string, ev interface{}) {
+
+	// Get selected item
+	item := t.Selected()
+	if item == nil {
+		return
+	}
+	// If item is not a tree node, dispatch event to item
+	node, ok := item.(*TreeNode)
+	if !ok {
+		item.SetRoot(t.root)
+		item.GetPanel().Dispatch(evname, ev)
+		return
+	}
+	// If not enter key pressed, ignore
+	kev := ev.(*window.KeyEvent)
+	if evname != OnKeyDown || kev.Keycode != window.KeyEnter {
+		return
+	}
+	// Toggles the expansion state of the node
+	node.expanded = !node.expanded
+	node.update()
+	node.updateItems()
+}
+
+//
+// TreeNode methods
+//
+
+// newTreeNode creates and returns a pointer to a new TreeNode with
+// the specified text, tree and parent node
+func newTreeNode(text string, tree *Tree, parNode *TreeNode) *TreeNode {
+
+	n := new(TreeNode)
+	n.Panel.Initialize(0, 0)
+
+	// Initialize node label
+	n.label.initialize(text, StyleDefault.Font)
+	n.Panel.Add(&n.label)
+
+	// Create node icon
+	n.icon.initialize("", StyleDefault.FontIcon)
+	n.icon.SetFontSize(n.label.FontSize() * 1.3)
+	n.Panel.Add(&n.icon)
+
+	// Subscribe to events
+	n.Panel.Subscribe(OnMouseDown, n.onMouse)
+	n.tree = tree
+	n.parNode = parNode
+
+	n.update()
+	n.recalc()
+	return n
+}
+
+// Len returns the number of immediate children of this node
+func (n *TreeNode) Len() int {
+
+	return len(n.items)
+}
+
+// SetExpanded sets the expanded state of this node
+func (n *TreeNode) SetExpanded(state bool) {
+
+	n.expanded = state
+	n.update()
+	n.updateItems()
+}
+
+// FindChild searches for the specified child in this node and
+// all its children. If found, returns the parent node and
+// its position relative to the parent.
+// If not found returns nil and -1
+func (n *TreeNode) FindChild(child IPanel) (*TreeNode, int) {
+
+	for pos, curr := range n.items {
+		if curr == child {
+			return n, pos
+		}
+		node, ok := curr.(*TreeNode)
+		if ok {
+			par, pos := node.FindChild(child)
+			if par != nil {
+				return par, pos
+			}
+		}
+	}
+	return nil, -1
+}
+
+// InsertAt inserts a child panel at the specified position in this node
+// If the position is invalid, the function panics
+func (n *TreeNode) InsertAt(pos int, child IPanel) {
+
+	if pos < 0 || pos > len(n.items) {
+		panic("TreeNode.InsertAt(): Invalid position")
+	}
+	// Insert item in the items array
+	n.items = append(n.items, nil)
+	copy(n.items[pos+1:], n.items[pos:])
+	n.items[pos] = child
+	if n.expanded {
+		n.updateItems()
+	}
+}
+
+// Add adds a child panel to this node
+func (n *TreeNode) Add(child IPanel) {
+
+	n.InsertAt(n.Len(), child)
+}
+
+// InsertNodeAt inserts a new node at the specified position in this node
+// If the position is invalid, the function panics
+func (n *TreeNode) InsertNodeAt(pos int, text string) *TreeNode {
+
+	if pos < 0 || pos > len(n.items) {
+		panic("TreeNode.InsertNodeAt(): Invalid position")
+	}
+	childNode := newTreeNode(text, n.tree, n)
+	// Insert item in the items array
+	n.items = append(n.items, nil)
+	copy(n.items[pos+1:], n.items[pos:])
+	n.items[pos] = childNode
+	if n.expanded {
+		n.updateItems()
+	}
+	return childNode
+}
+
+// AddNode adds a new node to this one and return its pointer
+func (n *TreeNode) AddNode(text string) *TreeNode {
+
+	return n.InsertNodeAt(n.Len(), text)
+}
+
+// Remove removes the specified child from this node or any
+// of its children nodes
+func (n *TreeNode) Remove(child IPanel) {
+
+	for pos, curr := range n.items {
+		if curr == child {
+			copy(n.items[pos:], n.items[pos+1:])
+			n.items[len(n.items)-1] = nil
+			n.items = n.items[:len(n.items)-1]
+			node, ok := curr.(*TreeNode)
+			if ok {
+				node.remove()
+			} else {
+				n.tree.List.Remove(curr)
+			}
+			n.updateItems()
+			return
+		}
+		node, ok := curr.(*TreeNode)
+		if ok {
+			node.Remove(child)
+		}
+	}
+}
+
+// onMouse receives mouse button events over the tree node panel
+func (n *TreeNode) onMouse(evname string, ev interface{}) {
+
+	switch evname {
+	case OnMouseDown:
+		n.expanded = !n.expanded
+		n.update()
+		n.recalc()
+		n.updateItems()
+	default:
+		return
+	}
+}
+
+// level returns the level of this node from the start of the tree
+func (n *TreeNode) level() int {
+
+	level := 0
+	parNode := n.parNode
+	for parNode != nil {
+		parNode = parNode.parNode
+		level++
+	}
+	return level
+}
+
+// applyStyles applies the specified style to this tree node
+func (n *TreeNode) applyStyle(s *TreeNodeStyle) {
+
+	n.SetMarginsFrom(&s.Margins)
+	n.SetBordersColor4(&s.BorderColor)
+	n.SetBordersFrom(&s.Border)
+	icode := 0
+	if n.expanded {
+		icode = 1
+	}
+	n.icon.SetText(string(s.Icons[icode]))
+}
+
+// update updates this tree node style
+func (n *TreeNode) update() {
+
+	n.applyStyle(&n.tree.styles.Node.Normal)
+}
+
+// recalc recalculates the positions of the internal node panels
+func (n *TreeNode) recalc() {
+
+	// icon position
+	n.icon.SetPosition(0, 0)
+
+	// Label position and width
+	n.label.SetPosition(n.icon.Width()+4, 0)
+	n.Panel.SetContentHeight(n.label.Height())
+	n.Panel.SetWidth(n.tree.ContentWidth())
+}
+
+// remove removes this node and all children from the tree list
+func (n *TreeNode) remove() {
+
+	n.tree.List.Remove(n)
+	n.removeItems()
+}
+
+// removeItems removes this node children from the tree list
+func (n *TreeNode) removeItems() {
+
+	for _, ipanel := range n.items {
+		// Remove item from scroller
+		n.tree.List.Remove(ipanel)
+		// If item is a node, remove all children
+		node, ok := ipanel.(*TreeNode)
+		if ok {
+			node.removeItems()
+			continue
+		}
+	}
+}
+
+// insert inserts this node and its expanded children in the tree list
+// at the specified position
+func (n *TreeNode) insert(pos int) int {
+
+	n.update()
+	n.tree.List.InsertAt(pos, n)
+	var padLeft float32 = n.tree.styles.Padlevel * float32(n.level())
+	n.tree.List.SetItemPadLeftAt(pos, padLeft)
+	pos++
+	return n.insertItems(pos)
+}
+
+// insertItems inserts this node items in the tree list
+// at the specified position
+func (n *TreeNode) insertItems(pos int) int {
+
+	if !n.expanded {
+		return pos
+	}
+	level := n.level() + 1
+	var padLeft float32 = n.tree.styles.Padlevel * float32(level)
+	for _, ipanel := range n.items {
+		// Insert node and its children
+		node, ok := ipanel.(*TreeNode)
+		if ok {
+			node.update()
+			n.tree.List.InsertAt(pos, ipanel)
+			n.tree.List.SetItemPadLeftAt(pos, padLeft)
+			pos++
+			pos = node.insertItems(pos)
+			continue
+		}
+		// Insert item
+		n.tree.List.InsertAt(pos, ipanel)
+		n.tree.List.SetItemPadLeftAt(pos, padLeft)
+		pos++
+	}
+	return pos
+}
+
+// updateItems updates this node items, removing or inserting them into the tree scroller
+func (n *TreeNode) updateItems() {
+
+	pos := n.tree.ItemPosition(n)
+	if pos < 0 {
+		return
+	}
+	n.removeItems()
+	n.insertItems(pos + 1)
+}

+ 46 - 0
gui/util.go

@@ -0,0 +1,46 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+type BorderSizes struct {
+	Top    float32
+	Right  float32
+	Bottom float32
+	Left   float32
+}
+
+func (bs *BorderSizes) Set(top, right, bottom, left float32) {
+
+	if top >= 0 {
+		bs.Top = top
+	}
+	if right >= 0 {
+		bs.Right = right
+	}
+	if bottom >= 0 {
+		bs.Bottom = bottom
+	}
+	if left >= 0 {
+		bs.Left = left
+	}
+}
+
+type Rect struct {
+	X      float32
+	Y      float32
+	Width  float32
+	Height float32
+}
+
+func (r *Rect) Contains(x, y float32) bool {
+
+	if x < r.X || x > r.X+r.Width {
+		return false
+	}
+	if y < r.Y || y > r.Y+r.Height {
+		return false
+	}
+	return true
+}

+ 186 - 0
gui/vboxlayout.go

@@ -0,0 +1,186 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+type VBoxLayout struct {
+	pan     IPanel
+	spacing float32 // vertical spacing between the children in pixels.
+	alignV  Align   // vertical alignment of the whole block of children
+}
+
+// Parameters for individual children
+type VBoxLayoutParams struct {
+	Expand float32 // item expand vertically factor (0 - no expand)
+	AlignH Align   // item horizontal alignment
+}
+
+// NewVBoxLayout creates and returns a pointer to a new horizontal box layout
+func NewVBoxLayout() *VBoxLayout {
+
+	bl := new(VBoxLayout)
+	bl.spacing = 0
+	bl.alignV = AlignTop
+	return bl
+}
+
+// SetSpacing sets the horizontal spacing between the items in pixels
+// and updates the layout if possible
+func (bl *VBoxLayout) SetSpacing(spacing float32) {
+
+	bl.spacing = spacing
+	bl.Recalc(bl.pan)
+}
+
+// SetAlignH sets the horizontal alignment of the whole group of items
+// inside the parent panel and updates the layout if possible.
+// This only has any effect if there are no expanded items.
+func (bl *VBoxLayout) SetAlignV(align Align) {
+
+	bl.alignV = align
+	bl.Recalc(bl.pan)
+}
+
+// Recalc recalculates and sets the position and sizes of all children
+func (bl *VBoxLayout) Recalc(ipan IPanel) {
+
+	// Saves the received panel
+	bl.pan = ipan
+	if bl.pan == nil {
+		return
+	}
+	parent := ipan.GetPanel()
+	if len(parent.Children()) == 0 {
+		return
+	}
+
+	// Calculates the total height, expanded height, fixed height and
+	// the sum of the expand factor for all items.
+	var theight float32 = 0
+	var eheight float32 = 0
+	var fheight float32 = 0
+	var texpand float32 = 0
+	ecount := 0
+	paramsDef := VBoxLayoutParams{Expand: 0, AlignH: AlignLeft}
+	for pos, obj := range parent.Children() {
+		pan := obj.(IPanel).GetPanel()
+		// Get item layout parameters or use default
+		params := paramsDef
+		if pan.layoutParams != nil {
+			params = *pan.layoutParams.(*VBoxLayoutParams)
+		}
+		// Calculate total height
+		theight += pan.Height()
+		if pos > 0 {
+			theight += bl.spacing
+		}
+		// Calculate height of expanded items
+		if params.Expand > 0 {
+			texpand += params.Expand
+			eheight += pan.Height()
+			if pos > 0 {
+				eheight += bl.spacing
+			}
+			ecount++
+			// Calculate width of fixed items
+		} else {
+			fheight += pan.Height()
+			if pos > 0 {
+				fheight += bl.spacing
+			}
+		}
+	}
+
+	// If there is at least on expanded item, all free space will be occupied
+	spaceMiddle := bl.spacing
+	var posY float32 = 0
+	if texpand > 0 {
+		// If there is free space, distribute space between expanded items
+		totalSpace := parent.ContentHeight() - theight
+		if totalSpace > 0 {
+			for _, obj := range parent.Children() {
+				pan := obj.(IPanel).GetPanel()
+				// Get item layout parameters or use default
+				params := paramsDef
+				if pan.layoutParams != nil {
+					params = *pan.layoutParams.(*VBoxLayoutParams)
+				}
+				if params.Expand > 0 {
+					iheight := totalSpace * params.Expand / texpand
+					pan.SetHeight(pan.Height() + iheight)
+				}
+			}
+			// No free space: distribute expanded items heights
+		} else {
+			for _, obj := range parent.Children() {
+				pan := obj.(IPanel).GetPanel()
+				// Get item layout parameters or use default
+				params := paramsDef
+				if pan.layoutParams != nil {
+					params = *pan.layoutParams.(*VBoxLayoutParams)
+				}
+				if params.Expand > 0 {
+					spacing := bl.spacing * float32(ecount-1)
+					iheight := (parent.ContentHeight() - spacing - fheight - bl.spacing) * params.Expand / texpand
+					pan.SetHeight(iheight)
+				}
+			}
+		}
+		// No expanded items: checks block vertical alignment
+	} else {
+		// Calculates initial y position which depends
+		// on the current horizontal alignment.
+		switch bl.alignV {
+		case AlignTop:
+			posY = 0
+		case AlignCenter:
+			posY = (parent.ContentHeight() - theight) / 2
+		case AlignBottom:
+			posY = parent.ContentHeight() - theight
+		case AlignHeight:
+			space := parent.ContentHeight() - theight + bl.spacing*float32(len(parent.Children())-1)
+			if space < 0 {
+				space = bl.spacing * float32(len(parent.Children())-1)
+			}
+			spaceMiddle = space / float32(len(parent.Children())+1)
+			posY = spaceMiddle
+		default:
+			log.Fatal("VBoxLayout: invalid global vertical alignment")
+		}
+	}
+
+	// Calculates the X position of each item considering
+	// it horizontal alignment
+	var posX float32
+	width := parent.ContentWidth()
+	for pos, obj := range parent.Children() {
+		pan := obj.(IPanel).GetPanel()
+		// Get item layout parameters or use default
+		params := paramsDef
+		if pan.layoutParams != nil {
+			params = *pan.layoutParams.(*VBoxLayoutParams)
+		}
+		cwidth := pan.Width()
+		switch params.AlignH {
+		case AlignLeft:
+			posX = 0
+		case AlignCenter:
+			posX = (width - cwidth) / 2
+		case AlignRight:
+			posX = width - cwidth
+		case AlignWidth:
+			posX = 0
+			pan.SetWidth(width)
+		default:
+			log.Fatal("VBoxLayout: invalid item horizontal alignment")
+		}
+		// Sets the child position
+		pan.SetPosition(posX, posY)
+		// Calculates next position
+		posY += pan.Height()
+		if pos < len(parent.Children())-1 {
+			posY += spaceMiddle
+		}
+	}
+}

+ 360 - 0
gui/window.go

@@ -0,0 +1,360 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gui
+
+import (
+	"github.com/g3n/engine/math32"
+	"github.com/g3n/engine/window"
+)
+
+/*********************************************
+
+ Window panel
+ +-----------------------------------------+
+ | Title panel                             |
+ +-----------------------------------------+
+ |  Content panel                          |
+ |  +-----------------------------------+  |
+ |  |                                   |  |
+ |  |                                   |  |
+ |  |                                   |  |
+ |  |                                   |  |
+ |  |                                   |  |
+ |  |                                   |  |
+ |  +-----------------------------------+  |
+ |                                         |
+ +-----------------------------------------+
+
+*********************************************/
+
+type Window struct {
+	Panel      // Embedded Panel
+	styles     *WindowStyles
+	title      *WindowTitle // internal optional title panel
+	client     Panel        // internal client panel
+	resizable  Resizable
+	overBorder string
+	drag       bool
+	mouseX     float32
+	mouseY     float32
+}
+
+type WindowStyle struct {
+	Border           BorderSizes
+	Paddings         BorderSizes
+	BorderColor      math32.Color4
+	TitleBorders     BorderSizes
+	TitleBorderColor math32.Color4
+	TitleBgColor     math32.Color4
+	TitleFgColor     math32.Color4
+}
+
+// All Window styles
+type WindowStyles struct {
+	Normal   WindowStyle
+	Over     WindowStyle
+	Focus    WindowStyle
+	Disabled WindowStyle
+}
+
+type Resizable int
+
+const (
+	ResizeTop = Resizable(1 << (iota + 1))
+	ResizeRight
+	ResizeBottom
+	ResizeLeft
+	ResizeAll = ResizeTop | ResizeRight | ResizeBottom | ResizeLeft
+)
+
+// NewWindow creates and returns a pointer to a new window with the
+// specified dimensions
+func NewWindow(width, height float32) *Window {
+
+	w := new(Window)
+	w.styles = &StyleDefault.Window
+
+	w.Panel.Initialize(width, height)
+	w.Panel.Subscribe(OnMouseDown, w.onMouse)
+	w.Panel.Subscribe(OnMouseUp, w.onMouse)
+	w.Panel.Subscribe(OnCursor, w.onCursor)
+	w.Panel.Subscribe(OnCursorEnter, w.onCursor)
+	w.Panel.Subscribe(OnCursorLeave, w.onCursor)
+	w.Panel.Subscribe(OnResize, func(evname string, ev interface{}) { w.recalc() })
+
+	w.client.Initialize(0, 0)
+	w.Panel.Add(&w.client)
+
+	w.recalc()
+	w.update()
+	return w
+}
+
+// SetResizable set the borders which are resizable
+func (w *Window) SetResizable(res Resizable) {
+
+	w.resizable = res
+}
+
+// SetTitle sets the title of this window
+func (w *Window) SetTitle(text string) {
+
+	if w.title == nil {
+		w.title = newWindowTitle(w, text)
+		w.Panel.Add(w.title)
+	} else {
+		w.title.label.SetText(text)
+	}
+	w.update()
+	w.recalc()
+}
+
+// Add adds a child panel to the client area of this window
+func (w *Window) Add(ichild IPanel) *Window {
+
+	w.client.Add(ichild)
+	return w
+}
+
+// SetLayout set the layout of this window content area
+func (w *Window) SetLayout(layout ILayout) *Window {
+
+	w.client.SetLayout(layout)
+	return w
+}
+
+// onMouse process subscribed mouse events over the window
+func (w *Window) onMouse(evname string, ev interface{}) {
+
+	mev := ev.(*window.MouseEvent)
+	switch evname {
+	case OnMouseDown:
+		w.SetForeground()
+		if w.overBorder != "" {
+			w.drag = true
+			w.mouseX = mev.Xpos
+			w.mouseY = mev.Ypos
+			w.root.SetMouseFocus(w)
+		}
+	case OnMouseUp:
+		w.drag = false
+		w.root.SetCursorNormal()
+		w.root.SetMouseFocus(nil)
+	default:
+		return
+	}
+	w.root.StopPropagation(StopAll)
+}
+
+// onCursor process subscribed cursor events over the window
+func (w *Window) onCursor(evname string, ev interface{}) {
+
+	cev := ev.(*window.CursorEvent)
+	switch evname {
+	case OnCursor:
+		if !w.drag {
+			cx := cev.Xpos - w.pospix.X
+			cy := cev.Ypos - w.pospix.Y
+			if cy <= w.borderSizes.Top {
+				if w.resizable&ResizeTop != 0 {
+					w.overBorder = "top"
+					w.root.SetCursorVResize()
+				}
+			} else if cy >= w.height-w.borderSizes.Bottom {
+				if w.resizable&ResizeBottom != 0 {
+					w.overBorder = "bottom"
+					w.root.SetCursorVResize()
+				}
+			} else if cx <= w.borderSizes.Left {
+				if w.resizable&ResizeLeft != 0 {
+					w.overBorder = "left"
+					w.root.SetCursorHResize()
+				}
+			} else if cx >= w.width-w.borderSizes.Right {
+				if w.resizable&ResizeRight != 0 {
+					w.overBorder = "right"
+					w.root.SetCursorHResize()
+				}
+			} else {
+				if w.overBorder != "" {
+					w.root.SetCursorNormal()
+					w.overBorder = ""
+				}
+			}
+		} else {
+			switch w.overBorder {
+			case "top":
+				delta := cev.Ypos - w.mouseY
+				w.mouseY = cev.Ypos
+				newHeight := w.Height() - delta
+				if newHeight < w.MinHeight() {
+					return
+				}
+				w.SetPositionY(w.Position().Y + delta)
+				w.SetHeight(newHeight)
+			case "right":
+				delta := cev.Xpos - w.mouseX
+				w.mouseX = cev.Xpos
+				newWidth := w.Width() + delta
+				w.SetWidth(newWidth)
+			case "bottom":
+				delta := cev.Ypos - w.mouseY
+				w.mouseY = cev.Ypos
+				newHeight := w.Height() + delta
+				w.SetHeight(newHeight)
+			case "left":
+				delta := cev.Xpos - w.mouseX
+				w.mouseX = cev.Xpos
+				newWidth := w.Width() - delta
+				if newWidth < w.MinWidth() {
+					return
+				}
+				w.SetPositionX(w.Position().X + delta)
+				w.SetWidth(newWidth)
+			}
+		}
+	case OnCursorLeave:
+		if !w.drag {
+			w.root.SetCursorNormal()
+		}
+	default:
+		return
+	}
+	w.root.StopPropagation(StopAll)
+}
+
+// update updates the button visual state
+func (w *Window) update() {
+
+	if !w.Enabled() {
+		w.applyStyle(&w.styles.Disabled)
+		return
+	}
+	w.applyStyle(&w.styles.Normal)
+}
+
+func (w *Window) applyStyle(s *WindowStyle) {
+
+	w.SetBordersColor4(&s.BorderColor)
+	w.SetBordersFrom(&s.Border)
+	w.SetPaddingsFrom(&s.Paddings)
+	if w.title != nil {
+		w.title.applyStyle(s)
+	}
+}
+
+// recalc recalculates the sizes and positions of the internal panels
+// from the outside to the inside.
+func (w *Window) recalc() {
+
+	// Window title
+	height := w.content.Height
+	width := w.content.Width
+	cx := float32(0)
+	cy := float32(0)
+	if w.title != nil {
+		w.title.SetWidth(w.content.Width)
+		w.title.recalc()
+		height -= w.title.height
+		cy = w.title.height
+	}
+
+	// Content area
+	w.client.SetPosition(cx, cy)
+	w.client.SetSize(width, height)
+}
+
+type WindowTitle struct {
+	Panel   // Embedded panel
+	win     *Window
+	label   Label
+	pressed bool
+	drag    bool
+	mouseX  float32
+	mouseY  float32
+}
+
+// newWindowTitle creates and returns a pointer to a window title panel
+func newWindowTitle(win *Window, text string) *WindowTitle {
+
+	wt := new(WindowTitle)
+	wt.win = win
+
+	wt.Panel.Initialize(0, 0)
+	wt.label.initialize(text, StyleDefault.Font)
+	wt.Panel.Add(&wt.label)
+
+	wt.Subscribe(OnMouseDown, wt.onMouse)
+	wt.Subscribe(OnMouseUp, wt.onMouse)
+	wt.Subscribe(OnCursor, wt.onCursor)
+	wt.Subscribe(OnCursorEnter, wt.onCursor)
+	wt.Subscribe(OnCursorLeave, wt.onCursor)
+
+	wt.recalc()
+	return wt
+}
+
+// onMouse process subscribed mouse button events over the window title
+func (wt *WindowTitle) onMouse(evname string, ev interface{}) {
+
+	mev := ev.(*window.MouseEvent)
+	switch evname {
+	case OnMouseDown:
+		wt.pressed = true
+		wt.mouseX = mev.Xpos
+		wt.mouseY = mev.Ypos
+		wt.win.root.SetMouseFocus(wt)
+	case OnMouseUp:
+		wt.pressed = false
+		wt.win.root.SetMouseFocus(nil)
+	default:
+		return
+	}
+	wt.win.root.StopPropagation(Stop3D)
+}
+
+// onCursor process subscribed cursor events over the window title
+func (wt *WindowTitle) onCursor(evname string, ev interface{}) {
+
+	cev := ev.(*window.CursorEvent)
+	switch evname {
+	case OnCursorEnter:
+		wt.win.root.SetCursorDrag()
+	case OnCursorLeave:
+		wt.win.root.SetCursorNormal()
+	case OnCursor:
+		if !wt.pressed {
+			wt.win.root.StopPropagation(Stop3D)
+			return
+		}
+		dy := wt.mouseY - cev.Ypos
+		dx := wt.mouseX - cev.Xpos
+		wt.mouseX = cev.Xpos
+		wt.mouseY = cev.Ypos
+		posX := wt.win.Position().X - dx
+		posY := wt.win.Position().Y - dy
+		wt.win.SetPosition(posX, posY)
+	default:
+		return
+	}
+	wt.win.root.StopPropagation(Stop3D)
+}
+
+// applyStyles sets the specified window title style
+func (wt *WindowTitle) applyStyle(s *WindowStyle) {
+
+	wt.SetBordersFrom(&s.TitleBorders)
+	wt.SetBordersColor4(&s.TitleBorderColor)
+	wt.SetColor4(&s.TitleBgColor)
+	wt.label.SetColor4(&s.TitleFgColor)
+}
+
+// recalc recalculates the height and position of the label in the title bar.
+func (wt *WindowTitle) recalc() {
+
+	xpos := (wt.width - wt.label.width) / 2
+	wt.label.SetPositionX(xpos)
+	wt.SetContentHeight(wt.label.Height())
+}

+ 94 - 0
hellog3n/main.go

@@ -0,0 +1,94 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This is a minimum G3N application showing how to create a window,
+// a scene, add some 3D objects to the scene and render it.
+// For more complete demos please see: https://github.com/g3n/g3nd
+package main
+
+import (
+	"github.com/g3n/engine/camera"
+	"github.com/g3n/engine/core"
+	"github.com/g3n/engine/geometry"
+	"github.com/g3n/engine/gls"
+	"github.com/g3n/engine/graphic"
+	"github.com/g3n/engine/light"
+	"github.com/g3n/engine/material"
+	"github.com/g3n/engine/math32"
+	"github.com/g3n/engine/renderer"
+	"github.com/g3n/engine/window"
+	"math"
+	"runtime"
+)
+
+func main() {
+
+	// Creates window and OpenGL context
+	win, err := window.New("glfw", 800, 600, "Hello G3N", false)
+	if err != nil {
+		panic(err)
+	}
+
+	// OpenGL functions must be executed in the same thread where
+	// the context was created (by window.New())
+	runtime.LockOSThread()
+
+	// Create OpenGL state
+	gs, err := gls.New()
+	if err != nil {
+		panic(err)
+	}
+
+	// Creates scene for 3D objects
+	scene := core.NewNode()
+
+	// Adds white ambient light to the scene
+	ambLight := light.NewAmbient(&math32.Color{1.0, 1.0, 1.0}, 0.5)
+	scene.Add(ambLight)
+
+	// Adds a perspective camera to the scene
+	width, height := win.GetSize()
+	aspect := float32(width) / float32(height)
+	camera := camera.NewPerspective(65, aspect, 0.01, 1000)
+	camera.SetPosition(0, 0, 5)
+
+	// Add an axis helper
+	axis := graphic.NewAxisHelper(2)
+	scene.Add(axis)
+
+	// Creates a wireframe sphere positioned at the center of the scene
+	geom := geometry.NewSphere(2, 16, 16, 0, math.Pi*2, 0, math.Pi)
+	mat := material.NewStandard(math32.NewColor(1, 1, 1))
+	mat.SetSide(material.SideDouble)
+	mat.SetWireframe(true)
+	sphere := graphic.NewMesh(geom, mat)
+	scene.Add(sphere)
+
+	// Creates a renderer and adds default shaders
+	rend := renderer.NewRenderer(gs)
+	err = rend.AddDefaultShaders()
+	if err != nil {
+		panic(err)
+	}
+
+	// Sets window background color
+	gs.ClearColor(0, 0, 0, 1.0)
+
+	// Render loop
+	for !win.ShouldClose() {
+
+		// Clear buffers
+		gs.Clear(gls.DEPTH_BUFFER_BIT | gls.STENCIL_BUFFER_BIT | gls.COLOR_BUFFER_BIT)
+
+		// Rotates the sphere a bit around the Z axis (up)
+		sphere.AddRotationY(0.005)
+
+		// Render the scene using the specified camera
+		rend.Render(scene, camera)
+
+		// Update window and checks for I/O events
+		win.SwapBuffers()
+		win.PollEvents()
+	}
+}

+ 68 - 0
light/ambient.go

@@ -0,0 +1,68 @@
+// Copyright 2016 The G3N Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package light
+
+import (
+	"github.com/g3n/engine/core"
+	"github.com/g3n/engine/gls"
+	"github.com/g3n/engine/math32"
+)
+
+type Ambient struct {
+	core.Node               // Embedded node
+	color     math32.Color  // Light color
+	intensity float32       // Light intensity
+	uColor    gls.Uniform3f // Light color uniform (color * intensity)
+}
+
+// NewAmbient returns a pointer to a new ambient color with the specified
+// color and intensity
+func NewAmbient(color *math32.Color, intensity float32) *Ambient {
+
+	la := new(Ambient)
+	la.Node.Init()
+
+	la.color = *color
+	la.intensity = intensity
+	la.uColor.Init("AmbientLightColor")
+	la.SetColor(color)
+	return la
+}
+
+// SetColor sets the color of this light
+func (la *Ambient) SetColor(color *math32.Color) {
+
+	la.color = *color
+	tmpColor := la.color
+	tmpColor.MultiplyScalar(la.intensity)
+	la.uColor.SetColor(&tmpColor)
+}
+
+// Color returns the current color of this light
+func (la *Ambient) Color() math32.Color {
+
+	return la.color
+}
+
+// SetIntensity sets the intensity of this light
+func (la *Ambient) SetIntensity(intensity float32) {
+
+	la.intensity = intensity
+	tmpColor := la.color
+	tmpColor.MultiplyScalar(la.intensity)
+	la.uColor.SetColor(&tmpColor)
+}
+
+// Intensity returns the current intensity of this light
+func (la *Ambient) Intensity() float32 {
+
+	return la.intensity
+}
+
+// RenderSetup is called by the engine before rendering the scene
+func (la *Ambient) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo, idx int) {
+
+	la.uColor.TransferIdx(gs, idx)
+}

+ 0 - 0
light/directional.go


Some files were not shown because too many files changed in this diff