danaugrs пре 7 година
родитељ
комит
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">
-  <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>
 
 ## 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
   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.
-* 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
 
@@ -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.
 * 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
 
 The following code shows a basic G3N application 
@@ -201,4 +211,3 @@ send pull requests.
 ## Community
 
 Join our [channel](https://gophers.slack.com/messages/g3n) on Gophers Slack.
-

+ 2 - 2
audio/al/al.go

@@ -7,10 +7,10 @@
 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 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 windows  LDFLAGS: -L${SRCDIR}/../windows/bin -lOpenAL32
 

+ 9 - 9
audio/audio_file.go

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

+ 8 - 1
audio/listener.go

@@ -11,11 +11,12 @@ import (
 	"github.com/g3n/engine/math32"
 )
 
-// Listener embeds a core.Node and
+// Listener is an audio listener positioned in space
 type Listener struct {
 	core.Node
 }
 
+// NewListener returns a pointer to a new Listener object.
 func NewListener() *Listener {
 
 	l := new(Listener)
@@ -23,32 +24,38 @@ func NewListener() *Listener {
 	return l
 }
 
+// SetVelocity sets the velocity of the listener with x, y, z components
 func (l *Listener) SetVelocity(vx, vy, vz float32) {
 
 	al.Listener3f(al.Velocity, vx, vy, vz)
 }
 
+// SetVelocityVec sets the velocity of the listener with a vector
 func (l *Listener) SetVelocityVec(v *math32.Vector3) {
 
 	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) {
 
 	return al.GetListener3f(al.Velocity)
 }
 
+// VelocityVec returns the velocity of the listener as a vector
 func (l *Listener) VelocityVec() math32.Vector3 {
 
 	vx, vy, vz := al.GetListener3f(al.Velocity)
 	return math32.Vector3{vx, vy, vz}
 }
 
+// SetGain sets the gain of the listener
 func (l *Listener) SetGain(gain float32) {
 
 	al.Listenerf(al.Gain, gain)
 }
 
+// Gain returns the gain of the listener
 func (l *Listener) Gain() float32 {
 
 	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
 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 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 windows  LDFLAGS: -L${SRCDIR}/../windows/bin -llibvorbisfile
 // #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)
 }
 
-// MinGain returns the current maximum gain of this player
+// MaxGain returns the current maximum gain of this player
 func (p *Player) MaxGain() float32 {
 
 	return al.GetSourcef(p.source, al.MaxGain)
@@ -282,7 +282,7 @@ func (p *Player) VelocityVec() math32.Vector3 {
 	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
 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
 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 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 windows  LDFLAGS: -L${SRCDIR}/../windows/bin -llibvorbis
 // #include "codec.h"

+ 1 - 1
audio/wave.go

@@ -10,7 +10,7 @@ import (
 	"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 {
 	Format     int     // OpenAl Format
 	Type       int     // Type field from wave header

+ 1 - 0
camera/perspective.go

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

+ 1 - 0
core/RenderInfo.go

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

+ 321 - 203
core/node.go

@@ -5,13 +5,12 @@
 package core
 
 import (
-	"strings"
-
 	"github.com/g3n/engine/gls"
 	"github.com/g3n/engine/math32"
+	"strings"
 )
 
-// Interface for all node types
+// INode is the interface for all node types.
 type INode interface {
 	GetNode() *Node
 	UpdateMatrixWorld()
@@ -20,25 +19,28 @@ type INode interface {
 	Dispose()
 }
 
+// Node represents an object in 3D space existing within a hierarchy.
 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 {
 
 	n := new(Node)
@@ -46,44 +48,72 @@ func NewNode() *Node {
 	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() {
 
 	n.Dispatcher.Initialize()
+
+	n.children = make([]INode, 0)
+	n.visible = true
+	n.changed = true
+
+	// Initialize spatial properties
 	n.position.Set(0, 0, 0)
-	n.rotation.Set(0, 0, 0)
-	n.quaternion.Set(0, 0, 0, 1)
 	n.scale.Set(1, 1, 1)
 	n.direction.Set(0, 0, 1)
+	n.rotation.Set(0, 0, 0)
+	n.quaternion.Set(0, 0, 0, 1)
 	n.matrix.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 {
 
 	return n
 }
 
-// Raycast satisfies the INode interface
+// Raycast satisfies the INode interface.
 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) {
 }
 
-// Dispose satisfies the INode interface
+// Dispose satisfies the INode interface.
 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,
-// 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.
 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
-// created by an external loader such as Collada
+// created by an external loader such as Collada.
 func (n *Node) LoaderID() string {
 
 	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
 // searching in all its children recursively.
 // 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)
 }
 
-// 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 {
 
 	var finder func(parent INode, id string) INode
@@ -156,74 +223,181 @@ func (n *Node) FindLoaderID(id string) INode {
 	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) {
 
 	n.position.Set(x, y, z)
 	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) {
 
 	n.position = *vpos
 	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) {
 
 	n.position.X = x
 	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) {
 
 	n.position.Y = y
 	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) {
 
 	n.position.Z = z
 	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 {
 
 	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) {
 
 	n.rotation.Set(x, y, z)
@@ -231,8 +405,17 @@ func (n *Node) SetRotation(x, y, z float32) {
 	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) {
 
 	n.rotation.X = x
@@ -240,8 +423,8 @@ func (n *Node) SetRotationX(x float32) {
 	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) {
 
 	n.rotation.Y = y
@@ -249,8 +432,8 @@ func (n *Node) SetRotationY(y float32) {
 	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) {
 
 	n.rotation.Z = z
@@ -258,8 +441,8 @@ func (n *Node) SetRotationZ(z float32) {
 	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) {
 
 	n.rotation.X += x
@@ -267,8 +450,8 @@ func (n *Node) AddRotationX(x float32) {
 	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) {
 
 	n.rotation.Y += y
@@ -276,8 +459,8 @@ func (n *Node) AddRotationY(y float32) {
 	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) {
 
 	n.rotation.Z += z
@@ -285,136 +468,123 @@ func (n *Node) AddRotationZ(z float32) {
 	n.changed = true
 }
 
-// Rotation returns the current rotation
+// Rotation returns the current rotation.
 func (n *Node) Rotation() math32.Vector3 {
 
 	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) {
 
 	n.quaternion.Set(x, y, z, w)
 	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) {
 
 	n.quaternion = *q
 	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) {
 
 	n.quaternion.Multiply(q)
 	n.changed = true
 }
 
-// Quaternion returns the current quaternion
+// Quaternion returns the current quaternion.
 func (n *Node) Quaternion() math32.Quaternion {
 
 	return n.quaternion
 }
 
-// SetScale sets this node scale fields
+// SetScale sets the scale.
 func (n *Node) SetScale(x, y, z float32) {
 
 	n.scale.Set(x, y, z)
 	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) {
 
 	n.scale = *scale
 	n.changed = true
 }
 
-// SetScaleX sets the X scale of this node
+// SetScaleX sets the X scale.
 func (n *Node) SetScaleX(sx float32) {
 
 	n.scale.X = sx
 	n.changed = true
 }
 
-// SetScaleY sets the Y scale of this node
+// SetScaleY sets the Y scale.
 func (n *Node) SetScaleY(sy float32) {
 
 	n.scale.Y = sy
 	n.changed = true
 }
 
-// SetScaleZ sets the Z scale of this node
+// SetScaleZ sets the Z scale.
 func (n *Node) SetScaleZ(sz float32) {
 
 	n.scale.Z = sz
 	n.changed = true
 }
 
-// Scale returns the current scale
+// Scale returns the current scale.
 func (n *Node) Scale() math32.Vector3 {
 
 	return n.scale
 }
 
-// SetDirection sets this node initial direction vector
+// SetDirection sets the direction.
 func (n *Node) SetDirection(x, y, z float32) {
 
 	n.direction.Set(x, y, z)
 	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) {
 
 	n.direction = *vdir
 	n.changed = true
 }
 
-// Direction returns this node initial direction
+// Direction returns the direction.
 func (n *Node) Direction() math32.Vector3 {
 
 	return n.direction
 }
 
-// SetMatrix sets this node local transformation matrix
+// SetMatrix sets the local transformation matrix.
 func (n *Node) SetMatrix(m *math32.Matrix4) {
 
 	n.matrix = *m
 	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 {
 
 	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) {
 
 	n.UpdateMatrixWorld()
 	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) {
 
 	var position math32.Vector3
@@ -423,8 +593,8 @@ func (n *Node) WorldQuaternion(result *math32.Quaternion) {
 	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) {
 
 	var quaternion math32.Quaternion
@@ -432,8 +602,8 @@ func (n *Node) WorldRotation(result *math32.Vector3) {
 	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) {
 
 	var position math32.Vector3
@@ -442,8 +612,8 @@ func (n *Node) WorldScale(result *math32.Vector3) {
 	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) {
 
 	var quaternion math32.Quaternion
@@ -452,123 +622,71 @@ func (n *Node) WorldDirection(result *math32.Vector3) {
 	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 {
 
 	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.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() {
 
-	n.UpdateMatrix()
 	if n.parent == nil {
-		n.matrixWorld = n.matrix
+		n.updateMatrixWorld(&n.matrix)
 	} else {
 		parent := n.parent.GetNode()
-		n.matrixWorld.MultiplyMatrices(&parent.matrixWorld, &n.matrix)
-	}
-	// Update this Node children matrices
-	for _, ichild := range n.children {
-		ichild.UpdateMatrixWorld()
-	}
-}
-
-// SetParent sets this node parent
-func (n *Node) SetParent(iparent INode) {
-
-	n.parent = iparent
-}
-
-// Parent returns this node parent
-func (n *Node) Parent() INode {
-
-	return n.parent
-}
-
-// Children returns the list of this node children
-func (n *Node) Children() []INode {
-
-	return n.children
-}
-
-// Add adds the specified INode to this node list of children
-func (n *Node) Add(ichild INode) *Node {
-
-	child := ichild.GetNode()
-	if n == child {
-		panic("Node.Add: object can't be added as a child of itself")
-		return nil
+		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"
 )
 
+// Raycaster represents an empty object that can cast rays and check for ray intersections.
 type Raycaster struct {
 	// The distance from the ray origin to the intersected points
 	// must be greater than the value of this field to be considered.
@@ -51,7 +52,7 @@ type Intersect struct {
 	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.
 func NewRaycaster(origin, direction *math32.Vector3) *Raycaster {
 
@@ -72,7 +73,9 @@ func (rc *Raycaster) IntersectObject(inode INode, recursive bool) []Intersect {
 
 	intersects := []Intersect{}
 	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
 }
 
@@ -86,7 +89,9 @@ func (rc *Raycaster) IntersectObjects(inodes []INode, recursive bool) []Intersec
 	for _, inode := range inodes {
 		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
 }
 
@@ -104,13 +109,3 @@ func (rc *Raycaster) intersectObject(inode INode, intersects *[]Intersect, recur
 	}
 	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"
 )
 
+// 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 {
 	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.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.Height = height
-	box.Depth = depth
+	box.Length = length
 	box.WidthSegments = widthSegments
 	box.HeightSegments = heightSegments
-	box.DepthSegments = depthSegments
+	box.LengthSegments = lengthSegments
 
 	// Create buffers
 	positions := math32.NewArrayF32(0, 16)
@@ -40,57 +63,53 @@ func NewBox(width, height, depth float64, widthSegments, heightSegments, depthSe
 	uvs := math32.NewArrayF32(0, 16)
 	indices := math32.NewArrayU32(0, 16)
 
-	width_half := width / 2
-	height_half := height / 2
-	depth_half := depth / 2
+	// Internal function to build each 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
+		gridX := box.WidthSegments
+		gridY := box.HeightSegments
 		var w string
 
 		if (u == "x" && v == "y") || (u == "y" && v == "x") {
 			w = "z"
 		} else if (u == "x" && v == "z") || (u == "z" && v == "x") {
 			w = "y"
-			gridY = depthSegments
+			gridY = box.LengthSegments
 		} else if (u == "z" && v == "y") || (u == "y" && v == "z") {
 			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
-		if depth > 0 {
+		if length > 0 {
 			normal.SetByName(w, 1)
 		} else {
 			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 ix := 0; ix < gridX1; ix++ {
 				var vector math32.Vector3
-				vector.SetByName(u, float32((float64(ix)*segment_width-width_half)*float64(udir)))
-				vector.SetByName(v, float32((float64(iy)*segment_height-height_half)*float64(vdir)))
-				vector.SetByName(w, float32(depth))
+				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)
 				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()
-		matIndex := materialIndex
-		// Generates the indices for the vertices, normals and uvs
 		for iy := 0; iy < gridY; iy++ {
 			for ix := 0; ix < gridX; ix++ {
 				a := ix + gridX1*iy
@@ -101,20 +120,27 @@ func NewBox(width, height, depth float64, widthSegments, heightSegments, depthSe
 			}
 		}
 		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.AddVBO(gls.NewVBO().AddAttrib("VertexPosition", 3).SetBuffer(positions))
 	box.AddVBO(gls.NewVBO().AddAttrib("VertexNormal", 3).SetBuffer(normals))
 	box.AddVBO(gls.NewVBO().AddAttrib("VertexTexcoord", 2).SetBuffer(uvs))
 
+	box.boundingBox = math32.Box3{math32.Vector3{-wHalf, -hHalf, -lHalf}, math32.Vector3{wHalf, hHalf, lHalf}}
+	box.boundingBoxValid = true
+
 	return box
 }

+ 21 - 11
geometry/circle.go

@@ -10,32 +10,41 @@ import (
 	"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 {
 	Geometry
 	Radius      float64
-	Segments    int
+	Segments    int // >= 3
 	ThetaStart  float64
 	ThetaLength float64
 }
 
-// NewCircle creates and returns a pointer to a new Circle geometry object.
-// The geometry is defined by its radius, the number of segments (triangles), minimum = 3,
-// the start angle in radians for the first segment (thetaStart) and
-// the central angle in radians (thetaLength) of the circular sector.
-func NewCircle(radius float64, segments int, thetaStart, thetaLength float64) *Circle {
+// 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.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.Segments = segments
 	circ.ThetaStart = thetaStart
 	circ.ThetaLength = thetaLength
 
-	if segments < 3 {
-		segments = 3
-	}
-
 	// Create buffers
 	positions := math32.NewArrayF32(0, 16)
 	normals := math32.NewArrayF32(0, 16)
@@ -51,10 +60,11 @@ func NewCircle(radius float64, segments int, thetaStart, thetaLength float64) *C
 	normal.Z = 1
 	normals.AppendVector3(&normal)
 
-	// Append circle center uv coord
+	// Append circle center uv coordinate
 	centerUV := math32.NewVector2(0.5, 0.5)
 	uvs.AppendVector2(centerUV)
 
+	// Generate the segments
 	for i := 0; i <= segments; i++ {
 		segment := thetaStart + float64(i)/float64(segments)*thetaLength
 

+ 1 - 0
geometry/cylinder.go

@@ -10,6 +10,7 @@ import (
 	"math"
 )
 
+// Cylinder represents a cylinder geometry
 type Cylinder struct {
 	Geometry
 	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) {
 
 	g.indices = indices
+	g.updateIndices = true
 	g.boundingBoxValid = false
 	g.boundingSphereValid = false
 }
@@ -139,10 +140,19 @@ func (g *Geometry) Indices() math32.ArrayU32 {
 // AddVBO adds a Vertex Buffer Object for this geometry
 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)
 }
 
-// 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.
 func (g *Geometry) VBO(attrib string) *gls.VBO {
 
@@ -166,7 +176,7 @@ func (g *Geometry) Items() int {
 	if vbo.AttribCount() == 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
@@ -179,17 +189,19 @@ func (g *Geometry) BoundingBox() math32.Box3 {
 	}
 
 	// Get buffer with position vertices
-	vbPos := g.VBO("VertexPosition")
-	if vbPos == nil {
+	vboPos := g.VBO("VertexPosition")
+	if vboPos == nil {
 		return g.boundingBox
 	}
-	positions := vbPos.Buffer()
+	stride := vboPos.Stride()
+	offset := vboPos.AttribOffset("VertexPosition")
+	positions := vboPos.Buffer()
 
 	// Calculates bounding box
 	var vertex math32.Vector3
 	g.boundingBox.Min.Set(0, 0, 0)
 	g.boundingBox.Max.Set(0, 0, 0)
-	for i := 0; i < positions.Size(); i += 3 {
+	for i := offset; i < positions.Size(); i += stride {
 		positions.GetVector3(i, &vertex)
 		g.boundingBox.ExpandByPoint(&vertex)
 	}
@@ -207,11 +219,13 @@ func (g *Geometry) BoundingSphere() math32.Sphere {
 	}
 
 	// Get buffer with position vertices
-	vbPos := g.VBO("VertexPosition")
-	if vbPos == nil {
+	vboPos := g.VBO("VertexPosition")
+	if vboPos == nil {
 		return g.boundingSphere
 	}
-	positions := vbPos.Buffer()
+	stride := vboPos.Stride()
+	offset := vboPos.AttribOffset("VertexPosition")
+	positions := vboPos.Buffer()
 
 	// Get/calculates the bounding box
 	box := g.BoundingBox()
@@ -222,7 +236,7 @@ func (g *Geometry) BoundingSphere() math32.Sphere {
 
 	// Find the radius of the bounding sphere
 	maxRadiusSq := float32(0.0)
-	for i := 0; i < positions.Size(); i += 3 {
+	for i := offset; i < positions.Size(); i += stride {
 		var vertex math32.Vector3
 		positions.GetVector3(i, &vertex)
 		maxRadiusSq = math32.Max(maxRadiusSq, center.DistanceToSquared(&vertex))
@@ -247,9 +261,11 @@ func (g *Geometry) ApplyMatrix(m *math32.Matrix4) {
 	if vboPos == nil {
 		return
 	}
+	stride := vboPos.Stride()
+	offset := vboPos.AttribOffset("VertexPosition")
 	positions := vboPos.Buffer()
 	// 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
 		positions.GetVector3(i, &vertex)
 		vertex.ApplyMatrix4(m)
@@ -262,11 +278,13 @@ func (g *Geometry) ApplyMatrix(m *math32.Matrix4) {
 	if vboNormals == nil {
 		return
 	}
+	stride = vboNormals.Stride()
+	offset = vboNormals.AttribOffset("VertexNormal")
 	normals := vboNormals.Buffer()
 	// Apply normal matrix to all normal vectors
 	var normalMatrix math32.Matrix3
 	normalMatrix.GetNormalMatrix(m)
-	for i := 0; i < normals.Size(); i += 3 {
+	for i := offset; i < normals.Size(); i += stride {
 		var vertex math32.Vector3
 		normals.GetVector3(i, &vertex)
 		vertex.ApplyMatrix3(&normalMatrix).Normalize()

+ 7 - 6
geometry/plane.go

@@ -9,6 +9,7 @@ import (
 	"github.com/g3n/engine/math32"
 )
 
+// Plane represents a plane geometry
 type Plane struct {
 	Geometry
 	Width          float32
@@ -31,14 +32,14 @@ func NewPlane(width, height float32, widthSegments, heightSegments int) *Plane {
 	plane.WidthSegments = widthSegments
 	plane.HeightSegments = heightSegments
 
-	width_half := width / 2
-	height_half := height / 2
+	widthHalf := width / 2
+	heightHalf := height / 2
 	gridX := widthSegments
 	gridY := heightSegments
 	gridX1 := gridX + 1
 	gridY1 := gridY + 1
-	segment_width := width / float32(gridX)
-	segment_height := height / float32(gridY)
+	segmentWidth := width / float32(gridX)
+	segmentHeight := height / float32(gridY)
 
 	// Create buffers
 	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.
 	for iy := 0; iy < gridY1; iy++ {
-		y := float32(iy)*segment_height - height_half
+		y := float32(iy)*segmentHeight - heightHalf
 		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)
 			normals.Append(0, 0, 1)
 			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"
 )
 
+// Sphere represents a sphere geometry
 type Sphere struct {
 	Geometry
 	Radius         float64
@@ -21,6 +22,7 @@ type Sphere struct {
 	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 {
 
 	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("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
 }

+ 2 - 0
geometry/torus.go

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

+ 1 - 0
gls/build.go

@@ -1,6 +1,7 @@
 // Copyright 2016 The G3N Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
+
 package gls
 
 // 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
 // OpenGL constants specified by "#define GL_*" directives contained in
 // "glcorearb.h" for an specific OpenGL version converted to Go constants.
+
 package gls
 
 const (

+ 32 - 2
gls/glapi.c

@@ -153,8 +153,38 @@ void glapiCheckError(int check) {
 // Internal function to abort process when error
 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"
 )
 
+// GLHeader is the definition of an OpenGL header file, with functions and constant definitions.
 type GLHeader struct {
 	Defines []GLDefine
 	Funcs   []GLFunc
 }
 
+// GLDefine is the definition of an OpenGL constant.
 type GLDefine struct {
 	Name  string
 	Value string
 }
 
+// GLFunc is the definition of an OpenGL function.
 type GLFunc struct {
 	Ptype    string    // type of function pointer (ex: PFNCULLFACEPROC)
 	Spacer   string    // spacer string for formatting
@@ -30,6 +33,7 @@ type GLFunc struct {
 	Params   []GLParam // array of function parameters
 }
 
+// GLParam is the definition of an argument to an OpenGL function (GLFunc).
 type GLParam struct {
 	Qualif string // optional parameter qualifier (ex: const)
 	CType  string // parameter C type
@@ -37,7 +41,7 @@ type GLParam struct {
 	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 {
 
 	// Parses the template
@@ -236,8 +240,38 @@ void glapiCheckError(int check) {
 // Internal function to abort process when error
 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
 // OpenGL constants specified by "#define GL_*" directives contained in
 // "glcorearb.h" for an specific OpenGL version converted to Go constants.
+
 package gls
 
 const (

+ 95 - 17
gls/gls.go

@@ -1,6 +1,7 @@
 // Copyright 2016 The G3N Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
+
 package gls
 
 // #include <stdlib.h>
@@ -51,17 +52,17 @@ type GLS struct {
 }
 
 // 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 {
 	Shaders    int    // Current number of shader programs
 	Vaos       int    // Number of Vertex Array Objects
 	Buffers    int    // Number of Buffer Objects
 	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.
@@ -80,10 +81,10 @@ const (
 	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
-// was established, such as by creating a new window.
+// is established, such as by creating a new window.
 func New() (*GLS, error) {
 
 	gs := new(GLS)
@@ -96,7 +97,7 @@ func New() (*GLS, error) {
 	gs.setDefaultState()
 	gs.checkErrors = true
 
-	// Preallocates conversion buffers
+	// Preallocate conversion buffers
 	size := 1 * 1024
 	gs.gobuf = make([]byte, size)
 	p := C.malloc(C.size_t(size))
@@ -118,7 +119,7 @@ func (gs *GLS) SetCheckErrors(enable bool) {
 	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 {
 
 	return gs.checkErrors
@@ -183,6 +184,9 @@ func (gs *GLS) Stats(s *Stats) {
 	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) {
 
 	if gs.activeTexture == texture {
@@ -192,26 +196,31 @@ func (gs *GLS) ActiveTexture(texture uint32) {
 	gs.activeTexture = texture
 }
 
+// AttachShader attaches the specified shader object to the specified program object.
 func (gs *GLS) AttachShader(program, shader uint32) {
 
 	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) {
 
 	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) {
 
 	C.glBindTexture(C.GLenum(target), C.GLuint(tex))
 }
 
+// BindVertexArray binds the vertex array object.
 func (gs *GLS) BindVertexArray(vao uint32) {
 
 	C.glBindVertexArray(C.GLuint(vao))
 }
 
+// BlendEquation sets the blend equations for all draw buffers.
 func (gs *GLS) BlendEquation(mode uint32) {
 
 	if gs.blendEquation == mode {
@@ -221,6 +230,8 @@ func (gs *GLS) BlendEquation(mode uint32) {
 	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) {
 
 	if gs.blendEquationRGB == modeRGB && gs.blendEquationAlpha == modeAlpha {
@@ -231,6 +242,8 @@ func (gs *GLS) BlendEquationSeparate(modeRGB uint32, modeAlpha uint32) {
 	gs.blendEquationAlpha = modeAlpha
 }
 
+// BlendFunc defines the operation of blending for
+// all draw buffers when blending is enabled.
 func (gs *GLS) BlendFunc(sfactor, dfactor uint32) {
 
 	if gs.blendSrc == sfactor && gs.blendDst == dfactor {
@@ -241,6 +254,8 @@ func (gs *GLS) BlendFunc(sfactor, dfactor uint32) {
 	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) {
 
 	if gs.blendSrcRGB == srcRGB && gs.blendDstRGB == dstRGB &&
@@ -254,66 +269,90 @@ func (gs *GLS) BlendFuncSeparate(srcRGB uint32, dstRGB uint32, srcAlpha uint32,
 	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) {
 
 	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) {
 
 	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) {
 
 	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) {
 
 	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 {
 
 	p := C.glCreateProgram()
 	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 {
 
 	h := C.glCreateShader(C.GLenum(stype))
 	return uint32(h)
 }
 
+// DeleteBuffers deletes n​buffer objects named
+// by the elements of the provided array.
 func (gs *GLS) DeleteBuffers(bufs ...uint32) {
 
 	C.glDeleteBuffers(C.GLsizei(len(bufs)), (*C.GLuint)(&bufs[0]))
 	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) {
 
 	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) {
 
 	C.glDeleteProgram(C.GLuint(program))
 }
 
+// DeleteTextures deletes n​textures named
+// by the elements of the provided array.
 func (gs *GLS) DeleteTextures(tex ...uint32) {
 
 	C.glDeleteTextures(C.GLsizei(len(tex)), (*C.GLuint)(&tex[0]))
 	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) {
 
 	C.glDeleteVertexArrays(C.GLsizei(len(vaos)), (*C.GLuint)(&vaos[0]))
 	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) {
 
 	if gs.depthFunc == mode {
@@ -323,6 +362,7 @@ func (gs *GLS) DepthFunc(mode uint32) {
 	gs.depthFunc = mode
 }
 
+// DepthMask enables or disables writing into the depth buffer.
 func (gs *GLS) DepthMask(flag bool) {
 
 	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) {
 
 	C.glDrawArrays(C.GLenum(mode), C.GLint(first), C.GLsizei(count))
 	gs.stats.Drawcalls++
 }
 
+// DrawBuffer specifies which color buffers are to be drawn into.
 func (gs *GLS) DrawBuffer(mode uint32) {
 
 	C.glDrawBuffer(C.GLenum(mode))
 }
 
+// DrawElements renders primitives from array data.
 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)))
 	gs.stats.Drawcalls++
 }
 
+// Enable enables the specified capability.
 func (gs *GLS) Enable(cap int) {
 
 	if gs.capabilities[cap] == capEnabled {
@@ -366,11 +410,7 @@ func (gs *GLS) Enable(cap int) {
 	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) {
 
 	if gs.capabilities[cap] == capDisabled {
@@ -381,11 +421,19 @@ func (gs *GLS) Disable(cap int) {
 	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) {
 
 	C.glCullFace(C.GLenum(mode))
 }
 
+// FrontFace defines front- and back-facing polygons.
 func (gs *GLS) FrontFace(mode uint32) {
 
 	if gs.frontFace == mode {
@@ -395,6 +443,7 @@ func (gs *GLS) FrontFace(mode uint32) {
 	gs.frontFace = mode
 }
 
+// GenBuffer generates a​buffer object name.
 func (gs *GLS) GenBuffer() uint32 {
 
 	var buf uint32
@@ -403,11 +452,13 @@ func (gs *GLS) GenBuffer() uint32 {
 	return buf
 }
 
+// GenerateMipmap generates mipmaps for the specified texture target.
 func (gs *GLS) GenerateMipmap(target uint32) {
 
 	C.glGenerateMipmap(C.GLenum(target))
 }
 
+// GenTexture generates a texture object name.
 func (gs *GLS) GenTexture() uint32 {
 
 	var tex uint32
@@ -416,6 +467,7 @@ func (gs *GLS) GenTexture() uint32 {
 	return tex
 }
 
+// GenVertexArray generates a vertex array object name.
 func (gs *GLS) GenVertexArray() uint32 {
 
 	var vao uint32
@@ -424,12 +476,14 @@ func (gs *GLS) GenVertexArray() uint32 {
 	return vao
 }
 
+// GetAttribLocation returns the location of the specified attribute variable.
 func (gs *GLS) GetAttribLocation(program uint32, name string) int32 {
 
 	loc := C.glGetAttribLocation(C.GLuint(program), gs.gobufStr(name))
 	return int32(loc)
 }
 
+// GetProgramiv returns the specified parameter from the specified program object.
 func (gs *GLS) GetProgramiv(program, pname uint32, params *int32) {
 
 	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])
 }
 
+// GetString returns a string describing the specified aspect of the current GL connection.
 func (gs *GLS) GetString(name uint32) string {
 
 	cs := C.glGetString(C.GLenum(name))
@@ -472,11 +527,13 @@ func (gs *GLS) GetUniformLocation(program uint32, name string) int32 {
 	return int32(loc)
 }
 
+// GetViewport returns the current viewport information.
 func (gs *GLS) GetViewport() (x, y, width, height int32) {
 
 	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) {
 
 	if gs.lineWidth == width {
@@ -486,27 +543,32 @@ func (gs *GLS) LineWidth(width float32) {
 	gs.lineWidth = width
 }
 
+// LinkProgram links the specified program object.
 func (gs *GLS) LinkProgram(program uint32) {
 
 	C.glLinkProgram(C.GLuint(program))
 }
 
+// GetShaderiv returns the specified parameter from the specified shader object.
 func (gs *GLS) GetShaderiv(shader, pname uint32, params *int32) {
 
 	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) {
 
 	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) {
 
 	csource := gs.cbufStr(src)
 	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{}) {
 
 	C.glTexImage2D(C.GLenum(target),
@@ -520,11 +582,13 @@ func (gs *GLS) TexImage2D(target uint32, level int32, iformat int32, width int32
 		ptr(data))
 }
 
+// TexParameteri sets the specified texture parameter on the specified texture.
 func (gs *GLS) TexParameteri(target uint32, pname uint32, param int32) {
 
 	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) {
 
 	if gs.polygonModeFace == face && gs.polygonModeMode == mode {
@@ -535,6 +599,7 @@ func (gs *GLS) PolygonMode(face, mode uint32) {
 	gs.polygonModeMode = mode
 }
 
+// PolygonOffset sets the scale and units used to calculate depth values.
 func (gs *GLS) PolygonOffset(factor float32, units float32) {
 
 	if gs.polygonOffsetFactor == factor && gs.polygonOffsetUnits == units {
@@ -545,54 +610,63 @@ func (gs *GLS) PolygonOffset(factor float32, units float32) {
 	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) {
 
 	C.glUniform1i(C.GLint(location), C.GLint(v0))
 	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) {
 
 	C.glUniform1f(C.GLint(location), C.GLfloat(v0))
 	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) {
 
 	C.glUniform2f(C.GLint(location), C.GLfloat(v0), C.GLfloat(v1))
 	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) {
 
 	C.glUniform3f(C.GLint(location), C.GLfloat(v0), C.GLfloat(v1), C.GLfloat(v2))
 	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) {
 
 	C.glUniform4f(C.GLint(location), C.GLfloat(v0), C.GLfloat(v1), C.GLfloat(v2), C.GLfloat(v3))
 	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) {
 
 	C.glUniformMatrix3fv(C.GLint(location), C.GLsizei(count), bool2c(transpose), (*C.GLfloat)(pm))
 	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) {
 
 	C.glUniformMatrix4fv(C.GLint(location), C.GLsizei(count), bool2c(transpose), (*C.GLfloat)(pm))
 	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) {
 
 	C.glUniform1fv(C.GLint(location), C.GLsizei(count), (*C.GLfloat)(&v[0]))
 	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) {
 
 	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++
 }
 
+// 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) {
 
 	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++
 }
 
+// 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) {
 
 	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++
 }
 
+// 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) {
 
 	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) {
 
 	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
 }
 
-// Use set this program as the current program.
+// UseProgram sets the specified program as the current program.
 func (gs *GLS) UseProgram(prog *Program) {
 
 	if prog.handle == 0 {

+ 3 - 3
gls/program.go

@@ -13,7 +13,7 @@ import (
 	"strings"
 )
 
-// Shader Program Object
+// Program represents a shader program.
 type Program struct {
 	// Shows source code in error messages
 	ShowSource bool
@@ -50,7 +50,7 @@ func (gs *GLS) NewProgram() *Program {
 	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.
 func (prog *Program) AddShader(stype uint32, source string, defines map[string]interface{}) {
 
@@ -144,7 +144,7 @@ func (prog *Program) Handle() uint32 {
 	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.
 func (prog *Program) GetAttribLocation(name string) int32 {
 

+ 64 - 30
gls/vbo.go

@@ -9,23 +9,23 @@ import (
 	"unsafe"
 )
 
-// VBO abstracts an OpenGL Vertex Buffer Object
+// VBO abstracts an OpenGL Vertex Buffer Object.
 type VBO struct {
-	gs      *GLS            // reference to GLS state
+	gs      *GLS            // Reference to OpenGL state
 	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
 	buffer  math32.ArrayF32 // Data buffer
 	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 {
 	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 {
 
 	vbo := new(VBO)
@@ -33,7 +33,7 @@ func NewVBO() *VBO {
 	return vbo
 }
 
-// init initializes this VBO
+// init initializes the VBO.
 func (vbo *VBO) init() {
 
 	vbo.gs = nil
@@ -43,7 +43,7 @@ func (vbo *VBO) init() {
 	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 {
 
 	vbo.attribs = append(vbo.attribs, VBOattrib{
@@ -53,8 +53,8 @@ func (vbo *VBO) AddAttrib(name string, itemSize int32) *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 {
 
 	for _, attr := range vbo.attribs {
@@ -65,20 +65,26 @@ func (vbo *VBO) Attrib(name string) *VBOattrib {
 	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 {
 
 	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 {
 
 	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.
 func (vbo *VBO) Dispose() {
 
@@ -88,7 +94,7 @@ func (vbo *VBO) Dispose() {
 	vbo.gs = nil
 }
 
-// Sets the VBO buffer
+// SetBuffer sets the VBO buffer.
 func (vbo *VBO) SetBuffer(buffer math32.ArrayF32) *VBO {
 
 	vbo.buffer = buffer
@@ -96,40 +102,65 @@ func (vbo *VBO) SetBuffer(buffer math32.ArrayF32) *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.
 func (vbo *VBO) SetUsage(usage uint32) {
 
 	vbo.usage = usage
 }
 
-// Buffer returns pointer to the VBO buffer
+// Buffer returns a pointer to the VBO buffer.
 func (vbo *VBO) Buffer() *math32.ArrayF32 {
 
 	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() {
 
 	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 {
 
 	stride := 0
-	elsize := int(unsafe.Sizeof(float32(0)))
 	for _, attrib := range vbo.attribs {
-		stride += elsize * int(attrib.ItemSize)
+		stride += int(attrib.ItemSize)
 	}
 	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) {
 
 	// If the VBO buffer is empty, ignore
@@ -141,11 +172,11 @@ func (vbo *VBO) Transfer(gs *GLS) {
 	if vbo.gs == nil {
 		vbo.handle = gs.GenBuffer()
 		gs.BindBuffer(ARRAY_BUFFER, vbo.handle)
-		// Calculates stride
-		stride := vbo.Stride()
+		// Calculates stride size
+		strideSize := vbo.StrideSize()
 		// For each attribute
-		var items uint32 = 0
-		var offset uint32 = 0
+		var items uint32
+		var offset uint32
 		elsize := int32(unsafe.Sizeof(float32(0)))
 		for _, attrib := range vbo.attribs {
 			// 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
 			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)
 			offset = uint32(elsize) * items
 		}
 		vbo.gs = gs // this indicates that the vbo was initialized
 	}
+
+	// If nothing has changed, no need to transfer data to OpenGL
 	if !vbo.update {
 		return
 	}
+
 	// Transfer the VBO data to OpenGL
 	gs.BindBuffer(ARRAY_BUFFER, vbo.handle)
 	gs.BufferData(ARRAY_BUFFER, vbo.buffer.Bytes(), &vbo.buffer[0], vbo.usage)

+ 2 - 1
graphic/axis_helper.go

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

+ 62 - 12
graphic/graphic.go

@@ -9,6 +9,7 @@ import (
 	"github.com/g3n/engine/geometry"
 	"github.com/g3n/engine/gls"
 	"github.com/g3n/engine/material"
+	"github.com/g3n/engine/math32"
 )
 
 // Graphic is a Node which has a visible representation in the scene.
@@ -21,11 +22,15 @@ type Graphic struct {
 	materials  []GraphicMaterial  // Materials
 	mode       uint32             // OpenGL primitive
 	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
 // 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 {
 	imat     material.IMaterial // Associated material
 	start    int                // Index of first element in the geometry
@@ -33,13 +38,15 @@ type GraphicMaterial struct {
 	igraphic IGraphic           // Graphic which contains this GraphicMaterial
 }
 
-// Interface for all Graphics
+// IGraphic is the interface for all Graphic objects.
 type IGraphic interface {
 	core.INode
 	GetGraphic() *Graphic
 	GetGeometry() *geometry.Geometry
 	Renderable() bool
 	SetRenderable(bool)
+	Cullable() bool
+	SetCullable(bool)
 	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.materials = make([]GraphicMaterial, 0)
 	gr.renderable = true
+	gr.cullable = true
 	return gr
 }
 
 // GetGraphic satisfies the IGraphic interface and
-// returns pointer to the base Graphic
+// returns pointer to the base Graphic.
 func (gr *Graphic) GetGraphic() *Graphic {
 
 	return gr
 }
 
 // GetGeometry satisfies the IGraphic interface and returns
-// a pointer to the geometry associated with this graphic
+// a pointer to the geometry associated with this graphic.
 func (gr *Graphic) GetGeometry() *geometry.Geometry {
 
 	return gr.igeom.GetGeometry()
 }
 
-// Dispose overrides the embedded Node Dispose method
+// Dispose overrides the embedded Node Dispose method.
 func (gr *Graphic) Dispose() {
 
 	gr.igeom.Dispose()
@@ -88,20 +96,34 @@ func (gr *Graphic) Dispose() {
 }
 
 // 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) {
 
 	gr.renderable = state
 }
 
 // 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 {
 
 	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.
 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)
 }
 
-// Add group material
+// AddGroupMaterial adds a material for the specified geometry group.
 func (gr *Graphic) AddGroupMaterial(igr IGraphic, imat material.IMaterial, gindex int) {
 
 	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)
 }
 
-// Materials returns slice with this graphic materials
+// Materials returns slice with this graphic materials.
 func (gr *Graphic) Materials() []GraphicMaterial {
 
 	return gr.materials
 }
 
-// GetMaterial returns the  material associated with the specified vertex position
+// GetMaterial returns the material associated with the specified vertex position.
 func (gr *Graphic) GetMaterial(vpos int) material.IMaterial {
 
 	for _, gmat := range gr.materials {
@@ -146,12 +168,40 @@ func (gr *Graphic) GetMaterial(vpos int) material.IMaterial {
 	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 {
 
 	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) {
 
 	// 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"
 )
 
+// GridHelper is the visual representation of a grid
 type GridHelper struct {
 	Lines
 }
@@ -21,14 +22,14 @@ func NewGridHelper(size, step float32, color *math32.Color) *GridHelper {
 
 	grid := new(GridHelper)
 
-	half_size := size / 2
+	half := size / 2
 	positions := math32.NewArrayF32(0, 0)
-	for i := -half_size; i <= half_size; i += step {
+	for i := -half; i <= half; i += step {
 		positions.Append(
-			-half_size, 0, i, color.R, color.G, color.B,
-			half_size, 0, i, color.R, color.G, color.B,
-			i, 0, -half_size, color.R, color.G, color.B,
-			i, 0, half_size, color.R, color.G, color.B,
+			-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
 	mat := material.NewBasic()
-	mat.SetLineWidth(1.0)
 
 	// Initialize lines with the specified geometry and material
 	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/gls"
 	"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 {
 	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
-// with the specified geometry and material
+// with the specified geometry and material.
 func NewLineStrip(igeom geometry.IGeometry, imat material.IMaterial) *LineStrip {
 
 	l := new(LineStrip)
 	l.Graphic.Init(igeom, gls.LINE_STRIP)
 	l.AddMaterial(l, imat, 0, 0)
-	l.uniMVP.Init("MVP")
+	l.uniMVPm.Init("MVP")
 	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) {
 
-	// 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])
 }
 
 // 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) {
 
 	lineRaycast(l, rc, intersects, 1)

+ 11 - 14
graphic/lines.go

@@ -12,19 +12,21 @@ import (
 	"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 {
 	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) {
 
 	l.Graphic.Init(igeom, gls.LINES)
 	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 {
 
 	l := new(Lines)
@@ -32,28 +34,23 @@ func NewLines(igeom geometry.IGeometry, imat material.IMaterial) *Lines {
 	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) {
 
-	// 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])
 }
 
 // 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) {
 
 	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) {
 
 	// Get the bounding sphere

+ 25 - 25
graphic/mesh.go

@@ -12,16 +12,17 @@ import (
 	"github.com/g3n/engine/math32"
 )
 
+// Mesh is a Graphic with uniforms for the model, view, projection, and normal matrices.
 type Mesh struct {
 	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
-// 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 {
 
 	m := new(Mesh)
@@ -29,14 +30,15 @@ func NewMesh(igeom geometry.IGeometry, imat material.IMaterial) *Mesh {
 	return m
 }
 
+// Init initializes the Mesh and its uniforms.
 func (m *Mesh) Init(igeom geometry.IGeometry, imat material.IMaterial) {
 
 	m.Graphic.Init(igeom, gls.TRIANGLES)
 
 	// 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
 	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) {
 
 	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) {
 
 	m.Graphic.AddGroupMaterial(m, imat, gindex)
@@ -60,23 +63,20 @@ func (m *Mesh) AddGroupMaterial(imat material.IMaterial, gindex int) {
 // the model matrices.
 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])
 
-	// 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])
 
-	// Calculates normal matrix and updates uniform
+	// Calculates normal matrix and transfer uniform
 	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])
 }
 
@@ -152,9 +152,7 @@ func (m *Mesh) Raycast(rc *core.Raycaster, intersects *[]core.Intersect) {
 	positions := vboPos.Buffer()
 	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
 	if indices.Size() > 0 {
@@ -178,7 +176,9 @@ func (m *Mesh) Raycast(rc *core.Raycaster, intersects *[]core.Intersect) {
 		}
 		// Geometry has NO indexed vertices
 	} 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
 			a := i / 3
 			b := a + 1

+ 22 - 21
graphic/normals_helper.go

@@ -12,29 +12,30 @@ import (
 	"github.com/g3n/engine/math32"
 )
 
+// NormalsHelper is the visual representation of the normals of a target object.
 type NormalsHelper struct {
 	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.
-// 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 {
 
 	// Creates new Normals helper
 	nh := new(NormalsHelper)
 	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
-	nh.tgeom = ig.GetGeometry()
+	nh.targetGeometry = ig.GetGeometry()
 
 	// Get the number of target vertex positions
-	vertices := nh.tgeom.VBO("VertexPosition")
+	vertices := nh.targetGeometry.VBO("VertexPosition")
 	n := vertices.Buffer().Size() * 2
 
 	// Creates this helper geometry
@@ -53,8 +54,8 @@ func NewNormalsHelper(ig IGraphic, size float32, color *math32.Color, lineWidth
 	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() {
 
 	var v1 math32.Vector3
@@ -62,29 +63,29 @@ func (nh *NormalsHelper) Update() {
 	var normalMatrix math32.Matrix3
 
 	// Updates the target object matrix and get its normal matrix
-	matrixWorld := nh.target.MatrixWorld()
+	matrixWorld := nh.targetNode.MatrixWorld()
 	normalMatrix.GetNormalMatrix(&matrixWorld)
 
 	// Get the target positions and normals buffers
-	tposvbo := nh.tgeom.VBO("VertexPosition")
-	tpositions := tposvbo.Buffer()
-	tnormvbo := nh.tgeom.VBO("VertexNormal")
-	tnormals := tnormvbo.Buffer()
+	tPosVBO := nh.targetGeometry.VBO("VertexPosition")
+	tPositions := tPosVBO.Buffer()
+	tNormVBO := nh.targetGeometry.VBO("VertexNormal")
+	tNormals := tNormVBO.Buffer()
 
 	// Get this object positions buffer
 	geom := nh.GetGeometry()
-	posvbo := geom.VBO("VertexPosition")
-	positions := posvbo.Buffer()
+	posVBO := geom.VBO("VertexPosition")
+	positions := posVBO.Buffer()
 
 	// 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
 		// to get the base for this normal line segment.
-		tpositions.GetVector3(pos, &v1)
+		tPositions.GetVector3(pos, &v1)
 		v1.ApplyMatrix4(&matrixWorld)
 
 		// 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)
 
 		// 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+3, &v2)
 	}
-	posvbo.Update()
+	posVBO.Update()
 }

+ 14 - 16
graphic/points.go

@@ -12,9 +12,10 @@ import (
 	"github.com/g3n/engine/math32"
 )
 
+// Points represents a geometry containing only points
 type Points struct {
 	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
@@ -26,26 +27,21 @@ func NewPoints(igeom geometry.IGeometry, imat material.IMaterial) *Points {
 	if imat != nil {
 		p.AddMaterial(p, imat, 0, 0)
 	}
-	p.uniMVPM.Init("MVP")
+	p.uniMVPm.Init("MVP")
 	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) {
 
-	// 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
-	location := p.uniMVPM.Location(gs)
+	mvpm := p.ModelViewProjectionMatrix()
+	location := p.uniMVPm.Location(gs)
 	gs.UniformMatrix4fv(location, 1, false, &mvpm[0])
 }
 
 // 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) {
 
 	// 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
-	vbPos := geom.VBO("VertexPosition")
-	if vbPos == nil {
+	vboPos := geom.VBO("VertexPosition")
+	if vboPos == nil {
 		panic("points.Raycast(): VertexPosition VBO not found")
 	}
-	positions := vbPos.Buffer()
+	positions := vboPos.Buffer()
 
 	var point math32.Vector3
 	indices := geom.Indices()
@@ -111,8 +107,10 @@ func (p *Points) Raycast(rc *core.Raycaster, intersects *[]core.Intersect) {
 			testPoint(&point, i)
 		}
 	} 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)
 		}
 	}

+ 24 - 20
graphic/skybox.go

@@ -13,26 +13,29 @@ import (
 	"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 {
 	DirAndPrefix string
 	Extension    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) {
 
 	skybox := new(Skybox)
 
-	geom := geometry.NewBox(50, 50, 50, 1, 1, 1)
+	geom := geometry.NewCube(1)
 	skybox.Graphic.Init(geom, gls.TRIANGLES)
+	skybox.Graphic.SetCullable(false)
 
 	for i := 0; i < 6; i++ {
 		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.SetSide(material.SideBack)
 		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)
 	}
 
 	// 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
 }
@@ -59,11 +68,6 @@ func NewSkybox(data SkyboxData) (*Skybox, error) {
 // the model matrices.
 func (skybox *Skybox) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
 
-	// TODO
-	// Disable writes to the depth buffer (call glDepthMask(GL_FALSE)).
-	// This will cause every other object to draw over the skybox, making it always appear "behind" everything else.
-	// Since writes to the depth buffer are off, it doesn't matter how small the skybox is as long as it's larger than the camera's near clip plane.
-
 	var mvm math32.Matrix4
 	mvm.Copy(&rinfo.ViewMatrix)
 
@@ -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?
 
 	// Transfer mvp uniform
-	location := skybox.uniMVM.Location(gs)
+	location := skybox.uniMVm.Location(gs)
 	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 = skybox.uniMVPM.Location(gs)
+	location = skybox.uniMVPm.Location(gs)
 	gs.UniformMatrix4fv(location, 1, false, &mvpm[0])
 
 	// Calculates normal matrix and updates uniform
 	var nm math32.Matrix3
 	nm.GetNormalMatrix(&mvm)
-	location = skybox.uniNM.Location(gs)
+	location = skybox.uniNm.Location(gs)
 	gs.UniformMatrix3fv(location, 1, false, &nm[0])
 }

+ 5 - 3
graphic/sprite.go

@@ -12,6 +12,7 @@ import (
 	"github.com/g3n/engine/math32"
 )
 
+// Sprite is a potentially animated image positioned in space that always faces the camera.
 type Sprite struct {
 	Graphic             // Embedded graphic
 	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
 }
 
+// RenderSetup sets up the rendering of the sprite.
 func (s *Sprite) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
 
 	// Calculates model view matrix
@@ -73,12 +75,12 @@ func (s *Sprite) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
 	rotation.X = 0
 	rotation.Y = 0
 	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
 	var mvpm math32.Matrix4
-	mvpm.MultiplyMatrices(&rinfo.ProjMatrix, &mvm_new)
+	mvpm.MultiplyMatrices(&rinfo.ProjMatrix, &mvmNew)
 	location := s.uniMVPM.Location(gs)
 	gs.UniformMatrix4fv(location, 1, false, &mvpm[0])
 }

+ 1 - 1
gui/align.go

@@ -8,7 +8,7 @@ package gui
 type Align int
 
 const (
-	AlignNone   = Align(iota) // No aligh
+	AlignNone   = Align(iota) // No alignment
 	AlignLeft                 // Align horizontally at left
 	AlignRight                // Align horizontally at right
 	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:
 // https://github.com/google/material-design-icons
-//
+
 package icon
 
 const (
@@ -940,7 +940,7 @@ const (
 	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
 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
 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 = "_"
 
 // Panel and layout types
@@ -81,7 +81,7 @@ const (
 	AttribAspectHeight   = "aspectheight"  // float32
 	AttribAspectWidth    = "aspectwidth"   // float32
 	AttribBgColor        = "bgcolor"       // Color4
-	AttribBorders        = "borders"       // BorderSizes
+	AttribBorders        = "borders"       // RectBounds
 	AttribBorderColor    = "bordercolor"   // Color4
 	AttribChecked        = "checked"       // bool
 	AttribColor          = "color"         // Color4
@@ -114,12 +114,12 @@ const (
 	AttribLineSpacing    = "linespacing"   // float32
 	AttribLines          = "lines"         // int
 	AttribMargin         = "margin"        // float32
-	AttribMargins        = "margins"       // BorderSizes
+	AttribMargins        = "margins"       // RectBounds
 	AttribMinwidth       = "minwidth"      // float32 Table
 	AttribAutoHeight     = "autoheight"    // bool
 	AttribAutoWidth      = "autowidth"     // bool
 	AttribName           = "name"          // string
-	AttribPaddings       = "paddings"      // BorderSizes
+	AttribPaddings       = "paddings"      // RectBounds
 	AttribPanel0         = "panel0"        // map[string]interface{}
 	AttribPanel1         = "panel1"        // map[string]interface{}
 	AttribParentInternal = "parent_"       // string (internal attribute)
@@ -387,11 +387,8 @@ func (b *Builder) ParseString(desc string) error {
 				}
 			}
 			return ms, nil
-
-		default:
-			return v, nil
 		}
-		return nil, nil
+		return v, nil
 	}
 
 	// 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
 	if am[AttribMargins] != nil {
-		panel.SetMarginsFrom(am[AttribMargins].(*BorderSizes))
+		panel.SetMarginsFrom(am[AttribMargins].(*RectBounds))
 	}
 
 	// Set optional border sizes
 	if am[AttribBorders] != nil {
-		panel.SetBordersFrom(am[AttribBorders].(*BorderSizes))
+		panel.SetBordersFrom(am[AttribBorders].(*RectBounds))
 	}
 
 	// Set optional border color
@@ -563,7 +560,7 @@ func (b *Builder) SetAttribs(am map[string]interface{}, ipan IPanel) error {
 
 	// Set optional paddings sizes
 	if am[AttribPaddings] != nil {
-		panel.SetPaddingsFrom(am[AttribPaddings].(*BorderSizes))
+		panel.SetPaddingsFrom(am[AttribPaddings].(*RectBounds))
 	}
 
 	// Set optional panel color
@@ -968,10 +965,10 @@ func AttribCheckBorderSizes(b *Builder, am map[string]interface{}, fname string)
 		return nil
 	}
 	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
 	}
-	am[fname] = &BorderSizes{va[0], va[1], va[2], va[3]}
+	am[fname] = &RectBounds{va[0], va[1], va[2], va[3]}
 	return nil
 }
 
@@ -1017,10 +1014,8 @@ func AttribCheckFloat(b *Builder, am map[string]interface{}, fname string) error
 	case float64:
 		am[fname] = float32(n)
 		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

+ 1 - 1
gui/builder_panel.go

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

+ 15 - 24
gui/button.go

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

+ 6 - 2
gui/chart.go

@@ -101,6 +101,7 @@ func (ch *Chart) SetTitle(title string, size float64) {
 	// Sets title
 	if ch.title == nil {
 		ch.title = NewLabel(title)
+		ch.title.SetColor4(math32.NewColor4("black"))
 		ch.Add(ch.title)
 	}
 	ch.title.SetText(title)
@@ -170,6 +171,7 @@ func (ch *Chart) SetScaleX(lines int, color *math32.Color) {
 	value := ch.firstX
 	for i := 0; i < lines; i++ {
 		l := NewLabel(fmt.Sprintf(ch.formatX, value))
+		l.SetColor4(math32.NewColor4("black"))
 		l.SetFontSize(ch.fontSizeX)
 		ch.Add(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)
 	for i := 0; i < lines; i++ {
 		l := NewLabel(fmt.Sprintf(ch.formatY, value))
+		l.SetColor4(math32.NewColor4("black"))
 		l.SetFontSize(ch.fontSizeY)
 		ch.Add(l)
 		ch.labelsY = append(ch.labelsY, l)
@@ -282,7 +285,7 @@ func (ch *Chart) SetRangeYauto(auto bool) {
 	ch.updateGraphs()
 }
 
-// Returns the current y range
+// RangeY returns the current y range
 func (ch *Chart) RangeY() (minY, maxY float32) {
 
 	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 {
 	Panel                   // Embedded panel

+ 8 - 16
gui/checkradio.go

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

+ 12 - 0
gui/control_folder.go

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

+ 4 - 0
gui/docklayout.go

@@ -4,9 +4,11 @@
 
 package gui
 
+// DockLayout is the layout for docking panels to the internal edges of their parent.
 type DockLayout struct {
 }
 
+// DockLayoutParams specifies the edge to dock to.
 type DockLayoutParams struct {
 	Edge int
 }
@@ -19,11 +21,13 @@ const (
 	DockCenter
 )
 
+// NewDockLayout returns a pointer to a new DockLayout.
 func NewDockLayout() *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) {
 
 	pan := ipan.GetPanel()

+ 35 - 46
gui/dropdown.go

@@ -6,10 +6,10 @@ package gui
 
 import (
 	"github.com/g3n/engine/gui/assets/icon"
-	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/window"
 )
 
+// DropDown represents a dropdown GUI element.
 type DropDown struct {
 	Panel                        // Embedded panel
 	icon         *Label          // internal label with icon
@@ -23,21 +23,15 @@ type DropDown struct {
 	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 {
-	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.
@@ -58,8 +52,8 @@ func NewDropDown(width float32, item *ImageLabel) *DropDown {
 	dd.Panel.Add(dd.litem)
 
 	// 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.Panel.Add(dd.icon)
 
@@ -72,6 +66,7 @@ func NewDropDown(width float32, item *ImageLabel) *DropDown {
 	dd.list.Subscribe(OnMouseDown, dd.onListMouse)
 	dd.list.Subscribe(OnMouseOut, dd.onListMouse)
 	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.update()
@@ -80,21 +75,21 @@ func NewDropDown(width float32, item *ImageLabel) *DropDown {
 	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) {
 
 	dd.list.Add(item)
 }
 
 // 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) {
 
 	dd.list.InsertAt(pos, item)
 }
 
 // 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) {
 
 	dd.list.RemoveAt(pos)
@@ -112,8 +107,7 @@ func (dd *DropDown) Len() int {
 	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 {
 
 	return dd.selItem
@@ -165,14 +159,12 @@ func (dd *DropDown) onCursor(evname string, ev interface{}) {
 
 	if evname == OnCursorEnter {
 		dd.overDropdown = true
-		dd.update()
-		return
 	}
 	if evname == OnCursorLeave {
 		dd.overDropdown = false
-		dd.update()
-		return
 	}
+	dd.update()
+	dd.root.StopPropagation(StopAll)
 }
 
 // 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
-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
 // from the list.
@@ -264,24 +256,21 @@ func (dd *DropDown) recalc() {
 func (dd *DropDown) update() {
 
 	if dd.overDropdown || dd.overList {
-		dd.applyStyle(dd.styles.Over)
+		dd.applyStyle(&dd.styles.Over)
 		dd.list.ApplyStyle(StyleOver)
 		return
 	}
 	if dd.focus {
-		dd.applyStyle(dd.styles.Focus)
+		dd.applyStyle(&dd.styles.Focus)
 		dd.list.ApplyStyle(StyleFocus)
 		return
 	}
-	dd.applyStyle(dd.styles.Normal)
+	dd.applyStyle(&dd.styles.Normal)
 	dd.list.ApplyStyle(StyleNormal)
 }
 
 // applyStyle applies the specified style
 func (dd *DropDown) applyStyle(s *DropDownStyle) {
 
-	dd.SetBordersFrom(&s.Border)
-	dd.SetBordersColor4(&s.BorderColor)
-	dd.SetPaddingsFrom(&s.Paddings)
-	dd.SetColor(&s.BgColor)
+	dd.Panel.ApplyStyle(&s.PanelStyle)
 }

+ 15 - 10
gui/edit.go

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

+ 1 - 0
gui/filllayout.go

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

+ 22 - 27
gui/folder.go

@@ -8,6 +8,7 @@ import (
 	"github.com/g3n/engine/math32"
 )
 
+// Folder represents a folder GUI element.
 type Folder struct {
 	Panel               // Embedded panel
 	label        Label  // Folder label
@@ -18,25 +19,23 @@ type Folder struct {
 	alignRight   bool
 }
 
+// FolderStyle contains the styling of a Folder.
 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
 }
 
+// FolderStyles contains a FolderStyle for each valid GUI state.
 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
-// with the specified text and initial width
+// with the specified text and initial width.
 func NewFolder(text string, width float32, contentPanel IPanel) *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
-// 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) {
 
 	f.Panel.Initialize(width, 0)
@@ -57,7 +56,7 @@ func (f *Folder) Initialize(text string, width float32, contentPanel IPanel) {
 
 	// Create icon
 	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)
 
 	// Setup content panel
@@ -76,7 +75,7 @@ func (f *Folder) Initialize(text string, width float32, contentPanel IPanel) {
 	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) {
 
 	f.styles = fs
@@ -84,7 +83,7 @@ func (f *Folder) SetStyles(fs *FolderStyles) {
 }
 
 // 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) {
 
 	f.alignRight = state
@@ -92,7 +91,7 @@ func (f *Folder) SetAlignRight(state bool) {
 }
 
 // TotalHeight returns this folder total height
-// considering the contents panel, if visible
+// considering the contents panel, if visible.
 func (f *Folder) TotalHeight() float32 {
 
 	height := f.Height()
@@ -102,7 +101,7 @@ func (f *Folder) TotalHeight() float32 {
 	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{}) {
 
 	switch evname {
@@ -139,29 +138,25 @@ func (f *Folder) onCursor(evname string, ev interface{}) {
 func (f *Folder) update() {
 
 	if f.cursorOver {
-		f.applyStyle(f.styles.Over)
+		f.applyStyle(&f.styles.Over)
 		return
 	}
-	f.applyStyle(f.styles.Normal)
+	f.applyStyle(&f.styles.Normal)
 }
 
 // applyStyle applies the specified style
 func (f *Folder) applyStyle(s *FolderStyle) {
 
-	f.SetMarginsFrom(&s.Margins)
-	f.SetBordersColor4(&s.BorderColor)
-	f.SetBordersFrom(&s.Border)
-	f.SetPaddingsFrom(&s.Paddings)
-	f.SetColor(&s.BgColor)
+	f.Panel.ApplyStyle(&s.PanelStyle)
 
 	icode := 0
 	if f.contentPanel.GetPanel().Visible() {
 		icode = 1
 	}
 	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() {

+ 5 - 5
gui/hboxlayout.go

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

+ 1 - 0
gui/ilayout.go

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

+ 5 - 14
gui/image_button.go

@@ -5,11 +5,11 @@
 package gui
 
 import (
-	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/texture"
 	"github.com/g3n/engine/window"
 )
 
+// ImageButton represents an image button GUI element
 type ImageButton struct {
 	*Panel                                             // Embedded Panel
 	label       *Label                                 // Label panel
@@ -32,13 +32,7 @@ const (
 )
 
 // ImageButton style
-type ImageButtonStyle struct {
-	Border      BorderSizes
-	Paddings    BorderSizes
-	BorderColor math32.Color4
-	BgColor     math32.Color4
-	FgColor     math32.Color
-}
+type ImageButtonStyle BasicStyle
 
 // All ImageButton styles
 type ImageButtonStyles struct {
@@ -117,7 +111,7 @@ func (b *ImageButton) SetIcon(icode string) {
 	b.iconLabel = true
 	if b.label == nil {
 		// Create icon
-		b.label = NewLabel(icode, true)
+		b.label = NewIcon(icode)
 		b.Panel.Add(b.label)
 	} else {
 		b.label.SetText(icode)
@@ -253,12 +247,9 @@ func (b *ImageButton) update() {
 // applyStyle applies the specified button style
 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 {
-		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
 }
 
-// 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
 // with the specified text for the label and no image/icon
@@ -78,8 +72,8 @@ func (il *ImageLabel) SetIcon(icon string) {
 		il.image = 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.icon.SetText(icon)
@@ -194,10 +188,7 @@ func (il *ImageLabel) CopyFields(other *ImageLabel) {
 // applyStyle applies the specified image label style
 func (il *ImageLabel) applyStyle(s *ImageLabelStyle) {
 
-	il.SetBordersColor4(&s.BorderColor)
-	il.SetBordersFrom(&s.Border)
-	il.SetPaddingsFrom(&s.Paddings)
-	il.SetColor4(&s.BgColor)
+	il.Panel.ApplyStyle(&s.PanelStyle)
 	if il.icon != nil {
 		il.icon.SetColor4(&s.FgColor)
 	}
@@ -212,7 +203,7 @@ func (il *ImageLabel) recalc() {
 	height := il.Panel.ContentHeight()
 
 	// Image or icon width
-	var imgWidth float32 = 0
+	var imgWidth float32
 	var spacing float32
 	if il.image != nil {
 		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"
 )
 
-// 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 {
-	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)
-	if len(icon) > 0 && icon[0] {
-		l.initialize(msg, StyleDefault().FontIcon)
-	} else {
-		l.initialize(msg, StyleDefault().Font)
-	}
+	l.initialize(msg, font)
 	return l
 }
 
 // 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) {
 
 	l.font = font
 	l.Panel.Initialize(0, 0)
-	l.fontSize = 14
-	l.fontDPI = 72
-	l.lineSpacing = 1.0
-	l.bgColor = math32.Color4{0, 0, 0, 0}
-	l.fgColor = math32.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)
 }
 
-// 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
-	l.currentText = msg
-	if msg == "" {
-		msg = " "
+	l.text = text
+	if text == "" {
+		text = " "
 	}
 
 	// 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 {
-		l.tex = texture.NewTexture2DFromRGBA(canvas.RGBA)
+		l.tex = texture.NewTexture2DFromRGBA(textImage)
 		l.tex.SetMagFilter(gls.NEAREST)
 		l.tex.SetMinFilter(gls.NEAREST)
 		l.Panel.Material().AddTexture(l.tex)
 		// Otherwise update texture with new image
 	} else {
-		l.tex.SetFromRGBA(canvas.RGBA)
+		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 {
 
-	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 {
 
-	l.fgColor.FromColor(color, 1.0)
-	l.SetText(l.currentText)
+	l.style.FgColor.FromColor(color, 1.0)
+	l.SetText(l.text)
 	return l
 }
 
-// SetColor4 sets the color4 of the label text
+// SetColor4 sets the text color.
 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
 }
 
-// Color returns the current color of the label text
+// Color returns the text color.
 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
 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
 }
 
-// SetBgColor4 sets the color4 of the label background
+// SetBgColor4 sets the background color.
 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
 }
 
-// BgColor returns the current color the label background
+// BgColor returns returns the background color.
 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) {
 
 	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 {
 
-	l.fontSize = size
-	l.SetText(l.currentText)
+	l.style.PointSize = size
+	l.SetText(l.text)
 	return l
 }
 
-// FontSize returns the current label font size
+// FontSize returns the point size of the font.
 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 {
 
-	l.fontDPI = dpi
-	l.SetText(l.currentText)
+	l.style.DPI = dpi
+	l.SetText(l.text)
 	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.
-// The default value is 1.0
 func (l *Label) SetLineSpacing(spacing float64) *Label {
 
-	l.lineSpacing = spacing
-	l.SetText(l.currentText)
+	l.style.LineSpacing = spacing
+	l.SetText(l.text)
 	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
 // specified line and column.
 // It is normally used by the Edit widget.
 func (l *Label) setTextCaret(msg string, mx, width, line, col int) {
 
 	// Set font properties
-	l.font.SetSize(l.fontSize)
-	l.font.SetDPI(l.fontDPI)
-	l.font.SetLineSpacing(l.lineSpacing)
-	l.font.SetBgColor4(&l.bgColor)
-	l.font.SetFgColor4(&l.fgColor)
+	l.font.SetAttributes(&l.style.FontAttributes)
+	l.font.SetColor(&l.style.FgColor)
 
 	// Create canvas and draw text
 	_, 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)
 
 	// 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
 	l.Panel.SetContentSize(float32(width), float32(height))
-	l.currentText = msg
+	l.text = msg
 }

+ 26 - 33
gui/list.go

@@ -5,12 +5,12 @@
 package gui
 
 import (
-	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/window"
 )
 
+// List represents a list GUI element
 type List struct {
-	Scroller             // Embedded scroller
+	ItemScroller         // Embedded scroller
 	styles   *ListStyles // Pointer to styles
 	single   bool        // Single selection flag (default is true)
 	focus    bool        // has keyboard focus
@@ -19,8 +19,7 @@ type List struct {
 	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 {
 	Panel               // Container panel
 	item        IPanel  // Original item
@@ -30,11 +29,13 @@ type ListItem struct {
 	list        *List   // Pointer to list
 }
 
+// ListStyles
 type ListStyles struct {
-	Scroller *ScrollerStyles
+	Scroller *ItemScrollerStyles
 	Item     *ListItemStyles
 }
 
+// ListItemStyles
 type ListItemStyles struct {
 	Normal      ListItemStyle
 	Over        ListItemStyle
@@ -43,15 +44,10 @@ type ListItemStyles struct {
 	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"
 
 // 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.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 {
 		li.keyNext = window.KeyDown
@@ -116,7 +112,7 @@ func (li *List) Single() bool {
 func (li *List) SetStyles(s *ListStyles) {
 
 	li.styles = s
-	li.Scroller.SetStyles(li.styles.Scroller)
+	li.ItemScroller.SetStyles(li.styles.Scroller)
 	li.update()
 }
 
@@ -127,11 +123,11 @@ func (li *List) Add(item IPanel) {
 }
 
 // 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) {
 
 	litem := newListItem(li, item)
-	li.Scroller.InsertAt(pos, litem)
+	li.ItemScroller.InsertAt(pos, litem)
 	litem.Panel.Subscribe(OnMouseDown, litem.onMouse)
 	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 {
 
 	// Remove the list item from the internal scroller
-	pan := li.Scroller.RemoveAt(pos)
+	pan := li.ItemScroller.RemoveAt(pos)
 	litem := pan.(*ListItem)
 
 	// 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
 func (li *List) ItemAt(pos int) IPanel {
 
-	item := li.Scroller.ItemAt(pos)
+	item := li.ItemScroller.ItemAt(pos)
 	if item == nil {
 		return nil
 	}
@@ -197,7 +193,7 @@ func (li *List) Selected() []IPanel {
 	return sel
 }
 
-// Select selects or unselects the specified item
+// SetSelected selects or unselects the specified item
 func (li *List) SetSelected(item IPanel, state bool) {
 
 	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) {
 
 	litem.selected = state
 	//litem.item.SetSelected2(state)
 }
 
-// setHighlighted sets this item selected state
+// SetHighlighted sets this item selected state
 func (litem *ListItem) SetHighlighted(state bool) {
 
 	litem.highlighted = state
@@ -538,10 +534,7 @@ func (litem *ListItem) update() {
 // applyStyle applies the specified style to this ListItem
 func (litem *ListItem) applyStyle(s *ListItemStyle) {
 
-	litem.SetBordersFrom(&s.Border)
-	litem.SetBordersColor4(&s.BorderColor)
-	pads := s.Paddings
-	pads.Left += litem.padLeft
-	litem.SetPaddingsFrom(&pads)
-	litem.SetColor4(&s.BgColor)
+	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/math32"
 	"github.com/g3n/engine/window"
+
 	"time"
 )
 
@@ -21,13 +22,7 @@ type Menu struct {
 }
 
 // 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
 type MenuBodyStyles struct {
@@ -63,14 +58,11 @@ type MenuItem struct {
 
 // MenuItemStyle describes the style of a menu item
 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
@@ -206,7 +198,7 @@ func (m *Menu) AddMenu(text string, subm *Menu) *MenuItem {
 	mi.submenu.autoOpen = true
 	mi.menu = m
 	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.submenu)
@@ -478,10 +470,7 @@ func (m *Menu) update() {
 // applyStyle applies the specified menu body style
 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
@@ -605,7 +594,7 @@ func (mi *MenuItem) SetIcon(icon string) *MenuItem {
 		mi.licon = nil
 	}
 	// Sets the new icon
-	mi.licon = NewLabel(icon, true)
+	mi.licon = NewIcon(icon)
 	mi.Panel.Add(mi.licon)
 	mi.update()
 	return mi
@@ -827,15 +816,12 @@ func (mi *MenuItem) update() {
 // applyStyle applies the specified menu item style
 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 {
 		mi.licon.SetPaddingsFrom(&mis.IconPaddings)
 	}
 	if mi.label != nil {
-		mi.label.SetColor(&mis.FgColor)
+		mi.label.SetColor4(&mis.FgColor)
 	}
 	if mi.shortcut != nil {
 		mi.shortcut.SetPaddingsFrom(&mis.ShortcutPaddings)

+ 89 - 52
gui/panel.go

@@ -46,7 +46,11 @@ type IPanel interface {
 	SetRoot(*Root)
 	LostKeyFocus()
 	TotalHeight() float32
+	TotalWidth() float32
 	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.
@@ -54,30 +58,30 @@ type IPanel interface {
 // and a content area. The content area can be associated with a texture
 // It is the building block of most GUI widgets.
 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
 		bounds        math32.Vector4 // panel bounds 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 (
 	deltaZ    = -0.000001      // delta Z for bounded 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
 // returns pointer to this panel
-func (pan *Panel) GetPanel() *Panel {
+func (p *Panel) GetPanel() *Panel {
 
-	return pan
+	return p
 }
 
 // SetRoot satisfies the IPanel interface
@@ -206,7 +226,14 @@ func (p *Panel) LostKeyFocus() {
 // height of this panel considering visible not bounded children
 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
@@ -328,15 +355,15 @@ func (p *Panel) SetMargins(top, right, bottom, left float32) {
 }
 
 // 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.resize(p.calcWidth(), p.calcHeight(), true)
 }
 
 // Margins returns the current margin sizes in pixels
-func (p *Panel) Margins() BorderSizes {
+func (p *Panel) Margins() RectBounds {
 
 	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
-// 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.resize(p.calcWidth(), p.calcHeight(), true)
 }
 
 // Borders returns this panel current border sizes
-func (p *Panel) Borders() BorderSizes {
+func (p *Panel) Borders() RectBounds {
 
 	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
-// 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.resize(p.calcWidth(), p.calcHeight(), true)
 }
 
 // Paddings returns this panel padding sizes in pixels
-func (p *Panel) Paddings() BorderSizes {
+func (p *Panel) Paddings() RectBounds {
 
 	return p.paddingSizes
 }
@@ -399,7 +426,7 @@ func (p *Panel) SetBordersColor4(color *math32.Color4) {
 	p.SetChanged(true)
 }
 
-// BorderColor4 returns current border color
+// BordersColor4 returns current border color
 func (p *Panel) BordersColor4() math32.Color4 {
 
 	return p.udata.bordersColor
@@ -436,6 +463,18 @@ func (p *Panel) Color4() math32.Color4 {
 	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.
 // The external size of the panel may increase or decrease to acomodate
 // 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.
-// 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) {
 
 	p.SetContentSize(width, p.content.Height)
 }
 
 // SetContentHeight sets this panel content height to the specified dimension in pixels.
-// The external size of the panel may increase or decrease to acomodate the new width
+// The external size of the panel may increase or decrease to accommodate the new width
 func (p *Panel) SetContentHeight(height float32) {
 
 	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.
 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 true
@@ -701,16 +738,16 @@ func (p *Panel) setZ(z, zunb float32) (float32, float32) {
 			z, zunb = ichild.(IPanel).GetPanel().setZ(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

+ 33 - 25
gui/root.go

@@ -22,7 +22,7 @@ type Root struct {
 	mouseFocus        IPanel         // current child panel with mouse focus
 	scrollFocus       IPanel         // current child panel with scroll focus
 	modalPanel        IPanel         // current modal panel
-	targets           listPanelZ     // preallocated list of target panels
+	targets           []IPanel       // preallocated list of target panels
 }
 
 const (
@@ -152,34 +152,44 @@ func (r *Root) StopPropagation(events int) {
 	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() {
 
 	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)
 }
 
-// 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() {
 
 	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() {
 
 	r.win.SetStandardCursor(window.VResizeCursor)
 }
 
+// TODO allow setting a custom cursor
+
 // onKey is called when key events are received
 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)
 }
 
-// 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{}) {
 
 	// 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]
 
 	// 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)
 	checkPanel = func(ipan IPanel) {
 		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
 	// 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
 
 	// Send events to panels
@@ -401,14 +416,7 @@ func (r *Root) canDispatch(ipan IPanel) bool {
 	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 {
-	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 {
@@ -35,21 +37,23 @@ type scrollBarButton struct {
 	pressed bool       // mouse button pressed flag
 	mouseX  float32    // last mouse click x 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
@@ -78,7 +82,7 @@ func newScrollBar(width, height float32, vertical bool) *ScrollBar {
 // initialize initializes this scrollbar
 func (sb *ScrollBar) initialize(width, height float32, vertical bool) {
 
-	sb.style = &StyleDefault().ScrollBar
+	sb.styles = &StyleDefault().ScrollBar
 	sb.vertical = vertical
 	sb.Panel.Initialize(width, height)
 	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(OnCursor, sb.button.onCursor)
 	sb.button.SetMargins(1, 1, 1, 1)
+	sb.button.Size = sb.styles.Normal.ButtonLength
 	sb.button.sb = sb
 	sb.Add(&sb.button)
 
@@ -96,15 +101,36 @@ func (sb *ScrollBar) initialize(width, height float32, vertical bool) {
 	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
 // The returned value is between 0.0 and 1.0
 func (sb *ScrollBar) Value() float64 {
 
 	if sb.vertical {
-		return float64(sb.button.Position().Y) / (float64(sb.content.Height) - float64(sb.button.height))
-	} else {
-		return float64(sb.button.Position().X) / (float64(sb.content.Width) - float64(sb.button.width))
+		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
@@ -145,23 +171,34 @@ func (sb *ScrollBar) onMouse(evname string, ev interface{}) {
 func (sb *ScrollBar) recalc() {
 
 	if sb.vertical {
-		sb.button.SetSize(sb.content.Width, sb.style.Button.Size)
+		sb.button.SetSize(sb.content.Width, sb.button.Size)
 	} 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() {
 
-	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
@@ -196,11 +233,11 @@ func (button *scrollBarButton) onCursor(evname string, ev interface{}) {
 	if button.sb.vertical {
 		dy := button.mouseY - e.Ypos
 		py := button.Position().Y
-		button.SetPositionY(math32.Clamp(py-dy, 0, button.sb.content.Height-button.sb.style.Button.Size))
+		button.SetPositionY(math32.Clamp(py-dy, 0, button.sb.content.Height-button.Size))
 	} else {
 		dx := button.mouseX - e.Xpos
 		px := button.Position().X
-		button.SetPositionX(math32.Clamp(px-dx, 0, button.sb.content.Width-button.sb.style.Button.Size))
+		button.SetPositionX(math32.Clamp(px-dx, 0, button.sb.content.Width-button.Size))
 	}
 	button.mouseX = e.Xpos
 	button.mouseY = e.Ypos

Разлика између датотеке није приказан због своје велике величине
+ 442 - 450
gui/scroller.go


+ 5 - 14
gui/slider.go

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

+ 5 - 2
gui/splitter.go

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

+ 20 - 3
gui/style.go

@@ -5,21 +5,25 @@
 package gui
 
 import (
+	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/text"
 )
 
-// All styles
+// Style contains the styles for all GUI elements
 type Style struct {
+	Color         ColorStyle
 	Font          *text.Font
 	FontIcon      *text.Font
+	Label         LabelStyle
 	Button        ButtonStyles
 	CheckRadio    CheckRadioStyles
 	Edit          EditStyles
-	ScrollBar     ScrollBarStyle
+	ScrollBar     ScrollBarStyles
 	Slider        SliderStyles
 	Splitter      SplitterStyles
 	Window        WindowStyles
-	Scroller      ScrollerStyles
+	ItemScroller  ItemScrollerStyles
+	Scroller      ScrollerStyle
 	List          ListStyles
 	DropDown      DropDownStyles
 	Folder        FolderStyles
@@ -31,6 +35,19 @@ type Style struct {
 	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 (
 	StyleOver = iota + 1
 	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
 
-// Sets the default style
+// init sets the default style
 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 {
 
 	return defaultStyle

Разлика између датотеке није приказан због своје велике величине
+ 318 - 794
gui/style_light.go


+ 17 - 37
gui/tabbar.go

@@ -7,7 +7,6 @@ package gui
 import (
 	"fmt"
 
-	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/window"
 )
 
@@ -26,18 +25,13 @@ type TabBar struct {
 }
 
 // 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
 type TabBarStyles struct {
 	SepHeight          float32     // Separator width
 	ListButtonIcon     string      // Icon for list button
-	ListButtonPaddings BorderSizes // Paddings for list button
+	ListButtonPaddings RectBounds  // Paddings for list button
 	Normal             TabBarStyle // Style for normal exhibition
 	Over               TabBarStyle // Style when cursor is over the TabBar
 	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
-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
 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
@@ -91,7 +78,7 @@ func NewTabBar(width, height float32) *TabBar {
 	tb.Add(tb.list)
 
 	// 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.Subscribe(OnMouseDown, tb.onListButton)
 	tb.Add(tb.listButton)
@@ -293,10 +280,7 @@ func (tb *TabBar) onListChange(evname string, ev interface{}) {
 // applyStyle applies the specified TabBar style
 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)
 }
 
@@ -425,7 +409,7 @@ func newTab(text string, tb *TabBar, styles *TabStyles) *Tab {
 	// Setup the header panel
 	tab.header.Initialize(0, 0)
 	tab.label = NewLabel(text)
-	tab.iconClose = NewLabel(styles.IconClose, true)
+	tab.iconClose = NewIcon(styles.IconClose)
 	tab.header.Add(tab.label)
 	tab.header.Add(tab.iconClose)
 	// Creates the bottom panel
@@ -509,7 +493,7 @@ func (tab *Tab) SetIcon(icon string) *Tab {
 	}
 	// Creates or updates icon
 	if tab.icon == nil {
-		tab.icon = NewLabel(icon, true)
+		tab.icon = NewIcon(icon)
 		tab.icon.SetPaddingsFrom(&tab.styles.IconPaddings)
 		tab.header.Add(tab.icon)
 	} else {
@@ -626,11 +610,7 @@ func (tab *Tab) minWidth() float32 {
 // applyStyle applies the specified Tab style to the Tab header
 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
@@ -657,7 +637,7 @@ func (tab *Tab) setBottomPanel() {
 
 	if tab.selected {
 		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.SetPosition(bx, tab.header.Height())
 	}

+ 24 - 60
gui/table.go

@@ -106,55 +106,31 @@ type TableCell struct {
 type TableFormatFunc func(cell TableCell) string
 
 // 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
-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
-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
 type TableResizerStyle struct {
 	Width       float32
-	Border      BorderSizes
+	Border      RectBounds
 	BorderColor math32.Color4
 	BgColor     math32.Color4
 }
 
 // TableStyles describes all styles of the table header and rows
 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
@@ -259,7 +235,7 @@ func NewTable(width, height float32, cols []TableColumn) (*Table, error) {
 		c.resize = cdesc.Resize
 		// Adds optional sort icon
 		if c.sort != TableSortNone {
-			c.ricon = NewLabel(string(tableSortedNoneIcon), true)
+			c.ricon = NewIcon(string(tableSortedNoneIcon))
 			c.Add(c.ricon)
 			c.ricon.Subscribe(OnMouseDown, func(evname string, ev interface{}) {
 				t.onRicon(evname, c)
@@ -818,8 +794,8 @@ func (t *Table) onCursor(evname string, ev interface{}) {
 func (t *Table) onCursorPos(evname string, ev interface{}) {
 
 	// 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 t.resizing {
@@ -1253,7 +1229,7 @@ func (t *Table) recalcHeader() {
 				continue
 			}
 			// 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
 			w := factor * wspace
 			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
-// 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
 func (t *Table) calcMaxFirst() int {
 
@@ -1569,7 +1545,7 @@ func (t *Table) calcMaxFirst() int {
 func (t *Table) updateRowStyle(ri int) {
 
 	row := t.rows[ri]
-	var trs *TableRowStyle
+	var trs TableRowStyle
 	if ri == t.rowCursor {
 		trs = t.styles.RowCursor
 	} else if row.selected {
@@ -1581,24 +1557,18 @@ func (t *Table) updateRowStyle(ri int) {
 			trs = t.styles.RowOdd
 		}
 	}
-	t.applyRowStyle(row, trs)
+	t.applyRowStyle(row, &trs)
 }
 
 // applyHeaderStyle applies style to the specified table header
 // the last header panel does not the right border.
 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
@@ -1606,10 +1576,7 @@ func (t *Table) applyRowStyle(trow *tableRow, trs *TableRowStyle) {
 
 	for i := 0; i < len(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() {
 
 	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

+ 19 - 21
gui/tree.go

@@ -9,31 +9,32 @@ import (
 	"github.com/g3n/engine/window"
 )
 
+// Tree is the tree structure GUI element.
 type Tree struct {
 	List               // Embedded list panel
 	styles *TreeStyles // Pointer to styles
 }
 
+// TreeStyles contains the styling of all tree components for each valid GUI state.
 type TreeStyles struct {
 	List     *ListStyles     // Styles for the embedded list
 	Node     *TreeNodeStyles // Styles for the node panel
 	Padlevel float32         // Left padding indentation
 }
 
+// TreeNodeStyles contains a TreeNodeStyle for each valid GUI state.
 type TreeNodeStyles struct {
 	Normal TreeNodeStyle
 }
 
+// TreeNodeStyle contains the styling of a TreeNode.
 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
 }
 
+// TreeNode is a tree node.
 type TreeNode struct {
 	Panel              // Embedded panel
 	label    Label     // Node label
@@ -44,7 +45,7 @@ type TreeNode struct {
 	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 {
 
 	t := new(Tree)
@@ -53,7 +54,7 @@ func NewTree(width, height float32) *Tree {
 }
 
 // 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) {
 
 	t.List.initialize(true, width, height)
@@ -63,7 +64,7 @@ func (t *Tree) Initialize(width, height float32) {
 	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) {
 
 	t.styles = s
@@ -71,13 +72,13 @@ func (t *Tree) SetStyles(s *TreeStyles) {
 	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) {
 
 	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) {
 
 	t.List.Add(ichild)
@@ -85,7 +86,7 @@ func (t *Tree) Add(ichild IPanel) {
 
 // InsertNodeAt inserts at the specified position a new tree node
 // with the specified text at the end of this tree
-// and returns pointer to the new node
+// and returns pointer to the new node.
 func (t *Tree) InsertNodeAt(pos int, text string) *TreeNode {
 
 	n := newTreeNode(text, t, nil)
@@ -95,8 +96,8 @@ func (t *Tree) InsertNodeAt(pos int, text string) *TreeNode {
 	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 {
 
 	n := newTreeNode(text, t, nil)
@@ -211,7 +212,7 @@ func newTreeNode(text string, tree *Tree, parNode *TreeNode) *TreeNode {
 
 	// Create node icon
 	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)
 
 	// Subscribe to events
@@ -362,17 +363,14 @@ func (n *TreeNode) level() int {
 // applyStyles applies the specified style to this tree node
 func (n *TreeNode) applyStyle(s *TreeNodeStyle) {
 
-	n.SetMarginsFrom(&s.Margins)
-	n.SetBordersColor4(&s.BorderColor)
-	n.SetBordersFrom(&s.Border)
-	n.SetColor4(&s.BgColor)
+	n.Panel.ApplyStyle(&s.PanelStyle)
 	icode := 0
 	if n.expanded {
 		icode = 1
 	}
 	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

+ 7 - 2
gui/util.go

@@ -4,14 +4,17 @@
 
 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
 	Right  float32
 	Bottom 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 {
 		bs.Top = top
@@ -27,6 +30,7 @@ func (bs *BorderSizes) Set(top, right, bottom, left float32) {
 	}
 }
 
+// Rect represents a rectangle.
 type Rect struct {
 	X      float32
 	Y      float32
@@ -34,6 +38,7 @@ type Rect struct {
 	Height float32
 }
 
+// Contains determines whether a 2D point is inside the Rect.
 func (r *Rect) Contains(x, y float32) bool {
 
 	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
 // 	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()
 //
 // 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
 	// the sum of the expand factor for all items.
 	var theight float32
-	var eheight float32
+	//var eheight float32
 	var fheight float32
 	var texpand float32
 	ecount := 0
@@ -157,10 +157,10 @@ func (bl *VBoxLayout) Recalc(ipan IPanel) {
 		// Calculate height of expanded items
 		if params.Expand > 0 {
 			texpand += params.Expand
-			eheight += pan.Height()
-			if pos > 0 {
-				eheight += bl.spacing
-			}
+			//eheight += pan.Height()
+			//if pos > 0 {
+			//	eheight += bl.spacing
+			//}
 			ecount++
 			// Calculate width of fixed items
 		} else {

+ 9 - 5
gui/window.go

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

+ 1 - 0
light/ambient.go

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

+ 1 - 0
light/point.go

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

+ 1 - 0
light/spot.go

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

+ 2 - 3
loader/collada/animation.go

@@ -56,6 +56,7 @@ func (at *AnimationTarget) SetLoop(loop bool) {
 	at.loop = loop
 }
 
+// SetStart sets the initial offset value
 func (at *AnimationTarget) SetStart(v float32) {
 
 	at.start = v
@@ -240,7 +241,7 @@ func actionScaleZ(at *AnimationTarget, v float32) {
 	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
 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
 	case "BSPLINE":
 		return si.linearInterp(inp, idx), true
-	default:
-		return 0, false
 	}
 
 	return 0, false

+ 4 - 2
loader/collada/collada.go

@@ -111,6 +111,7 @@ type Contributor struct {
 	SourceData    string
 }
 
+// Dump prints out information about the Contributor
 func (c *Contributor) Dump(out io.Writer, indent int) {
 
 	fmt.Fprintf(out, "%sContributor:\n", sIndent(indent))
@@ -148,6 +149,7 @@ type Asset struct {
 	UpAxis      string
 }
 
+// Dump prints out information about the Asset
 func (a *Asset) Dump(out io.Writer, indent int) {
 
 	fmt.Fprintf(out, "%sAsset:\n", sIndent(indent))
@@ -165,6 +167,7 @@ type Scene struct {
 	InstanceVisualScene *InstanceVisualScene
 }
 
+// Dump prints out information about the Scene
 func (s *Scene) Dump(out io.Writer, indent int) {
 
 	fmt.Fprintf(out, "%sScene:\n", sIndent(indent))
@@ -181,6 +184,7 @@ type InstanceVisualScene struct {
 	Url  string
 }
 
+// Dump prints out information about the InstanceVisualScene
 func (ivs *InstanceVisualScene) Dump(out io.Writer, indent int) {
 
 	if ivs == nil {
@@ -350,7 +354,6 @@ func (d *Decoder) decAsset(assetStart xml.StartElement, a *Asset) error {
 			continue
 		}
 	}
-	return nil
 }
 
 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
 		}
 	}
-	return nil
 }
 
 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) {
 
 	fmt.Fprintf(out, "%sSource id:%s name:%s\n", sIndent(indent), s.Id, s.Name)
@@ -49,6 +50,7 @@ type NameArray struct {
 	Data  []string
 }
 
+// Dump prints out information about the NameArray
 func (na *NameArray) Dump(out io.Writer, indent int) {
 
 	fmt.Fprintf(out, "%sNameArray id:%s count:%d\n", sIndent(indent), na.Id, na.Count)
@@ -65,6 +67,7 @@ type FloatArray struct {
 	Data  []float32
 }
 
+// Dump prints out information about the FloatArray
 func (fa *FloatArray) Dump(out io.Writer, indent int) {
 
 	fmt.Fprintf(out, "%sFloatArray id:%s count:%d\n", sIndent(indent), fa.Id, fa.Count)
@@ -82,6 +85,7 @@ type Accessor struct {
 	Params []Param
 }
 
+// Dump prints out information about the Accessor
 func (ac *Accessor) Dump(out io.Writer, indent int) {
 
 	fmt.Fprintf(out, "%sAccessor source:%s count:%d stride:%d\n",
@@ -100,6 +104,7 @@ type Param struct {
 	Type string
 }
 
+// Dump prints out information about the Param
 func (p *Param) Dump(out io.Writer, indent int) {
 
 	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
 		}
 	}
-	return source, nil
 }
 
 // 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 {

+ 2 - 3
loader/collada/geometry.go

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

+ 4 - 3
loader/collada/library_animations.go

@@ -20,6 +20,7 @@ type LibraryAnimations struct {
 	Animation []*Animation
 }
 
+// Dump prints out information about the LibraryAnimations
 func (la *LibraryAnimations) Dump(out io.Writer, indent int) {
 
 	if la == nil {
@@ -43,6 +44,7 @@ type Animation struct {
 	Channel   []*Channel
 }
 
+// Dump prints out information about the Animation
 func (an *Animation) Dump(out io.Writer, indent int) {
 
 	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) {
 
 	fmt.Fprintf(out, "%sSampler id:%s\n", sIndent(indent), sp.Id)
@@ -87,6 +90,7 @@ type Channel struct {
 	Target string
 }
 
+// Dump prints out information about the Channel
 func (ch *Channel) Dump(out io.Writer, indent int) {
 
 	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
 		}
 	}
-	return nil
 }
 
 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
 		}
 	}
-	return nil
 }
 
 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
 		}
 	}
-	return nil
 }
 
 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 {
 	Id     string
@@ -21,6 +21,7 @@ type LibraryEffects struct {
 	Effect []*Effect
 }
 
+// Dump prints out information about the LibraryEffects
 func (le *LibraryEffects) Dump(out io.Writer, indent int) {
 
 	if le == nil {
@@ -42,6 +43,7 @@ type Effect struct {
 	Profile []interface{}
 }
 
+// Dump prints out information about the Effect
 func (ef *Effect) Dump(out io.Writer, indent int) {
 
 	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 {
 	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) {
 
 	fmt.Printf("%sProfileCOMMON id:%s\n", sIndent(indent), pc.Id)
@@ -97,6 +100,7 @@ type Newparam struct {
 	ParameterType interface{}
 }
 
+// Dump prints out information about the Newparam
 func (np *Newparam) Dump(out io.Writer, indent int) {
 
 	fmt.Printf("%sNewparam sid:%s\n", sIndent(indent), np.Sid)
@@ -117,6 +121,7 @@ type Surface struct {
 	Init interface{}
 }
 
+// Dump prints out information about the Surface
 func (sf *Surface) Dump(out io.Writer, indent int) {
 
 	fmt.Printf("%sSurface type:%s\n", sIndent(indent), sf.Type)
@@ -134,6 +139,7 @@ type Sampler2D struct {
 	Source string
 }
 
+// Dump prints out information about the Sampler2D
 func (sp *Sampler2D) Dump(out io.Writer, indent int) {
 
 	fmt.Printf("%sSampler2D\n", sIndent(indent))
@@ -157,6 +163,7 @@ type Blinn struct {
 	IndexOfRefraction interface{}
 }
 
+// Dump prints out information about the Blinn
 func (bl *Blinn) Dump(out io.Writer, indent int) {
 
 	fmt.Fprintf(out, "%sBlinn\n", sIndent(indent))
@@ -215,6 +222,7 @@ type Phong struct {
 	IndexOfRefraction interface{}
 }
 
+// Dump prints out information about the Phong
 func (ph *Phong) Dump(out io.Writer, indent int) {
 
 	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)
 }
 
+// DumpColorOrTexture prints out information about the Color or Texture
 func DumpColorOrTexture(name string, v interface{}, out io.Writer, indent int) {
 
 	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) {
 
 	if v == nil {
@@ -268,6 +278,7 @@ type Color struct {
 	Data [4]float32
 }
 
+// Dump prints out information about the Color
 func (c *Color) Dump(out io.Writer, indent int) {
 
 	fmt.Fprintf(out, "%sColor sid:%s data:%v\n", sIndent(indent), c.Sid, c.Data)
@@ -281,6 +292,7 @@ type Float struct {
 	Data float32
 }
 
+// Dump prints out information about the Float
 func (f *Float) Dump(out io.Writer, indent int) {
 
 	fmt.Fprintf(out, "%sFloat sid:%s data:%v\n", sIndent(indent), f.Sid, f.Data)
@@ -294,6 +306,7 @@ type Texture struct {
 	Texcoord string
 }
 
+// Dump prints out information about the Texture
 func (t *Texture) Dump(out io.Writer, indent int) {
 
 	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
 		}
 	}
-	return nil
 }
 
 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
 		}
 	}
-	return nil
 }
 
 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
 		}
 	}
-	return nil
 }
 
 func (d *Decoder) decProfileCommonNewparam(start xml.StartElement, pc *ProfileCOMMON) error {
@@ -404,7 +414,6 @@ func (d *Decoder) decProfileCommonNewparam(start xml.StartElement, pc *ProfileCO
 			continue
 		}
 	}
-	return nil
 }
 
 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
 		}
 	}
-	return nil
 }
 
 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
 		}
 	}
-	return nil
 }
 
 func (d *Decoder) decProfileCommonTechnique(start xml.StartElement, pc *ProfileCOMMON) error {
@@ -477,7 +484,6 @@ func (d *Decoder) decProfileCommonTechnique(start xml.StartElement, pc *ProfileC
 			continue
 		}
 	}
-	return nil
 }
 
 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
 		}
 	}
-	return nil
 }
 
 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
 		}
 	}
-	return nil
 }
 
 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 {
 	Asset    *Asset
 	Geometry []*Geometry
 }
 
+// Dump prints out information about the LibraryGeometries
 func (lg *LibraryGeometries) Dump(out io.Writer, indent int) {
 
 	if lg == nil {
@@ -43,6 +44,7 @@ type Geometry struct {
 	GeometricElement interface{} // Geometry type object (Mesh|others)
 }
 
+// Dump prints out information about the Geometry
 func (g *Geometry) Dump(out io.Writer, indent int) {
 
 	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)
 }
 
+// Dump prints out information about the Mesh
 func (m *Mesh) Dump(out io.Writer, indent int) {
 
 	fmt.Fprintf(out, "%sMesh:\n", sIndent(indent))
@@ -91,6 +94,7 @@ type Vertices struct {
 	Input []Input
 }
 
+// Dump prints out information about the Vertices
 func (v *Vertices) Dump(out io.Writer, indent int) {
 
 	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
 }
 
+// Dump prints out information about the Input
 func (i *Input) Dump(out io.Writer, indent int) {
 
 	fmt.Fprintf(out, "%sInput semantic:%s source:%s\n", sIndent(indent), i.Semantic, i.Source)
@@ -124,6 +129,7 @@ type Polylist struct {
 	P        []int
 }
 
+// Dump prints out information about the Polylist
 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)
@@ -167,6 +173,7 @@ type Lines struct {
 	P        []int
 }
 
+// Dump prints out information about the Lines
 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)
@@ -210,6 +217,7 @@ type Tristrips struct {
 	P        []int
 }
 
+// Dump prints out information about the Tristrips
 func (is *InputShared) Dump(out io.Writer, indent int) {
 
 	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
 		}
 	}
-	return nil
 }
 
 // decGeometry receives the start element of a geometry and
@@ -277,7 +284,6 @@ func (d *Decoder) decGeometry(start xml.StartElement, lg *LibraryGeometries) err
 			continue
 		}
 	}
-	return nil
 }
 
 // decMesh decodes the mesh from the specified geometry
@@ -329,7 +335,6 @@ func (d *Decoder) decMesh(start xml.StartElement, geom *Geometry) error {
 			continue
 		}
 	}
-	return nil
 }
 
 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)
 		}
 	}
-	return nil
 }
 
 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
 		}
 	}
-	return nil
 }
 
 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
 		}
 	}
-	return nil
 }
 
 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 {
 	Id    string
@@ -20,6 +20,7 @@ type LibraryImages struct {
 	Image []*Image
 }
 
+// Dump prints out information about the LibraryImages
 func (li *LibraryImages) Dump(out io.Writer, indent int) {
 
 	if li == nil {
@@ -44,6 +45,7 @@ type Image struct {
 	ImageSource interface{}
 }
 
+// Dump prints out information about the Image
 func (img *Image) Dump(out io.Writer, indent int) {
 
 	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 {
 	Uri string
 }
 
+// Dump prints out information about the InitFrom
 func (initf *InitFrom) Dump(out io.Writer, indent int) {
 
 	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
 		}
 	}
-	return nil
 }
 
 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
 		}
 	}
-	return nil
 }
 
 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 {
 	Id    string
@@ -21,6 +21,7 @@ type LibraryLights struct {
 	Light []*Light
 }
 
+// Dump prints out information about the LibraryLights
 func (ll *LibraryLights) Dump(out io.Writer, indent int) {
 
 	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) {
 
 	fmt.Fprintf(out, "%sLights id:%s name:%s\n", sIndent(indent), li.Id, li.Name)
@@ -68,6 +70,7 @@ type Ambient struct {
 	Color LightColor
 }
 
+// Dump prints out information about the Ambient
 func (amb *Ambient) Dump(out io.Writer, indent int) {
 
 	fmt.Fprintf(out, "%sAmbient\n", sIndent(indent))
@@ -82,6 +85,7 @@ type Directional struct {
 	Color LightColor
 }
 
+// Dump prints out information about the Directional
 func (dir *Directional) Dump(out io.Writer, indent int) {
 
 	fmt.Fprintf(out, "%sDirectional\n", sIndent(indent))
@@ -99,6 +103,7 @@ type Point struct {
 	QuadraticAttenuation *FloatValue
 }
 
+// Dump prints out information about the Point
 func (pl *Point) Dump(out io.Writer, indent int) {
 
 	fmt.Fprintf(out, "%sPoint\n", sIndent(indent))
@@ -121,6 +126,7 @@ type Spot struct {
 	FalloffExponent      *FloatValue
 }
 
+// Dump prints out information about the Spot
 func (sl *Spot) Dump(out io.Writer, indent int) {
 
 	fmt.Fprintf(out, "%sSpot\n", sIndent(indent))
@@ -141,6 +147,7 @@ type FloatValue struct {
 	Value float32
 }
 
+// Dump prints out information about the FloatValue
 func (fv *FloatValue) Dump(name string, out io.Writer, indent int) {
 
 	if fv == nil {
@@ -157,6 +164,7 @@ type LightColor struct {
 	Data [3]float32
 }
 
+// Dump prints out information about the LightColor
 func (lc *LightColor) Dump(out io.Writer, indent int) {
 
 	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
 		}
 	}
-	return nil
 }
 
 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
 		}
 	}
-	return nil
 }
 
 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
 		}
 	}
-	return nil
 }
 
 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
 		}
 	}
-	return nil
 }
 
 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 {
 	Id       string
@@ -20,6 +20,7 @@ type LibraryMaterials struct {
 	Material []*Material
 }
 
+// Dump prints out information about the LibraryMaterials
 func (lm *LibraryMaterials) Dump(out io.Writer, indent int) {
 
 	if lm == nil {
@@ -41,6 +42,7 @@ type Material struct {
 	InstanceEffect InstanceEffect
 }
 
+// Dump prints out information about the Material
 func (mat *Material) Dump(out io.Writer, indent int) {
 
 	fmt.Fprintf(out, "%sMaterial id:%s name:%s\n", sIndent(indent), mat.Id, mat.Name)
@@ -57,6 +59,7 @@ type InstanceEffect struct {
 	Url  string
 }
 
+// Dump prints out information about the InstanceEffect
 func (ie *InstanceEffect) Dump(out io.Writer, indent int) {
 
 	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
 		}
 	}
-	return nil
 }
 
 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
 		}
 	}
-	return nil
 }
 
 func (d *Decoder) decInstanceEffect(start xml.StartElement, ie *InstanceEffect) error {
@@ -131,5 +132,4 @@ func (d *Decoder) decInstanceEffect(start xml.StartElement, ie *InstanceEffect)
 			continue
 		}
 	}
-	return nil
 }

+ 14 - 10
loader/collada/library_visual_scenes.go

@@ -12,13 +12,14 @@ import (
 )
 
 //
-// Library Visual Scenes
+// LibraryVisualScenes
 //
 type LibraryVisualScenes struct {
 	Asset       *Asset
 	VisualScene []*VisualScene
 }
 
+// Dump prints out information about the LibraryVisualScenes
 func (lv *LibraryVisualScenes) Dump(out io.Writer, indent int) {
 
 	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 {
 	Id   string
@@ -43,6 +44,7 @@ type VisualScene struct {
 	Node []*Node // Array of nodes
 }
 
+// Dump prints out information about the VisualScene
 func (vs *VisualScene) Dump(out io.Writer, indent int) {
 
 	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 {
 	Id                     string
@@ -65,6 +67,7 @@ type Node struct {
 	Node                   []*Node // Array of children nodes
 }
 
+// Dump prints out information about the Node
 func (n *Node) Dump(out io.Writer, indent int) {
 
 	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
 }
 
+// Dump prints out information about the Matrix
 func (m *Matrix) Dump(out io.Writer, indent int) {
 
 	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
 }
 
+// Dump prints out information about the Rotate
 func (r *Rotate) Dump(out io.Writer, indent int) {
 
 	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
 }
 
+// Dump prints out information about the Translate
 func (t *Translate) Dump(out io.Writer, indent int) {
 
 	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
 }
 
+// Dump prints out information about the Scale
 func (s *Scale) Dump(out io.Writer, indent int) {
 
 	fmt.Fprintf(out, "%sScale sid:%s data:%v\n", sIndent(indent), s.Sid, s.Data)
@@ -154,6 +161,7 @@ type InstanceGeometry struct {
 	BindMaterial *BindMaterial
 }
 
+// Dump prints out information about the InstanceGeometry
 func (ig *InstanceGeometry) Dump(out io.Writer, indent int) {
 
 	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) {
 
 	fmt.Fprintf(out, "%sBindMaterial\n", sIndent(indent))
@@ -195,6 +204,7 @@ type InstanceMaterial struct {
 	BindVertexInput []BindVertexInput
 }
 
+// Dump prints out information about the InstanceMaterial
 func (im *InstanceMaterial) Dump(out io.Writer, indent int) {
 
 	fmt.Fprintf(out, "%sInstanceMaterial sid:%s name:%s target:%s symbol:%s\n",
@@ -222,6 +232,7 @@ type BindVertexInput struct {
 	InputSet      uint
 }
 
+// Dump prints out information about the BindVertexInput
 func (bvi *BindVertexInput) Dump(out io.Writer, indent int) {
 
 	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
 		}
 	}
-	return nil
 }
 
 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 {
@@ -339,7 +348,6 @@ func (d *Decoder) decNode(nodeStart xml.StartElement, parent *[]*Node) error {
 			continue
 		}
 	}
-	return nil
 }
 
 func (d *Decoder) decMatrix(cdata []byte, n *Node) error {
@@ -414,7 +422,6 @@ func (d *Decoder) decInstanceGeometry(start xml.StartElement, n *Node) error {
 			continue
 		}
 	}
-	return nil
 }
 
 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
 		}
 	}
-	return nil
 }
 
 func (d *Decoder) decBindMaterialTechniqueCommon(start xml.StartElement, bm *BindMaterial) error {
@@ -453,7 +459,6 @@ func (d *Decoder) decBindMaterialTechniqueCommon(start xml.StartElement, bm *Bin
 			continue
 		}
 	}
-	return nil
 }
 
 func (d *Decoder) decInstanceMaterial(start xml.StartElement, bm *BindMaterial) error {
@@ -483,5 +488,4 @@ func (d *Decoder) decInstanceMaterial(start xml.StartElement, bm *BindMaterial)
 			continue
 		}
 	}
-	return nil
 }

+ 0 - 10
loader/collada/material.go

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

+ 1 - 0
loader/collada/scene.go

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

+ 2 - 2
material/basic.go

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

+ 18 - 2
material/material.go

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

+ 113 - 86
math32/box2.go

@@ -4,73 +4,91 @@
 
 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 {
 	min 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 {
 
-	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 {
-		this.min = *min
+		b.min = *min
 	} else {
-		this.min.Set(Infinity, Infinity)
+		b.min.Set(Infinity, Infinity)
 	}
 	if max != nil {
-		this.max = *max
+		b.max = *max
 	} 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++ {
-		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
 	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
 	if optionalTarget == nil {
@@ -78,10 +96,14 @@ func (this *Box2) Center(optionalTarget *Vector2) *Vector2 {
 	} else {
 		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
 	if optionalTarget == nil {
@@ -89,74 +111,71 @@ func (this *Box2) Size(optionalTarget *Vector2) *Vector2 {
 	} else {
 		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 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 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.
-	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 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
 	if optionalTarget == nil {
@@ -164,38 +183,46 @@ func (this *Box2) ClampPoint(point *Vector2, optionalTarget *Vector2) *Vector2 {
 	} else {
 		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)
-	clampedPoint := v1.Copy(point).Clamp(&this.min, &this.max)
+	clampedPoint := v1.Copy(point).Clamp(&b.min, &b.max)
 	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
 
+// Box3 represents a 3D bounding box defined by two points:
+// the point with minimum coordinates and the point with maximum coordinates.
 type Box3 struct {
 	Min 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 {
 
-	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 {
-		this.Min = *min
+		b.Min = *min
 	} else {
-		this.Min.Set(Infinity, Infinity, Infinity)
+		b.Min.Set(Infinity, Infinity, Infinity)
 	}
 	if max != nil {
-		this.Max = *max
+		b.Max = *max
 	} 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++ {
-		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)
 	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
 	if optionalTarget == nil {
@@ -87,10 +98,14 @@ func (this *Box3) Center(optionalTarget *Vector3) *Vector3 {
 	} else {
 		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
 	if optionalTarget == nil {
@@ -98,80 +113,74 @@ func (this *Box3) Size(optionalTarget *Vector3) *Vector3 {
 	} else {
 		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 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 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.
-	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 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
 	if optionalTarget == nil {
@@ -179,17 +188,20 @@ func (this *Box3) ClampPoint(point *Vector3, optionalTarget *Vector3) *Vector3 {
 	} else {
 		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
-	clampedPoint := v1.Copy(point).Clamp(&this.Min, &this.Max)
+	clampedPoint := v1.Copy(point).Clamp(&b.Min, &b.Max)
 	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 result *Sphere
@@ -199,27 +211,33 @@ func (this *Box3) GetBoundingSphere(optionalTarget *Sphere) *Sphere {
 		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
 }
 
-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{
 		Vector3{},
@@ -232,34 +250,38 @@ func (this *Box3) ApplyMatrix4(matrix *Matrix4) *Box3 {
 		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
 // the standard web color's RGB component values
 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
 
+// Frustum represents a frustum
 type Frustum struct {
 	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 {
 
-	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 {
-		this.planes[0] = *p0
+		f.planes[0] = *p0
 	}
 	if p1 != nil {
-		this.planes[1] = *p1
+		f.planes[1] = *p1
 	}
 	if p2 != nil {
-		this.planes[2] = *p2
+		f.planes[2] = *p2
 	}
 	if p3 != nil {
-		this.planes[3] = *p3
+		f.planes[3] = *p3
 	}
 	if p4 != nil {
-		this.planes[4] = *p4
+		f.planes[4] = *p4
 	}
 	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++ {
-		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]
 	me1 := m[1]
 	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[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
 
 	for i := 0; i < 6; i++ {
@@ -118,13 +114,14 @@ func (this *Frustum) IntersectsSphere(sphere *Sphere) bool {
 	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 p2 Vector3
 
 	for i := 0; i < 6; i++ {
-		plane := &this.planes[i]
+		plane := &f.planes[i]
 		if plane.normal.X > 0 {
 			p1.X = box.Min.X
 		} else {
@@ -136,9 +133,9 @@ func (this *Frustum) IntersectsBox(box *Box3) bool {
 			p2.X = box.Min.X
 		}
 		if plane.normal.Y > 0 {
-			p2.Y = box.Min.Y
+			p1.Y = box.Min.Y
 		} else {
-			p2.Y = box.Max.Y
+			p1.Y = box.Max.Y
 		}
 		if plane.normal.Y > 0 {
 			p2.Y = box.Max.Y
@@ -151,7 +148,7 @@ func (this *Frustum) IntersectsBox(box *Box3) bool {
 			p1.Z = box.Max.Z
 		}
 		if plane.normal.Z > 0 {
-			p1.Z = box.Max.Z
+			p2.Z = box.Max.Z
 		} else {
 			p2.Z = box.Min.Z
 		}
@@ -169,17 +166,19 @@ func (this *Frustum) IntersectsBox(box *Box3) bool {
 	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++ {
-		if this.planes[i].DistanceToPoint(point) < 0 {
+		if f.planes[i].DistanceToPoint(point) < 0 {
 			return false
 		}
 	}
 	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
 
+// Line3 represents a 3D line segment defined by a start and an end point.
 type Line3 struct {
 	start 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 {
 
-	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 {
-		this.start = *start
+		l.start = *start
 	}
 	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
 	if optionalTarget == nil {
@@ -42,10 +50,12 @@ func (this *Line3) Center(optionalTarget *Vector3) *Vector3 {
 	} else {
 		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
 	if optionalTarget == nil {
@@ -53,76 +63,38 @@ func (this *Line3) Delta(optionalTarget *Vector3) *Vector3 {
 	} else {
 		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))
 
+// DegToRad converts a number from degrees to radians
 func DegToRad(degrees float32) float32 {
 
 	return degrees * degreeToRadiansFactor
 }
 
+// RadToDeg converts a number from radians to degrees
 func RadToDeg(radians float32) float32 {
 
 	return radians * radianToDegreesFactor
 }
 
+// Clamp clamps x to the provided closed interval [a, b]
 func Clamp(x, a, b float32) float32 {
 
-	// Clamp value to range <a, b>
 	if x < a {
 		return a
 	}
@@ -40,6 +42,7 @@ func Clamp(x, a, b float32) float32 {
 	return x
 }
 
+// ClampInt clamps x to the provided closed interval [a, b]
 func ClampInt(x, a, b int) int {
 
 	if x < a {
@@ -51,15 +54,6 @@ func ClampInt(x, a, b int) int {
 	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 {
 	return float32(math.Abs(float64(v)))
 }

+ 81 - 100
math32/plane.go

@@ -6,114 +6,117 @@ package math32
 
 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 {
 	normal   Vector3
 	constant float32
 }
 
+// NewPlane creates and returns a new plane from a normal vector and a constant.
 func NewPlane(normal *Vector3, constant float32) *Plane {
 
-	this := new(Plane)
+	p := new(Plane)
 	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 v2 Vector3
 
 	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)?
-	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)
-
 }
 
-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 result *Vector3
@@ -124,24 +127,26 @@ func (this *Plane) IntersectLine(line *Line3, optionalTarget *Vector3) *Vector3
 	}
 
 	direction := line.Delta(&v1)
-	denominator := this.normal.Dot(direction)
+	denominator := p.normal.Dot(direction)
 	if denominator == 0 {
 		// line is coplanar, return origin
-		if this.DistanceToPoint(&line.start) == 0 {
+		if p.DistanceToPoint(&line.start) == 0 {
 			return result.Copy(&line.start)
 		}
 		// Unsure if this is the correct method to handle this case.
 		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 {
 		return nil
 	}
 	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
 	if optionalTarget == nil {
@@ -149,49 +154,25 @@ func (this *Plane) CoplanarPoint(optionalTarget *Vector3) *Vector3 {
 	} else {
 		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)
 }

+ 0 - 0
math32/quaternion.go


Неке датотеке нису приказане због велике количине промена