Permalink
Browse files

Automatically calculate normals as needed for meshes missing normal data

Also, validate mesh when #data is accessed.
  • Loading branch information...
1 parent 1739b49 commit 1716a6197acd701bbefbadc970f07ee1181f3a58 @sinisterchipmunk committed Jun 9, 2012
@@ -10,7 +10,7 @@ class Mesh
constructor: (options) ->
@_valid = false
- @_data = new Jax.Mesh.Data
+ @data = new Jax.Mesh.Data
@_bounds = new Jax.Mesh.Bounds
@_color = new Jax.Color
@_initialized = false
@@ -48,8 +48,10 @@ class Mesh
@_data
set: (d) ->
@invalidate()
+ @_data.dispose() if @_data
@_data = d
@_data.addEventListener 'colorChanged', => @fireEvent 'colorChanged'
+ @_data.addEventListener 'shouldRecalculateNormals', => @recalculateNormals()
@define 'color'
get: -> @_color
@@ -82,7 +84,35 @@ class Mesh
@_submesh = submesh
draw_mode: GL_POINTS
-
+
+ ###
+ Immediately recalculates this mesh's vertex normals.
+
+ This method is meant to be overridden by subclasses. The default implementation just
+ builds a vector from the calculated center of the mesh to each vertex and normalizes
+ that vector.
+
+ Note that if this mesh has more than 65535 vertices, its sub-mesh will not automatically
+ have its normals recalculated, so you'll need to call `mesh.submesh.recalculateNormals()`.
+
+ Returns true.
+ ###
+ recalcNormal = vec3.create()
+ recalculateNormals: ->
+ normals = @data.normalBuffer
+ vertices = @data.vertexBuffer
+ center = @bounds.center
+ for i in [0...vertices.length] by 3
+ recalcNormal[0] = vertices[i]
+ recalcNormal[1] = vertices[i+1]
+ recalcNormal[2] = vertices[i+2]
+ vec3.subtract recalcNormal, center, recalcNormal
+ vec3.normalize recalcNormal
+ normals[i ] = recalcNormal[0]
+ normals[i+1] = recalcNormal[1]
+ normals[i+2] = recalcNormal[2]
+ true
+
render: (context, model, material) ->
@validate() unless @_valid
if material
@@ -128,7 +158,7 @@ class Mesh
[vertices, colors, textures, normals, indices] = [[], [], [], [], []]
@init vertices, colors, textures, normals, indices
@submesh = @split vertices, colors, textures, normals, indices if vertices.length > 65535*3
- @_data = new Jax.Mesh.Data vertices, colors, textures, normals, indices
+ @data = new Jax.Mesh.Data vertices, colors, textures, normals, indices
@_data.color = @_color
this
@@ -134,12 +134,45 @@ class Jax.Mesh.Data
@bind @_context unless @_bound
for key, target of mapping
- switch key
- when 'vertices' then vars.set target, @vertexWrapper
- when 'colors' then vars.set target, @colorWrapper
- when 'textures' then vars.set target, @textureCoordsWrapper
- when 'normals' then vars.set target, @normalWrapper
- else throw new Error "Mapping key must be one of 'vertices', 'colors', 'textures', 'normals'"
+ if vars.set
+ # TODO phase out vars.set in favor of direct assignment
+ switch key
+ when 'vertices' then vars.set target, @vertexWrapper
+ when 'colors' then vars.set target, @colorWrapper
+ when 'textures' then vars.set target, @textureCoordsWrapper
+ when 'normals'
+ @recalculateNormals() if @shouldRecalculateNormals()
+ vars.set target, @normalWrapper
+ else throw new Error "Mapping key must be one of 'vertices', 'colors', 'textures', 'normals'"
+ else
+ switch key
+ when 'vertices' then vars[target] = @vertexWrapper
+ when 'colors' then vars[target] = @colorWrapper
+ when 'textures' then vars[target] = @textureCoordsWrapper
+ when 'normals'
+ @recalculateNormals() if @shouldRecalculateNormals()
+ vars[target] = @normalWrapper
+ else throw new Error "Mapping key must be one of 'vertices', 'colors', 'textures', 'normals'"
+
+ ###
+ Requests this data set's normals to be recalculated. Note that this does not directly
+ perform the recalculation. Instead, it fires a `shouldRecalculateNormals` event, so
+ that the object containing this mesh data can control the method in which normals
+ are calculated. For example, a point cloud might calculate its normals entirely
+ differently from a triangle mesh, and it is not the responsibility of `Jax.Mesh.Data`
+ to keep track of which algorithm it should use.
+ ###
+ recalculateNormals: () ->
+ @fireEvent 'shouldRecalculateNormals'
+ @_shouldRecalculateNormals = false
+ @invalidate()
+ true
+
+ ###
+ Returns true if the mesh data has detected that its normal data should be recalculated.
+ ###
+ shouldRecalculateNormals: () ->
+ return @_shouldRecalculateNormals
###
Allocate or reallocate the typed array buffer and data views. This is called during
@@ -190,21 +223,15 @@ class Jax.Mesh.Data
_csize = 4 * Float32Array.BYTES_PER_ELEMENT
_array_buffer = @_array_buffer
length = @length
+ if normals.length is 0 then @_shouldRecalculateNormals = true
+ else @_shouldRecalculateNormals = false
for ofs in [0...length]
[vofs, cofs, tofs] = [ofs * 3, ofs * 4, ofs * 2]
_vbuf[vofs ] = vertices[vofs ]
_vbuf[vofs+1] = vertices[vofs+1]
_vbuf[vofs+2] = vertices[vofs+2]
- if normals.length <= vofs
- tmpvec3[0] = vertices[vofs]
- tmpvec3[1] = vertices[vofs+1]
- tmpvec3[2] = vertices[vofs+2]
- vec3.normalize tmpvec3
- _nbuf[vofs ] = tmpvec3[0]
- _nbuf[vofs+1] = tmpvec3[1]
- _nbuf[vofs+2] = tmpvec3[2]
- else
+ unless @_shouldRecalculateNormals
_nbuf[vofs ] = normals[vofs ]
_nbuf[vofs+1] = normals[vofs+1]
_nbuf[vofs+2] = normals[vofs+2]
@@ -1,104 +1,106 @@
// Support functions used by Jax.Mesh
-Jax.OldMesh.prototype.eachTriangle = function(callback) {
- var mesh = this;
- var vertcount, a;
+if (Jax.OldMesh) {
+ Jax.OldMesh.prototype.eachTriangle = function(callback) {
+ var mesh = this;
+ var vertcount, a;
- var indices = mesh.getIndexBuffer(), vertices = mesh.vertices;
- if (vertices.length == 0) return;
+ var indices = mesh.getIndexBuffer(), vertices = mesh.vertices;
+ if (vertices.length == 0) return;
- if (indices) {
- if (indices.length == 0) return;
- indices = indices.getTypedArray();
- vertcount = indices.length;
- } else
- vertcount = vertices.length;
+ if (indices) {
+ if (indices.length == 0) return;
+ indices = indices.getTypedArray();
+ vertcount = indices.length;
+ } else
+ vertcount = vertices.length;
- function call(i1, i2, i3) {
- var v1, v2, v3, i = false;
+ function call(i1, i2, i3) {
+ var v1, v2, v3, i = false;
- if (indices) {
- i = true;
- v1 = vertices[indices[i1]];
- v2 = vertices[indices[i2]];
- v3 = vertices[indices[i3]];
- } else {
- i = false;
- v1 = vertices[i1];
- v2 = vertices[i2];
- v3 = vertices[i3];
- }
+ if (indices) {
+ i = true;
+ v1 = vertices[indices[i1]];
+ v2 = vertices[indices[i2]];
+ v3 = vertices[indices[i3]];
+ } else {
+ i = false;
+ v1 = vertices[i1];
+ v2 = vertices[i2];
+ v3 = vertices[i3];
+ }
- if (!v1 || !v2 || !v3) return;
- callback(v1.array, v2.array, v3.array);
- }
+ if (!v1 || !v2 || !v3) return;
+ callback(v1.array, v2.array, v3.array);
+ }
- switch(mesh.draw_mode) {
- case GL_TRIANGLE_STRIP:
- for (a = 2; a < vertcount; a += 2) {
- call(a-2, a-1, a);
- call(a, a-1, a+1);
- }
- break;
- case GL_TRIANGLES:
- for (a = 0; a < vertcount; a += 3)
- call(a, a+1, a+2);
- break;
- case GL_TRIANGLE_FAN:
- for (a = 2; a < vertcount; a++)
- call(0, a-1, a);
- break;
- default:
- return;
- }
-};
+ switch(mesh.draw_mode) {
+ case GL_TRIANGLE_STRIP:
+ for (a = 2; a < vertcount; a += 2) {
+ call(a-2, a-1, a);
+ call(a, a-1, a+1);
+ }
+ break;
+ case GL_TRIANGLES:
+ for (a = 0; a < vertcount; a += 3)
+ call(a, a+1, a+2);
+ break;
+ case GL_TRIANGLE_FAN:
+ for (a = 2; a < vertcount; a++)
+ call(0, a-1, a);
+ break;
+ default:
+ return;
+ }
+ };
-Jax.OldMesh.prototype.buildTriangles = function() {
- var mesh = this;
- mesh.triangles.clear();
+ Jax.OldMesh.prototype.buildTriangles = function() {
+ var mesh = this;
+ mesh.triangles.clear();
- mesh.eachTriangle(function(v1, v2, v3) {
- var tri = new Jax.Geometry.Triangle();
- tri.assign(v1, v2, v3);
- mesh.triangles.push(tri);
- });
-};
+ mesh.eachTriangle(function(v1, v2, v3) {
+ var tri = new Jax.Geometry.Triangle();
+ tri.assign(v1, v2, v3);
+ mesh.triangles.push(tri);
+ });
+ };
-Jax.OldMesh.prototype.calculateBounds = function(vertices) {
- var self = this;
- if (vertices.length == 0) {
- self.bounds.left = self.bounds.right = 0;
- self.bounds.top = self.bounds.bottom = 0;
- self.bounds.front = self.bounds.back = 0;
- self.bounds.width = self.bounds.height = self.bounds.depth = 0;
- } else {
- self.bounds.left = self.bounds.right = null;
- self.bounds.top = self.bounds.bottom = null;
- self.bounds.front = self.bounds.back = null;
- self.bounds.width = self.bounds.height = self.bounds.depth = null;
- }
+ Jax.OldMesh.prototype.calculateBounds = function(vertices) {
+ var self = this;
+ if (vertices.length == 0) {
+ self.bounds.left = self.bounds.right = 0;
+ self.bounds.top = self.bounds.bottom = 0;
+ self.bounds.front = self.bounds.back = 0;
+ self.bounds.width = self.bounds.height = self.bounds.depth = 0;
+ } else {
+ self.bounds.left = self.bounds.right = null;
+ self.bounds.top = self.bounds.bottom = null;
+ self.bounds.front = self.bounds.back = null;
+ self.bounds.width = self.bounds.height = self.bounds.depth = null;
+ }
- var i, v;
+ var i, v;
- for (i = 0; i < vertices.length; i++)
- {
- // x, i % 3 == 0
- v = vertices[i];
- if (self.bounds.left == null || v < self.bounds.left) self.bounds.left = v;
- if (self.bounds.right == null || v > self.bounds.right) self.bounds.right = v;
+ for (i = 0; i < vertices.length; i++)
+ {
+ // x, i % 3 == 0
+ v = vertices[i];
+ if (self.bounds.left == null || v < self.bounds.left) self.bounds.left = v;
+ if (self.bounds.right == null || v > self.bounds.right) self.bounds.right = v;
- // y, i % 3 == 1
- v = vertices[++i];
- if (self.bounds.bottom== null || v < self.bounds.bottom) self.bounds.bottom = v;
- if (self.bounds.top == null || v > self.bounds.top) self.bounds.top = v;
+ // y, i % 3 == 1
+ v = vertices[++i];
+ if (self.bounds.bottom== null || v < self.bounds.bottom) self.bounds.bottom = v;
+ if (self.bounds.top == null || v > self.bounds.top) self.bounds.top = v;
- // z, i % 3 == 2
- v = vertices[++i];
- if (self.bounds.front == null || v > self.bounds.front) self.bounds.front = v;
- if (self.bounds.back == null || v < self.bounds.back) self.bounds.back = v;
- }
+ // z, i % 3 == 2
+ v = vertices[++i];
+ if (self.bounds.front == null || v > self.bounds.front) self.bounds.front = v;
+ if (self.bounds.back == null || v < self.bounds.back) self.bounds.back = v;
+ }
- self.bounds.width = self.bounds.right - self.bounds.left;
- self.bounds.height= self.bounds.top - self.bounds.bottom;
- self.bounds.depth = self.bounds.front - self.bounds.back;
-};
+ self.bounds.width = self.bounds.right - self.bounds.left;
+ self.bounds.height= self.bounds.top - self.bounds.bottom;
+ self.bounds.depth = self.bounds.front - self.bounds.back;
+ };
+}
Oops, something went wrong.

0 comments on commit 1716a61

Please sign in to comment.