summaryrefslogtreecommitdiffstats
path: root/o3d/samples
diff options
context:
space:
mode:
authorpathorn@chromium.org <pathorn@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-18 18:17:34 +0000
committerpathorn@chromium.org <pathorn@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-18 18:17:34 +0000
commitd832c34a518a1cf072fe4e0f7e00ac158eb8cadd (patch)
treea6ba1136ded4fb68cdadeffe86051e20f262cd0e /o3d/samples
parent2bb044402ba016977b58014a22b0631ab7ed605d (diff)
downloadchromium_src-d832c34a518a1cf072fe4e0f7e00ac158eb8cadd.zip
chromium_src-d832c34a518a1cf072fe4e0f7e00ac158eb8cadd.tar.gz
chromium_src-d832c34a518a1cf072fe4e0f7e00ac158eb8cadd.tar.bz2
o3d-webgl: Shader support for skinning.
Requires setting skinning in properly in o3djs/effect.js BUG=none TEST=o3d-webgl-samples/skinning.html runs much faster. Review URL: http://codereview.chromium.org/2873071 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@56558 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'o3d/samples')
-rw-r--r--o3d/samples/o3d-webgl-samples/skinning.html2
-rw-r--r--o3d/samples/o3d-webgl/draw_list.js10
-rw-r--r--o3d/samples/o3d-webgl/effect.js29
-rw-r--r--o3d/samples/o3d-webgl/param.js23
-rw-r--r--o3d/samples/o3d-webgl/primitive.js11
-rw-r--r--o3d/samples/o3d-webgl/skin.js319
-rw-r--r--o3d/samples/o3d-webgl/stream.js10
-rw-r--r--o3d/samples/o3d-webgl/vertex_source.js15
-rw-r--r--o3d/samples/o3djs/effect.js92
-rw-r--r--o3d/samples/o3djs/primitives.js3
10 files changed, 469 insertions, 45 deletions
diff --git a/o3d/samples/o3d-webgl-samples/skinning.html b/o3d/samples/o3d-webgl-samples/skinning.html
index c1b75bc..f8450be 100644
--- a/o3d/samples/o3d-webgl-samples/skinning.html
+++ b/o3d/samples/o3d-webgl-samples/skinning.html
@@ -121,7 +121,7 @@ function initStep2(clientElements) {
// Create a cylinder.
var vertexInfo = o3djs.primitives.createCylinderVertices(
- 40, 200, 12, 20,
+ 40, 200, 120, 200,
[[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
diff --git a/o3d/samples/o3d-webgl/draw_list.js b/o3d/samples/o3d-webgl/draw_list.js
index 2c79e12..68f635d 100644
--- a/o3d/samples/o3d-webgl/draw_list.js
+++ b/o3d/samples/o3d-webgl/draw_list.js
@@ -140,11 +140,15 @@ o3d.DrawList.prototype.render = function() {
var paramObjects = [
transform,
drawElement,
- element,
+ element
+ ];
+ if (element.streamBank) {
+ paramObjects.push(element.streamBank);
+ }
+ paramObjects.push(
material,
effect,
- o3d.Param.SAS
- ];
+ o3d.Param.SAS);
effect.searchForParams_(paramObjects);
diff --git a/o3d/samples/o3d-webgl/effect.js b/o3d/samples/o3d-webgl/effect.js
index 65da4a3..3d9f85e 100644
--- a/o3d/samples/o3d-webgl/effect.js
+++ b/o3d/samples/o3d-webgl/effect.js
@@ -296,7 +296,8 @@ o3d.Effect.prototype.getUniforms_ =
this.program_, this.gl.ACTIVE_UNIFORMS);
for (var i = 0; i < numUniforms; ++i) {
var info = this.gl.getActiveUniform(this.program_, i);
- if (info.name.indexOf('[') != -1) {
+ var name = info.name;
+ if (name.indexOf('[') != -1) {
// This is an array param and we need to individually query each item in
// the array to get its location.
var baseName = info.name.substring(0, info.name.indexOf('['));
@@ -312,10 +313,10 @@ o3d.Effect.prototype.getUniforms_ =
};
} else {
// Not an array param.
- this.uniforms_[info.name] = {
+ this.uniforms_[name] = {
info: info,
kind: o3d.Effect.ELEMENT,
- location: this.gl.getUniformLocation(this.program_, info.name)
+ location: this.gl.getUniformLocation(this.program_, name)
};
}
}
@@ -391,9 +392,22 @@ o3d.Effect.semanticMap_ = {
'texCoord4': {semantic: o3d.Stream.TEXCOORD, index: 4, gl_index: 9},
'texCoord5': {semantic: o3d.Stream.TEXCOORD, index: 5, gl_index: 10},
'texCoord6': {semantic: o3d.Stream.TEXCOORD, index: 6, gl_index: 11},
- 'texCoord7': {semantic: o3d.Stream.TEXCOORD, index: 7, gl_index: 12}
+ 'texCoord7': {semantic: o3d.Stream.TEXCOORD, index: 7, gl_index: 12},
+ 'influenceWeights': {semantic: o3d.Stream.INFLUENCE_WEIGHTS, index: 0,
+ gl_index: 13},
+ 'influenceIndices': {semantic: o3d.Stream.INFLUENCE_INDICES, index: 0,
+ gl_index: 14}
};
+o3d.Effect.reverseSemanticMap_ = [];
+(function(){
+ var revmap = o3d.Effect.reverseSemanticMap_;
+ for (var key in o3d.Effect.semanticMap_) {
+ var value = o3d.Effect.semanticMap_[key];
+ revmap[value.semantic] = revmap[value.semantic] || [];
+ revmap[value.semantic][value.index] = value.gl_index;
+ }
+})();
/**
* For each of the effect's uniform parameters, creates corresponding
@@ -419,7 +433,12 @@ o3d.Effect.prototype.createUniformParameters =
if (!sasTypes[name]) {
switch (uniformData.kind) {
case o3d.Effect.ARRAY:
- param_object.createParam(name, 'ParamParamArray');
+ var param = param_object.createParam(name, 'ParamParamArray');
+ var array = new o3d.ParamArray;
+ array.gl = this.gl;
+ array.resize(uniformData.info.size,
+ paramTypes[uniformData.info.type]);
+ param.value = array;
break;
case o3d.Effect.STRUCT:
o3d.notImplemented();
diff --git a/o3d/samples/o3d-webgl/param.js b/o3d/samples/o3d-webgl/param.js
index d30af44..0f8a819 100644
--- a/o3d/samples/o3d-webgl/param.js
+++ b/o3d/samples/o3d-webgl/param.js
@@ -341,6 +341,29 @@ o3d.ParamParamArray = function() {
};
o3d.inherit('ParamParamArray', 'Param');
+/**
+ * Acts like ParamParamArray, but asks its owner object to update the array
+ * contents every time its value is queried.
+ *
+ * @constructor
+ * @extends {o3d.ParamParamArray}
+ */
+o3d.ParamParamArrayOutput = function() {
+ o3d.ParamParamArray.call(this);
+};
+o3d.inherit('ParamParamArrayOutput', 'ParamParamArray');
+o3d.ParamParamArrayOutput.prototype.__defineGetter__("value",
+ function() {
+ this.owner_.updateOutputs(this);
+ return this.value_;
+ }
+);
+o3d.ParamParamArrayOutput.prototype.__defineSetter__("value",
+ function(value) {
+ // Creating a new array is fine.
+ this.value_ = value;
+ }
+);
/**
* @constructor
diff --git a/o3d/samples/o3d-webgl/primitive.js b/o3d/samples/o3d-webgl/primitive.js
index 358c3955..7c15ac2 100644
--- a/o3d/samples/o3d-webgl/primitive.js
+++ b/o3d/samples/o3d-webgl/primitive.js
@@ -127,19 +127,20 @@ o3d.Primitive.prototype.render = function() {
for (var semantic_index = 0;
semantic_index < streams.length;
++semantic_index) {
- var gl_index = semantic + semantic_index - 1;
- var stream_param = streams[semantic_index];
+ var gl_index = o3d.Effect.reverseSemanticMap_[semantic][semantic_index];
+ var stream = streams[semantic_index].stream;
+ var field = stream.field;
+ var buffer = field.buffer;
+ var stream_param = streams[semantic_index];
while (!stream_param.owner_.updateStreams &&
stream_param.inputConnection) {
stream_param = stream_param.inputConnection;
}
if (stream_param.owner_.updateStreams) {
+ // By now, stream_param should point to the SkinEval's streams.
stream_param.owner_.updateStreams(); // Triggers updating.
}
- var stream = streams[semantic_index].stream;
- var field = stream.field;
- var buffer = field.buffer;
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer.gl_buffer_);
this.gl.enableVertexAttribArray(gl_index);
diff --git a/o3d/samples/o3d-webgl/skin.js b/o3d/samples/o3d-webgl/skin.js
index ea16430..cbec71f 100644
--- a/o3d/samples/o3d-webgl/skin.js
+++ b/o3d/samples/o3d-webgl/skin.js
@@ -77,10 +77,105 @@ o3d.Skin = function() {
* @private
*/
this.highest_influences_ = 0;
+
+ /**
+ * True if the list of weights and matrix index streams could be downloaded.
+ *
+ * @type {boolean}
+ * @private
+ */
+ this.supports_vertex_shader_ = false;
+
+ /**
+ * Whether the WEIGHTS and MATRIX_INDICES buffers are up-to-date.
+ *
+ * @type {boolean}
+ * @private
+ */
+ this.buffers_valid_ = false;
+
+ /**
+ * Vertex buffer containing indices and weights for this skin.
+ *
+ * @type {VertexBuffer}
+ */
+ this.vertex_buffer_ = null;
+
+ /**
+ * Buffer in which to store vertex weights.
+ * weights_field_ will be passed into the WEIGHTS stream.
+ *
+ * @type {o3d.Field}
+ * @private
+ */
+ this.weights_field_ = null;
+
+ /**
+ * Buffer in which to store list of matrix_indices per vertex.
+ * matrix_indices_field_ will be passed into the MATRIX_INDICES stream.
+ *
+ * @type {o3d.Field}
+ * @private
+ */
+ this.matrix_indices_field_ = null;
};
o3d.inherit('Skin', 'NamedObject');
/**
+ * Updates the weights and indices vertex buffers attached to this skin, only
+ * if they have been invalidated by some param change.
+ * Also checks if we can support skinning based on the number of bones we need
+ * in this skin.
+ * @private
+ * @return {boolean} true if our mesh is small enough to enable skin shader.
+ */
+o3d.Skin.prototype.updateVertexShader = function() {
+ if (!this.buffers_valid_) {
+ var numcomp = 4;
+ var numvert = this.influences.length;
+ if (!this.weights_field_ && !this.matrix_indices_field_) {
+ var vertex_buffer = new o3d.VertexBuffer;
+ vertex_buffer.gl = this.gl;
+ this.weights_field_ =
+ vertex_buffer.createField("FloatField", numcomp);
+ this.matrix_indices_field_ =
+ vertex_buffer.createField("FloatField", numcomp);
+ vertex_buffer.allocateElements(numvert);
+ }
+ var ii, jj;
+ var weights_field = this.weights_field_;
+ var indices_field = this.matrix_indices_field_;
+ var highest_influences = this.getHighestInfluences();
+
+ this.buffers_valid_ = true;
+ weights_field.buffer.lock();
+ indices_field.buffer.lock();
+ var max_num_bones = o3d.SkinEval.getMaxNumBones(this);
+ this.supports_vertex_shader_ = (highest_influences <= numcomp) &&
+ (this.inverseBindPoseMatrices.length <= max_num_bones);
+ if (this.supports_vertex_shader_) {
+ // NOTE: If you make these Array's instead, you must initialize to 0.
+ var weights_arr = new Float32Array(numvert * numcomp);
+ var indices_arr = new Float32Array(numvert * numcomp);
+ // Float32rray is initialized to 0 by default.
+ for (ii = 0; ii < numvert; ++ii) {
+ var influence = this.influences[ii];
+ for (jj = 0; jj < influence.length && jj < numcomp * 2; jj += 2) {
+ indices_arr[ii * numcomp + jj / 2] = influence[jj];
+ weights_arr[ii * numcomp + jj / 2] = influence[jj + 1];
+ }
+ }
+ weights_field.setAt(0, weights_arr);
+ indices_field.setAt(0, indices_arr);
+ }
+ // Otherwise, weights will be filled with 0's by default.
+ weights_field.buffer.unlock();
+ indices_field.buffer.unlock();
+ }
+ return this.supports_vertex_shader_;
+};
+
+/**
* Sets the influences for an individual vertex.
*
* @param {number} vertex_index The index of the vertex to set influences for.
@@ -98,6 +193,7 @@ o3d.Skin.prototype.setVertexInfluences = function(
}
this.influences[vertex_index] = influences;
this.info_valid_ = false;
+ this.buffers_valid_ = false;
};
/**
@@ -228,6 +324,13 @@ o3d.SkinEval = function() {
this.bones_ = [];
/**
+ * Float32 array containing all matrices in 3x4 format.
+ * @type {Float32Array}
+ * @private
+ */
+ this.bone_array_ = null;
+
+ /**
* Cache of StreamInfo objects for input values. Saved to avoid reallocating.
* @type {!Array<o3d.SkinEval.StreamInfo>}
* @private
@@ -240,6 +343,16 @@ o3d.SkinEval = function() {
* @private
*/
this.output_stream_infos_ = [];
+
+ /**
+ * The base matrix to subtract from the matrices before skinning.
+ *
+ * @type {ParamArray}
+ * @private
+ */
+ this.createParam("boneToWorld3x4", "ParamParamArrayOutput");
+
+ this.usingSkinShader = 0.0;
};
o3d.inherit('SkinEval', 'StreamBank');
@@ -251,6 +364,28 @@ o3d.inherit('SkinEval', 'StreamBank');
o3d.ParamObject.setUpO3DParam_(o3d.SkinEval, "base", "ParamMatrix4");
/**
+ * Non-zero if we are using a shader for skinning.
+ *
+ * Holds state of whether the boneToWorld3x4 uniform is being used. If its
+ * value has been read, copy over the original vertex buffers, else we
+ * assume the shader does not support skinning.
+ *
+ * Do not write to this value. Set disableShader to true instead.
+ *
+ * @type {number}
+ * @see disableShader
+ */
+o3d.ParamObject.setUpO3DParam_(o3d.SkinEval, "usingSkinShader", "ParamFloat");
+
+/**
+ * Set this value to true to force skin not to use a shader.
+ *
+ * @type {boolean}
+ * @default false
+ */
+o3d.ParamObject.setUpO3DParam_(o3d.SkinEval, "disableShader", "ParamBoolean");
+
+/**
* The Skin to use for skinning.
* @type {Skin}
*/
@@ -263,6 +398,62 @@ o3d.ParamObject.setUpO3DParam_(o3d.SkinEval, "skin", "ParamSkin");
o3d.ParamObject.setUpO3DParam_(o3d.SkinEval, "matrices", "ParamArray");
/**
+ * Skins use 3 vec4's per matrix, since the last row is redundant.
+ * If we don't know, 32 is safe minimum value required by standard = (128-32)/3.
+ * @param {!o3d.NamedObject} obj Some object with access to gl.
+ * @return {number} Maximum number of bones allowed for shader-based skinning.
+ */
+o3d.SkinEval.getMaxNumBones = function(obj) {
+ // Quote from spec:
+ // GL_MAX_VERTEX_UNIFORM_VECTORS
+ // params returns one value, the maximum number of four-element
+ // floating-point, integer, or boolean vectors that can be held in
+ // uniform variable storage for a vertex shader.
+ // The value must be at least 128. See glUniform.
+ var gl = obj.gl;
+ var maxVertexUniformVectors = gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS);
+ return Math.floor((maxVertexUniformVectors - 32) / 3);
+};
+
+/**
+ * Someone bound a stream to this SkinEval. Enable the shader on the primitive,
+ * and bind any additional weights or indices streams if necessary.
+ *
+ * @param {o3d.VertexSource} dest VertexSource that bound to this VertexSource.
+ * @param {o3d.ParamVertexBufferStream} dest_param Other param which was bound.
+ * @override
+ */
+o3d.SkinEval.prototype.streamWasBound_ = function(
+ dest, semantic, semantic_index) {
+ this.skin.updateVertexShader();
+
+ if (this.skin.weights_field_ && this.skin.matrix_indices_field_) {
+ var weights_stream = dest.getVertexStream(o3d.Stream.INFLUENCE_WEIGHTS, 0);
+ var indices_stream = dest.getVertexStream(o3d.Stream.INFLUENCE_INDICES, 0);
+ if (!weights_stream || !indices_stream ||
+ weights_stream.field != this.skin.weights_field_ ||
+ indices_stream.field != this.skin.matrix_indices_field_) {
+ dest.setVertexStream(o3d.Stream.INFLUENCE_WEIGHTS, 0,
+ this.skin.weights_field_, 0);
+ dest.setVertexStream(o3d.Stream.INFLUENCE_INDICES, 0,
+ this.skin.matrix_indices_field_, 0);
+ }
+
+ var destParam = dest.getParam("boneToWorld3x4");
+ if (!destParam) {
+ destParam = dest.createParam("boneToWorld3x4", "ParamParamArray");
+ }
+ destParam.bind(this.getParam("boneToWorld3x4"));
+
+ destParam = dest.getParam("usingSkinShader");
+ if (!destParam) {
+ destParam = dest.createParam("usingSkinShader", "ParamFloat");
+ }
+ destParam.bind(this.getParam("usingSkinShader"));
+ }
+};
+
+/**
* Multiplies input by weight, and adds with and returns into output.
*
* @param {!Array<!Array<number>>} input Input matrix4 to weight.
@@ -299,14 +490,16 @@ o3d.SkinEval.prototype.multiplyAdd_ = function(input, weight, output) {
};
/**
+ * Initializes all the input and output streams for this mesh, but does no
+ * copying yet. You should follow with uninitStreams_ when you are done.
+ *
* @param {o3d.Skin} skin The skin.
* @private
*/
-o3d.SkinEval.prototype.doSkinning_ = function(skin) {
+o3d.SkinEval.prototype.initStreams_ = function(skin) {
var ii, jj, ll, num_streams;
- var influences_array = skin.influences;
- var num_vertices = influences_array.length;
+ var num_vertices = this.skin.influences.length;
// Update our inputs, lock all the inputs and outputs and check that we have
// the same number of vertices as vertex influences.
for (ii = 0, num_streams = 0; ii < this.vertex_streams_.length; ++ii) {
@@ -318,14 +511,14 @@ o3d.SkinEval.prototype.doSkinning_ = function(skin) {
// Make sure our upstream streams are ready
var input = source_param.inputConnection;
if (input && input.isAClassName("ParamVertexBufferStream")) {
- input.updateOutputs(); // will automatically mark us as valid.
+ input.owner_.updateStreams(); // will automatically mark us as valid.
} else {
// Mark source_param as valid so we don't evaluate a second time.
// TODO(pathorn): Caching previous computed values.
}
var source_stream = source_param.stream;
- if (source_stream.getMaxVertices() != num_vertices) {
+ if (source_stream.getMaxVertices_() != num_vertices) {
// TODO: Change semantic below to semantic_name.
this.gl.client.error_callback("SkinEval.doSkinning_: "
+ "stream " + source_stream.semantic + " index "
@@ -370,7 +563,7 @@ o3d.SkinEval.prototype.doSkinning_ = function(skin) {
+ destination_param.className + " not ParamVertexBufferStream");
}
var destination_stream = destination_param.stream;
- if (destination_stream.getMaxVertices() != num_vertices) {
+ if (destination_stream.getMaxVertices_() != num_vertices) {
this.gl.client.error_callback("SkinEval.doSkinning_: "
+ "stream " + destination_stream.semantic + " index "
+ destination_stream.semanticIndex + " targeted by SkinEval '"
@@ -399,7 +592,41 @@ o3d.SkinEval.prototype.doSkinning_ = function(skin) {
}
}
- // skin.
+ this.input_stream_infos_.length = num_streams;
+ this.output_stream_infos_.length = num_streams;
+};
+
+/**
+ * Saves the result of the skinning operation in the graphics hardware.
+ *
+ * @private
+ */
+o3d.SkinEval.prototype.uninitStreams_ = function() {
+ // Unlock any buffers that were locked during skinning
+ for (ii = 0; ii < this.input_stream_infos_.length; ++ii) {
+ this.input_stream_infos_[ii].uninit();
+ }
+ for (ii = 0; ii < this.output_stream_infos_.length; ++ii) {
+ var output_streams = this.output_stream_infos_[ii];
+ for (var jj = 0; jj < output_streams.length; ++jj) {
+ output_streams[jj].uninit();
+ }
+ }
+};
+
+/**
+ * Does skinning in software. Performs the actual matrix-point mulitplications
+ * and saves the result in all the output streams.
+ *
+ * @private
+ */
+o3d.SkinEval.prototype.doSkinning_ = function() {
+ this.initStreams_();
+
+ var ii, jj, ll;
+ var influences_array = this.skin.influences;
+ var num_vertices = influences_array.length;
+
for (ii = 0; ii < num_vertices; ++ii) {
var influences = influences_array[ii];
if (influences.length) {
@@ -422,7 +649,7 @@ o3d.SkinEval.prototype.doSkinning_ = function(skin) {
}
// for each source, compute and copy to destination.
- for (jj = 0; jj < num_streams; ++jj) {
+ for (jj = 0; jj < this.input_stream_infos_.length; ++jj) {
var input_stream_info = this.input_stream_infos_[jj];
input_stream_info.compute_function_(accumulated_matrix);
var output_streams = this.output_stream_infos_[jj];
@@ -433,10 +660,14 @@ o3d.SkinEval.prototype.doSkinning_ = function(skin) {
}
}
}
+
+ this.uninitStreams_();
};
/**
- * Updates the Bones.
+ * Updates the bones from this.matrices.
+ *
+ * @private
*/
o3d.SkinEval.prototype.updateBones_ = function() {
// Get our matrices.
@@ -498,19 +729,71 @@ o3d.SkinEval.prototype.updateBones_ = function() {
* Updates the VertexBuffers bound to streams on this VertexSource.
*/
o3d.SkinEval.prototype.updateStreams = function() {
+ if (this.disableShader || !this.usingSkinShader ||
+ !this.skin.updateVertexShader()) {
+ this.updateBones_();
+ this.doSkinning_(this.skin);
+ this.usingSkinShader = 0.0;
+ }
+};
+
+/**
+ * Should be called on boneToWorld3x4's get value.
+ * Updates ParamArray for bones uniform sent to vertex shader.
+ *
+ * @param {ParamParamArrayOutput} param The array uniform parameter to update.
+ * @return {ParamArray} The array containing all of the float4 params.
+ */
+o3d.SkinEval.prototype.updateOutputs = function(param) {
this.updateBones_();
- this.doSkinning_(this.skin);
- var ii;
- // Unlock any buffers that were locked during skinning
- for (ii = 0; ii < this.input_stream_infos_.length; ++ii) {
- this.input_stream_infos_[ii].uninit();
+ if (!this.bone_array_) {
+ this.bone_array_ = new o3d.ParamArray;
+ this.bone_array_.gl = this.gl;
+ var max_num_bones = o3d.SkinEval.getMaxNumBones(this);
+ this.bone_array_.resize(max_num_bones * 3, "ParamFloat4");
+ param.value = this.bone_array_;
}
- for (ii = 0; ii < this.output_stream_infos_.length; ++ii) {
- var output_streams = this.output_stream_infos_[ii];
- for (var jj = 0; jj < output_streams.length; ++jj) {
- output_streams[jj].uninit();
+ var boneArray = this.bone_array_;
+ var ii, jj;
+ if (!this.disableShader && this.skin.updateVertexShader()) {
+ // Is this the first time the param has been read?
+ if (!this.usingSkinShader) {
+ // If so, we disable skinning in software.
+ this.usingSkinShader = 1.0;
+ // Copy the default positions of all vertex buffers.
+ this.initStreams_();
+ var num_vertices = this.skin.influences.length;
+ for (ii = 0; ii < this.input_stream_infos_.length; ++ii) {
+ var input_stream_info = this.input_stream_infos_[ii];
+ var output_streams = this.output_stream_infos_[ii];
+ for (jj = 0; jj < output_streams.length; ++jj) {
+ var values = input_stream_info.field_.getAt(0, num_vertices);
+ output_streams[jj].field_.setAt(0, values);
+ }
+ }
+ this.uninitStreams_();
+ }
+ var row;
+ for (ii = 0; ii < this.bones_.length; ++ii) {
+ var bone = this.bones_[ii];
+ row = boneArray.getParam(ii*3);
+ row.value[0] = bone[0][0];
+ row.value[1] = bone[1][0];
+ row.value[2] = bone[2][0];
+ row.value[3] = bone[3][0];
+ row = boneArray.getParam(ii*3 + 1);
+ row.value[0] = bone[0][1];
+ row.value[1] = bone[1][1];
+ row.value[2] = bone[2][1];
+ row.value[3] = bone[3][1];
+ row = boneArray.getParam(ii*3 + 2);
+ row.value[0] = bone[0][2];
+ row.value[1] = bone[1][2];
+ row.value[2] = bone[2][2];
+ row.value[3] = bone[3][2];
}
}
+ return boneArray;
};
/**
diff --git a/o3d/samples/o3d-webgl/stream.js b/o3d/samples/o3d-webgl/stream.js
index 2008f90..322b137 100644
--- a/o3d/samples/o3d-webgl/stream.js
+++ b/o3d/samples/o3d-webgl/stream.js
@@ -41,7 +41,7 @@ o3d.Stream = function(semantic, semantic_index, field, start_index) {
this.field = field;
this.startIndex = start_index;
};
-o3d.inherit('Stream', 'NamedObject')
+o3d.inherit('Stream', 'NamedObject');
/**
* @type {number}
@@ -56,7 +56,9 @@ o3d.Stream.Semantic = goog.typedef;
* TANGENT,
* BINORMAL,
* COLOR,
- * TEXCOORD
+ * TEXCOORD,
+ * INFLUENCE_WEIGHTS,
+ * INFLUENCE_INDICES
*
* Semantics used when binding buffers to the streambank. They determine how
* the Stream links up to the shader inputs.
@@ -68,6 +70,8 @@ o3d.Stream.TANGENT = 3;
o3d.Stream.BINORMAL = 4;
o3d.Stream.COLOR = 5;
o3d.Stream.TEXCOORD = 6;
+o3d.Stream.INFLUENCE_WEIGHTS = 7;
+o3d.Stream.INFLUENCE_INDICES = 8;
@@ -106,7 +110,7 @@ o3d.Stream.prototype.startIndex = 0;
* and its buffer.
* @private
*/
-o3d.Stream.prototype.getMaxVertices = function() {
+o3d.Stream.prototype.getMaxVertices_ = function() {
var buffer = this.field.buffer;
if (!buffer)
return 0;
diff --git a/o3d/samples/o3d-webgl/vertex_source.js b/o3d/samples/o3d-webgl/vertex_source.js
index 0e9741d..18cccd3 100644
--- a/o3d/samples/o3d-webgl/vertex_source.js
+++ b/o3d/samples/o3d-webgl/vertex_source.js
@@ -62,7 +62,9 @@ o3d.VertexSource.prototype.bindStream = function(
dest_param.stream.field.className &&
source_param.stream.field.numComponents ==
dest_param.stream.field.numComponents) {
- return dest_param.bind(source_param);
+ dest_param.bind(source_param);
+ source.streamWasBound_(this, semantic, semantic_index);
+ return true;
}
}
@@ -100,3 +102,14 @@ o3d.VertexSource.prototype.getVertexStreamParam = function(
o3d.notImplemented();
};
+/**
+ * Used by bindStream. Derived classes may override if needed.
+ *
+ * @param {o3d.VertexSource} dest VertexSource that bound to this VertexSource.
+ * @param {o3d.ParamVertexBufferStream} dest_param Other param which was bound.
+ * @protected
+ */
+o3d.VertexSource.prototype.streamWasBound_ = function(
+ dest, semantic, semantic_index) {
+};
+
diff --git a/o3d/samples/o3djs/effect.js b/o3d/samples/o3djs/effect.js
index dd49595..37c74d2 100644
--- a/o3d/samples/o3djs/effect.js
+++ b/o3d/samples/o3djs/effect.js
@@ -393,10 +393,11 @@ o3djs.effect.setLanguage = function(language) {
* @param {boolean} specular Whether to include stuff for diffuse
* calculations.
* @param {boolean} bumpSampler Whether there is a bump sampler.
+ * @param {boolean} skinning Whether this mesh has a skin.
* @return {string} The code for the declarations.
*/
o3djs.effect.buildAttributeDecls =
- function(material, diffuse, specular, bumpSampler) {
+ function(material, diffuse, specular, bumpSampler, skinning) {
var str = o3djs.effect.BEGIN_IN_STRUCT +
o3djs.effect.ATTRIBUTE + o3djs.effect.FLOAT4 + ' ' + 'position' +
o3djs.effect.semanticSuffix('POSITION') + ';\n';
@@ -404,6 +405,12 @@ o3djs.effect.buildAttributeDecls =
str += o3djs.effect.ATTRIBUTE + o3djs.effect.FLOAT3 + ' ' + 'normal' +
o3djs.effect.semanticSuffix('NORMAL') + ';\n';
}
+ if (skinning) {
+ str += o3djs.effect.ATTRIBUTE + o3djs.effect.FLOAT4 + ' influenceWeights' +
+ o3djs.effect.semanticSuffix('BLENDWEIGHT') + ';\n';
+ str += o3djs.effect.ATTRIBUTE + o3djs.effect.FLOAT4 + ' influenceIndices' +
+ o3djs.effect.semanticSuffix('BLENDINDICES') + ';\n';
+ }
str += o3djs.effect.buildTexCoords(material, false) +
o3djs.effect.buildBumpInputCoords(bumpSampler) +
o3djs.effect.END_STRUCT;
@@ -824,6 +831,21 @@ o3djs.effect.buildStandardShaderString = function(material,
var p = o3djs.effect;
var bumpSampler = material.getParam('bumpSampler');
var bumpUVInterpolant;
+ var skinning;
+
+ var maxSkinInfluences = 4;
+
+ // Hardcode reasonable maximum for number of skinning uniforms.
+ // glsl: Table 6.19: minimum MAX_VERTEX_UNIFORM_VECTORS is 128.
+ // (DX9 requires a minimum of 256, so not a problem in o3d).
+ var maxSkinUniforms = 36 * 3;
+ if (o3djs.base.o3d && o3djs.base.o3d.SkinEval &&
+ o3djs.base.o3d.SkinEval.getMaxNumBones) {
+ maxSkinUniforms = o3d.SkinEval.getMaxNumBones(material) * 3;
+ skinning = true;
+ } else {
+ skinning = false;
+ }
/**
* Extracts the texture type from a texture param.
@@ -892,6 +914,17 @@ o3djs.effect.buildStandardShaderString = function(material,
};
/**
+ * If skinning is enabled, builds the bone matrix uniform variables needed
+ * for skinning. Otherwise, returns the empty string.
+ * @return {string} The effect code for skinning uniforms.
+ */
+ var buildSkinningUniforms = function() {
+ return skinning ? 'uniform ' + p.FLOAT4 + ' boneToWorld3x4' +
+ '[' + maxSkinUniforms + '];\n' +
+ 'uniform float usingSkinShader;\n' : '';
+ };
+
+ /**
* Builds uniform parameters for a given color input. If the material
* has a sampler parameter, a sampler uniform is created, otherwise a
* float4 color value is created.
@@ -953,6 +986,7 @@ o3djs.effect.buildStandardShaderString = function(material,
var buildConstantShaderString = function(material, descriptions) {
descriptions.push('constant');
return buildCommonVertexUniforms() +
+ buildSkinningUniforms() +
buildVertexDecls(material, false, false) +
p.beginVertexShaderMain() +
positionVertexShaderCode() +
@@ -980,6 +1014,7 @@ o3djs.effect.buildStandardShaderString = function(material,
descriptions.push('lambert');
return buildCommonVertexUniforms() +
buildLightingUniforms() +
+ buildSkinningUniforms() +
buildVertexDecls(material, true, false) +
p.beginVertexShaderMain() +
p.buildUVPassthroughs(material) +
@@ -1027,6 +1062,7 @@ o3djs.effect.buildStandardShaderString = function(material,
descriptions.push('phong');
return buildCommonVertexUniforms() +
buildLightingUniforms() +
+ buildSkinningUniforms() +
buildVertexDecls(material, true, true) +
p.beginVertexShaderMain() +
p.buildUVPassthroughs(material) +
@@ -1085,6 +1121,7 @@ o3djs.effect.buildStandardShaderString = function(material,
descriptions.push('phong');
return buildCommonVertexUniforms() +
buildLightingUniforms() +
+ buildSkinningUniforms() +
buildVertexDecls(material, true, true) +
p.beginVertexShaderMain() +
p.buildUVPassthroughs(material) +
@@ -1135,9 +1172,27 @@ o3djs.effect.buildStandardShaderString = function(material,
* @return {string} The code for the vertex shader.
*/
var positionVertexShaderCode = function() {
- return ' ' + p.VERTEX_VARYING_PREFIX + 'position = ' +
- p.mul(p.ATTRIBUTE_PREFIX +
- 'position', 'worldViewProjection') + ';\n';
+ var attribute_position = p.ATTRIBUTE_PREFIX + 'position';
+ if (skinning) {
+ return ' ' + p.FLOAT4 + ' weightedpos = ' + attribute_position + ';\n' +
+ ' for (int i = 0; i < ' + maxSkinInfluences + '; i++) {\n' +
+ ' ' + p.FLOAT4 + ' temp = ' + p.FLOAT4 + '(' +
+ 'dot(boneToWorld3x4[int(influenceIndices[i] * 3.0)], ' +
+ attribute_position + '),\n' +
+ ' dot(boneToWorld3x4[int(influenceIndices[i] * 3.0 + 1.0)], ' +
+ attribute_position + '),\n' +
+ ' dot(boneToWorld3x4[int(influenceIndices[i] * 3.0 + 2.0)], ' +
+ attribute_position + '),\n' +
+ ' 1.0);\n' +
+ ' weightedpos += usingSkinShader * influenceWeights[i] * ' +
+ '(temp - ' + attribute_position + ');\n' +
+ ' }\n' +
+ ' ' + p.VERTEX_VARYING_PREFIX + 'position = ' +
+ p.mul('weightedpos', 'worldViewProjection') + ';\n';
+ } else {
+ return ' ' + p.VERTEX_VARYING_PREFIX + 'position = ' +
+ p.mul(attribute_position, 'worldViewProjection') + ';\n';
+ }
};
/**
@@ -1145,10 +1200,29 @@ o3djs.effect.buildStandardShaderString = function(material,
* @return {string} The code for the vertex shader.
*/
var normalVertexShaderCode = function() {
- return ' ' + p.VERTEX_VARYING_PREFIX + 'normal = ' +
- p.mul(p.FLOAT4 + '(' +
- p.ATTRIBUTE_PREFIX +
- 'normal, 0)', 'worldInverseTranspose') + '.xyz;\n';
+ var attribute_normal = p.ATTRIBUTE_PREFIX + 'normal';
+ if (skinning) {
+ return ' ' + p.FLOAT3 + ' weightednorm = ' + attribute_normal + ';\n' +
+ ' for (int i = 0; i < ' + maxSkinInfluences + '; i++) {\n' +
+ ' ' + p.FLOAT3 + ' temp = ' + p.FLOAT3 + '(' +
+ 'dot(boneToWorld3x4[int(influenceIndices[i] * 3.0)].xyz, ' +
+ attribute_normal + '),\n' +
+ ' dot(boneToWorld3x4[int(influenceIndices[i] * 3.0 + 1.0)].xyz, ' +
+ attribute_normal + '),\n' +
+ ' dot(boneToWorld3x4[int(influenceIndices[i] * 3.0 + 2.0)].xyz, ' +
+ attribute_normal + '));\n' +
+ ' weightednorm += usingSkinShader * influenceWeights[i] * ' +
+ '(temp - ' + attribute_normal + ');\n' +
+ ' }\n' +
+ ' ' + p.VERTEX_VARYING_PREFIX + 'normal = ' +
+ p.mul(p.FLOAT4 + '(' + 'weightednorm' + ', 0)',
+ 'worldInverseTranspose') + '.xyz;\n';
+ } else {
+ return ' ' + p.VERTEX_VARYING_PREFIX + 'normal = ' +
+ p.mul(p.FLOAT4 + '(' +
+ attribute_normal + ', 0)', 'worldInverseTranspose') +
+ '.xyz;\n';
+ }
};
/**
@@ -1228,7 +1302,7 @@ o3djs.effect.buildStandardShaderString = function(material,
*/
var buildVertexDecls = function(material, diffuse, specular) {
return p.buildAttributeDecls(
- material, diffuse, specular, bumpSampler) +
+ material, diffuse, specular, bumpSampler, skinning) +
p.buildVaryingDecls(
material, diffuse, specular, bumpSampler);
};
diff --git a/o3d/samples/o3djs/primitives.js b/o3d/samples/o3djs/primitives.js
index c698d93..f736305 100644
--- a/o3d/samples/o3djs/primitives.js
+++ b/o3d/samples/o3djs/primitives.js
@@ -540,6 +540,9 @@ o3djs.primitives.VertexInfoBase.prototype.createShapeByType = function(
requiredStream.addElement(1, 1, 1, 1);
}
break;
+ case o3djs.base.o3d.Stream.INFLUENCE_WEIGHTS:
+ case o3djs.base.o3d.Stream.INFLUENCE_INDICES:
+ break;
default:
throw 'Missing stream for semantic ' + semantic +
' with semantic index ' + semanticIndex;