danaugrs 7 лет назад
Родитель
Сommit
0e49c32c43
100 измененных файлов с 4014 добавлено и 2988 удалено
  1. 1 0
      .gitattributes
  2. 18 9
      README.md
  3. 2 2
      audio/al/al.go
  4. 9 9
      audio/audio_file.go
  5. 8 1
      audio/listener.go
  6. 2 2
      audio/ov/vorbisfile.go
  7. 2 2
      audio/player.go
  8. 2 2
      audio/vorbis/vorbis.go
  9. 1 1
      audio/wave.go
  10. 1 0
      camera/perspective.go
  11. 1 0
      core/RenderInfo.go
  12. 321 203
      core/node.go
  13. 8 13
      core/raycaster.go
  14. 69 43
      geometry/box.go
  15. 21 11
      geometry/circle.go
  16. 1 0
      geometry/cylinder.go
  17. 30 12
      geometry/geometry.go
  18. 7 6
      geometry/plane.go
  19. 8 1
      geometry/sphere.go
  20. 2 0
      geometry/torus.go
  21. 1 0
      gls/build.go
  22. 1 0
      gls/consts.go
  23. 32 2
      gls/glapi.c
  24. 38 3
      gls/glapi2go/template.go
  25. 95 17
      gls/gls.go
  26. 3 3
      gls/program.go
  27. 64 30
      gls/vbo.go
  28. 2 1
      graphic/axis_helper.go
  29. 62 12
      graphic/graphic.go
  30. 7 7
      graphic/grid_helper.go
  31. 9 14
      graphic/line_strip.go
  32. 11 14
      graphic/lines.go
  33. 25 25
      graphic/mesh.go
  34. 22 21
      graphic/normals_helper.go
  35. 14 16
      graphic/points.go
  36. 24 20
      graphic/skybox.go
  37. 5 3
      graphic/sprite.go
  38. 1 1
      gui/align.go
  39. 4 4
      gui/assets/icon/icodes.go
  40. 11 16
      gui/builder.go
  41. 1 1
      gui/builder_panel.go
  42. 15 24
      gui/button.go
  43. 6 2
      gui/chart.go
  44. 8 16
      gui/checkradio.go
  45. 12 0
      gui/control_folder.go
  46. 4 0
      gui/docklayout.go
  47. 35 46
      gui/dropdown.go
  48. 15 10
      gui/edit.go
  49. 1 0
      gui/filllayout.go
  50. 22 27
      gui/folder.go
  51. 5 5
      gui/hboxlayout.go
  52. 1 0
      gui/ilayout.go
  53. 5 14
      gui/image_button.go
  54. 6 15
      gui/imagelabel.go
  55. 643 0
      gui/itemscroller.go
  56. 114 91
      gui/label.go
  57. 26 33
      gui/list.go
  58. 12 26
      gui/menu.go
  59. 89 52
      gui/panel.go
  60. 33 25
      gui/root.go
  61. 68 31
      gui/scrollbar.go
  62. 442 450
      gui/scroller.go
  63. 5 14
      gui/slider.go
  64. 5 2
      gui/splitter.go
  65. 20 3
      gui/style.go
  66. 402 0
      gui/style_dark.go
  67. 3 3
      gui/style_default.go
  68. 318 794
      gui/style_light.go
  69. 17 37
      gui/tabbar.go
  70. 24 60
      gui/table.go
  71. 19 21
      gui/tree.go
  72. 7 2
      gui/util.go
  73. 6 6
      gui/vboxlayout.go
  74. 9 5
      gui/window.go
  75. 1 0
      light/ambient.go
  76. 1 0
      light/point.go
  77. 1 0
      light/spot.go
  78. 2 3
      loader/collada/animation.go
  79. 4 2
      loader/collada/collada.go
  80. 5 2
      loader/collada/common.go
  81. 2 3
      loader/collada/geometry.go
  82. 4 3
      loader/collada/library_animations.go
  83. 15 11
      loader/collada/library_effects.go
  84. 9 7
      loader/collada/library_geometries.go
  85. 5 4
      loader/collada/library_images.go
  86. 9 5
      loader/collada/library_lights.go
  87. 4 4
      loader/collada/library_materials.go
  88. 14 10
      loader/collada/library_visual_scenes.go
  89. 0 10
      loader/collada/material.go
  90. 1 0
      loader/collada/scene.go
  91. 2 2
      material/basic.go
  92. 18 2
      material/material.go
  93. 113 86
      math32/box2.go
  94. 141 119
      math32/box3.go
  95. 147 147
      math32/color.go
  96. 45 46
      math32/frustum.go
  97. 43 71
      math32/line3.go
  98. 4 10
      math32/math.go
  99. 81 100
      math32/plane.go
  100. 0 0
      math32/quaternion.go

+ 1 - 0
.gitattributes

@@ -0,0 +1 @@
+audio/windows/* linguist-vendored

+ 18 - 9
README.md

@@ -1,14 +1,18 @@
-# G3N - Go 3D Game Engine
 
 
-[![GoDoc](https://godoc.org/github.com/g3n/engine?status.svg)](https://godoc.org/github.com/g3n/engine)
+<p align="center"><img width="150" src="https://github.com/g3n/g3nd/blob/master/data/images/g3n_logo.png" alt="G3N Banner"/></p>
+<p align="center">
+  <a href="https://godoc.org/github.com/g3n/engine"><img src="https://godoc.org/github.com/g3n/engine?status.svg" alt="Godoc"></img></a>
+  <a href="https://goreportcard.com/report/github.com/g3n/engine"><img src="https://goreportcard.com/badge/github.com/g3n/engine"  alt="Go Report Card"/></a>
+</p>
+<p><h1 align="center">G3N - Go 3D Game Engine</h1></p>
 
 
-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.
+G3N (pronounced "gen") is an OpenGL 3D game engine written in Go.
+G3N was heavily inspired by [three.js](https://threejs.org/).
 
 
-### **To see G3N in action try the [G3N demo](https://github.com/g3n/g3nd).**
+### **To see G3N in action try the [G3N demo](https://github.com/g3n/g3nd) or the [Gokoban](https://github.com/danaugrs/gokoban) award winning game.**
 
 
 <p align="center">
 <p align="center">
-  <img style="float: right;" src="https://github.com/g3n/g3n.github.io/blob/master/g3n_banner_small.png" alt="G3N Banner"/>
+  <img style="float: right;" src="https://raw.githubusercontent.com/g3n/g3nd/master/data/images/g3nd_screenshots.png" alt="G3ND In Action"/>
 </p>
 </p>
 
 
 ## Highlighted Projects
 ## Highlighted Projects
@@ -40,9 +44,11 @@ In all cases it is necessary to have a gcc compatible C compiler installed.
 * For Windows the necessary audio libraries sources and `dlls` are supplied but they need to be installed
 * For Windows the necessary audio libraries sources and `dlls` are supplied but they need to be installed
   manually. Please see [Audio libraries for Windows](audio/windows) for details.
   manually. Please see [Audio libraries for Windows](audio/windows) for details.
   We tested the Windows build using the [mingw-w64](https://mingw-w64.org) toolchain.
   We tested the Windows build using the [mingw-w64](https://mingw-w64.org) toolchain.
-* Currently not tested on OS X.
+* (Not fully-tested on OSX) On OSX, you should install the development files of OpenAL and Vorbis. If
+  your are using [Homebrew](https://brew.sh/) as your package manager, run:
+  `brew install libvorbis openal-soft`
 
 
-G3N was only tested with Go1.7.4+
+G3N requires Go 1.8+
 
 
 ## Installation
 ## Installation
 
 
@@ -68,6 +74,10 @@ install the packages. Make sure your GOPATH is set correctly.
 * Spatial audio support allowing playing sound from wave or Ogg Vorbis files.
 * Spatial audio support allowing playing sound from wave or Ogg Vorbis files.
 * Users' applications can use their own vertex and fragment shaders.
 * Users' applications can use their own vertex and fragment shaders.
 
 
+<p align="center">
+  <img style="float: right;" src="https://github.com/g3n/g3n.github.io/blob/master/g3n_banner_small.png" alt="G3N Banner"/>
+</p>
+
 ## Basic application
 ## Basic application
 
 
 The following code shows a basic G3N application 
 The following code shows a basic G3N application 
@@ -201,4 +211,3 @@ send pull requests.
 ## Community
 ## Community
 
 
 Join our [channel](https://gophers.slack.com/messages/g3n) on Gophers Slack.
 Join our [channel](https://gophers.slack.com/messages/g3n) on Gophers Slack.
-

+ 2 - 2
audio/al/al.go

@@ -7,10 +7,10 @@
 package al
 package al
 
 
 /*
 /*
-#cgo darwin   CFLAGS:  -DGO_DARWIN  -I/usr/include/AL
+#cgo darwin   CFLAGS:  -DGO_DARWIN  -I/usr/local/opt/openal-soft/include/AL -I/usr/include/AL
 #cgo linux    CFLAGS:  -DGO_LINUX   -I/usr/include/AL
 #cgo linux    CFLAGS:  -DGO_LINUX   -I/usr/include/AL
 #cgo windows  CFLAGS:  -DGO_WINDOWS -I${SRCDIR}/../windows/openal-soft-1.18.2/include/AL
 #cgo windows  CFLAGS:  -DGO_WINDOWS -I${SRCDIR}/../windows/openal-soft-1.18.2/include/AL
-#cgo darwin   LDFLAGS: -lopenal
+#cgo darwin   LDFLAGS: -L/usr/local/opt/openal-soft/lib -lopenal
 #cgo linux    LDFLAGS: -lopenal
 #cgo linux    LDFLAGS: -lopenal
 #cgo windows  LDFLAGS: -L${SRCDIR}/../windows/bin -lOpenAL32
 #cgo windows  LDFLAGS: -L${SRCDIR}/../windows/bin -lOpenAL32
 
 

+ 9 - 9
audio/audio_file.go

@@ -13,6 +13,7 @@ import (
 	"unsafe"
 	"unsafe"
 )
 )
 
 
+// AudioInfo represents the information associated to an audio file
 type AudioInfo struct {
 type AudioInfo struct {
 	Format     int     // OpenAl Format
 	Format     int     // OpenAl Format
 	Channels   int     // Number of channels
 	Channels   int     // Number of channels
@@ -23,6 +24,7 @@ type AudioInfo struct {
 	TotalTime  float64 // Total time in seconds
 	TotalTime  float64 // Total time in seconds
 }
 }
 
 
+// AudioFile represents an audio file
 type AudioFile struct {
 type AudioFile struct {
 	wavef   *os.File  // Pointer to wave file opened filed (nil for vorbis)
 	wavef   *os.File  // Pointer to wave file opened filed (nil for vorbis)
 	vorbisf *ov.File  // Pointer to vorbis file structure (nil for wave)
 	vorbisf *ov.File  // Pointer to vorbis file structure (nil for wave)
@@ -131,26 +133,24 @@ func (af *AudioFile) Seek(pos uint) error {
 	return ov.PcmSeek(af.vorbisf, int64(pos))
 	return ov.PcmSeek(af.vorbisf, int64(pos))
 }
 }
 
 
-// AudioInfo returns the audio info structure for this audio file
+// Info returns the audio info structure for this audio file
 func (af *AudioFile) Info() AudioInfo {
 func (af *AudioFile) Info() AudioInfo {
 
 
 	return af.info
 	return af.info
 }
 }
 
 
-// CurrenTime returns the current time in seconds for the current
-// file read position
+// CurrentTime returns the current time in seconds for the current file read position
 func (af *AudioFile) CurrentTime() float64 {
 func (af *AudioFile) CurrentTime() float64 {
 
 
 	if af.vorbisf != nil {
 	if af.vorbisf != nil {
 		pos, _ := ov.TimeTell(af.vorbisf)
 		pos, _ := ov.TimeTell(af.vorbisf)
 		return pos
 		return pos
-	} else {
-		pos, err := af.wavef.Seek(0, 1)
-		if err != nil {
-			return 0
-		}
-		return float64(pos) / float64(af.info.BytesSec)
 	}
 	}
+	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
 // Looping returns the current looping state of this audio file

+ 8 - 1
audio/listener.go

@@ -11,11 +11,12 @@ import (
 	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/math32"
 )
 )
 
 
-// Listener embeds a core.Node and
+// Listener is an audio listener positioned in space
 type Listener struct {
 type Listener struct {
 	core.Node
 	core.Node
 }
 }
 
 
+// NewListener returns a pointer to a new Listener object.
 func NewListener() *Listener {
 func NewListener() *Listener {
 
 
 	l := new(Listener)
 	l := new(Listener)
@@ -23,32 +24,38 @@ func NewListener() *Listener {
 	return l
 	return l
 }
 }
 
 
+// SetVelocity sets the velocity of the listener with x, y, z components
 func (l *Listener) SetVelocity(vx, vy, vz float32) {
 func (l *Listener) SetVelocity(vx, vy, vz float32) {
 
 
 	al.Listener3f(al.Velocity, vx, vy, vz)
 	al.Listener3f(al.Velocity, vx, vy, vz)
 }
 }
 
 
+// SetVelocityVec sets the velocity of the listener with a vector
 func (l *Listener) SetVelocityVec(v *math32.Vector3) {
 func (l *Listener) SetVelocityVec(v *math32.Vector3) {
 
 
 	al.Listener3f(al.Velocity, v.X, v.Y, v.Z)
 	al.Listener3f(al.Velocity, v.X, v.Y, v.Z)
 }
 }
 
 
+// Velocity returns the velocity of the listener as x, y, z components
 func (l *Listener) Velocity() (float32, float32, float32) {
 func (l *Listener) Velocity() (float32, float32, float32) {
 
 
 	return al.GetListener3f(al.Velocity)
 	return al.GetListener3f(al.Velocity)
 }
 }
 
 
+// VelocityVec returns the velocity of the listener as a vector
 func (l *Listener) VelocityVec() math32.Vector3 {
 func (l *Listener) VelocityVec() math32.Vector3 {
 
 
 	vx, vy, vz := al.GetListener3f(al.Velocity)
 	vx, vy, vz := al.GetListener3f(al.Velocity)
 	return math32.Vector3{vx, vy, vz}
 	return math32.Vector3{vx, vy, vz}
 }
 }
 
 
+// SetGain sets the gain of the listener
 func (l *Listener) SetGain(gain float32) {
 func (l *Listener) SetGain(gain float32) {
 
 
 	al.Listenerf(al.Gain, gain)
 	al.Listenerf(al.Gain, gain)
 }
 }
 
 
+// Gain returns the gain of the listener
 func (l *Listener) Gain() float32 {
 func (l *Listener) Gain() float32 {
 
 
 	return al.GetListenerf(al.Gain)
 	return al.GetListenerf(al.Gain)

+ 2 - 2
audio/ov/vorbisfile.go

@@ -6,10 +6,10 @@
 // The libvorbisfile C API reference is at: https://xiph.org/vorbis/doc/vorbisfile/reference.html
 // The libvorbisfile C API reference is at: https://xiph.org/vorbis/doc/vorbisfile/reference.html
 package ov
 package ov
 
 
-// #cgo darwin   CFLAGS:  -DGO_DARWIN  -I/usr/include/vorbis
+// #cgo darwin   CFLAGS:  -DGO_DARWIN  -I/usr/include/vorbis -I/usr/local/include/vorbis
 // #cgo linux    CFLAGS:  -DGO_LINUX   -I/usr/include/vorbis
 // #cgo linux    CFLAGS:  -DGO_LINUX   -I/usr/include/vorbis
 // #cgo windows  CFLAGS:  -DGO_WINDOWS -I${SRCDIR}/../windows/libvorbis-1.3.5/include/vorbis -I${SRCDIR}/../windows/libogg-1.3.3/include
 // #cgo windows  CFLAGS:  -DGO_WINDOWS -I${SRCDIR}/../windows/libvorbis-1.3.5/include/vorbis -I${SRCDIR}/../windows/libogg-1.3.3/include
-// #cgo darwin   LDFLAGS: -lvorbisfile
+// #cgo darwin   LDFLAGS: -L/usr/lib -L/usr/local/lib -lvorbisfile
 // #cgo linux    LDFLAGS: -lvorbisfile
 // #cgo linux    LDFLAGS: -lvorbisfile
 // #cgo windows  LDFLAGS: -L${SRCDIR}/../windows/bin -llibvorbisfile
 // #cgo windows  LDFLAGS: -L${SRCDIR}/../windows/bin -llibvorbisfile
 // #include <stdlib.h>
 // #include <stdlib.h>

+ 2 - 2
audio/player.go

@@ -195,7 +195,7 @@ func (p *Player) SetMinGain(gain float32) {
 	al.Sourcef(p.source, al.MinGain, gain)
 	al.Sourcef(p.source, al.MinGain, gain)
 }
 }
 
 
-// MinGain returns the current maximum gain of this player
+// MaxGain returns the current maximum gain of this player
 func (p *Player) MaxGain() float32 {
 func (p *Player) MaxGain() float32 {
 
 
 	return al.GetSourcef(p.source, al.MaxGain)
 	return al.GetSourcef(p.source, al.MaxGain)
@@ -282,7 +282,7 @@ func (p *Player) VelocityVec() math32.Vector3 {
 	return math32.Vector3{vx, vy, vz}
 	return math32.Vector3{vx, vy, vz}
 }
 }
 
 
-// SetRollofFactor sets this player rolloff factor user to calculate
+// SetRolloffFactor sets this player rolloff factor user to calculate
 // the gain attenuation by distance
 // the gain attenuation by distance
 func (p *Player) SetRolloffFactor(rfactor float32) {
 func (p *Player) SetRolloffFactor(rfactor float32) {
 
 

+ 2 - 2
audio/vorbis/vorbis.go

@@ -6,10 +6,10 @@
 // See API reference at: https://xiph.org/vorbis/doc/libvorbis/reference.html
 // See API reference at: https://xiph.org/vorbis/doc/libvorbis/reference.html
 package vorbis
 package vorbis
 
 
-// #cgo darwin   CFLAGS:  -DGO_DARWIN  -I/usr/include/vorbis
+// #cgo darwin   CFLAGS:  -DGO_DARWIN  -I/usr/include/vorbis -I/usr/local/include/vorbis
 // #cgo linux    CFLAGS:  -DGO_LINUX   -I/usr/include/vorbis
 // #cgo linux    CFLAGS:  -DGO_LINUX   -I/usr/include/vorbis
 // #cgo windows  CFLAGS:  -DGO_WINDOWS -I${SRCDIR}/../windows/libvorbis-1.3.5/include/vorbis -I${SRCDIR}/../windows/libogg-1.3.3/include
 // #cgo windows  CFLAGS:  -DGO_WINDOWS -I${SRCDIR}/../windows/libvorbis-1.3.5/include/vorbis -I${SRCDIR}/../windows/libogg-1.3.3/include
-// #cgo darwin   LDFLAGS: -lvorbis
+// #cgo darwin   LDFLAGS: -L/usr/lib -L/usr/local/lib -lvorbis
 // #cgo linux    LDFLAGS: -lvorbis
 // #cgo linux    LDFLAGS: -lvorbis
 // #cgo windows  LDFLAGS: -L${SRCDIR}/../windows/bin -llibvorbis
 // #cgo windows  LDFLAGS: -L${SRCDIR}/../windows/bin -llibvorbis
 // #include "codec.h"
 // #include "codec.h"

+ 1 - 1
audio/wave.go

@@ -10,7 +10,7 @@ import (
 	"os"
 	"os"
 )
 )
 
 
-// WaveSpecs describes the characterists of the audio encoded in a wave file.
+// WaveSpecs describes the characteristics of the audio encoded in a wave file.
 type WaveSpecs struct {
 type WaveSpecs struct {
 	Format     int     // OpenAl Format
 	Format     int     // OpenAl Format
 	Type       int     // Type field from wave header
 	Type       int     // Type field from wave header

+ 1 - 0
camera/perspective.go

@@ -9,6 +9,7 @@ import (
 	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/math32"
 )
 )
 
 
+// Perspective represents a perspective camera
 type Perspective struct {
 type Perspective struct {
 	Camera                     // Embedded camera
 	Camera                     // Embedded camera
 	fov         float32        // field of view in degrees
 	fov         float32        // field of view in degrees

+ 1 - 0
core/RenderInfo.go

@@ -8,6 +8,7 @@ import (
 	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/math32"
 )
 )
 
 
+// RenderInfo is passed into Render/RenderSetup calls
 type RenderInfo struct {
 type RenderInfo struct {
 	ViewMatrix math32.Matrix4 // Current camera view matrix
 	ViewMatrix math32.Matrix4 // Current camera view matrix
 	ProjMatrix math32.Matrix4 // Current camera projection matrix
 	ProjMatrix math32.Matrix4 // Current camera projection matrix

+ 321 - 203
core/node.go

@@ -5,13 +5,12 @@
 package core
 package core
 
 
 import (
 import (
-	"strings"
-
 	"github.com/g3n/engine/gls"
 	"github.com/g3n/engine/gls"
 	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/math32"
+	"strings"
 )
 )
 
 
-// Interface for all node types
+// INode is the interface for all node types.
 type INode interface {
 type INode interface {
 	GetNode() *Node
 	GetNode() *Node
 	UpdateMatrixWorld()
 	UpdateMatrixWorld()
@@ -20,25 +19,28 @@ type INode interface {
 	Dispose()
 	Dispose()
 }
 }
 
 
+// Node represents an object in 3D space existing within a hierarchy.
 type Node struct {
 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
-	changed     bool              // Node position/orientation/scale changed
-	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
+	Dispatcher             // Embedded event dispatcher
+	parent     INode       // Parent node
+	children   []INode     // Children nodes
+	name       string      // Optional node name
+	loaderID   string      // ID used by loader
+	visible    bool        // Whether the node is visible
+	changed    bool        // Whether the position/orientation/scale changed
+	userData   interface{} // Generic user data
+
+	// Spatial properties
+	position    math32.Vector3    // Node position in 3D space (relative to parent)
+	scale       math32.Vector3    // Node scale (relative to parent)
+	direction   math32.Vector3    // Initial direction (relative to parent)
+	rotation    math32.Vector3    // Node rotation specified in Euler angles (relative to parent)
+	quaternion  math32.Quaternion // Node rotation specified as a Quaternion (relative to parent)
+	matrix      math32.Matrix4    // Local transform matrix. Contains all position/rotation/scale information (relative to parent)
+	matrixWorld math32.Matrix4    // World transform matrix. Contains all absolute position/rotation/scale information (i.e. relative to very top parent, generally the scene)
+}
+
+// NewNode returns a pointer to a new Node.
 func NewNode() *Node {
 func NewNode() *Node {
 
 
 	n := new(Node)
 	n := new(Node)
@@ -46,44 +48,72 @@ func NewNode() *Node {
 	return n
 	return n
 }
 }
 
 
-// Init initializes this Node
-// It is normally use by other types which embed a Node
+// Init initializes the node.
+// Normally called by other types which embed a Node.
 func (n *Node) Init() {
 func (n *Node) Init() {
 
 
 	n.Dispatcher.Initialize()
 	n.Dispatcher.Initialize()
+
+	n.children = make([]INode, 0)
+	n.visible = true
+	n.changed = true
+
+	// Initialize spatial properties
 	n.position.Set(0, 0, 0)
 	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.scale.Set(1, 1, 1)
 	n.direction.Set(0, 0, 1)
 	n.direction.Set(0, 0, 1)
+	n.rotation.Set(0, 0, 0)
+	n.quaternion.Set(0, 0, 0, 1)
 	n.matrix.Identity()
 	n.matrix.Identity()
 	n.matrixWorld.Identity()
 	n.matrixWorld.Identity()
-	n.children = make([]INode, 0)
-	n.visible = true
-	n.changed = true
 }
 }
 
 
-// GetNode satisfies the INode interface and returns
-// a pointer to the embedded Node
+// GetNode satisfies the INode interface
+// and returns a pointer to the embedded Node.
 func (n *Node) GetNode() *Node {
 func (n *Node) GetNode() *Node {
 
 
 	return n
 	return n
 }
 }
 
 
-// Raycast satisfies the INode interface
+// Raycast satisfies the INode interface.
 func (n *Node) Raycast(rc *Raycaster, intersects *[]Intersect) {
 func (n *Node) Raycast(rc *Raycaster, intersects *[]Intersect) {
 }
 }
 
 
-// Render satisfies the INode interface
+// Render satisfies the INode interface.
 func (n *Node) Render(gs *gls.GLS) {
 func (n *Node) Render(gs *gls.GLS) {
 }
 }
 
 
-// Dispose satisfies the INode interface
+// Dispose satisfies the INode interface.
 func (n *Node) Dispose() {
 func (n *Node) Dispose() {
 }
 }
 
 
+// SetParent sets the parent.
+func (n *Node) SetParent(iparent INode) {
+
+	n.parent = iparent
+}
+
+// Parent returns the parent.
+func (n *Node) Parent() INode {
+
+	return n.parent
+}
+
+// SetName sets the (optional) name.
+// The name can be used for debugging or other purposes.
+func (n *Node) SetName(name string) {
+
+	n.name = name
+}
+
+// Name returns the (optional) name.
+func (n *Node) Name() string {
+
+	return n.name
+}
+
 // SetLoaderID is normally used by external loaders, such as Collada,
 // 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
+// to assign an ID to the node with the ID value in the node description.
 // Can be used to find other loaded nodes.
 // Can be used to find other loaded nodes.
 func (n *Node) SetLoaderID(id string) {
 func (n *Node) SetLoaderID(id string) {
 
 
@@ -91,12 +121,49 @@ func (n *Node) SetLoaderID(id string) {
 }
 }
 
 
 // LoaderID returns an optional ID set when this node was
 // LoaderID returns an optional ID set when this node was
-// created by an external loader such as Collada
+// created by an external loader such as Collada.
 func (n *Node) LoaderID() string {
 func (n *Node) LoaderID() string {
 
 
 	return n.loaderID
 	return n.loaderID
 }
 }
 
 
+// SetVisible sets the visibility of the node.
+func (n *Node) SetVisible(state bool) {
+
+	n.visible = state
+	n.changed = true
+}
+
+// Visible returns the visibility of the node.
+func (n *Node) Visible() bool {
+
+	return n.visible
+}
+
+// SetChanged sets the changed flag of the node.
+func (n *Node) SetChanged(changed bool) {
+
+	n.changed = changed
+}
+
+// Changed returns the changed flag of the node.
+func (n *Node) Changed() bool {
+
+	return n.changed
+}
+
+// SetUserData sets the generic user data associated to the node.
+func (n *Node) SetUserData(data interface{}) {
+
+	n.userData = data
+}
+
+// UserData returns the generic user data associated to the node.
+func (n *Node) UserData() interface{} {
+
+	return n.userData
+}
+
 // FindPath finds a node with the specified path starting with this node and
 // FindPath finds a node with the specified path starting with this node and
 // searching in all its children recursively.
 // searching in all its children recursively.
 // A path is the sequence of the names from the first node to the desired node
 // A path is the sequence of the names from the first node to the desired node
@@ -134,9 +201,9 @@ func (n *Node) FindPath(path string) INode {
 	return finder(n, path)
 	return finder(n, path)
 }
 }
 
 
-// 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
+// FindLoaderID looks in the specified node and in all its children
+// for a node with the specified loaderID and if found returns it.
+// Returns nil if not found.
 func (n *Node) FindLoaderID(id string) INode {
 func (n *Node) FindLoaderID(id string) INode {
 
 
 	var finder func(parent INode, id string) INode
 	var finder func(parent INode, id string) INode
@@ -156,74 +223,181 @@ func (n *Node) FindLoaderID(id string) INode {
 	return finder(n, id)
 	return finder(n, id)
 }
 }
 
 
-// SetChanged sets this node changed flag
-func (n *Node) SetChanged(changed bool) {
+// Children returns the list of children.
+func (n *Node) Children() []INode {
 
 
-	n.changed = changed
+	return n.children
 }
 }
 
 
-// Changed returns this Node changed flag
-func (n *Node) Changed() bool {
+// Add adds the specified node to the list of children and sets its parent pointer.
+// If the specified node had a parent, the specified node is removed from the original parent's list of children.
+func (n *Node) Add(ichild INode) *Node {
 
 
-	return n.changed
+	n.setParentOf(ichild)
+	n.children = append(n.children, ichild)
+	return n
 }
 }
 
 
-// SetName set an option name for the node.
-// This name can be used for debugging or other purposes.
-func (n *Node) SetName(name string) {
+// AddAt adds the specified node to the list of children at the specified index and sets its parent pointer.
+// If the specified node had a parent, the specified node is removed from the original parent's list of children.
+func (n *Node) AddAt(idx int, ichild INode) {
 
 
-	n.name = name
+	// Validate position
+	if idx < 0 || idx > len(n.children) {
+		panic("Node.AddAt: invalid position")
+	}
+
+	n.setParentOf(ichild)
+
+	// Insert child in the specified position
+	n.children = append(n.children, nil)
+	copy(n.children[idx+1:], n.children[idx:])
+	n.children[idx] = ichild
 }
 }
 
 
-// Name returns current optional name for this node
-func (n *Node) Name() string {
+// setParentOf is used by Add and AddAt.
+// It verifies that the node is not being added to itself and sets the parent pointer of the specified node.
+// If the specified node had a parent, the specified node is removed from the original parent's list of children.
+// It does not add the specified node to the list of children.
+func (n *Node) setParentOf(ichild INode) {
 
 
-	return n.name
+	child := ichild.GetNode()
+	if n == child {
+		panic("Node.{Add,AddAt}: object can't be added as a child of itself")
+	}
+	// If the specified node already has a parent,
+	// remove it from the original parent's list of children
+	if child.parent != nil {
+		child.parent.GetNode().Remove(ichild)
+	}
+	child.parent = n
+}
+
+// ChildAt returns the child at the specified index.
+func (n *Node) ChildAt(idx int) INode {
+
+	if idx < 0 || idx >= len(n.children) {
+		return nil
+	}
+	return n.children[idx]
+}
+
+// ChildIndex returns the index of the specified child (-1 if not found).
+func (n *Node) ChildIndex(ichild INode) int {
+
+	for idx := 0; idx < len(n.children); idx++ {
+		if n.children[idx] == ichild {
+			return idx
+		}
+	}
+	return -1
+}
+
+// Remove removes the specified INode from the 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
+}
+
+// RemoveAt removes the child at the specified index.
+func (n *Node) RemoveAt(idx int) INode {
+
+	// Validate position
+	if idx < 0 || idx >= len(n.children) {
+		panic("Node.RemoveAt: invalid position")
+	}
+
+	child := n.children[idx]
+
+	// Remove child from children list
+	copy(n.children[idx:], n.children[idx+1:])
+	n.children[len(n.children)-1] = nil
+	n.children = n.children[:len(n.children)-1]
+
+	return child
+}
+
+// RemoveAll removes all children.
+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 of all children.
+// If 'recurs' is true, call DisposeChildren on each child 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]
 }
 }
 
 
-// SetPosition sets this node world position
+// SetPosition sets the position.
 func (n *Node) SetPosition(x, y, z float32) {
 func (n *Node) SetPosition(x, y, z float32) {
 
 
 	n.position.Set(x, y, z)
 	n.position.Set(x, y, z)
 	n.changed = true
 	n.changed = true
 }
 }
 
 
-// SetPositionVec sets this node position from the specified vector pointer
+// SetPositionVec sets the position based on the specified vector pointer.
 func (n *Node) SetPositionVec(vpos *math32.Vector3) {
 func (n *Node) SetPositionVec(vpos *math32.Vector3) {
 
 
 	n.position = *vpos
 	n.position = *vpos
 	n.changed = true
 	n.changed = true
 }
 }
 
 
-// SetPositionX sets the x coordinate of this node position
+// SetPositionX sets the X coordinate of the position.
 func (n *Node) SetPositionX(x float32) {
 func (n *Node) SetPositionX(x float32) {
 
 
 	n.position.X = x
 	n.position.X = x
 	n.changed = true
 	n.changed = true
 }
 }
 
 
-// SetPositionY sets the y coordinate of this node position
+// SetPositionY sets the Y coordinate of the position.
 func (n *Node) SetPositionY(y float32) {
 func (n *Node) SetPositionY(y float32) {
 
 
 	n.position.Y = y
 	n.position.Y = y
 	n.changed = true
 	n.changed = true
 }
 }
 
 
-// SetPositionZ sets the z coordinate of this node position
+// SetPositionZ sets the Z coordinate of the position.
 func (n *Node) SetPositionZ(z float32) {
 func (n *Node) SetPositionZ(z float32) {
 
 
 	n.position.Z = z
 	n.position.Z = z
 	n.changed = true
 	n.changed = true
 }
 }
 
 
-// Position returns the current node position as a vector
+// Position returns the position as a vector.
 func (n *Node) Position() math32.Vector3 {
 func (n *Node) Position() math32.Vector3 {
 
 
 	return n.position
 	return n.position
 }
 }
 
 
-// SetRotation sets the three fields of the node rotation in radians
-// The node quaternion is updated
+// SetRotation sets the rotation in radians.
+// The stored quaternion is updated accordingly.
 func (n *Node) SetRotation(x, y, z float32) {
 func (n *Node) SetRotation(x, y, z float32) {
 
 
 	n.rotation.Set(x, y, z)
 	n.rotation.Set(x, y, z)
@@ -231,8 +405,17 @@ func (n *Node) SetRotation(x, y, z float32) {
 	n.changed = true
 	n.changed = true
 }
 }
 
 
-// SetRotationX sets the x rotation angle in radians
-// The node quaternion is updated
+// SetRotationVec sets the rotation in radians based on the specified vector pointer.
+// The stored quaternion is updated accordingly.
+func (n *Node) SetRotationVec(vrot *math32.Vector3) {
+
+	n.rotation = *vrot
+	n.quaternion.SetFromEuler(&n.rotation)
+	n.changed = true
+}
+
+// SetRotationX sets the X rotation to the specified angle in radians.
+// The stored quaternion is updated accordingly.
 func (n *Node) SetRotationX(x float32) {
 func (n *Node) SetRotationX(x float32) {
 
 
 	n.rotation.X = x
 	n.rotation.X = x
@@ -240,8 +423,8 @@ func (n *Node) SetRotationX(x float32) {
 	n.changed = true
 	n.changed = true
 }
 }
 
 
-// SetRotationY sets the y rotation angle in radians
-// The node quaternion is updated
+// SetRotationY sets the Y rotation to the specified angle in radians.
+// The stored quaternion is updated accordingly.
 func (n *Node) SetRotationY(y float32) {
 func (n *Node) SetRotationY(y float32) {
 
 
 	n.rotation.Y = y
 	n.rotation.Y = y
@@ -249,8 +432,8 @@ func (n *Node) SetRotationY(y float32) {
 	n.changed = true
 	n.changed = true
 }
 }
 
 
-// SetRotationZ sets the z rotation angle in radians
-// The node quaternion is updated
+// SetRotationZ sets the Z rotation to the specified angle in radians.
+// The stored quaternion is updated accordingly.
 func (n *Node) SetRotationZ(z float32) {
 func (n *Node) SetRotationZ(z float32) {
 
 
 	n.rotation.Z = z
 	n.rotation.Z = z
@@ -258,8 +441,8 @@ func (n *Node) SetRotationZ(z float32) {
 	n.changed = true
 	n.changed = true
 }
 }
 
 
-// AddRotationX adds to the current rotation x coordinate in radians
-// The node quaternion is updated
+// AddRotationX adds to the current X rotation the specified angle in radians.
+// The stored quaternion is updated accordingly.
 func (n *Node) AddRotationX(x float32) {
 func (n *Node) AddRotationX(x float32) {
 
 
 	n.rotation.X += x
 	n.rotation.X += x
@@ -267,8 +450,8 @@ func (n *Node) AddRotationX(x float32) {
 	n.changed = true
 	n.changed = true
 }
 }
 
 
-// AddRotationY adds to the current rotation y coordinate in radians
-// The node quaternion is updated
+// AddRotationY adds to the current Y rotation the specified angle in radians.
+// The stored quaternion is updated accordingly.
 func (n *Node) AddRotationY(y float32) {
 func (n *Node) AddRotationY(y float32) {
 
 
 	n.rotation.Y += y
 	n.rotation.Y += y
@@ -276,8 +459,8 @@ func (n *Node) AddRotationY(y float32) {
 	n.changed = true
 	n.changed = true
 }
 }
 
 
-// AddRotationZ adds to the current rotation z coordinate in radians
-// The node quaternion is updated
+// AddRotationZ adds to the current Z rotation the specified angle in radians.
+// The stored quaternion is updated accordingly.
 func (n *Node) AddRotationZ(z float32) {
 func (n *Node) AddRotationZ(z float32) {
 
 
 	n.rotation.Z += z
 	n.rotation.Z += z
@@ -285,136 +468,123 @@ func (n *Node) AddRotationZ(z float32) {
 	n.changed = true
 	n.changed = true
 }
 }
 
 
-// Rotation returns the current rotation
+// Rotation returns the current rotation.
 func (n *Node) Rotation() math32.Vector3 {
 func (n *Node) Rotation() math32.Vector3 {
 
 
 	return n.rotation
 	return n.rotation
 }
 }
 
 
-// SetQuaternion sets this node  quaternion with the specified fields
+// SetQuaternion sets the quaternion based on the specified quaternion unit multiples.
 func (n *Node) SetQuaternion(x, y, z, w float32) {
 func (n *Node) SetQuaternion(x, y, z, w float32) {
 
 
 	n.quaternion.Set(x, y, z, w)
 	n.quaternion.Set(x, y, z, w)
 	n.changed = true
 	n.changed = true
 }
 }
 
 
-// SetQuaternionQuat sets this node quaternion from the specified quaternion pointer
+// SetQuaternionQuat sets the quaternion based on the specified quaternion pointer.
 func (n *Node) SetQuaternionQuat(q *math32.Quaternion) {
 func (n *Node) SetQuaternionQuat(q *math32.Quaternion) {
 
 
 	n.quaternion = *q
 	n.quaternion = *q
 	n.changed = true
 	n.changed = true
 }
 }
 
 
-// QuaternionMult multiplies the quaternion by the specified quaternion
+// QuaternionMult multiplies the current quaternion by the specified quaternion.
 func (n *Node) QuaternionMult(q *math32.Quaternion) {
 func (n *Node) QuaternionMult(q *math32.Quaternion) {
 
 
 	n.quaternion.Multiply(q)
 	n.quaternion.Multiply(q)
 	n.changed = true
 	n.changed = true
 }
 }
 
 
-// Quaternion returns the current quaternion
+// Quaternion returns the current quaternion.
 func (n *Node) Quaternion() math32.Quaternion {
 func (n *Node) Quaternion() math32.Quaternion {
 
 
 	return n.quaternion
 	return n.quaternion
 }
 }
 
 
-// SetScale sets this node scale fields
+// SetScale sets the scale.
 func (n *Node) SetScale(x, y, z float32) {
 func (n *Node) SetScale(x, y, z float32) {
 
 
 	n.scale.Set(x, y, z)
 	n.scale.Set(x, y, z)
 	n.changed = true
 	n.changed = true
 }
 }
 
 
-// SetScaleVec sets this node scale from a pointer to a Vector3
+// SetScaleVec sets the scale based on the specified vector pointer.
 func (n *Node) SetScaleVec(scale *math32.Vector3) {
 func (n *Node) SetScaleVec(scale *math32.Vector3) {
 
 
 	n.scale = *scale
 	n.scale = *scale
 	n.changed = true
 	n.changed = true
 }
 }
 
 
-// SetScaleX sets the X scale of this node
+// SetScaleX sets the X scale.
 func (n *Node) SetScaleX(sx float32) {
 func (n *Node) SetScaleX(sx float32) {
 
 
 	n.scale.X = sx
 	n.scale.X = sx
 	n.changed = true
 	n.changed = true
 }
 }
 
 
-// SetScaleY sets the Y scale of this node
+// SetScaleY sets the Y scale.
 func (n *Node) SetScaleY(sy float32) {
 func (n *Node) SetScaleY(sy float32) {
 
 
 	n.scale.Y = sy
 	n.scale.Y = sy
 	n.changed = true
 	n.changed = true
 }
 }
 
 
-// SetScaleZ sets the Z scale of this node
+// SetScaleZ sets the Z scale.
 func (n *Node) SetScaleZ(sz float32) {
 func (n *Node) SetScaleZ(sz float32) {
 
 
 	n.scale.Z = sz
 	n.scale.Z = sz
 	n.changed = true
 	n.changed = true
 }
 }
 
 
-// Scale returns the current scale
+// Scale returns the current scale.
 func (n *Node) Scale() math32.Vector3 {
 func (n *Node) Scale() math32.Vector3 {
 
 
 	return n.scale
 	return n.scale
 }
 }
 
 
-// SetDirection sets this node initial direction vector
+// SetDirection sets the direction.
 func (n *Node) SetDirection(x, y, z float32) {
 func (n *Node) SetDirection(x, y, z float32) {
 
 
 	n.direction.Set(x, y, z)
 	n.direction.Set(x, y, z)
 	n.changed = true
 	n.changed = true
 }
 }
 
 
-// SetDirectionVec sets this node initial direction vector from a pointer to Vector3
+// SetDirectionVec sets the direction based on a vector pointer.
 func (n *Node) SetDirectionVec(vdir *math32.Vector3) {
 func (n *Node) SetDirectionVec(vdir *math32.Vector3) {
 
 
 	n.direction = *vdir
 	n.direction = *vdir
 	n.changed = true
 	n.changed = true
 }
 }
 
 
-// Direction returns this node initial direction
+// Direction returns the direction.
 func (n *Node) Direction() math32.Vector3 {
 func (n *Node) Direction() math32.Vector3 {
 
 
 	return n.direction
 	return n.direction
 }
 }
 
 
-// SetMatrix sets this node local transformation matrix
+// SetMatrix sets the local transformation matrix.
 func (n *Node) SetMatrix(m *math32.Matrix4) {
 func (n *Node) SetMatrix(m *math32.Matrix4) {
 
 
 	n.matrix = *m
 	n.matrix = *m
 	n.changed = true
 	n.changed = true
 }
 }
 
 
-// Matrix returns a copy of this node local transformation matrix
+// Matrix returns a copy of the local transformation matrix.
 func (n *Node) Matrix() math32.Matrix4 {
 func (n *Node) Matrix() math32.Matrix4 {
 
 
 	return n.matrix
 	return n.matrix
 }
 }
 
 
-// SetVisible sets the node visibility state
-func (n *Node) SetVisible(state bool) {
-
-	n.visible = state
-	n.changed = true
-}
-
-// 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.
+// WorldPosition updates the world matrix and sets
+// the specified vector to the current world position of this node.
 func (n *Node) WorldPosition(result *math32.Vector3) {
 func (n *Node) WorldPosition(result *math32.Vector3) {
 
 
 	n.UpdateMatrixWorld()
 	n.UpdateMatrixWorld()
 	result.SetFromMatrixPosition(&n.matrixWorld)
 	result.SetFromMatrixPosition(&n.matrixWorld)
 }
 }
 
 
-// WorldQuaternion sets the specified result quaternion with
-// this node current world quaternion
+// WorldQuaternion updates the world matrix and sets
+// the specified quaternion to the current world quaternion of this node.
 func (n *Node) WorldQuaternion(result *math32.Quaternion) {
 func (n *Node) WorldQuaternion(result *math32.Quaternion) {
 
 
 	var position math32.Vector3
 	var position math32.Vector3
@@ -423,8 +593,8 @@ func (n *Node) WorldQuaternion(result *math32.Quaternion) {
 	n.matrixWorld.Decompose(&position, result, &scale)
 	n.matrixWorld.Decompose(&position, result, &scale)
 }
 }
 
 
-// WorldRotation sets the specified result vector with
-// current world rotation of this node in Euler angles.
+// WorldRotation updates the world matrix and sets
+// the specified vector to the current world rotation of this node in Euler angles.
 func (n *Node) WorldRotation(result *math32.Vector3) {
 func (n *Node) WorldRotation(result *math32.Vector3) {
 
 
 	var quaternion math32.Quaternion
 	var quaternion math32.Quaternion
@@ -432,8 +602,8 @@ func (n *Node) WorldRotation(result *math32.Vector3) {
 	result.SetFromQuaternion(&quaternion)
 	result.SetFromQuaternion(&quaternion)
 }
 }
 
 
-// WorldScale sets the specified result vector with
-// the current world scale of this node
+// WorldScale updates the world matrix and sets
+// the specified vector to the current world scale of this node.
 func (n *Node) WorldScale(result *math32.Vector3) {
 func (n *Node) WorldScale(result *math32.Vector3) {
 
 
 	var position math32.Vector3
 	var position math32.Vector3
@@ -442,8 +612,8 @@ func (n *Node) WorldScale(result *math32.Vector3) {
 	n.matrixWorld.Decompose(&position, &quaternion, result)
 	n.matrixWorld.Decompose(&position, &quaternion, result)
 }
 }
 
 
-// WorldDirection updates this object world matrix and sets
-// the current world direction.
+// WorldDirection updates the world matrix and sets
+// the specified vector to the current world direction of this node.
 func (n *Node) WorldDirection(result *math32.Vector3) {
 func (n *Node) WorldDirection(result *math32.Vector3) {
 
 
 	var quaternion math32.Quaternion
 	var quaternion math32.Quaternion
@@ -452,123 +622,71 @@ func (n *Node) WorldDirection(result *math32.Vector3) {
 	result.ApplyQuaternion(&quaternion)
 	result.ApplyQuaternion(&quaternion)
 }
 }
 
 
-// MatrixWorld returns a copy of this node matrix world
+// MatrixWorld returns a copy of the matrix world of this node.
 func (n *Node) MatrixWorld() math32.Matrix4 {
 func (n *Node) MatrixWorld() math32.Matrix4 {
 
 
 	return n.matrixWorld
 	return n.matrixWorld
 }
 }
 
 
-// UpdateMatrix updates this node local matrix transform from its
-// current position, quaternion and scale.
-func (n *Node) UpdateMatrix() {
+// UpdateMatrix updates (if necessary) the local transform matrix
+// of this node based on its position, quaternion, and scale.
+func (n *Node) UpdateMatrix() bool {
 
 
+	if !n.changed {
+		return false
+	}
 	n.matrix.Compose(&n.position, &n.quaternion, &n.scale)
 	n.matrix.Compose(&n.position, &n.quaternion, &n.scale)
+	n.changed = false
+	return true
 }
 }
 
 
-// UpdateMatrixWorld updates this node world transform matrix and of all its children
+// UpdateMatrixWorld updates the world transform matrix for this node and for all of its children.
 func (n *Node) UpdateMatrixWorld() {
 func (n *Node) UpdateMatrixWorld() {
 
 
-	n.UpdateMatrix()
 	if n.parent == nil {
 	if n.parent == nil {
-		n.matrixWorld = n.matrix
+		n.updateMatrixWorld(&n.matrix)
 	} else {
 	} else {
 		parent := n.parent.GetNode()
 		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
+		n.updateMatrixWorld(&parent.matrixWorld)
 	}
 	}
-	// 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 {
+// updateMatrixWorld is used internally by UpdateMatrixWorld.
+// If the local transform matrix has changed, this method updates it and also the world matrix of this node.
+// Children are updated recursively. If any node has changed, then we update the world matrix
+// of all of its descendants regardless if their local matrices have changed.
+func (n *Node) updateMatrixWorld(parentMatrixWorld *math32.Matrix4) {
 
 
-	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
-}
+	// If the local transform matrix for this node has changed then we need to update the local
+	// matrix for this node and also the world matrix for this and all subsequent nodes.
+	if n.UpdateMatrix() {
+		n.matrixWorld.MultiplyMatrices(parentMatrixWorld, &n.matrix)
 
 
-// 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)
+		// Update matrices of children recursively, always updating the world matrix
+		for _, ichild := range n.children {
+			ichild.GetNode().updateMatrixWorldNoCheck(&n.matrixWorld)
 		}
 		}
-	}
-	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)
+	} else {
+		// Update matrices of children recursively, continuing to check for changes
+		for _, ichild := range n.children {
+			ichild.GetNode().updateMatrixWorld(&n.matrixWorld)
 		}
 		}
-		ichild.Dispose()
 	}
 	}
-	n.children = n.children[0:0]
 }
 }
 
 
-// SetUserData sets this node associated generic user data
-func (n *Node) SetUserData(data interface{}) {
+// updateMatrixWorldNoCheck is used internally by updateMatrixWorld.
+// This method should be called when a node has changed since it always updates the matrix world.
+func (n *Node) updateMatrixWorldNoCheck(parentMatrixWorld *math32.Matrix4) {
 
 
-	n.userData = data
-}
+	// Update the local transform matrix (if necessary)
+	n.UpdateMatrix()
 
 
-// UserData returns this node associated generic user data
-func (n *Node) UserData() interface{} {
+	// Always update the matrix world since an ancestor of this node has changed
+	// (i.e. and ancestor had its local transform matrix modified)
+	n.matrixWorld.MultiplyMatrices(parentMatrixWorld, &n.matrix)
 
 
-	return n.userData
-}
+	// Update matrices of children recursively
+	for _, ichild := range n.children {
+		ichild.GetNode().updateMatrixWorldNoCheck(&n.matrixWorld)
+	}
+}

+ 8 - 13
core/raycaster.go

@@ -9,6 +9,7 @@ import (
 	"sort"
 	"sort"
 )
 )
 
 
+// Raycaster represents an empty object that can cast rays and check for ray intersections.
 type Raycaster struct {
 type Raycaster struct {
 	// The distance from the ray origin to the intersected points
 	// The distance from the ray origin to the intersected points
 	// must be greater than the value of this field to be considered.
 	// must be greater than the value of this field to be considered.
@@ -51,7 +52,7 @@ type Intersect struct {
 	Index uint32
 	Index uint32
 }
 }
 
 
-// New creates and returns a pointer to a new raycaster object
+// NewRaycaster creates and returns a pointer to a new raycaster object
 // with the specified origin and direction.
 // with the specified origin and direction.
 func NewRaycaster(origin, direction *math32.Vector3) *Raycaster {
 func NewRaycaster(origin, direction *math32.Vector3) *Raycaster {
 
 
@@ -72,7 +73,9 @@ func (rc *Raycaster) IntersectObject(inode INode, recursive bool) []Intersect {
 
 
 	intersects := []Intersect{}
 	intersects := []Intersect{}
 	rc.intersectObject(inode, &intersects, recursive)
 	rc.intersectObject(inode, &intersects, recursive)
-	sort.Sort(Intersects(intersects))
+	sort.Slice(intersects, func(i, j int) bool {
+		return intersects[i].Distance < intersects[j].Distance
+	})
 	return intersects
 	return intersects
 }
 }
 
 
@@ -86,7 +89,9 @@ func (rc *Raycaster) IntersectObjects(inodes []INode, recursive bool) []Intersec
 	for _, inode := range inodes {
 	for _, inode := range inodes {
 		rc.intersectObject(inode, &intersects, recursive)
 		rc.intersectObject(inode, &intersects, recursive)
 	}
 	}
-	sort.Sort(Intersects(intersects))
+	sort.Slice(intersects, func(i, j int) bool {
+		return intersects[i].Distance < intersects[j].Distance
+	})
 	return intersects
 	return intersects
 }
 }
 
 
@@ -104,13 +109,3 @@ func (rc *Raycaster) intersectObject(inode INode, intersects *[]Intersect, recur
 	}
 	}
 	return
 	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
-}

+ 69 - 43
geometry/box.go

@@ -9,30 +9,53 @@ import (
 	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/math32"
 )
 )
 
 
+// Box represents the geometry of a rectangular cuboid.
+// See https://en.wikipedia.org/wiki/Cuboid#Rectangular_cuboid for more details.
+// A Box geometry is defined by its width, height, and length and also by the number
+// of segments in each dimension.
 type Box struct {
 type Box struct {
 	Geometry
 	Geometry
-	Width          float64
-	Height         float64
-	Depth          float64
-	WidthSegments  int
-	HeightSegments int
-	DepthSegments  int
+	Width          float32
+	Height         float32
+	Length         float32
+	WidthSegments  int // > 0
+	HeightSegments int // > 0
+	LengthSegments int // > 0
 }
 }
 
 
-// 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 {
+// NewCube creates a new cube geometry of the specified size.
+func NewCube(size float32) *Box {
+	return NewSegmentedBox(size, size, size, 1, 1, 1)
+}
+
+// NewSegmentedCube creates a cube geometry of the specified size and number of segments.
+func NewSegmentedCube(size float32, segments int) *Box {
+	return NewSegmentedBox(size, size, size, segments, segments, segments)
+}
+
+// NewBox creates a box geometry of the specified width, height, and length.
+func NewBox(width, height, length float32) *Box {
+	return NewSegmentedBox(width, height, length, 1, 1, 1)
+}
+
+// NewSegmentedBox creates a box geometry of the specified size and with the specified number
+// of segments in each dimension. This is the Box constructor with most tunable parameters.
+func NewSegmentedBox(width, height, length float32, widthSegments, heightSegments, lengthSegments int) *Box {
 
 
 	box := new(Box)
 	box := new(Box)
 	box.Geometry.Init()
 	box.Geometry.Init()
 
 
+	// Validate arguments
+	if widthSegments <= 0 || heightSegments <= 0 || lengthSegments <= 0 {
+		panic("Invalid argument(s). All segment quantities should be greater than zero.")
+	}
+
 	box.Width = width
 	box.Width = width
 	box.Height = height
 	box.Height = height
-	box.Depth = depth
+	box.Length = length
 	box.WidthSegments = widthSegments
 	box.WidthSegments = widthSegments
 	box.HeightSegments = heightSegments
 	box.HeightSegments = heightSegments
-	box.DepthSegments = depthSegments
+	box.LengthSegments = lengthSegments
 
 
 	// Create buffers
 	// Create buffers
 	positions := math32.NewArrayF32(0, 16)
 	positions := math32.NewArrayF32(0, 16)
@@ -40,57 +63,53 @@ func NewBox(width, height, depth float64, widthSegments, heightSegments, depthSe
 	uvs := math32.NewArrayF32(0, 16)
 	uvs := math32.NewArrayF32(0, 16)
 	indices := math32.NewArrayU32(0, 16)
 	indices := math32.NewArrayU32(0, 16)
 
 
-	width_half := width / 2
-	height_half := height / 2
-	depth_half := depth / 2
+	// Internal function to build each of the six box planes
+	buildPlane := func(u, v string, udir, vdir int, width, height, length float32, materialIndex uint) {
 
 
-	// 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
 		offset := positions.Len() / 3
+		gridX := box.WidthSegments
+		gridY := box.HeightSegments
 		var w string
 		var w string
 
 
 		if (u == "x" && v == "y") || (u == "y" && v == "x") {
 		if (u == "x" && v == "y") || (u == "y" && v == "x") {
 			w = "z"
 			w = "z"
 		} else if (u == "x" && v == "z") || (u == "z" && v == "x") {
 		} else if (u == "x" && v == "z") || (u == "z" && v == "x") {
 			w = "y"
 			w = "y"
-			gridY = depthSegments
+			gridY = box.LengthSegments
 		} else if (u == "z" && v == "y") || (u == "y" && v == "z") {
 		} else if (u == "z" && v == "y") || (u == "y" && v == "z") {
 			w = "x"
 			w = "x"
-			gridX = depthSegments
+			gridX = box.LengthSegments
 		}
 		}
 
 
-		gridX1 := gridX + 1
-		gridY1 := gridY + 1
-		segment_width := width / float64(gridX)
-		segment_height := height / float64(gridY)
 		var normal math32.Vector3
 		var normal math32.Vector3
-		if depth > 0 {
+		if length > 0 {
 			normal.SetByName(w, 1)
 			normal.SetByName(w, 1)
 		} else {
 		} else {
 			normal.SetByName(w, -1)
 			normal.SetByName(w, -1)
 		}
 		}
 
 
-		// Generates the plane vertices, normals and uv coordinates.
+		wHalf := width / 2
+		hHalf := height / 2
+		gridX1 := gridX + 1
+		gridY1 := gridY + 1
+		segmentWidth := width / float32(gridX)
+		segmentHeight := height / float32(gridY)
+
+		// Generate the plane vertices, normals, and uv coordinates
 		for iy := 0; iy < gridY1; iy++ {
 		for iy := 0; iy < gridY1; iy++ {
 			for ix := 0; ix < gridX1; ix++ {
 			for ix := 0; ix < gridX1; ix++ {
 				var vector math32.Vector3
 				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))
+				vector.SetByName(u, (float32(ix)*segmentWidth-wHalf)*float32(udir))
+				vector.SetByName(v, (float32(iy)*segmentHeight-hHalf)*float32(vdir))
+				vector.SetByName(w, length)
 				positions.AppendVector3(&vector)
 				positions.AppendVector3(&vector)
 				normals.AppendVector3(&normal)
 				normals.AppendVector3(&normal)
-				uvs.Append(float32(float64(ix)/float64(gridX)), float32(float64(1)-(float64(iy)/float64(gridY))))
+				uvs.Append(float32(ix)/float32(gridX), float32(1)-(float32(iy)/float32(gridY)))
 			}
 			}
 		}
 		}
 
 
+		// Generate the indices for the vertices, normals and uv coordinates
 		gstart := indices.Size()
 		gstart := indices.Size()
-		matIndex := materialIndex
-		// Generates the indices for the vertices, normals and uvs
 		for iy := 0; iy < gridY; iy++ {
 		for iy := 0; iy < gridY; iy++ {
 			for ix := 0; ix < gridX; ix++ {
 			for ix := 0; ix < gridX; ix++ {
 				a := ix + gridX1*iy
 				a := ix + gridX1*iy
@@ -101,20 +120,27 @@ func NewBox(width, height, depth float64, widthSegments, heightSegments, depthSe
 			}
 			}
 		}
 		}
 		gcount := indices.Size() - gstart
 		gcount := indices.Size() - gstart
-		box.AddGroup(gstart, gcount, int(matIndex))
+		box.AddGroup(gstart, gcount, int(materialIndex))
 	}
 	}
 
 
-	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
+	wHalf := box.Width / 2
+	hHalf := box.Height / 2
+	lHalf := box.Length / 2
+
+	buildPlane("z", "y", -1, -1, box.Length, box.Height, wHalf, 0) // px
+	buildPlane("z", "y", 1, -1, box.Length, box.Height, -wHalf, 1) // nx
+	buildPlane("x", "z", 1, 1, box.Width, box.Length, hHalf, 2)    // py
+	buildPlane("x", "z", 1, -1, box.Width, box.Length, -hHalf, 3)  // ny
+	buildPlane("x", "y", 1, -1, box.Width, box.Height, lHalf, 4)   // pz
+	buildPlane("x", "y", -1, -1, box.Width, box.Height, -lHalf, 5) // nz
 
 
 	box.SetIndices(indices)
 	box.SetIndices(indices)
 	box.AddVBO(gls.NewVBO().AddAttrib("VertexPosition", 3).SetBuffer(positions))
 	box.AddVBO(gls.NewVBO().AddAttrib("VertexPosition", 3).SetBuffer(positions))
 	box.AddVBO(gls.NewVBO().AddAttrib("VertexNormal", 3).SetBuffer(normals))
 	box.AddVBO(gls.NewVBO().AddAttrib("VertexNormal", 3).SetBuffer(normals))
 	box.AddVBO(gls.NewVBO().AddAttrib("VertexTexcoord", 2).SetBuffer(uvs))
 	box.AddVBO(gls.NewVBO().AddAttrib("VertexTexcoord", 2).SetBuffer(uvs))
 
 
+	box.boundingBox = math32.Box3{math32.Vector3{-wHalf, -hHalf, -lHalf}, math32.Vector3{wHalf, hHalf, lHalf}}
+	box.boundingBoxValid = true
+
 	return box
 	return box
 }
 }

+ 21 - 11
geometry/circle.go

@@ -10,32 +10,41 @@ import (
 	"math"
 	"math"
 )
 )
 
 
+// Circle represents the geometry of a filled circle (i.e. a disk)
+// The center of the circle is at the origin, and theta runs counter-clockwise
+// on the XY plane, starting at (x,y,z)=(1,0,0).
 type Circle struct {
 type Circle struct {
 	Geometry
 	Geometry
 	Radius      float64
 	Radius      float64
-	Segments    int
+	Segments    int // >= 3
 	ThetaStart  float64
 	ThetaStart  float64
 	ThetaLength 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 {
+// NewCircle creates a new circle geometry with the specified radius
+// and number of radial segments/triangles (minimum 3).
+func NewCircle(radius float64, segments int) *Circle {
+	return NewCircleSector(radius, segments, 0, 2*math.Pi)
+}
+
+// NewCircleSector creates a new circle or circular sector geometry with the specified radius,
+// number of radial segments/triangles (minimum 3), sector start angle in radians (thetaStart),
+// and sector size angle in radians (thetaLength). This is the Circle constructor with most tunable parameters.
+func NewCircleSector(radius float64, segments int, thetaStart, thetaLength float64) *Circle {
 
 
 	circ := new(Circle)
 	circ := new(Circle)
 	circ.Geometry.Init()
 	circ.Geometry.Init()
 
 
+	// Validate arguments
+	if segments < 3 {
+		panic("Invalid argument: segments. The number of segments needs to be greater or equal to 3.")
+	}
+
 	circ.Radius = radius
 	circ.Radius = radius
 	circ.Segments = segments
 	circ.Segments = segments
 	circ.ThetaStart = thetaStart
 	circ.ThetaStart = thetaStart
 	circ.ThetaLength = thetaLength
 	circ.ThetaLength = thetaLength
 
 
-	if segments < 3 {
-		segments = 3
-	}
-
 	// Create buffers
 	// Create buffers
 	positions := math32.NewArrayF32(0, 16)
 	positions := math32.NewArrayF32(0, 16)
 	normals := math32.NewArrayF32(0, 16)
 	normals := math32.NewArrayF32(0, 16)
@@ -51,10 +60,11 @@ func NewCircle(radius float64, segments int, thetaStart, thetaLength float64) *C
 	normal.Z = 1
 	normal.Z = 1
 	normals.AppendVector3(&normal)
 	normals.AppendVector3(&normal)
 
 
-	// Append circle center uv coord
+	// Append circle center uv coordinate
 	centerUV := math32.NewVector2(0.5, 0.5)
 	centerUV := math32.NewVector2(0.5, 0.5)
 	uvs.AppendVector2(centerUV)
 	uvs.AppendVector2(centerUV)
 
 
+	// Generate the segments
 	for i := 0; i <= segments; i++ {
 	for i := 0; i <= segments; i++ {
 		segment := thetaStart + float64(i)/float64(segments)*thetaLength
 		segment := thetaStart + float64(i)/float64(segments)*thetaLength
 
 

+ 1 - 0
geometry/cylinder.go

@@ -10,6 +10,7 @@ import (
 	"math"
 	"math"
 )
 )
 
 
+// Cylinder represents a cylinder geometry
 type Cylinder struct {
 type Cylinder struct {
 	Geometry
 	Geometry
 	RadiusTop      float64
 	RadiusTop      float64

+ 30 - 12
geometry/geometry.go

@@ -126,6 +126,7 @@ func (g *Geometry) GroupAt(idx int) *Group {
 func (g *Geometry) SetIndices(indices math32.ArrayU32) {
 func (g *Geometry) SetIndices(indices math32.ArrayU32) {
 
 
 	g.indices = indices
 	g.indices = indices
+	g.updateIndices = true
 	g.boundingBoxValid = false
 	g.boundingBoxValid = false
 	g.boundingSphereValid = false
 	g.boundingSphereValid = false
 }
 }
@@ -139,10 +140,19 @@ func (g *Geometry) Indices() math32.ArrayU32 {
 // AddVBO adds a Vertex Buffer Object for this geometry
 // AddVBO adds a Vertex Buffer Object for this geometry
 func (g *Geometry) AddVBO(vbo *gls.VBO) {
 func (g *Geometry) AddVBO(vbo *gls.VBO) {
 
 
+	// Check that the provided VBO doesn't have conflicting attributes with existing VBOs
+	for _, existingVbo := range g.vbos {
+		for _, attrib := range vbo.Attributes() {
+			if existingVbo.Attrib(attrib.Name) != nil {
+				panic("Geometry.AddVBO: geometry already has a VBO with attribute " + attrib.Name)
+			}
+		}
+	}
+
 	g.vbos = append(g.vbos, vbo)
 	g.vbos = append(g.vbos, vbo)
 }
 }
 
 
-// VBO returns a pointer to this geometry VBO for the specified attribute.
+// VBO returns a pointer to this geometry's VBO which contain the specified attribute.
 // Returns nil if the VBO is not found.
 // Returns nil if the VBO is not found.
 func (g *Geometry) VBO(attrib string) *gls.VBO {
 func (g *Geometry) VBO(attrib string) *gls.VBO {
 
 
@@ -166,7 +176,7 @@ func (g *Geometry) Items() int {
 	if vbo.AttribCount() == 0 {
 	if vbo.AttribCount() == 0 {
 		return 0
 		return 0
 	}
 	}
-	return vbo.Buffer().Bytes() / vbo.Stride()
+	return vbo.Buffer().Bytes() / vbo.StrideSize()
 }
 }
 
 
 // BoundingBox computes the bounding box of the geometry if necessary
 // BoundingBox computes the bounding box of the geometry if necessary
@@ -179,17 +189,19 @@ func (g *Geometry) BoundingBox() math32.Box3 {
 	}
 	}
 
 
 	// Get buffer with position vertices
 	// Get buffer with position vertices
-	vbPos := g.VBO("VertexPosition")
-	if vbPos == nil {
+	vboPos := g.VBO("VertexPosition")
+	if vboPos == nil {
 		return g.boundingBox
 		return g.boundingBox
 	}
 	}
-	positions := vbPos.Buffer()
+	stride := vboPos.Stride()
+	offset := vboPos.AttribOffset("VertexPosition")
+	positions := vboPos.Buffer()
 
 
 	// Calculates bounding box
 	// Calculates bounding box
 	var vertex math32.Vector3
 	var vertex math32.Vector3
 	g.boundingBox.Min.Set(0, 0, 0)
 	g.boundingBox.Min.Set(0, 0, 0)
 	g.boundingBox.Max.Set(0, 0, 0)
 	g.boundingBox.Max.Set(0, 0, 0)
-	for i := 0; i < positions.Size(); i += 3 {
+	for i := offset; i < positions.Size(); i += stride {
 		positions.GetVector3(i, &vertex)
 		positions.GetVector3(i, &vertex)
 		g.boundingBox.ExpandByPoint(&vertex)
 		g.boundingBox.ExpandByPoint(&vertex)
 	}
 	}
@@ -207,11 +219,13 @@ func (g *Geometry) BoundingSphere() math32.Sphere {
 	}
 	}
 
 
 	// Get buffer with position vertices
 	// Get buffer with position vertices
-	vbPos := g.VBO("VertexPosition")
-	if vbPos == nil {
+	vboPos := g.VBO("VertexPosition")
+	if vboPos == nil {
 		return g.boundingSphere
 		return g.boundingSphere
 	}
 	}
-	positions := vbPos.Buffer()
+	stride := vboPos.Stride()
+	offset := vboPos.AttribOffset("VertexPosition")
+	positions := vboPos.Buffer()
 
 
 	// Get/calculates the bounding box
 	// Get/calculates the bounding box
 	box := g.BoundingBox()
 	box := g.BoundingBox()
@@ -222,7 +236,7 @@ func (g *Geometry) BoundingSphere() math32.Sphere {
 
 
 	// Find the radius of the bounding sphere
 	// Find the radius of the bounding sphere
 	maxRadiusSq := float32(0.0)
 	maxRadiusSq := float32(0.0)
-	for i := 0; i < positions.Size(); i += 3 {
+	for i := offset; i < positions.Size(); i += stride {
 		var vertex math32.Vector3
 		var vertex math32.Vector3
 		positions.GetVector3(i, &vertex)
 		positions.GetVector3(i, &vertex)
 		maxRadiusSq = math32.Max(maxRadiusSq, center.DistanceToSquared(&vertex))
 		maxRadiusSq = math32.Max(maxRadiusSq, center.DistanceToSquared(&vertex))
@@ -247,9 +261,11 @@ func (g *Geometry) ApplyMatrix(m *math32.Matrix4) {
 	if vboPos == nil {
 	if vboPos == nil {
 		return
 		return
 	}
 	}
+	stride := vboPos.Stride()
+	offset := vboPos.AttribOffset("VertexPosition")
 	positions := vboPos.Buffer()
 	positions := vboPos.Buffer()
 	// Apply matrix to all position vertices
 	// Apply matrix to all position vertices
-	for i := 0; i < positions.Size(); i += 3 {
+	for i := offset; i < positions.Size(); i += stride {
 		var vertex math32.Vector3
 		var vertex math32.Vector3
 		positions.GetVector3(i, &vertex)
 		positions.GetVector3(i, &vertex)
 		vertex.ApplyMatrix4(m)
 		vertex.ApplyMatrix4(m)
@@ -262,11 +278,13 @@ func (g *Geometry) ApplyMatrix(m *math32.Matrix4) {
 	if vboNormals == nil {
 	if vboNormals == nil {
 		return
 		return
 	}
 	}
+	stride = vboNormals.Stride()
+	offset = vboNormals.AttribOffset("VertexNormal")
 	normals := vboNormals.Buffer()
 	normals := vboNormals.Buffer()
 	// Apply normal matrix to all normal vectors
 	// Apply normal matrix to all normal vectors
 	var normalMatrix math32.Matrix3
 	var normalMatrix math32.Matrix3
 	normalMatrix.GetNormalMatrix(m)
 	normalMatrix.GetNormalMatrix(m)
-	for i := 0; i < normals.Size(); i += 3 {
+	for i := offset; i < normals.Size(); i += stride {
 		var vertex math32.Vector3
 		var vertex math32.Vector3
 		normals.GetVector3(i, &vertex)
 		normals.GetVector3(i, &vertex)
 		vertex.ApplyMatrix3(&normalMatrix).Normalize()
 		vertex.ApplyMatrix3(&normalMatrix).Normalize()

+ 7 - 6
geometry/plane.go

@@ -9,6 +9,7 @@ import (
 	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/math32"
 )
 )
 
 
+// Plane represents a plane geometry
 type Plane struct {
 type Plane struct {
 	Geometry
 	Geometry
 	Width          float32
 	Width          float32
@@ -31,14 +32,14 @@ func NewPlane(width, height float32, widthSegments, heightSegments int) *Plane {
 	plane.WidthSegments = widthSegments
 	plane.WidthSegments = widthSegments
 	plane.HeightSegments = heightSegments
 	plane.HeightSegments = heightSegments
 
 
-	width_half := width / 2
-	height_half := height / 2
+	widthHalf := width / 2
+	heightHalf := height / 2
 	gridX := widthSegments
 	gridX := widthSegments
 	gridY := heightSegments
 	gridY := heightSegments
 	gridX1 := gridX + 1
 	gridX1 := gridX + 1
 	gridY1 := gridY + 1
 	gridY1 := gridY + 1
-	segment_width := width / float32(gridX)
-	segment_height := height / float32(gridY)
+	segmentWidth := width / float32(gridX)
+	segmentHeight := height / float32(gridY)
 
 
 	// Create buffers
 	// Create buffers
 	positions := math32.NewArrayF32(0, 16)
 	positions := math32.NewArrayF32(0, 16)
@@ -48,9 +49,9 @@ func NewPlane(width, height float32, widthSegments, heightSegments int) *Plane {
 
 
 	// Generate plane vertices, vertices normals and vertices texture mappings.
 	// Generate plane vertices, vertices normals and vertices texture mappings.
 	for iy := 0; iy < gridY1; iy++ {
 	for iy := 0; iy < gridY1; iy++ {
-		y := float32(iy)*segment_height - height_half
+		y := float32(iy)*segmentHeight - heightHalf
 		for ix := 0; ix < gridX1; ix++ {
 		for ix := 0; ix < gridX1; ix++ {
-			x := float32(ix)*segment_width - width_half
+			x := float32(ix)*segmentWidth - widthHalf
 			positions.Append(float32(x), float32(-y), 0)
 			positions.Append(float32(x), float32(-y), 0)
 			normals.Append(0, 0, 1)
 			normals.Append(0, 0, 1)
 			uvs.Append(float32(float64(ix)/float64(gridX)), float32(float64(1)-(float64(iy)/float64(gridY))))
 			uvs.Append(float32(float64(ix)/float64(gridX)), float32(float64(1)-(float64(iy)/float64(gridY))))

+ 8 - 1
geometry/sphere.go

@@ -10,6 +10,7 @@ import (
 	"math"
 	"math"
 )
 )
 
 
+// Sphere represents a sphere geometry
 type Sphere struct {
 type Sphere struct {
 	Geometry
 	Geometry
 	Radius         float64
 	Radius         float64
@@ -21,6 +22,7 @@ type Sphere struct {
 	ThetaLength    float64
 	ThetaLength    float64
 }
 }
 
 
+// NewSphere returns a pointer to a new Sphere geometry object
 func NewSphere(radius float64, widthSegments, heightSegments int, phiStart, phiLength, thetaStart, thetaLength float64) *Sphere {
 func NewSphere(radius float64, widthSegments, heightSegments int, phiStart, phiLength, thetaStart, thetaLength float64) *Sphere {
 
 
 	s := new(Sphere)
 	s := new(Sphere)
@@ -85,6 +87,11 @@ func NewSphere(radius float64, widthSegments, heightSegments int, phiStart, phiL
 	s.AddVBO(gls.NewVBO().AddAttrib("VertexNormal", 3).SetBuffer(normals))
 	s.AddVBO(gls.NewVBO().AddAttrib("VertexNormal", 3).SetBuffer(normals))
 	s.AddVBO(gls.NewVBO().AddAttrib("VertexTexcoord", 2).SetBuffer(uvs))
 	s.AddVBO(gls.NewVBO().AddAttrib("VertexTexcoord", 2).SetBuffer(uvs))
 
 
-	//s.BoundingSphere = math32.NewSphere(math32.NewVector3(0, 0, 0), float32(radius))
+	r := float32(s.Radius)
+	s.boundingSphere = math32.Sphere{math32.Vector3{0, 0, 0}, r}
+	s.boundingSphereValid = true
+	s.boundingBox = math32.Box3{math32.Vector3{-r, -r, -r}, math32.Vector3{r, r, r}}
+	s.boundingBoxValid = true
+
 	return s
 	return s
 }
 }

+ 2 - 0
geometry/torus.go

@@ -10,6 +10,7 @@ import (
 	"math"
 	"math"
 )
 )
 
 
+// Torus represents a torus geometry
 type Torus struct {
 type Torus struct {
 	Geometry                // embedded geometry
 	Geometry                // embedded geometry
 	Radius          float64 // Torus radius
 	Radius          float64 // Torus radius
@@ -19,6 +20,7 @@ type Torus struct {
 	Arc             float64 // Central angle
 	Arc             float64 // Central angle
 }
 }
 
 
+// NewTorus returns a pointer to a new torus geometry
 func NewTorus(radius, tube float64, radialSegments, tubularSegments int, arc float64) *Torus {
 func NewTorus(radius, tube float64, radialSegments, tubularSegments int, arc float64) *Torus {
 
 
 	t := new(Torus)
 	t := new(Torus)

+ 1 - 0
gls/build.go

@@ -1,6 +1,7 @@
 // Copyright 2016 The G3N Authors. All rights reserved.
 // Copyright 2016 The G3N Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 // license that can be found in the LICENSE file.
+
 package gls
 package gls
 
 
 // Generation of API files: glapi.c, glapi.h, consts.go
 // Generation of API files: glapi.c, glapi.h, consts.go

+ 1 - 0
gls/consts.go

@@ -1,6 +1,7 @@
 // This file was generated automatically by "glapi2go" and contains all
 // This file was generated automatically by "glapi2go" and contains all
 // OpenGL constants specified by "#define GL_*" directives contained in
 // OpenGL constants specified by "#define GL_*" directives contained in
 // "glcorearb.h" for an specific OpenGL version converted to Go constants.
 // "glcorearb.h" for an specific OpenGL version converted to Go constants.
+
 package gls
 package gls
 
 
 const (
 const (

+ 32 - 2
gls/glapi.c

@@ -153,8 +153,38 @@ void glapiCheckError(int check) {
 // Internal function to abort process when error
 // Internal function to abort process when error
 static void panic(GLenum err, const char* fname) {
 static void panic(GLenum err, const char* fname) {
 
 
-	printf("\nGLAPI Error: %d calling: %s\n", err, fname);
-	exit(1);
+		const char *msg;
+		switch(err) {
+    	case GL_NO_ERROR:
+    		msg = "No error";
+    		break;
+    	case GL_INVALID_ENUM:
+    		msg = "An unacceptable value is specified for an enumerated argument";
+    		break;
+    	case GL_INVALID_VALUE:
+    		msg = "A numeric argument is out of range";
+    		break;
+    	case GL_INVALID_OPERATION:
+    		msg = "The specified operation is not allowed in the current state";
+    		break;
+    	case GL_INVALID_FRAMEBUFFER_OPERATION:
+    		msg = "The framebuffer object is not complete";
+    		break;
+    	case GL_OUT_OF_MEMORY:
+    		msg = "There is not enough memory left to execute the command";
+    		break;
+    	case GL_STACK_UNDERFLOW:
+    		msg = "An attempt has been made to perform an operation that would cause an internal stack to underflow";
+    		break;
+    	case GL_STACK_OVERFLOW:
+    		msg = "An attempt has been made to perform an operation that would cause an internal stack to overflow";
+    		break;
+    	default:
+    		msg = "Unexpected error";
+    		break;
+    }
+    printf("\nGLAPI Error: %s (%d) calling: %s\n", msg, err, fname);
+    exit(1);
 }
 }
 
 
 
 

+ 38 - 3
gls/glapi2go/template.go

@@ -7,16 +7,19 @@ import (
 	"text/template"
 	"text/template"
 )
 )
 
 
+// GLHeader is the definition of an OpenGL header file, with functions and constant definitions.
 type GLHeader struct {
 type GLHeader struct {
 	Defines []GLDefine
 	Defines []GLDefine
 	Funcs   []GLFunc
 	Funcs   []GLFunc
 }
 }
 
 
+// GLDefine is the definition of an OpenGL constant.
 type GLDefine struct {
 type GLDefine struct {
 	Name  string
 	Name  string
 	Value string
 	Value string
 }
 }
 
 
+// GLFunc is the definition of an OpenGL function.
 type GLFunc struct {
 type GLFunc struct {
 	Ptype    string    // type of function pointer (ex: PFNCULLFACEPROC)
 	Ptype    string    // type of function pointer (ex: PFNCULLFACEPROC)
 	Spacer   string    // spacer string for formatting
 	Spacer   string    // spacer string for formatting
@@ -30,6 +33,7 @@ type GLFunc struct {
 	Params   []GLParam // array of function parameters
 	Params   []GLParam // array of function parameters
 }
 }
 
 
+// GLParam is the definition of an argument to an OpenGL function (GLFunc).
 type GLParam struct {
 type GLParam struct {
 	Qualif string // optional parameter qualifier (ex: const)
 	Qualif string // optional parameter qualifier (ex: const)
 	CType  string // parameter C type
 	CType  string // parameter C type
@@ -37,7 +41,7 @@ type GLParam struct {
 	Name   string // parameter name with possible pointer operator
 	Name   string // parameter name with possible pointer operator
 }
 }
 
 
-// genFile generates file from the specified template
+// genFile generates file from the specified template.
 func genFile(templText string, td *GLHeader, fout string, gosrc bool) error {
 func genFile(templText string, td *GLHeader, fout string, gosrc bool) error {
 
 
 	// Parses the template
 	// Parses the template
@@ -236,8 +240,38 @@ void glapiCheckError(int check) {
 // Internal function to abort process when error
 // Internal function to abort process when error
 static void panic(GLenum err, const char* fname) {
 static void panic(GLenum err, const char* fname) {
 
 
-	printf("\nGLAPI Error: %d calling: %s\n", err, fname);
-	exit(1);
+		const char *msg;
+		switch(err) {
+    	case GL_NO_ERROR:
+    		msg = "No error";
+    		break;
+    	case GL_INVALID_ENUM:
+    		msg = "An unacceptable value is specified for an enumerated argument";
+    		break;
+    	case GL_INVALID_VALUE:
+    		msg = "A numeric argument is out of range";
+    		break;
+    	case GL_INVALID_OPERATION:
+    		msg = "The specified operation is not allowed in the current state";
+    		break;
+    	case GL_INVALID_FRAMEBUFFER_OPERATION:
+    		msg = "The framebuffer object is not complete";
+    		break;
+    	case GL_OUT_OF_MEMORY:
+    		msg = "There is not enough memory left to execute the command";
+    		break;
+    	case GL_STACK_UNDERFLOW:
+    		msg = "An attempt has been made to perform an operation that would cause an internal stack to underflow";
+    		break;
+    	case GL_STACK_OVERFLOW:
+    		msg = "An attempt has been made to perform an operation that would cause an internal stack to overflow";
+    		break;
+    	default:
+    		msg = "Unexpected error";
+    		break;
+    }
+    printf("\nGLAPI Error: %s (%d) calling: %s\n", msg, err, fname);
+    exit(1);
 }
 }
 
 
 
 
@@ -336,6 +370,7 @@ const templCONSTS = `
 // This file was generated automatically by "glapi2go" and contains all
 // This file was generated automatically by "glapi2go" and contains all
 // OpenGL constants specified by "#define GL_*" directives contained in
 // OpenGL constants specified by "#define GL_*" directives contained in
 // "glcorearb.h" for an specific OpenGL version converted to Go constants.
 // "glcorearb.h" for an specific OpenGL version converted to Go constants.
+
 package gls
 package gls
 
 
 const (
 const (

+ 95 - 17
gls/gls.go

@@ -1,6 +1,7 @@
 // Copyright 2016 The G3N Authors. All rights reserved.
 // Copyright 2016 The G3N Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 // license that can be found in the LICENSE file.
+
 package gls
 package gls
 
 
 // #include <stdlib.h>
 // #include <stdlib.h>
@@ -51,17 +52,17 @@ type GLS struct {
 }
 }
 
 
 // Stats contains counters of OpenGL resources being used as well
 // Stats contains counters of OpenGL resources being used as well
-// the cummulative numbers of some OpenGL calls for performance evaluation.
+// the cumulative numbers of some OpenGL calls for performance evaluation.
 type Stats struct {
 type Stats struct {
 	Shaders    int    // Current number of shader programs
 	Shaders    int    // Current number of shader programs
 	Vaos       int    // Number of Vertex Array Objects
 	Vaos       int    // Number of Vertex Array Objects
 	Buffers    int    // Number of Buffer Objects
 	Buffers    int    // Number of Buffer Objects
 	Textures   int    // Number of Textures
 	Textures   int    // Number of Textures
-	Caphits    uint64 // Cummulative number of hits for Enable/Disable
-	UnilocHits uint64 // Cummulative number of uniform location cache hits
-	UnilocMiss uint64 // Cummulative number of uniform location cache misses
-	Unisets    uint64 // Cummulative number of uniform sets
-	Drawcalls  uint64 // Cummulative number of draw calls
+	Caphits    uint64 // Cumulative number of hits for Enable/Disable
+	UnilocHits uint64 // Cumulative number of uniform location cache hits
+	UnilocMiss uint64 // Cumulative number of uniform location cache misses
+	Unisets    uint64 // Cumulative number of uniform sets
+	Drawcalls  uint64 // Cumulative number of draw calls
 }
 }
 
 
 // Polygon side view.
 // Polygon side view.
@@ -80,10 +81,10 @@ const (
 	intTrue     = 1
 	intTrue     = 1
 )
 )
 
 
-// New creates and returns a new instance of an GLS object
-// which encapsulates the state of an OpenGL context
+// New creates and returns a new instance of a GLS object,
+// which encapsulates the state of an OpenGL context.
 // This should be called only after an active OpenGL context
 // This should be called only after an active OpenGL context
-// was established, such as by creating a new window.
+// is established, such as by creating a new window.
 func New() (*GLS, error) {
 func New() (*GLS, error) {
 
 
 	gs := new(GLS)
 	gs := new(GLS)
@@ -96,7 +97,7 @@ func New() (*GLS, error) {
 	gs.setDefaultState()
 	gs.setDefaultState()
 	gs.checkErrors = true
 	gs.checkErrors = true
 
 
-	// Preallocates conversion buffers
+	// Preallocate conversion buffers
 	size := 1 * 1024
 	size := 1 * 1024
 	gs.gobuf = make([]byte, size)
 	gs.gobuf = make([]byte, size)
 	p := C.malloc(C.size_t(size))
 	p := C.malloc(C.size_t(size))
@@ -118,7 +119,7 @@ func (gs *GLS) SetCheckErrors(enable bool) {
 	gs.checkErrors = enable
 	gs.checkErrors = enable
 }
 }
 
 
-// ChecksErrors returns if error checking is enabled or not.
+// CheckErrors returns if error checking is enabled or not.
 func (gs *GLS) CheckErrors() bool {
 func (gs *GLS) CheckErrors() bool {
 
 
 	return gs.checkErrors
 	return gs.checkErrors
@@ -183,6 +184,9 @@ func (gs *GLS) Stats(s *Stats) {
 	s.Shaders = len(gs.programs)
 	s.Shaders = len(gs.programs)
 }
 }
 
 
+// ActiveTexture selects which texture unit subsequent texture state calls
+// will affect. The number of texture units an implementation supports is
+// implementation dependent, but must be at least 48 in GL 3.3.
 func (gs *GLS) ActiveTexture(texture uint32) {
 func (gs *GLS) ActiveTexture(texture uint32) {
 
 
 	if gs.activeTexture == texture {
 	if gs.activeTexture == texture {
@@ -192,26 +196,31 @@ func (gs *GLS) ActiveTexture(texture uint32) {
 	gs.activeTexture = texture
 	gs.activeTexture = texture
 }
 }
 
 
+// AttachShader attaches the specified shader object to the specified program object.
 func (gs *GLS) AttachShader(program, shader uint32) {
 func (gs *GLS) AttachShader(program, shader uint32) {
 
 
 	C.glAttachShader(C.GLuint(program), C.GLuint(shader))
 	C.glAttachShader(C.GLuint(program), C.GLuint(shader))
 }
 }
 
 
+// BindBuffer binds a buffer object to the specified buffer binding point.
 func (gs *GLS) BindBuffer(target int, vbo uint32) {
 func (gs *GLS) BindBuffer(target int, vbo uint32) {
 
 
 	C.glBindBuffer(C.GLenum(target), C.GLuint(vbo))
 	C.glBindBuffer(C.GLenum(target), C.GLuint(vbo))
 }
 }
 
 
+// BindTexture lets you create or use a named texture.
 func (gs *GLS) BindTexture(target int, tex uint32) {
 func (gs *GLS) BindTexture(target int, tex uint32) {
 
 
 	C.glBindTexture(C.GLenum(target), C.GLuint(tex))
 	C.glBindTexture(C.GLenum(target), C.GLuint(tex))
 }
 }
 
 
+// BindVertexArray binds the vertex array object.
 func (gs *GLS) BindVertexArray(vao uint32) {
 func (gs *GLS) BindVertexArray(vao uint32) {
 
 
 	C.glBindVertexArray(C.GLuint(vao))
 	C.glBindVertexArray(C.GLuint(vao))
 }
 }
 
 
+// BlendEquation sets the blend equations for all draw buffers.
 func (gs *GLS) BlendEquation(mode uint32) {
 func (gs *GLS) BlendEquation(mode uint32) {
 
 
 	if gs.blendEquation == mode {
 	if gs.blendEquation == mode {
@@ -221,6 +230,8 @@ func (gs *GLS) BlendEquation(mode uint32) {
 	gs.blendEquation = mode
 	gs.blendEquation = mode
 }
 }
 
 
+// BlendEquationSeparate sets the blend equations for all draw buffers
+// allowing different equations for the RGB and alpha components.
 func (gs *GLS) BlendEquationSeparate(modeRGB uint32, modeAlpha uint32) {
 func (gs *GLS) BlendEquationSeparate(modeRGB uint32, modeAlpha uint32) {
 
 
 	if gs.blendEquationRGB == modeRGB && gs.blendEquationAlpha == modeAlpha {
 	if gs.blendEquationRGB == modeRGB && gs.blendEquationAlpha == modeAlpha {
@@ -231,6 +242,8 @@ func (gs *GLS) BlendEquationSeparate(modeRGB uint32, modeAlpha uint32) {
 	gs.blendEquationAlpha = modeAlpha
 	gs.blendEquationAlpha = modeAlpha
 }
 }
 
 
+// BlendFunc defines the operation of blending for
+// all draw buffers when blending is enabled.
 func (gs *GLS) BlendFunc(sfactor, dfactor uint32) {
 func (gs *GLS) BlendFunc(sfactor, dfactor uint32) {
 
 
 	if gs.blendSrc == sfactor && gs.blendDst == dfactor {
 	if gs.blendSrc == sfactor && gs.blendDst == dfactor {
@@ -241,6 +254,8 @@ func (gs *GLS) BlendFunc(sfactor, dfactor uint32) {
 	gs.blendDst = dfactor
 	gs.blendDst = dfactor
 }
 }
 
 
+// BlendFuncSeparate defines the operation of blending for all draw buffers when blending
+// is enabled, allowing different operations for the RGB and alpha components.
 func (gs *GLS) BlendFuncSeparate(srcRGB uint32, dstRGB uint32, srcAlpha uint32, dstAlpha uint32) {
 func (gs *GLS) BlendFuncSeparate(srcRGB uint32, dstRGB uint32, srcAlpha uint32, dstAlpha uint32) {
 
 
 	if gs.blendSrcRGB == srcRGB && gs.blendDstRGB == dstRGB &&
 	if gs.blendSrcRGB == srcRGB && gs.blendDstRGB == dstRGB &&
@@ -254,66 +269,90 @@ func (gs *GLS) BlendFuncSeparate(srcRGB uint32, dstRGB uint32, srcAlpha uint32,
 	gs.blendDstAlpha = dstAlpha
 	gs.blendDstAlpha = dstAlpha
 }
 }
 
 
+// BufferData creates a new data store for the buffer object currently
+// bound to target, deleting any pre-existing data store.
 func (gs *GLS) BufferData(target uint32, size int, data interface{}, usage uint32) {
 func (gs *GLS) BufferData(target uint32, size int, data interface{}, usage uint32) {
 
 
 	C.glBufferData(C.GLenum(target), C.GLsizeiptr(size), ptr(data), C.GLenum(usage))
 	C.glBufferData(C.GLenum(target), C.GLsizeiptr(size), ptr(data), C.GLenum(usage))
 }
 }
 
 
+// ClearColor specifies the red, green, blue, and alpha values
+// used by glClear to clear the color buffers.
 func (gs *GLS) ClearColor(r, g, b, a float32) {
 func (gs *GLS) ClearColor(r, g, b, a float32) {
 
 
 	C.glClearColor(C.GLfloat(r), C.GLfloat(g), C.GLfloat(b), C.GLfloat(a))
 	C.glClearColor(C.GLfloat(r), C.GLfloat(g), C.GLfloat(b), C.GLfloat(a))
 }
 }
 
 
+// Clear sets the bitplane area of the window to values previously
+// selected by ClearColor, ClearDepth, and ClearStencil.
 func (gs *GLS) Clear(mask uint) {
 func (gs *GLS) Clear(mask uint) {
 
 
 	C.glClear(C.GLbitfield(mask))
 	C.glClear(C.GLbitfield(mask))
 }
 }
 
 
+// CompileShader compiles the source code strings that
+// have been stored in the specified shader object.
 func (gs *GLS) CompileShader(shader uint32) {
 func (gs *GLS) CompileShader(shader uint32) {
 
 
 	C.glCompileShader(C.GLuint(shader))
 	C.glCompileShader(C.GLuint(shader))
 }
 }
 
 
+// CreateProgram creates an empty program object and returns
+// a non-zero value by which it can be referenced.
 func (gs *GLS) CreateProgram() uint32 {
 func (gs *GLS) CreateProgram() uint32 {
 
 
 	p := C.glCreateProgram()
 	p := C.glCreateProgram()
 	return uint32(p)
 	return uint32(p)
 }
 }
 
 
+// CreateShader creates an empty shader object and returns
+// a non-zero value by which it can be referenced.
 func (gs *GLS) CreateShader(stype uint32) uint32 {
 func (gs *GLS) CreateShader(stype uint32) uint32 {
 
 
 	h := C.glCreateShader(C.GLenum(stype))
 	h := C.glCreateShader(C.GLenum(stype))
 	return uint32(h)
 	return uint32(h)
 }
 }
 
 
+// DeleteBuffers deletes n​buffer objects named
+// by the elements of the provided array.
 func (gs *GLS) DeleteBuffers(bufs ...uint32) {
 func (gs *GLS) DeleteBuffers(bufs ...uint32) {
 
 
 	C.glDeleteBuffers(C.GLsizei(len(bufs)), (*C.GLuint)(&bufs[0]))
 	C.glDeleteBuffers(C.GLsizei(len(bufs)), (*C.GLuint)(&bufs[0]))
 	gs.stats.Buffers -= len(bufs)
 	gs.stats.Buffers -= len(bufs)
 }
 }
 
 
+// DeleteShader frees the memory and invalidates the name
+// associated with the specified shader object.
 func (gs *GLS) DeleteShader(shader uint32) {
 func (gs *GLS) DeleteShader(shader uint32) {
 
 
 	C.glDeleteShader(C.GLuint(shader))
 	C.glDeleteShader(C.GLuint(shader))
 }
 }
 
 
+// DeleteProgram frees the memory and invalidates the name
+// associated with the specified program object.
 func (gs *GLS) DeleteProgram(program uint32) {
 func (gs *GLS) DeleteProgram(program uint32) {
 
 
 	C.glDeleteProgram(C.GLuint(program))
 	C.glDeleteProgram(C.GLuint(program))
 }
 }
 
 
+// DeleteTextures deletes n​textures named
+// by the elements of the provided array.
 func (gs *GLS) DeleteTextures(tex ...uint32) {
 func (gs *GLS) DeleteTextures(tex ...uint32) {
 
 
 	C.glDeleteTextures(C.GLsizei(len(tex)), (*C.GLuint)(&tex[0]))
 	C.glDeleteTextures(C.GLsizei(len(tex)), (*C.GLuint)(&tex[0]))
 	gs.stats.Textures -= len(tex)
 	gs.stats.Textures -= len(tex)
 }
 }
 
 
+// DeleteVertexArrays deletes n​vertex array objects named
+// by the elements of the provided array.
 func (gs *GLS) DeleteVertexArrays(vaos ...uint32) {
 func (gs *GLS) DeleteVertexArrays(vaos ...uint32) {
 
 
 	C.glDeleteVertexArrays(C.GLsizei(len(vaos)), (*C.GLuint)(&vaos[0]))
 	C.glDeleteVertexArrays(C.GLsizei(len(vaos)), (*C.GLuint)(&vaos[0]))
 	gs.stats.Vaos -= len(vaos)
 	gs.stats.Vaos -= len(vaos)
 }
 }
 
 
+// DepthFunc specifies the function used to compare each incoming pixel
+// depth value with the depth value present in the depth buffer.
 func (gs *GLS) DepthFunc(mode uint32) {
 func (gs *GLS) DepthFunc(mode uint32) {
 
 
 	if gs.depthFunc == mode {
 	if gs.depthFunc == mode {
@@ -323,6 +362,7 @@ func (gs *GLS) DepthFunc(mode uint32) {
 	gs.depthFunc = mode
 	gs.depthFunc = mode
 }
 }
 
 
+// DepthMask enables or disables writing into the depth buffer.
 func (gs *GLS) DepthMask(flag bool) {
 func (gs *GLS) DepthMask(flag bool) {
 
 
 	if gs.depthMask == intTrue && flag {
 	if gs.depthMask == intTrue && flag {
@@ -339,23 +379,27 @@ func (gs *GLS) DepthMask(flag bool) {
 	}
 	}
 }
 }
 
 
+// DrawArrays renders primitives from array data.
 func (gs *GLS) DrawArrays(mode uint32, first int32, count int32) {
 func (gs *GLS) DrawArrays(mode uint32, first int32, count int32) {
 
 
 	C.glDrawArrays(C.GLenum(mode), C.GLint(first), C.GLsizei(count))
 	C.glDrawArrays(C.GLenum(mode), C.GLint(first), C.GLsizei(count))
 	gs.stats.Drawcalls++
 	gs.stats.Drawcalls++
 }
 }
 
 
+// DrawBuffer specifies which color buffers are to be drawn into.
 func (gs *GLS) DrawBuffer(mode uint32) {
 func (gs *GLS) DrawBuffer(mode uint32) {
 
 
 	C.glDrawBuffer(C.GLenum(mode))
 	C.glDrawBuffer(C.GLenum(mode))
 }
 }
 
 
+// DrawElements renders primitives from array data.
 func (gs *GLS) DrawElements(mode uint32, count int32, itype uint32, start uint32) {
 func (gs *GLS) DrawElements(mode uint32, count int32, itype uint32, start uint32) {
 
 
 	C.glDrawElements(C.GLenum(mode), C.GLsizei(count), C.GLenum(itype), unsafe.Pointer(uintptr(start)))
 	C.glDrawElements(C.GLenum(mode), C.GLsizei(count), C.GLenum(itype), unsafe.Pointer(uintptr(start)))
 	gs.stats.Drawcalls++
 	gs.stats.Drawcalls++
 }
 }
 
 
+// Enable enables the specified capability.
 func (gs *GLS) Enable(cap int) {
 func (gs *GLS) Enable(cap int) {
 
 
 	if gs.capabilities[cap] == capEnabled {
 	if gs.capabilities[cap] == capEnabled {
@@ -366,11 +410,7 @@ func (gs *GLS) Enable(cap int) {
 	gs.capabilities[cap] = capEnabled
 	gs.capabilities[cap] = capEnabled
 }
 }
 
 
-func (gs *GLS) EnableVertexAttribArray(index uint32) {
-
-	C.glEnableVertexAttribArray(C.GLuint(index))
-}
-
+// Disable disables the specified capability.
 func (gs *GLS) Disable(cap int) {
 func (gs *GLS) Disable(cap int) {
 
 
 	if gs.capabilities[cap] == capDisabled {
 	if gs.capabilities[cap] == capDisabled {
@@ -381,11 +421,19 @@ func (gs *GLS) Disable(cap int) {
 	gs.capabilities[cap] = capDisabled
 	gs.capabilities[cap] = capDisabled
 }
 }
 
 
+// EnableVertexAttribArray enables a generic vertex attribute array.
+func (gs *GLS) EnableVertexAttribArray(index uint32) {
+
+	C.glEnableVertexAttribArray(C.GLuint(index))
+}
+
+// CullFace specifies whether front- or back-facing facets can be culled.
 func (gs *GLS) CullFace(mode uint32) {
 func (gs *GLS) CullFace(mode uint32) {
 
 
 	C.glCullFace(C.GLenum(mode))
 	C.glCullFace(C.GLenum(mode))
 }
 }
 
 
+// FrontFace defines front- and back-facing polygons.
 func (gs *GLS) FrontFace(mode uint32) {
 func (gs *GLS) FrontFace(mode uint32) {
 
 
 	if gs.frontFace == mode {
 	if gs.frontFace == mode {
@@ -395,6 +443,7 @@ func (gs *GLS) FrontFace(mode uint32) {
 	gs.frontFace = mode
 	gs.frontFace = mode
 }
 }
 
 
+// GenBuffer generates a​buffer object name.
 func (gs *GLS) GenBuffer() uint32 {
 func (gs *GLS) GenBuffer() uint32 {
 
 
 	var buf uint32
 	var buf uint32
@@ -403,11 +452,13 @@ func (gs *GLS) GenBuffer() uint32 {
 	return buf
 	return buf
 }
 }
 
 
+// GenerateMipmap generates mipmaps for the specified texture target.
 func (gs *GLS) GenerateMipmap(target uint32) {
 func (gs *GLS) GenerateMipmap(target uint32) {
 
 
 	C.glGenerateMipmap(C.GLenum(target))
 	C.glGenerateMipmap(C.GLenum(target))
 }
 }
 
 
+// GenTexture generates a texture object name.
 func (gs *GLS) GenTexture() uint32 {
 func (gs *GLS) GenTexture() uint32 {
 
 
 	var tex uint32
 	var tex uint32
@@ -416,6 +467,7 @@ func (gs *GLS) GenTexture() uint32 {
 	return tex
 	return tex
 }
 }
 
 
+// GenVertexArray generates a vertex array object name.
 func (gs *GLS) GenVertexArray() uint32 {
 func (gs *GLS) GenVertexArray() uint32 {
 
 
 	var vao uint32
 	var vao uint32
@@ -424,12 +476,14 @@ func (gs *GLS) GenVertexArray() uint32 {
 	return vao
 	return vao
 }
 }
 
 
+// GetAttribLocation returns the location of the specified attribute variable.
 func (gs *GLS) GetAttribLocation(program uint32, name string) int32 {
 func (gs *GLS) GetAttribLocation(program uint32, name string) int32 {
 
 
 	loc := C.glGetAttribLocation(C.GLuint(program), gs.gobufStr(name))
 	loc := C.glGetAttribLocation(C.GLuint(program), gs.gobufStr(name))
 	return int32(loc)
 	return int32(loc)
 }
 }
 
 
+// GetProgramiv returns the specified parameter from the specified program object.
 func (gs *GLS) GetProgramiv(program, pname uint32, params *int32) {
 func (gs *GLS) GetProgramiv(program, pname uint32, params *int32) {
 
 
 	C.glGetProgramiv(C.GLuint(program), C.GLenum(pname), (*C.GLint)(params))
 	C.glGetProgramiv(C.GLuint(program), C.GLenum(pname), (*C.GLint)(params))
@@ -459,6 +513,7 @@ func (gs *GLS) GetShaderInfoLog(shader uint32) string {
 	return string(gs.gobuf[:length])
 	return string(gs.gobuf[:length])
 }
 }
 
 
+// GetString returns a string describing the specified aspect of the current GL connection.
 func (gs *GLS) GetString(name uint32) string {
 func (gs *GLS) GetString(name uint32) string {
 
 
 	cs := C.glGetString(C.GLenum(name))
 	cs := C.glGetString(C.GLenum(name))
@@ -472,11 +527,13 @@ func (gs *GLS) GetUniformLocation(program uint32, name string) int32 {
 	return int32(loc)
 	return int32(loc)
 }
 }
 
 
+// GetViewport returns the current viewport information.
 func (gs *GLS) GetViewport() (x, y, width, height int32) {
 func (gs *GLS) GetViewport() (x, y, width, height int32) {
 
 
 	return gs.viewportX, gs.viewportY, gs.viewportWidth, gs.viewportHeight
 	return gs.viewportX, gs.viewportY, gs.viewportWidth, gs.viewportHeight
 }
 }
 
 
+// LineWidth specifies the rasterized width of both aliased and antialiased lines.
 func (gs *GLS) LineWidth(width float32) {
 func (gs *GLS) LineWidth(width float32) {
 
 
 	if gs.lineWidth == width {
 	if gs.lineWidth == width {
@@ -486,27 +543,32 @@ func (gs *GLS) LineWidth(width float32) {
 	gs.lineWidth = width
 	gs.lineWidth = width
 }
 }
 
 
+// LinkProgram links the specified program object.
 func (gs *GLS) LinkProgram(program uint32) {
 func (gs *GLS) LinkProgram(program uint32) {
 
 
 	C.glLinkProgram(C.GLuint(program))
 	C.glLinkProgram(C.GLuint(program))
 }
 }
 
 
+// GetShaderiv returns the specified parameter from the specified shader object.
 func (gs *GLS) GetShaderiv(shader, pname uint32, params *int32) {
 func (gs *GLS) GetShaderiv(shader, pname uint32, params *int32) {
 
 
 	C.glGetShaderiv(C.GLuint(shader), C.GLenum(pname), (*C.GLint)(params))
 	C.glGetShaderiv(C.GLuint(shader), C.GLenum(pname), (*C.GLint)(params))
 }
 }
 
 
+// Scissor defines the scissor box rectangle in window coordinates.
 func (gs *GLS) Scissor(x, y int32, width, height uint32) {
 func (gs *GLS) Scissor(x, y int32, width, height uint32) {
 
 
 	C.glScissor(C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height))
 	C.glScissor(C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height))
 }
 }
 
 
+// ShaderSource sets the source code for the specified shader object.
 func (gs *GLS) ShaderSource(shader uint32, src string) {
 func (gs *GLS) ShaderSource(shader uint32, src string) {
 
 
 	csource := gs.cbufStr(src)
 	csource := gs.cbufStr(src)
 	C.glShaderSource(C.GLuint(shader), 1, (**C.GLchar)(unsafe.Pointer(&csource)), nil)
 	C.glShaderSource(C.GLuint(shader), 1, (**C.GLchar)(unsafe.Pointer(&csource)), nil)
 }
 }
 
 
+// TexImage2D specifies a two-dimensional texture image.
 func (gs *GLS) TexImage2D(target uint32, level int32, iformat int32, width int32, height int32, border int32, format uint32, itype uint32, data interface{}) {
 func (gs *GLS) TexImage2D(target uint32, level int32, iformat int32, width int32, height int32, border int32, format uint32, itype uint32, data interface{}) {
 
 
 	C.glTexImage2D(C.GLenum(target),
 	C.glTexImage2D(C.GLenum(target),
@@ -520,11 +582,13 @@ func (gs *GLS) TexImage2D(target uint32, level int32, iformat int32, width int32
 		ptr(data))
 		ptr(data))
 }
 }
 
 
+// TexParameteri sets the specified texture parameter on the specified texture.
 func (gs *GLS) TexParameteri(target uint32, pname uint32, param int32) {
 func (gs *GLS) TexParameteri(target uint32, pname uint32, param int32) {
 
 
 	C.glTexParameteri(C.GLenum(target), C.GLenum(pname), C.GLint(param))
 	C.glTexParameteri(C.GLenum(target), C.GLenum(pname), C.GLint(param))
 }
 }
 
 
+// PolygonMode controls the interpretation of polygons for rasterization.
 func (gs *GLS) PolygonMode(face, mode uint32) {
 func (gs *GLS) PolygonMode(face, mode uint32) {
 
 
 	if gs.polygonModeFace == face && gs.polygonModeMode == mode {
 	if gs.polygonModeFace == face && gs.polygonModeMode == mode {
@@ -535,6 +599,7 @@ func (gs *GLS) PolygonMode(face, mode uint32) {
 	gs.polygonModeMode = mode
 	gs.polygonModeMode = mode
 }
 }
 
 
+// PolygonOffset sets the scale and units used to calculate depth values.
 func (gs *GLS) PolygonOffset(factor float32, units float32) {
 func (gs *GLS) PolygonOffset(factor float32, units float32) {
 
 
 	if gs.polygonOffsetFactor == factor && gs.polygonOffsetUnits == units {
 	if gs.polygonOffsetFactor == factor && gs.polygonOffsetUnits == units {
@@ -545,54 +610,63 @@ func (gs *GLS) PolygonOffset(factor float32, units float32) {
 	gs.polygonOffsetUnits = units
 	gs.polygonOffsetUnits = units
 }
 }
 
 
+// Uniform1i sets the value of an int uniform variable for the current program object.
 func (gs *GLS) Uniform1i(location int32, v0 int32) {
 func (gs *GLS) Uniform1i(location int32, v0 int32) {
 
 
 	C.glUniform1i(C.GLint(location), C.GLint(v0))
 	C.glUniform1i(C.GLint(location), C.GLint(v0))
 	gs.stats.Unisets++
 	gs.stats.Unisets++
 }
 }
 
 
+// Uniform1f sets the value of a float uniform variable for the current program object.
 func (gs *GLS) Uniform1f(location int32, v0 float32) {
 func (gs *GLS) Uniform1f(location int32, v0 float32) {
 
 
 	C.glUniform1f(C.GLint(location), C.GLfloat(v0))
 	C.glUniform1f(C.GLint(location), C.GLfloat(v0))
 	gs.stats.Unisets++
 	gs.stats.Unisets++
 }
 }
 
 
+// Uniform2f sets the value of a vec2 uniform variable for the current program object.
 func (gs *GLS) Uniform2f(location int32, v0, v1 float32) {
 func (gs *GLS) Uniform2f(location int32, v0, v1 float32) {
 
 
 	C.glUniform2f(C.GLint(location), C.GLfloat(v0), C.GLfloat(v1))
 	C.glUniform2f(C.GLint(location), C.GLfloat(v0), C.GLfloat(v1))
 	gs.stats.Unisets++
 	gs.stats.Unisets++
 }
 }
 
 
+// Uniform3f sets the value of a vec3 uniform variable for the current program object.
 func (gs *GLS) Uniform3f(location int32, v0, v1, v2 float32) {
 func (gs *GLS) Uniform3f(location int32, v0, v1, v2 float32) {
 
 
 	C.glUniform3f(C.GLint(location), C.GLfloat(v0), C.GLfloat(v1), C.GLfloat(v2))
 	C.glUniform3f(C.GLint(location), C.GLfloat(v0), C.GLfloat(v1), C.GLfloat(v2))
 	gs.stats.Unisets++
 	gs.stats.Unisets++
 }
 }
 
 
+// Uniform4f sets the value of a vec4 uniform variable for the current program object.
 func (gs *GLS) Uniform4f(location int32, v0, v1, v2, v3 float32) {
 func (gs *GLS) Uniform4f(location int32, v0, v1, v2, v3 float32) {
 
 
 	C.glUniform4f(C.GLint(location), C.GLfloat(v0), C.GLfloat(v1), C.GLfloat(v2), C.GLfloat(v3))
 	C.glUniform4f(C.GLint(location), C.GLfloat(v0), C.GLfloat(v1), C.GLfloat(v2), C.GLfloat(v3))
 	gs.stats.Unisets++
 	gs.stats.Unisets++
 }
 }
 
 
+// UniformMatrix3fv sets the value of one or many 3x3 float matrices for the current program object.
 func (gs *GLS) UniformMatrix3fv(location int32, count int32, transpose bool, pm *float32) {
 func (gs *GLS) UniformMatrix3fv(location int32, count int32, transpose bool, pm *float32) {
 
 
 	C.glUniformMatrix3fv(C.GLint(location), C.GLsizei(count), bool2c(transpose), (*C.GLfloat)(pm))
 	C.glUniformMatrix3fv(C.GLint(location), C.GLsizei(count), bool2c(transpose), (*C.GLfloat)(pm))
 	gs.stats.Unisets++
 	gs.stats.Unisets++
 }
 }
 
 
+// UniformMatrix4fv sets the value of one or many 4x4 float matrices for the current program object.
 func (gs *GLS) UniformMatrix4fv(location int32, count int32, transpose bool, pm *float32) {
 func (gs *GLS) UniformMatrix4fv(location int32, count int32, transpose bool, pm *float32) {
 
 
 	C.glUniformMatrix4fv(C.GLint(location), C.GLsizei(count), bool2c(transpose), (*C.GLfloat)(pm))
 	C.glUniformMatrix4fv(C.GLint(location), C.GLsizei(count), bool2c(transpose), (*C.GLfloat)(pm))
 	gs.stats.Unisets++
 	gs.stats.Unisets++
 }
 }
 
 
+// Uniform1fv sets the value of one or many float uniform variables for the current program object.
 func (gs *GLS) Uniform1fv(location int32, count int32, v []float32) {
 func (gs *GLS) Uniform1fv(location int32, count int32, v []float32) {
 
 
 	C.glUniform1fv(C.GLint(location), C.GLsizei(count), (*C.GLfloat)(&v[0]))
 	C.glUniform1fv(C.GLint(location), C.GLsizei(count), (*C.GLfloat)(&v[0]))
 	gs.stats.Unisets++
 	gs.stats.Unisets++
 }
 }
 
 
+// Uniform2fv sets the value of one or many vec2 uniform variables for the current program object.
 func (gs *GLS) Uniform2fv(location int32, count int32, v *float32) {
 func (gs *GLS) Uniform2fv(location int32, count int32, v *float32) {
 
 
 	C.glUniform2fv(C.GLint(location), C.GLsizei(count), (*C.GLfloat)(v))
 	C.glUniform2fv(C.GLint(location), C.GLsizei(count), (*C.GLfloat)(v))
@@ -605,6 +679,7 @@ func (gs *GLS) Uniform2fvUP(location int32, count int32, v unsafe.Pointer) {
 	gs.stats.Unisets++
 	gs.stats.Unisets++
 }
 }
 
 
+// Uniform3fv sets the value of one or many vec3 uniform variables for the current program object.
 func (gs *GLS) Uniform3fv(location int32, count int32, v *float32) {
 func (gs *GLS) Uniform3fv(location int32, count int32, v *float32) {
 
 
 	C.glUniform3fv(C.GLint(location), C.GLsizei(count), (*C.GLfloat)(v))
 	C.glUniform3fv(C.GLint(location), C.GLsizei(count), (*C.GLfloat)(v))
@@ -617,6 +692,7 @@ func (gs *GLS) Uniform3fvUP(location int32, count int32, v unsafe.Pointer) {
 	gs.stats.Unisets++
 	gs.stats.Unisets++
 }
 }
 
 
+// Uniform4fv sets the value of one or many vec4 uniform variables for the current program object.
 func (gs *GLS) Uniform4fv(location int32, count int32, v []float32) {
 func (gs *GLS) Uniform4fv(location int32, count int32, v []float32) {
 
 
 	C.glUniform4fv(C.GLint(location), C.GLsizei(count), (*C.GLfloat)(&v[0]))
 	C.glUniform4fv(C.GLint(location), C.GLsizei(count), (*C.GLfloat)(&v[0]))
@@ -629,11 +705,13 @@ func (gs *GLS) Uniform4fvUP(location int32, count int32, v unsafe.Pointer) {
 	gs.stats.Unisets++
 	gs.stats.Unisets++
 }
 }
 
 
+// VertexAttribPointer defines an array of generic vertex attribute data.
 func (gs *GLS) VertexAttribPointer(index uint32, size int32, xtype uint32, normalized bool, stride int32, offset uint32) {
 func (gs *GLS) VertexAttribPointer(index uint32, size int32, xtype uint32, normalized bool, stride int32, offset uint32) {
 
 
 	C.glVertexAttribPointer(C.GLuint(index), C.GLint(size), C.GLenum(xtype), bool2c(normalized), C.GLsizei(stride), unsafe.Pointer(uintptr(offset)))
 	C.glVertexAttribPointer(C.GLuint(index), C.GLint(size), C.GLenum(xtype), bool2c(normalized), C.GLsizei(stride), unsafe.Pointer(uintptr(offset)))
 }
 }
 
 
+// Viewport sets the viewport.
 func (gs *GLS) Viewport(x, y, width, height int32) {
 func (gs *GLS) Viewport(x, y, width, height int32) {
 
 
 	C.glViewport(C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height))
 	C.glViewport(C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height))
@@ -643,7 +721,7 @@ func (gs *GLS) Viewport(x, y, width, height int32) {
 	gs.viewportHeight = height
 	gs.viewportHeight = height
 }
 }
 
 
-// Use set this program as the current program.
+// UseProgram sets the specified program as the current program.
 func (gs *GLS) UseProgram(prog *Program) {
 func (gs *GLS) UseProgram(prog *Program) {
 
 
 	if prog.handle == 0 {
 	if prog.handle == 0 {

+ 3 - 3
gls/program.go

@@ -13,7 +13,7 @@ import (
 	"strings"
 	"strings"
 )
 )
 
 
-// Shader Program Object
+// Program represents a shader program.
 type Program struct {
 type Program struct {
 	// Shows source code in error messages
 	// Shows source code in error messages
 	ShowSource bool
 	ShowSource bool
@@ -50,7 +50,7 @@ func (gs *GLS) NewProgram() *Program {
 	return prog
 	return prog
 }
 }
 
 
-// AddShaders adds a shader to this program.
+// AddShader adds a shader to this program.
 // This must be done before the program is built.
 // This must be done before the program is built.
 func (prog *Program) AddShader(stype uint32, source string, defines map[string]interface{}) {
 func (prog *Program) AddShader(stype uint32, source string, defines map[string]interface{}) {
 
 
@@ -144,7 +144,7 @@ func (prog *Program) Handle() uint32 {
 	return prog.handle
 	return prog.handle
 }
 }
 
 
-// GetAttributeLocation returns the location of the specified attribute
+// GetAttribLocation returns the location of the specified attribute
 // in this program. This location is internally cached.
 // in this program. This location is internally cached.
 func (prog *Program) GetAttribLocation(name string) int32 {
 func (prog *Program) GetAttribLocation(name string) int32 {
 
 

+ 64 - 30
gls/vbo.go

@@ -9,23 +9,23 @@ import (
 	"unsafe"
 	"unsafe"
 )
 )
 
 
-// VBO abstracts an OpenGL Vertex Buffer Object
+// VBO abstracts an OpenGL Vertex Buffer Object.
 type VBO struct {
 type VBO struct {
-	gs      *GLS            // reference to GLS state
+	gs      *GLS            // Reference to OpenGL state
 	handle  uint32          // OpenGL handle for this VBO
 	handle  uint32          // OpenGL handle for this VBO
-	usage   uint32          // Expected usage patter of the buffer
+	usage   uint32          // Expected usage pattern of the buffer
 	update  bool            // Update flag
 	update  bool            // Update flag
 	buffer  math32.ArrayF32 // Data buffer
 	buffer  math32.ArrayF32 // Data buffer
 	attribs []VBOattrib     // List of attributes
 	attribs []VBOattrib     // List of attributes
 }
 }
 
 
-// VBOattrib describes one attribute of an OpenGL Vertex Buffer Object
+// VBOattrib describes one attribute of an OpenGL Vertex Buffer Object.
 type VBOattrib struct {
 type VBOattrib struct {
 	Name     string // Name of of the attribute
 	Name     string // Name of of the attribute
-	ItemSize int32  // Number of elements for each item
+	ItemSize int32  // Number of elements
 }
 }
 
 
-// NewVBO creates and returns a pointer to a new OpenGL Vertex Buffer Object
+// NewVBO creates and returns a pointer to a new OpenGL Vertex Buffer Object.
 func NewVBO() *VBO {
 func NewVBO() *VBO {
 
 
 	vbo := new(VBO)
 	vbo := new(VBO)
@@ -33,7 +33,7 @@ func NewVBO() *VBO {
 	return vbo
 	return vbo
 }
 }
 
 
-// init initializes this VBO
+// init initializes the VBO.
 func (vbo *VBO) init() {
 func (vbo *VBO) init() {
 
 
 	vbo.gs = nil
 	vbo.gs = nil
@@ -43,7 +43,7 @@ func (vbo *VBO) init() {
 	vbo.attribs = make([]VBOattrib, 0)
 	vbo.attribs = make([]VBOattrib, 0)
 }
 }
 
 
-// AddAttrib adds a new attribute to this VBO
+// AddAttrib adds a new attribute to the VBO.
 func (vbo *VBO) AddAttrib(name string, itemSize int32) *VBO {
 func (vbo *VBO) AddAttrib(name string, itemSize int32) *VBO {
 
 
 	vbo.attribs = append(vbo.attribs, VBOattrib{
 	vbo.attribs = append(vbo.attribs, VBOattrib{
@@ -53,8 +53,8 @@ func (vbo *VBO) AddAttrib(name string, itemSize int32) *VBO {
 	return vbo
 	return vbo
 }
 }
 
 
-// Attrib finds and returns pointer the attribute with the specified name
-// or nil if not found
+// Attrib finds and returns a pointer to the VBO attribute with the specified name.
+// Returns nil if not found.
 func (vbo *VBO) Attrib(name string) *VBOattrib {
 func (vbo *VBO) Attrib(name string) *VBOattrib {
 
 
 	for _, attr := range vbo.attribs {
 	for _, attr := range vbo.attribs {
@@ -65,20 +65,26 @@ func (vbo *VBO) Attrib(name string) *VBOattrib {
 	return nil
 	return nil
 }
 }
 
 
-// AttribAt returns pointer to the VBO attribute at the specified index
+// AttribAt returns a pointer to the VBO attribute at the specified index.
 func (vbo *VBO) AttribAt(idx int) *VBOattrib {
 func (vbo *VBO) AttribAt(idx int) *VBOattrib {
 
 
 	return &vbo.attribs[idx]
 	return &vbo.attribs[idx]
 }
 }
 
 
-// AttribCount returns the current number of attributes for this VBO
+// AttribCount returns the current number of attributes for this VBO.
 func (vbo *VBO) AttribCount() int {
 func (vbo *VBO) AttribCount() int {
 
 
 	return len(vbo.attribs)
 	return len(vbo.attribs)
 }
 }
 
 
-// Dispose disposes of this VBO OpenGL resources
-// As currently the VBO is used only by the Geometry object
+// Attributes returns the attributes for this VBO.
+func (vbo *VBO) Attributes() []VBOattrib {
+
+	return vbo.attribs
+}
+
+// Dispose disposes of the OpenGL resources used by the VBO.
+// As currently the VBO is used only by Geometry objects
 // it is not referenced counted.
 // it is not referenced counted.
 func (vbo *VBO) Dispose() {
 func (vbo *VBO) Dispose() {
 
 
@@ -88,7 +94,7 @@ func (vbo *VBO) Dispose() {
 	vbo.gs = nil
 	vbo.gs = nil
 }
 }
 
 
-// Sets the VBO buffer
+// SetBuffer sets the VBO buffer.
 func (vbo *VBO) SetBuffer(buffer math32.ArrayF32) *VBO {
 func (vbo *VBO) SetBuffer(buffer math32.ArrayF32) *VBO {
 
 
 	vbo.buffer = buffer
 	vbo.buffer = buffer
@@ -96,40 +102,65 @@ func (vbo *VBO) SetBuffer(buffer math32.ArrayF32) *VBO {
 	return vbo
 	return vbo
 }
 }
 
 
-// Sets the expected usage pattern of the buffer.
+// SetUsage sets the expected usage pattern of the buffer.
 // The default value is GL_STATIC_DRAW.
 // The default value is GL_STATIC_DRAW.
 func (vbo *VBO) SetUsage(usage uint32) {
 func (vbo *VBO) SetUsage(usage uint32) {
 
 
 	vbo.usage = usage
 	vbo.usage = usage
 }
 }
 
 
-// Buffer returns pointer to the VBO buffer
+// Buffer returns a pointer to the VBO buffer.
 func (vbo *VBO) Buffer() *math32.ArrayF32 {
 func (vbo *VBO) Buffer() *math32.ArrayF32 {
 
 
 	return &vbo.buffer
 	return &vbo.buffer
 }
 }
 
 
-// Updates sets the update flag to force the VBO update
+// Update sets the update flag to force the VBO update.
 func (vbo *VBO) Update() {
 func (vbo *VBO) Update() {
 
 
 	vbo.update = true
 	vbo.update = true
 }
 }
 
 
-// Stride returns the stride of this VBO which is the number of bytes
-// used by one group attributes side by side in the buffer
-// Ex: x y z r g b x y z r g b ...x y z r g b
-// The stride will be: sizeof(float) * 6 = 24
+// AttribOffset returns the total number of elements from
+// all attributes preceding the specified attribute.
+func (vbo *VBO) AttribOffset(name string) int {
+
+	elementCount := 0
+	for _, attr := range vbo.attribs {
+		if attr.Name == name {
+			return elementCount
+		}
+		elementCount += int(attr.ItemSize)
+	}
+	return elementCount
+}
+
+// Stride returns the stride of the VBO, which is the number of elements in
+// one complete set of group attributes. E.g. for an interleaved VBO with two attributes:
+// "VertexPosition" (3 elements) and "VertexTexcoord" (2 elements), the stride would be 5:
+// [X, Y, Z, U, V], X, Y, Z, U, V, X, Y, Z, U, V... X, Y, Z, U, V.
 func (vbo *VBO) Stride() int {
 func (vbo *VBO) Stride() int {
 
 
 	stride := 0
 	stride := 0
-	elsize := int(unsafe.Sizeof(float32(0)))
 	for _, attrib := range vbo.attribs {
 	for _, attrib := range vbo.attribs {
-		stride += elsize * int(attrib.ItemSize)
+		stride += int(attrib.ItemSize)
 	}
 	}
 	return stride
 	return stride
 }
 }
 
 
-// Transfer is called internally and transfer the data in the VBO buffer to OpenGL if necessary
+// StrideSize returns the number of bytes used by one complete set of group attributes.
+// E.g. for an interleaved VBO with two attributes: "VertexPosition" (3 elements)
+// and "VertexTexcoord" (2 elements), the stride would be 5:
+// [X, Y, Z, U, V], X, Y, Z, U, V, X, Y, Z, U, V... X, Y, Z, U, V
+// and the stride size would be: sizeof(float)*stride = 4*5 = 20
+func (vbo *VBO) StrideSize() int {
+
+	stride := vbo.Stride()
+	elsize := int(unsafe.Sizeof(float32(0)))
+	return stride * elsize
+}
+
+// Transfer (called internally) transfers the data from the VBO buffer to OpenGL if necessary.
 func (vbo *VBO) Transfer(gs *GLS) {
 func (vbo *VBO) Transfer(gs *GLS) {
 
 
 	// If the VBO buffer is empty, ignore
 	// If the VBO buffer is empty, ignore
@@ -141,11 +172,11 @@ func (vbo *VBO) Transfer(gs *GLS) {
 	if vbo.gs == nil {
 	if vbo.gs == nil {
 		vbo.handle = gs.GenBuffer()
 		vbo.handle = gs.GenBuffer()
 		gs.BindBuffer(ARRAY_BUFFER, vbo.handle)
 		gs.BindBuffer(ARRAY_BUFFER, vbo.handle)
-		// Calculates stride
-		stride := vbo.Stride()
+		// Calculates stride size
+		strideSize := vbo.StrideSize()
 		// For each attribute
 		// For each attribute
-		var items uint32 = 0
-		var offset uint32 = 0
+		var items uint32
+		var offset uint32
 		elsize := int32(unsafe.Sizeof(float32(0)))
 		elsize := int32(unsafe.Sizeof(float32(0)))
 		for _, attrib := range vbo.attribs {
 		for _, attrib := range vbo.attribs {
 			// Get attribute location in the current program
 			// Get attribute location in the current program
@@ -155,15 +186,18 @@ func (vbo *VBO) Transfer(gs *GLS) {
 			}
 			}
 			// Enables attribute and sets its stride and offset in the buffer
 			// Enables attribute and sets its stride and offset in the buffer
 			gs.EnableVertexAttribArray(uint32(loc))
 			gs.EnableVertexAttribArray(uint32(loc))
-			gs.VertexAttribPointer(uint32(loc), attrib.ItemSize, FLOAT, false, int32(stride), offset)
+			gs.VertexAttribPointer(uint32(loc), attrib.ItemSize, FLOAT, false, int32(strideSize), offset)
 			items += uint32(attrib.ItemSize)
 			items += uint32(attrib.ItemSize)
 			offset = uint32(elsize) * items
 			offset = uint32(elsize) * items
 		}
 		}
 		vbo.gs = gs // this indicates that the vbo was initialized
 		vbo.gs = gs // this indicates that the vbo was initialized
 	}
 	}
+
+	// If nothing has changed, no need to transfer data to OpenGL
 	if !vbo.update {
 	if !vbo.update {
 		return
 		return
 	}
 	}
+
 	// Transfer the VBO data to OpenGL
 	// Transfer the VBO data to OpenGL
 	gs.BindBuffer(ARRAY_BUFFER, vbo.handle)
 	gs.BindBuffer(ARRAY_BUFFER, vbo.handle)
 	gs.BufferData(ARRAY_BUFFER, vbo.buffer.Bytes(), &vbo.buffer[0], vbo.usage)
 	gs.BufferData(ARRAY_BUFFER, vbo.buffer.Bytes(), &vbo.buffer[0], vbo.usage)

+ 2 - 1
graphic/axis_helper.go

@@ -11,10 +11,12 @@ import (
 	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/math32"
 )
 )
 
 
+// AxisHelper is the visual representation of the three axes
 type AxisHelper struct {
 type AxisHelper struct {
 	Lines
 	Lines
 }
 }
 
 
+// NewAxisHelper returns a pointer to a new AxisHelper object
 func NewAxisHelper(size float32) *AxisHelper {
 func NewAxisHelper(size float32) *AxisHelper {
 
 
 	axis := new(AxisHelper)
 	axis := new(AxisHelper)
@@ -39,7 +41,6 @@ func NewAxisHelper(size float32) *AxisHelper {
 
 
 	// Creates line material
 	// Creates line material
 	mat := material.NewBasic()
 	mat := material.NewBasic()
-	mat.SetLineWidth(2.0)
 
 
 	// Initialize lines with the specified geometry and material
 	// Initialize lines with the specified geometry and material
 	axis.Lines.Init(geom, mat)
 	axis.Lines.Init(geom, mat)

+ 62 - 12
graphic/graphic.go

@@ -9,6 +9,7 @@ import (
 	"github.com/g3n/engine/geometry"
 	"github.com/g3n/engine/geometry"
 	"github.com/g3n/engine/gls"
 	"github.com/g3n/engine/gls"
 	"github.com/g3n/engine/material"
 	"github.com/g3n/engine/material"
+	"github.com/g3n/engine/math32"
 )
 )
 
 
 // Graphic is a Node which has a visible representation in the scene.
 // Graphic is a Node which has a visible representation in the scene.
@@ -21,11 +22,15 @@ type Graphic struct {
 	materials  []GraphicMaterial  // Materials
 	materials  []GraphicMaterial  // Materials
 	mode       uint32             // OpenGL primitive
 	mode       uint32             // OpenGL primitive
 	renderable bool               // Renderable flag
 	renderable bool               // Renderable flag
+	cullable   bool               // Cullable flag
+
+	mvm        math32.Matrix4     // Cached ModelView matrix
+	mvpm       math32.Matrix4     // Cached ModelViewProjection matrix
 }
 }
 
 
 // GraphicMaterial specifies the material to be used for
 // GraphicMaterial specifies the material to be used for
 // a subset of vertices from the Graphic geometry
 // a subset of vertices from the Graphic geometry
-// A Graphic object has at least one GraphicMaterial
+// A Graphic object has at least one GraphicMaterial.
 type GraphicMaterial struct {
 type GraphicMaterial struct {
 	imat     material.IMaterial // Associated material
 	imat     material.IMaterial // Associated material
 	start    int                // Index of first element in the geometry
 	start    int                // Index of first element in the geometry
@@ -33,13 +38,15 @@ type GraphicMaterial struct {
 	igraphic IGraphic           // Graphic which contains this GraphicMaterial
 	igraphic IGraphic           // Graphic which contains this GraphicMaterial
 }
 }
 
 
-// Interface for all Graphics
+// IGraphic is the interface for all Graphic objects.
 type IGraphic interface {
 type IGraphic interface {
 	core.INode
 	core.INode
 	GetGraphic() *Graphic
 	GetGraphic() *Graphic
 	GetGeometry() *geometry.Geometry
 	GetGeometry() *geometry.Geometry
 	Renderable() bool
 	Renderable() bool
 	SetRenderable(bool)
 	SetRenderable(bool)
+	Cullable() bool
+	SetCullable(bool)
 	RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo)
 	RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo)
 }
 }
 
 
@@ -61,24 +68,25 @@ func (gr *Graphic) Init(igeom geometry.IGeometry, mode uint32) *Graphic {
 	gr.mode = mode
 	gr.mode = mode
 	gr.materials = make([]GraphicMaterial, 0)
 	gr.materials = make([]GraphicMaterial, 0)
 	gr.renderable = true
 	gr.renderable = true
+	gr.cullable = true
 	return gr
 	return gr
 }
 }
 
 
 // GetGraphic satisfies the IGraphic interface and
 // GetGraphic satisfies the IGraphic interface and
-// returns pointer to the base Graphic
+// returns pointer to the base Graphic.
 func (gr *Graphic) GetGraphic() *Graphic {
 func (gr *Graphic) GetGraphic() *Graphic {
 
 
 	return gr
 	return gr
 }
 }
 
 
 // GetGeometry satisfies the IGraphic interface and returns
 // GetGeometry satisfies the IGraphic interface and returns
-// a pointer to the geometry associated with this graphic
+// a pointer to the geometry associated with this graphic.
 func (gr *Graphic) GetGeometry() *geometry.Geometry {
 func (gr *Graphic) GetGeometry() *geometry.Geometry {
 
 
 	return gr.igeom.GetGeometry()
 	return gr.igeom.GetGeometry()
 }
 }
 
 
-// Dispose overrides the embedded Node Dispose method
+// Dispose overrides the embedded Node Dispose method.
 func (gr *Graphic) Dispose() {
 func (gr *Graphic) Dispose() {
 
 
 	gr.igeom.Dispose()
 	gr.igeom.Dispose()
@@ -88,20 +96,34 @@ func (gr *Graphic) Dispose() {
 }
 }
 
 
 // SetRenderable satisfies the IGraphic interface and
 // SetRenderable satisfies the IGraphic interface and
-// sets the renderable state of this Graphic (default = true)
+// sets the renderable state of this Graphic (default = true).
 func (gr *Graphic) SetRenderable(state bool) {
 func (gr *Graphic) SetRenderable(state bool) {
 
 
 	gr.renderable = state
 	gr.renderable = state
 }
 }
 
 
 // Renderable satisfies the IGraphic interface and
 // Renderable satisfies the IGraphic interface and
-// returns the renderable state of this graphic
+// returns the renderable state of this graphic.
 func (gr *Graphic) Renderable() bool {
 func (gr *Graphic) Renderable() bool {
 
 
 	return gr.renderable
 	return gr.renderable
 }
 }
 
 
-// Add material for the specified subset of vertices.
+// SetCullable satisfies the IGraphic interface and
+// sets the cullable state of this Graphic (default = true).
+func (gr *Graphic) SetCullable(state bool) {
+
+	gr.cullable = state
+}
+
+// Cullable satisfies the IGraphic interface and
+// returns the cullable state of this graphic.
+func (gr *Graphic) Cullable() bool {
+
+	return gr.cullable
+}
+
+// AddMaterial adds a material for the specified subset of vertices.
 // If the material applies to all vertices, start and count must be 0.
 // 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) {
 func (gr *Graphic) AddMaterial(igr IGraphic, imat material.IMaterial, start, count int) {
 
 
@@ -114,7 +136,7 @@ func (gr *Graphic) AddMaterial(igr IGraphic, imat material.IMaterial, start, cou
 	gr.materials = append(gr.materials, gmat)
 	gr.materials = append(gr.materials, gmat)
 }
 }
 
 
-// Add group material
+// AddGroupMaterial adds a material for the specified geometry group.
 func (gr *Graphic) AddGroupMaterial(igr IGraphic, imat material.IMaterial, gindex int) {
 func (gr *Graphic) AddGroupMaterial(igr IGraphic, imat material.IMaterial, gindex int) {
 
 
 	geom := gr.igeom.GetGeometry()
 	geom := gr.igeom.GetGeometry()
@@ -125,13 +147,13 @@ func (gr *Graphic) AddGroupMaterial(igr IGraphic, imat material.IMaterial, ginde
 	gr.AddMaterial(igr, imat, group.Start, group.Count)
 	gr.AddMaterial(igr, imat, group.Start, group.Count)
 }
 }
 
 
-// Materials returns slice with this graphic materials
+// Materials returns slice with this graphic materials.
 func (gr *Graphic) Materials() []GraphicMaterial {
 func (gr *Graphic) Materials() []GraphicMaterial {
 
 
 	return gr.materials
 	return gr.materials
 }
 }
 
 
-// GetMaterial returns the  material associated with the specified vertex position
+// GetMaterial returns the material associated with the specified vertex position.
 func (gr *Graphic) GetMaterial(vpos int) material.IMaterial {
 func (gr *Graphic) GetMaterial(vpos int) material.IMaterial {
 
 
 	for _, gmat := range gr.materials {
 	for _, gmat := range gr.materials {
@@ -146,12 +168,40 @@ func (gr *Graphic) GetMaterial(vpos int) material.IMaterial {
 	return nil
 	return nil
 }
 }
 
 
+// CalculateMatrices calculates the model view and model view projection matrices.
+func (g *Graphic) CalculateMatrices(gs *gls.GLS, rinfo *core.RenderInfo) {
+
+	// Calculate model view and model view projection matrices
+	mw := g.MatrixWorld()
+	g.mvm.MultiplyMatrices(&rinfo.ViewMatrix, &mw)
+	g.mvpm.MultiplyMatrices(&rinfo.ProjMatrix, &g.mvm)
+}
+
+// ModelViewMatrix returns the last cached model view matrix for this graphic.
+func (g *Graphic) ModelViewMatrix() *math32.Matrix4 {
+
+	return &g.mvm
+}
+
+// ModelViewProjectionMatrix returns the last cached model view projection matrix for this graphic.
+func (g *Graphic) ModelViewProjectionMatrix() *math32.Matrix4 {
+
+	return &g.mvpm
+}
+
+// GetMaterial returns the material associated with the GraphicMaterial.
 func (grmat *GraphicMaterial) GetMaterial() material.IMaterial {
 func (grmat *GraphicMaterial) GetMaterial() material.IMaterial {
 
 
 	return grmat.imat
 	return grmat.imat
 }
 }
 
 
-// Render is called by the renderer to render this graphic material
+// GetGraphic returns the graphic associated with the GraphicMaterial.
+func (grmat *GraphicMaterial) GetGraphic() IGraphic {
+
+	return grmat.igraphic
+}
+
+// Render is called by the renderer to render this graphic material.
 func (grmat *GraphicMaterial) Render(gs *gls.GLS, rinfo *core.RenderInfo) {
 func (grmat *GraphicMaterial) Render(gs *gls.GLS, rinfo *core.RenderInfo) {
 
 
 	// Setup the associated material (set states and transfer material uniforms and textures)
 	// Setup the associated material (set states and transfer material uniforms and textures)

+ 7 - 7
graphic/grid_helper.go

@@ -11,6 +11,7 @@ import (
 	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/math32"
 )
 )
 
 
+// GridHelper is the visual representation of a grid
 type GridHelper struct {
 type GridHelper struct {
 	Lines
 	Lines
 }
 }
@@ -21,14 +22,14 @@ func NewGridHelper(size, step float32, color *math32.Color) *GridHelper {
 
 
 	grid := new(GridHelper)
 	grid := new(GridHelper)
 
 
-	half_size := size / 2
+	half := size / 2
 	positions := math32.NewArrayF32(0, 0)
 	positions := math32.NewArrayF32(0, 0)
-	for i := -half_size; i <= half_size; i += step {
+	for i := -half; i <= half; i += step {
 		positions.Append(
 		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,
+			-half, 0, i, color.R, color.G, color.B,
+			half, 0, i, color.R, color.G, color.B,
+			i, 0, -half, color.R, color.G, color.B,
+			i, 0, half, color.R, color.G, color.B,
 		)
 		)
 	}
 	}
 
 
@@ -43,7 +44,6 @@ func NewGridHelper(size, step float32, color *math32.Color) *GridHelper {
 
 
 	// Creates material
 	// Creates material
 	mat := material.NewBasic()
 	mat := material.NewBasic()
-	mat.SetLineWidth(1.0)
 
 
 	// Initialize lines with the specified geometry and material
 	// Initialize lines with the specified geometry and material
 	grid.Lines.Init(geom, mat)
 	grid.Lines.Init(geom, mat)

+ 9 - 14
graphic/line_strip.go

@@ -9,41 +9,36 @@ import (
 	"github.com/g3n/engine/geometry"
 	"github.com/g3n/engine/geometry"
 	"github.com/g3n/engine/gls"
 	"github.com/g3n/engine/gls"
 	"github.com/g3n/engine/material"
 	"github.com/g3n/engine/material"
-	"github.com/g3n/engine/math32"
 )
 )
 
 
+// LineStrip is a Graphic which is rendered as a collection of connected lines.
 type LineStrip struct {
 type LineStrip struct {
 	Graphic             // Embedded graphic object
 	Graphic             // Embedded graphic object
-	uniMVP  gls.Uniform // Model view projection matrix uniform location cache
+	uniMVPm gls.Uniform // Model view projection matrix uniform location cache
 }
 }
 
 
 // NewLineStrip creates and returns a pointer to a new LineStrip graphic
 // NewLineStrip creates and returns a pointer to a new LineStrip graphic
-// with the specified geometry and material
+// with the specified geometry and material.
 func NewLineStrip(igeom geometry.IGeometry, imat material.IMaterial) *LineStrip {
 func NewLineStrip(igeom geometry.IGeometry, imat material.IMaterial) *LineStrip {
 
 
 	l := new(LineStrip)
 	l := new(LineStrip)
 	l.Graphic.Init(igeom, gls.LINE_STRIP)
 	l.Graphic.Init(igeom, gls.LINE_STRIP)
 	l.AddMaterial(l, imat, 0, 0)
 	l.AddMaterial(l, imat, 0, 0)
-	l.uniMVP.Init("MVP")
+	l.uniMVPm.Init("MVP")
 	return l
 	return l
 }
 }
 
 
-// RenderSetup is called by the engine before drawing this geometry
+// RenderSetup is called by the engine before drawing this geometry.
 func (l *LineStrip) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
 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)
-
-	// Transfer mvpm uniform
-	location := l.uniMVP.Location(gs)
+	// Transfer model view projection matrix uniform
+	mvpm := l.ModelViewProjectionMatrix()
+	location := l.uniMVPm.Location(gs)
 	gs.UniformMatrix4fv(location, 1, false, &mvpm[0])
 	gs.UniformMatrix4fv(location, 1, false, &mvpm[0])
 }
 }
 
 
 // Raycast satisfies the INode interface and checks the intersections
 // Raycast satisfies the INode interface and checks the intersections
-// of this geometry with the specified raycaster
+// of this geometry with the specified raycaster.
 func (l *LineStrip) Raycast(rc *core.Raycaster, intersects *[]core.Intersect) {
 func (l *LineStrip) Raycast(rc *core.Raycaster, intersects *[]core.Intersect) {
 
 
 	lineRaycast(l, rc, intersects, 1)
 	lineRaycast(l, rc, intersects, 1)

+ 11 - 14
graphic/lines.go

@@ -12,19 +12,21 @@ import (
 	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/math32"
 )
 )
 
 
-// Lines is a Graphic which is rendered as a collection of independent lines
+// Lines is a Graphic which is rendered as a collection of independent lines.
 type Lines struct {
 type Lines struct {
 	Graphic             // Embedded graphic object
 	Graphic             // Embedded graphic object
-	uniMVP  gls.Uniform // Model view projection matrix uniform location cache
+	uniMVPm gls.Uniform // Model view projection matrix uniform location cache
 }
 }
 
 
+// Init initializes the Lines object and adds the specified material.
 func (l *Lines) Init(igeom geometry.IGeometry, imat material.IMaterial) {
 func (l *Lines) Init(igeom geometry.IGeometry, imat material.IMaterial) {
 
 
 	l.Graphic.Init(igeom, gls.LINES)
 	l.Graphic.Init(igeom, gls.LINES)
 	l.AddMaterial(l, imat, 0, 0)
 	l.AddMaterial(l, imat, 0, 0)
-	l.uniMVP.Init("MVP")
+	l.uniMVPm.Init("MVP")
 }
 }
 
 
+// NewLines returns a pointer to a new Lines object.
 func NewLines(igeom geometry.IGeometry, imat material.IMaterial) *Lines {
 func NewLines(igeom geometry.IGeometry, imat material.IMaterial) *Lines {
 
 
 	l := new(Lines)
 	l := new(Lines)
@@ -32,28 +34,23 @@ func NewLines(igeom geometry.IGeometry, imat material.IMaterial) *Lines {
 	return l
 	return l
 }
 }
 
 
-// RenderSetup is called by the engine before drawing this geometry
+// RenderSetup is called by the engine before drawing this geometry.
 func (l *Lines) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
 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)
-
-	// Transfer mvpm uniform
-	location := l.uniMVP.Location(gs)
+	// Transfer model view projection matrix uniform
+	mvpm := l.ModelViewProjectionMatrix()
+	location := l.uniMVPm.Location(gs)
 	gs.UniformMatrix4fv(location, 1, false, &mvpm[0])
 	gs.UniformMatrix4fv(location, 1, false, &mvpm[0])
 }
 }
 
 
 // Raycast satisfies the INode interface and checks the intersections
 // Raycast satisfies the INode interface and checks the intersections
-// of this geometry with the specified raycaster
+// of this geometry with the specified raycaster.
 func (l *Lines) Raycast(rc *core.Raycaster, intersects *[]core.Intersect) {
 func (l *Lines) Raycast(rc *core.Raycaster, intersects *[]core.Intersect) {
 
 
 	lineRaycast(l, rc, intersects, 2)
 	lineRaycast(l, rc, intersects, 2)
 }
 }
 
 
-// Internal function used by raycasting for Lines and LineStrip
+// Internal function used by raycasting for Lines and LineStrip.
 func lineRaycast(igr IGraphic, rc *core.Raycaster, intersects *[]core.Intersect, step int) {
 func lineRaycast(igr IGraphic, rc *core.Raycaster, intersects *[]core.Intersect, step int) {
 
 
 	// Get the bounding sphere
 	// Get the bounding sphere

+ 25 - 25
graphic/mesh.go

@@ -12,16 +12,17 @@ import (
 	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/math32"
 )
 )
 
 
+// Mesh is a Graphic with uniforms for the model, view, projection, and normal matrices.
 type Mesh struct {
 type Mesh struct {
 	Graphic             // Embedded graphic
 	Graphic             // Embedded graphic
-	uniMVM  gls.Uniform // Model view matrix uniform location cache
-	uniMVPM gls.Uniform // Model view projection matrix uniform cache
-	uniNM   gls.Uniform // Normal matrix uniform cache
+	uniMVm  gls.Uniform // Model view matrix uniform location cache
+	uniMVPm gls.Uniform // Model view projection matrix uniform cache
+	uniNm   gls.Uniform // Normal matrix uniform cache
 }
 }
 
 
-// NewMesh creates and returns a pointer to a mesh with the specified geometry and material
+// 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
 // If the mesh has multi materials, the material specified here must be nil and the
-// individual materials must be add using "AddMateria" or AddGroupMaterial"
+// individual materials must be add using "AddMaterial" or AddGroupMaterial".
 func NewMesh(igeom geometry.IGeometry, imat material.IMaterial) *Mesh {
 func NewMesh(igeom geometry.IGeometry, imat material.IMaterial) *Mesh {
 
 
 	m := new(Mesh)
 	m := new(Mesh)
@@ -29,14 +30,15 @@ func NewMesh(igeom geometry.IGeometry, imat material.IMaterial) *Mesh {
 	return m
 	return m
 }
 }
 
 
+// Init initializes the Mesh and its uniforms.
 func (m *Mesh) Init(igeom geometry.IGeometry, imat material.IMaterial) {
 func (m *Mesh) Init(igeom geometry.IGeometry, imat material.IMaterial) {
 
 
 	m.Graphic.Init(igeom, gls.TRIANGLES)
 	m.Graphic.Init(igeom, gls.TRIANGLES)
 
 
 	// Initialize uniforms
 	// Initialize uniforms
-	m.uniMVM.Init("ModelViewMatrix")
-	m.uniMVPM.Init("MVP")
-	m.uniNM.Init("NormalMatrix")
+	m.uniMVm.Init("ModelViewMatrix")
+	m.uniMVPm.Init("MVP")
+	m.uniNm.Init("NormalMatrix")
 
 
 	// Adds single material if not nil
 	// Adds single material if not nil
 	if imat != nil {
 	if imat != nil {
@@ -44,12 +46,13 @@ func (m *Mesh) Init(igeom geometry.IGeometry, imat material.IMaterial) {
 	}
 	}
 }
 }
 
 
+// AddMaterial adds a material for the specified subset of vertices.
 func (m *Mesh) AddMaterial(imat material.IMaterial, start, count int) {
 func (m *Mesh) AddMaterial(imat material.IMaterial, start, count int) {
 
 
 	m.Graphic.AddMaterial(m, imat, start, count)
 	m.Graphic.AddMaterial(m, imat, start, count)
 }
 }
 
 
-// Add group material
+// AddGroupMaterial adds a material for the specified geometry group.
 func (m *Mesh) AddGroupMaterial(imat material.IMaterial, gindex int) {
 func (m *Mesh) AddGroupMaterial(imat material.IMaterial, gindex int) {
 
 
 	m.Graphic.AddGroupMaterial(m, imat, gindex)
 	m.Graphic.AddGroupMaterial(m, imat, gindex)
@@ -60,23 +63,20 @@ func (m *Mesh) AddGroupMaterial(imat material.IMaterial, gindex int) {
 // the model matrices.
 // the model matrices.
 func (m *Mesh) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
 func (m *Mesh) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
 
 
-	// Calculates model view matrix and transfer uniform
-	mw := m.MatrixWorld()
-	var mvm math32.Matrix4
-	mvm.MultiplyMatrices(&rinfo.ViewMatrix, &mw)
-	location := m.uniMVM.Location(gs)
+	// Transfer uniform for model view matrix
+	mvm := m.ModelViewMatrix()
+	location := m.uniMVm.Location(gs)
 	gs.UniformMatrix4fv(location, 1, false, &mvm[0])
 	gs.UniformMatrix4fv(location, 1, false, &mvm[0])
 
 
-	// Calculates model view projection matrix and updates uniform
-	var mvpm math32.Matrix4
-	mvpm.MultiplyMatrices(&rinfo.ProjMatrix, &mvm)
-	location = m.uniMVPM.Location(gs)
+	// Transfer uniform for model view projection matrix
+	mvpm := m.ModelViewProjectionMatrix()
+	location = m.uniMVPm.Location(gs)
 	gs.UniformMatrix4fv(location, 1, false, &mvpm[0])
 	gs.UniformMatrix4fv(location, 1, false, &mvpm[0])
 
 
-	// Calculates normal matrix and updates uniform
+	// Calculates normal matrix and transfer uniform
 	var nm math32.Matrix3
 	var nm math32.Matrix3
-	nm.GetNormalMatrix(&mvm)
-	location = m.uniNM.Location(gs)
+	nm.GetNormalMatrix(mvm)
+	location = m.uniNm.Location(gs)
 	gs.UniformMatrix3fv(location, 1, false, &nm[0])
 	gs.UniformMatrix3fv(location, 1, false, &nm[0])
 }
 }
 
 
@@ -152,9 +152,7 @@ func (m *Mesh) Raycast(rc *core.Raycaster, intersects *[]core.Intersect) {
 	positions := vboPos.Buffer()
 	positions := vboPos.Buffer()
 	indices := geom.Indices()
 	indices := geom.Indices()
 
 
-	var vA math32.Vector3
-	var vB math32.Vector3
-	var vC math32.Vector3
+	var vA, vB, vC math32.Vector3
 
 
 	// Geometry has indexed vertices
 	// Geometry has indexed vertices
 	if indices.Size() > 0 {
 	if indices.Size() > 0 {
@@ -178,7 +176,9 @@ func (m *Mesh) Raycast(rc *core.Raycaster, intersects *[]core.Intersect) {
 		}
 		}
 		// Geometry has NO indexed vertices
 		// Geometry has NO indexed vertices
 	} else {
 	} else {
-		for i := 0; i < positions.Size(); i += 9 {
+		stride := vboPos.Stride()
+		offset := vboPos.AttribOffset("VertexPosition")
+		for i := offset; i < positions.Size(); i += stride {
 			// Get face indices
 			// Get face indices
 			a := i / 3
 			a := i / 3
 			b := a + 1
 			b := a + 1

+ 22 - 21
graphic/normals_helper.go

@@ -12,29 +12,30 @@ import (
 	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/math32"
 )
 )
 
 
+// NormalsHelper is the visual representation of the normals of a target object.
 type NormalsHelper struct {
 type NormalsHelper struct {
 	Lines
 	Lines
-	size   float32
-	target *core.Node
-	tgeom  *geometry.Geometry
+	size           float32
+	targetNode     *core.Node
+	targetGeometry *geometry.Geometry
 }
 }
 
 
 // NewNormalsHelper creates, initializes and returns a pointer to Normals helper object.
 // NewNormalsHelper creates, initializes and returns a pointer to Normals helper object.
-// This helper shows the normal vectors of the specified object.
+// This helper shows the surface normals of the specified object.
 func NewNormalsHelper(ig IGraphic, size float32, color *math32.Color, lineWidth float32) *NormalsHelper {
 func NewNormalsHelper(ig IGraphic, size float32, color *math32.Color, lineWidth float32) *NormalsHelper {
 
 
 	// Creates new Normals helper
 	// Creates new Normals helper
 	nh := new(NormalsHelper)
 	nh := new(NormalsHelper)
 	nh.size = size
 	nh.size = size
 
 
-	// Saves the object to show the normals
-	nh.target = ig.GetNode()
+	// Save the object to show the normals
+	nh.targetNode = ig.GetNode()
 
 
 	// Get the geometry of the target object
 	// Get the geometry of the target object
-	nh.tgeom = ig.GetGeometry()
+	nh.targetGeometry = ig.GetGeometry()
 
 
 	// Get the number of target vertex positions
 	// Get the number of target vertex positions
-	vertices := nh.tgeom.VBO("VertexPosition")
+	vertices := nh.targetGeometry.VBO("VertexPosition")
 	n := vertices.Buffer().Size() * 2
 	n := vertices.Buffer().Size() * 2
 
 
 	// Creates this helper geometry
 	// Creates this helper geometry
@@ -53,8 +54,8 @@ func NewNormalsHelper(ig IGraphic, size float32, color *math32.Color, lineWidth
 	return nh
 	return nh
 }
 }
 
 
-// Update should be called in the render loop to update the normals from the
-// target object
+// Update should be called in the render loop to
+// update the normals based on the target object.
 func (nh *NormalsHelper) Update() {
 func (nh *NormalsHelper) Update() {
 
 
 	var v1 math32.Vector3
 	var v1 math32.Vector3
@@ -62,29 +63,29 @@ func (nh *NormalsHelper) Update() {
 	var normalMatrix math32.Matrix3
 	var normalMatrix math32.Matrix3
 
 
 	// Updates the target object matrix and get its normal matrix
 	// Updates the target object matrix and get its normal matrix
-	matrixWorld := nh.target.MatrixWorld()
+	matrixWorld := nh.targetNode.MatrixWorld()
 	normalMatrix.GetNormalMatrix(&matrixWorld)
 	normalMatrix.GetNormalMatrix(&matrixWorld)
 
 
 	// Get the target positions and normals buffers
 	// Get the target positions and normals buffers
-	tposvbo := nh.tgeom.VBO("VertexPosition")
-	tpositions := tposvbo.Buffer()
-	tnormvbo := nh.tgeom.VBO("VertexNormal")
-	tnormals := tnormvbo.Buffer()
+	tPosVBO := nh.targetGeometry.VBO("VertexPosition")
+	tPositions := tPosVBO.Buffer()
+	tNormVBO := nh.targetGeometry.VBO("VertexNormal")
+	tNormals := tNormVBO.Buffer()
 
 
 	// Get this object positions buffer
 	// Get this object positions buffer
 	geom := nh.GetGeometry()
 	geom := nh.GetGeometry()
-	posvbo := geom.VBO("VertexPosition")
-	positions := posvbo.Buffer()
+	posVBO := geom.VBO("VertexPosition")
+	positions := posVBO.Buffer()
 
 
 	// For each target object vertex position:
 	// For each target object vertex position:
-	for pos := 0; pos < tpositions.Size(); pos += 3 {
+	for pos := 0; pos < tPositions.Size(); pos += 3 {
 		// Get the target vertex position and apply the current world matrix transform
 		// Get the target vertex position and apply the current world matrix transform
 		// to get the base for this normal line segment.
 		// to get the base for this normal line segment.
-		tpositions.GetVector3(pos, &v1)
+		tPositions.GetVector3(pos, &v1)
 		v1.ApplyMatrix4(&matrixWorld)
 		v1.ApplyMatrix4(&matrixWorld)
 
 
 		// Calculates the end position of the normal line segment
 		// Calculates the end position of the normal line segment
-		tnormals.GetVector3(pos, &v2)
+		tNormals.GetVector3(pos, &v2)
 		v2.ApplyMatrix3(&normalMatrix).Normalize().MultiplyScalar(nh.size).Add(&v1)
 		v2.ApplyMatrix3(&normalMatrix).Normalize().MultiplyScalar(nh.size).Add(&v1)
 
 
 		// Sets the line segment representing the normal of the current target position
 		// Sets the line segment representing the normal of the current target position
@@ -92,5 +93,5 @@ func (nh *NormalsHelper) Update() {
 		positions.SetVector3(2*pos, &v1)
 		positions.SetVector3(2*pos, &v1)
 		positions.SetVector3(2*pos+3, &v2)
 		positions.SetVector3(2*pos+3, &v2)
 	}
 	}
-	posvbo.Update()
+	posVBO.Update()
 }
 }

+ 14 - 16
graphic/points.go

@@ -12,9 +12,10 @@ import (
 	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/math32"
 )
 )
 
 
+// Points represents a geometry containing only points
 type Points struct {
 type Points struct {
 	Graphic             // Embedded graphic
 	Graphic             // Embedded graphic
-	uniMVPM gls.Uniform // Model view projection matrix uniform location cache
+	uniMVPm gls.Uniform // Model view projection matrix uniform location cache
 }
 }
 
 
 // NewPoints creates and returns a graphic points object with the specified
 // NewPoints creates and returns a graphic points object with the specified
@@ -26,26 +27,21 @@ func NewPoints(igeom geometry.IGeometry, imat material.IMaterial) *Points {
 	if imat != nil {
 	if imat != nil {
 		p.AddMaterial(p, imat, 0, 0)
 		p.AddMaterial(p, imat, 0, 0)
 	}
 	}
-	p.uniMVPM.Init("MVP")
+	p.uniMVPm.Init("MVP")
 	return p
 	return p
 }
 }
 
 
-// RenderSetup is called by the engine before rendering this graphic
+// RenderSetup is called by the engine before rendering this graphic.
 func (p *Points) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
 func (p *Points) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
 
 
-	// Calculates model view projection matrix
-	mw := p.MatrixWorld()
-	var mvpm math32.Matrix4
-	mvpm.MultiplyMatrices(&rinfo.ViewMatrix, &mw)
-	mvpm.MultiplyMatrices(&rinfo.ProjMatrix, &mvpm)
-
 	// Transfer model view projection matrix uniform
 	// Transfer model view projection matrix uniform
-	location := p.uniMVPM.Location(gs)
+	mvpm := p.ModelViewProjectionMatrix()
+	location := p.uniMVPm.Location(gs)
 	gs.UniformMatrix4fv(location, 1, false, &mvpm[0])
 	gs.UniformMatrix4fv(location, 1, false, &mvpm[0])
 }
 }
 
 
 // Raycast satisfies the INode interface and checks the intersections
 // Raycast satisfies the INode interface and checks the intersections
-// of this geometry with the specified raycaster
+// of this geometry with the specified raycaster.
 func (p *Points) Raycast(rc *core.Raycaster, intersects *[]core.Intersect) {
 func (p *Points) Raycast(rc *core.Raycaster, intersects *[]core.Intersect) {
 
 
 	// Checks intersection with the bounding sphere transformed to world coordinates
 	// Checks intersection with the bounding sphere transformed to world coordinates
@@ -95,11 +91,11 @@ func (p *Points) Raycast(rc *core.Raycaster, intersects *[]core.Intersect) {
 	}
 	}
 
 
 	// Get buffer with position vertices
 	// Get buffer with position vertices
-	vbPos := geom.VBO("VertexPosition")
-	if vbPos == nil {
+	vboPos := geom.VBO("VertexPosition")
+	if vboPos == nil {
 		panic("points.Raycast(): VertexPosition VBO not found")
 		panic("points.Raycast(): VertexPosition VBO not found")
 	}
 	}
-	positions := vbPos.Buffer()
+	positions := vboPos.Buffer()
 
 
 	var point math32.Vector3
 	var point math32.Vector3
 	indices := geom.Indices()
 	indices := geom.Indices()
@@ -111,8 +107,10 @@ func (p *Points) Raycast(rc *core.Raycaster, intersects *[]core.Intersect) {
 			testPoint(&point, i)
 			testPoint(&point, i)
 		}
 		}
 	} else {
 	} else {
-		for i := 0; i < positions.Size()/3; i++ {
-			positions.GetVector3(i*3, &point)
+		stride := vboPos.Stride()
+		offset := vboPos.AttribOffset("VertexPosition")
+		for i := offset; i < positions.Size(); i += stride {
+			positions.GetVector3(i, &point)
 			testPoint(&point, i)
 			testPoint(&point, i)
 		}
 		}
 	}
 	}

+ 24 - 20
graphic/skybox.go

@@ -13,26 +13,29 @@ import (
 	"github.com/g3n/engine/texture"
 	"github.com/g3n/engine/texture"
 )
 )
 
 
+// Skybox is the Graphic that represents a skybox.
+type Skybox struct {
+	Graphic             // embedded graphic object
+	uniMVm  gls.Uniform // model view matrix uniform location cache
+	uniMVPm gls.Uniform // model view projection matrix uniform cache
+	uniNm   gls.Uniform // normal matrix uniform cache
+}
+
+// SkyboxData contains the data necessary to locate the textures for a Skybox in a concise manner.
 type SkyboxData struct {
 type SkyboxData struct {
 	DirAndPrefix string
 	DirAndPrefix string
 	Extension    string
 	Extension    string
 	Suffixes     [6]string
 	Suffixes     [6]string
 }
 }
 
 
-type Skybox struct {
-	Graphic             // embedded graphic object
-	uniMVM  gls.Uniform // model view matrix uniform location cache
-	uniMVPM gls.Uniform // model view projection matrix uniform cache
-	uniNM   gls.Uniform // normal matrix uniform cache
-}
-
-// NewSkybox creates and returns a pointer to a skybox with the specified textures
+// NewSkybox creates and returns a pointer to a Skybox with the specified textures.
 func NewSkybox(data SkyboxData) (*Skybox, error) {
 func NewSkybox(data SkyboxData) (*Skybox, error) {
 
 
 	skybox := new(Skybox)
 	skybox := new(Skybox)
 
 
-	geom := geometry.NewBox(50, 50, 50, 1, 1, 1)
+	geom := geometry.NewCube(1)
 	skybox.Graphic.Init(geom, gls.TRIANGLES)
 	skybox.Graphic.Init(geom, gls.TRIANGLES)
+	skybox.Graphic.SetCullable(false)
 
 
 	for i := 0; i < 6; i++ {
 	for i := 0; i < 6; i++ {
 		tex, err := texture.NewTexture2DFromImage(data.DirAndPrefix + data.Suffixes[i] + "." + data.Extension)
 		tex, err := texture.NewTexture2DFromImage(data.DirAndPrefix + data.Suffixes[i] + "." + data.Extension)
@@ -43,13 +46,19 @@ func NewSkybox(data SkyboxData) (*Skybox, error) {
 		matFace.AddTexture(tex)
 		matFace.AddTexture(tex)
 		matFace.SetSide(material.SideBack)
 		matFace.SetSide(material.SideBack)
 		matFace.SetUseLights(material.UseLightAmbient)
 		matFace.SetUseLights(material.UseLightAmbient)
+
+		// 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.
+		// It doesn't matter how small/big the skybox is as long as it's visible by the camera (within near/far planes).
+		matFace.SetDepthMask(false)
+
 		skybox.AddGroupMaterial(skybox, matFace, i)
 		skybox.AddGroupMaterial(skybox, matFace, i)
 	}
 	}
 
 
 	// Creates uniforms
 	// Creates uniforms
-	skybox.uniMVM.Init("ModelViewMatrix")
-	skybox.uniMVPM.Init("MVP")
-	skybox.uniNM.Init("NormalMatrix")
+	skybox.uniMVm.Init("ModelViewMatrix")
+	skybox.uniMVPm.Init("MVP")
+	skybox.uniNm.Init("NormalMatrix")
 
 
 	return skybox, nil
 	return skybox, nil
 }
 }
@@ -59,11 +68,6 @@ func NewSkybox(data SkyboxData) (*Skybox, error) {
 // the model matrices.
 // the model matrices.
 func (skybox *Skybox) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
 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
 	var mvm math32.Matrix4
 	mvm.Copy(&rinfo.ViewMatrix)
 	mvm.Copy(&rinfo.ViewMatrix)
 
 
@@ -74,18 +78,18 @@ func (skybox *Skybox) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
 	// mvm.ExtractRotation(&rinfo.ViewMatrix) // TODO <- ExtractRotation does not work as expected?
 	// mvm.ExtractRotation(&rinfo.ViewMatrix) // TODO <- ExtractRotation does not work as expected?
 
 
 	// Transfer mvp uniform
 	// Transfer mvp uniform
-	location := skybox.uniMVM.Location(gs)
+	location := skybox.uniMVm.Location(gs)
 	gs.UniformMatrix4fv(location, 1, false, &mvm[0])
 	gs.UniformMatrix4fv(location, 1, false, &mvm[0])
 
 
 	// Calculates model view projection matrix and updates uniform
 	// Calculates model view projection matrix and updates uniform
 	var mvpm math32.Matrix4
 	var mvpm math32.Matrix4
 	mvpm.MultiplyMatrices(&rinfo.ProjMatrix, &mvm)
 	mvpm.MultiplyMatrices(&rinfo.ProjMatrix, &mvm)
-	location = skybox.uniMVPM.Location(gs)
+	location = skybox.uniMVPm.Location(gs)
 	gs.UniformMatrix4fv(location, 1, false, &mvpm[0])
 	gs.UniformMatrix4fv(location, 1, false, &mvpm[0])
 
 
 	// Calculates normal matrix and updates uniform
 	// Calculates normal matrix and updates uniform
 	var nm math32.Matrix3
 	var nm math32.Matrix3
 	nm.GetNormalMatrix(&mvm)
 	nm.GetNormalMatrix(&mvm)
-	location = skybox.uniNM.Location(gs)
+	location = skybox.uniNm.Location(gs)
 	gs.UniformMatrix3fv(location, 1, false, &nm[0])
 	gs.UniformMatrix3fv(location, 1, false, &nm[0])
 }
 }

+ 5 - 3
graphic/sprite.go

@@ -12,6 +12,7 @@ import (
 	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/math32"
 )
 )
 
 
+// Sprite is a potentially animated image positioned in space that always faces the camera.
 type Sprite struct {
 type Sprite struct {
 	Graphic             // Embedded graphic
 	Graphic             // Embedded graphic
 	uniMVPM gls.Uniform // Model view projection matrix uniform location cache
 	uniMVPM gls.Uniform // Model view projection matrix uniform location cache
@@ -55,6 +56,7 @@ func NewSprite(width, height float32, imat material.IMaterial) *Sprite {
 	return s
 	return s
 }
 }
 
 
+// RenderSetup sets up the rendering of the sprite.
 func (s *Sprite) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
 func (s *Sprite) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
 
 
 	// Calculates model view matrix
 	// Calculates model view matrix
@@ -73,12 +75,12 @@ func (s *Sprite) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
 	rotation.X = 0
 	rotation.X = 0
 	rotation.Y = 0
 	rotation.Y = 0
 	quaternion.SetFromEuler(&rotation)
 	quaternion.SetFromEuler(&rotation)
-	var mvm_new math32.Matrix4
-	mvm_new.Compose(&position, &quaternion, &scale)
+	var mvmNew math32.Matrix4
+	mvmNew.Compose(&position, &quaternion, &scale)
 
 
 	// Calculates final MVP and updates uniform
 	// Calculates final MVP and updates uniform
 	var mvpm math32.Matrix4
 	var mvpm math32.Matrix4
-	mvpm.MultiplyMatrices(&rinfo.ProjMatrix, &mvm_new)
+	mvpm.MultiplyMatrices(&rinfo.ProjMatrix, &mvmNew)
 	location := s.uniMVPM.Location(gs)
 	location := s.uniMVPM.Location(gs)
 	gs.UniformMatrix4fv(location, 1, false, &mvpm[0])
 	gs.UniformMatrix4fv(location, 1, false, &mvpm[0])
 }
 }

+ 1 - 1
gui/align.go

@@ -8,7 +8,7 @@ package gui
 type Align int
 type Align int
 
 
 const (
 const (
-	AlignNone   = Align(iota) // No aligh
+	AlignNone   = Align(iota) // No alignment
 	AlignLeft                 // Align horizontally at left
 	AlignLeft                 // Align horizontally at left
 	AlignRight                // Align horizontally at right
 	AlignRight                // Align horizontally at right
 	AlignWidth                // Align horizontally using all width
 	AlignWidth                // Align horizontally using all width

+ 4 - 4
gui/assets/icon/icodes.go

@@ -1,8 +1,8 @@
-//
-// This file was generated from the original 'codepoints' file
+// File generated by g3nicodes. Do not edit.
+// This file is based on the original 'codepoints' file
 // from the material design icon fonts:
 // from the material design icon fonts:
 // https://github.com/google/material-design-icons
 // https://github.com/google/material-design-icons
-//
+
 package icon
 package icon
 
 
 const (
 const (
@@ -940,7 +940,7 @@ const (
 	ZoomOutMap                            = string(0xe56b)
 	ZoomOutMap                            = string(0xe56b)
 )
 )
 
 
-// IconCodepoint returns the codepoint for the specified icon name.
+// Codepoint returns the codepoint for the specified icon name.
 // Returns 0 if the name not found
 // Returns 0 if the name not found
 func Codepoint(name string) string {
 func Codepoint(name string) string {
 
 

+ 11 - 16
gui/builder.go

@@ -39,7 +39,7 @@ type BuilderFunc func(*Builder, map[string]interface{}) (IPanel, error)
 // AttribCheckFunc is the type for all attribute check functions
 // AttribCheckFunc is the type for all attribute check functions
 type AttribCheckFunc func(b *Builder, am map[string]interface{}, fname string) error
 type AttribCheckFunc func(b *Builder, am map[string]interface{}, fname string) error
 
 
-// IgnoreSuffix specified the suffix of ignored keys
+// IgnoreSuffix specifies the suffix of ignored keys
 const IgnoreSuffix = "_"
 const IgnoreSuffix = "_"
 
 
 // Panel and layout types
 // Panel and layout types
@@ -81,7 +81,7 @@ const (
 	AttribAspectHeight   = "aspectheight"  // float32
 	AttribAspectHeight   = "aspectheight"  // float32
 	AttribAspectWidth    = "aspectwidth"   // float32
 	AttribAspectWidth    = "aspectwidth"   // float32
 	AttribBgColor        = "bgcolor"       // Color4
 	AttribBgColor        = "bgcolor"       // Color4
-	AttribBorders        = "borders"       // BorderSizes
+	AttribBorders        = "borders"       // RectBounds
 	AttribBorderColor    = "bordercolor"   // Color4
 	AttribBorderColor    = "bordercolor"   // Color4
 	AttribChecked        = "checked"       // bool
 	AttribChecked        = "checked"       // bool
 	AttribColor          = "color"         // Color4
 	AttribColor          = "color"         // Color4
@@ -114,12 +114,12 @@ const (
 	AttribLineSpacing    = "linespacing"   // float32
 	AttribLineSpacing    = "linespacing"   // float32
 	AttribLines          = "lines"         // int
 	AttribLines          = "lines"         // int
 	AttribMargin         = "margin"        // float32
 	AttribMargin         = "margin"        // float32
-	AttribMargins        = "margins"       // BorderSizes
+	AttribMargins        = "margins"       // RectBounds
 	AttribMinwidth       = "minwidth"      // float32 Table
 	AttribMinwidth       = "minwidth"      // float32 Table
 	AttribAutoHeight     = "autoheight"    // bool
 	AttribAutoHeight     = "autoheight"    // bool
 	AttribAutoWidth      = "autowidth"     // bool
 	AttribAutoWidth      = "autowidth"     // bool
 	AttribName           = "name"          // string
 	AttribName           = "name"          // string
-	AttribPaddings       = "paddings"      // BorderSizes
+	AttribPaddings       = "paddings"      // RectBounds
 	AttribPanel0         = "panel0"        // map[string]interface{}
 	AttribPanel0         = "panel0"        // map[string]interface{}
 	AttribPanel1         = "panel1"        // map[string]interface{}
 	AttribPanel1         = "panel1"        // map[string]interface{}
 	AttribParentInternal = "parent_"       // string (internal attribute)
 	AttribParentInternal = "parent_"       // string (internal attribute)
@@ -387,11 +387,8 @@ func (b *Builder) ParseString(desc string) error {
 				}
 				}
 			}
 			}
 			return ms, nil
 			return ms, nil
-
-		default:
-			return v, nil
 		}
 		}
-		return nil, nil
+		return v, nil
 	}
 	}
 
 
 	// Get map[string]interface{} with lower case keys from parsed descritor
 	// Get map[string]interface{} with lower case keys from parsed descritor
@@ -548,12 +545,12 @@ func (b *Builder) SetAttribs(am map[string]interface{}, ipan IPanel) error {
 
 
 	// Set optional margin sizes
 	// Set optional margin sizes
 	if am[AttribMargins] != nil {
 	if am[AttribMargins] != nil {
-		panel.SetMarginsFrom(am[AttribMargins].(*BorderSizes))
+		panel.SetMarginsFrom(am[AttribMargins].(*RectBounds))
 	}
 	}
 
 
 	// Set optional border sizes
 	// Set optional border sizes
 	if am[AttribBorders] != nil {
 	if am[AttribBorders] != nil {
-		panel.SetBordersFrom(am[AttribBorders].(*BorderSizes))
+		panel.SetBordersFrom(am[AttribBorders].(*RectBounds))
 	}
 	}
 
 
 	// Set optional border color
 	// Set optional border color
@@ -563,7 +560,7 @@ func (b *Builder) SetAttribs(am map[string]interface{}, ipan IPanel) error {
 
 
 	// Set optional paddings sizes
 	// Set optional paddings sizes
 	if am[AttribPaddings] != nil {
 	if am[AttribPaddings] != nil {
-		panel.SetPaddingsFrom(am[AttribPaddings].(*BorderSizes))
+		panel.SetPaddingsFrom(am[AttribPaddings].(*RectBounds))
 	}
 	}
 
 
 	// Set optional panel color
 	// Set optional panel color
@@ -968,10 +965,10 @@ func AttribCheckBorderSizes(b *Builder, am map[string]interface{}, fname string)
 		return nil
 		return nil
 	}
 	}
 	if len(va) == 1 {
 	if len(va) == 1 {
-		am[fname] = &BorderSizes{va[0], va[0], va[0], va[0]}
+		am[fname] = &RectBounds{va[0], va[0], va[0], va[0]}
 		return nil
 		return nil
 	}
 	}
-	am[fname] = &BorderSizes{va[0], va[1], va[2], va[3]}
+	am[fname] = &RectBounds{va[0], va[1], va[2], va[3]}
 	return nil
 	return nil
 }
 }
 
 
@@ -1017,10 +1014,8 @@ func AttribCheckFloat(b *Builder, am map[string]interface{}, fname string) error
 	case float64:
 	case float64:
 		am[fname] = float32(n)
 		am[fname] = float32(n)
 		return nil
 		return nil
-	default:
-		return b.err(am, fname, fmt.Sprintf("Not a number:%T", v))
 	}
 	}
-	return nil
+	return b.err(am, fname, fmt.Sprintf("Not a number:%T", v))
 }
 }
 
 
 // AttribCheckInt checks and convert attribute to int
 // AttribCheckInt checks and convert attribute to int

+ 1 - 1
gui/builder_panel.go

@@ -89,7 +89,7 @@ func buildLabel(b *Builder, am map[string]interface{}) (IPanel, error) {
 
 
 	var label *Label
 	var label *Label
 	if am[AttribIcon] != nil {
 	if am[AttribIcon] != nil {
-		label = NewLabel(am[AttribIcon].(string), true)
+		label = NewIcon(am[AttribIcon].(string))
 	} else if am[AttribText] != nil {
 	} else if am[AttribText] != nil {
 		label = NewLabel(am[AttribText].(string))
 		label = NewLabel(am[AttribText].(string))
 	} else {
 	} else {

+ 15 - 24
gui/button.go

@@ -5,7 +5,6 @@
 package gui
 package gui
 
 
 import (
 import (
-	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/window"
 	"github.com/g3n/engine/window"
 )
 )
 
 
@@ -22,6 +21,7 @@ import (
 
 
 ****************************************/
 ****************************************/
 
 
+// Button represents a button GUI element
 type Button struct {
 type Button struct {
 	*Panel                  // Embedded Panel
 	*Panel                  // Embedded Panel
 	Label     *Label        // Label panel
 	Label     *Label        // Label panel
@@ -33,13 +33,7 @@ type Button struct {
 }
 }
 
 
 // Button style
 // Button style
-type ButtonStyle struct {
-	Border      BorderSizes
-	Paddings    BorderSizes
-	BorderColor math32.Color4
-	BgColor     math32.Color
-	FgColor     math32.Color
-}
+type ButtonStyle BasicStyle
 
 
 // All Button styles
 // All Button styles
 type ButtonStyles struct {
 type ButtonStyles struct {
@@ -61,15 +55,15 @@ func NewButton(text string) *Button {
 	b.Panel = NewPanel(0, 0)
 	b.Panel = NewPanel(0, 0)
 
 
 	// Subscribe to panel events
 	// 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(OnCursor, b.onCursor)
-	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() })
+	b.Subscribe(OnKeyDown, b.onKey)
+	b.Subscribe(OnKeyUp, b.onKey)
+	b.Subscribe(OnMouseUp, b.onMouse)
+	b.Subscribe(OnMouseDown, b.onMouse)
+	b.Subscribe(OnCursor, b.onCursor)
+	b.Subscribe(OnCursorEnter, b.onCursor)
+	b.Subscribe(OnCursorLeave, b.onCursor)
+	b.Subscribe(OnEnable, func(name string, ev interface{}) { b.update() })
+	b.Subscribe(OnResize, func(name string, ev interface{}) { b.recalc() })
 
 
 	// Creates label
 	// Creates label
 	b.Label = NewLabel(text)
 	b.Label = NewLabel(text)
@@ -85,7 +79,7 @@ func NewButton(text string) *Button {
 // If there is currently a selected image, it is removed
 // If there is currently a selected image, it is removed
 func (b *Button) SetIcon(icode string) {
 func (b *Button) SetIcon(icode string) {
 
 
-	ico := NewLabel(icode, true)
+	ico := NewIcon(icode)
 	if b.image != nil {
 	if b.image != nil {
 		b.Panel.Remove(b.image)
 		b.Panel.Remove(b.image)
 		b.image = nil
 		b.image = nil
@@ -199,14 +193,11 @@ func (b *Button) update() {
 // applyStyle applies the specified button style
 // applyStyle applies the specified button style
 func (b *Button) applyStyle(bs *ButtonStyle) {
 func (b *Button) applyStyle(bs *ButtonStyle) {
 
 
-	b.SetBordersColor4(&bs.BorderColor)
-	b.SetBordersFrom(&bs.Border)
-	b.SetPaddingsFrom(&bs.Paddings)
-	b.SetColor(&bs.BgColor)
+	b.Panel.ApplyStyle(&bs.PanelStyle)
 	if b.icon != nil {
 	if b.icon != nil {
-		b.icon.SetColor(&bs.FgColor)
+		b.icon.SetColor4(&bs.FgColor)
 	}
 	}
-	//b.Label.SetColor(&bs.FgColor)
+	b.Label.SetColor4(&bs.FgColor)
 }
 }
 
 
 // recalc recalculates all dimensions and position from inside out
 // recalc recalculates all dimensions and position from inside out

+ 6 - 2
gui/chart.go

@@ -101,6 +101,7 @@ func (ch *Chart) SetTitle(title string, size float64) {
 	// Sets title
 	// Sets title
 	if ch.title == nil {
 	if ch.title == nil {
 		ch.title = NewLabel(title)
 		ch.title = NewLabel(title)
+		ch.title.SetColor4(math32.NewColor4("black"))
 		ch.Add(ch.title)
 		ch.Add(ch.title)
 	}
 	}
 	ch.title.SetText(title)
 	ch.title.SetText(title)
@@ -170,6 +171,7 @@ func (ch *Chart) SetScaleX(lines int, color *math32.Color) {
 	value := ch.firstX
 	value := ch.firstX
 	for i := 0; i < lines; i++ {
 	for i := 0; i < lines; i++ {
 		l := NewLabel(fmt.Sprintf(ch.formatX, value))
 		l := NewLabel(fmt.Sprintf(ch.formatX, value))
+		l.SetColor4(math32.NewColor4("black"))
 		l.SetFontSize(ch.fontSizeX)
 		l.SetFontSize(ch.fontSizeX)
 		ch.Add(l)
 		ch.Add(l)
 		ch.labelsX = append(ch.labelsX, l)
 		ch.labelsX = append(ch.labelsX, l)
@@ -220,6 +222,7 @@ func (ch *Chart) SetScaleY(lines int, color *math32.Color) {
 	step := (ch.maxY - ch.minY) / float32(lines-1)
 	step := (ch.maxY - ch.minY) / float32(lines-1)
 	for i := 0; i < lines; i++ {
 	for i := 0; i < lines; i++ {
 		l := NewLabel(fmt.Sprintf(ch.formatY, value))
 		l := NewLabel(fmt.Sprintf(ch.formatY, value))
+		l.SetColor4(math32.NewColor4("black"))
 		l.SetFontSize(ch.fontSizeY)
 		l.SetFontSize(ch.fontSizeY)
 		ch.Add(l)
 		ch.Add(l)
 		ch.labelsY = append(ch.labelsY, l)
 		ch.labelsY = append(ch.labelsY, l)
@@ -282,7 +285,7 @@ func (ch *Chart) SetRangeYauto(auto bool) {
 	ch.updateGraphs()
 	ch.updateGraphs()
 }
 }
 
 
-// Returns the current y range
+// RangeY returns the current y range
 func (ch *Chart) RangeY() (minY, maxY float32) {
 func (ch *Chart) RangeY() (minY, maxY float32) {
 
 
 	return ch.minY, ch.maxY
 	return ch.minY, ch.maxY
@@ -601,7 +604,8 @@ func (sy *chartScaleY) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
 }
 }
 
 
 //
 //
-// Graph
+// Graph is the GUI element that represents a single plotted function.
+// A Chart has an array of Graph objects.
 //
 //
 type Graph struct {
 type Graph struct {
 	Panel                   // Embedded panel
 	Panel                   // Embedded panel

+ 8 - 16
gui/checkradio.go

@@ -6,7 +6,6 @@ package gui
 
 
 import (
 import (
 	"github.com/g3n/engine/gui/assets/icon"
 	"github.com/g3n/engine/gui/assets/icon"
-	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/window"
 	"github.com/g3n/engine/window"
 )
 )
 
 
@@ -17,6 +16,7 @@ const (
 	radioOFF = string(icon.RadioButtonUnchecked)
 	radioOFF = string(icon.RadioButtonUnchecked)
 )
 )
 
 
+// CheckRadio is a GUI element that can be either a checkbox or a radio button
 type CheckRadio struct {
 type CheckRadio struct {
 	Panel             // Embedded panel
 	Panel             // Embedded panel
 	Label      *Label // Text label
 	Label      *Label // Text label
@@ -31,14 +31,10 @@ type CheckRadio struct {
 	subroot    bool // indicates root subcription
 	subroot    bool // indicates root subcription
 }
 }
 
 
-type CheckRadioStyle struct {
-	Border      BorderSizes
-	Paddings    BorderSizes
-	BorderColor math32.Color4
-	BgColor     math32.Color4
-	FgColor     math32.Color
-}
+// CheckRadioStyle contains the styling of a CheckRadio
+type CheckRadioStyle BasicStyle
 
 
+// CheckRadioStyles contains an CheckRadioStyle for each valid GUI state
 type CheckRadioStyles struct {
 type CheckRadioStyles struct {
 	Normal   CheckRadioStyle
 	Normal   CheckRadioStyle
 	Over     CheckRadioStyle
 	Over     CheckRadioStyle
@@ -94,7 +90,7 @@ func newCheckRadio(check bool, text string) *CheckRadio {
 	cb.Panel.Add(cb.Label)
 	cb.Panel.Add(cb.Label)
 
 
 	// Creates icon label
 	// Creates icon label
-	cb.icon = NewLabel(" ", true)
+	cb.icon = NewIcon(" ")
 	cb.Panel.Add(cb.icon)
 	cb.Panel.Add(cb.icon)
 
 
 	cb.recalc()
 	cb.recalc()
@@ -250,13 +246,9 @@ func (cb *CheckRadio) update() {
 // setStyle sets the specified checkradio style
 // setStyle sets the specified checkradio style
 func (cb *CheckRadio) applyStyle(s *CheckRadioStyle) {
 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)
+	cb.Panel.ApplyStyle(&s.PanelStyle)
+	cb.icon.SetColor4(&s.FgColor)
+	cb.Label.SetColor4(&s.FgColor)
 }
 }
 
 
 // recalc recalculates dimensions and position from inside out
 // recalc recalculates dimensions and position from inside out

+ 12 - 0
gui/control_folder.go

@@ -8,17 +8,20 @@ import (
 	"fmt"
 	"fmt"
 )
 )
 
 
+// ControlFolder represents a folder with controls.
 type ControlFolder struct {
 type ControlFolder struct {
 	Folder                      // Embedded folder
 	Folder                      // Embedded folder
 	tree   Tree                 // control tree
 	tree   Tree                 // control tree
 	styles *ControlFolderStyles // Pointer to styles
 	styles *ControlFolderStyles // Pointer to styles
 }
 }
 
 
+// ControlFolderStyles contains the styling for the valid GUI states of the components of a ControlFolder.
 type ControlFolderStyles struct {
 type ControlFolderStyles struct {
 	Folder *FolderStyles
 	Folder *FolderStyles
 	Tree   *TreeStyles
 	Tree   *TreeStyles
 }
 }
 
 
+// ControlFolderGroup represents a group of controls in the control folder.
 type ControlFolderGroup struct {
 type ControlFolderGroup struct {
 	control *ControlFolder
 	control *ControlFolder
 	node    *TreeNode
 	node    *TreeNode
@@ -48,21 +51,25 @@ func (f *ControlFolder) Initialize(text string, width float32) {
 	f.Folder.SetAlignRight(false)
 	f.Folder.SetAlignRight(false)
 }
 }
 
 
+// Clear clears the control folder's tree
 func (f *ControlFolder) Clear() {
 func (f *ControlFolder) Clear() {
 
 
 	f.tree.Clear()
 	f.tree.Clear()
 }
 }
 
 
+// RemoveAt removes the IPanel at the specified position from the control folder's tree
 func (f *ControlFolder) RemoveAt(pos int) IPanel {
 func (f *ControlFolder) RemoveAt(pos int) IPanel {
 
 
 	return f.tree.RemoveAt(pos)
 	return f.tree.RemoveAt(pos)
 }
 }
 
 
+// AddPanel adds an IPanel to the control folder's tree
 func (f *ControlFolder) AddPanel(pan IPanel) {
 func (f *ControlFolder) AddPanel(pan IPanel) {
 
 
 	f.tree.Add(pan)
 	f.tree.Add(pan)
 }
 }
 
 
+// AddCheckBox adds a checkbox to the control folder's tree
 func (f *ControlFolder) AddCheckBox(text string) *CheckRadio {
 func (f *ControlFolder) AddCheckBox(text string) *CheckRadio {
 
 
 	cb := NewCheckBox(text)
 	cb := NewCheckBox(text)
@@ -70,6 +77,7 @@ func (f *ControlFolder) AddCheckBox(text string) *CheckRadio {
 	return cb
 	return cb
 }
 }
 
 
+// AddSlider adds a slider to the control folder's tree
 func (f *ControlFolder) AddSlider(text string, sf, v float32) *Slider {
 func (f *ControlFolder) AddSlider(text string, sf, v float32) *Slider {
 
 
 	cont, slider := f.newSlider(text, sf, v)
 	cont, slider := f.newSlider(text, sf, v)
@@ -77,6 +85,7 @@ func (f *ControlFolder) AddSlider(text string, sf, v float32) *Slider {
 	return slider
 	return slider
 }
 }
 
 
+// AddGroup adds a group to the control folder
 func (f *ControlFolder) AddGroup(text string) *ControlFolderGroup {
 func (f *ControlFolder) AddGroup(text string) *ControlFolderGroup {
 
 
 	g := new(ControlFolderGroup)
 	g := new(ControlFolderGroup)
@@ -98,6 +107,7 @@ func (f *ControlFolder) SetStyles(fs *ControlFolderStyles) {
 
 
 }
 }
 
 
+// AddCheckBox adds a checkbox to the control folder group
 func (g *ControlFolderGroup) AddCheckBox(text string) *CheckRadio {
 func (g *ControlFolderGroup) AddCheckBox(text string) *CheckRadio {
 
 
 	cb := NewCheckBox(text)
 	cb := NewCheckBox(text)
@@ -105,6 +115,7 @@ func (g *ControlFolderGroup) AddCheckBox(text string) *CheckRadio {
 	return cb
 	return cb
 }
 }
 
 
+// AddSlider adds a slider to the control folder group
 func (g *ControlFolderGroup) AddSlider(text string, sf, v float32) *Slider {
 func (g *ControlFolderGroup) AddSlider(text string, sf, v float32) *Slider {
 
 
 	cont, slider := g.control.newSlider(text, sf, v)
 	cont, slider := g.control.newSlider(text, sf, v)
@@ -112,6 +123,7 @@ func (g *ControlFolderGroup) AddSlider(text string, sf, v float32) *Slider {
 	return slider
 	return slider
 }
 }
 
 
+// AddPanel adds a panel to the control folder group
 func (g *ControlFolderGroup) AddPanel(pan IPanel) {
 func (g *ControlFolderGroup) AddPanel(pan IPanel) {
 
 
 	g.node.Add(pan)
 	g.node.Add(pan)

+ 4 - 0
gui/docklayout.go

@@ -4,9 +4,11 @@
 
 
 package gui
 package gui
 
 
+// DockLayout is the layout for docking panels to the internal edges of their parent.
 type DockLayout struct {
 type DockLayout struct {
 }
 }
 
 
+// DockLayoutParams specifies the edge to dock to.
 type DockLayoutParams struct {
 type DockLayoutParams struct {
 	Edge int
 	Edge int
 }
 }
@@ -19,11 +21,13 @@ const (
 	DockCenter
 	DockCenter
 )
 )
 
 
+// NewDockLayout returns a pointer to a new DockLayout.
 func NewDockLayout() *DockLayout {
 func NewDockLayout() *DockLayout {
 
 
 	return new(DockLayout)
 	return new(DockLayout)
 }
 }
 
 
+// Recalc (which satisfies the ILayout interface) recalculates the positions and sizes of the children panels.
 func (dl *DockLayout) Recalc(ipan IPanel) {
 func (dl *DockLayout) Recalc(ipan IPanel) {
 
 
 	pan := ipan.GetPanel()
 	pan := ipan.GetPanel()

+ 35 - 46
gui/dropdown.go

@@ -6,10 +6,10 @@ package gui
 
 
 import (
 import (
 	"github.com/g3n/engine/gui/assets/icon"
 	"github.com/g3n/engine/gui/assets/icon"
-	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/window"
 	"github.com/g3n/engine/window"
 )
 )
 
 
+// DropDown represents a dropdown GUI element.
 type DropDown struct {
 type DropDown struct {
 	Panel                        // Embedded panel
 	Panel                        // Embedded panel
 	icon         *Label          // internal label with icon
 	icon         *Label          // internal label with icon
@@ -23,21 +23,15 @@ type DropDown struct {
 	clickOut     bool
 	clickOut     bool
 }
 }
 
 
-// DropDown list style
-type DropDownStyle struct {
-	Border      BorderSizes
-	Paddings    BorderSizes
-	BorderColor math32.Color4
-	BgColor     math32.Color
-	FgColor     math32.Color
-}
+// DropDownStyle contains the styling of a DropDown.
+type DropDownStyle BasicStyle
 
 
-// DropDown list styles
+// DropDownStyles contains a DropDownStyle for each valid GUI state.
 type DropDownStyles struct {
 type DropDownStyles struct {
-	Normal   *DropDownStyle
-	Over     *DropDownStyle
-	Focus    *DropDownStyle
-	Disabled *DropDownStyle
+	Normal   DropDownStyle
+	Over     DropDownStyle
+	Focus    DropDownStyle
+	Disabled DropDownStyle
 }
 }
 
 
 // NewDropDown creates and returns a pointer to a new drop down widget with the specified width.
 // NewDropDown creates and returns a pointer to a new drop down widget with the specified width.
@@ -58,8 +52,8 @@ func NewDropDown(width float32, item *ImageLabel) *DropDown {
 	dd.Panel.Add(dd.litem)
 	dd.Panel.Add(dd.litem)
 
 
 	// Create icon
 	// Create icon
-	dd.icon = NewLabel(" ", true)
-	dd.icon.SetFontSize(StyleDefault().Font.Size() * 1.3)
+	dd.icon = NewIcon(" ")
+	dd.icon.SetFontSize(StyleDefault().Label.PointSize * 1.3)
 	dd.icon.SetText(string(icon.ArrowDropDown))
 	dd.icon.SetText(string(icon.ArrowDropDown))
 	dd.Panel.Add(dd.icon)
 	dd.Panel.Add(dd.icon)
 
 
@@ -72,6 +66,7 @@ func NewDropDown(width float32, item *ImageLabel) *DropDown {
 	dd.list.Subscribe(OnMouseDown, dd.onListMouse)
 	dd.list.Subscribe(OnMouseDown, dd.onListMouse)
 	dd.list.Subscribe(OnMouseOut, dd.onListMouse)
 	dd.list.Subscribe(OnMouseOut, dd.onListMouse)
 	dd.list.Subscribe(OnChange, dd.onListChangeEvent)
 	dd.list.Subscribe(OnChange, dd.onListChangeEvent)
+	dd.list.Subscribe(OnCursor, func(evname string, ev interface{}) { dd.root.StopPropagation(StopAll) })
 	dd.Panel.Add(dd.list)
 	dd.Panel.Add(dd.list)
 
 
 	dd.update()
 	dd.update()
@@ -80,21 +75,21 @@ func NewDropDown(width float32, item *ImageLabel) *DropDown {
 	return dd
 	return dd
 }
 }
 
 
-// Add add a list item at the end of the list
+// Add adds a list item at the end of the list
 func (dd *DropDown) Add(item *ImageLabel) {
 func (dd *DropDown) Add(item *ImageLabel) {
 
 
 	dd.list.Add(item)
 	dd.list.Add(item)
 }
 }
 
 
 // InsertAt inserts a list item at the specified position
 // InsertAt inserts a list item at the specified position
-// Returs true if the item was successfuly inserted
+// Returs true if the item was successfully inserted
 func (dd *DropDown) InsertAt(pos int, item *ImageLabel) {
 func (dd *DropDown) InsertAt(pos int, item *ImageLabel) {
 
 
 	dd.list.InsertAt(pos, item)
 	dd.list.InsertAt(pos, item)
 }
 }
 
 
 // RemoveAt removes the list item from the specified position
 // RemoveAt removes the list item from the specified position
-// Returs true if the item was successfuly removed
+// Returs true if the item was successfully removed
 func (dd *DropDown) RemoveAt(pos int) {
 func (dd *DropDown) RemoveAt(pos int) {
 
 
 	dd.list.RemoveAt(pos)
 	dd.list.RemoveAt(pos)
@@ -112,8 +107,7 @@ func (dd *DropDown) Len() int {
 	return dd.list.Len()
 	return dd.list.Len()
 }
 }
 
 
-// Returns the currently selected item or nil if not item
-// was selected
+// Selected returns the currently selected item or nil if no item was selected
 func (dd *DropDown) Selected() *ImageLabel {
 func (dd *DropDown) Selected() *ImageLabel {
 
 
 	return dd.selItem
 	return dd.selItem
@@ -165,14 +159,12 @@ func (dd *DropDown) onCursor(evname string, ev interface{}) {
 
 
 	if evname == OnCursorEnter {
 	if evname == OnCursorEnter {
 		dd.overDropdown = true
 		dd.overDropdown = true
-		dd.update()
-		return
 	}
 	}
 	if evname == OnCursorLeave {
 	if evname == OnCursorLeave {
 		dd.overDropdown = false
 		dd.overDropdown = false
-		dd.update()
-		return
 	}
 	}
+	dd.update()
+	dd.root.StopPropagation(StopAll)
 }
 }
 
 
 // onListMouseEvent receives mouse events over the list
 // onListMouseEvent receives mouse events over the list
@@ -208,20 +200,20 @@ func (dd *DropDown) onListMouse(evname string, ev interface{}) {
 }
 }
 
 
 // onListCursor receives subscribed events over the list
 // 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
-	}
-
-}
+//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
+//	}
+//	dd.root.StopPropagation(StopAll)
+//}
 
 
 // copySelected copy to the dropdown panel the selected item
 // copySelected copy to the dropdown panel the selected item
 // from the list.
 // from the list.
@@ -264,24 +256,21 @@ func (dd *DropDown) recalc() {
 func (dd *DropDown) update() {
 func (dd *DropDown) update() {
 
 
 	if dd.overDropdown || dd.overList {
 	if dd.overDropdown || dd.overList {
-		dd.applyStyle(dd.styles.Over)
+		dd.applyStyle(&dd.styles.Over)
 		dd.list.ApplyStyle(StyleOver)
 		dd.list.ApplyStyle(StyleOver)
 		return
 		return
 	}
 	}
 	if dd.focus {
 	if dd.focus {
-		dd.applyStyle(dd.styles.Focus)
+		dd.applyStyle(&dd.styles.Focus)
 		dd.list.ApplyStyle(StyleFocus)
 		dd.list.ApplyStyle(StyleFocus)
 		return
 		return
 	}
 	}
-	dd.applyStyle(dd.styles.Normal)
+	dd.applyStyle(&dd.styles.Normal)
 	dd.list.ApplyStyle(StyleNormal)
 	dd.list.ApplyStyle(StyleNormal)
 }
 }
 
 
 // applyStyle applies the specified style
 // applyStyle applies the specified style
 func (dd *DropDown) applyStyle(s *DropDownStyle) {
 func (dd *DropDown) applyStyle(s *DropDownStyle) {
 
 
-	dd.SetBordersFrom(&s.Border)
-	dd.SetBordersColor4(&s.BorderColor)
-	dd.SetPaddingsFrom(&s.Paddings)
-	dd.SetColor(&s.BgColor)
+	dd.Panel.ApplyStyle(&s.PanelStyle)
 }
 }

+ 15 - 10
gui/edit.go

@@ -12,6 +12,7 @@ import (
 	"time"
 	"time"
 )
 )
 
 
+// Edit represents a text edit box GUI element
 type Edit struct {
 type Edit struct {
 	Label              // Embedded label
 	Label              // Embedded label
 	MaxLength   int    // Maximum number of characters
 	MaxLength   int    // Maximum number of characters
@@ -26,16 +27,18 @@ type Edit struct {
 	styles      *EditStyles
 	styles      *EditStyles
 }
 }
 
 
+// EditStyle contains the styling of an Edit
 type EditStyle struct {
 type EditStyle struct {
-	Border      BorderSizes
-	Paddings    BorderSizes
+	Border      RectBounds
+	Paddings    RectBounds
 	BorderColor math32.Color4
 	BorderColor math32.Color4
-	BgColor     math32.Color
+	BgColor     math32.Color4
 	BgAlpha     float32
 	BgAlpha     float32
-	FgColor     math32.Color
-	HolderColor math32.Color
+	FgColor     math32.Color4
+	HolderColor math32.Color4
 }
 }
 
 
+// EditStyles contains an EditStyle for each valid GUI state
 type EditStyles struct {
 type EditStyles struct {
 	Normal   EditStyle
 	Normal   EditStyle
 	Over     EditStyle
 	Over     EditStyle
@@ -92,7 +95,7 @@ func (ed *Edit) Text() string {
 // SetFontSize sets label font size (overrides Label.SetFontSize)
 // SetFontSize sets label font size (overrides Label.SetFontSize)
 func (ed *Edit) SetFontSize(size float64) *Edit {
 func (ed *Edit) SetFontSize(size float64) *Edit {
 
 
-	ed.Label.fontSize = size
+	ed.Label.SetFontSize(size)
 	ed.redraw(ed.focus)
 	ed.redraw(ed.focus)
 	return ed
 	return ed
 }
 }
@@ -276,12 +279,14 @@ func (ed *Edit) onMouse(evname string, ev interface{}) {
 func (ed *Edit) onCursor(evname string, ev interface{}) {
 func (ed *Edit) onCursor(evname string, ev interface{}) {
 
 
 	if evname == OnCursorEnter {
 	if evname == OnCursorEnter {
+		ed.root.SetCursorText()
 		ed.cursorOver = true
 		ed.cursorOver = true
 		ed.update()
 		ed.update()
 		ed.root.StopPropagation(Stop3D)
 		ed.root.StopPropagation(Stop3D)
 		return
 		return
 	}
 	}
 	if evname == OnCursorLeave {
 	if evname == OnCursorLeave {
+		ed.root.SetCursorNormal()
 		ed.cursorOver = false
 		ed.cursorOver = false
 		ed.update()
 		ed.update()
 		ed.root.StopPropagation(Stop3D)
 		ed.root.StopPropagation(Stop3D)
@@ -327,15 +332,15 @@ func (ed *Edit) applyStyle(s *EditStyle) {
 	ed.SetBordersFrom(&s.Border)
 	ed.SetBordersFrom(&s.Border)
 	ed.SetBordersColor4(&s.BorderColor)
 	ed.SetBordersColor4(&s.BorderColor)
 	ed.SetPaddingsFrom(&s.Paddings)
 	ed.SetPaddingsFrom(&s.Paddings)
-	ed.Label.SetColor(&s.FgColor)
-	ed.Label.SetBgColor(&s.BgColor)
+	ed.Label.SetColor4(&s.FgColor)
+	ed.Label.SetBgColor4(&s.BgColor)
 	//ed.Label.SetBgAlpha(s.BgAlpha)
 	//ed.Label.SetBgAlpha(s.BgAlpha)
 
 
 	if !ed.focus && len(ed.text) == 0 && len(ed.placeHolder) > 0 {
 	if !ed.focus && len(ed.text) == 0 && len(ed.placeHolder) > 0 {
-		ed.Label.SetColor(&s.HolderColor)
+		ed.Label.SetColor4(&s.HolderColor)
 		ed.Label.setTextCaret(ed.placeHolder, editMarginX, ed.width, -1, ed.col)
 		ed.Label.setTextCaret(ed.placeHolder, editMarginX, ed.width, -1, ed.col)
 	} else {
 	} else {
-		ed.Label.SetColor(&s.FgColor)
+		ed.Label.SetColor4(&s.FgColor)
 		ed.redraw(ed.focus)
 		ed.redraw(ed.focus)
 	}
 	}
 }
 }

+ 1 - 0
gui/filllayout.go

@@ -4,6 +4,7 @@
 
 
 package gui
 package gui
 
 
+// FillLayout is the simple layout where the assigned panel "fills" its parent in the specified dimension(s)
 type FillLayout struct {
 type FillLayout struct {
 	width  bool
 	width  bool
 	height bool
 	height bool

+ 22 - 27
gui/folder.go

@@ -8,6 +8,7 @@ import (
 	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/math32"
 )
 )
 
 
+// Folder represents a folder GUI element.
 type Folder struct {
 type Folder struct {
 	Panel               // Embedded panel
 	Panel               // Embedded panel
 	label        Label  // Folder label
 	label        Label  // Folder label
@@ -18,25 +19,23 @@ type Folder struct {
 	alignRight   bool
 	alignRight   bool
 }
 }
 
 
+// FolderStyle contains the styling of a Folder.
 type FolderStyle struct {
 type FolderStyle struct {
-	Margins     BorderSizes
-	Border      BorderSizes
-	Paddings    BorderSizes
-	BorderColor math32.Color4
-	BgColor     math32.Color
-	FgColor     math32.Color
+	PanelStyle
+	FgColor     math32.Color4
 	Icons       [2]string
 	Icons       [2]string
 }
 }
 
 
+// FolderStyles contains a FolderStyle for each valid GUI state.
 type FolderStyles struct {
 type FolderStyles struct {
-	Normal   *FolderStyle
-	Over     *FolderStyle
-	Focus    *FolderStyle
-	Disabled *FolderStyle
+	Normal   FolderStyle
+	Over     FolderStyle
+	Focus    FolderStyle
+	Disabled FolderStyle
 }
 }
 
 
 // NewFolder creates and returns a pointer to a new folder widget
 // NewFolder creates and returns a pointer to a new folder widget
-// with the specified text and initial width
+// with the specified text and initial width.
 func NewFolder(text string, width float32, contentPanel IPanel) *Folder {
 func NewFolder(text string, width float32, contentPanel IPanel) *Folder {
 
 
 	f := new(Folder)
 	f := new(Folder)
@@ -45,7 +44,7 @@ func NewFolder(text string, width float32, contentPanel IPanel) *Folder {
 }
 }
 
 
 // Initialize initializes the Folder with the specified text and initial width
 // Initialize initializes the Folder with the specified text and initial width
-// It is normally used when the folder is embedded in another object
+// It is normally used when the folder is embedded in another object.
 func (f *Folder) Initialize(text string, width float32, contentPanel IPanel) {
 func (f *Folder) Initialize(text string, width float32, contentPanel IPanel) {
 
 
 	f.Panel.Initialize(width, 0)
 	f.Panel.Initialize(width, 0)
@@ -57,7 +56,7 @@ func (f *Folder) Initialize(text string, width float32, contentPanel IPanel) {
 
 
 	// Create icon
 	// Create icon
 	f.icon.initialize("", StyleDefault().FontIcon)
 	f.icon.initialize("", StyleDefault().FontIcon)
-	f.icon.SetFontSize(f.label.FontSize() * 1.3)
+	f.icon.SetFontSize(StyleDefault().Label.PointSize * 1.3)
 	f.Panel.Add(&f.icon)
 	f.Panel.Add(&f.icon)
 
 
 	// Setup content panel
 	// Setup content panel
@@ -76,7 +75,7 @@ func (f *Folder) Initialize(text string, width float32, contentPanel IPanel) {
 	f.recalc()
 	f.recalc()
 }
 }
 
 
-// SetStyles set the folder styles overriding the default style
+// SetStyles set the folder styles overriding the default style.
 func (f *Folder) SetStyles(fs *FolderStyles) {
 func (f *Folder) SetStyles(fs *FolderStyles) {
 
 
 	f.styles = fs
 	f.styles = fs
@@ -84,7 +83,7 @@ func (f *Folder) SetStyles(fs *FolderStyles) {
 }
 }
 
 
 // SetAlignRight sets the side of the alignment of the content panel
 // SetAlignRight sets the side of the alignment of the content panel
-// in relation to the folder
+// in relation to the folder.
 func (f *Folder) SetAlignRight(state bool) {
 func (f *Folder) SetAlignRight(state bool) {
 
 
 	f.alignRight = state
 	f.alignRight = state
@@ -92,7 +91,7 @@ func (f *Folder) SetAlignRight(state bool) {
 }
 }
 
 
 // TotalHeight returns this folder total height
 // TotalHeight returns this folder total height
-// considering the contents panel, if visible
+// considering the contents panel, if visible.
 func (f *Folder) TotalHeight() float32 {
 func (f *Folder) TotalHeight() float32 {
 
 
 	height := f.Height()
 	height := f.Height()
@@ -102,7 +101,7 @@ func (f *Folder) TotalHeight() float32 {
 	return height
 	return height
 }
 }
 
 
-// onMouse receives mouse button events over the folder panel
+// onMouse receives mouse button events over the folder panel.
 func (f *Folder) onMouse(evname string, ev interface{}) {
 func (f *Folder) onMouse(evname string, ev interface{}) {
 
 
 	switch evname {
 	switch evname {
@@ -139,29 +138,25 @@ func (f *Folder) onCursor(evname string, ev interface{}) {
 func (f *Folder) update() {
 func (f *Folder) update() {
 
 
 	if f.cursorOver {
 	if f.cursorOver {
-		f.applyStyle(f.styles.Over)
+		f.applyStyle(&f.styles.Over)
 		return
 		return
 	}
 	}
-	f.applyStyle(f.styles.Normal)
+	f.applyStyle(&f.styles.Normal)
 }
 }
 
 
 // applyStyle applies the specified style
 // applyStyle applies the specified style
 func (f *Folder) applyStyle(s *FolderStyle) {
 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)
+	f.Panel.ApplyStyle(&s.PanelStyle)
 
 
 	icode := 0
 	icode := 0
 	if f.contentPanel.GetPanel().Visible() {
 	if f.contentPanel.GetPanel().Visible() {
 		icode = 1
 		icode = 1
 	}
 	}
 	f.icon.SetText(string(s.Icons[icode]))
 	f.icon.SetText(string(s.Icons[icode]))
-	f.icon.SetColor(&s.FgColor)
-	f.label.SetBgColor(&s.BgColor)
-	f.label.SetColor(&s.FgColor)
+	f.icon.SetColor4(&s.FgColor)
+	f.label.SetBgColor4(&s.BgColor)
+	f.label.SetColor4(&s.FgColor)
 }
 }
 
 
 func (f *Folder) recalc() {
 func (f *Folder) recalc() {

+ 5 - 5
gui/hboxlayout.go

@@ -134,7 +134,7 @@ func (bl *HBoxLayout) Recalc(ipan IPanel) {
 	// Calculates the total width, expanded width, fixed width and
 	// Calculates the total width, expanded width, fixed width and
 	// the sum of the expand factor for all items.
 	// the sum of the expand factor for all items.
 	var twidth float32
 	var twidth float32
-	var ewidth float32
+	//var ewidth float32
 	var fwidth float32
 	var fwidth float32
 	var texpand float32
 	var texpand float32
 	ecount := 0
 	ecount := 0
@@ -157,10 +157,10 @@ func (bl *HBoxLayout) Recalc(ipan IPanel) {
 		// Calculate width of expanded items
 		// Calculate width of expanded items
 		if params.Expand > 0 {
 		if params.Expand > 0 {
 			texpand += params.Expand
 			texpand += params.Expand
-			ewidth += pan.Width()
-			if pos > 0 {
-				ewidth += bl.spacing
-			}
+			//ewidth += pan.Width()
+			//if pos > 0 {
+			//	ewidth += bl.spacing
+			//}
 			ecount++
 			ecount++
 			// Calculate width of fixed items
 			// Calculate width of fixed items
 		} else {
 		} else {

+ 1 - 0
gui/ilayout.go

@@ -4,6 +4,7 @@
 
 
 package gui
 package gui
 
 
+// ILayout is the interface for layouts
 type ILayout interface {
 type ILayout interface {
 	Recalc(ipan IPanel)
 	Recalc(ipan IPanel)
 }
 }

+ 5 - 14
gui/image_button.go

@@ -5,11 +5,11 @@
 package gui
 package gui
 
 
 import (
 import (
-	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/texture"
 	"github.com/g3n/engine/texture"
 	"github.com/g3n/engine/window"
 	"github.com/g3n/engine/window"
 )
 )
 
 
+// ImageButton represents an image button GUI element
 type ImageButton struct {
 type ImageButton struct {
 	*Panel                                             // Embedded Panel
 	*Panel                                             // Embedded Panel
 	label       *Label                                 // Label panel
 	label       *Label                                 // Label panel
@@ -32,13 +32,7 @@ const (
 )
 )
 
 
 // ImageButton style
 // ImageButton style
-type ImageButtonStyle struct {
-	Border      BorderSizes
-	Paddings    BorderSizes
-	BorderColor math32.Color4
-	BgColor     math32.Color4
-	FgColor     math32.Color
-}
+type ImageButtonStyle BasicStyle
 
 
 // All ImageButton styles
 // All ImageButton styles
 type ImageButtonStyles struct {
 type ImageButtonStyles struct {
@@ -117,7 +111,7 @@ func (b *ImageButton) SetIcon(icode string) {
 	b.iconLabel = true
 	b.iconLabel = true
 	if b.label == nil {
 	if b.label == nil {
 		// Create icon
 		// Create icon
-		b.label = NewLabel(icode, true)
+		b.label = NewIcon(icode)
 		b.Panel.Add(b.label)
 		b.Panel.Add(b.label)
 	} else {
 	} else {
 		b.label.SetText(icode)
 		b.label.SetText(icode)
@@ -253,12 +247,9 @@ func (b *ImageButton) update() {
 // applyStyle applies the specified button style
 // applyStyle applies the specified button style
 func (b *ImageButton) applyStyle(bs *ImageButtonStyle) {
 func (b *ImageButton) applyStyle(bs *ImageButtonStyle) {
 
 
-	b.SetBordersColor4(&bs.BorderColor)
-	b.SetBordersFrom(&bs.Border)
-	b.SetPaddingsFrom(&bs.Paddings)
-	b.SetColor4(&bs.BgColor)
+	b.Panel.ApplyStyle(&bs.PanelStyle)
 	if b.label != nil {
 	if b.label != nil {
-		b.label.SetColor(&bs.FgColor)
+		b.label.SetColor4(&bs.FgColor)
 	}
 	}
 }
 }
 
 

+ 6 - 15
gui/imagelabel.go

@@ -29,14 +29,8 @@ type ImageLabel struct {
 	icon  *Label // optional internal icon label
 	icon  *Label // optional internal icon label
 }
 }
 
 
-// ImageLabel style
-type ImageLabelStyle struct {
-	Border      BorderSizes
-	Paddings    BorderSizes
-	BorderColor math32.Color4
-	BgColor     math32.Color4
-	FgColor     math32.Color4
-}
+// ImageLabelStyle
+type ImageLabelStyle BasicStyle
 
 
 // NewImageLabel creates and returns a pointer to a new image label widget
 // NewImageLabel creates and returns a pointer to a new image label widget
 // with the specified text for the label and no image/icon
 // with the specified text for the label and no image/icon
@@ -78,8 +72,8 @@ func (il *ImageLabel) SetIcon(icon string) {
 		il.image = nil
 		il.image = nil
 	}
 	}
 	if il.icon == nil {
 	if il.icon == nil {
-		il.icon = NewLabel(icon, true)
-		il.icon.SetFontSize(il.label.FontSize() * 1.4)
+		il.icon = NewIcon(icon)
+		il.icon.SetFontSize(StyleDefault().Label.PointSize * 1.4)
 		il.Panel.Add(il.icon)
 		il.Panel.Add(il.icon)
 	}
 	}
 	il.icon.SetText(icon)
 	il.icon.SetText(icon)
@@ -194,10 +188,7 @@ func (il *ImageLabel) CopyFields(other *ImageLabel) {
 // applyStyle applies the specified image label style
 // applyStyle applies the specified image label style
 func (il *ImageLabel) applyStyle(s *ImageLabelStyle) {
 func (il *ImageLabel) applyStyle(s *ImageLabelStyle) {
 
 
-	il.SetBordersColor4(&s.BorderColor)
-	il.SetBordersFrom(&s.Border)
-	il.SetPaddingsFrom(&s.Paddings)
-	il.SetColor4(&s.BgColor)
+	il.Panel.ApplyStyle(&s.PanelStyle)
 	if il.icon != nil {
 	if il.icon != nil {
 		il.icon.SetColor4(&s.FgColor)
 		il.icon.SetColor4(&s.FgColor)
 	}
 	}
@@ -212,7 +203,7 @@ func (il *ImageLabel) recalc() {
 	height := il.Panel.ContentHeight()
 	height := il.Panel.ContentHeight()
 
 
 	// Image or icon width
 	// Image or icon width
-	var imgWidth float32 = 0
+	var imgWidth float32
 	var spacing float32
 	var spacing float32
 	if il.image != nil {
 	if il.image != nil {
 		imgWidth = il.image.Width()
 		imgWidth = il.image.Width()

+ 643 - 0
gui/itemscroller.go

@@ -0,0 +1,643 @@
+// 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"
+	"math"
+)
+
+// ItemScroller is the GUI element that allows scrolling of IPanels
+type ItemScroller struct {
+	Panel                              // Embedded panel
+	vert           bool                // vertical/horizontal scroller flag
+	styles         *ItemScrollerStyles // 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
+	autoButtonSize bool                // scroll button size is adjusted relative to content/view
+	scrollBarEvent bool
+}
+
+// ItemScrollerStyle contains the styling of a ItemScroller
+type ItemScrollerStyle BasicStyle
+
+// ItemScrollerStyles contains a ItemScrollerStyle for each valid GUI state
+type ItemScrollerStyles struct {
+	Normal   ItemScrollerStyle
+	Over     ItemScrollerStyle
+	Focus    ItemScrollerStyle
+	Disabled ItemScrollerStyle
+}
+
+// NewVScroller creates and returns a pointer to a new vertical scroller panel
+// with the specified dimensions.
+func NewVScroller(width, height float32) *ItemScroller {
+
+	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) *ItemScroller {
+
+	return newScroller(false, width, height)
+}
+
+// newScroller creates and returns a pointer to a new ItemScroller panel
+// with the specified layout orientation and initial dimensions
+func newScroller(vert bool, width, height float32) *ItemScroller {
+
+	s := new(ItemScroller)
+	s.initialize(vert, width, height)
+	return s
+}
+
+// Clear removes and disposes of all the scroller children
+func (s *ItemScroller) Clear() {
+
+	s.Panel.DisposeChildren(true)
+	s.first = 0
+	s.hscroll = nil
+	s.vscroll = nil
+	s.items = s.items[0:0]
+	s.update()
+	s.recalc()
+}
+
+// Len return the number of items in the scroller
+func (s *ItemScroller) Len() int {
+
+	return len(s.items)
+}
+
+// Add appends the specified item to the end of the scroller
+func (s *ItemScroller) Add(item IPanel) {
+
+	s.InsertAt(len(s.items), item)
+}
+
+// InsertAt inserts an item at the specified position
+func (s *ItemScroller) InsertAt(pos int, item IPanel) {
+
+	// Validates position
+	if pos < 0 || pos > len(s.items) {
+		panic("ItemScroller.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 *ItemScroller) RemoveAt(pos int) IPanel {
+
+	// Validates position
+	if pos < 0 || pos >= len(s.items) {
+		panic("ItemScroller.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()
+	return item
+}
+
+// Remove removes the specified item from the ItemScroller
+func (s *ItemScroller) Remove(item IPanel) {
+
+	for p, curr := range s.items {
+		if curr == item {
+			s.RemoveAt(p)
+			return
+		}
+	}
+}
+
+// ItemAt returns the item at the specified position.
+// Returns nil if the position is invalid.
+func (s *ItemScroller) 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 *ItemScroller) 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 *ItemScroller) First() int {
+
+	return s.first
+}
+
+// SetFirst set the position of first visible if possible
+func (s *ItemScroller) 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 *ItemScroller) ScrollDown() {
+
+	max := s.maxFirst()
+	if s.first >= max {
+		return
+	}
+	s.first++
+	s.recalc()
+}
+
+// ScrollUp scrolls the list up one item if possible
+func (s *ItemScroller) 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 *ItemScroller) ItemVisible(pos int) bool {
+
+	if pos < s.first {
+		return false
+	}
+
+	// Vertical scroller
+	if s.vert {
+		var height float32
+		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
+	var width float32
+	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 *ItemScroller) SetStyles(ss *ItemScrollerStyles) {
+
+	s.styles = ss
+	s.update()
+}
+
+// ApplyStyle applies the specified style to the ItemScroller
+func (s *ItemScroller) ApplyStyle(style int) {
+
+	switch style {
+	case StyleOver:
+		s.applyStyle(&s.styles.Over)
+	case StyleFocus:
+		s.applyStyle(&s.styles.Focus)
+	case StyleNormal:
+		s.applyStyle(&s.styles.Normal)
+	case StyleDef:
+		s.update()
+	}
+}
+
+// SetAutoWidth sets the maximum automatic width
+func (s *ItemScroller) SetAutoWidth(maxWidth float32) {
+
+	s.maxAutoWidth = maxWidth
+}
+
+// SetAutoHeight sets the maximum automatic height
+func (s *ItemScroller) SetAutoHeight(maxHeight float32) {
+
+	s.maxAutoHeight = maxHeight
+}
+
+// SetAutoButtonSize specified whether the scrollbutton size should be adjusted relative to the size of the content/view
+func (s *ItemScroller) SetAutoButtonSize(autoButtonSize bool) {
+
+	s.autoButtonSize = autoButtonSize
+}
+
+// initialize initializes this scroller and is normally used by other types which contains a scroller
+func (s *ItemScroller) initialize(vert bool, width, height float32) {
+
+	s.vert = vert
+	s.autoButtonSize = true
+	s.Panel.Initialize(width, height)
+	s.styles = &StyleDefault().ItemScroller
+
+	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 *ItemScroller) 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 *ItemScroller) 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)
+}
+
+// onResize receives resize events
+func (s *ItemScroller) onResize(evname string, ev interface{}) {
+
+	s.recalc()
+}
+
+// autoSize resizes the scroller if necessary
+func (s *ItemScroller) autoSize() {
+
+	if s.maxAutoWidth == 0 && s.maxAutoHeight == 0 {
+		return
+	}
+
+	var width float32
+	var height float32
+	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 *ItemScroller) recalc() {
+
+	if s.vert {
+		s.vRecalc()
+	} else {
+		s.hRecalc()
+	}
+}
+
+// vRecalc recalculates for the vertical scroller
+func (s *ItemScroller) vRecalc() {
+
+	// Checks if scroll bar should be visible or not
+	scroll := false
+	if s.first > 0 {
+		scroll = true
+	} else {
+		var posY float32
+		for _, item := range s.items[s.first:] {
+			posY += item.TotalHeight()
+			if posY > s.height {
+				scroll = true
+				break
+			}
+		}
+	}
+	s.setVScrollBar(scroll)
+
+	// Compute size of scroll button
+	if scroll && s.autoButtonSize {
+		var totalHeight float32
+		for _, item := range s.items {
+			// TODO OPTIMIZATION
+			// Break when the view/content proportion becomes smaller than the minimum button size
+			totalHeight += item.TotalHeight()
+		}
+		s.vscroll.SetButtonSize(s.height * s.height/totalHeight)
+	}
+
+	// Items width
+	width := s.ContentWidth()
+	if scroll {
+		width -= s.vscroll.Width()
+	}
+
+	var posY float32
+	// 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 *ItemScroller) hRecalc() {
+
+	// Checks if scroll bar should be visible or not
+	scroll := false
+	if s.first > 0 {
+		scroll = true
+	} else {
+		var posX float32
+		for _, item := range s.items[s.first:] {
+			posX += item.GetPanel().Width()
+			if posX > s.width {
+				scroll = true
+				break
+			}
+		}
+	}
+	s.setHScrollBar(scroll)
+
+	// Compute size of scroll button
+	if scroll && s.autoButtonSize {
+		var totalWidth float32
+		for _, item := range s.items {
+			// TODO OPTIMIZATION
+			// Break when the view/content proportion becomes smaller than the minimum button size
+			totalWidth += item.GetPanel().Width()
+		}
+		s.hscroll.SetButtonSize(s.width * s.width/totalWidth)
+	}
+
+	// Items height
+	height := s.ContentHeight()
+	if scroll {
+		height -= s.hscroll.Height()
+	}
+
+	var posX float32
+	// 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 *ItemScroller) maxFirst() int {
+
+	// Vertical scroller
+	if s.vert {
+		var height float32
+		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
+	var width float32
+	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 *ItemScroller) 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 *ItemScroller) 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 *ItemScroller) 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 *ItemScroller) 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 *ItemScroller) applyStyle(st *ItemScrollerStyle) {
+
+	s.Panel.ApplyStyle(&st.PanelStyle)
+}

+ 114 - 91
gui/label.go

@@ -11,193 +11,216 @@ import (
 	"github.com/g3n/engine/texture"
 	"github.com/g3n/engine/texture"
 )
 )
 
 
-// Label is a panel which contains a texture for rendering text
-// The content size of the label panel is the exact size of texture
+// Label is a panel which contains a texture with text.
+// The content size of the label panel is the exact size of the texture.
 type Label struct {
 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 text font.
-// If icon is true the text is drawn using the default icon font
-func NewLabel(msg string, icon ...bool) *Label {
+	Panel                    // Embedded Panel
+	font  *text.Font         // TrueType font face
+	tex   *texture.Texture2D // Texture with text
+	style *LabelStyle        // The style of the panel and font attributes
+	text  string             // Text being displayed
+}
+
+// LabelStyle contains all the styling attributes of a Label.
+// It's essentially a BasicStyle combined with FontAttributes.
+type LabelStyle struct {
+	PanelStyle
+	text.FontAttributes
+	FgColor math32.Color4
+}
+
+// NewLabel creates and returns a label panel with
+// the specified text drawn using the default text font.
+func NewLabel(text string) *Label {
+	return NewLabelWithFont(text, StyleDefault().Font)
+}
+
+// NewLabel creates and returns a label panel with
+// the specified text drawn using the default icon font.
+func NewIcon(icon string) *Label {
+	return NewLabelWithFont(icon, StyleDefault().FontIcon)
+}
+
+// NewLabelWithFont creates and returns a label panel with
+// the specified text drawn using the specified font.
+func NewLabelWithFont(msg string, font *text.Font) *Label {
 
 
 	l := new(Label)
 	l := new(Label)
-	if len(icon) > 0 && icon[0] {
-		l.initialize(msg, StyleDefault().FontIcon)
-	} else {
-		l.initialize(msg, StyleDefault().Font)
-	}
+	l.initialize(msg, font)
 	return l
 	return l
 }
 }
 
 
 // initialize initializes this label and is normally used by other
 // initialize initializes this label and is normally used by other
-// gui types which contains a label.
+// components which contain a label.
 func (l *Label) initialize(msg string, font *text.Font) {
 func (l *Label) initialize(msg string, font *text.Font) {
 
 
 	l.font = font
 	l.font = font
 	l.Panel.Initialize(0, 0)
 	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.Color4{0, 0, 0, 1}
+
+	// TODO: Remove this hack in an elegant way e.g. set the label style depending of if it's an icon or text label and have two defaults (one for icon labels one for text tabels)
+	if font != StyleDefault().FontIcon {
+		l.Panel.SetPaddings(2, 0, 2, 0)
+	}
+
+	// Copy the style based on the default Label style
+	styleCopy := StyleDefault().Label
+	l.style = &styleCopy
+
 	l.SetText(msg)
 	l.SetText(msg)
 }
 }
 
 
-// SetText draws the label text using the current font
-func (l *Label) SetText(msg string) {
+// SetText sets and draws the label text using the font.
+func (l *Label) SetText(text string) {
 
 
 	// Need at least a character to get dimensions
 	// Need at least a character to get dimensions
-	l.currentText = msg
-	if msg == "" {
-		msg = " "
+	l.text = text
+	if text == "" {
+		text = " "
 	}
 	}
 
 
 	// Set font properties
 	// 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(msg)
-	// 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, msg, l.font)
+	l.font.SetAttributes(&l.style.FontAttributes)
+	l.font.SetColor(&l.style.FgColor)
 
 
-	// Creates texture if if doesnt exist.
+	// Create an image with the text
+	textImage := l.font.DrawText(text)
+
+	// Create texture if it doesn't exist yet
 	if l.tex == nil {
 	if l.tex == nil {
-		l.tex = texture.NewTexture2DFromRGBA(canvas.RGBA)
+		l.tex = texture.NewTexture2DFromRGBA(textImage)
 		l.tex.SetMagFilter(gls.NEAREST)
 		l.tex.SetMagFilter(gls.NEAREST)
 		l.tex.SetMinFilter(gls.NEAREST)
 		l.tex.SetMinFilter(gls.NEAREST)
 		l.Panel.Material().AddTexture(l.tex)
 		l.Panel.Material().AddTexture(l.tex)
 		// Otherwise update texture with new image
 		// Otherwise update texture with new image
 	} else {
 	} else {
-		l.tex.SetFromRGBA(canvas.RGBA)
+		l.tex.SetFromRGBA(textImage)
 	}
 	}
 
 
-	// Updates label panel dimensions
-	l.Panel.SetContentSize(float32(width), float32(height))
+	// Update label panel dimensions
+	l.Panel.SetContentSize(float32(textImage.Rect.Dx()), float32(textImage.Rect.Dy()))
 }
 }
 
 
-// Text returns the current label text
+// Text returns the label text.
 func (l *Label) Text() string {
 func (l *Label) Text() string {
 
 
-	return l.currentText
+	return l.text
 }
 }
 
 
-// SetColor sets the color of the label text
-// The color alpha is set to 1.0
+// SetColor sets the text color.
+// Alpha is set to 1 (opaque).
 func (l *Label) SetColor(color *math32.Color) *Label {
 func (l *Label) SetColor(color *math32.Color) *Label {
 
 
-	l.fgColor.FromColor(color, 1.0)
-	l.SetText(l.currentText)
+	l.style.FgColor.FromColor(color, 1.0)
+	l.SetText(l.text)
 	return l
 	return l
 }
 }
 
 
-// SetColor4 sets the color4 of the label text
+// SetColor4 sets the text color.
 func (l *Label) SetColor4(color4 *math32.Color4) *Label {
 func (l *Label) SetColor4(color4 *math32.Color4) *Label {
 
 
-	l.fgColor = *color4
-	l.SetText(l.currentText)
+	l.style.FgColor = *color4
+	l.SetText(l.text)
 	return l
 	return l
 }
 }
 
 
-// Color returns the current color of the label text
+// Color returns the text color.
 func (l *Label) Color() math32.Color4 {
 func (l *Label) Color() math32.Color4 {
 
 
-	return l.fgColor
+	return l.style.FgColor
 }
 }
 
 
-// SetBgColor sets the color of the label background
+// SetBgColor sets the background color.
 // The color alpha is set to 1.0
 // The color alpha is set to 1.0
 func (l *Label) SetBgColor(color *math32.Color) *Label {
 func (l *Label) SetBgColor(color *math32.Color) *Label {
 
 
-	l.bgColor.FromColor(color, 1.0)
-	l.Panel.SetColor4(&l.bgColor)
-	l.SetText(l.currentText)
+	l.style.BgColor.FromColor(color, 1.0)
+	l.Panel.SetColor4(&l.style.BgColor)
+	l.SetText(l.text)
 	return l
 	return l
 }
 }
 
 
-// SetBgColor4 sets the color4 of the label background
+// SetBgColor4 sets the background color.
 func (l *Label) SetBgColor4(color *math32.Color4) *Label {
 func (l *Label) SetBgColor4(color *math32.Color4) *Label {
 
 
-	l.bgColor = *color
-	l.Panel.SetColor4(&l.bgColor)
-	l.SetText(l.currentText)
+	l.style.BgColor = *color
+	l.Panel.SetColor4(&l.style.BgColor)
+	l.SetText(l.text)
 	return l
 	return l
 }
 }
 
 
-// BgColor returns the current color the label background
+// BgColor returns returns the background color.
 func (l *Label) BgColor() math32.Color4 {
 func (l *Label) BgColor() math32.Color4 {
 
 
-	return l.bgColor
+	return l.style.BgColor
 }
 }
 
 
-// SetFont sets this label text or icon font
+// SetFont sets the font.
 func (l *Label) SetFont(f *text.Font) {
 func (l *Label) SetFont(f *text.Font) {
 
 
 	l.font = f
 	l.font = f
-	l.SetText(l.currentText)
+	l.SetText(l.text)
 }
 }
 
 
-// SetFontSize sets label font size
+// Font returns the font.
+func (l *Label) Font() *text.Font {
+
+	return l.font
+}
+
+// SetFontSize sets the point size of the font.
 func (l *Label) SetFontSize(size float64) *Label {
 func (l *Label) SetFontSize(size float64) *Label {
 
 
-	l.fontSize = size
-	l.SetText(l.currentText)
+	l.style.PointSize = size
+	l.SetText(l.text)
 	return l
 	return l
 }
 }
 
 
-// FontSize returns the current label font size
+// FontSize returns the point size of the font.
 func (l *Label) FontSize() float64 {
 func (l *Label) FontSize() float64 {
 
 
-	return l.fontSize
+	return l.style.PointSize
 }
 }
 
 
-// SetFontDPI sets the font dots per inch
+// SetFontDPI sets the resolution of the font in dots per inch (DPI).
 func (l *Label) SetFontDPI(dpi float64) *Label {
 func (l *Label) SetFontDPI(dpi float64) *Label {
 
 
-	l.fontDPI = dpi
-	l.SetText(l.currentText)
+	l.style.DPI = dpi
+	l.SetText(l.text)
 	return l
 	return l
 }
 }
 
 
+// FontDPI returns the resolution of the font in dots per inch (DPI).
+func (l *Label) FontDPI() float64 {
+
+	return l.style.DPI
+}
+
 // SetLineSpacing sets the spacing between lines.
 // SetLineSpacing sets the spacing between lines.
-// The default value is 1.0
 func (l *Label) SetLineSpacing(spacing float64) *Label {
 func (l *Label) SetLineSpacing(spacing float64) *Label {
 
 
-	l.lineSpacing = spacing
-	l.SetText(l.currentText)
+	l.style.LineSpacing = spacing
+	l.SetText(l.text)
 	return l
 	return l
 }
 }
 
 
+// LineSpacing returns the spacing between lines.
+func (l *Label) LineSpacing() float64 {
+
+	return l.style.LineSpacing
+}
+
 // setTextCaret sets the label text and draws a caret at the
 // setTextCaret sets the label text and draws a caret at the
 // specified line and column.
 // specified line and column.
 // It is normally used by the Edit widget.
 // It is normally used by the Edit widget.
 func (l *Label) setTextCaret(msg string, mx, width, line, col int) {
 func (l *Label) setTextCaret(msg string, mx, width, line, col int) {
 
 
 	// Set font properties
 	// 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)
+	l.font.SetAttributes(&l.style.FontAttributes)
+	l.font.SetColor(&l.style.FgColor)
 
 
 	// Create canvas and draw text
 	// Create canvas and draw text
 	_, height := l.font.MeasureText(msg)
 	_, height := l.font.MeasureText(msg)
-	canvas := text.NewCanvas(width, height, &l.bgColor)
+	canvas := text.NewCanvas(width, height, &l.style.BgColor)
 	canvas.DrawTextCaret(mx, 0, msg, l.font, line, col)
 	canvas.DrawTextCaret(mx, 0, msg, l.font, line, col)
 
 
 	// Creates texture if if doesnt exist.
 	// Creates texture if if doesnt exist.
@@ -214,5 +237,5 @@ func (l *Label) setTextCaret(msg string, mx, width, line, col int) {
 
 
 	// Updates label panel dimensions
 	// Updates label panel dimensions
 	l.Panel.SetContentSize(float32(width), float32(height))
 	l.Panel.SetContentSize(float32(width), float32(height))
-	l.currentText = msg
+	l.text = msg
 }
 }

+ 26 - 33
gui/list.go

@@ -5,12 +5,12 @@
 package gui
 package gui
 
 
 import (
 import (
-	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/window"
 	"github.com/g3n/engine/window"
 )
 )
 
 
+// List represents a list GUI element
 type List struct {
 type List struct {
-	Scroller             // Embedded scroller
+	ItemScroller         // Embedded scroller
 	styles   *ListStyles // Pointer to styles
 	styles   *ListStyles // Pointer to styles
 	single   bool        // Single selection flag (default is true)
 	single   bool        // Single selection flag (default is true)
 	focus    bool        // has keyboard focus
 	focus    bool        // has keyboard focus
@@ -19,8 +19,7 @@ type List struct {
 	keyPrev  window.Key  // Code of key to select previous item
 	keyPrev  window.Key  // Code of key to select previous item
 }
 }
 
 
-// All items inserted into the list are
-// encapsulated inside a ListItem
+// ListItem encapsulates each item inserted into the list
 type ListItem struct {
 type ListItem struct {
 	Panel               // Container panel
 	Panel               // Container panel
 	item        IPanel  // Original item
 	item        IPanel  // Original item
@@ -30,11 +29,13 @@ type ListItem struct {
 	list        *List   // Pointer to list
 	list        *List   // Pointer to list
 }
 }
 
 
+// ListStyles
 type ListStyles struct {
 type ListStyles struct {
-	Scroller *ScrollerStyles
+	Scroller *ItemScrollerStyles
 	Item     *ListItemStyles
 	Item     *ListItemStyles
 }
 }
 
 
+// ListItemStyles
 type ListItemStyles struct {
 type ListItemStyles struct {
 	Normal      ListItemStyle
 	Normal      ListItemStyle
 	Over        ListItemStyle
 	Over        ListItemStyle
@@ -43,15 +44,10 @@ type ListItemStyles struct {
 	SelHigh     ListItemStyle
 	SelHigh     ListItemStyle
 }
 }
 
 
-type ListItemStyle struct {
-	Border      BorderSizes
-	Paddings    BorderSizes
-	BorderColor math32.Color4
-	BgColor     math32.Color4
-	FgColor     math32.Color
-}
+// ListItemStyle
+type ListItemStyle BasicStyle
 
 
-// Event sent to list item child panel on resize
+// OnListItemResize is the identifier of the event dispatched when a ListItem's child panel is resized
 const OnListItemResize = "gui.OnListItemResize"
 const OnListItemResize = "gui.OnListItemResize"
 
 
 // NewVList creates and returns a pointer to a new vertical list panel
 // NewVList creates and returns a pointer to a new vertical list panel
@@ -82,12 +78,12 @@ func (li *List) initialize(vert bool, width, height float32) {
 	li.styles = &StyleDefault().List
 	li.styles = &StyleDefault().List
 	li.single = true
 	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)
-	li.Scroller.Subscribe(OnKeyRepeat, li.onKeyEvent)
+	li.ItemScroller.initialize(vert, width, height)
+	li.ItemScroller.SetStyles(li.styles.Scroller)
+	li.ItemScroller.adjustItem = true
+	li.ItemScroller.Subscribe(OnMouseDown, li.onMouseEvent)
+	li.ItemScroller.Subscribe(OnKeyDown, li.onKeyEvent)
+	li.ItemScroller.Subscribe(OnKeyRepeat, li.onKeyEvent)
 
 
 	if vert {
 	if vert {
 		li.keyNext = window.KeyDown
 		li.keyNext = window.KeyDown
@@ -116,7 +112,7 @@ func (li *List) Single() bool {
 func (li *List) SetStyles(s *ListStyles) {
 func (li *List) SetStyles(s *ListStyles) {
 
 
 	li.styles = s
 	li.styles = s
-	li.Scroller.SetStyles(li.styles.Scroller)
+	li.ItemScroller.SetStyles(li.styles.Scroller)
 	li.update()
 	li.update()
 }
 }
 
 
@@ -127,11 +123,11 @@ func (li *List) Add(item IPanel) {
 }
 }
 
 
 // InsertAt inserts a list item at the specified position
 // InsertAt inserts a list item at the specified position
-// Returs true if the item was successfuly inserted
+// Returs true if the item was successfully inserted
 func (li *List) InsertAt(pos int, item IPanel) {
 func (li *List) InsertAt(pos int, item IPanel) {
 
 
 	litem := newListItem(li, item)
 	litem := newListItem(li, item)
-	li.Scroller.InsertAt(pos, litem)
+	li.ItemScroller.InsertAt(pos, litem)
 	litem.Panel.Subscribe(OnMouseDown, litem.onMouse)
 	litem.Panel.Subscribe(OnMouseDown, litem.onMouse)
 	litem.Panel.Subscribe(OnCursorEnter, litem.onCursor)
 	litem.Panel.Subscribe(OnCursorEnter, litem.onCursor)
 }
 }
@@ -140,7 +136,7 @@ func (li *List) InsertAt(pos int, item IPanel) {
 func (li *List) RemoveAt(pos int) IPanel {
 func (li *List) RemoveAt(pos int) IPanel {
 
 
 	// Remove the list item from the internal scroller
 	// Remove the list item from the internal scroller
-	pan := li.Scroller.RemoveAt(pos)
+	pan := li.ItemScroller.RemoveAt(pos)
 	litem := pan.(*ListItem)
 	litem := pan.(*ListItem)
 
 
 	// Remove item from the list item children and disposes of the list item panel
 	// Remove item from the list item children and disposes of the list item panel
@@ -164,7 +160,7 @@ func (li *List) Remove(item IPanel) {
 // ItemAt returns the list item at the specified position
 // ItemAt returns the list item at the specified position
 func (li *List) ItemAt(pos int) IPanel {
 func (li *List) ItemAt(pos int) IPanel {
 
 
-	item := li.Scroller.ItemAt(pos)
+	item := li.ItemScroller.ItemAt(pos)
 	if item == nil {
 	if item == nil {
 		return nil
 		return nil
 	}
 	}
@@ -197,7 +193,7 @@ func (li *List) Selected() []IPanel {
 	return sel
 	return sel
 }
 }
 
 
-// Select selects or unselects the specified item
+// SetSelected selects or unselects the specified item
 func (li *List) SetSelected(item IPanel, state bool) {
 func (li *List) SetSelected(item IPanel, state bool) {
 
 
 	for _, curr := range li.items {
 	for _, curr := range li.items {
@@ -502,14 +498,14 @@ func (litem *ListItem) onCursor(evname string, ev interface{}) {
 	}
 	}
 }
 }
 
 
-// setSelected sets this item selected state
+// SetSelected sets this item selected state
 func (litem *ListItem) SetSelected(state bool) {
 func (litem *ListItem) SetSelected(state bool) {
 
 
 	litem.selected = state
 	litem.selected = state
 	//litem.item.SetSelected2(state)
 	//litem.item.SetSelected2(state)
 }
 }
 
 
-// setHighlighted sets this item selected state
+// SetHighlighted sets this item selected state
 func (litem *ListItem) SetHighlighted(state bool) {
 func (litem *ListItem) SetHighlighted(state bool) {
 
 
 	litem.highlighted = state
 	litem.highlighted = state
@@ -538,10 +534,7 @@ func (litem *ListItem) update() {
 // applyStyle applies the specified style to this ListItem
 // applyStyle applies the specified style to this ListItem
 func (litem *ListItem) applyStyle(s *ListItemStyle) {
 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)
+	styleCopy := s.PanelStyle
+	styleCopy.Padding.Left += litem.padLeft
+	litem.Panel.ApplyStyle(&styleCopy)
 }
 }

+ 12 - 26
gui/menu.go

@@ -8,6 +8,7 @@ import (
 	"github.com/g3n/engine/gui/assets/icon"
 	"github.com/g3n/engine/gui/assets/icon"
 	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/window"
 	"github.com/g3n/engine/window"
+
 	"time"
 	"time"
 )
 )
 
 
@@ -21,13 +22,7 @@ type Menu struct {
 }
 }
 
 
 // MenuBodyStyle describes the style of the menu body
 // MenuBodyStyle describes the style of the menu body
-type MenuBodyStyle struct {
-	Border      BorderSizes
-	Paddings    BorderSizes
-	BorderColor math32.Color4
-	BgColor     math32.Color
-	FgColor     math32.Color
-}
+type MenuBodyStyle BasicStyle
 
 
 // MenuBodyStyles describes all styles of the menu body
 // MenuBodyStyles describes all styles of the menu body
 type MenuBodyStyles struct {
 type MenuBodyStyles struct {
@@ -63,14 +58,11 @@ type MenuItem struct {
 
 
 // MenuItemStyle describes the style of a menu item
 // MenuItemStyle describes the style of a menu item
 type MenuItemStyle struct {
 type MenuItemStyle struct {
-	Border           BorderSizes
-	Paddings         BorderSizes
-	BorderColor      math32.Color4
-	BgColor          math32.Color
-	FgColor          math32.Color
-	IconPaddings     BorderSizes
-	ShortcutPaddings BorderSizes
-	RiconPaddings    BorderSizes
+	PanelStyle
+	FgColor          math32.Color4
+	IconPaddings     RectBounds
+	ShortcutPaddings RectBounds
+	RiconPaddings    RectBounds
 }
 }
 
 
 // MenuItemStyles describes all the menu item styles
 // MenuItemStyles describes all the menu item styles
@@ -206,7 +198,7 @@ func (m *Menu) AddMenu(text string, subm *Menu) *MenuItem {
 	mi.submenu.autoOpen = true
 	mi.submenu.autoOpen = true
 	mi.menu = m
 	mi.menu = m
 	if !m.bar {
 	if !m.bar {
-		mi.ricon = NewLabel(string(icon.PlayArrow), true)
+		mi.ricon = NewIcon(string(icon.PlayArrow))
 		mi.Panel.Add(mi.ricon)
 		mi.Panel.Add(mi.ricon)
 	}
 	}
 	mi.Panel.Add(mi.submenu)
 	mi.Panel.Add(mi.submenu)
@@ -478,10 +470,7 @@ func (m *Menu) update() {
 // applyStyle applies the specified menu body style
 // applyStyle applies the specified menu body style
 func (m *Menu) applyStyle(mbs *MenuBodyStyle) {
 func (m *Menu) applyStyle(mbs *MenuBodyStyle) {
 
 
-	m.SetBordersFrom(&mbs.Border)
-	m.SetBordersColor4(&mbs.BorderColor)
-	m.SetPaddingsFrom(&mbs.Paddings)
-	m.SetColor(&mbs.BgColor)
+	m.Panel.ApplyStyle(&mbs.PanelStyle)
 }
 }
 
 
 // recalc recalculates the positions of this menu internal items
 // recalc recalculates the positions of this menu internal items
@@ -605,7 +594,7 @@ func (mi *MenuItem) SetIcon(icon string) *MenuItem {
 		mi.licon = nil
 		mi.licon = nil
 	}
 	}
 	// Sets the new icon
 	// Sets the new icon
-	mi.licon = NewLabel(icon, true)
+	mi.licon = NewIcon(icon)
 	mi.Panel.Add(mi.licon)
 	mi.Panel.Add(mi.licon)
 	mi.update()
 	mi.update()
 	return mi
 	return mi
@@ -827,15 +816,12 @@ func (mi *MenuItem) update() {
 // applyStyle applies the specified menu item style
 // applyStyle applies the specified menu item style
 func (mi *MenuItem) applyStyle(mis *MenuItemStyle) {
 func (mi *MenuItem) applyStyle(mis *MenuItemStyle) {
 
 
-	mi.SetBordersFrom(&mis.Border)
-	mi.SetBordersColor4(&mis.BorderColor)
-	mi.SetPaddingsFrom(&mis.Paddings)
-	mi.SetColor(&mis.BgColor)
+	mi.Panel.ApplyStyle(&mis.PanelStyle)
 	if mi.licon != nil {
 	if mi.licon != nil {
 		mi.licon.SetPaddingsFrom(&mis.IconPaddings)
 		mi.licon.SetPaddingsFrom(&mis.IconPaddings)
 	}
 	}
 	if mi.label != nil {
 	if mi.label != nil {
-		mi.label.SetColor(&mis.FgColor)
+		mi.label.SetColor4(&mis.FgColor)
 	}
 	}
 	if mi.shortcut != nil {
 	if mi.shortcut != nil {
 		mi.shortcut.SetPaddingsFrom(&mis.ShortcutPaddings)
 		mi.shortcut.SetPaddingsFrom(&mis.ShortcutPaddings)

+ 89 - 52
gui/panel.go

@@ -46,7 +46,11 @@ type IPanel interface {
 	SetRoot(*Root)
 	SetRoot(*Root)
 	LostKeyFocus()
 	LostKeyFocus()
 	TotalHeight() float32
 	TotalHeight() float32
+	TotalWidth() float32
 	SetLayout(ILayout)
 	SetLayout(ILayout)
+	SetPosition(x, y float32)
+	SetPositionX(x float32)
+	SetPositionY(y float32)
 }
 }
 
 
 // Panel is 2D rectangular graphic which by default has a quad (2 triangles) geometry.
 // Panel is 2D rectangular graphic which by default has a quad (2 triangles) geometry.
@@ -54,30 +58,30 @@ type IPanel interface {
 // and a content area. The content area can be associated with a texture
 // and a content area. The content area can be associated with a texture
 // It is the building block of most GUI widgets.
 // It is the building block of most GUI widgets.
 type Panel struct {
 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
-	pospix           math32.Vector3     // absolute position in pixels
-	posclip          math32.Vector3     // position in clip (NDC) coordinates
-	wclip            float32            // width in clip coordinates
-	hclip            float32            // height in clip coordinates
-	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
-	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
-	uniMatrix        gls.Uniform        // model matrix uniform location cache
-	uniPanel         gls.Uniform        // panel parameters uniform location cache
+	*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  RectBounds         // external margin sizes in pixel coordinates
+	borderSizes  RectBounds         // border sizes in pixel coordinates
+	paddingSizes RectBounds         // padding sizes in pixel coordinates
+	content      Rect               // current content rectangle in pixel coordinates
+	pospix       math32.Vector3     // absolute position in pixels
+	posclip      math32.Vector3     // position in clip (NDC) coordinates
+	wclip        float32            // width in clip coordinates
+	hclip        float32            // height in clip coordinates
+	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
+	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
+	uniMatrix        gls.Uniform    // model matrix uniform location cache
+	uniPanel         gls.Uniform    // panel parameters uniform location cache
 	udata            struct {           // Combined uniform data 8 * vec4
 	udata            struct {           // Combined uniform data 8 * vec4
 		bounds        math32.Vector4 // panel bounds in texture coordinates
 		bounds        math32.Vector4 // panel bounds in texture coordinates
 		borders       math32.Vector4 // panel borders in texture coordinates
 		borders       math32.Vector4 // panel borders in texture coordinates
@@ -91,6 +95,22 @@ type Panel struct {
 	}
 	}
 }
 }
 
 
+// PanelStyle contains all the styling attributes of a Panel.
+type PanelStyle struct {
+	Margin      RectBounds
+	Border      RectBounds
+	Padding     RectBounds
+	BorderColor math32.Color4
+	BgColor     math32.Color4
+}
+
+// BasicStyle extends PanelStyle by adding a foreground color.
+// Many GUI components can be styled using BasicStyle or redeclared versions thereof (e.g. ButtonStyle)
+type BasicStyle struct {
+	PanelStyle
+	FgColor     math32.Color4
+}
+
 const (
 const (
 	deltaZ    = -0.000001      // delta Z for bounded panels
 	deltaZ    = -0.000001      // delta Z for bounded panels
 	deltaZunb = deltaZ * 10000 // delta Z for unbounded panels
 	deltaZunb = deltaZ * 10000 // delta Z for unbounded panels
@@ -180,9 +200,9 @@ func (p *Panel) InitializeGraphic(width, height float32, gr *graphic.Graphic) {
 
 
 // GetPanel satisfies the IPanel interface and
 // GetPanel satisfies the IPanel interface and
 // returns pointer to this panel
 // returns pointer to this panel
-func (pan *Panel) GetPanel() *Panel {
+func (p *Panel) GetPanel() *Panel {
 
 
-	return pan
+	return p
 }
 }
 
 
 // SetRoot satisfies the IPanel interface
 // SetRoot satisfies the IPanel interface
@@ -206,7 +226,14 @@ func (p *Panel) LostKeyFocus() {
 // height of this panel considering visible not bounded children
 // height of this panel considering visible not bounded children
 func (p *Panel) TotalHeight() float32 {
 func (p *Panel) TotalHeight() float32 {
 
 
-	return p.Height()
+	return p.height
+}
+
+// TotalWidth satisfies the IPanel interface and returns the total
+// width of this panel considering visible not bounded children
+func (p *Panel) TotalWidth() float32 {
+
+	return p.width
 }
 }
 
 
 // Material returns a pointer for this panel core.Material
 // Material returns a pointer for this panel core.Material
@@ -328,15 +355,15 @@ func (p *Panel) SetMargins(top, right, bottom, left float32) {
 }
 }
 
 
 // SetMarginsFrom sets this panel margins sizes from the specified
 // SetMarginsFrom sets this panel margins sizes from the specified
-// BorderSizes pointer and recalculates the panel external size
-func (p *Panel) SetMarginsFrom(src *BorderSizes) {
+// RectBounds pointer and recalculates the panel external size
+func (p *Panel) SetMarginsFrom(src *RectBounds) {
 
 
 	p.marginSizes = *src
 	p.marginSizes = *src
 	p.resize(p.calcWidth(), p.calcHeight(), true)
 	p.resize(p.calcWidth(), p.calcHeight(), true)
 }
 }
 
 
 // Margins returns the current margin sizes in pixels
 // Margins returns the current margin sizes in pixels
-func (p *Panel) Margins() BorderSizes {
+func (p *Panel) Margins() RectBounds {
 
 
 	return p.marginSizes
 	return p.marginSizes
 }
 }
@@ -350,15 +377,15 @@ func (p *Panel) SetBorders(top, right, bottom, left float32) {
 }
 }
 
 
 // SetBordersFrom sets this panel border sizes from the specified
 // SetBordersFrom sets this panel border sizes from the specified
-// BorderSizes pointer and recalculates the panel size
-func (p *Panel) SetBordersFrom(src *BorderSizes) {
+// RectBounds pointer and recalculates the panel size
+func (p *Panel) SetBordersFrom(src *RectBounds) {
 
 
 	p.borderSizes = *src
 	p.borderSizes = *src
 	p.resize(p.calcWidth(), p.calcHeight(), true)
 	p.resize(p.calcWidth(), p.calcHeight(), true)
 }
 }
 
 
 // Borders returns this panel current border sizes
 // Borders returns this panel current border sizes
-func (p *Panel) Borders() BorderSizes {
+func (p *Panel) Borders() RectBounds {
 
 
 	return p.borderSizes
 	return p.borderSizes
 }
 }
@@ -371,15 +398,15 @@ func (p *Panel) SetPaddings(top, right, bottom, left float32) {
 }
 }
 
 
 // SetPaddingsFrom sets this panel padding sizes from the specified
 // SetPaddingsFrom sets this panel padding sizes from the specified
-// BorderSizes pointer and recalculates the panel size
-func (p *Panel) SetPaddingsFrom(src *BorderSizes) {
+// RectBounds pointer and recalculates the panel size
+func (p *Panel) SetPaddingsFrom(src *RectBounds) {
 
 
 	p.paddingSizes = *src
 	p.paddingSizes = *src
 	p.resize(p.calcWidth(), p.calcHeight(), true)
 	p.resize(p.calcWidth(), p.calcHeight(), true)
 }
 }
 
 
 // Paddings returns this panel padding sizes in pixels
 // Paddings returns this panel padding sizes in pixels
-func (p *Panel) Paddings() BorderSizes {
+func (p *Panel) Paddings() RectBounds {
 
 
 	return p.paddingSizes
 	return p.paddingSizes
 }
 }
@@ -399,7 +426,7 @@ func (p *Panel) SetBordersColor4(color *math32.Color4) {
 	p.SetChanged(true)
 	p.SetChanged(true)
 }
 }
 
 
-// BorderColor4 returns current border color
+// BordersColor4 returns current border color
 func (p *Panel) BordersColor4() math32.Color4 {
 func (p *Panel) BordersColor4() math32.Color4 {
 
 
 	return p.udata.bordersColor
 	return p.udata.bordersColor
@@ -436,6 +463,18 @@ func (p *Panel) Color4() math32.Color4 {
 	return p.udata.contentColor
 	return p.udata.contentColor
 }
 }
 
 
+// ApplyStyle applies the provided PanelStyle to the panel
+func (p *Panel) ApplyStyle(ps *PanelStyle) {
+
+	p.udata.bordersColor = ps.BorderColor
+	p.udata.paddingsColor = ps.BgColor
+	p.udata.contentColor = ps.BgColor
+	p.marginSizes = ps.Margin
+	p.borderSizes = ps.Border
+	p.paddingSizes = ps.Padding
+	p.resize(p.calcWidth(), p.calcHeight(), true)
+}
+
 // SetContentSize sets this panel content size to the specified dimensions.
 // SetContentSize sets this panel content size to the specified dimensions.
 // The external size of the panel may increase or decrease to acomodate
 // The external size of the panel may increase or decrease to acomodate
 // the new content size.
 // the new content size.
@@ -445,14 +484,14 @@ func (p *Panel) SetContentSize(width, height float32) {
 }
 }
 
 
 // SetContentWidth sets this panel content width to the specified dimension in pixels.
 // 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
+// The external size of the panel may increase or decrease to accommodate the new width
 func (p *Panel) SetContentWidth(width float32) {
 func (p *Panel) SetContentWidth(width float32) {
 
 
 	p.SetContentSize(width, p.content.Height)
 	p.SetContentSize(width, p.content.Height)
 }
 }
 
 
 // SetContentHeight sets this panel content height to the specified dimension in pixels.
 // 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
+// The external size of the panel may increase or decrease to accommodate the new width
 func (p *Panel) SetContentHeight(height float32) {
 func (p *Panel) SetContentHeight(height float32) {
 
 
 	p.SetContentSize(p.content.Width, height)
 	p.SetContentSize(p.content.Width, height)
@@ -560,10 +599,8 @@ func (p *Panel) ContainsPosition(x, y float32) bool {
 // Unlike "ContainsPosition" is does not consider the panel margins.
 // Unlike "ContainsPosition" is does not consider the panel margins.
 func (p *Panel) InsideBorders(x, y float32) bool {
 func (p *Panel) InsideBorders(x, y float32) bool {
 
 
-	if x < (p.pospix.X+p.marginSizes.Left) || x >= (p.pospix.X+p.width-p.marginSizes.Right) {
-		return false
-	}
-	if y < (p.pospix.Y+p.marginSizes.Top) || y >= (p.pospix.Y+p.height-p.marginSizes.Bottom) {
+	if 	x < (p.pospix.X + p.marginSizes.Left) || x >= (p.pospix.X + p.width - p.marginSizes.Right) ||
+		y < (p.pospix.Y + p.marginSizes.Top) ||	y >= (p.pospix.Y + p.height - p.marginSizes.Bottom) {
 		return false
 		return false
 	}
 	}
 	return true
 	return true
@@ -701,16 +738,16 @@ func (p *Panel) setZ(z, zunb float32) (float32, float32) {
 			z, zunb = ichild.(IPanel).GetPanel().setZ(z, zunb)
 			z, zunb = ichild.(IPanel).GetPanel().setZ(z, zunb)
 		}
 		}
 		return z, zunb
 		return z, zunb
-		// Unbounded panel
-	} else {
-		p.SetPositionZ(zunb)
-		zchild := zunb + deltaZ
-		zunb += deltaZunb
-		for _, ichild := range p.Children() {
-			_, zunb = ichild.(IPanel).GetPanel().setZ(zchild, zunb)
-		}
-		return z, zunb
 	}
 	}
+
+	// Unbounded panel
+	p.SetPositionZ(zunb)
+	zchild := zunb + deltaZ
+	zunb += deltaZunb
+	for _, ichild := range p.Children() {
+		_, zunb = ichild.(IPanel).GetPanel().setZ(zchild, zunb)
+	}
+	return z, zunb
 }
 }
 
 
 // updateBounds is called by UpdateMatrixWorld() and calculates this panel
 // updateBounds is called by UpdateMatrixWorld() and calculates this panel

+ 33 - 25
gui/root.go

@@ -22,7 +22,7 @@ type Root struct {
 	mouseFocus        IPanel         // current child panel with mouse focus
 	mouseFocus        IPanel         // current child panel with mouse focus
 	scrollFocus       IPanel         // current child panel with scroll focus
 	scrollFocus       IPanel         // current child panel with scroll focus
 	modalPanel        IPanel         // current modal panel
 	modalPanel        IPanel         // current modal panel
-	targets           listPanelZ     // preallocated list of target panels
+	targets           []IPanel       // preallocated list of target panels
 }
 }
 
 
 const (
 const (
@@ -152,34 +152,44 @@ func (r *Root) StopPropagation(events int) {
 	r.stopPropagation |= events
 	r.stopPropagation |= events
 }
 }
 
 
-// SetCursorNormal sets the cursor of the associated window to
-// standard type
+// SetCursorNormal sets the cursor over the associated window to the standard type.
 func (r *Root) SetCursorNormal() {
 func (r *Root) SetCursorNormal() {
 
 
 	r.win.SetStandardCursor(window.ArrowCursor)
 	r.win.SetStandardCursor(window.ArrowCursor)
 }
 }
 
 
-// SetCursorDrag sets the cursor of the associated window to
-// drag type
-func (r *Root) SetCursorDrag() {
+// SetCursorText sets the cursor over the associated window to the I-Beam type.
+func (r *Root) SetCursorText() {
+
+	r.win.SetStandardCursor(window.IBeamCursor)
+}
+
+// SetCursorText sets the cursor over the associated window to the crosshair type.
+func (r *Root) SetCursorCrosshair() {
+
+	r.win.SetStandardCursor(window.CrosshairCursor)
+}
+
+// SetCursorHand sets the cursor over the associated window to the hand type.
+func (r *Root) SetCursorHand() {
 
 
 	r.win.SetStandardCursor(window.HandCursor)
 	r.win.SetStandardCursor(window.HandCursor)
 }
 }
 
 
-// SetCursorHResize sets the cursor of the associated window to
-// horizontal resize type
+// SetCursorHResize sets the cursor over the associated window to the horizontal resize type.
 func (r *Root) SetCursorHResize() {
 func (r *Root) SetCursorHResize() {
 
 
 	r.win.SetStandardCursor(window.HResizeCursor)
 	r.win.SetStandardCursor(window.HResizeCursor)
 }
 }
 
 
-// SetCursorVResize sets the cursor of the associated window to
-// vertical resize type
+// SetCursorVResize sets the cursor over the associated window to the vertical resize type.
 func (r *Root) SetCursorVResize() {
 func (r *Root) SetCursorVResize() {
 
 
 	r.win.SetStandardCursor(window.VResizeCursor)
 	r.win.SetStandardCursor(window.VResizeCursor)
 }
 }
 
 
+// TODO allow setting a custom cursor
+
 // onKey is called when key events are received
 // onKey is called when key events are received
 func (r *Root) onKey(evname string, ev interface{}) {
 func (r *Root) onKey(evname string, ev interface{}) {
 
 
@@ -234,8 +244,8 @@ func (r *Root) onCursor(evname string, ev interface{}) {
 	r.sendPanels(cev.Xpos, cev.Ypos, evname, ev)
 	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
+// sendPanel sends a mouse or cursor event to focused panel or panels
+// which contain the specified screen position
 func (r *Root) sendPanels(x, y float32, evname string, ev interface{}) {
 func (r *Root) sendPanels(x, y float32, evname string, ev interface{}) {
 
 
 	// If there is panel with MouseFocus send only to this panel
 	// If there is panel with MouseFocus send only to this panel
@@ -255,7 +265,7 @@ func (r *Root) sendPanels(x, y float32, evname string, ev interface{}) {
 	r.targets = r.targets[0:0]
 	r.targets = r.targets[0:0]
 
 
 	// checkPanel checks recursively if the specified panel and
 	// checkPanel checks recursively if the specified panel and
-	// any of its child contains the mouse position
+	// any of its children contain the mouse position
 	var checkPanel func(ipan IPanel)
 	var checkPanel func(ipan IPanel)
 	checkPanel = func(ipan IPanel) {
 	checkPanel = func(ipan IPanel) {
 		pan := ipan.GetPanel()
 		pan := ipan.GetPanel()
@@ -307,7 +317,12 @@ func (r *Root) sendPanels(x, y float32, evname string, ev interface{}) {
 
 
 	// Sorts panels by absolute Z with the most foreground panels first
 	// Sorts panels by absolute Z with the most foreground panels first
 	// and sends event to all panels or until a stop is requested
 	// and sends event to all panels or until a stop is requested
-	sort.Sort(r.targets)
+	sort.Slice(r.targets, func(i, j int) bool {
+		iz := r.targets[i].GetPanel().Position().Z
+		jz := r.targets[j].GetPanel().Position().Z
+		return iz < jz
+	})
+
 	r.stopPropagation = 0
 	r.stopPropagation = 0
 
 
 	// Send events to panels
 	// Send events to panels
@@ -401,14 +416,7 @@ func (r *Root) canDispatch(ipan IPanel) bool {
 	return checkChildren(r.modalPanel)
 	return checkChildren(r.modalPanel)
 }
 }
 
 
-// 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().Position().Z
-	jz := p[j].GetPanel().Position().Z
-	return iz < jz
-}
+//func (r *Root) applyStyleRecursively(s *Style) {
+//	// TODO
+//	// This should probably be in Panel ?
+//}

+ 68 - 31
gui/scrollbar.go

@@ -22,11 +22,13 @@ import (
 
 
 **/
 **/
 
 
+// ScrollBar is the scrollbar GUI element.
 type ScrollBar struct {
 type ScrollBar struct {
-	Panel                    // Embedded panel
-	style    *ScrollBarStyle // pointer to current style
-	vertical bool            // type of scrollbar
-	button   scrollBarButton // scrollbar button
+	Panel                       // Embedded panel
+	styles     *ScrollBarStyles // styles of the scrollbar
+	vertical   bool             // type of scrollbar
+	button     scrollBarButton  // scrollbar button
+	cursorOver bool
 }
 }
 
 
 type scrollBarButton struct {
 type scrollBarButton struct {
@@ -35,21 +37,23 @@ type scrollBarButton struct {
 	pressed bool       // mouse button pressed flag
 	pressed bool       // mouse button pressed flag
 	mouseX  float32    // last mouse click x position
 	mouseX  float32    // last mouse click x position
 	mouseY  float32    // last mouse click y position
 	mouseY  float32    // last mouse click y position
+	Size    float32    // button size
+	MinSize float32    // minimum button size
 }
 }
 
 
-type ScrollBarStyle struct {
-	Paddings     BorderSizes
-	Borders      BorderSizes
-	BordersColor math32.Color4
-	Color        math32.Color
-	Button       ScrollBarButtonStyle
+// ScrollBarStyles contains a ScrollBarStyle for each valid GUI state.
+type ScrollBarStyles struct {
+	Normal   ScrollBarStyle
+	Over     ScrollBarStyle
+	Disabled ScrollBarStyle
 }
 }
 
 
-type ScrollBarButtonStyle struct {
-	Borders      BorderSizes
-	BordersColor math32.Color4
-	Color        math32.Color
-	Size         float32
+// ScrollBarStyle contains the styling of a ScrollBar.
+type ScrollBarStyle struct {
+	PanelStyle
+	Button       PanelStyle
+	ButtonLength float32 // This is the default/minimum button length
+	// TODO ScrollSpeed ?
 }
 }
 
 
 // NewVScrollBar creates and returns a pointer to a new vertical scroll bar
 // NewVScrollBar creates and returns a pointer to a new vertical scroll bar
@@ -78,7 +82,7 @@ func newScrollBar(width, height float32, vertical bool) *ScrollBar {
 // initialize initializes this scrollbar
 // initialize initializes this scrollbar
 func (sb *ScrollBar) initialize(width, height float32, vertical bool) {
 func (sb *ScrollBar) initialize(width, height float32, vertical bool) {
 
 
-	sb.style = &StyleDefault().ScrollBar
+	sb.styles = &StyleDefault().ScrollBar
 	sb.vertical = vertical
 	sb.vertical = vertical
 	sb.Panel.Initialize(width, height)
 	sb.Panel.Initialize(width, height)
 	sb.Panel.Subscribe(OnMouseDown, sb.onMouse)
 	sb.Panel.Subscribe(OnMouseDown, sb.onMouse)
@@ -89,6 +93,7 @@ func (sb *ScrollBar) initialize(width, height float32, vertical bool) {
 	sb.button.Panel.Subscribe(OnMouseUp, sb.button.onMouse)
 	sb.button.Panel.Subscribe(OnMouseUp, sb.button.onMouse)
 	sb.button.Panel.Subscribe(OnCursor, sb.button.onCursor)
 	sb.button.Panel.Subscribe(OnCursor, sb.button.onCursor)
 	sb.button.SetMargins(1, 1, 1, 1)
 	sb.button.SetMargins(1, 1, 1, 1)
+	sb.button.Size = sb.styles.Normal.ButtonLength
 	sb.button.sb = sb
 	sb.button.sb = sb
 	sb.Add(&sb.button)
 	sb.Add(&sb.button)
 
 
@@ -96,15 +101,36 @@ func (sb *ScrollBar) initialize(width, height float32, vertical bool) {
 	sb.recalc()
 	sb.recalc()
 }
 }
 
 
+// SetButtonSize sets the button size
+func (sb *ScrollBar) SetButtonSize(size float32) {
+
+	// Clamp to minimum size if requested size smaller than minimum
+	if size > sb.button.MinSize {
+		sb.button.Size = size
+	} else {
+		sb.button.Size = sb.button.MinSize
+	}
+	sb.recalc()
+}
+
 // Value returns the current position of the button in the scrollbar
 // Value returns the current position of the button in the scrollbar
 // The returned value is between 0.0 and 1.0
 // The returned value is between 0.0 and 1.0
 func (sb *ScrollBar) Value() float64 {
 func (sb *ScrollBar) Value() float64 {
 
 
 	if sb.vertical {
 	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))
+		den := float64(sb.content.Height) - float64(sb.button.height)
+		if den == 0 {
+			return 0
+		}
+		return float64(sb.button.Position().Y) / den
 	}
 	}
+
+	// horizontal
+	den := float64(sb.content.Width) - float64(sb.button.width)
+	if den == 0 {
+		return 0
+	}
+	return float64(sb.button.Position().X) / den
 }
 }
 
 
 // SetValue sets the position of the button of the scrollbar
 // SetValue sets the position of the button of the scrollbar
@@ -145,23 +171,34 @@ func (sb *ScrollBar) onMouse(evname string, ev interface{}) {
 func (sb *ScrollBar) recalc() {
 func (sb *ScrollBar) recalc() {
 
 
 	if sb.vertical {
 	if sb.vertical {
-		sb.button.SetSize(sb.content.Width, sb.style.Button.Size)
+		sb.button.SetSize(sb.content.Width, sb.button.Size)
 	} else {
 	} else {
-		sb.button.SetSize(sb.style.Button.Size, sb.content.Height)
+		sb.button.SetSize(sb.button.Size, sb.content.Height)
 	}
 	}
 }
 }
 
 
-// update updates border sizes and colors
+// update updates the visual state
 func (sb *ScrollBar) update() {
 func (sb *ScrollBar) update() {
 
 
-	sb.SetBordersFrom(&sb.style.Borders)
-	sb.SetPaddingsFrom(&sb.style.Paddings)
-	sb.SetBordersColor4(&sb.style.BordersColor)
-	sb.SetColor(&sb.style.Color)
+	// TODO disabling the scrollbar only affects style, needs to affect behavior
+	if !sb.Enabled() {
+		sb.applyStyle(&sb.styles.Disabled)
+		return
+	}
+	// TODO cursorOver is never set to true for the scrollbar
+	if sb.cursorOver {
+		sb.applyStyle(&sb.styles.Over)
+		return
+	}
+	sb.applyStyle(&sb.styles.Normal)
+}
+
+// update updates border sizes and colors
+func (sb *ScrollBar) applyStyle(sbs *ScrollBarStyle) {
 
 
-	sb.button.SetBordersFrom(&sb.style.Button.Borders)
-	sb.button.SetBordersColor4(&sb.style.Button.BordersColor)
-	sb.button.SetColor(&sb.style.Button.Color)
+	sb.Panel.ApplyStyle(&sbs.PanelStyle)
+	sb.button.ApplyStyle(&sbs.Button)
+	sb.button.MinSize = sbs.ButtonLength
 }
 }
 
 
 // onMouse receives subscribed mouse events for the scroll bar button
 // onMouse receives subscribed mouse events for the scroll bar button
@@ -196,11 +233,11 @@ func (button *scrollBarButton) onCursor(evname string, ev interface{}) {
 	if button.sb.vertical {
 	if button.sb.vertical {
 		dy := button.mouseY - e.Ypos
 		dy := button.mouseY - e.Ypos
 		py := button.Position().Y
 		py := button.Position().Y
-		button.SetPositionY(math32.Clamp(py-dy, 0, button.sb.content.Height-button.sb.style.Button.Size))
+		button.SetPositionY(math32.Clamp(py-dy, 0, button.sb.content.Height-button.Size))
 	} else {
 	} else {
 		dx := button.mouseX - e.Xpos
 		dx := button.mouseX - e.Xpos
 		px := button.Position().X
 		px := button.Position().X
-		button.SetPositionX(math32.Clamp(px-dx, 0, button.sb.content.Width-button.sb.style.Button.Size))
+		button.SetPositionX(math32.Clamp(px-dx, 0, button.sb.content.Width-button.Size))
 	}
 	}
 	button.mouseX = e.Xpos
 	button.mouseX = e.Xpos
 	button.mouseY = e.Ypos
 	button.mouseY = e.Ypos

Разница между файлами не показана из-за своего большого размера
+ 442 - 450
gui/scroller.go


+ 5 - 14
gui/slider.go

@@ -5,7 +5,6 @@
 package gui
 package gui
 
 
 import (
 import (
-	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/window"
 	"github.com/g3n/engine/window"
 )
 )
 
 
@@ -23,6 +22,7 @@ import (
 
 
 **/
 **/
 
 
+// Slider is the GUI element for sliders and progress bars
 type Slider struct {
 type Slider struct {
 	Panel                     // Embedded panel
 	Panel                     // Embedded panel
 	slider      Panel         // embedded slider panel
 	slider      Panel         // embedded slider panel
@@ -36,16 +36,10 @@ type Slider struct {
 	scaleFactor float32       // scale factor (default = 1.0)
 	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
-}
+// SliderStyle contains the styling of a Slider
+type SliderStyle BasicStyle
 
 
-// All Slider styles
+// SliderStyles contains a SliderStyle for each valid GUI state
 type SliderStyles struct {
 type SliderStyles struct {
 	Normal   SliderStyle
 	Normal   SliderStyle
 	Over     SliderStyle
 	Over     SliderStyle
@@ -289,10 +283,7 @@ func (s *Slider) update() {
 // applyStyle applies the specified slider style
 // applyStyle applies the specified slider style
 func (s *Slider) applyStyle(ss *SliderStyle) {
 func (s *Slider) applyStyle(ss *SliderStyle) {
 
 
-	s.SetBordersColor4(&ss.BorderColor)
-	s.SetBordersFrom(&ss.Border)
-	s.SetPaddingsFrom(&ss.Paddings)
-	s.Panel.SetColor4(&ss.BgColor)
+	s.Panel.ApplyStyle(&ss.PanelStyle)
 	s.slider.SetColor4(&ss.FgColor)
 	s.slider.SetColor4(&ss.FgColor)
 }
 }
 
 

+ 5 - 2
gui/splitter.go

@@ -9,6 +9,7 @@ import (
 	"github.com/g3n/engine/window"
 	"github.com/g3n/engine/window"
 )
 )
 
 
+// Splitter is a GUI element that splits two panels and can be adjusted
 type Splitter struct {
 type Splitter struct {
 	Panel                     // Embedded panel
 	Panel                     // Embedded panel
 	P0        Panel           // Left/Top panel
 	P0        Panel           // Left/Top panel
@@ -22,12 +23,14 @@ type Splitter struct {
 	mouseOver bool            // mouse is over the spacer panel
 	mouseOver bool            // mouse is over the spacer panel
 }
 }
 
 
+// SplitterStyle contains the styling of a Splitter
 type SplitterStyle struct {
 type SplitterStyle struct {
 	SpacerBorderColor math32.Color4
 	SpacerBorderColor math32.Color4
-	SpacerColor       math32.Color
+	SpacerColor       math32.Color4
 	SpacerSize        float32
 	SpacerSize        float32
 }
 }
 
 
+// SplitterStyles contains a SplitterStyle for each valid GUI state
 type SplitterStyles struct {
 type SplitterStyles struct {
 	Normal SplitterStyle
 	Normal SplitterStyle
 	Over   SplitterStyle
 	Over   SplitterStyle
@@ -198,7 +201,7 @@ func (s *Splitter) update() {
 func (s *Splitter) applyStyle(ss *SplitterStyle) {
 func (s *Splitter) applyStyle(ss *SplitterStyle) {
 
 
 	s.spacer.SetBordersColor4(&ss.SpacerBorderColor)
 	s.spacer.SetBordersColor4(&ss.SpacerBorderColor)
-	s.spacer.SetColor(&ss.SpacerColor)
+	s.spacer.SetColor4(&ss.SpacerColor)
 	if s.horiz {
 	if s.horiz {
 		s.spacer.SetWidth(ss.SpacerSize)
 		s.spacer.SetWidth(ss.SpacerSize)
 	} else {
 	} else {

+ 20 - 3
gui/style.go

@@ -5,21 +5,25 @@
 package gui
 package gui
 
 
 import (
 import (
+	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/text"
 	"github.com/g3n/engine/text"
 )
 )
 
 
-// All styles
+// Style contains the styles for all GUI elements
 type Style struct {
 type Style struct {
+	Color         ColorStyle
 	Font          *text.Font
 	Font          *text.Font
 	FontIcon      *text.Font
 	FontIcon      *text.Font
+	Label         LabelStyle
 	Button        ButtonStyles
 	Button        ButtonStyles
 	CheckRadio    CheckRadioStyles
 	CheckRadio    CheckRadioStyles
 	Edit          EditStyles
 	Edit          EditStyles
-	ScrollBar     ScrollBarStyle
+	ScrollBar     ScrollBarStyles
 	Slider        SliderStyles
 	Slider        SliderStyles
 	Splitter      SplitterStyles
 	Splitter      SplitterStyles
 	Window        WindowStyles
 	Window        WindowStyles
-	Scroller      ScrollerStyles
+	ItemScroller  ItemScrollerStyles
+	Scroller      ScrollerStyle
 	List          ListStyles
 	List          ListStyles
 	DropDown      DropDownStyles
 	DropDown      DropDownStyles
 	Folder        FolderStyles
 	Folder        FolderStyles
@@ -31,6 +35,19 @@ type Style struct {
 	TabBar        TabBarStyles
 	TabBar        TabBarStyles
 }
 }
 
 
+// ColorStyle defines the main colors used.
+type ColorStyle struct {
+	BgDark    math32.Color4
+	BgMed     math32.Color4
+	BgNormal  math32.Color4
+	BgOver    math32.Color4
+	Highlight math32.Color4
+	Select    math32.Color4
+	Text      math32.Color4
+	TextDis   math32.Color4
+}
+
+// States that a GUI element can be in
 const (
 const (
 	StyleOver = iota + 1
 	StyleOver = iota + 1
 	StyleFocus
 	StyleFocus

+ 402 - 0
gui/style_dark.go

@@ -0,0 +1,402 @@
+// 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/gui/assets/icon"
+	"github.com/g3n/engine/math32"
+	"github.com/g3n/engine/text"
+)
+
+// NewLightStyle creates and returns a pointer to the a new "light" style
+func NewDarkStyle() *Style {
+
+	// Fonts to use
+	const fontName = "fonts/FreeSans.ttf"
+	const iconName = "fonts/MaterialIcons-Regular.ttf"
+	s := new(Style)
+
+	// Creates text font
+	fontData := assets.MustAsset(fontName)
+	font, err := text.NewFontFromData(fontData)
+	if err != nil {
+		panic(err)
+	}
+	s.Font = font
+
+	// Creates icon font
+	fontIconData := assets.MustAsset(iconName)
+	fontIcon, err := text.NewFontFromData(fontIconData)
+	if err != nil {
+		panic(err)
+	}
+	s.FontIcon = fontIcon
+
+	zeroBounds := RectBounds{0, 0, 0, 0}
+	oneBounds := RectBounds{1, 1, 1, 1}
+	twoBounds := RectBounds{2, 2, 2, 2}
+
+	s.Color.BgDark = math32.Color4{43.0/256.0, 43.0/256.0, 43.0/256.0, 1}
+	s.Color.BgMed = math32.Color4{49.0/256.0, 51.0/256.0, 53.0/256.0, 1}
+	s.Color.BgNormal = math32.Color4{60.0/256.0, 63.0/256.0, 65.0/256.0, 1}
+	s.Color.BgOver = math32.Color4{70.0/256.0, 74.0/256.0, 77.0/256.0, 1}
+	s.Color.Highlight = math32.Color4{75.0/256.0, 110.0/256.0, 175.0/256.0, 1}
+	s.Color.Select = math32.Color4{13.0/256.0, 41.0/256.0, 62.0/256.0, 1}
+	s.Color.Text = math32.Color4{1, 1, 1, 1}
+	s.Color.TextDis = math32.Color4{0.4, 0.4, 0.4, 1}
+
+	borderColor := s.Color.BgDark
+	transparent := math32.Color4{0, 0, 0, 0}
+
+	//bgColorBlue := math32.Color4{59.0/256.0, 71.0/256.0, 84.0/256.0, 1}
+	//bgColorBlue2 := math32.Color4{59.0/256.0, 71.0/256.0, 120.0/256.0, 1}
+	//bgColorBlueDark := math32.Color4{49.0/256.0, 59.0/256.0, 69.0/256.0, 1}
+	//bgColorGrey := math32.Color4{85.0/256.0, 85.0/256.0, 85.0/256.0, 1}
+
+	//bgColorOld := math32.Color4{0.85, 0.85, 0.85, 1}
+
+	// Label style
+	s.Label = LabelStyle{}
+	s.Label.FontAttributes = text.FontAttributes{}
+	s.Label.FontAttributes.PointSize = 14
+	s.Label.FontAttributes.DPI = 72
+	s.Label.FontAttributes.Hinting = text.HintingNone
+	s.Label.FontAttributes.LineSpacing = 1.0
+	s.Label.BgColor = math32.Color4{1,1,1, 0}
+	s.Label.FgColor = math32.Color4{1,1,1,1}
+
+	// Button styles
+	s.Button = ButtonStyles{}
+	s.Button.Normal = ButtonStyle{}
+	s.Button.Normal.Border = oneBounds
+	s.Button.Normal.Padding = RectBounds{2, 4, 2, 4}
+	s.Button.Normal.BorderColor = s.Color.BgDark
+	s.Button.Normal.BgColor = s.Color.BgMed
+	s.Button.Normal.FgColor = s.Color.Text
+	s.Button.Over = s.Button.Normal
+	s.Button.Over.BgColor = s.Color.BgOver
+	s.Button.Focus = s.Button.Over
+	s.Button.Pressed = s.Button.Over
+	s.Button.Pressed.Border = RectBounds{2, 2, 2, 2}
+	s.Button.Pressed.Padding = RectBounds{2, 2, 0, 4}
+	s.Button.Disabled = s.Button.Normal
+	s.Button.Disabled.BorderColor = s.Color.TextDis
+	s.Button.Disabled.FgColor = s.Color.TextDis
+
+	// CheckRadio styles
+	s.CheckRadio = CheckRadioStyles{}
+	s.CheckRadio.Normal = CheckRadioStyle{}
+	s.CheckRadio.Normal.BorderColor = borderColor
+	s.CheckRadio.Normal.BgColor = transparent
+	s.CheckRadio.Normal.FgColor = s.Color.Text
+	s.CheckRadio.Over = s.CheckRadio.Normal
+	s.CheckRadio.Over.BgColor = s.Color.BgOver
+	s.CheckRadio.Focus = s.CheckRadio.Over
+	s.CheckRadio.Disabled = s.CheckRadio.Normal
+	s.CheckRadio.Disabled.FgColor = s.Color.TextDis
+
+	// Edit styles
+	s.Edit = EditStyles{}
+	s.Edit.Normal = EditStyle{
+		Border:      oneBounds,
+		Paddings:    zeroBounds,
+		BorderColor: borderColor,
+		BgColor:     s.Color.BgMed,
+		BgAlpha:     1.0,
+		FgColor:     s.Color.Text,
+		HolderColor: math32.Color4{0.4, 0.4, 0.4, 1},
+	}
+	s.Edit.Over = s.Edit.Normal
+	s.Edit.Over.BgColor = s.Color.BgNormal
+	s.Edit.Focus = s.Edit.Normal
+	s.Edit.Disabled = s.Edit.Normal
+	s.Edit.Disabled.FgColor = s.Color.TextDis
+
+	// ScrollBar styles
+	s.ScrollBar = ScrollBarStyles{}
+	s.ScrollBar.Normal = ScrollBarStyle{}
+	s.ScrollBar.Normal.Padding = oneBounds
+	s.ScrollBar.Normal.BgColor = math32.Color4{0, 0, 0, 0.2}
+	s.ScrollBar.Normal.ButtonLength = 32
+	s.ScrollBar.Normal.Button = PanelStyle{
+		BgColor:     math32.Color4{0.8, 0.8, 0.8, 0.5},
+	}
+	s.ScrollBar.Over = s.ScrollBar.Normal
+	s.ScrollBar.Disabled = s.ScrollBar.Normal
+
+	// Slider styles
+	s.Slider = SliderStyles{}
+	s.Slider.Normal = SliderStyle{}
+	s.Slider.Normal.Border = oneBounds
+	s.Slider.Normal.BorderColor = borderColor
+	s.Slider.Normal.BgColor = s.Color.BgDark
+	s.Slider.Normal.FgColor = s.Color.Highlight //bgColorBlue2 //math32.Color4{0, 0.4, 0, 1}
+	s.Slider.Normal.FgColor.A = 0.5
+	s.Slider.Over = s.Slider.Normal
+	s.Slider.Over.BgColor = s.Color.BgNormal
+	s.Slider.Over.FgColor = s.Color.Highlight //math32.Color4{0, 0.5, 0, 1}
+	s.Slider.Focus = s.Slider.Over
+	s.Slider.Disabled = s.Slider.Normal
+
+	// Splitter styles
+	s.Splitter = SplitterStyles{}
+	s.Splitter.Normal = SplitterStyle{
+		SpacerBorderColor: borderColor,
+		SpacerColor:       s.Color.BgNormal,
+		SpacerSize:        6,
+	}
+	s.Splitter.Over = s.Splitter.Normal
+	s.Splitter.Over.SpacerColor = s.Color.BgOver
+	s.Splitter.Drag = s.Splitter.Over
+
+	// Window styles
+	s.Window = WindowStyles{}
+	s.Window.Normal = WindowStyle{
+		Border:           RectBounds{2, 2, 2, 2},
+		Paddings:         zeroBounds,
+		BorderColor:      s.Color.BgDark,
+		TitleBorders:     RectBounds{0, 0, 1, 0},
+		TitleBorderColor: math32.Color4{0, 0, 0, 1},
+		TitleBgColor:     s.Color.Select,
+		TitleFgColor:     s.Color.Text,
+	}
+	s.Window.Over = s.Window.Normal
+	s.Window.Focus = s.Window.Normal
+	s.Window.Disabled = s.Window.Normal
+
+	// ItemScroller styles
+	s.Scroller = ScrollerStyle{}
+	s.Scroller.VerticalScrollbar = ScrollerScrollbarStyle{}
+	s.Scroller.VerticalScrollbar.ScrollBarStyle = s.ScrollBar.Normal
+	s.Scroller.VerticalScrollbar.Broadness = 12
+	s.Scroller.VerticalScrollbar.Position = ScrollbarRight
+	s.Scroller.VerticalScrollbar.OverlapContent = true
+	s.Scroller.VerticalScrollbar.AutoSizeButton = true
+	s.Scroller.HorizontalScrollbar = s.Scroller.VerticalScrollbar
+	s.Scroller.HorizontalScrollbar.Position = ScrollbarBottom
+	s.Scroller.ScrollbarInterlocking = ScrollbarInterlockingNone
+	s.Scroller.CornerCovered = true
+	s.Scroller.CornerPanel = PanelStyle{}
+	s.Scroller.CornerPanel.BgColor = math32.Color4{0, 0, 0, 0.2}
+	s.Scroller.Border = oneBounds
+	s.Scroller.BorderColor = borderColor
+	s.Scroller.BgColor = s.Color.BgNormal
+
+	// ItemScroller styles
+	s.ItemScroller = ItemScrollerStyles{}
+	s.ItemScroller.Normal = ItemScrollerStyle{}
+	s.ItemScroller.Normal.Border = oneBounds
+	s.ItemScroller.Normal.BorderColor = borderColor
+	s.ItemScroller.Normal.BgColor = s.Color.BgNormal
+	s.ItemScroller.Normal.FgColor = s.Color.Text
+	s.ItemScroller.Over = s.ItemScroller.Normal
+	//s.ItemScroller.Over.BgColor = bgColorOver
+	s.ItemScroller.Focus = s.ItemScroller.Over
+	s.ItemScroller.Disabled = s.ItemScroller.Normal
+
+	// ItemList styles
+	s.List = ListStyles{}
+	s.List.Scroller = &s.ItemScroller
+	s.List.Item = &ListItemStyles{}
+	s.List.Item.Normal = ListItemStyle{}
+	s.List.Item.Normal.Border = RectBounds{0, 0, 1, 0}
+	s.List.Item.Normal.Padding = RectBounds{0, 0, 0, 2}
+	s.List.Item.Normal.BorderColor = math32.Color4{0, 0, 0, 0}
+	s.List.Item.Normal.BgColor = transparent
+	s.List.Item.Normal.FgColor = s.Color.Text
+	s.List.Item.Over = s.List.Item.Normal
+	s.List.Item.Over.BgColor = s.Color.BgOver
+	s.List.Item.Over.FgColor = s.Color.Select
+	s.List.Item.Selected = s.List.Item.Normal
+	s.List.Item.Selected.BgColor = s.Color.Highlight
+	s.List.Item.Selected.FgColor = s.Color.Select
+	s.List.Item.Highlighted = s.List.Item.Normal
+	s.List.Item.Highlighted.BorderColor = math32.Color4{0, 0, 0, 1}
+	s.List.Item.Highlighted.BgColor = s.Color.BgOver
+	s.List.Item.Highlighted.FgColor = s.Color.Text
+	s.List.Item.SelHigh = s.List.Item.Highlighted
+	s.List.Item.SelHigh.BgColor = s.Color.BgNormal
+	s.List.Item.SelHigh.FgColor = s.Color.Select
+
+	// DropDown styles
+	s.DropDown = DropDownStyles{}
+	s.DropDown.Normal = DropDownStyle{}
+	s.DropDown.Normal.Border = oneBounds
+	s.DropDown.Normal.Padding = RectBounds{0, 0, 0, 2}
+	s.DropDown.Normal.BorderColor = borderColor
+	s.DropDown.Normal.BgColor = s.Color.BgNormal
+	s.DropDown.Normal.FgColor = s.Color.Text
+	s.DropDown.Over = s.DropDown.Normal
+	s.DropDown.Over.BgColor = s.Color.BgOver
+	s.DropDown.Focus = s.DropDown.Over
+	s.DropDown.Disabled = s.DropDown.Normal
+
+	// Folder styles
+	s.Folder = FolderStyles{}
+	s.Folder.Normal = FolderStyle{}
+	s.Folder.Normal.Border = oneBounds
+	s.Folder.Normal.Padding = RectBounds{2, 0, 2, 2}
+	s.Folder.Normal.BorderColor = borderColor
+	s.Folder.Normal.BgColor = s.Color.BgNormal
+	s.Folder.Normal.FgColor = s.Color.Text
+	s.Folder.Normal.Icons = [2]string{icon.ExpandMore, icon.ExpandLess}
+	s.Folder.Over = s.Folder.Normal
+	s.Folder.Over.BgColor = s.Color.BgOver
+	s.Folder.Focus = s.Folder.Over
+	s.Folder.Focus.Padding = twoBounds
+	s.Folder.Disabled = s.Folder.Focus
+
+	// Tree styles
+	s.Tree = TreeStyles{}
+	s.Tree.Padlevel = 28.0
+	s.Tree.List = &s.List
+	s.Tree.Node = &TreeNodeStyles{}
+	s.Tree.Node.Normal = TreeNodeStyle{}
+	s.Tree.Node.Normal.BorderColor = borderColor
+	s.Tree.Node.Normal.BgColor = transparent
+	s.Tree.Node.Normal.FgColor = s.Color.Text
+	s.Tree.Node.Normal.Icons = [2]string{icon.ExpandMore, icon.ExpandLess}
+
+	// ControlFolder styles
+	s.ControlFolder = ControlFolderStyles{}
+	s.ControlFolder.Folder = &FolderStyles{}
+	s.ControlFolder.Folder.Normal = s.Folder.Normal
+	s.ControlFolder.Folder.Normal.BorderColor = math32.Color4{0, 0, 0, 0}
+	s.ControlFolder.Folder.Normal.BgColor = math32.Color4{0, 0.5, 1, 1}
+	s.ControlFolder.Folder.Over = s.ControlFolder.Folder.Normal
+	s.ControlFolder.Folder.Focus = s.ControlFolder.Folder.Normal
+	s.ControlFolder.Folder.Focus.Padding = twoBounds
+	s.ControlFolder.Folder.Disabled = s.ControlFolder.Folder.Focus
+	s.ControlFolder.Tree = &TreeStyles{}
+	s.ControlFolder.Tree.Padlevel = 2.0
+	s.ControlFolder.Tree.List = &ListStyles{}
+	scrollerStylesCopy := *s.List.Scroller
+	s.ControlFolder.Tree.List.Scroller = &scrollerStylesCopy
+	s.ControlFolder.Tree.List.Scroller.Normal.Padding = RectBounds{0, 2, 0, 0}
+	s.ControlFolder.Tree.List.Scroller.Over.Padding = RectBounds{0, 2, 0, 0}
+	s.ControlFolder.Tree.List.Scroller.Focus.Padding = RectBounds{0, 2, 0, 0}
+	s.ControlFolder.Tree.List.Scroller.Disabled.Padding = RectBounds{0, 2, 0, 0}
+	s.ControlFolder.Tree.List.Item = s.List.Item
+	s.ControlFolder.Tree.Node = &TreeNodeStyles{}
+	s.ControlFolder.Tree.Node.Normal = s.Tree.Node.Normal
+
+	// Menu styles
+	s.Menu = MenuStyles{}
+	s.Menu.Body = &MenuBodyStyles{}
+	s.Menu.Body.Normal = MenuBodyStyle{}
+	s.Menu.Body.Normal.Border = oneBounds
+	s.Menu.Body.Normal.Padding = twoBounds
+	s.Menu.Body.Normal.BorderColor = borderColor
+	s.Menu.Body.Normal.BgColor = s.Color.BgNormal
+	s.Menu.Body.Normal.FgColor = s.Color.Text
+	s.Menu.Body.Over = s.Menu.Body.Normal
+	s.Menu.Body.Over.BgColor = s.Color.BgOver
+	s.Menu.Body.Focus = s.Menu.Body.Over
+	s.Menu.Body.Disabled = s.Menu.Body.Normal
+	s.Menu.Item = &MenuItemStyles{}
+	s.Menu.Item.Normal = MenuItemStyle{}
+	s.Menu.Item.Normal.Padding = RectBounds{2, 4, 2, 2}
+	s.Menu.Item.Normal.BorderColor = borderColor
+	s.Menu.Item.Normal.BgColor = s.Color.BgNormal
+	s.Menu.Item.Normal.FgColor = s.Color.Text
+	s.Menu.Item.Normal.IconPaddings = RectBounds{0, 6, 0, 4}
+	s.Menu.Item.Normal.ShortcutPaddings = RectBounds{0, 0, 0, 10}
+	s.Menu.Item.Normal.RiconPaddings = RectBounds{2, 0, 0, 4}
+	s.Menu.Item.Over = s.Menu.Item.Normal
+	s.Menu.Item.Over.BgColor = s.Color.Highlight
+	s.Menu.Item.Disabled = s.Menu.Item.Normal
+	s.Menu.Item.Disabled.FgColor = s.Color.TextDis
+	s.Menu.Item.Separator = MenuItemStyle{}
+	s.Menu.Item.Separator.Border = twoBounds
+	s.Menu.Item.Separator.Padding = zeroBounds
+	s.Menu.Item.Separator.BorderColor = math32.Color4{0, 0, 0, 0}
+	s.Menu.Item.Separator.BgColor = math32.Color4{0.6, 0.6, 0.6, 1}
+	s.Menu.Item.Separator.FgColor = s.Color.Text
+
+	// Table styles
+	s.Table = TableStyles{}
+	s.Table.Header = TableHeaderStyle{}
+	s.Table.Header.Border = RectBounds{0, 1, 1, 0}
+	s.Table.Header.Padding = twoBounds
+	s.Table.Header.BorderColor = s.Color.BgNormal
+	s.Table.Header.BgColor = s.Color.BgDark
+	s.Table.Header.FgColor = s.Color.Text
+	s.Table.RowEven = TableRowStyle{}
+	s.Table.RowEven.Border = RectBounds{0, 1, 1, 0}
+	s.Table.RowEven.Padding = twoBounds
+	s.Table.RowEven.BorderColor = s.Color.BgDark
+	s.Table.RowEven.BgColor = s.Color.BgNormal
+	s.Table.RowEven.FgColor = s.Color.Text
+	s.Table.RowOdd = s.Table.RowEven
+	s.Table.RowOdd.BgColor = s.Color.BgMed
+	s.Table.RowCursor = s.Table.RowEven
+	s.Table.RowCursor.BgColor = s.Color.Highlight
+	s.Table.RowSel = s.Table.RowEven
+	s.Table.RowSel.BgColor = s.Color.Select
+	s.Table.Status = TableStatusStyle{}
+	s.Table.Status.Border = RectBounds{1, 0, 0, 0}
+	s.Table.Status.Padding = twoBounds
+	s.Table.Status.BorderColor = borderColor
+	s.Table.Status.BgColor = s.Color.BgDark
+	s.Table.Status.FgColor = s.Color.Text
+	s.Table.Resizer = TableResizerStyle{
+		Width:       4,
+		Border:      zeroBounds,
+		BorderColor: borderColor,
+		BgColor:     math32.Color4{0.4, 0.4, 0.4, 0.6},
+	}
+
+	// ImageButton styles
+	s.ImageButton = ImageButtonStyles{}
+	s.ImageButton.Normal = ImageButtonStyle{}
+	s.ImageButton.Normal.BgColor = transparent
+	s.ImageButton.Normal.FgColor = s.Color.Text
+	s.ImageButton.Over = s.ImageButton.Normal
+	s.ImageButton.Over.BgColor = s.Color.BgOver
+	s.ImageButton.Focus = s.ImageButton.Over
+	s.ImageButton.Pressed = s.ImageButton.Over
+	s.ImageButton.Pressed.Border = oneBounds
+	s.ImageButton.Disabled = s.ImageButton.Normal
+	s.ImageButton.Disabled.FgColor = s.Color.TextDis
+
+	// TabBar styles
+	s.TabBar = TabBarStyles{
+		SepHeight:          1,
+		ListButtonIcon:     icon.MoreVert,
+		ListButtonPaddings: RectBounds{2, 4, 0, 0},
+	}
+	s.TabBar.Normal = TabBarStyle{}
+	s.TabBar.Normal.Border = oneBounds
+	s.TabBar.Normal.Padding = RectBounds{2, 0, 0, 0}
+	s.TabBar.Normal.BorderColor = borderColor
+	s.TabBar.Normal.BgColor = s.Color.BgMed
+	s.TabBar.Over = s.TabBar.Normal
+	//s.TabBar.Over.BgColor = s.Color.BgOver
+	s.TabBar.Focus = s.TabBar.Normal
+	s.TabBar.Focus.BgColor = transparent
+	s.TabBar.Disabled = s.TabBar.Focus
+	s.TabBar.Tab = TabStyles{
+		IconPaddings:  RectBounds{2, 2, 0, 0},
+		ImagePaddings: RectBounds{0, 2, 0, 0},
+		IconClose:     icon.Clear,
+	}
+	s.TabBar.Tab.Normal = TabStyle{}
+	s.TabBar.Tab.Normal.Margin = RectBounds{0, 2, 0, 2}
+	s.TabBar.Tab.Normal.Border = RectBounds{1, 1, 0, 1}
+	s.TabBar.Tab.Normal.Padding = twoBounds
+	s.TabBar.Tab.Normal.BorderColor = borderColor
+	s.TabBar.Tab.Normal.BgColor = s.Color.BgNormal
+	s.TabBar.Tab.Normal.FgColor = s.Color.Text
+	s.TabBar.Tab.Over = s.TabBar.Tab.Normal
+	s.TabBar.Tab.Over.BgColor = s.Color.BgOver
+	s.TabBar.Tab.Focus = s.TabBar.Tab.Normal
+	s.TabBar.Tab.Focus.BgColor = transparent
+	s.TabBar.Tab.Disabled = s.TabBar.Tab.Focus
+	s.TabBar.Tab.Selected = s.TabBar.Tab.Normal
+	s.TabBar.Tab.Selected.BgColor = s.Color.BgOver
+
+	return s
+}

+ 3 - 3
gui/style_default.go

@@ -6,13 +6,13 @@ package gui
 
 
 var defaultStyle *Style
 var defaultStyle *Style
 
 
-// Sets the default style
+// init sets the default style
 func init() {
 func init() {
 
 
-	defaultStyle = NewLightStyle()
+	defaultStyle = NewDarkStyle()
 }
 }
 
 
-// StyleDefault() returns a pointer to the current default style
+// StyleDefault returns a pointer to the current default style
 func StyleDefault() *Style {
 func StyleDefault() *Style {
 
 
 	return defaultStyle
 	return defaultStyle

Разница между файлами не показана из-за своего большого размера
+ 318 - 794
gui/style_light.go


+ 17 - 37
gui/tabbar.go

@@ -7,7 +7,6 @@ package gui
 import (
 import (
 	"fmt"
 	"fmt"
 
 
-	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/window"
 	"github.com/g3n/engine/window"
 )
 )
 
 
@@ -26,18 +25,13 @@ type TabBar struct {
 }
 }
 
 
 // TabBarStyle describes the style of the TabBar
 // TabBarStyle describes the style of the TabBar
-type TabBarStyle struct {
-	Border      BorderSizes   // Border sizes
-	Paddings    BorderSizes   // Padding sizes
-	BorderColor math32.Color4 // Border color
-	BgColor     math32.Color4 // Background color
-}
+type TabBarStyle BasicStyle
 
 
 // TabBarStyles describes all the TabBarStyles
 // TabBarStyles describes all the TabBarStyles
 type TabBarStyles struct {
 type TabBarStyles struct {
 	SepHeight          float32     // Separator width
 	SepHeight          float32     // Separator width
 	ListButtonIcon     string      // Icon for list button
 	ListButtonIcon     string      // Icon for list button
-	ListButtonPaddings BorderSizes // Paddings for list button
+	ListButtonPaddings RectBounds  // Paddings for list button
 	Normal             TabBarStyle // Style for normal exhibition
 	Normal             TabBarStyle // Style for normal exhibition
 	Over               TabBarStyle // Style when cursor is over the TabBar
 	Over               TabBarStyle // Style when cursor is over the TabBar
 	Focus              TabBarStyle // Style when the TabBar has key focus
 	Focus              TabBarStyle // Style when the TabBar has key focus
@@ -46,25 +40,18 @@ type TabBarStyles struct {
 }
 }
 
 
 // TabStyle describes the style of the individual Tabs header
 // TabStyle describes the style of the individual Tabs header
-type TabStyle struct {
-	Margins     BorderSizes   // Tab header margins
-	Border      BorderSizes   // Tab header borders
-	Paddings    BorderSizes   // Tab header paddings
-	BorderColor math32.Color4 // Tab header border color
-	BgColor     math32.Color4 // Tab header background color
-	FgColor     math32.Color  // Tab header color for icon and text
-}
+type TabStyle BasicStyle
 
 
 // TabStyles describes all Tab styles
 // TabStyles describes all Tab styles
 type TabStyles struct {
 type TabStyles struct {
-	IconPaddings  BorderSizes // Paddings for optional icon
-	ImagePaddings BorderSizes // Paddings for optional image
-	IconClose     string      // Codepoint for close icon in Tab header
-	Normal        TabStyle    // Style for normal exhibition
-	Over          TabStyle    // Style when cursor is over the Tab
-	Focus         TabStyle    // Style when the Tab has key focus
-	Disabled      TabStyle    // Style when the Tab is disabled
-	Selected      TabStyle    // Style when the Tab is selected
+	IconPaddings  RectBounds // Paddings for optional icon
+	ImagePaddings RectBounds // Paddings for optional image
+	IconClose     string     // Codepoint for close icon in Tab header
+	Normal        TabStyle   // Style for normal exhibition
+	Over          TabStyle   // Style when cursor is over the Tab
+	Focus         TabStyle   // Style when the Tab has key focus
+	Disabled      TabStyle   // Style when the Tab is disabled
+	Selected      TabStyle   // Style when the Tab is selected
 }
 }
 
 
 // NewTabBar creates and returns a pointer to a new TabBar widget
 // NewTabBar creates and returns a pointer to a new TabBar widget
@@ -91,7 +78,7 @@ func NewTabBar(width, height float32) *TabBar {
 	tb.Add(tb.list)
 	tb.Add(tb.list)
 
 
 	// Creates list icon button
 	// Creates list icon button
-	tb.listButton = NewLabel(tb.styles.ListButtonIcon, true)
+	tb.listButton = NewIcon(tb.styles.ListButtonIcon)
 	tb.listButton.SetPaddingsFrom(&tb.styles.ListButtonPaddings)
 	tb.listButton.SetPaddingsFrom(&tb.styles.ListButtonPaddings)
 	tb.listButton.Subscribe(OnMouseDown, tb.onListButton)
 	tb.listButton.Subscribe(OnMouseDown, tb.onListButton)
 	tb.Add(tb.listButton)
 	tb.Add(tb.listButton)
@@ -293,10 +280,7 @@ func (tb *TabBar) onListChange(evname string, ev interface{}) {
 // applyStyle applies the specified TabBar style
 // applyStyle applies the specified TabBar style
 func (tb *TabBar) applyStyle(s *TabBarStyle) {
 func (tb *TabBar) applyStyle(s *TabBarStyle) {
 
 
-	tb.SetBordersFrom(&s.Border)
-	tb.SetBordersColor4(&s.BorderColor)
-	tb.SetPaddingsFrom(&s.Paddings)
-	tb.SetColor4(&s.BgColor)
+	tb.Panel.ApplyStyle(&s.PanelStyle)
 	tb.separator.SetColor4(&s.BorderColor)
 	tb.separator.SetColor4(&s.BorderColor)
 }
 }
 
 
@@ -425,7 +409,7 @@ func newTab(text string, tb *TabBar, styles *TabStyles) *Tab {
 	// Setup the header panel
 	// Setup the header panel
 	tab.header.Initialize(0, 0)
 	tab.header.Initialize(0, 0)
 	tab.label = NewLabel(text)
 	tab.label = NewLabel(text)
-	tab.iconClose = NewLabel(styles.IconClose, true)
+	tab.iconClose = NewIcon(styles.IconClose)
 	tab.header.Add(tab.label)
 	tab.header.Add(tab.label)
 	tab.header.Add(tab.iconClose)
 	tab.header.Add(tab.iconClose)
 	// Creates the bottom panel
 	// Creates the bottom panel
@@ -509,7 +493,7 @@ func (tab *Tab) SetIcon(icon string) *Tab {
 	}
 	}
 	// Creates or updates icon
 	// Creates or updates icon
 	if tab.icon == nil {
 	if tab.icon == nil {
-		tab.icon = NewLabel(icon, true)
+		tab.icon = NewIcon(icon)
 		tab.icon.SetPaddingsFrom(&tab.styles.IconPaddings)
 		tab.icon.SetPaddingsFrom(&tab.styles.IconPaddings)
 		tab.header.Add(tab.icon)
 		tab.header.Add(tab.icon)
 	} else {
 	} else {
@@ -626,11 +610,7 @@ func (tab *Tab) minWidth() float32 {
 // applyStyle applies the specified Tab style to the Tab header
 // applyStyle applies the specified Tab style to the Tab header
 func (tab *Tab) applyStyle(s *TabStyle) {
 func (tab *Tab) applyStyle(s *TabStyle) {
 
 
-	tab.header.SetMarginsFrom(&s.Margins)
-	tab.header.SetBordersFrom(&s.Border)
-	tab.header.SetBordersColor4(&s.BorderColor)
-	tab.header.SetPaddingsFrom(&s.Paddings)
-	tab.header.SetColor4(&s.BgColor)
+	tab.header.GetPanel().ApplyStyle(&s.PanelStyle)
 }
 }
 
 
 // update updates the Tab header visual style
 // update updates the Tab header visual style
@@ -657,7 +637,7 @@ func (tab *Tab) setBottomPanel() {
 
 
 	if tab.selected {
 	if tab.selected {
 		bwidth := tab.header.ContentWidth() + tab.header.Paddings().Left + tab.header.Paddings().Right
 		bwidth := tab.header.ContentWidth() + tab.header.Paddings().Left + tab.header.Paddings().Right
-		bx := tab.styles.Selected.Margins.Left + tab.styles.Selected.Border.Left
+		bx := tab.styles.Selected.Margin.Left + tab.styles.Selected.Border.Left
 		tab.bottom.SetSize(bwidth, tab.tb.styles.SepHeight)
 		tab.bottom.SetSize(bwidth, tab.tb.styles.SepHeight)
 		tab.bottom.SetPosition(bx, tab.header.Height())
 		tab.bottom.SetPosition(bx, tab.header.Height())
 	}
 	}

+ 24 - 60
gui/table.go

@@ -106,55 +106,31 @@ type TableCell struct {
 type TableFormatFunc func(cell TableCell) string
 type TableFormatFunc func(cell TableCell) string
 
 
 // TableHeaderStyle describes the style of the table header
 // TableHeaderStyle describes the style of the table header
-type TableHeaderStyle struct {
-	Border      BorderSizes
-	Paddings    BorderSizes
-	BorderColor math32.Color4
-	BgColor     math32.Color
-	FgColor     math32.Color
-}
+type TableHeaderStyle BasicStyle
 
 
 // TableRowStyle describes the style of the table row
 // TableRowStyle describes the style of the table row
-type TableRowStyle struct {
-	Border      BorderSizes
-	Paddings    BorderSizes
-	BorderColor math32.Color4
-	BgColor     math32.Color
-	FgColor     math32.Color
-}
-
-// TableRowStyles describes all styles for the table row
-type TableRowStyles struct {
-	Normal   TableRowStyle
-	Selected TableRowStyle
-}
+type TableRowStyle BasicStyle
 
 
 // TableStatusStyle describes the style of the table status line panel
 // TableStatusStyle describes the style of the table status line panel
-type TableStatusStyle struct {
-	Border      BorderSizes
-	Paddings    BorderSizes
-	BorderColor math32.Color4
-	BgColor     math32.Color
-	FgColor     math32.Color
-}
+type TableStatusStyle BasicStyle
 
 
 // TableResizerStyle describes the style of the table resizer panel
 // TableResizerStyle describes the style of the table resizer panel
 type TableResizerStyle struct {
 type TableResizerStyle struct {
 	Width       float32
 	Width       float32
-	Border      BorderSizes
+	Border      RectBounds
 	BorderColor math32.Color4
 	BorderColor math32.Color4
 	BgColor     math32.Color4
 	BgColor     math32.Color4
 }
 }
 
 
 // TableStyles describes all styles of the table header and rows
 // TableStyles describes all styles of the table header and rows
 type TableStyles struct {
 type TableStyles struct {
-	Header    *TableHeaderStyle
-	RowEven   *TableRowStyle
-	RowOdd    *TableRowStyle
-	RowCursor *TableRowStyle
-	RowSel    *TableRowStyle
-	Status    *TableStatusStyle
-	Resizer   *TableResizerStyle
+	Header    TableHeaderStyle
+	RowEven   TableRowStyle
+	RowOdd    TableRowStyle
+	RowCursor TableRowStyle
+	RowSel    TableRowStyle
+	Status    TableStatusStyle
+	Resizer   TableResizerStyle
 }
 }
 
 
 // TableClickEvent describes a mouse click event over a table
 // TableClickEvent describes a mouse click event over a table
@@ -259,7 +235,7 @@ func NewTable(width, height float32, cols []TableColumn) (*Table, error) {
 		c.resize = cdesc.Resize
 		c.resize = cdesc.Resize
 		// Adds optional sort icon
 		// Adds optional sort icon
 		if c.sort != TableSortNone {
 		if c.sort != TableSortNone {
-			c.ricon = NewLabel(string(tableSortedNoneIcon), true)
+			c.ricon = NewIcon(string(tableSortedNoneIcon))
 			c.Add(c.ricon)
 			c.Add(c.ricon)
 			c.ricon.Subscribe(OnMouseDown, func(evname string, ev interface{}) {
 			c.ricon.Subscribe(OnMouseDown, func(evname string, ev interface{}) {
 				t.onRicon(evname, c)
 				t.onRicon(evname, c)
@@ -818,8 +794,8 @@ func (t *Table) onCursor(evname string, ev interface{}) {
 func (t *Table) onCursorPos(evname string, ev interface{}) {
 func (t *Table) onCursorPos(evname string, ev interface{}) {
 
 
 	// Convert mouse window coordinates to table content coordinates
 	// Convert mouse window coordinates to table content coordinates
-	kev := ev.(*window.CursorEvent)
-	cx, _ := t.ContentCoords(kev.Xpos, kev.Ypos)
+	cev := ev.(*window.CursorEvent)
+	cx, _ := t.ContentCoords(cev.Xpos, cev.Ypos)
 
 
 	// If user is dragging the resizer, updates its position
 	// If user is dragging the resizer, updates its position
 	if t.resizing {
 	if t.resizing {
@@ -1253,7 +1229,7 @@ func (t *Table) recalcHeader() {
 				continue
 				continue
 			}
 			}
 			// There is space available and if column is expandable,
 			// There is space available and if column is expandable,
-			// expands it proportionaly to the other expandable columns
+			// expands it proportionally to the other expandable columns
 			factor := c.expand / totalExpand
 			factor := c.expand / totalExpand
 			w := factor * wspace
 			w := factor * wspace
 			c.SetWidth(c.Width() + w)
 			c.SetWidth(c.Width() + w)
@@ -1541,7 +1517,7 @@ func (t *Table) onVScrollBar(evname string, ev interface{}) {
 }
 }
 
 
 // calcMaxFirst calculates the maximum index of the first visible row
 // calcMaxFirst calculates the maximum index of the first visible row
-// such as the remaing rows fits completely inside the table
+// such as the remaining rows fits completely inside the table
 // It is used when scrolling the table vertically
 // It is used when scrolling the table vertically
 func (t *Table) calcMaxFirst() int {
 func (t *Table) calcMaxFirst() int {
 
 
@@ -1569,7 +1545,7 @@ func (t *Table) calcMaxFirst() int {
 func (t *Table) updateRowStyle(ri int) {
 func (t *Table) updateRowStyle(ri int) {
 
 
 	row := t.rows[ri]
 	row := t.rows[ri]
-	var trs *TableRowStyle
+	var trs TableRowStyle
 	if ri == t.rowCursor {
 	if ri == t.rowCursor {
 		trs = t.styles.RowCursor
 		trs = t.styles.RowCursor
 	} else if row.selected {
 	} else if row.selected {
@@ -1581,24 +1557,18 @@ func (t *Table) updateRowStyle(ri int) {
 			trs = t.styles.RowOdd
 			trs = t.styles.RowOdd
 		}
 		}
 	}
 	}
-	t.applyRowStyle(row, trs)
+	t.applyRowStyle(row, &trs)
 }
 }
 
 
 // applyHeaderStyle applies style to the specified table header
 // applyHeaderStyle applies style to the specified table header
 // the last header panel does not the right border.
 // the last header panel does not the right border.
 func (t *Table) applyHeaderStyle(h *Panel, last bool) {
 func (t *Table) applyHeaderStyle(h *Panel, last bool) {
 
 
-	s := t.styles.Header
-	borders := s.Border
-	if !last {
-		h.SetBordersFrom(&borders)
-	} else {
-		borders.Right = 0
-		h.SetBordersFrom(&borders)
+	styleCopy := t.styles.Header.PanelStyle
+	if last {
+		styleCopy.Border.Right = 0
 	}
 	}
-	h.SetBordersColor4(&s.BorderColor)
-	h.SetPaddingsFrom(&s.Paddings)
-	h.SetColor(&s.BgColor)
+	h.ApplyStyle(&styleCopy)
 }
 }
 
 
 // applyRowStyle applies the specified style to all cells for the specified table row
 // applyRowStyle applies the specified style to all cells for the specified table row
@@ -1606,10 +1576,7 @@ func (t *Table) applyRowStyle(trow *tableRow, trs *TableRowStyle) {
 
 
 	for i := 0; i < len(trow.cells); i++ {
 	for i := 0; i < len(trow.cells); i++ {
 		cell := trow.cells[i]
 		cell := trow.cells[i]
-		cell.SetBordersFrom(&trs.Border)
-		cell.SetBordersColor4(&trs.BorderColor)
-		cell.SetPaddingsFrom(&trs.Paddings)
-		cell.SetColor(&trs.BgColor)
+		cell.ApplyStyle(&trs.PanelStyle)
 	}
 	}
 }
 }
 
 
@@ -1617,10 +1584,7 @@ func (t *Table) applyRowStyle(trow *tableRow, trs *TableRowStyle) {
 func (t *Table) applyStatusStyle() {
 func (t *Table) applyStatusStyle() {
 
 
 	s := t.styles.Status
 	s := t.styles.Status
-	t.statusPanel.SetBordersFrom(&s.Border)
-	t.statusPanel.SetBordersColor4(&s.BorderColor)
-	t.statusPanel.SetPaddingsFrom(&s.Paddings)
-	t.statusPanel.SetColor(&s.BgColor)
+	t.statusPanel.ApplyStyle(&s.PanelStyle)
 }
 }
 
 
 // applyResizerStyle applies the status style
 // applyResizerStyle applies the status style

+ 19 - 21
gui/tree.go

@@ -9,31 +9,32 @@ import (
 	"github.com/g3n/engine/window"
 	"github.com/g3n/engine/window"
 )
 )
 
 
+// Tree is the tree structure GUI element.
 type Tree struct {
 type Tree struct {
 	List               // Embedded list panel
 	List               // Embedded list panel
 	styles *TreeStyles // Pointer to styles
 	styles *TreeStyles // Pointer to styles
 }
 }
 
 
+// TreeStyles contains the styling of all tree components for each valid GUI state.
 type TreeStyles struct {
 type TreeStyles struct {
 	List     *ListStyles     // Styles for the embedded list
 	List     *ListStyles     // Styles for the embedded list
 	Node     *TreeNodeStyles // Styles for the node panel
 	Node     *TreeNodeStyles // Styles for the node panel
 	Padlevel float32         // Left padding indentation
 	Padlevel float32         // Left padding indentation
 }
 }
 
 
+// TreeNodeStyles contains a TreeNodeStyle for each valid GUI state.
 type TreeNodeStyles struct {
 type TreeNodeStyles struct {
 	Normal TreeNodeStyle
 	Normal TreeNodeStyle
 }
 }
 
 
+// TreeNodeStyle contains the styling of a TreeNode.
 type TreeNodeStyle struct {
 type TreeNodeStyle struct {
-	Margins     BorderSizes
-	Border      BorderSizes
-	Paddings    BorderSizes
-	BorderColor math32.Color4
-	BgColor     math32.Color4
-	FgColor     math32.Color
+	PanelStyle
+	FgColor     math32.Color4
 	Icons       [2]string
 	Icons       [2]string
 }
 }
 
 
+// TreeNode is a tree node.
 type TreeNode struct {
 type TreeNode struct {
 	Panel              // Embedded panel
 	Panel              // Embedded panel
 	label    Label     // Node label
 	label    Label     // Node label
@@ -44,7 +45,7 @@ type TreeNode struct {
 	expanded bool      // Node expanded flag
 	expanded bool      // Node expanded flag
 }
 }
 
 
-// NewTree creates and returns a pointer to a new tree widget
+// NewTree creates and returns a pointer to a new tree widget.
 func NewTree(width, height float32) *Tree {
 func NewTree(width, height float32) *Tree {
 
 
 	t := new(Tree)
 	t := new(Tree)
@@ -53,7 +54,7 @@ func NewTree(width, height float32) *Tree {
 }
 }
 
 
 // Initialize initializes the tree with the specified initial width and height
 // Initialize initializes the tree with the specified initial width and height
-// It is normally used when the folder is embedded in another object
+// It is normally used when the folder is embedded in another object.
 func (t *Tree) Initialize(width, height float32) {
 func (t *Tree) Initialize(width, height float32) {
 
 
 	t.List.initialize(true, width, height)
 	t.List.initialize(true, width, height)
@@ -63,7 +64,7 @@ func (t *Tree) Initialize(width, height float32) {
 	t.List.Subscribe(OnCursor, t.onCursor)
 	t.List.Subscribe(OnCursor, t.onCursor)
 }
 }
 
 
-// SetStyles set the tree styles overriding the default style
+// SetStyles sets the tree styles overriding the default style.
 func (t *Tree) SetStyles(s *TreeStyles) {
 func (t *Tree) SetStyles(s *TreeStyles) {
 
 
 	t.styles = s
 	t.styles = s
@@ -71,13 +72,13 @@ func (t *Tree) SetStyles(s *TreeStyles) {
 	t.update()
 	t.update()
 }
 }
 
 
-// InsertAt inserts a child panel at the specified position in the tree
+// InsertAt inserts a child panel at the specified position in the tree.
 func (t *Tree) InsertAt(pos int, child IPanel) {
 func (t *Tree) InsertAt(pos int, child IPanel) {
 
 
 	t.List.InsertAt(pos, child)
 	t.List.InsertAt(pos, child)
 }
 }
 
 
-// Add child panel to the end tree
+// Add child panel to the end tree.
 func (t *Tree) Add(ichild IPanel) {
 func (t *Tree) Add(ichild IPanel) {
 
 
 	t.List.Add(ichild)
 	t.List.Add(ichild)
@@ -85,7 +86,7 @@ func (t *Tree) Add(ichild IPanel) {
 
 
 // InsertNodeAt inserts at the specified position a new tree node
 // InsertNodeAt inserts at the specified position a new tree node
 // with the specified text at the end of this tree
 // with the specified text at the end of this tree
-// and returns pointer to the new node
+// and returns pointer to the new node.
 func (t *Tree) InsertNodeAt(pos int, text string) *TreeNode {
 func (t *Tree) InsertNodeAt(pos int, text string) *TreeNode {
 
 
 	n := newTreeNode(text, t, nil)
 	n := newTreeNode(text, t, nil)
@@ -95,8 +96,8 @@ func (t *Tree) InsertNodeAt(pos int, text string) *TreeNode {
 	return 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
+// AddNode adds a new tree node with the specified text
+// at the end of this tree and returns a pointer to the new node.
 func (t *Tree) AddNode(text string) *TreeNode {
 func (t *Tree) AddNode(text string) *TreeNode {
 
 
 	n := newTreeNode(text, t, nil)
 	n := newTreeNode(text, t, nil)
@@ -211,7 +212,7 @@ func newTreeNode(text string, tree *Tree, parNode *TreeNode) *TreeNode {
 
 
 	// Create node icon
 	// Create node icon
 	n.icon.initialize("", StyleDefault().FontIcon)
 	n.icon.initialize("", StyleDefault().FontIcon)
-	n.icon.SetFontSize(n.label.FontSize() * 1.3)
+	n.icon.SetFontSize(StyleDefault().Label.PointSize * 1.3)
 	n.Panel.Add(&n.icon)
 	n.Panel.Add(&n.icon)
 
 
 	// Subscribe to events
 	// Subscribe to events
@@ -362,17 +363,14 @@ func (n *TreeNode) level() int {
 // applyStyles applies the specified style to this tree node
 // applyStyles applies the specified style to this tree node
 func (n *TreeNode) applyStyle(s *TreeNodeStyle) {
 func (n *TreeNode) applyStyle(s *TreeNodeStyle) {
 
 
-	n.SetMarginsFrom(&s.Margins)
-	n.SetBordersColor4(&s.BorderColor)
-	n.SetBordersFrom(&s.Border)
-	n.SetColor4(&s.BgColor)
+	n.Panel.ApplyStyle(&s.PanelStyle)
 	icode := 0
 	icode := 0
 	if n.expanded {
 	if n.expanded {
 		icode = 1
 		icode = 1
 	}
 	}
 	n.icon.SetText(string(s.Icons[icode]))
 	n.icon.SetText(string(s.Icons[icode]))
-	n.icon.SetColor(&s.FgColor)
-	n.label.SetColor(&s.FgColor)
+	n.icon.SetColor4(&s.FgColor)
+	n.label.SetColor4(&s.FgColor)
 }
 }
 
 
 // update updates this tree node style
 // update updates this tree node style

+ 7 - 2
gui/util.go

@@ -4,14 +4,17 @@
 
 
 package gui
 package gui
 
 
-type BorderSizes struct {
+// RectBounds specifies the size of the boundaries of a rectangle.
+// It can represent the thickness of the borders, the margins, or the padding of a rectangle.
+type RectBounds struct {
 	Top    float32
 	Top    float32
 	Right  float32
 	Right  float32
 	Bottom float32
 	Bottom float32
 	Left   float32
 	Left   float32
 }
 }
 
 
-func (bs *BorderSizes) Set(top, right, bottom, left float32) {
+// Set sets the values of the RectBounds.
+func (bs *RectBounds) Set(top, right, bottom, left float32) {
 
 
 	if top >= 0 {
 	if top >= 0 {
 		bs.Top = top
 		bs.Top = top
@@ -27,6 +30,7 @@ func (bs *BorderSizes) Set(top, right, bottom, left float32) {
 	}
 	}
 }
 }
 
 
+// Rect represents a rectangle.
 type Rect struct {
 type Rect struct {
 	X      float32
 	X      float32
 	Y      float32
 	Y      float32
@@ -34,6 +38,7 @@ type Rect struct {
 	Height float32
 	Height float32
 }
 }
 
 
+// Contains determines whether a 2D point is inside the Rect.
 func (r *Rect) Contains(x, y float32) bool {
 func (r *Rect) Contains(x, y float32) bool {
 
 
 	if x < r.X || x > r.X+r.Width {
 	if x < r.X || x > r.X+r.Width {

+ 6 - 6
gui/vboxlayout.go

@@ -18,7 +18,7 @@ package gui
 // 	AlignCenter: Try to align the group of children in the center if the panel height is
 // 	AlignCenter: Try to align the group of children in the center if the panel height is
 // 	greater the the sum of the children heights + spacing.
 // 	greater the the sum of the children heights + spacing.
 //
 //
-// 	AlignHeight - Try to align the individual children vertically with the same same space between each other.
+// 	AlignHeight: Try to align the individual children vertically with the same same space between each other.
 // 	Each individual child can be aligned horizontally by SetLayoutParameters()
 // 	Each individual child can be aligned horizontally by SetLayoutParameters()
 //
 //
 // If the layout method SetAutoHeight(true) is called, the panel minimum content height will be the
 // If the layout method SetAutoHeight(true) is called, the panel minimum content height will be the
@@ -134,7 +134,7 @@ func (bl *VBoxLayout) Recalc(ipan IPanel) {
 	// Calculates the total height, expanded height, fixed height and
 	// Calculates the total height, expanded height, fixed height and
 	// the sum of the expand factor for all items.
 	// the sum of the expand factor for all items.
 	var theight float32
 	var theight float32
-	var eheight float32
+	//var eheight float32
 	var fheight float32
 	var fheight float32
 	var texpand float32
 	var texpand float32
 	ecount := 0
 	ecount := 0
@@ -157,10 +157,10 @@ func (bl *VBoxLayout) Recalc(ipan IPanel) {
 		// Calculate height of expanded items
 		// Calculate height of expanded items
 		if params.Expand > 0 {
 		if params.Expand > 0 {
 			texpand += params.Expand
 			texpand += params.Expand
-			eheight += pan.Height()
-			if pos > 0 {
-				eheight += bl.spacing
-			}
+			//eheight += pan.Height()
+			//if pos > 0 {
+			//	eheight += bl.spacing
+			//}
 			ecount++
 			ecount++
 			// Calculate width of fixed items
 			// Calculate width of fixed items
 		} else {
 		} else {

+ 9 - 5
gui/window.go

@@ -29,6 +29,7 @@ import (
 
 
 *********************************************/
 *********************************************/
 
 
+// Window represents a window GUI element
 type Window struct {
 type Window struct {
 	Panel      // Embedded Panel
 	Panel      // Embedded Panel
 	styles     *WindowStyles
 	styles     *WindowStyles
@@ -41,17 +42,18 @@ type Window struct {
 	mouseY     float32
 	mouseY     float32
 }
 }
 
 
+// WindowStyle contains the styling of a Window
 type WindowStyle struct {
 type WindowStyle struct {
-	Border           BorderSizes
-	Paddings         BorderSizes
+	Border           RectBounds
+	Paddings         RectBounds
 	BorderColor      math32.Color4
 	BorderColor      math32.Color4
-	TitleBorders     BorderSizes
+	TitleBorders     RectBounds
 	TitleBorderColor math32.Color4
 	TitleBorderColor math32.Color4
 	TitleBgColor     math32.Color4
 	TitleBgColor     math32.Color4
 	TitleFgColor     math32.Color4
 	TitleFgColor     math32.Color4
 }
 }
 
 
-// All Window styles
+// WindowStyles contains a WindowStyle for each valid GUI state
 type WindowStyles struct {
 type WindowStyles struct {
 	Normal   WindowStyle
 	Normal   WindowStyle
 	Over     WindowStyle
 	Over     WindowStyle
@@ -59,6 +61,7 @@ type WindowStyles struct {
 	Disabled WindowStyle
 	Disabled WindowStyle
 }
 }
 
 
+// ResizeBorders specifies which window borders can be resized
 type ResizeBorders int
 type ResizeBorders int
 
 
 const (
 const (
@@ -263,6 +266,7 @@ func (w *Window) recalc() {
 	w.client.SetSize(width, height)
 	w.client.SetSize(width, height)
 }
 }
 
 
+// WindowTitle represents the title bar of a Window
 type WindowTitle struct {
 type WindowTitle struct {
 	Panel   // Embedded panel
 	Panel   // Embedded panel
 	win     *Window
 	win     *Window
@@ -316,7 +320,7 @@ func (wt *WindowTitle) onMouse(evname string, ev interface{}) {
 func (wt *WindowTitle) onCursor(evname string, ev interface{}) {
 func (wt *WindowTitle) onCursor(evname string, ev interface{}) {
 
 
 	if evname == OnCursorEnter {
 	if evname == OnCursorEnter {
-		wt.win.root.SetCursorDrag()
+		wt.win.root.SetCursorHand()
 	} else if evname == OnCursorLeave {
 	} else if evname == OnCursorLeave {
 		wt.win.root.SetCursorNormal()
 		wt.win.root.SetCursorNormal()
 	} else if evname == OnCursor {
 	} else if evname == OnCursor {

+ 1 - 0
light/ambient.go

@@ -10,6 +10,7 @@ import (
 	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/math32"
 )
 )
 
 
+// Ambient represents an ambient light
 type Ambient struct {
 type Ambient struct {
 	core.Node              // Embedded node
 	core.Node              // Embedded node
 	color     math32.Color // Light color
 	color     math32.Color // Light color

+ 1 - 0
light/point.go

@@ -12,6 +12,7 @@ import (
 	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/math32"
 )
 )
 
 
+// Point is an omnidirectional light source
 type Point struct {
 type Point struct {
 	core.Node              // Embedded node
 	core.Node              // Embedded node
 	color     math32.Color // Light color
 	color     math32.Color // Light color

+ 1 - 0
light/spot.go

@@ -12,6 +12,7 @@ import (
 	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/math32"
 )
 )
 
 
+// Spot represents a spotlight
 type Spot struct {
 type Spot struct {
 	core.Node                // Embedded node
 	core.Node                // Embedded node
 	color     math32.Color   // Light color
 	color     math32.Color   // Light color

+ 2 - 3
loader/collada/animation.go

@@ -56,6 +56,7 @@ func (at *AnimationTarget) SetLoop(loop bool) {
 	at.loop = loop
 	at.loop = loop
 }
 }
 
 
+// SetStart sets the initial offset value
 func (at *AnimationTarget) SetStart(v float32) {
 func (at *AnimationTarget) SetStart(v float32) {
 
 
 	at.start = v
 	at.start = v
@@ -240,7 +241,7 @@ func actionScaleZ(at *AnimationTarget, v float32) {
 	at.target.GetNode().SetScaleZ(v)
 	at.target.GetNode().SetScaleZ(v)
 }
 }
 
 
-// NewSampler creates and returns a pointer to a new SamplerInstance built
+// NewSamplerInstance creates and returns a pointer to a new SamplerInstance built
 // with data from the specified Collada animation and URI
 // with data from the specified Collada animation and URI
 func NewSamplerInstance(ca *Animation, uri string) (*SamplerInstance, error) {
 func NewSamplerInstance(ca *Animation, uri string) (*SamplerInstance, error) {
 
 
@@ -343,8 +344,6 @@ func (si *SamplerInstance) Interpolate(inp float32) (float32, bool) {
 		return si.linearInterp(inp, idx), true
 		return si.linearInterp(inp, idx), true
 	case "BSPLINE":
 	case "BSPLINE":
 		return si.linearInterp(inp, idx), true
 		return si.linearInterp(inp, idx), true
-	default:
-		return 0, false
 	}
 	}
 
 
 	return 0, false
 	return 0, false

+ 4 - 2
loader/collada/collada.go

@@ -111,6 +111,7 @@ type Contributor struct {
 	SourceData    string
 	SourceData    string
 }
 }
 
 
+// Dump prints out information about the Contributor
 func (c *Contributor) Dump(out io.Writer, indent int) {
 func (c *Contributor) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sContributor:\n", sIndent(indent))
 	fmt.Fprintf(out, "%sContributor:\n", sIndent(indent))
@@ -148,6 +149,7 @@ type Asset struct {
 	UpAxis      string
 	UpAxis      string
 }
 }
 
 
+// Dump prints out information about the Asset
 func (a *Asset) Dump(out io.Writer, indent int) {
 func (a *Asset) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sAsset:\n", sIndent(indent))
 	fmt.Fprintf(out, "%sAsset:\n", sIndent(indent))
@@ -165,6 +167,7 @@ type Scene struct {
 	InstanceVisualScene *InstanceVisualScene
 	InstanceVisualScene *InstanceVisualScene
 }
 }
 
 
+// Dump prints out information about the Scene
 func (s *Scene) Dump(out io.Writer, indent int) {
 func (s *Scene) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sScene:\n", sIndent(indent))
 	fmt.Fprintf(out, "%sScene:\n", sIndent(indent))
@@ -181,6 +184,7 @@ type InstanceVisualScene struct {
 	Url  string
 	Url  string
 }
 }
 
 
+// Dump prints out information about the InstanceVisualScene
 func (ivs *InstanceVisualScene) Dump(out io.Writer, indent int) {
 func (ivs *InstanceVisualScene) Dump(out io.Writer, indent int) {
 
 
 	if ivs == nil {
 	if ivs == nil {
@@ -350,7 +354,6 @@ func (d *Decoder) decAsset(assetStart xml.StartElement, a *Asset) error {
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decScene(start xml.StartElement, dom *Collada) error {
 func (d *Decoder) decScene(start xml.StartElement, dom *Collada) error {
@@ -369,7 +372,6 @@ func (d *Decoder) decScene(start xml.StartElement, dom *Collada) error {
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decInstanceVisualScene(start xml.StartElement, s *Scene) error {
 func (d *Decoder) decInstanceVisualScene(start xml.StartElement, s *Scene) error {

+ 5 - 2
loader/collada/common.go

@@ -25,6 +25,7 @@ type Source struct {
 	}
 	}
 }
 }
 
 
+// Dump prints out information about the Source
 func (s *Source) Dump(out io.Writer, indent int) {
 func (s *Source) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sSource id:%s name:%s\n", sIndent(indent), s.Id, s.Name)
 	fmt.Fprintf(out, "%sSource id:%s name:%s\n", sIndent(indent), s.Id, s.Name)
@@ -49,6 +50,7 @@ type NameArray struct {
 	Data  []string
 	Data  []string
 }
 }
 
 
+// Dump prints out information about the NameArray
 func (na *NameArray) Dump(out io.Writer, indent int) {
 func (na *NameArray) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sNameArray id:%s count:%d\n", sIndent(indent), na.Id, na.Count)
 	fmt.Fprintf(out, "%sNameArray id:%s count:%d\n", sIndent(indent), na.Id, na.Count)
@@ -65,6 +67,7 @@ type FloatArray struct {
 	Data  []float32
 	Data  []float32
 }
 }
 
 
+// Dump prints out information about the FloatArray
 func (fa *FloatArray) Dump(out io.Writer, indent int) {
 func (fa *FloatArray) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sFloatArray id:%s count:%d\n", sIndent(indent), fa.Id, fa.Count)
 	fmt.Fprintf(out, "%sFloatArray id:%s count:%d\n", sIndent(indent), fa.Id, fa.Count)
@@ -82,6 +85,7 @@ type Accessor struct {
 	Params []Param
 	Params []Param
 }
 }
 
 
+// Dump prints out information about the Accessor
 func (ac *Accessor) Dump(out io.Writer, indent int) {
 func (ac *Accessor) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sAccessor source:%s count:%d stride:%d\n",
 	fmt.Fprintf(out, "%sAccessor source:%s count:%d stride:%d\n",
@@ -100,6 +104,7 @@ type Param struct {
 	Type string
 	Type string
 }
 }
 
 
+// Dump prints out information about the Param
 func (p *Param) Dump(out io.Writer, indent int) {
 func (p *Param) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sParam name:%s type:%s\n", sIndent(indent), p.Name, p.Type)
 	fmt.Fprintf(out, "%sParam name:%s type:%s\n", sIndent(indent), p.Name, p.Type)
@@ -143,7 +148,6 @@ func (d *Decoder) decSource(start xml.StartElement) (*Source, error) {
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return source, nil
 }
 }
 
 
 // decSource decodes the float array from the specified source
 // decSource decodes the float array from the specified source
@@ -227,7 +231,6 @@ func (d *Decoder) decAcessor(start xml.StartElement, source *Source) error {
 			}
 			}
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decParam(start xml.StartElement, accessor *Accessor) error {
 func (d *Decoder) decParam(start xml.StartElement, accessor *Accessor) error {

+ 2 - 3
loader/collada/geometry.go

@@ -65,7 +65,6 @@ func (d *Decoder) NewGeometry(id string) (geometry.IGeometry, uint32, error) {
 	default:
 	default:
 		return nil, 0, fmt.Errorf("GeometryElement:%T not supported", gt)
 		return nil, 0, fmt.Errorf("GeometryElement:%T not supported", gt)
 	}
 	}
-	return nil, 0, nil
 }
 }
 
 
 func newMesh(m *Mesh) (*geometry.Geometry, uint32, error) {
 func newMesh(m *Mesh) (*geometry.Geometry, uint32, error) {
@@ -141,7 +140,7 @@ func newMeshPolylist(m *Mesh, pels []interface{}) (*geometry.Geometry, uint32, e
 
 
 	// Creates vertices attributes map for reusing indices
 	// Creates vertices attributes map for reusing indices
 	mVindex := make(map[[8]float32]uint32)
 	mVindex := make(map[[8]float32]uint32)
-	var index uint32 = 0
+	var index uint32
 	geomGroups := make([]geometry.Group, 0)
 	geomGroups := make([]geometry.Group, 0)
 	groupMatindex := 0
 	groupMatindex := 0
 	// For each Polylist
 	// For each Polylist
@@ -358,7 +357,7 @@ func newMeshLines(m *Mesh, ln *Lines) (*geometry.Geometry, uint32, error) {
 
 
 	mVindex := make(map[[3]float32]uint32)
 	mVindex := make(map[[3]float32]uint32)
 	inputCount := len(ln.Input)
 	inputCount := len(ln.Input)
-	var index uint32 = 0
+	var index uint32
 	for i := 0; i < len(ln.P); i += inputCount {
 	for i := 0; i < len(ln.P); i += inputCount {
 		// Vertex position
 		// Vertex position
 		var vx [3]float32
 		var vx [3]float32

+ 4 - 3
loader/collada/library_animations.go

@@ -20,6 +20,7 @@ type LibraryAnimations struct {
 	Animation []*Animation
 	Animation []*Animation
 }
 }
 
 
+// Dump prints out information about the LibraryAnimations
 func (la *LibraryAnimations) Dump(out io.Writer, indent int) {
 func (la *LibraryAnimations) Dump(out io.Writer, indent int) {
 
 
 	if la == nil {
 	if la == nil {
@@ -43,6 +44,7 @@ type Animation struct {
 	Channel   []*Channel
 	Channel   []*Channel
 }
 }
 
 
+// Dump prints out information about the Animation
 func (an *Animation) Dump(out io.Writer, indent int) {
 func (an *Animation) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sAnimation id:%s name:%s\n", sIndent(indent), an.Id, an.Name)
 	fmt.Fprintf(out, "%sAnimation id:%s name:%s\n", sIndent(indent), an.Id, an.Name)
@@ -70,6 +72,7 @@ type Sampler struct {
 
 
 }
 }
 
 
+// Dump prints out information about the Sampler
 func (sp *Sampler) Dump(out io.Writer, indent int) {
 func (sp *Sampler) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sSampler id:%s\n", sIndent(indent), sp.Id)
 	fmt.Fprintf(out, "%sSampler id:%s\n", sIndent(indent), sp.Id)
@@ -87,6 +90,7 @@ type Channel struct {
 	Target string
 	Target string
 }
 }
 
 
+// Dump prints out information about the Channel
 func (ch *Channel) Dump(out io.Writer, indent int) {
 func (ch *Channel) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sChannel source:%s target:%s\n", sIndent(indent), ch.Source, ch.Target)
 	fmt.Fprintf(out, "%sChannel source:%s target:%s\n", sIndent(indent), ch.Source, ch.Target)
@@ -112,7 +116,6 @@ func (d *Decoder) decLibraryAnimations(start xml.StartElement, dom *Collada) err
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decAnimation(start xml.StartElement, la *LibraryAnimations) error {
 func (d *Decoder) decAnimation(start xml.StartElement, la *LibraryAnimations) error {
@@ -150,7 +153,6 @@ func (d *Decoder) decAnimation(start xml.StartElement, la *LibraryAnimations) er
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decSampler(start xml.StartElement, anim *Animation) error {
 func (d *Decoder) decSampler(start xml.StartElement, anim *Animation) error {
@@ -173,7 +175,6 @@ func (d *Decoder) decSampler(start xml.StartElement, anim *Animation) error {
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decChannel(start xml.StartElement, anim *Animation) error {
 func (d *Decoder) decChannel(start xml.StartElement, anim *Animation) error {

+ 15 - 11
loader/collada/library_effects.go

@@ -12,7 +12,7 @@ import (
 )
 )
 
 
 //
 //
-// Library Effects
+// LibraryEffects
 //
 //
 type LibraryEffects struct {
 type LibraryEffects struct {
 	Id     string
 	Id     string
@@ -21,6 +21,7 @@ type LibraryEffects struct {
 	Effect []*Effect
 	Effect []*Effect
 }
 }
 
 
+// Dump prints out information about the LibraryEffects
 func (le *LibraryEffects) Dump(out io.Writer, indent int) {
 func (le *LibraryEffects) Dump(out io.Writer, indent int) {
 
 
 	if le == nil {
 	if le == nil {
@@ -42,6 +43,7 @@ type Effect struct {
 	Profile []interface{}
 	Profile []interface{}
 }
 }
 
 
+// Dump prints out information about the Effect
 func (ef *Effect) Dump(out io.Writer, indent int) {
 func (ef *Effect) Dump(out io.Writer, indent int) {
 
 
 	fmt.Printf("%sEffect id:%s name:%s\n", sIndent(indent), ef.Id, ef.Name)
 	fmt.Printf("%sEffect id:%s name:%s\n", sIndent(indent), ef.Id, ef.Name)
@@ -56,7 +58,7 @@ func (ef *Effect) Dump(out io.Writer, indent int) {
 }
 }
 
 
 //
 //
-// Profile COMMON
+// ProfileCOMMON
 //
 //
 type ProfileCOMMON struct {
 type ProfileCOMMON struct {
 	Id        string
 	Id        string
@@ -70,6 +72,7 @@ type ProfileCOMMON struct {
 	}
 	}
 }
 }
 
 
+// Dump prints out information about the ProfileCOMMON
 func (pc *ProfileCOMMON) Dump(out io.Writer, indent int) {
 func (pc *ProfileCOMMON) Dump(out io.Writer, indent int) {
 
 
 	fmt.Printf("%sProfileCOMMON id:%s\n", sIndent(indent), pc.Id)
 	fmt.Printf("%sProfileCOMMON id:%s\n", sIndent(indent), pc.Id)
@@ -97,6 +100,7 @@ type Newparam struct {
 	ParameterType interface{}
 	ParameterType interface{}
 }
 }
 
 
+// Dump prints out information about the Newparam
 func (np *Newparam) Dump(out io.Writer, indent int) {
 func (np *Newparam) Dump(out io.Writer, indent int) {
 
 
 	fmt.Printf("%sNewparam sid:%s\n", sIndent(indent), np.Sid)
 	fmt.Printf("%sNewparam sid:%s\n", sIndent(indent), np.Sid)
@@ -117,6 +121,7 @@ type Surface struct {
 	Init interface{}
 	Init interface{}
 }
 }
 
 
+// Dump prints out information about the Surface
 func (sf *Surface) Dump(out io.Writer, indent int) {
 func (sf *Surface) Dump(out io.Writer, indent int) {
 
 
 	fmt.Printf("%sSurface type:%s\n", sIndent(indent), sf.Type)
 	fmt.Printf("%sSurface type:%s\n", sIndent(indent), sf.Type)
@@ -134,6 +139,7 @@ type Sampler2D struct {
 	Source string
 	Source string
 }
 }
 
 
+// Dump prints out information about the Sampler2D
 func (sp *Sampler2D) Dump(out io.Writer, indent int) {
 func (sp *Sampler2D) Dump(out io.Writer, indent int) {
 
 
 	fmt.Printf("%sSampler2D\n", sIndent(indent))
 	fmt.Printf("%sSampler2D\n", sIndent(indent))
@@ -157,6 +163,7 @@ type Blinn struct {
 	IndexOfRefraction interface{}
 	IndexOfRefraction interface{}
 }
 }
 
 
+// Dump prints out information about the Blinn
 func (bl *Blinn) Dump(out io.Writer, indent int) {
 func (bl *Blinn) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sBlinn\n", sIndent(indent))
 	fmt.Fprintf(out, "%sBlinn\n", sIndent(indent))
@@ -215,6 +222,7 @@ type Phong struct {
 	IndexOfRefraction interface{}
 	IndexOfRefraction interface{}
 }
 }
 
 
+// Dump prints out information about the Phong
 func (ph *Phong) Dump(out io.Writer, indent int) {
 func (ph *Phong) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sPhong\n", sIndent(indent))
 	fmt.Fprintf(out, "%sPhong\n", sIndent(indent))
@@ -231,6 +239,7 @@ func (ph *Phong) Dump(out io.Writer, indent int) {
 	DumpFloatOrParam("IndexOfRefraction", ph.IndexOfRefraction, out, ind)
 	DumpFloatOrParam("IndexOfRefraction", ph.IndexOfRefraction, out, ind)
 }
 }
 
 
+// DumpColorOrTexture prints out information about the Color or Texture
 func DumpColorOrTexture(name string, v interface{}, out io.Writer, indent int) {
 func DumpColorOrTexture(name string, v interface{}, out io.Writer, indent int) {
 
 
 	if v == nil {
 	if v == nil {
@@ -246,6 +255,7 @@ func DumpColorOrTexture(name string, v interface{}, out io.Writer, indent int) {
 	}
 	}
 }
 }
 
 
+// DumpFloatOrParam prints out information about the Float or Param
 func DumpFloatOrParam(name string, v interface{}, out io.Writer, indent int) {
 func DumpFloatOrParam(name string, v interface{}, out io.Writer, indent int) {
 
 
 	if v == nil {
 	if v == nil {
@@ -268,6 +278,7 @@ type Color struct {
 	Data [4]float32
 	Data [4]float32
 }
 }
 
 
+// Dump prints out information about the Color
 func (c *Color) Dump(out io.Writer, indent int) {
 func (c *Color) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sColor sid:%s data:%v\n", sIndent(indent), c.Sid, c.Data)
 	fmt.Fprintf(out, "%sColor sid:%s data:%v\n", sIndent(indent), c.Sid, c.Data)
@@ -281,6 +292,7 @@ type Float struct {
 	Data float32
 	Data float32
 }
 }
 
 
+// Dump prints out information about the Float
 func (f *Float) Dump(out io.Writer, indent int) {
 func (f *Float) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sFloat sid:%s data:%v\n", sIndent(indent), f.Sid, f.Data)
 	fmt.Fprintf(out, "%sFloat sid:%s data:%v\n", sIndent(indent), f.Sid, f.Data)
@@ -294,6 +306,7 @@ type Texture struct {
 	Texcoord string
 	Texcoord string
 }
 }
 
 
+// Dump prints out information about the Texture
 func (t *Texture) Dump(out io.Writer, indent int) {
 func (t *Texture) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sTexture texture:%s texcoord:%v\n", sIndent(indent), t.Texture, t.Texcoord)
 	fmt.Fprintf(out, "%sTexture texture:%s texcoord:%v\n", sIndent(indent), t.Texture, t.Texcoord)
@@ -321,7 +334,6 @@ func (d *Decoder) decLibraryEffects(start xml.StartElement, dom *Collada) error
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decEffect(start xml.StartElement, le *LibraryEffects) error {
 func (d *Decoder) decEffect(start xml.StartElement, le *LibraryEffects) error {
@@ -345,7 +357,6 @@ func (d *Decoder) decEffect(start xml.StartElement, le *LibraryEffects) error {
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decEffectProfileCommon(start xml.StartElement, e *Effect) error {
 func (d *Decoder) decEffectProfileCommon(start xml.StartElement, e *Effect) error {
@@ -375,7 +386,6 @@ func (d *Decoder) decEffectProfileCommon(start xml.StartElement, e *Effect) erro
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decProfileCommonNewparam(start xml.StartElement, pc *ProfileCOMMON) error {
 func (d *Decoder) decProfileCommonNewparam(start xml.StartElement, pc *ProfileCOMMON) error {
@@ -404,7 +414,6 @@ func (d *Decoder) decProfileCommonNewparam(start xml.StartElement, pc *ProfileCO
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decSurface(start xml.StartElement, np *Newparam) error {
 func (d *Decoder) decSurface(start xml.StartElement, np *Newparam) error {
@@ -423,7 +432,6 @@ func (d *Decoder) decSurface(start xml.StartElement, np *Newparam) error {
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decSampler2D(start xml.StartElement, np *Newparam) error {
 func (d *Decoder) decSampler2D(start xml.StartElement, np *Newparam) error {
@@ -441,7 +449,6 @@ func (d *Decoder) decSampler2D(start xml.StartElement, np *Newparam) error {
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decProfileCommonTechnique(start xml.StartElement, pc *ProfileCOMMON) error {
 func (d *Decoder) decProfileCommonTechnique(start xml.StartElement, pc *ProfileCOMMON) error {
@@ -477,7 +484,6 @@ func (d *Decoder) decProfileCommonTechnique(start xml.StartElement, pc *ProfileC
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decBlinn(start xml.StartElement, pc *ProfileCOMMON) error {
 func (d *Decoder) decBlinn(start xml.StartElement, pc *ProfileCOMMON) error {
@@ -558,7 +564,6 @@ func (d *Decoder) decBlinn(start xml.StartElement, pc *ProfileCOMMON) error {
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decPhong(start xml.StartElement, pc *ProfileCOMMON) error {
 func (d *Decoder) decPhong(start xml.StartElement, pc *ProfileCOMMON) error {
@@ -639,7 +644,6 @@ func (d *Decoder) decPhong(start xml.StartElement, pc *ProfileCOMMON) error {
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decColorOrTexture(start xml.StartElement, dest *interface{}) error {
 func (d *Decoder) decColorOrTexture(start xml.StartElement, dest *interface{}) error {

+ 9 - 7
loader/collada/library_geometries.go

@@ -12,13 +12,14 @@ import (
 )
 )
 
 
 //
 //
-// Library Geometries
+// LibraryGeometries
 //
 //
 type LibraryGeometries struct {
 type LibraryGeometries struct {
 	Asset    *Asset
 	Asset    *Asset
 	Geometry []*Geometry
 	Geometry []*Geometry
 }
 }
 
 
+// Dump prints out information about the LibraryGeometries
 func (lg *LibraryGeometries) Dump(out io.Writer, indent int) {
 func (lg *LibraryGeometries) Dump(out io.Writer, indent int) {
 
 
 	if lg == nil {
 	if lg == nil {
@@ -43,6 +44,7 @@ type Geometry struct {
 	GeometricElement interface{} // Geometry type object (Mesh|others)
 	GeometricElement interface{} // Geometry type object (Mesh|others)
 }
 }
 
 
+// Dump prints out information about the Geometry
 func (g *Geometry) Dump(out io.Writer, indent int) {
 func (g *Geometry) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sGeometry id:%s name:%s\n", sIndent(indent), g.Id, g.Name)
 	fmt.Fprintf(out, "%sGeometry id:%s name:%s\n", sIndent(indent), g.Id, g.Name)
@@ -64,6 +66,7 @@ type Mesh struct {
 	PrimitiveElements []interface{} // Geometry primitives (polylist|others)
 	PrimitiveElements []interface{} // Geometry primitives (polylist|others)
 }
 }
 
 
+// Dump prints out information about the Mesh
 func (m *Mesh) Dump(out io.Writer, indent int) {
 func (m *Mesh) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sMesh:\n", sIndent(indent))
 	fmt.Fprintf(out, "%sMesh:\n", sIndent(indent))
@@ -91,6 +94,7 @@ type Vertices struct {
 	Input []Input
 	Input []Input
 }
 }
 
 
+// Dump prints out information about the Vertices
 func (v *Vertices) Dump(out io.Writer, indent int) {
 func (v *Vertices) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sVertices id:%s name:%s\n", sIndent(indent), v.Id, v.Name)
 	fmt.Fprintf(out, "%sVertices id:%s name:%s\n", sIndent(indent), v.Id, v.Name)
@@ -107,6 +111,7 @@ type Input struct {
 	Source   string // source URL
 	Source   string // source URL
 }
 }
 
 
+// Dump prints out information about the Input
 func (i *Input) Dump(out io.Writer, indent int) {
 func (i *Input) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sInput semantic:%s source:%s\n", sIndent(indent), i.Semantic, i.Source)
 	fmt.Fprintf(out, "%sInput semantic:%s source:%s\n", sIndent(indent), i.Semantic, i.Source)
@@ -124,6 +129,7 @@ type Polylist struct {
 	P        []int
 	P        []int
 }
 }
 
 
+// Dump prints out information about the Polylist
 func (pl *Polylist) Dump(out io.Writer, indent int) {
 func (pl *Polylist) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sPolylist name:%s count:%d material:%s\n", sIndent(indent), pl.Name, pl.Count, pl.Material)
 	fmt.Fprintf(out, "%sPolylist name:%s count:%d material:%s\n", sIndent(indent), pl.Name, pl.Count, pl.Material)
@@ -167,6 +173,7 @@ type Lines struct {
 	P        []int
 	P        []int
 }
 }
 
 
+// Dump prints out information about the Lines
 func (ln *Lines) Dump(out io.Writer, indent int) {
 func (ln *Lines) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sLines name:%s count:%d material:%s\n", sIndent(indent), ln.Name, ln.Count, ln.Material)
 	fmt.Fprintf(out, "%sLines name:%s count:%d material:%s\n", sIndent(indent), ln.Name, ln.Count, ln.Material)
@@ -210,6 +217,7 @@ type Tristrips struct {
 	P        []int
 	P        []int
 }
 }
 
 
+// Dump prints out information about the Tristrips
 func (is *InputShared) Dump(out io.Writer, indent int) {
 func (is *InputShared) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sInputShared offset:%d semantic:%s source:%s set:%d\n",
 	fmt.Fprintf(out, "%sInputShared offset:%d semantic:%s source:%s set:%d\n",
@@ -247,7 +255,6 @@ func (d *Decoder) decLibraryGeometries(start xml.StartElement, dom *Collada) err
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 // decGeometry receives the start element of a geometry and
 // decGeometry receives the start element of a geometry and
@@ -277,7 +284,6 @@ func (d *Decoder) decGeometry(start xml.StartElement, lg *LibraryGeometries) err
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 // decMesh decodes the mesh from the specified geometry
 // decMesh decodes the mesh from the specified geometry
@@ -329,7 +335,6 @@ func (d *Decoder) decMesh(start xml.StartElement, geom *Geometry) error {
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decVertices(start xml.StartElement, mesh *Mesh) error {
 func (d *Decoder) decVertices(start xml.StartElement, mesh *Mesh) error {
@@ -352,7 +357,6 @@ func (d *Decoder) decVertices(start xml.StartElement, mesh *Mesh) error {
 			mesh.Vertices.Input = append(mesh.Vertices.Input, inp)
 			mesh.Vertices.Input = append(mesh.Vertices.Input, inp)
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decInput(start xml.StartElement) (Input, error) {
 func (d *Decoder) decInput(start xml.StartElement) (Input, error) {
@@ -395,7 +399,6 @@ func (d *Decoder) decLines(start xml.StartElement, mesh *Mesh) error {
 			ln.P = p
 			ln.P = p
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decPolylist(start xml.StartElement, mesh *Mesh) error {
 func (d *Decoder) decPolylist(start xml.StartElement, mesh *Mesh) error {
@@ -439,7 +442,6 @@ func (d *Decoder) decPolylist(start xml.StartElement, mesh *Mesh) error {
 			pl.P = p
 			pl.P = p
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decInputShared(start xml.StartElement) (InputShared, error) {
 func (d *Decoder) decInputShared(start xml.StartElement) (InputShared, error) {

+ 5 - 4
loader/collada/library_images.go

@@ -11,7 +11,7 @@ import (
 )
 )
 
 
 //
 //
-// Library Images
+// LibraryImages
 //
 //
 type LibraryImages struct {
 type LibraryImages struct {
 	Id    string
 	Id    string
@@ -20,6 +20,7 @@ type LibraryImages struct {
 	Image []*Image
 	Image []*Image
 }
 }
 
 
+// Dump prints out information about the LibraryImages
 func (li *LibraryImages) Dump(out io.Writer, indent int) {
 func (li *LibraryImages) Dump(out io.Writer, indent int) {
 
 
 	if li == nil {
 	if li == nil {
@@ -44,6 +45,7 @@ type Image struct {
 	ImageSource interface{}
 	ImageSource interface{}
 }
 }
 
 
+// Dump prints out information about the Image
 func (img *Image) Dump(out io.Writer, indent int) {
 func (img *Image) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sImage id:%s name:%s\n", sIndent(indent), img.Id, img.Name)
 	fmt.Fprintf(out, "%sImage id:%s name:%s\n", sIndent(indent), img.Id, img.Name)
@@ -55,12 +57,13 @@ func (img *Image) Dump(out io.Writer, indent int) {
 }
 }
 
 
 //
 //
-//
+// InitFrom
 //
 //
 type InitFrom struct {
 type InitFrom struct {
 	Uri string
 	Uri string
 }
 }
 
 
+// Dump prints out information about the InitFrom
 func (initf *InitFrom) Dump(out io.Writer, indent int) {
 func (initf *InitFrom) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sInitFrom:%s\n", sIndent(indent), initf.Uri)
 	fmt.Fprintf(out, "%sInitFrom:%s\n", sIndent(indent), initf.Uri)
@@ -86,7 +89,6 @@ func (d *Decoder) decLibraryImages(start xml.StartElement, dom *Collada) error {
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decImage(start xml.StartElement, li *LibraryImages) error {
 func (d *Decoder) decImage(start xml.StartElement, li *LibraryImages) error {
@@ -109,7 +111,6 @@ func (d *Decoder) decImage(start xml.StartElement, li *LibraryImages) error {
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decImageSource(start xml.StartElement, cdata []byte, img *Image) error {
 func (d *Decoder) decImageSource(start xml.StartElement, cdata []byte, img *Image) error {

+ 9 - 5
loader/collada/library_lights.go

@@ -12,7 +12,7 @@ import (
 )
 )
 
 
 //
 //
-// Library Lights
+// LibraryLights
 //
 //
 type LibraryLights struct {
 type LibraryLights struct {
 	Id    string
 	Id    string
@@ -21,6 +21,7 @@ type LibraryLights struct {
 	Light []*Light
 	Light []*Light
 }
 }
 
 
+// Dump prints out information about the LibraryLights
 func (ll *LibraryLights) Dump(out io.Writer, indent int) {
 func (ll *LibraryLights) Dump(out io.Writer, indent int) {
 
 
 	if ll == nil {
 	if ll == nil {
@@ -43,6 +44,7 @@ type Light struct {
 	}
 	}
 }
 }
 
 
+// Dump prints out information about the Light
 func (li *Light) Dump(out io.Writer, indent int) {
 func (li *Light) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sLights id:%s name:%s\n", sIndent(indent), li.Id, li.Name)
 	fmt.Fprintf(out, "%sLights id:%s name:%s\n", sIndent(indent), li.Id, li.Name)
@@ -68,6 +70,7 @@ type Ambient struct {
 	Color LightColor
 	Color LightColor
 }
 }
 
 
+// Dump prints out information about the Ambient
 func (amb *Ambient) Dump(out io.Writer, indent int) {
 func (amb *Ambient) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sAmbient\n", sIndent(indent))
 	fmt.Fprintf(out, "%sAmbient\n", sIndent(indent))
@@ -82,6 +85,7 @@ type Directional struct {
 	Color LightColor
 	Color LightColor
 }
 }
 
 
+// Dump prints out information about the Directional
 func (dir *Directional) Dump(out io.Writer, indent int) {
 func (dir *Directional) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sDirectional\n", sIndent(indent))
 	fmt.Fprintf(out, "%sDirectional\n", sIndent(indent))
@@ -99,6 +103,7 @@ type Point struct {
 	QuadraticAttenuation *FloatValue
 	QuadraticAttenuation *FloatValue
 }
 }
 
 
+// Dump prints out information about the Point
 func (pl *Point) Dump(out io.Writer, indent int) {
 func (pl *Point) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sPoint\n", sIndent(indent))
 	fmt.Fprintf(out, "%sPoint\n", sIndent(indent))
@@ -121,6 +126,7 @@ type Spot struct {
 	FalloffExponent      *FloatValue
 	FalloffExponent      *FloatValue
 }
 }
 
 
+// Dump prints out information about the Spot
 func (sl *Spot) Dump(out io.Writer, indent int) {
 func (sl *Spot) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sSpot\n", sIndent(indent))
 	fmt.Fprintf(out, "%sSpot\n", sIndent(indent))
@@ -141,6 +147,7 @@ type FloatValue struct {
 	Value float32
 	Value float32
 }
 }
 
 
+// Dump prints out information about the FloatValue
 func (fv *FloatValue) Dump(name string, out io.Writer, indent int) {
 func (fv *FloatValue) Dump(name string, out io.Writer, indent int) {
 
 
 	if fv == nil {
 	if fv == nil {
@@ -157,6 +164,7 @@ type LightColor struct {
 	Data [3]float32
 	Data [3]float32
 }
 }
 
 
+// Dump prints out information about the LightColor
 func (lc *LightColor) Dump(out io.Writer, indent int) {
 func (lc *LightColor) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sColor sid:%s data:%v\n", sIndent(indent), lc.Sid, lc.Data)
 	fmt.Fprintf(out, "%sColor sid:%s data:%v\n", sIndent(indent), lc.Sid, lc.Data)
@@ -182,7 +190,6 @@ func (d *Decoder) decLibraryLights(start xml.StartElement, dom *Collada) error {
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decLight(start xml.StartElement, ll *LibraryLights) error {
 func (d *Decoder) decLight(start xml.StartElement, ll *LibraryLights) error {
@@ -205,7 +212,6 @@ func (d *Decoder) decLight(start xml.StartElement, ll *LibraryLights) error {
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decLightTechniqueCommon(start xml.StartElement, li *Light) error {
 func (d *Decoder) decLightTechniqueCommon(start xml.StartElement, li *Light) error {
@@ -319,7 +325,6 @@ func (d *Decoder) decPoint(start xml.StartElement, li *Light) error {
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decSpot(start xml.StartElement, li *Light) error {
 func (d *Decoder) decSpot(start xml.StartElement, li *Light) error {
@@ -380,7 +385,6 @@ func (d *Decoder) decSpot(start xml.StartElement, li *Light) error {
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decFloatValue(start xml.StartElement, cdata []byte) (*FloatValue, error) {
 func (d *Decoder) decFloatValue(start xml.StartElement, cdata []byte) (*FloatValue, error) {

+ 4 - 4
loader/collada/library_materials.go

@@ -11,7 +11,7 @@ import (
 )
 )
 
 
 //
 //
-// Library Materials
+// LibraryMaterials
 //
 //
 type LibraryMaterials struct {
 type LibraryMaterials struct {
 	Id       string
 	Id       string
@@ -20,6 +20,7 @@ type LibraryMaterials struct {
 	Material []*Material
 	Material []*Material
 }
 }
 
 
+// Dump prints out information about the LibraryMaterials
 func (lm *LibraryMaterials) Dump(out io.Writer, indent int) {
 func (lm *LibraryMaterials) Dump(out io.Writer, indent int) {
 
 
 	if lm == nil {
 	if lm == nil {
@@ -41,6 +42,7 @@ type Material struct {
 	InstanceEffect InstanceEffect
 	InstanceEffect InstanceEffect
 }
 }
 
 
+// Dump prints out information about the Material
 func (mat *Material) Dump(out io.Writer, indent int) {
 func (mat *Material) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sMaterial id:%s name:%s\n", sIndent(indent), mat.Id, mat.Name)
 	fmt.Fprintf(out, "%sMaterial id:%s name:%s\n", sIndent(indent), mat.Id, mat.Name)
@@ -57,6 +59,7 @@ type InstanceEffect struct {
 	Url  string
 	Url  string
 }
 }
 
 
+// Dump prints out information about the InstanceEffect
 func (ie *InstanceEffect) Dump(out io.Writer, indent int) {
 func (ie *InstanceEffect) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sInstanceEffect id:%s name:%s url:%s\n",
 	fmt.Fprintf(out, "%sInstanceEffect id:%s name:%s url:%s\n",
@@ -85,7 +88,6 @@ func (d *Decoder) decLibraryMaterials(start xml.StartElement, dom *Collada) erro
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decMaterial(start xml.StartElement, lm *LibraryMaterials) error {
 func (d *Decoder) decMaterial(start xml.StartElement, lm *LibraryMaterials) error {
@@ -108,7 +110,6 @@ func (d *Decoder) decMaterial(start xml.StartElement, lm *LibraryMaterials) erro
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decInstanceEffect(start xml.StartElement, ie *InstanceEffect) error {
 func (d *Decoder) decInstanceEffect(start xml.StartElement, ie *InstanceEffect) error {
@@ -131,5 +132,4 @@ func (d *Decoder) decInstanceEffect(start xml.StartElement, ie *InstanceEffect)
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }

+ 14 - 10
loader/collada/library_visual_scenes.go

@@ -12,13 +12,14 @@ import (
 )
 )
 
 
 //
 //
-// Library Visual Scenes
+// LibraryVisualScenes
 //
 //
 type LibraryVisualScenes struct {
 type LibraryVisualScenes struct {
 	Asset       *Asset
 	Asset       *Asset
 	VisualScene []*VisualScene
 	VisualScene []*VisualScene
 }
 }
 
 
+// Dump prints out information about the LibraryVisualScenes
 func (lv *LibraryVisualScenes) Dump(out io.Writer, indent int) {
 func (lv *LibraryVisualScenes) Dump(out io.Writer, indent int) {
 
 
 	if lv == nil {
 	if lv == nil {
@@ -35,7 +36,7 @@ func (lv *LibraryVisualScenes) Dump(out io.Writer, indent int) {
 }
 }
 
 
 //
 //
-// A visual scene contain all the nodes of a visual scene
+// VisualScene contains all the nodes of a visual scene
 //
 //
 type VisualScene struct {
 type VisualScene struct {
 	Id   string
 	Id   string
@@ -43,6 +44,7 @@ type VisualScene struct {
 	Node []*Node // Array of nodes
 	Node []*Node // Array of nodes
 }
 }
 
 
+// Dump prints out information about the VisualScene
 func (vs *VisualScene) Dump(out io.Writer, indent int) {
 func (vs *VisualScene) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sVisualScene id:%s name:%s\n", sIndent(indent), vs.Id, vs.Name)
 	fmt.Fprintf(out, "%sVisualScene id:%s name:%s\n", sIndent(indent), vs.Id, vs.Name)
@@ -52,7 +54,7 @@ func (vs *VisualScene) Dump(out io.Writer, indent int) {
 }
 }
 
 
 //
 //
-// Base Node is embedded in each node instance
+// Node is embedded in each node instance
 //
 //
 type Node struct {
 type Node struct {
 	Id                     string
 	Id                     string
@@ -65,6 +67,7 @@ type Node struct {
 	Node                   []*Node // Array of children nodes
 	Node                   []*Node // Array of children nodes
 }
 }
 
 
+// Dump prints out information about the Node
 func (n *Node) Dump(out io.Writer, indent int) {
 func (n *Node) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sNode id:%s name:%s sid:%s type:%s layer:%v\n",
 	fmt.Fprintf(out, "%sNode id:%s name:%s sid:%s type:%s layer:%v\n",
@@ -101,6 +104,7 @@ type Matrix struct {
 	Data [16]float32
 	Data [16]float32
 }
 }
 
 
+// Dump prints out information about the Matrix
 func (m *Matrix) Dump(out io.Writer, indent int) {
 func (m *Matrix) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sMatrix sid:%s data:%v\n", sIndent(indent), m.Sid, m.Data)
 	fmt.Fprintf(out, "%sMatrix sid:%s data:%v\n", sIndent(indent), m.Sid, m.Data)
@@ -114,6 +118,7 @@ type Rotate struct {
 	Data [4]float32
 	Data [4]float32
 }
 }
 
 
+// Dump prints out information about the Rotate
 func (r *Rotate) Dump(out io.Writer, indent int) {
 func (r *Rotate) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sRotate sid:%s data:%v\n", sIndent(indent), r.Sid, r.Data)
 	fmt.Fprintf(out, "%sRotate sid:%s data:%v\n", sIndent(indent), r.Sid, r.Data)
@@ -127,6 +132,7 @@ type Translate struct {
 	Data [3]float32
 	Data [3]float32
 }
 }
 
 
+// Dump prints out information about the Translate
 func (t *Translate) Dump(out io.Writer, indent int) {
 func (t *Translate) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sTranslate sid:%s data:%v\n", sIndent(indent), t.Sid, t.Data)
 	fmt.Fprintf(out, "%sTranslate sid:%s data:%v\n", sIndent(indent), t.Sid, t.Data)
@@ -140,6 +146,7 @@ type Scale struct {
 	Data [3]float32
 	Data [3]float32
 }
 }
 
 
+// Dump prints out information about the Scale
 func (s *Scale) Dump(out io.Writer, indent int) {
 func (s *Scale) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sScale sid:%s data:%v\n", sIndent(indent), s.Sid, s.Data)
 	fmt.Fprintf(out, "%sScale sid:%s data:%v\n", sIndent(indent), s.Sid, s.Data)
@@ -154,6 +161,7 @@ type InstanceGeometry struct {
 	BindMaterial *BindMaterial
 	BindMaterial *BindMaterial
 }
 }
 
 
+// Dump prints out information about the InstanceGeometry
 func (ig *InstanceGeometry) Dump(out io.Writer, indent int) {
 func (ig *InstanceGeometry) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sInstanceGeometry url:%s name:%s\n", sIndent(indent), ig.Url, ig.Name)
 	fmt.Fprintf(out, "%sInstanceGeometry url:%s name:%s\n", sIndent(indent), ig.Url, ig.Name)
@@ -172,6 +180,7 @@ type BindMaterial struct {
 	}
 	}
 }
 }
 
 
+// Dump prints out information about the BindMaterial
 func (bm *BindMaterial) Dump(out io.Writer, indent int) {
 func (bm *BindMaterial) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sBindMaterial\n", sIndent(indent))
 	fmt.Fprintf(out, "%sBindMaterial\n", sIndent(indent))
@@ -195,6 +204,7 @@ type InstanceMaterial struct {
 	BindVertexInput []BindVertexInput
 	BindVertexInput []BindVertexInput
 }
 }
 
 
+// Dump prints out information about the InstanceMaterial
 func (im *InstanceMaterial) Dump(out io.Writer, indent int) {
 func (im *InstanceMaterial) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sInstanceMaterial sid:%s name:%s target:%s symbol:%s\n",
 	fmt.Fprintf(out, "%sInstanceMaterial sid:%s name:%s target:%s symbol:%s\n",
@@ -222,6 +232,7 @@ type BindVertexInput struct {
 	InputSet      uint
 	InputSet      uint
 }
 }
 
 
+// Dump prints out information about the BindVertexInput
 func (bvi *BindVertexInput) Dump(out io.Writer, indent int) {
 func (bvi *BindVertexInput) Dump(out io.Writer, indent int) {
 
 
 	fmt.Fprintf(out, "%sBindVertexInput semantic:%s InputSemantic:%s InputSet:%d\n",
 	fmt.Fprintf(out, "%sBindVertexInput semantic:%s InputSemantic:%s InputSet:%d\n",
@@ -247,7 +258,6 @@ func (d *Decoder) decLibraryVisualScenes(start xml.StartElement, dom *Collada) e
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decVisualScene(vsStart xml.StartElement, lv *LibraryVisualScenes) error {
 func (d *Decoder) decVisualScene(vsStart xml.StartElement, lv *LibraryVisualScenes) error {
@@ -274,7 +284,6 @@ func (d *Decoder) decVisualScene(vsStart xml.StartElement, lv *LibraryVisualScen
 			}
 			}
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decNode(nodeStart xml.StartElement, parent *[]*Node) error {
 func (d *Decoder) decNode(nodeStart xml.StartElement, parent *[]*Node) error {
@@ -339,7 +348,6 @@ func (d *Decoder) decNode(nodeStart xml.StartElement, parent *[]*Node) error {
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decMatrix(cdata []byte, n *Node) error {
 func (d *Decoder) decMatrix(cdata []byte, n *Node) error {
@@ -414,7 +422,6 @@ func (d *Decoder) decInstanceGeometry(start xml.StartElement, n *Node) error {
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decBindMaterial(start xml.StartElement, dest **BindMaterial) error {
 func (d *Decoder) decBindMaterial(start xml.StartElement, dest **BindMaterial) error {
@@ -434,7 +441,6 @@ func (d *Decoder) decBindMaterial(start xml.StartElement, dest **BindMaterial) e
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decBindMaterialTechniqueCommon(start xml.StartElement, bm *BindMaterial) error {
 func (d *Decoder) decBindMaterialTechniqueCommon(start xml.StartElement, bm *BindMaterial) error {
@@ -453,7 +459,6 @@ func (d *Decoder) decBindMaterialTechniqueCommon(start xml.StartElement, bm *Bin
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }
 
 
 func (d *Decoder) decInstanceMaterial(start xml.StartElement, bm *BindMaterial) error {
 func (d *Decoder) decInstanceMaterial(start xml.StartElement, bm *BindMaterial) error {
@@ -483,5 +488,4 @@ func (d *Decoder) decInstanceMaterial(start xml.StartElement, bm *BindMaterial)
 			continue
 			continue
 		}
 		}
 	}
 	}
-	return nil
 }
 }

+ 0 - 10
loader/collada/material.go

@@ -68,7 +68,6 @@ func (d *Decoder) NewMaterial(id string) (material.IMaterial, error) {
 	default:
 	default:
 		return nil, fmt.Errorf("Invalid shader element")
 		return nil, fmt.Errorf("Invalid shader element")
 	}
 	}
-	return nil, nil
 }
 }
 
 
 // GetTexture2D returns a pointer to an instance of the Texture2D
 // GetTexture2D returns a pointer to an instance of the Texture2D
@@ -210,9 +209,6 @@ func getColor(ci interface{}) math32.Color {
 	switch c := ci.(type) {
 	switch c := ci.(type) {
 	case *Color:
 	case *Color:
 		return math32.Color{c.Data[0], c.Data[1], c.Data[2]}
 		return math32.Color{c.Data[0], c.Data[1], c.Data[2]}
-		break
-	default:
-		return math32.Color{}
 	}
 	}
 	return math32.Color{}
 	return math32.Color{}
 }
 }
@@ -222,9 +218,6 @@ func getColor4(ci interface{}) math32.Color4 {
 	switch c := ci.(type) {
 	switch c := ci.(type) {
 	case Color:
 	case Color:
 		return math32.Color4{c.Data[0], c.Data[1], c.Data[2], c.Data[3]}
 		return math32.Color4{c.Data[0], c.Data[1], c.Data[2], c.Data[3]}
-		break
-	default:
-		return math32.Color4{}
 	}
 	}
 	return math32.Color4{}
 	return math32.Color4{}
 }
 }
@@ -234,9 +227,6 @@ func getFloatOrParam(vi interface{}) float32 {
 	switch v := vi.(type) {
 	switch v := vi.(type) {
 	case *Float:
 	case *Float:
 		return v.Data
 		return v.Data
-		break
-	default:
-		return 0
 	}
 	}
 	return 0
 	return 0
 }
 }

+ 1 - 0
loader/collada/scene.go

@@ -14,6 +14,7 @@ import (
 	"strings"
 	"strings"
 )
 )
 
 
+// NewScene returns a new collada empty scene
 func (d *Decoder) NewScene() (core.INode, error) {
 func (d *Decoder) NewScene() (core.INode, error) {
 
 
 	sc := d.dom.Scene
 	sc := d.dom.Scene

+ 2 - 2
material/basic.go

@@ -4,12 +4,12 @@
 
 
 package material
 package material
 
 
-import ()
-
+// Basic is a simple material that uses the 'basic' shader
 type Basic struct {
 type Basic struct {
 	Material // Embedded material
 	Material // Embedded material
 }
 }
 
 
+// NewBasic returns a pointer to a new Basic material
 func NewBasic() *Basic {
 func NewBasic() *Basic {
 
 
 	mb := new(Basic)
 	mb := new(Basic)

+ 18 - 2
material/material.go

@@ -11,9 +11,10 @@ import (
 	"github.com/g3n/engine/texture"
 	"github.com/g3n/engine/texture"
 )
 )
 
 
-// Material visible side(s)
+// Side represents the material's visible side(s)
 type Side int
 type Side int
 
 
+// The face side(s) to be rendered. The non-rendered side will be culled to improve performance.
 const (
 const (
 	SideFront  Side = 0
 	SideFront  Side = 0
 	SideBack   Side = 1
 	SideBack   Side = 1
@@ -23,6 +24,7 @@ const (
 // Blending
 // Blending
 type Blending int
 type Blending int
 
 
+// The various blending types
 const (
 const (
 	BlendingNone        Blending = 0
 	BlendingNone        Blending = 0
 	BlendingNormal      Blending = 1
 	BlendingNormal      Blending = 1
@@ -32,7 +34,7 @@ const (
 	BlendingCustom      Blending = 5
 	BlendingCustom      Blending = 5
 )
 )
 
 
-// Use lights flags
+// UseLights flags
 type UseLights int
 type UseLights int
 
 
 const (
 const (
@@ -60,6 +62,7 @@ type Material struct {
 	shaderUnique     bool                 // shader has only one instance (does not depend on lights or textures)
 	shaderUnique     bool                 // shader has only one instance (does not depend on lights or textures)
 	uselights        UseLights            // consider lights for shader selection
 	uselights        UseLights            // consider lights for shader selection
 	sidevis          Side                 // sides visible
 	sidevis          Side                 // sides visible
+	transparent      bool                 // whether at all transparent
 	wireframe        bool                 // show as wirefrme
 	wireframe        bool                 // show as wirefrme
 	depthMask        bool                 // Enable writing into the depth buffer
 	depthMask        bool                 // Enable writing into the depth buffer
 	depthTest        bool                 // Enable depth buffer test
 	depthTest        bool                 // Enable depth buffer test
@@ -90,6 +93,7 @@ func (mat *Material) Init() *Material {
 	mat.refcount = 1
 	mat.refcount = 1
 	mat.uselights = UseLightAll
 	mat.uselights = UseLightAll
 	mat.sidevis = SideFront
 	mat.sidevis = SideFront
+	mat.transparent = false
 	mat.wireframe = false
 	mat.wireframe = false
 	mat.depthMask = true
 	mat.depthMask = true
 	mat.depthFunc = gls.LEQUAL
 	mat.depthFunc = gls.LEQUAL
@@ -186,6 +190,18 @@ func (mat *Material) Side() Side {
 	return mat.sidevis
 	return mat.sidevis
 }
 }
 
 
+// SetTransparent sets whether this material is transparent
+func (mat *Material) SetTransparent(state bool) {
+
+	mat.transparent = state
+}
+
+// Transparent returns whether this material is transparent
+func (mat *Material) Transparent() bool {
+
+	return mat.transparent
+}
+
 func (mat *Material) SetWireframe(state bool) {
 func (mat *Material) SetWireframe(state bool) {
 
 
 	mat.wireframe = state
 	mat.wireframe = state

+ 113 - 86
math32/box2.go

@@ -4,73 +4,91 @@
 
 
 package math32
 package math32
 
 
+// Box2 represents a 2D bounding box defined by two points:
+// the point with minimum coordinates and the point with maximum coordinates.
 type Box2 struct {
 type Box2 struct {
 	min Vector2
 	min Vector2
 	max Vector2
 	max Vector2
 }
 }
 
 
+// NewBox2 creates and returns a pointer to a new Box2 defined
+// by its minimum and maximum coordinates.
 func NewBox2(min, max *Vector2) *Box2 {
 func NewBox2(min, max *Vector2) *Box2 {
 
 
-	this := new(Box2)
-	this.Set(min, max)
-	return this
+	b := new(Box2)
+	b.Set(min, max)
+	return b
 }
 }
 
 
-func (this *Box2) Set(min, max *Vector2) *Box2 {
+// Set sets this bounding box minimum and maximum coordinates.
+// Returns pointer to this updated bounding box.
+func (b *Box2) Set(min, max *Vector2) *Box2 {
 
 
 	if min != nil {
 	if min != nil {
-		this.min = *min
+		b.min = *min
 	} else {
 	} else {
-		this.min.Set(Infinity, Infinity)
+		b.min.Set(Infinity, Infinity)
 	}
 	}
 	if max != nil {
 	if max != nil {
-		this.max = *max
+		b.max = *max
 	} else {
 	} else {
-		this.max.Set(-Infinity, -Infinity)
+		b.max.Set(-Infinity, -Infinity)
 	}
 	}
-	return this
+	return b
 }
 }
 
 
-func (this *Box2) SetFromPoints(points []*Vector2) *Box2 {
+// SetFromPoints set this bounding box from the specified array of points.
+// Returns pointer to this updated bounding box.
+func (b *Box2) SetFromPoints(points []*Vector2) *Box2 {
 
 
-	this.MakeEmpty()
+	b.MakeEmpty()
 	for i := 0; i < len(points); i++ {
 	for i := 0; i < len(points); i++ {
-		this.ExpandByPoint(points[i])
+		b.ExpandByPoint(points[i])
 	}
 	}
-	return this
+	return b
 }
 }
 
 
-func (this *Box2) SetFromCenterAndSize(center, size *Vector2) *Box2 {
+// SetFromCenterAndSize set this bounding box from a center point and size.
+// Size is a vector from the minimum point to the maximum point.
+// Returns pointer to this updated bounding box.
+func (b *Box2) SetFromCenterAndSize(center, size *Vector2) *Box2 {
 
 
 	var v1 Vector2
 	var v1 Vector2
 	halfSize := v1.Copy(size).MultiplyScalar(0.5)
 	halfSize := v1.Copy(size).MultiplyScalar(0.5)
-	this.min.Copy(center).Sub(halfSize)
-	this.max.Copy(center).Add(halfSize)
-	return this
+	b.min.Copy(center).Sub(halfSize)
+	b.max.Copy(center).Add(halfSize)
+	return b
 }
 }
 
 
-func (this *Box2) Copy(box *Box2) *Box2 {
+// Copy copy other to this bounding box.
+// Returns pointer to this updated bounding box.
+func (b *Box2) Copy(box *Box2) *Box2 {
 
 
-	this.min = box.min
-	this.max = box.max
-	return this
+	b.min = box.min
+	b.max = box.max
+	return b
 }
 }
 
 
-func (this *Box2) MakeEmpty() *Box2 {
+// MakeEmpty set this bounding box to empty.
+// Returns pointer to this updated bounding box.
+func (b *Box2) MakeEmpty() *Box2 {
 
 
-	this.min.X = Infinity
-	this.min.Y = Infinity
-	this.max.X = -Infinity
-	this.max.Y = -Infinity
-	return this
+	b.min.X = Infinity
+	b.min.Y = Infinity
+	b.max.X = -Infinity
+	b.max.Y = -Infinity
+	return b
 }
 }
 
 
-func (this *Box2) Empty() bool {
+// Empty returns if this bounding box is empty.
+func (b *Box2) Empty() bool {
 
 
-	return (this.max.X < this.min.X) || (this.max.Y < this.min.Y)
+	return (b.max.X < b.min.X) || (b.max.Y < b.min.Y)
 }
 }
 
 
-func (this *Box2) Center(optionalTarget *Vector2) *Vector2 {
+// Center calculates the center point of this bounding box and
+// stores its pointer to optionalTarget, if not nil, and also returns it.
+func (b *Box2) Center(optionalTarget *Vector2) *Vector2 {
 
 
 	var result *Vector2
 	var result *Vector2
 	if optionalTarget == nil {
 	if optionalTarget == nil {
@@ -78,10 +96,14 @@ func (this *Box2) Center(optionalTarget *Vector2) *Vector2 {
 	} else {
 	} else {
 		result = optionalTarget
 		result = optionalTarget
 	}
 	}
-	return result.AddVectors(&this.min, &this.max).MultiplyScalar(0.5)
+	return result.AddVectors(&b.min, &b.max).MultiplyScalar(0.5)
 }
 }
 
 
-func (this *Box2) Size(optionalTarget *Vector2) *Vector2 {
+// Size calculates the size of this bounding box: the vector  from
+// its minimum point to its maximum point.
+// Store pointer to the calculated size into optionalTarget, if not nil,
+// and also returns it.
+func (b *Box2) Size(optionalTarget *Vector2) *Vector2 {
 
 
 	var result *Vector2
 	var result *Vector2
 	if optionalTarget == nil {
 	if optionalTarget == nil {
@@ -89,74 +111,71 @@ func (this *Box2) Size(optionalTarget *Vector2) *Vector2 {
 	} else {
 	} else {
 		result = optionalTarget
 		result = optionalTarget
 	}
 	}
-	return result.SubVectors(&this.min, &this.max)
+	return result.SubVectors(&b.min, &b.max)
 }
 }
 
 
-func (this *Box2) ExpandByPoint(point *Vector2) *Box2 {
+// ExpandByPoint may expand this bounding box to include the specified point.
+// Returns pointer to this updated bounding box.
+func (b *Box2) ExpandByPoint(point *Vector2) *Box2 {
 
 
-	this.min.Min(point)
-	this.max.Max(point)
-	return this
+	b.min.Min(point)
+	b.max.Max(point)
+	return b
 }
 }
 
 
-func (this *Box2) ExpandByVector(vector *Vector2) *Box2 {
+// ExpandByVector expands this bounding box by the specified vector.
+// Returns pointer to this updated bounding box.
+func (b *Box2) ExpandByVector(vector *Vector2) *Box2 {
 
 
-	this.min.Sub(vector)
-	this.max.Add(vector)
-	return this
+	b.min.Sub(vector)
+	b.max.Add(vector)
+	return b
 }
 }
 
 
-func (this *Box2) ExpandByScalar(scalar float32) *Box2 {
+// ExpandByScalar expands this bounding box by the specified scalar.
+// Returns pointer to this updated bounding box.
+func (b *Box2) ExpandByScalar(scalar float32) *Box2 {
 
 
-	this.min.AddScalar(-scalar)
-	this.max.AddScalar(scalar)
-	return this
+	b.min.AddScalar(-scalar)
+	b.max.AddScalar(scalar)
+	return b
 }
 }
 
 
-func (this *Box2) ContainsPoint(point *Vector2) bool {
+// ContainsPoint returns if this bounding box contains the specified point.
+func (b *Box2) ContainsPoint(point *Vector2) bool {
 
 
-	if point.X < this.min.X || point.X > this.max.X ||
-		point.Y < this.min.Y || point.Y > this.max.Y {
+	if point.X < b.min.X || point.X > b.max.X ||
+		point.Y < b.min.Y || point.Y > b.max.Y {
 		return false
 		return false
 	}
 	}
 	return true
 	return true
 }
 }
 
 
-func (this *Box2) ContainsBox(box *Box2) bool {
+// ContainsBox returns if this bounding box contains other box.
+func (b *Box2) ContainsBox(other *Box2) bool {
 
 
-	if (this.min.X <= box.min.X) && (box.max.X <= this.max.X) &&
-		(this.min.Y <= box.min.Y) && (box.max.Y <= this.max.Y) {
+	if (b.min.X <= other.min.X) && (other.max.X <= b.max.X) &&
+		(b.min.Y <= other.min.Y) && (other.max.Y <= b.max.Y) {
 		return true
 		return true
 
 
 	}
 	}
 	return false
 	return false
 }
 }
 
 
-func (this *Box2) GetParameter(point *Vector2, optionalTarget *Vector2) *Vector2 {
-
-	var result *Vector2
-	if optionalTarget == nil {
-		result = NewVector2(0, 0)
-	} else {
-		result = optionalTarget
-	}
-	return result.Set(
-		(point.X-this.min.X)/(this.max.X-this.min.X),
-		(point.Y-this.min.Y)/(this.max.Y-this.min.Y),
-	)
-}
-
-func (this *Box2) IsIntersectionBox(box *Box2) bool {
+// IsIntersectionBox returns if other box intersects this one.
+func (b *Box2) IsIntersectionBox(other *Box2) bool {
 
 
 	// using 6 splitting planes to rule out intersections.
 	// using 6 splitting planes to rule out intersections.
-	if box.max.X < this.min.X || box.min.X > this.max.X ||
-		box.max.Y < this.min.Y || box.min.Y > this.max.Y {
+	if other.max.X < b.min.X || other.min.X > b.max.X ||
+		other.max.Y < b.min.Y || other.min.Y > b.max.Y {
 		return false
 		return false
 	}
 	}
 	return true
 	return true
 }
 }
 
 
-func (this *Box2) ClampPoint(point *Vector2, optionalTarget *Vector2) *Vector2 {
+// ClampPoint calculates a new point which is the specified point clamped inside this box.
+// Stores the pointer to this new point into optionaTarget, if not nil, and also returns it.
+func (b *Box2) ClampPoint(point *Vector2, optionalTarget *Vector2) *Vector2 {
 
 
 	var result *Vector2
 	var result *Vector2
 	if optionalTarget == nil {
 	if optionalTarget == nil {
@@ -164,38 +183,46 @@ func (this *Box2) ClampPoint(point *Vector2, optionalTarget *Vector2) *Vector2 {
 	} else {
 	} else {
 		result = optionalTarget
 		result = optionalTarget
 	}
 	}
-	return result.Copy(point).Clamp(&this.min, &this.max)
+	return result.Copy(point).Clamp(&b.min, &b.max)
 }
 }
 
 
-func (this *Box2) DistanceToPoint(point *Vector2) float32 {
+// DistanceToPoint returns the distance from this box to the specified point.
+func (b *Box2) DistanceToPoint(point *Vector2) float32 {
 
 
 	v1 := NewVector2(0, 0)
 	v1 := NewVector2(0, 0)
-	clampedPoint := v1.Copy(point).Clamp(&this.min, &this.max)
+	clampedPoint := v1.Copy(point).Clamp(&b.min, &b.max)
 	return clampedPoint.Sub(point).Length()
 	return clampedPoint.Sub(point).Length()
 }
 }
 
 
-func (this *Box2) Intersect(box *Box2) *Box2 {
+// Intersect sets this box to the intersection with other box.
+// Returns pointer to this updated bounding box.
+func (b *Box2) Intersect(other *Box2) *Box2 {
 
 
-	this.min.Max(&box.min)
-	this.max.Min(&box.max)
-	return this
+	b.min.Max(&other.min)
+	b.max.Min(&other.max)
+	return b
 }
 }
 
 
-func (this *Box2) Union(box *Box2) *Box2 {
+// Union set this box to the union with other box.
+// Returns pointer to this updated bounding box.
+func (b *Box2) Union(other *Box2) *Box2 {
 
 
-	this.min.Min(&box.min)
-	this.max.Max(&box.max)
-	return this
+	b.min.Min(&other.min)
+	b.max.Max(&other.max)
+	return b
 }
 }
 
 
-func (this *Box2) Translate(offset *Vector2) *Box2 {
+// Translate translates the position of this box by offset.
+// Returns pointer to this updated box.
+func (b *Box2) Translate(offset *Vector2) *Box2 {
 
 
-	this.min.Add(offset)
-	this.max.Add(offset)
-	return this
+	b.min.Add(offset)
+	b.max.Add(offset)
+	return b
 }
 }
 
 
-func (this *Box2) Equals(box *Box2) bool {
+// Equals returns if this box is equal to other
+func (b *Box2) Equals(other *Box2) bool {
 
 
-	return box.min.Equals(&this.min) && box.max.Equals(&this.max)
+	return other.min.Equals(&b.min) && other.max.Equals(&b.max)
 }
 }

+ 141 - 119
math32/box3.go

@@ -4,82 +4,93 @@
 
 
 package math32
 package math32
 
 
+// Box3 represents a 3D bounding box defined by two points:
+// the point with minimum coordinates and the point with maximum coordinates.
 type Box3 struct {
 type Box3 struct {
 	Min Vector3
 	Min Vector3
 	Max Vector3
 	Max Vector3
 }
 }
 
 
+// NewBox3 creates and returns a pointer to a new Box3 defined
+// by its minimum and maximum coordinates.
 func NewBox3(min, max *Vector3) *Box3 {
 func NewBox3(min, max *Vector3) *Box3 {
 
 
-	this := new(Box3)
-	this.Set(min, max)
-	return this
+	b := new(Box3)
+	b.Set(min, max)
+	return b
 }
 }
 
 
-func (this *Box3) Set(min, max *Vector3) *Box3 {
+// Set sets this bounding box minimum and maximum coordinates.
+// Returns pointer to this updated bounding box.
+func (b *Box3) Set(min, max *Vector3) *Box3 {
 
 
 	if min != nil {
 	if min != nil {
-		this.Min = *min
+		b.Min = *min
 	} else {
 	} else {
-		this.Min.Set(Infinity, Infinity, Infinity)
+		b.Min.Set(Infinity, Infinity, Infinity)
 	}
 	}
 	if max != nil {
 	if max != nil {
-		this.Max = *max
+		b.Max = *max
 	} else {
 	} else {
-		this.Max.Set(-Infinity, -Infinity, -Infinity)
+		b.Max.Set(-Infinity, -Infinity, -Infinity)
 	}
 	}
-	return this
+	return b
 }
 }
 
 
-func (this *Box3) SetFromPoints(points []Vector3) *Box3 {
+// SetFromPoints set this bounding box from the specified array of points.
+// Returns pointer to this updated bounding box.
+func (b *Box3) SetFromPoints(points []Vector3) *Box3 {
 
 
-	this.MakeEmpty()
+	b.MakeEmpty()
 	for i := 0; i < len(points); i++ {
 	for i := 0; i < len(points); i++ {
-		this.ExpandByPoint(&points[i])
+		b.ExpandByPoint(&points[i])
 	}
 	}
-	return this
+	return b
 }
 }
 
 
-func (this *Box3) SetFromCenterAndSize(center, size *Vector3) *Box3 {
+// SetFromCenterAndSize set this bounding box from a center point and size.
+// Size is a vector from the minimum point to the maximum point.
+// Returns pointer to this updated bounding box.
+func (b *Box3) SetFromCenterAndSize(center, size *Vector3) *Box3 {
 
 
 	v1 := NewVector3(0, 0, 0)
 	v1 := NewVector3(0, 0, 0)
 	halfSize := v1.Copy(size).MultiplyScalar(0.5)
 	halfSize := v1.Copy(size).MultiplyScalar(0.5)
-	this.Min.Copy(center).Sub(halfSize)
-	this.Max.Copy(center).Add(halfSize)
-	return this
+	b.Min.Copy(center).Sub(halfSize)
+	b.Max.Copy(center).Add(halfSize)
+	return b
 }
 }
 
 
-//func (this *Box3) SetFromObject(object *Object3D) *Box3 {
-//
-//	// TODO object.UpdateMatrixWorld(true)
-//
-//	return this
-//}
+// Copy copy other to this bounding box.
+// Returns pointer to this updated bounding box.
+func (b *Box3) Copy(other *Box3) *Box3 {
 
 
-func (this *Box3) Copy(box *Box3) *Box3 {
-
-	this.Min = box.Min
-	this.Max = box.Max
-	return this
+	b.Min = other.Min
+	b.Max = other.Max
+	return b
 }
 }
 
 
-func (this *Box3) MakeEmpty() *Box3 {
-
-	this.Min.X = Infinity
-	this.Min.Y = Infinity
-	this.Min.Z = Infinity
-	this.Max.X = -Infinity
-	this.Max.Y = -Infinity
-	this.Max.Z = -Infinity
-	return this
+// MakeEmpty set this bounding box to empty.
+// Returns pointer to this updated bounding box.
+func (b *Box3) MakeEmpty() *Box3 {
+
+	b.Min.X = Infinity
+	b.Min.Y = Infinity
+	b.Min.Z = Infinity
+	b.Max.X = -Infinity
+	b.Max.Y = -Infinity
+	b.Max.Z = -Infinity
+	return b
 }
 }
 
 
-func (this *Box3) Empty() bool {
+// Empty returns if this bounding box is empty.
+func (b *Box3) Empty() bool {
 
 
-	return (this.Max.X < this.Min.X) || (this.Max.Y < this.Min.Y) || (this.Max.Z < this.Min.Z)
+	return (b.Max.X < b.Min.X) || (b.Max.Y < b.Min.Y) || (b.Max.Z < b.Min.Z)
 }
 }
 
 
-func (this *Box3) Center(optionalTarget *Vector3) *Vector3 {
+// Center calculates the center point of this bounding box and
+// stores its pointer to optionalTarget, if not nil, and also returns it.
+func (b *Box3) Center(optionalTarget *Vector3) *Vector3 {
 
 
 	var result *Vector3
 	var result *Vector3
 	if optionalTarget == nil {
 	if optionalTarget == nil {
@@ -87,10 +98,14 @@ func (this *Box3) Center(optionalTarget *Vector3) *Vector3 {
 	} else {
 	} else {
 		result = optionalTarget
 		result = optionalTarget
 	}
 	}
-	return result.AddVectors(&this.Min, &this.Max).MultiplyScalar(0.5)
+	return result.AddVectors(&b.Min, &b.Max).MultiplyScalar(0.5)
 }
 }
 
 
-func (this *Box3) Size(optionalTarget *Vector3) *Vector3 {
+// Size calculates the size of this bounding box: the vector  from
+// its minimum point to its maximum point.
+// Store pointer to the calculated size into optionalTarget, if not nil,
+// and also returns it.
+func (b *Box3) Size(optionalTarget *Vector3) *Vector3 {
 
 
 	var result *Vector3
 	var result *Vector3
 	if optionalTarget == nil {
 	if optionalTarget == nil {
@@ -98,80 +113,74 @@ func (this *Box3) Size(optionalTarget *Vector3) *Vector3 {
 	} else {
 	} else {
 		result = optionalTarget
 		result = optionalTarget
 	}
 	}
-	return result.SubVectors(&this.Min, &this.Max)
+	return result.SubVectors(&b.Min, &b.Max)
 }
 }
 
 
-func (this *Box3) ExpandByPoint(point *Vector3) *Box3 {
+// ExpandByPoint may expand this bounding box to include the specified point.
+// Returns pointer to this updated bounding box.
+func (b *Box3) ExpandByPoint(point *Vector3) *Box3 {
 
 
-	this.Min.Min(point)
-	this.Max.Max(point)
-	return this
+	b.Min.Min(point)
+	b.Max.Max(point)
+	return b
 }
 }
 
 
-func (this *Box3) ExpandByVector(vector *Vector3) *Box3 {
+// ExpandByVector expands this bounding box by the specified vector.
+// Returns pointer to this updated bounding box.
+func (b *Box3) ExpandByVector(vector *Vector3) *Box3 {
 
 
-	this.Min.Sub(vector)
-	this.Max.Add(vector)
-	return this
+	b.Min.Sub(vector)
+	b.Max.Add(vector)
+	return b
 }
 }
 
 
-func (this *Box3) ExpandByScalar(scalar float32) *Box3 {
+// ExpandByScalar expands this bounding box by the specified scalar.
+// Returns pointer to this updated bounding box.
+func (b *Box3) ExpandByScalar(scalar float32) *Box3 {
 
 
-	this.Min.AddScalar(-scalar)
-	this.Max.AddScalar(scalar)
-	return this
+	b.Min.AddScalar(-scalar)
+	b.Max.AddScalar(scalar)
+	return b
 }
 }
 
 
-func (this *Box3) ContainsPoint(point *Vector3) bool {
+// ContainsPoint returns if this bounding box contains the specified point.
+func (b *Box3) ContainsPoint(point *Vector3) bool {
 
 
-	if point.X < this.Min.X || point.X > this.Max.X ||
-		point.Y < this.Min.Y || point.Y > this.Max.Y ||
-		point.Z < this.Min.Z || point.Z > this.Max.Z {
+	if point.X < b.Min.X || point.X > b.Max.X ||
+		point.Y < b.Min.Y || point.Y > b.Max.Y ||
+		point.Z < b.Min.Z || point.Z > b.Max.Z {
 		return false
 		return false
 	}
 	}
 	return true
 	return true
 }
 }
 
 
-func (this *Box3) ContainsBox(box *Box3) bool {
+// ContainsBox returns if this bounding box contains other box.
+func (b *Box3) ContainsBox(box *Box3) bool {
 
 
-	if (this.Min.X <= box.Max.X) && (box.Max.X <= this.Max.X) &&
-		(this.Min.Y <= box.Min.Y) && (box.Max.Y <= this.Max.Y) &&
-		(this.Min.Z <= box.Min.Z) && (box.Max.Z <= this.Max.Z) {
+	if (b.Min.X <= box.Max.X) && (box.Max.X <= b.Max.X) &&
+		(b.Min.Y <= box.Min.Y) && (box.Max.Y <= b.Max.Y) &&
+		(b.Min.Z <= box.Min.Z) && (box.Max.Z <= b.Max.Z) {
 		return true
 		return true
 
 
 	}
 	}
 	return false
 	return false
 }
 }
 
 
-func (this *Box3) GetParameter(point *Vector3, optionalTarget *Vector3) *Vector3 {
-
-	// This can potentially have a divide by zero if the box
-	// has a size dimension of 0.
-	var result *Vector3
-	if optionalTarget == nil {
-		result = NewVector3(0, 0, 0)
-	} else {
-		result = optionalTarget
-	}
-	return result.Set(
-		(point.X-this.Min.X)/(this.Max.X-this.Min.X),
-		(point.Y-this.Min.Y)/(this.Max.Y-this.Min.Y),
-		(point.Z-this.Min.Z)/(this.Max.Z-this.Min.Z),
-	)
-}
-
-func (this *Box3) IsIntersectionBox(box *Box3) bool {
+// IsIntersectionBox returns if other box intersects this one.
+func (b *Box3) IsIntersectionBox(other *Box3) bool {
 
 
 	// using 6 splitting planes to rule out intersections.
 	// using 6 splitting planes to rule out intersections.
-	if box.Max.X < this.Min.X || box.Min.X > this.Max.X ||
-		box.Max.Y < this.Min.Y || box.Min.Y > this.Max.Y ||
-		box.Max.Z < this.Min.Z || box.Min.Z > this.Max.Z {
+	if other.Max.X < b.Min.X || other.Min.X > b.Max.X ||
+		other.Max.Y < b.Min.Y || other.Min.Y > b.Max.Y ||
+		other.Max.Z < b.Min.Z || other.Min.Z > b.Max.Z {
 		return false
 		return false
 	}
 	}
 	return true
 	return true
 }
 }
 
 
-func (this *Box3) ClampPoint(point *Vector3, optionalTarget *Vector3) *Vector3 {
+// ClampPoint calculates a new point which is the specified point clamped inside this box.
+// Stores the pointer to this new point into optionaTarget, if not nil, and also returns it.
+func (b *Box3) ClampPoint(point *Vector3, optionalTarget *Vector3) *Vector3 {
 
 
 	var result *Vector3
 	var result *Vector3
 	if optionalTarget == nil {
 	if optionalTarget == nil {
@@ -179,17 +188,20 @@ func (this *Box3) ClampPoint(point *Vector3, optionalTarget *Vector3) *Vector3 {
 	} else {
 	} else {
 		result = optionalTarget
 		result = optionalTarget
 	}
 	}
-	return result.Copy(point).Clamp(&this.Min, &this.Max)
+	return result.Copy(point).Clamp(&b.Min, &b.Max)
 }
 }
 
 
-func (this *Box3) DistanceToPoint(point *Vector3) float32 {
+// DistanceToPoint returns the distance from this box to the specified point.
+func (b *Box3) DistanceToPoint(point *Vector3) float32 {
 
 
 	var v1 Vector3
 	var v1 Vector3
-	clampedPoint := v1.Copy(point).Clamp(&this.Min, &this.Max)
+	clampedPoint := v1.Copy(point).Clamp(&b.Min, &b.Max)
 	return clampedPoint.Sub(point).Length()
 	return clampedPoint.Sub(point).Length()
 }
 }
 
 
-func (this *Box3) GetBoundingSphere(optionalTarget *Sphere) *Sphere {
+// GetBoundingSphere creates a bounding sphere to this bounding box.
+// Store its pointer into optionalTarget, if not nil, and also returns it.
+func (b *Box3) GetBoundingSphere(optionalTarget *Sphere) *Sphere {
 
 
 	var v1 Vector3
 	var v1 Vector3
 	var result *Sphere
 	var result *Sphere
@@ -199,27 +211,33 @@ func (this *Box3) GetBoundingSphere(optionalTarget *Sphere) *Sphere {
 		result = optionalTarget
 		result = optionalTarget
 	}
 	}
 
 
-	result.Center = *this.Center(nil)
-	result.Radius = this.Size(&v1).Length() * 0.5
+	result.Center = *b.Center(nil)
+	result.Radius = b.Size(&v1).Length() * 0.5
 
 
 	return result
 	return result
 }
 }
 
 
-func (this *Box3) Intersect(box *Box3) *Box3 {
+// Intersect sets this box to the intersection with other box.
+// Returns pointer to this updated bounding box.
+func (b *Box3) Intersect(other *Box3) *Box3 {
 
 
-	this.Min.Max(&box.Min)
-	this.Max.Min(&box.Max)
-	return this
+	b.Min.Max(&other.Min)
+	b.Max.Min(&other.Max)
+	return b
 }
 }
 
 
-func (this *Box3) Union(box *Box3) *Box3 {
+// Union set this box to the union with other box.
+// Returns pointer to this updated bounding box.
+func (b *Box3) Union(other *Box3) *Box3 {
 
 
-	this.Min.Min(&box.Min)
-	this.Max.Max(&box.Max)
-	return this
+	b.Min.Min(&other.Min)
+	b.Max.Max(&other.Max)
+	return b
 }
 }
 
 
-func (this *Box3) ApplyMatrix4(matrix *Matrix4) *Box3 {
+// ApplyMatrix4 applies the specified matrix to the vertices of this bounding box.
+// Returns pointer to this updated bounding box.
+func (b *Box3) ApplyMatrix4(matrix *Matrix4) *Box3 {
 
 
 	points := []Vector3{
 	points := []Vector3{
 		Vector3{},
 		Vector3{},
@@ -232,34 +250,38 @@ func (this *Box3) ApplyMatrix4(matrix *Matrix4) *Box3 {
 		Vector3{},
 		Vector3{},
 	}
 	}
 
 
-	points[0].Set(this.Min.X, this.Min.Y, this.Min.Z).ApplyMatrix4(matrix) // 000
-	points[1].Set(this.Min.X, this.Min.Y, this.Max.Z).ApplyMatrix4(matrix) // 001
-	points[2].Set(this.Min.X, this.Max.Y, this.Min.Z).ApplyMatrix4(matrix) // 010
-	points[3].Set(this.Min.X, this.Max.Y, this.Max.Z).ApplyMatrix4(matrix) // 011
-	points[4].Set(this.Max.X, this.Min.Y, this.Min.Z).ApplyMatrix4(matrix) // 100
-	points[5].Set(this.Max.X, this.Min.Y, this.Max.Z).ApplyMatrix4(matrix) // 101
-	points[6].Set(this.Max.X, this.Max.Y, this.Min.Z).ApplyMatrix4(matrix) // 110
-	points[7].Set(this.Max.X, this.Max.Y, this.Max.Z).ApplyMatrix4(matrix) // 111
+	points[0].Set(b.Min.X, b.Min.Y, b.Min.Z).ApplyMatrix4(matrix) // 000
+	points[1].Set(b.Min.X, b.Min.Y, b.Max.Z).ApplyMatrix4(matrix) // 001
+	points[2].Set(b.Min.X, b.Max.Y, b.Min.Z).ApplyMatrix4(matrix) // 010
+	points[3].Set(b.Min.X, b.Max.Y, b.Max.Z).ApplyMatrix4(matrix) // 011
+	points[4].Set(b.Max.X, b.Min.Y, b.Min.Z).ApplyMatrix4(matrix) // 100
+	points[5].Set(b.Max.X, b.Min.Y, b.Max.Z).ApplyMatrix4(matrix) // 101
+	points[6].Set(b.Max.X, b.Max.Y, b.Min.Z).ApplyMatrix4(matrix) // 110
+	points[7].Set(b.Max.X, b.Max.Y, b.Max.Z).ApplyMatrix4(matrix) // 111
 
 
-	this.MakeEmpty()
-	this.SetFromPoints(points)
+	b.MakeEmpty()
+	b.SetFromPoints(points)
 
 
-	return this
+	return b
 }
 }
 
 
-func (this *Box3) Translate(offset *Vector3) *Box3 {
+// Translate translates the position of this box by offset.
+// Returns pointer to this updated box.
+func (b *Box3) Translate(offset *Vector3) *Box3 {
 
 
-	this.Min.Add(offset)
-	this.Max.Add(offset)
-	return this
+	b.Min.Add(offset)
+	b.Max.Add(offset)
+	return b
 }
 }
 
 
-func (this *Box3) Equals(box *Box3) bool {
+// Equals returns if this box is equal to other
+func (b *Box3) Equals(other *Box3) bool {
 
 
-	return box.Min.Equals(&this.Min) && box.Max.Equals(&this.Max)
+	return other.Min.Equals(&b.Min) && other.Max.Equals(&b.Max)
 }
 }
 
 
-func (this *Box3) Clone() *Box3 {
+// Clone creates and returns a pointer to copy of this bounding box
+func (b *Box3) Clone() *Box3 {
 
 
-	return NewBox3(&this.Min, &this.Max)
+	return NewBox3(&b.Min, &b.Max)
 }
 }

+ 147 - 147
math32/color.go

@@ -148,151 +148,151 @@ func IsColorName(name string) (Color, bool) {
 // mapColorNames maps standard web color names to a Color with
 // mapColorNames maps standard web color names to a Color with
 // the standard web color's RGB component values
 // the standard web color's RGB component values
 var mapColorNames = map[string]Color{
 var mapColorNames = map[string]Color{
-	"aliceblue":            Color{0.941, 0.973, 1.000},
-	"antiquewhite":         Color{0.980, 0.922, 0.843},
-	"aqua":                 Color{0.000, 1.000, 1.000},
-	"aquamarine":           Color{0.498, 1.000, 0.831},
-	"azure":                Color{0.941, 1.000, 1.000},
-	"beige":                Color{0.961, 0.961, 0.863},
-	"bisque":               Color{1.000, 0.894, 0.769},
-	"black":                Color{0.000, 0.000, 0.000},
-	"blanchedalmond":       Color{1.000, 0.922, 0.804},
-	"blue":                 Color{0.000, 0.000, 1.000},
-	"blueviolet":           Color{0.541, 0.169, 0.886},
-	"brown":                Color{0.647, 0.165, 0.165},
-	"burlywood":            Color{0.871, 0.722, 0.529},
-	"cadetblue":            Color{0.373, 0.620, 0.627},
-	"chartreuse":           Color{0.498, 1.000, 0.000},
-	"chocolate":            Color{0.824, 0.412, 0.118},
-	"coral":                Color{1.000, 0.498, 0.314},
-	"cornflowerblue":       Color{0.392, 0.584, 0.929},
-	"cornsilk":             Color{1.000, 0.973, 0.863},
-	"crimson":              Color{0.863, 0.078, 0.235},
-	"cyan":                 Color{0.000, 1.000, 1.000},
-	"darkblue":             Color{0.000, 0.000, 0.545},
-	"darkcyan":             Color{0.000, 0.545, 0.545},
-	"darkgoldenrod":        Color{0.722, 0.525, 0.043},
-	"darkgray":             Color{0.663, 0.663, 0.663},
-	"darkgreen":            Color{0.000, 0.392, 0.000},
-	"darkgrey":             Color{0.663, 0.663, 0.663},
-	"darkkhaki":            Color{0.741, 0.718, 0.420},
-	"darkmagenta":          Color{0.545, 0.000, 0.545},
-	"darkolivegreen":       Color{0.333, 0.420, 0.184},
-	"darkorange":           Color{1.000, 0.549, 0.000},
-	"darkorchid":           Color{0.600, 0.196, 0.800},
-	"darkred":              Color{0.545, 0.000, 0.000},
-	"darksalmon":           Color{0.914, 0.588, 0.478},
-	"darkseagreen":         Color{0.561, 0.737, 0.561},
-	"darkslateblue":        Color{0.282, 0.239, 0.545},
-	"darkslategray":        Color{0.184, 0.310, 0.310},
-	"darkslategrey":        Color{0.184, 0.310, 0.310},
-	"darkturquoise":        Color{0.000, 0.808, 0.820},
-	"darkviolet":           Color{0.580, 0.000, 0.827},
-	"deeppink":             Color{1.000, 0.078, 0.576},
-	"deepskyblue":          Color{0.000, 0.749, 1.000},
-	"dimgray":              Color{0.412, 0.412, 0.412},
-	"dimgrey":              Color{0.412, 0.412, 0.412},
-	"dodgerblue":           Color{0.118, 0.565, 1.000},
-	"firebrick":            Color{0.698, 0.133, 0.133},
-	"floralwhite":          Color{1.000, 0.980, 0.941},
-	"forestgreen":          Color{0.133, 0.545, 0.133},
-	"fuchsia":              Color{1.000, 0.000, 1.000},
-	"gainsboro":            Color{0.863, 0.863, 0.863},
-	"ghostwhite":           Color{0.973, 0.973, 1.000},
-	"gold":                 Color{1.000, 0.843, 0.000},
-	"goldenrod":            Color{0.855, 0.647, 0.125},
-	"gray":                 Color{0.502, 0.502, 0.502},
-	"green":                Color{0.000, 0.502, 0.000},
-	"greenyellow":          Color{0.678, 1.000, 0.184},
-	"grey":                 Color{0.502, 0.502, 0.502},
-	"honeydew":             Color{0.941, 1.000, 0.941},
-	"hotpink":              Color{1.000, 0.412, 0.706},
-	"indianred":            Color{0.804, 0.361, 0.361},
-	"indigo":               Color{0.294, 0.000, 0.510},
-	"ivory":                Color{1.000, 1.000, 0.941},
-	"khaki":                Color{0.941, 0.902, 0.549},
-	"lavender":             Color{0.902, 0.902, 0.980},
-	"lavenderblush":        Color{1.000, 0.941, 0.961},
-	"lawngreen":            Color{0.486, 0.988, 0.000},
-	"lemonchiffon":         Color{1.000, 0.980, 0.804},
-	"lightblue":            Color{0.678, 0.847, 0.902},
-	"lightcoral":           Color{0.941, 0.502, 0.502},
-	"lightcyan":            Color{0.878, 1.000, 1.000},
-	"lightgoldenrodyellow": Color{0.980, 0.980, 0.824},
-	"lightgray":            Color{0.827, 0.827, 0.827},
-	"lightgreen":           Color{0.565, 0.933, 0.565},
-	"lightgrey":            Color{0.827, 0.827, 0.827},
-	"lightpink":            Color{1.000, 0.714, 0.757},
-	"lightsalmon":          Color{1.000, 0.627, 0.478},
-	"lightseagreen":        Color{0.125, 0.698, 0.667},
-	"lightskyblue":         Color{0.529, 0.808, 0.980},
-	"lightslategray":       Color{0.467, 0.533, 0.600},
-	"lightslategrey":       Color{0.467, 0.533, 0.600},
-	"lightsteelblue":       Color{0.690, 0.769, 0.871},
-	"lightyellow":          Color{1.000, 1.000, 0.878},
-	"lime":                 Color{0.000, 1.000, 0.000},
-	"limegreen":            Color{0.196, 0.804, 0.196},
-	"linen":                Color{0.980, 0.941, 0.902},
-	"magenta":              Color{1.000, 0.000, 1.000},
-	"maroon":               Color{0.502, 0.000, 0.000},
-	"mediumaquamarine":     Color{0.400, 0.804, 0.667},
-	"mediumblue":           Color{0.000, 0.000, 0.804},
-	"mediumorchid":         Color{0.729, 0.333, 0.827},
-	"mediumpurple":         Color{0.576, 0.439, 0.859},
-	"mediumseagreen":       Color{0.235, 0.702, 0.443},
-	"mediumslateblue":      Color{0.482, 0.408, 0.933},
-	"mediumspringgreen":    Color{0.000, 0.980, 0.604},
-	"mediumturquoise":      Color{0.282, 0.820, 0.800},
-	"mediumvioletred":      Color{0.780, 0.082, 0.522},
-	"midnightblue":         Color{0.098, 0.098, 0.439},
-	"mintcream":            Color{0.961, 1.000, 0.980},
-	"mistyrose":            Color{1.000, 0.894, 0.882},
-	"moccasin":             Color{1.000, 0.894, 0.710},
-	"navajowhite":          Color{1.000, 0.871, 0.678},
-	"navy":                 Color{0.000, 0.000, 0.502},
-	"oldlace":              Color{0.992, 0.961, 0.902},
-	"olive":                Color{0.502, 0.502, 0.000},
-	"olivedrab":            Color{0.420, 0.557, 0.137},
-	"orange":               Color{1.000, 0.647, 0.000},
-	"orangered":            Color{1.000, 0.271, 0.000},
-	"orchid":               Color{0.855, 0.439, 0.839},
-	"palegoldenrod":        Color{0.933, 0.910, 0.667},
-	"palegreen":            Color{0.596, 0.984, 0.596},
-	"paleturquoise":        Color{0.686, 0.933, 0.933},
-	"palevioletred":        Color{0.859, 0.439, 0.576},
-	"papayawhip":           Color{1.000, 0.937, 0.835},
-	"peachpuff":            Color{1.000, 0.855, 0.725},
-	"peru":                 Color{0.804, 0.522, 0.247},
-	"pink":                 Color{1.000, 0.753, 0.796},
-	"plum":                 Color{0.867, 0.627, 0.867},
-	"powderblue":           Color{0.690, 0.878, 0.902},
-	"purple":               Color{0.502, 0.000, 0.502},
-	"red":                  Color{1.000, 0.000, 0.000},
-	"rosybrown":            Color{0.737, 0.561, 0.561},
-	"royalblue":            Color{0.255, 0.412, 0.882},
-	"saddlebrown":          Color{0.545, 0.271, 0.075},
-	"salmon":               Color{0.980, 0.502, 0.447},
-	"sandybrown":           Color{0.957, 0.643, 0.376},
-	"seagreen":             Color{0.180, 0.545, 0.341},
-	"seashell":             Color{1.000, 0.961, 0.933},
-	"sienna":               Color{0.627, 0.322, 0.176},
-	"silver":               Color{0.753, 0.753, 0.753},
-	"skyblue":              Color{0.529, 0.808, 0.922},
-	"slateblue":            Color{0.416, 0.353, 0.804},
-	"slategray":            Color{0.439, 0.502, 0.565},
-	"slategrey":            Color{0.439, 0.502, 0.565},
-	"snow":                 Color{1.000, 0.980, 0.980},
-	"springgreen":          Color{0.000, 1.000, 0.498},
-	"steelblue":            Color{0.275, 0.510, 0.706},
-	"tan":                  Color{0.824, 0.706, 0.549},
-	"teal":                 Color{0.000, 0.502, 0.502},
-	"thistle":              Color{0.847, 0.749, 0.847},
-	"tomato":               Color{1.000, 0.388, 0.278},
-	"turquoise":            Color{0.251, 0.878, 0.816},
-	"violet":               Color{0.933, 0.510, 0.933},
-	"wheat":                Color{0.961, 0.871, 0.702},
-	"white":                Color{1.000, 1.000, 1.000},
-	"whitesmoke":           Color{0.961, 0.961, 0.961},
-	"yellow":               Color{1.000, 1.000, 0.000},
-	"yellowgreen":          Color{0.604, 0.804, 0.196},
+	"aliceblue":            {0.941, 0.973, 1.000},
+	"antiquewhite":         {0.980, 0.922, 0.843},
+	"aqua":                 {0.000, 1.000, 1.000},
+	"aquamarine":           {0.498, 1.000, 0.831},
+	"azure":                {0.941, 1.000, 1.000},
+	"beige":                {0.961, 0.961, 0.863},
+	"bisque":               {1.000, 0.894, 0.769},
+	"black":                {0.000, 0.000, 0.000},
+	"blanchedalmond":       {1.000, 0.922, 0.804},
+	"blue":                 {0.000, 0.000, 1.000},
+	"blueviolet":           {0.541, 0.169, 0.886},
+	"brown":                {0.647, 0.165, 0.165},
+	"burlywood":            {0.871, 0.722, 0.529},
+	"cadetblue":            {0.373, 0.620, 0.627},
+	"chartreuse":           {0.498, 1.000, 0.000},
+	"chocolate":            {0.824, 0.412, 0.118},
+	"coral":                {1.000, 0.498, 0.314},
+	"cornflowerblue":       {0.392, 0.584, 0.929},
+	"cornsilk":             {1.000, 0.973, 0.863},
+	"crimson":              {0.863, 0.078, 0.235},
+	"cyan":                 {0.000, 1.000, 1.000},
+	"darkblue":             {0.000, 0.000, 0.545},
+	"darkcyan":             {0.000, 0.545, 0.545},
+	"darkgoldenrod":        {0.722, 0.525, 0.043},
+	"darkgray":             {0.663, 0.663, 0.663},
+	"darkgreen":            {0.000, 0.392, 0.000},
+	"darkgrey":             {0.663, 0.663, 0.663},
+	"darkkhaki":            {0.741, 0.718, 0.420},
+	"darkmagenta":          {0.545, 0.000, 0.545},
+	"darkolivegreen":       {0.333, 0.420, 0.184},
+	"darkorange":           {1.000, 0.549, 0.000},
+	"darkorchid":           {0.600, 0.196, 0.800},
+	"darkred":              {0.545, 0.000, 0.000},
+	"darksalmon":           {0.914, 0.588, 0.478},
+	"darkseagreen":         {0.561, 0.737, 0.561},
+	"darkslateblue":        {0.282, 0.239, 0.545},
+	"darkslategray":        {0.184, 0.310, 0.310},
+	"darkslategrey":        {0.184, 0.310, 0.310},
+	"darkturquoise":        {0.000, 0.808, 0.820},
+	"darkviolet":           {0.580, 0.000, 0.827},
+	"deeppink":             {1.000, 0.078, 0.576},
+	"deepskyblue":          {0.000, 0.749, 1.000},
+	"dimgray":              {0.412, 0.412, 0.412},
+	"dimgrey":              {0.412, 0.412, 0.412},
+	"dodgerblue":           {0.118, 0.565, 1.000},
+	"firebrick":            {0.698, 0.133, 0.133},
+	"floralwhite":          {1.000, 0.980, 0.941},
+	"forestgreen":          {0.133, 0.545, 0.133},
+	"fuchsia":              {1.000, 0.000, 1.000},
+	"gainsboro":            {0.863, 0.863, 0.863},
+	"ghostwhite":           {0.973, 0.973, 1.000},
+	"gold":                 {1.000, 0.843, 0.000},
+	"goldenrod":            {0.855, 0.647, 0.125},
+	"gray":                 {0.502, 0.502, 0.502},
+	"green":                {0.000, 0.502, 0.000},
+	"greenyellow":          {0.678, 1.000, 0.184},
+	"grey":                 {0.502, 0.502, 0.502},
+	"honeydew":             {0.941, 1.000, 0.941},
+	"hotpink":              {1.000, 0.412, 0.706},
+	"indianred":            {0.804, 0.361, 0.361},
+	"indigo":               {0.294, 0.000, 0.510},
+	"ivory":                {1.000, 1.000, 0.941},
+	"khaki":                {0.941, 0.902, 0.549},
+	"lavender":             {0.902, 0.902, 0.980},
+	"lavenderblush":        {1.000, 0.941, 0.961},
+	"lawngreen":            {0.486, 0.988, 0.000},
+	"lemonchiffon":         {1.000, 0.980, 0.804},
+	"lightblue":            {0.678, 0.847, 0.902},
+	"lightcoral":           {0.941, 0.502, 0.502},
+	"lightcyan":            {0.878, 1.000, 1.000},
+	"lightgoldenrodyellow": {0.980, 0.980, 0.824},
+	"lightgray":            {0.827, 0.827, 0.827},
+	"lightgreen":           {0.565, 0.933, 0.565},
+	"lightgrey":            {0.827, 0.827, 0.827},
+	"lightpink":            {1.000, 0.714, 0.757},
+	"lightsalmon":          {1.000, 0.627, 0.478},
+	"lightseagreen":        {0.125, 0.698, 0.667},
+	"lightskyblue":         {0.529, 0.808, 0.980},
+	"lightslategray":       {0.467, 0.533, 0.600},
+	"lightslategrey":       {0.467, 0.533, 0.600},
+	"lightsteelblue":       {0.690, 0.769, 0.871},
+	"lightyellow":          {1.000, 1.000, 0.878},
+	"lime":                 {0.000, 1.000, 0.000},
+	"limegreen":            {0.196, 0.804, 0.196},
+	"linen":                {0.980, 0.941, 0.902},
+	"magenta":              {1.000, 0.000, 1.000},
+	"maroon":               {0.502, 0.000, 0.000},
+	"mediumaquamarine":     {0.400, 0.804, 0.667},
+	"mediumblue":           {0.000, 0.000, 0.804},
+	"mediumorchid":         {0.729, 0.333, 0.827},
+	"mediumpurple":         {0.576, 0.439, 0.859},
+	"mediumseagreen":       {0.235, 0.702, 0.443},
+	"mediumslateblue":      {0.482, 0.408, 0.933},
+	"mediumspringgreen":    {0.000, 0.980, 0.604},
+	"mediumturquoise":      {0.282, 0.820, 0.800},
+	"mediumvioletred":      {0.780, 0.082, 0.522},
+	"midnightblue":         {0.098, 0.098, 0.439},
+	"mintcream":            {0.961, 1.000, 0.980},
+	"mistyrose":            {1.000, 0.894, 0.882},
+	"moccasin":             {1.000, 0.894, 0.710},
+	"navajowhite":          {1.000, 0.871, 0.678},
+	"navy":                 {0.000, 0.000, 0.502},
+	"oldlace":              {0.992, 0.961, 0.902},
+	"olive":                {0.502, 0.502, 0.000},
+	"olivedrab":            {0.420, 0.557, 0.137},
+	"orange":               {1.000, 0.647, 0.000},
+	"orangered":            {1.000, 0.271, 0.000},
+	"orchid":               {0.855, 0.439, 0.839},
+	"palegoldenrod":        {0.933, 0.910, 0.667},
+	"palegreen":            {0.596, 0.984, 0.596},
+	"paleturquoise":        {0.686, 0.933, 0.933},
+	"palevioletred":        {0.859, 0.439, 0.576},
+	"papayawhip":           {1.000, 0.937, 0.835},
+	"peachpuff":            {1.000, 0.855, 0.725},
+	"peru":                 {0.804, 0.522, 0.247},
+	"pink":                 {1.000, 0.753, 0.796},
+	"plum":                 {0.867, 0.627, 0.867},
+	"powderblue":           {0.690, 0.878, 0.902},
+	"purple":               {0.502, 0.000, 0.502},
+	"red":                  {1.000, 0.000, 0.000},
+	"rosybrown":            {0.737, 0.561, 0.561},
+	"royalblue":            {0.255, 0.412, 0.882},
+	"saddlebrown":          {0.545, 0.271, 0.075},
+	"salmon":               {0.980, 0.502, 0.447},
+	"sandybrown":           {0.957, 0.643, 0.376},
+	"seagreen":             {0.180, 0.545, 0.341},
+	"seashell":             {1.000, 0.961, 0.933},
+	"sienna":               {0.627, 0.322, 0.176},
+	"silver":               {0.753, 0.753, 0.753},
+	"skyblue":              {0.529, 0.808, 0.922},
+	"slateblue":            {0.416, 0.353, 0.804},
+	"slategray":            {0.439, 0.502, 0.565},
+	"slategrey":            {0.439, 0.502, 0.565},
+	"snow":                 {1.000, 0.980, 0.980},
+	"springgreen":          {0.000, 1.000, 0.498},
+	"steelblue":            {0.275, 0.510, 0.706},
+	"tan":                  {0.824, 0.706, 0.549},
+	"teal":                 {0.000, 0.502, 0.502},
+	"thistle":              {0.847, 0.749, 0.847},
+	"tomato":               {1.000, 0.388, 0.278},
+	"turquoise":            {0.251, 0.878, 0.816},
+	"violet":               {0.933, 0.510, 0.933},
+	"wheat":                {0.961, 0.871, 0.702},
+	"white":                {1.000, 1.000, 1.000},
+	"whitesmoke":           {0.961, 0.961, 0.961},
+	"yellow":               {1.000, 1.000, 0.000},
+	"yellowgreen":          {0.604, 0.804, 0.196},
 }
 }

+ 45 - 46
math32/frustum.go

@@ -4,69 +4,64 @@
 
 
 package math32
 package math32
 
 
+// Frustum represents a frustum
 type Frustum struct {
 type Frustum struct {
 	planes []Plane
 	planes []Plane
 }
 }
 
 
+func NewFrustumFromMatrix(m *Matrix4) *Frustum {
+	f := new(Frustum)
+	f.planes = make([]Plane, 6)
+	f.SetFromMatrix(m)
+	return f
+}
+
+// NewFrustum returns a pointer to a new Frustum object
 func NewFrustum(p0, p1, p2, p3, p4, p5 *Plane) *Frustum {
 func NewFrustum(p0, p1, p2, p3, p4, p5 *Plane) *Frustum {
 
 
-	this := new(Frustum)
-	this.planes = make([]Plane, 6)
-	if p0 != nil {
-		this.planes[0] = *p0
-	}
-	if p1 != nil {
-		this.planes[1] = *p1
-	}
-	if p2 != nil {
-		this.planes[2] = *p2
-	}
-	if p3 != nil {
-		this.planes[3] = *p3
-	}
-	if p4 != nil {
-		this.planes[4] = *p4
-	}
-	if p5 != nil {
-		this.planes[5] = *p5
-	}
-	return this
+	f := new(Frustum)
+	f.planes = make([]Plane, 6)
+	f.Set(p0, p1, p2, p3, p4, p5)
+	return f
 }
 }
 
 
-func (this *Frustum) Set(p0, p1, p2, p3, p4, p5 *Plane) *Frustum {
+// Set sets the frustum's planes
+func (f *Frustum) Set(p0, p1, p2, p3, p4, p5 *Plane) *Frustum {
 
 
 	if p0 != nil {
 	if p0 != nil {
-		this.planes[0] = *p0
+		f.planes[0] = *p0
 	}
 	}
 	if p1 != nil {
 	if p1 != nil {
-		this.planes[1] = *p1
+		f.planes[1] = *p1
 	}
 	}
 	if p2 != nil {
 	if p2 != nil {
-		this.planes[2] = *p2
+		f.planes[2] = *p2
 	}
 	}
 	if p3 != nil {
 	if p3 != nil {
-		this.planes[3] = *p3
+		f.planes[3] = *p3
 	}
 	}
 	if p4 != nil {
 	if p4 != nil {
-		this.planes[4] = *p4
+		f.planes[4] = *p4
 	}
 	}
 	if p5 != nil {
 	if p5 != nil {
-		this.planes[5] = *p5
+		f.planes[5] = *p5
 	}
 	}
-	return this
+	return f
 }
 }
 
 
-func (this *Frustum) Copy(frustum *Frustum) *Frustum {
+// Copy modifies the receiver frustum to match the provided frustum
+func (f *Frustum) Copy(frustum *Frustum) *Frustum {
 
 
 	for i := 0; i < 6; i++ {
 	for i := 0; i < 6; i++ {
-		this.planes[i] = frustum.planes[i]
+		f.planes[i] = frustum.planes[i]
 	}
 	}
-	return this
+	return f
 }
 }
 
 
-func (this *Frustum) SetFromMatrix(m *Matrix4) *Frustum {
+// SetFromMatrix sets the frustum's planes based on the specified Matrix4
+func (f *Frustum) SetFromMatrix(m *Matrix4) *Frustum {
 
 
-	planes := this.planes
+	planes := f.planes
 	me0 := m[0]
 	me0 := m[0]
 	me1 := m[1]
 	me1 := m[1]
 	me2 := m[2]
 	me2 := m[2]
@@ -91,7 +86,7 @@ func (this *Frustum) SetFromMatrix(m *Matrix4) *Frustum {
 	planes[4].SetComponents(me3-me2, me7-me6, me11-me10, me15-me14).Normalize()
 	planes[4].SetComponents(me3-me2, me7-me6, me11-me10, me15-me14).Normalize()
 	planes[5].SetComponents(me3+me2, me7+me6, me11+me10, me15+me14).Normalize()
 	planes[5].SetComponents(me3+me2, me7+me6, me11+me10, me15+me14).Normalize()
 
 
-	return this
+	return f
 }
 }
 
 
 /**
 /**
@@ -103,9 +98,10 @@ func (this *Frustum) IntersectsObject(geometry *core.Geometry) bool {
 }
 }
 */
 */
 
 
-func (this *Frustum) IntersectsSphere(sphere *Sphere) bool {
+// IntersectsSphere determines whether the specified sphere is intersecting the frustum
+func (f *Frustum) IntersectsSphere(sphere *Sphere) bool {
 
 
-	planes := this.planes
+	planes := f.planes
 	negRadius := -sphere.Radius
 	negRadius := -sphere.Radius
 
 
 	for i := 0; i < 6; i++ {
 	for i := 0; i < 6; i++ {
@@ -118,13 +114,14 @@ func (this *Frustum) IntersectsSphere(sphere *Sphere) bool {
 	return true
 	return true
 }
 }
 
 
-func (this *Frustum) IntersectsBox(box *Box3) bool {
+// IntersectsBox determines whether the specified box is intersecting the frustum
+func (f *Frustum) IntersectsBox(box *Box3) bool {
 
 
 	var p1 Vector3
 	var p1 Vector3
 	var p2 Vector3
 	var p2 Vector3
 
 
 	for i := 0; i < 6; i++ {
 	for i := 0; i < 6; i++ {
-		plane := &this.planes[i]
+		plane := &f.planes[i]
 		if plane.normal.X > 0 {
 		if plane.normal.X > 0 {
 			p1.X = box.Min.X
 			p1.X = box.Min.X
 		} else {
 		} else {
@@ -136,9 +133,9 @@ func (this *Frustum) IntersectsBox(box *Box3) bool {
 			p2.X = box.Min.X
 			p2.X = box.Min.X
 		}
 		}
 		if plane.normal.Y > 0 {
 		if plane.normal.Y > 0 {
-			p2.Y = box.Min.Y
+			p1.Y = box.Min.Y
 		} else {
 		} else {
-			p2.Y = box.Max.Y
+			p1.Y = box.Max.Y
 		}
 		}
 		if plane.normal.Y > 0 {
 		if plane.normal.Y > 0 {
 			p2.Y = box.Max.Y
 			p2.Y = box.Max.Y
@@ -151,7 +148,7 @@ func (this *Frustum) IntersectsBox(box *Box3) bool {
 			p1.Z = box.Max.Z
 			p1.Z = box.Max.Z
 		}
 		}
 		if plane.normal.Z > 0 {
 		if plane.normal.Z > 0 {
-			p1.Z = box.Max.Z
+			p2.Z = box.Max.Z
 		} else {
 		} else {
 			p2.Z = box.Min.Z
 			p2.Z = box.Min.Z
 		}
 		}
@@ -169,17 +166,19 @@ func (this *Frustum) IntersectsBox(box *Box3) bool {
 	return true
 	return true
 }
 }
 
 
-func (this *Frustum) ContainsPoint(point *Vector3) bool {
+// ContainsPoint determines whether the frustum contains the specified point
+func (f *Frustum) ContainsPoint(point *Vector3) bool {
 
 
 	for i := 0; i < 6; i++ {
 	for i := 0; i < 6; i++ {
-		if this.planes[i].DistanceToPoint(point) < 0 {
+		if f.planes[i].DistanceToPoint(point) < 0 {
 			return false
 			return false
 		}
 		}
 	}
 	}
 	return true
 	return true
 }
 }
 
 
-func (this *Frustum) Clone() *Frustum {
+// Clone returns a pointer to a new Frustum object with the same planes as the original
+func (f *Frustum) Clone() *Frustum {
 
 
-	return NewFrustum(nil, nil, nil, nil, nil, nil).Copy(this)
+	return NewFrustum(nil, nil, nil, nil, nil, nil).Copy(f)
 }
 }

+ 43 - 71
math32/line3.go

@@ -4,37 +4,45 @@
 
 
 package math32
 package math32
 
 
+// Line3 represents a 3D line segment defined by a start and an end point.
 type Line3 struct {
 type Line3 struct {
 	start Vector3
 	start Vector3
 	end   Vector3
 	end   Vector3
 }
 }
 
 
+// NewLine3 creates and returns a pointer to a new Line3 with the
+// specified start and end points.
 func NewLine3(start, end *Vector3) *Line3 {
 func NewLine3(start, end *Vector3) *Line3 {
 
 
-	this := new(Line3)
-	this.Set(start, end)
-	return this
+	l := new(Line3)
+	l.Set(start, end)
+	return l
 }
 }
 
 
-func (this *Line3) Set(start, end *Vector3) *Line3 {
+// Set sets this line segment start and end points.
+// Returns pointer to this updated line segment.
+func (l *Line3) Set(start, end *Vector3) *Line3 {
 
 
 	if start != nil {
 	if start != nil {
-		this.start = *start
+		l.start = *start
 	}
 	}
 	if end != nil {
 	if end != nil {
-		this.end = *end
+		l.end = *end
 	}
 	}
-	return this
+	return l
 }
 }
 
 
-func (this *Line3) Copy(line *Line3) *Line3 {
+// Copy copy other line segment to this one.
+// Returns pointer to this updated line segment.
+func (l *Line3) Copy(other *Line3) *Line3 {
 
 
-	this.start = line.start
-	this.end = line.end
-	return this
+	*l = *other
+	return l
 }
 }
 
 
-func (this *Line3) Center(optionalTarget *Vector3) *Vector3 {
+// Center calculates this line segment center point.
+// Store its pointer into optionalTarget, if not nil, and also returns it.
+func (l *Line3) Center(optionalTarget *Vector3) *Vector3 {
 
 
 	var result *Vector3
 	var result *Vector3
 	if optionalTarget == nil {
 	if optionalTarget == nil {
@@ -42,10 +50,12 @@ func (this *Line3) Center(optionalTarget *Vector3) *Vector3 {
 	} else {
 	} else {
 		result = optionalTarget
 		result = optionalTarget
 	}
 	}
-	return result.AddVectors(&this.start, &this.end).MultiplyScalar(0.5)
+	return result.AddVectors(&l.start, &l.end).MultiplyScalar(0.5)
 }
 }
 
 
-func (this *Line3) Delta(optionalTarget *Vector3) *Vector3 {
+// Delta calculates the vector from the start to end point of this line segment.
+// Store its pointer in optionalTarget, if not nil, and also returns it.
+func (l *Line3) Delta(optionalTarget *Vector3) *Vector3 {
 
 
 	var result *Vector3
 	var result *Vector3
 	if optionalTarget == nil {
 	if optionalTarget == nil {
@@ -53,76 +63,38 @@ func (this *Line3) Delta(optionalTarget *Vector3) *Vector3 {
 	} else {
 	} else {
 		result = optionalTarget
 		result = optionalTarget
 	}
 	}
-	return result.SubVectors(&this.end, &this.start).MultiplyScalar(0.5)
+	return result.SubVectors(&l.end, &l.start)
 }
 }
 
 
-func (this *Line3) DistanceSq() float32 {
+// DistanceSq returns the square of the distance from the start point to the end point.
+func (l *Line3) DistanceSq() float32 {
 
 
-	return this.start.DistanceToSquared(&this.end)
+	return l.start.DistanceToSquared(&l.end)
 }
 }
 
 
-func (this *Line3) Distance() float32 {
+// Distance returns the distance from the start point to the end point.
+func (l *Line3) Distance() float32 {
 
 
-	return this.start.DistanceTo(&this.end)
+	return l.start.DistanceTo(&l.end)
 }
 }
 
 
-func (this *Line3) At(t float32, optionalTarget *Vector3) *Vector3 {
+// ApplyMatrix4 applies the specified matrix to this line segment start and end points.
+// Returns pointer to this updated line segment.
+func (l *Line3) ApplyMatrix4(matrix *Matrix4) *Line3 {
 
 
-	var result *Vector3
-	if optionalTarget == nil {
-		result = NewVector3(0, 0, 0)
-	} else {
-		result = optionalTarget
-	}
-	return this.Delta(result).MultiplyScalar(t).Add(&this.start)
-}
-
-func (this *Line3) ClosestPointToPointParameter() func(*Vector3, bool) float32 {
-
-	startP := NewVector3(0, 0, 0)
-	startEnd := NewVector3(0, 0, 0)
-
-	return func(point *Vector3, clampToLine bool) float32 {
-		startP.SubVectors(point, &this.start)
-		startEnd.SubVectors(&this.end, &this.start)
-
-		startEnd2 := startEnd.Dot(startEnd)
-		startEnd_startP := startEnd.Dot(startP)
-
-		t := startEnd_startP / startEnd2
-		if clampToLine {
-			t = Clamp(t, 0, 1)
-		}
-		return t
-	}
-}
-
-func (this *Line3) ClosestPointToPoint(point *Vector3, clampToLine bool, optionalTarget *Vector3) *Vector3 {
-
-	t := this.ClosestPointToPointParameter()(point, clampToLine)
-	var result *Vector3
-	if optionalTarget == nil {
-		result = NewVector3(0, 0, 0)
-	} else {
-		result = optionalTarget
-	}
-	return this.Delta(result).MultiplyScalar(t).Add(&this.start)
-}
-
-func (this *Line3) ApplyMatrix4(matrix *Matrix4) *Line3 {
-
-	this.start.ApplyMatrix4(matrix)
-	this.end.ApplyMatrix4(matrix)
-
-	return this
+	l.start.ApplyMatrix4(matrix)
+	l.end.ApplyMatrix4(matrix)
+	return l
 }
 }
 
 
-func (this *Line3) Equals(line *Line3) bool {
+// Equals returns if this line segement is equal to other.
+func (l *Line3) Equals(other *Line3) bool {
 
 
-	return line.start.Equals(&this.start) && line.end.Equals(&this.end)
+	return other.start.Equals(&l.start) && other.end.Equals(&l.end)
 }
 }
 
 
-func (this *Line3) Clone() *Line3 {
+// Clone creates and returns a pointer to a copy of this line segment.
+func (l *Line3) Clone() *Line3 {
 
 
-	return NewLine3(&this.start, &this.end)
+	return NewLine3(&l.start, &l.end)
 }
 }

+ 4 - 10
math32/math.go

@@ -18,19 +18,21 @@ const radianToDegreesFactor = 180.0 / math.Pi
 
 
 var Infinity = float32(math.Inf(1))
 var Infinity = float32(math.Inf(1))
 
 
+// DegToRad converts a number from degrees to radians
 func DegToRad(degrees float32) float32 {
 func DegToRad(degrees float32) float32 {
 
 
 	return degrees * degreeToRadiansFactor
 	return degrees * degreeToRadiansFactor
 }
 }
 
 
+// RadToDeg converts a number from radians to degrees
 func RadToDeg(radians float32) float32 {
 func RadToDeg(radians float32) float32 {
 
 
 	return radians * radianToDegreesFactor
 	return radians * radianToDegreesFactor
 }
 }
 
 
+// Clamp clamps x to the provided closed interval [a, b]
 func Clamp(x, a, b float32) float32 {
 func Clamp(x, a, b float32) float32 {
 
 
-	// Clamp value to range <a, b>
 	if x < a {
 	if x < a {
 		return a
 		return a
 	}
 	}
@@ -40,6 +42,7 @@ func Clamp(x, a, b float32) float32 {
 	return x
 	return x
 }
 }
 
 
+// ClampInt clamps x to the provided closed interval [a, b]
 func ClampInt(x, a, b int) int {
 func ClampInt(x, a, b int) int {
 
 
 	if x < a {
 	if x < a {
@@ -51,15 +54,6 @@ func ClampInt(x, a, b int) int {
 	return x
 	return x
 }
 }
 
 
-func ClampBotton(x, a float32) float32 {
-
-	// Clamp value to range <a, inf)
-	if x < a {
-		return a
-	}
-	return x
-}
-
 func Abs(v float32) float32 {
 func Abs(v float32) float32 {
 	return float32(math.Abs(float64(v)))
 	return float32(math.Abs(float64(v)))
 }
 }

+ 81 - 100
math32/plane.go

@@ -6,114 +6,117 @@ package math32
 
 
 import ()
 import ()
 
 
+// Plane represents a plane in 3D space by its normal vector and a constant.
+// When the the normal vector is the unit vector the constant is the distance from the origin.
 type Plane struct {
 type Plane struct {
 	normal   Vector3
 	normal   Vector3
 	constant float32
 	constant float32
 }
 }
 
 
+// NewPlane creates and returns a new plane from a normal vector and a constant.
 func NewPlane(normal *Vector3, constant float32) *Plane {
 func NewPlane(normal *Vector3, constant float32) *Plane {
 
 
-	this := new(Plane)
+	p := new(Plane)
 	if normal != nil {
 	if normal != nil {
-		this.normal = *normal
+		p.normal = *normal
 	}
 	}
-	this.constant = constant
-	return this
+	p.constant = constant
+	return p
 }
 }
 
 
-func (this *Plane) Set(normal *Vector3, constant float32) *Plane {
+// Set sets this plane normal vector and constant.
+// Returns pointer to this updated plane.
+func (p *Plane) Set(normal *Vector3, constant float32) *Plane {
 
 
-	this.normal = *normal
-	this.constant = constant
-	return this
+	p.normal = *normal
+	p.constant = constant
+	return p
 }
 }
 
 
-func (this *Plane) SetComponents(x, y, z, w float32) *Plane {
+// SetComponents sets this plane normal vector components and constant.
+// Returns pointer to this updated plane.
+func (p *Plane) SetComponents(x, y, z, w float32) *Plane {
 
 
-	this.normal.Set(x, y, z)
-	this.constant = w
-	return this
+	p.normal.Set(x, y, z)
+	p.constant = w
+	return p
 }
 }
 
 
-func (this *Plane) SetFromNormalAndCoplanarPoint(normal *Vector3, point *Vector3) *Plane {
+// SetFromNormalAndCoplanarPoint sets this plane from a normal vector and a point on the plane.
+// Returns pointer to this updated plane.
+func (p *Plane) SetFromNormalAndCoplanarPoint(normal *Vector3, point *Vector3) *Plane {
 
 
-	this.normal.Copy(normal)
-	this.constant = -point.Dot(&this.normal)
-	return this
+	p.normal = *normal
+	p.constant = -point.Dot(&p.normal)
+	return p
 }
 }
 
 
-func (this *Plane) SetFromCoplanarPoints(a, b, c *Vector3) *Plane {
+// SetFromCoplanarPoints sets this plane from three coplanar points.
+// Returns pointer to this updated plane.
+func (p *Plane) SetFromCoplanarPoints(a, b, c *Vector3) *Plane {
 
 
 	var v1 Vector3
 	var v1 Vector3
 	var v2 Vector3
 	var v2 Vector3
 
 
 	normal := v1.SubVectors(c, b).Cross(v2.SubVectors(a, b)).Normalize()
 	normal := v1.SubVectors(c, b).Cross(v2.SubVectors(a, b)).Normalize()
 	// Q: should an error be thrown if normal is zero (e.g. degenerate plane)?
 	// Q: should an error be thrown if normal is zero (e.g. degenerate plane)?
-	this.SetFromNormalAndCoplanarPoint(normal, a)
-	return this
+	p.SetFromNormalAndCoplanarPoint(normal, a)
+	return p
 }
 }
 
 
-func (this *Plane) Copy(plane *Plane) *Plane {
+// Copy sets this plane to a copy of other.
+// Returns pointer to this updated plane.
+func (p *Plane) Copy(other *Plane) *Plane {
 
 
-	this.normal.Copy(&plane.normal)
-	this.constant = plane.constant
-	return this
+	p.normal.Copy(&other.normal)
+	p.constant = other.constant
+	return p
 }
 }
 
 
-func (this *Plane) Normalize() *Plane {
+// Normalize normalizes this plane normal vector and adjusts the constant.
+// Note: will lead to a divide by zero if the plane is invalid.
+// Returns pointer to this updated plane.
+func (p *Plane) Normalize() *Plane {
 
 
-	// Note: will lead to a divide by zero if the plane is invalid.
-	inverseNormalLength := 1.0 / this.normal.Length()
-	this.normal.MultiplyScalar(inverseNormalLength)
-	this.constant *= inverseNormalLength
-	return this
+	inverseNormalLength := 1.0 / p.normal.Length()
+	p.normal.MultiplyScalar(inverseNormalLength)
+	p.constant *= inverseNormalLength
+	return p
 }
 }
 
 
-func (this *Plane) Negate() *Plane {
+// Negate negates this plane normal.
+// Returns pointer to this updated plane.
+func (p *Plane) Negate() *Plane {
 
 
-	this.constant *= -1
-	this.normal.Negate()
-	return this
+	p.constant *= -1
+	p.normal.Negate()
+	return p
 }
 }
 
 
-func (this *Plane) DistanceToPoint(point *Vector3) float32 {
+// DistanceToPoint returns the distance of this plane from point.
+func (p *Plane) DistanceToPoint(point *Vector3) float32 {
 
 
-	return this.normal.Dot(point) + this.constant
+	return p.normal.Dot(point) + p.constant
 }
 }
 
 
-func (this *Plane) DistanceToSphere(sphere *Sphere) float32 {
+// DistanceToSphere returns the distance of this place from the sphere.
+func (p *Plane) DistanceToSphere(sphere *Sphere) float32 {
 
 
-	return this.DistanceToPoint(&sphere.Center) - sphere.Radius
+	return p.DistanceToPoint(&sphere.Center) - sphere.Radius
 }
 }
 
 
-func (this *Plane) ProjectPoint(point *Vector3, optionalTarget *Vector3) *Vector3 {
-
-	return this.OrthoPoint(point, optionalTarget).Sub(point).Negate()
-}
-
-func (this *Plane) OrthoPoint(point *Vector3, optionalTarget *Vector3) *Vector3 {
-
-	var result *Vector3
-	if optionalTarget == nil {
-		result = NewVector3(0, 0, 0)
-	} else {
-		result = optionalTarget
-	}
-	perpendicularMagnitude := this.DistanceToPoint(point)
-	return result.Copy(&this.normal).MultiplyScalar(perpendicularMagnitude)
-}
-
-func (this *Plane) IsIntersectionLine(line *Line3) bool {
-
-	// Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it.
-	startSign := this.DistanceToPoint(&line.start)
-	endSign := this.DistanceToPoint(&line.end)
+// IsIntersectionLine returns the line intersects this plane.
+func (p *Plane) IsIntersectionLine(line *Line3) bool {
 
 
+	startSign := p.DistanceToPoint(&line.start)
+	endSign := p.DistanceToPoint(&line.end)
 	return (startSign < 0 && endSign > 0) || (endSign < 0 && startSign > 0)
 	return (startSign < 0 && endSign > 0) || (endSign < 0 && startSign > 0)
-
 }
 }
 
 
-func (this *Plane) IntersectLine(line *Line3, optionalTarget *Vector3) *Vector3 {
+// IntersectLine calculates the point in the plane which intersets the specified line.
+// Sets the optionalTarget, if not nil to this point, and also returns it.
+// Returns nil if the line does not intersects the plane.
+func (p *Plane) IntersectLine(line *Line3, optionalTarget *Vector3) *Vector3 {
 
 
 	var v1 Vector3
 	var v1 Vector3
 	var result *Vector3
 	var result *Vector3
@@ -124,24 +127,26 @@ func (this *Plane) IntersectLine(line *Line3, optionalTarget *Vector3) *Vector3
 	}
 	}
 
 
 	direction := line.Delta(&v1)
 	direction := line.Delta(&v1)
-	denominator := this.normal.Dot(direction)
+	denominator := p.normal.Dot(direction)
 	if denominator == 0 {
 	if denominator == 0 {
 		// line is coplanar, return origin
 		// line is coplanar, return origin
-		if this.DistanceToPoint(&line.start) == 0 {
+		if p.DistanceToPoint(&line.start) == 0 {
 			return result.Copy(&line.start)
 			return result.Copy(&line.start)
 		}
 		}
 		// Unsure if this is the correct method to handle this case.
 		// Unsure if this is the correct method to handle this case.
 		return nil
 		return nil
 	}
 	}
 
 
-	var t = -(line.start.Dot(&this.normal) + this.constant) / denominator
+	var t = -(line.start.Dot(&p.normal) + p.constant) / denominator
 	if t < 0 || t > 1 {
 	if t < 0 || t > 1 {
 		return nil
 		return nil
 	}
 	}
 	return result.Copy(direction).MultiplyScalar(t).Add(&line.start)
 	return result.Copy(direction).MultiplyScalar(t).Add(&line.start)
 }
 }
 
 
-func (this *Plane) CoplanarPoint(optionalTarget *Vector3) *Vector3 {
+// CoplanarPoint sets the optionalTarget to a point in the plane and also returns it.
+// The point set and returned is the closest point from the origin.
+func (p *Plane) CoplanarPoint(optionalTarget *Vector3) *Vector3 {
 
 
 	var result *Vector3
 	var result *Vector3
 	if optionalTarget == nil {
 	if optionalTarget == nil {
@@ -149,49 +154,25 @@ func (this *Plane) CoplanarPoint(optionalTarget *Vector3) *Vector3 {
 	} else {
 	} else {
 		result = optionalTarget
 		result = optionalTarget
 	}
 	}
-	return result.Copy(&this.normal).MultiplyScalar(-this.constant)
-}
-
-func (this *Plane) ApplyMatrix4(matrix *Matrix4, optionalNormalMatrix *Matrix3) *Plane {
-	// compute new normal based on theory here:
-	// http://www.songho.ca/opengl/gl_normaltransform.html
-
-	var v1 Vector3
-	var v2 Vector3
-	m1 := NewMatrix3()
-
-	var normalMatrix *Matrix3
-	if optionalNormalMatrix != nil {
-		normalMatrix = optionalNormalMatrix
-	} else {
-		err := m1.GetNormalMatrix(matrix)
-		if err != nil {
-			return nil
-		}
-		normalMatrix = m1
-	}
-
-	newNormal := v1.Copy(&this.normal).ApplyMatrix3(normalMatrix)
-
-	newCoplanarPoint := this.CoplanarPoint(&v2)
-	newCoplanarPoint.ApplyMatrix4(matrix)
-
-	this.SetFromNormalAndCoplanarPoint(newNormal, newCoplanarPoint)
-	return this
+	return result.Copy(&p.normal).MultiplyScalar(-p.constant)
 }
 }
 
 
-func (this *Plane) Translate(offset *Vector3) *Plane {
+// Translate translates this plane in the direction of its normal by offset.
+// Returns pointer to this updated plane.
+func (p *Plane) Translate(offset *Vector3) *Plane {
 
 
-	this.constant = this.constant - offset.Dot(&this.normal)
-	return this
+	p.constant = p.constant - offset.Dot(&p.normal)
+	return p
 }
 }
 
 
-func (this *Plane) Equals(plane *Plane) bool {
+// Equals returns if this plane is equal to other
+func (p *Plane) Equals(other *Plane) bool {
 
 
-	return plane.normal.Equals(&this.normal) && (plane.constant == this.constant)
+	return other.normal.Equals(&p.normal) && (other.constant == p.constant)
 }
 }
 
 
-func (this *Plane) Clone(plane *Plane) *Plane {
+// Clone creates and returns a pointer to a copy of this plane.
+func (p *Plane) Clone(plane *Plane) *Plane {
 
 
 	return NewPlane(&plane.normal, plane.constant)
 	return NewPlane(&plane.normal, plane.constant)
 }
 }

+ 0 - 0
math32/quaternion.go


Некоторые файлы не были показаны из-за большого количества измененных файлов