Add cube, cylinder, and sphere mesh generators

Contributed by Brandon Jones

Signed-off-by: John McCutchan <john@johnmccutchan.com>
diff --git a/AUTHORS.txt b/AUTHORS.txt
index 3a1c1e7..5fe18b0 100644
--- a/AUTHORS.txt
+++ b/AUTHORS.txt
@@ -4,5 +4,6 @@
 
 Contributors:
 
-Adam Singer <financeCoding@gmail.com>
+Brandon Jones <bajones@google.com>
 Don Olmstead <don.j.olmstead@gmail.com>
+Adam Singer <financeCoding@gmail.com>
diff --git a/ChangeLog.txt b/ChangeLog.txt
index 38c0e70..90eef2a 100644
--- a/ChangeLog.txt
+++ b/ChangeLog.txt
@@ -1,3 +1,7 @@
+v 1.4.0 - November 2013
+
+- Add basic mesh generators (contributed by Brandon Jones)
+
 v 1.3.5 - July 2013
 
 - Class names now start with upper case, following Dart style guide.
@@ -59,7 +63,7 @@
 v 0.8.5 - July 29 2012
 
 - 33% faster matrix matrix multiply
-- Fix generated operator[]= 
+- Fix generated operator[]=
 - Fix OpenGL lookat and perspective matrix constructors
 - Fix mat4x4 rotation constructors
 - Fix mat4x4 multiplied with vector3 not applying translation
@@ -85,7 +89,7 @@
 - Fixed adjoint matrix code generation
 - Added selfAdd, selfSub, selfScale and selfNegate to matrix classes
 - Added serialization support for Float32Array and Vectors/Matrices
-  
-v 0.0.0 - March 20 2012 
+
+v 0.0.0 - March 20 2012
 
 - Initial release
diff --git a/bin/mesh_generator.dart b/bin/mesh_generator.dart
new file mode 100644
index 0000000..4199184
--- /dev/null
+++ b/bin/mesh_generator.dart
@@ -0,0 +1,94 @@
+/*
+  Copyright (C) 2013 John McCutchan <john@johnmccutchan.com>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+*/
+
+library vector_math_mesh_generator;
+
+import 'dart:convert';
+import 'package:vector_math/vector_math_geometry.dart';
+
+MeshGeometry generateCube(List<String> args) {
+  if (args.length != 3) {
+    return null;
+  }
+  num width = double.parse(args[0]);
+  num height = double.parse(args[1]);
+  num depth = double.parse(args[2]);
+  var generator = new CubeGenerator();
+  MeshGeometry geometry = generator.createCube(width, height, depth);
+  return geometry;
+}
+
+MeshGeometry generateSphere(List<String> args) {
+  if (args.length != 1) {
+    return null;
+  }
+  num radius = double.parse(args[0]);
+  var generator = new SphereGenerator();
+  MeshGeometry geometry = generator.createSphere(radius);
+  return geometry;
+}
+
+MeshGeometry generateCylinder(List<String> args) {
+  if (args.length != 3) {
+    return null;
+  }
+  num topRadius = double.parse(args[0]);
+  num bottomRadius = double.parse(args[1]);
+  num height = double.parse(args[2]);
+  var generator = new CylinderGenerator();
+  MeshGeometry geometry = generator.createCylinder(topRadius, bottomRadius,
+                                                   height);
+  return geometry;
+}
+
+Map<String, Function> generators = {
+  'cube': generateCube,
+  'sphere': generateSphere,
+  'cylinder': generateCylinder
+};
+
+
+main(List<String> args_) {
+  List<String> args = new List.from(args_, growable: true);
+
+  if (args.length == 0) {
+    print('mesh_generator.dart <type> [<arg0> ... <argN>]');
+    print('');
+    print('<type> = cube, sphere, cylinder');
+    print('mesh_generator.dart cube width height depth');
+    print('mesh_generator.dart sphere radius');
+    print('mesh_generator.dart cylinder topRadius bottomRadius height');
+    print('');
+    return;
+  }
+  var type = args.removeAt(0);
+  var generator = generators[type];
+  if (generator == null) {
+    print('Could not find generator for $type');
+    return;
+  }
+  MeshGeometry geometry = generator(args);
+  if (geometry == null) {
+    print('Error generating geometry for $type');
+    return;
+  }
+  print(JSON.encode(geometry));
+}
\ No newline at end of file
diff --git a/lib/src/vector_math_geometry/attribute_generators.dart b/lib/src/vector_math_geometry/attribute_generators.dart
index 589a7c6..58f59fa 100644
--- a/lib/src/vector_math_geometry/attribute_generators.dart
+++ b/lib/src/vector_math_geometry/attribute_generators.dart
@@ -27,9 +27,9 @@
 void generateNormals(Vector3List normals, Vector3List positions,
                      Uint16List indices) {
   Vector3 p0 = new Vector3.zero(),
-      p1 = new Vector3.zero(),
-      p2 = new Vector3.zero(),
-      norm = new Vector3.zero();
+          p1 = new Vector3.zero(),
+          p2 = new Vector3.zero(),
+          norm = new Vector3.zero();
 
   // Loop through every polygon, find it's normal, and add that to the vertex
   // normals.
@@ -148,4 +148,5 @@
     tangents.load(i, tan);
     tangents[i] = tan.setValues(p0.x, p0.y, p0.z, sign);
   }
-}
\ No newline at end of file
+}
+
diff --git a/lib/src/vector_math_geometry/cube_generator.dart b/lib/src/vector_math_geometry/cube_generator.dart
new file mode 100644
index 0000000..999c455
--- /dev/null
+++ b/lib/src/vector_math_geometry/cube_generator.dart
@@ -0,0 +1,128 @@
+/*
+  Copyright (C) 2013 John McCutchan <john@johnmccutchan.com>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+*/
+
+part of vector_math_geometry;
+
+class CubeGenerator extends GeometryGenerator {
+  double _width;
+  double _height;
+  double _depth;
+
+  int get vertexCount => 24;
+  int get indexCount => 36;
+
+  MeshGeometry createCube(num width, num height, num depth,
+                          {flags: null}) {
+    _width = width.toDouble();
+    _height = height.toDouble();
+    _depth = depth.toDouble();
+
+    return _createGeometry(flags);
+  }
+
+  void _generateIndices(Uint16List indices) {
+    indices.setAll(0, [
+      0, 1, 2, 0, 2, 3,
+      4, 5, 6, 4, 6, 7,
+      8, 9, 10, 8, 10, 11,
+      12, 13, 14, 12, 14, 15,
+      16, 17, 18, 16, 18, 19,
+      20, 21, 22, 20, 22, 23
+    ]);
+  }
+
+  void _generatePositions(Vector3List positions, Uint16List indices) {
+    // Front
+    positions[0] = new Vector3(_width, _height, _depth);
+    positions[1] = new Vector3(-_width, _height, _depth);
+    positions[2] = new Vector3(-_width, -_height, _depth);
+    positions[3] = new Vector3(_width, -_height, _depth);
+
+    // Back
+    positions[4] = new Vector3(_width, -_height, -_depth);
+    positions[5] = new Vector3(-_width, -_height, -_depth);
+    positions[6] = new Vector3(-_width, _height, -_depth);
+    positions[7] = new Vector3(_width, _height, -_depth);
+
+    // Right
+    positions[8] = new Vector3(_width, -_height, _depth);
+    positions[9] = new Vector3(_width, -_height, -_depth);
+    positions[10] = new Vector3(_width, _height, -_depth);
+    positions[11] = new Vector3(_width, _height, _depth);
+
+    // Left
+    positions[12] = new Vector3(-_width, _height, _depth);
+    positions[13] = new Vector3(-_width, _height, -_depth);
+    positions[14] = new Vector3(-_width, -_height, -_depth);
+    positions[15] = new Vector3(-_width, -_height, _depth);
+
+    // Top
+    positions[16] = new Vector3(_width, _height, _depth);
+    positions[17] = new Vector3(_width, _height, -_depth);
+    positions[18] = new Vector3(-_width, _height, -_depth);
+    positions[19] = new Vector3(-_width, _height, _depth);
+
+    // Bottom
+    positions[20] = new Vector3(-_width, -_height, _depth);
+    positions[21] = new Vector3(-_width, -_height, -_depth);
+    positions[22] = new Vector3(_width, -_height, -_depth);
+    positions[23] = new Vector3(_width, -_height, _depth);
+  }
+
+  void _generateTexCoords(Vector2List texCoords, Vector3List positions,
+                          Uint16List indices) {
+    // Front
+    texCoords[0] = new Vector2(1.0, 0.0);
+    texCoords[1] = new Vector2(0.0, 0.0);
+    texCoords[2] = new Vector2(0.0, 1.0);
+    texCoords[3] = new Vector2(1.0, 1.0);
+
+    // Back
+    texCoords[4] = new Vector2(0.0, 1.0);
+    texCoords[5] = new Vector2(1.0, 1.0);
+    texCoords[6] = new Vector2(1.0, 0.0);
+    texCoords[7] = new Vector2(0.0, 0.0);
+
+    // Right
+    texCoords[8] = new Vector2(0.0, 1.0);
+    texCoords[9] = new Vector2(1.0, 1.0);
+    texCoords[10] = new Vector2(1.0, 0.0);
+    texCoords[11] = new Vector2(0.0, 0.0);
+
+    // Left
+    texCoords[12] = new Vector2(1.0, 0.0);
+    texCoords[13] = new Vector2(0.0, 0.0);
+    texCoords[14] = new Vector2(0.0, 1.0);
+    texCoords[15] = new Vector2(1.0, 1.0);
+
+    // Top
+    texCoords[16] = new Vector2(1.0, 1.0);
+    texCoords[17] = new Vector2(1.0, 0.0);
+    texCoords[18] = new Vector2(0.0, 0.0);
+    texCoords[19] = new Vector2(0.0, 1.0);
+
+    // Bottom
+    texCoords[20] = new Vector2(0.0, 0.0);
+    texCoords[21] = new Vector2(0.0, 1.0);
+    texCoords[22] = new Vector2(1.0, 1.0);
+    texCoords[23] = new Vector2(1.0, 0.0);
+  }
+}
\ No newline at end of file
diff --git a/lib/src/vector_math_geometry/cylinder_generator.dart b/lib/src/vector_math_geometry/cylinder_generator.dart
new file mode 100644
index 0000000..f06c81f
--- /dev/null
+++ b/lib/src/vector_math_geometry/cylinder_generator.dart
@@ -0,0 +1,158 @@
+/*
+  Copyright (C) 2013 John McCutchan <john@johnmccutchan.com>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+*/
+
+part of vector_math_geometry;
+
+class CylinderGenerator extends GeometryGenerator {
+  double _topRadius;
+  double _bottomRadius;
+  double _height;
+  int _segments;
+
+  int get vertexCount => ((_segments + 1) * 2) + (_segments * 2);
+  int get indexCount => (_segments * 6) + ((_segments - 2) * 6);
+
+  MeshGeometry createCylinder(num topRadius, num bottomRadius,
+                              num height, {int segments: 16, flags: null}) {
+    _topRadius = topRadius.toDouble();
+    _bottomRadius = bottomRadius.toDouble();
+    _height = height.toDouble();
+    _segments = segments;
+
+    return _createGeometry(flags);
+  }
+
+  void _generateIndices(Uint16List indices) {
+    int i = 0;
+
+    // Sides
+    int base1 = 0;
+    int base2 = _segments + 1;
+    for(int x = 0; x < _segments; ++x) {
+      indices[i++] = base1 + x;
+      indices[i++] = base1 + x + 1;
+      indices[i++] = base2 + x;
+
+      indices[i++] = base1 + x + 1;
+      indices[i++] = base2 + x + 1;
+      indices[i++] = base2 + x;
+    }
+
+    // Top cap
+    base1 = (_segments + 1) * 2;
+    for(int x = 1; x < _segments - 1; ++x) {
+      indices[i++] = base1;
+      indices[i++] = base1 + x + 1;
+      indices[i++] = base1 + x;
+    }
+
+    // Bottom cap
+    base1 = (_segments + 1) * 2 + _segments;
+    for(int x = 1; x < _segments - 1; ++x) {
+      indices[i++] = base1;
+      indices[i++] = base1 + x;
+      indices[i++] = base1 + x + 1;
+    }
+  }
+
+  void _generatePositions(Vector3List positions, Uint16List indices) {
+    int i = 0;
+
+    // Top
+    for (int x = 0; x <= _segments; ++x) {
+      double u = x / _segments;
+
+      positions[i++] = new Vector3(
+          _topRadius * Math.cos(u * Math.PI * 2.0),
+          _height * 0.5,
+          _topRadius * Math.sin(u * Math.PI * 2.0)
+      );
+    }
+
+    // Bottom
+    for (int x = 0; x <= _segments; ++x) {
+      double u = x / _segments;
+
+      positions[i++] = new Vector3(
+          _bottomRadius * Math.cos(u * Math.PI * 2.0),
+          _height * -0.5,
+          _bottomRadius * Math.sin(u * Math.PI * 2.0)
+      );
+    }
+
+    // Top cap
+    for (int x = 0; x < _segments; ++x) {
+      double u = x / _segments;
+
+      positions[i++] = new Vector3(
+          _topRadius * Math.cos(u * Math.PI * 2.0),
+          _height * 0.5,
+          _topRadius * Math.sin(u * Math.PI * 2.0)
+      );
+    }
+
+    // Bottom cap
+    for (int x = 0; x < _segments; ++x) {
+      double u = x / _segments;
+
+      positions[i++] = new Vector3(
+          _bottomRadius * Math.cos(u * Math.PI * 2.0),
+          _height * -0.5,
+          _bottomRadius * Math.sin(u * Math.PI * 2.0)
+      );
+    }
+  }
+
+  void _generateTexCoords(Vector2List texCoords, Vector3List positions,
+                          Uint16List indices) {
+    int i = 0;
+
+    // Cylinder top
+    for (int x = 0; x <= _segments; ++x) {
+      double u = 1.0 - (x / _segments);
+      texCoords[i++] = new Vector2(u, 0.0);
+    }
+
+    // Cylinder bottom
+    for (int x = 0; x <= _segments; ++x) {
+      double u = 1.0 - (x / _segments);
+      texCoords[i++] = new Vector2(u, 1.0);
+    }
+
+    // Top cap
+    for (int x = 0; x < _segments; ++x) {
+      double r = (x / _segments) * Math.PI * 2.0;
+      texCoords[i++] = new Vector2(
+          (Math.cos(r) * 0.5 + 0.5),
+          (Math.sin(r) * 0.5 + 0.5)
+      );
+    }
+
+    // Bottom cap
+    for (int x = 0; x < _segments; ++x) {
+      double r = (x / _segments) * Math.PI * 2.0;
+      texCoords[i++] = new Vector2(
+          (Math.cos(r) * 0.5 + 0.5),
+          (Math.sin(r) * 0.5 + 0.5)
+      );
+    }
+  }
+}
\ No newline at end of file
diff --git a/lib/src/vector_math_geometry/geometry_generator.dart b/lib/src/vector_math_geometry/geometry_generator.dart
new file mode 100644
index 0000000..9dd3f46
--- /dev/null
+++ b/lib/src/vector_math_geometry/geometry_generator.dart
@@ -0,0 +1,153 @@
+/*
+  Copyright (C) 2013 John McCutchan <john@johnmccutchan.com>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+*/
+
+part of vector_math_geometry;
+
+class GeometryGeneratorFlags {
+  final bool texCoords;
+  final bool normals;
+  final bool tangents;
+  final bool invertFaces;
+
+  GeometryGeneratorFlags({this.texCoords: true,
+                          this.normals: true,
+                          this.tangents: true,
+                          this.invertFaces: false});
+}
+
+abstract class GeometryGenerator {
+  int get vertexCount;
+  int get indexCount;
+
+  MeshGeometry _createGeometry(GeometryGeneratorFlags flags) {
+    MeshGeometry mesh = new MeshGeometry();
+
+    if (flags == null)
+      flags = new GeometryGeneratorFlags();
+
+    int stride = 12; // Position
+    if (flags.texCoords || flags.tangents)
+      stride += 8;
+    if (flags.normals)
+      stride += 12;
+    if (flags.tangents)
+      stride += 16;
+
+    VertexAttrib positionAttrib;
+    VertexAttrib texCoordAttrib;
+    VertexAttrib normalAttrib;
+    VertexAttrib tangentAttrib;;
+
+    Vector2List texCoordView;
+    Vector3List positionView;
+    Vector3List normalView;
+    Vector4List tangentView;
+
+    mesh.buffer = new Float32List(vertexCount * (stride~/4));
+    mesh.indices = new Uint16List(indexCount);
+
+    _generateIndices(mesh.indices);
+
+    int offset = 0;
+
+    positionAttrib = new VertexAttrib('POSITION', 3, 'float', stride, offset);
+    mesh.addAttrib(positionAttrib);
+    offset += 12;
+    positionView = mesh.getViewForAttrib('POSITION');
+    _generatePositions(positionView, mesh.indices);
+
+    if (flags.texCoords || flags.tangents) {
+      texCoordAttrib = new VertexAttrib('TEXCOORD0', 2, 'float', stride,
+                                        offset);
+      mesh.addAttrib(texCoordAttrib);
+      offset += 8;
+      texCoordView = mesh.getViewForAttrib('TEXCOORD0');
+      _generateTexCoords(texCoordView, positionView, mesh.indices);
+    }
+
+    if (flags.normals) {
+      normalAttrib = new VertexAttrib('NORMAL', 3, 'float', stride, offset);
+      mesh.addAttrib(normalAttrib);
+      offset += 12;
+      normalView = mesh.getViewForAttrib('NORMAL');
+      _generateNormals(normalView, positionView, mesh.indices);
+    }
+
+    if (flags.tangents) {
+      tangentAttrib = new VertexAttrib('TANGENT', 4, 'float', stride, offset);
+      mesh.addAttrib(tangentAttrib);
+      offset += 16;
+      tangentView = mesh.getViewForAttrib('TANGENT');
+      _generateTangents(tangentView, positionView, normalView, texCoordView,
+          mesh.indices);
+    }
+
+    if (flags.invertFaces) {
+      _invertMeshFaces(mesh);
+    }
+
+    return mesh;
+  }
+
+  void _generateIndices(Uint16List indices);
+
+  void _generatePositions(Vector3List positions, Uint16List indices);
+
+  void _generateTexCoords(Vector2List texCoords, Vector3List positions,
+                         Uint16List indices) {
+    for (int i = 0; i < positions.length; ++i) {
+      Vector3 p = positions[i];
+
+      // These are TERRIBLE texture coords, but it's better than nothing.
+      // Override this function and put better ones in place!
+      texCoords[i] = new Vector2(p.x + p.z, p.y + p.z);
+    }
+  }
+
+  void _generateNormals(Vector3List normals, Vector3List positions,
+                       Uint16List indices) {
+    generateNormals(normals, positions, indices);
+  }
+
+  void _generateTangents(Vector4List tangents, Vector3List positions,
+                         Vector3List normals, Vector2List texCoords,
+                        Uint16List indices) {
+    generateTangents(tangents, positions, normals, texCoords, indices);
+  }
+
+  void _invertMeshFaces(MeshGeometry mesh) {
+    // Swap all the triangle indices
+    for (int i=0; i < mesh.indices.length; i += 3) {
+      int tmp = mesh.indices[i];
+      mesh.indices[i] = mesh.indices[i+2];
+      mesh.indices[i+2] = tmp;
+    }
+
+    Vector3List normals = mesh.getViewForAttrib('NORMAL');
+    if (normals != null) {
+      for(int i=0; i < normals.length; ++i) {
+        normals[i] = -normals[i];
+      }
+    }
+
+    // TODO: Do the tangents need to be inverted? Maybe just the W component?
+  }
+}
\ No newline at end of file
diff --git a/lib/src/vector_math_geometry/mesh_geometry.dart b/lib/src/vector_math_geometry/mesh_geometry.dart
new file mode 100644
index 0000000..94236b0
--- /dev/null
+++ b/lib/src/vector_math_geometry/mesh_geometry.dart
@@ -0,0 +1,117 @@
+/*
+  Copyright (C) 2013 John McCutchan <john@johnmccutchan.com>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+*/
+
+part of vector_math_geometry;
+
+class VertexAttrib {
+  final String name;
+  final String type;
+  final int size;
+  final int stride;
+  final int offset;
+
+  VertexAttrib(this.name, this.size, this.type, this.stride, this.offset);
+
+  VectorList getView(Float32List buffer) {
+    int viewOffset = offset ~/ buffer.elementSizeInBytes;
+    int viewStride = stride ~/ buffer.elementSizeInBytes;
+    switch(size) {
+      case 2:
+        return new Vector2List.view(buffer, viewOffset, viewStride);
+      case 3:
+        return new Vector3List.view(buffer, viewOffset, viewStride);
+      case 4:
+        return new Vector4List.view(buffer, viewOffset, viewStride);
+    }
+  }
+
+  String get format {
+    return '$type$size';
+  }
+
+  Map toJson() {
+    return {
+      'format': format,
+      'name': name,
+      'offset': offset,
+      'stride': stride,
+      'size': size,
+      'type': type
+    };
+  }
+}
+
+class MeshGeometry {
+  Float32List buffer;
+  Uint16List indices;
+  List<VertexAttrib> attribs = new List<VertexAttrib>();
+
+  MeshGeometry() {}
+
+  MeshGeometry.fromJson(Map json) {
+    buffer = new Float32List.fromList(json["buffer"]);
+
+    if (json.containsKey("indices")) {
+      indices = new Uint16List.fromList(json["indices"]);
+    }
+
+    Map jsonAttribs = json["attribs"];
+    for (String key in jsonAttribs.keys) {
+      addAttrib(attribFromJson(key, jsonAttribs[key]));
+    }
+  }
+
+  Map toJson() {
+    Map r = {};
+    r['attributes'] = attribs;
+    r['indices'] = indices;
+    r['vertices'] = buffer;
+    return r;
+  }
+
+  static VertexAttrib attribFromJson(String name, Map json) {
+    return new VertexAttrib(name,
+                            json["size"],
+                            json["format"],
+                            json["stride"],
+                            json["offset"]);
+  }
+
+  // Estimates the number of vertices contained in this mesh
+  int get length {
+    if (attribs == null || attribs.length == 0)
+      return 0;
+    VertexAttrib a = attribs[0];
+    return buffer.lengthInBytes ~/ a.stride;
+  }
+
+  void addAttrib(VertexAttrib attrib) {
+    attribs.add(attrib);
+  }
+
+  dynamic getViewForAttrib(String name) {
+    for (VertexAttrib attrib in attribs) {
+      if (attrib.name == name)
+        return attrib.getView(buffer);
+    }
+    return null;
+  }
+}
diff --git a/lib/src/vector_math_geometry/sphere_generator.dart b/lib/src/vector_math_geometry/sphere_generator.dart
new file mode 100644
index 0000000..229ab32
--- /dev/null
+++ b/lib/src/vector_math_geometry/sphere_generator.dart
@@ -0,0 +1,110 @@
+/*
+  Copyright (C) 2013 John McCutchan <john@johnmccutchan.com>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+*/
+
+part of vector_math_geometry;
+
+class SphereGenerator extends GeometryGenerator {
+  double _radius;
+  int _latSegments;
+  int _lonSegments;
+
+  int get vertexCount => (_lonSegments + 1) * (_latSegments + 1);
+  int get indexCount => 6 * _lonSegments * _latSegments;
+
+  MeshGeometry createSphere(num radius, {int latSegments: 16,
+                            int lonSegments: 16, flags: null}) {
+    _radius = radius.toDouble();
+    _latSegments = latSegments;
+    _lonSegments = lonSegments;
+
+    return _createGeometry(flags);
+  }
+
+  void _generateIndices(Uint16List indices) {
+    int i = 0;
+    for (int y = 0; y < _latSegments; ++y) {
+      int base1 = (_lonSegments + 1) * y;
+      int base2 = (_lonSegments + 1) * (y+1);
+
+      for(int x = 0; x < _lonSegments; ++x) {
+        indices[i++] = base1 + x;
+        indices[i++] = base1 + x + 1;
+        indices[i++] = base2 + x;
+
+        indices[i++] = base1 + x + 1;
+        indices[i++] = base2 + x + 1;
+        indices[i++] = base2 + x;
+      }
+    }
+  }
+
+  void _generatePositions(Vector3List positions, Uint16List indices) {
+    int i = 0;
+    for (int y = 0; y <= _latSegments; ++y) {
+      double v = y / _latSegments;
+      double sv = Math.sin(v * Math.PI);
+      double cv = Math.cos(v * Math.PI);
+
+      for (int x = 0; x <= _lonSegments; ++x) {
+        double u = x / _lonSegments;
+
+        positions[i++] = new Vector3(
+            _radius * Math.cos(u * Math.PI * 2.0) * sv,
+            _radius * cv,
+            _radius * Math.sin(u * Math.PI * 2.0) * sv
+        );
+      }
+    }
+  }
+
+  void _generateTexCoords(Vector2List texCoords, Vector3List positions,
+                          Uint16List indices) {
+    int i = 0;
+    for (int y = 0; y <= _latSegments; ++y) {
+      double v = y / _latSegments;
+
+      for (int x = 0; x <= _lonSegments; ++x) {
+        double u = x / _lonSegments;
+        texCoords[i++] = new Vector2(u, v);
+      }
+    }
+  }
+
+  void _generateNormals(Vector3List normals, Vector3List positions,
+                        Uint16List indices) {
+    int i = 0;
+    for (int y = 0; y <= _latSegments; ++y) {
+      double v = y / _latSegments;
+      double sv = Math.sin(v * Math.PI);
+      double cv = Math.cos(v * Math.PI);
+
+      for (int x = 0; x <= _lonSegments; ++x) {
+        double u = x / _lonSegments;
+
+        normals[i++] = new Vector3(
+            Math.cos(u * Math.PI * 2.0) * sv,
+            cv,
+            Math.sin(u * Math.PI * 2.0) * sv
+        );
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/lib/src/vector_math_operations/matrix.dart b/lib/src/vector_math_operations/matrix.dart
index 569bc4e..f6935bc 100644
--- a/lib/src/vector_math_operations/matrix.dart
+++ b/lib/src/vector_math_operations/matrix.dart
@@ -306,7 +306,7 @@
                        b2.shuffle(Float32x4.ZZZZ) * a2 +
                        b2.shuffle(Float32x4.WWWW) * a3;
     var b3 = B[bOffset++];
-    out[outOffset++] = b3.shuffle(Float32x4.XXXX)*a0 +
+    out[outOffset++] = b3.shuffle(Float32x4.XXXX) * a0 +
                        b3.shuffle(Float32x4.YYYY) * a1 +
                        b3.shuffle(Float32x4.ZZZZ) * a2 +
                        b3.shuffle(Float32x4.WWWW) * a3;
diff --git a/lib/vector_math_geometry.dart b/lib/vector_math_geometry.dart
index f2ba469..7a688c5 100644
--- a/lib/vector_math_geometry.dart
+++ b/lib/vector_math_geometry.dart
@@ -20,8 +20,16 @@
 */
 
 library vector_math_geometry;
+
 import 'dart:typed_data';
+import 'dart:math' as Math;
+
 import 'package:vector_math/vector_math.dart';
 import 'package:vector_math/vector_math_lists.dart';
 
 part 'src/vector_math_geometry/attribute_generators.dart';
+part 'src/vector_math_geometry/cube_generator.dart';
+part 'src/vector_math_geometry/cylinder_generator.dart';
+part 'src/vector_math_geometry/geometry_generator.dart';
+part 'src/vector_math_geometry/mesh_geometry.dart';
+part 'src/vector_math_geometry/sphere_generator.dart';