diff options
author | petersont@google.com <petersont@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-09 23:48:08 +0000 |
---|---|---|
committer | petersont@google.com <petersont@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-09 23:48:08 +0000 |
commit | 8fee64afe7ba2867255af94eae719f6f6b5ddc33 (patch) | |
tree | 5ab5f082a0fe5f27bdef1b056a024e0384937993 /o3d/samples | |
parent | 2c1ca1a516107745daa0a892875d61944dd791f6 (diff) | |
download | chromium_src-8fee64afe7ba2867255af94eae719f6f6b5ddc33.zip chromium_src-8fee64afe7ba2867255af94eae719f6f6b5ddc33.tar.gz chromium_src-8fee64afe7ba2867255af94eae719f6f6b5ddc33.tar.bz2 |
Implemented picking, at last. The picking sample is already checked in, but to get the sample to draw the right thing required a bunch of ancillary bug fixes, also in this cl.
Review URL: http://codereview.chromium.org/2874008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@52021 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'o3d/samples')
-rw-r--r-- | o3d/samples/o3d-webgl-samples/hellocube-wireframe.html | 51 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/archive_request.js | 3 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/base.js | 36 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/bounding_box.js | 39 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/buffer.js | 54 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/client.js | 161 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/draw_list.js | 13 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/element.js | 11 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/object_base.js | 7 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/pack.js | 4 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/param.js | 31 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/param_object.js | 6 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/primitive.js | 304 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/shape.js | 28 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/state.js | 531 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/state_set.js | 15 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/stream_bank.js | 36 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/transform.js | 3 | ||||
-rw-r--r-- | o3d/samples/o3djs/event.js | 2 |
19 files changed, 983 insertions, 352 deletions
diff --git a/o3d/samples/o3d-webgl-samples/hellocube-wireframe.html b/o3d/samples/o3d-webgl-samples/hellocube-wireframe.html index 86eae19..48040b0 100644 --- a/o3d/samples/o3d-webgl-samples/hellocube-wireframe.html +++ b/o3d/samples/o3d-webgl-samples/hellocube-wireframe.html @@ -64,12 +64,13 @@ var g_clock = 0; var g_timeMult = 1; var g_cubeTransform; var g_cubePrimitive; +var g_material; var g_finished = false; // for selenium testing /** * Changes the primitive type of the displayed cube. Some primitive types will * only draw a partial cube. - * @param {string} type the desired primitive type + * @param {string} type The desired primitive type. */ function changePrimitive(type) { var indicesArray; @@ -151,6 +152,30 @@ function changePrimitive(type) { g_cubePrimitive.indexBuffer.set(indicesArray); } + +/** + * Changes the fill mode on the state associated with the cube material. + * @param {string} type The desired fill mode. + */ +function changeFillMode(mode_string) { + var mode = o3d.State.SOLID; + switch (mode_string) { + case 'Point': + mode = o3d.State.POINT; + break; + case 'Wireframe': + mode = o3d.State.WIREFRAME; + break; + case 'Solid': + default: + mode = o3d.State.SOLID; + break; + } + + g_material.state.getStateParam('FillMode').value = mode; +} + + /** * Creates an O3D shape representing a cube. The shape consists of * a single primitive with eight vertices. @@ -277,10 +302,12 @@ function initStep2(clientElements) { myEffect.loadPixelShaderFromString(pixelShaderString); // Create a Material for the mesh. - var myMaterial = g_pack.createObject('Material'); + g_material = g_pack.createObject('Material'); // Set the material's drawList. - myMaterial.drawList = viewInfo.performanceDrawList; + g_material.drawList = viewInfo.performanceDrawList; + + g_material.state = g_pack.createObject('State'); // Turn off culling. viewInfo.performanceDrawPassInfo.state.getStateParam('CullMode').value = @@ -288,13 +315,14 @@ function initStep2(clientElements) { // Apply our effect to this material. The effect tells the 3D hardware // which shaders to use. - myMaterial.effect = myEffect; + g_material.effect = myEffect; // Create the Shape for the cube mesh and assign its material. - var cubeShape = createCube(myMaterial); + var cubeShape = createCube(g_material); // Render with line lists to begin with. changePrimitive('LineList'); + changeFillMode('Solid'); // Create a new transform and parent the Shape under it. g_cubeTransform = g_pack.createObject('Transform'); @@ -332,7 +360,7 @@ This example shows how to display a cube in O3D using different primitive types. <form name="default_form" action="#" method="get"> <p> - Change type: + Primitive type: <select onchange="changePrimitive(this.options[this.selectedIndex].value);"> <option value="PointList">PointList</option> <option value="LineList" selected>LineList</option> @@ -340,7 +368,16 @@ This example shows how to display a cube in O3D using different primitive types. <option value="TriangleList">TriangleList</option> <option value="TriangleStrip">TriangleStrip</option> <option value="TriangleFan">TriangleFan</option> - </select> (PointList may be hard to see.) + </select> (PointList might be hard to see.) + </p> + + <p> + Fill mode: + <select onchange="changeFillMode(this.options[this.selectedIndex].value);"> + <option value="Point">Point</option> + <option value="Wireframe">Wireframe</option> + <option value="Solid" selected>Solid</option> + </select> </p> </form> diff --git a/o3d/samples/o3d-webgl/archive_request.js b/o3d/samples/o3d-webgl/archive_request.js index aa45ec6..ca2c95e 100644 --- a/o3d/samples/o3d-webgl/archive_request.js +++ b/o3d/samples/o3d-webgl/archive_request.js @@ -254,8 +254,7 @@ o3d.ArchiveRequest.prototype.stringEndsWith_ = function(string, suffix) { * if one is provided, calls onreadystatechange if this is the last of the * requests. * @param {o3d.RawData} rawData The current raw data object. - * @param {function(!o3d.RawData): void} An optional callback to call passing - * the current raw data as an argument. + * @param {Object} opt_exc An optional exception. * @private */ o3d.ArchiveRequest.prototype.resolvePendingRequest_ = diff --git a/o3d/samples/o3d-webgl/base.js b/o3d/samples/o3d-webgl/base.js index d5e01cc..ba23128 100644 --- a/o3d/samples/o3d-webgl/base.js +++ b/o3d/samples/o3d-webgl/base.js @@ -176,7 +176,40 @@ o3d.removeFromArray = function(array, object) { if (i >= 0) { array.splice(i, 1); } -} +}; + + +/** + * Determine whether a value is an array. Do not use instanceof because that + * will not work for V8 arrays (the browser thinks they are Objects). + * @param {*} value A value. + * @return {boolean} Whether the value is an array. + */ +o3d.isArray_ = function(value) { + var valueAsObject = /** @type {!Object} **/ (value); + return typeof(value) === 'object' && value !== null && + 'length' in valueAsObject && 'splice' in valueAsObject; +}; + + +/** + * Utility function to clone an object. + * + * @param {Object} object The object to clone. + * @return {Object} A clone of that object. + */ +o3d.clone = function(object) { + var result = o3d.isArray_(object) ? [] : {}; + for (var name in object) { + var property = object[name]; + if (typeof property == 'Object') { + result[name] = o3d.clone(property); + } else { + result[name] = property; + } + } + return result; +}; /** @@ -219,6 +252,7 @@ o3d.include('render_surface_set'); o3d.include('render_surface'); o3d.include('state'); o3d.include('draw_context'); +o3d.include('ray_intersection_info'); o3d.include('sampler'); o3d.include('transform'); o3d.include('pack'); diff --git a/o3d/samples/o3d-webgl/bounding_box.js b/o3d/samples/o3d-webgl/bounding_box.js index ebd7d09..a3da9aa 100644 --- a/o3d/samples/o3d-webgl/bounding_box.js +++ b/o3d/samples/o3d-webgl/bounding_box.js @@ -39,14 +39,16 @@ o3d.BoundingBox = function(opt_minExtent, opt_maxExtent) { o3d.ParamObject.call(this); - if (!opt_minExtent) { - opt_minExtent = [0, 0, 0]; - } - if (!opt_maxExtent) { - opt_maxExtent = [0, 0, 0]; + var minExtent = opt_minExtent || [0, 0, 0]; + var maxExtent = opt_maxExtent || [0, 0, 0]; + + this.minExtent = [minExtent[0], minExtent[1], minExtent[2]]; + this.maxExtent = [maxExtent[0], maxExtent[1], maxExtent[2]]; + + // If there were extents passed in, that validates the box. + if (opt_minExtent && opt_maxExtent) { + this.valid = true; } - this.minExtent = [opt_minExtent[0], opt_minExtent[1], opt_minExtent[2]]; - this.maxExtent = [opt_maxExtent[0], opt_maxExtent[1], opt_maxExtent[2]]; }; o3d.inherit('BoundingBox', 'ParamObject'); @@ -89,6 +91,7 @@ o3d.BoundingBox.fitBoxToPoints_ = function(points, opt_targetBox) { target.maxExtent[index] = Math.max(target.maxExtent[index], point[index]); } } + target.valid = true; return target; }; @@ -170,7 +173,7 @@ o3d.BoundingBox.prototype.intersectRay = end = [arguments[3], arguments[4], arguments[5]]; } - var result = new RayIntersectionInfo; + var result = new o3d.RayIntersectionInfo; if (this.valid) { result.valid = true; @@ -181,7 +184,7 @@ o3d.BoundingBox.prototype.intersectRay = var kLeft = 1; var kMiddle = 2; - var dir = [end[0] - start[0], end[1] - start[1], end[2] - start[2]]; + var direction = [end[0] - start[0], end[1] - start[1], end[2] - start[2]]; var coord = [0, 0, 0]; var inside = true; @@ -200,13 +203,13 @@ o3d.BoundingBox.prototype.intersectRay = // Find candidate planes; this loop can be avoided if rays cast all from // the eye (assumes perpsective view). for (var i = 0; i < kNumberOfDimensions; ++i) { - if (start[i] < min_extent_[i]) { + if (start[i] < this.minExtent[i]) { quadrant[i] = kLeft; - candidate_plane[i] = min_extent_[i]; + candidate_plane[i] = this.minExtent[i]; inside = false; - } else if (start[i] > max_extent_[i]) { + } else if (start[i] > this.maxExtent[i]) { quadrant[i] = kRight; - candidate_plane[i] = max_extent_[i]; + candidate_plane[i] = this.maxExtent[i]; inside = false; } else { quadrant[i] = kMiddle; @@ -215,13 +218,13 @@ o3d.BoundingBox.prototype.intersectRay = // Ray origin inside bounding box. if (inside) { - result.position = start; + result.position = start; result.inside = true; } else { // Calculate T distances to candidate planes. for (var i = 0; i < kNumberOfDimensions; ++i) { - if (quadrant[i] != kMiddle && dir[i] != 0.0) { - max_t[i] = (candidate_plane[i] - start[i]) / dir[i]; + if (quadrant[i] != kMiddle && direction[i] != 0.0) { + max_t[i] = (candidate_plane[i] - start[i]) / direction[i]; } else { max_t[i] = -1.0; } @@ -241,8 +244,8 @@ o3d.BoundingBox.prototype.intersectRay = } else { for (var i = 0; i < kNumberOfDimensions; ++i) { if (which_plane != i) { - coord[i] = start[i] + max_t[which_plane] * dir[i]; - if (coord[i] < min_extent_[i] || coord[i] > max_extent_[i]) { + coord[i] = start[i] + max_t[which_plane] * direction[i]; + if (coord[i] < this.minExtent[i] || coord[i] > this.maxExtent[i]) { result.intersected = false; break; } diff --git a/o3d/samples/o3d-webgl/buffer.js b/o3d/samples/o3d-webgl/buffer.js index d07583f..6cfeb50 100644 --- a/o3d/samples/o3d-webgl/buffer.js +++ b/o3d/samples/o3d-webgl/buffer.js @@ -37,17 +37,17 @@ * @constructor */ o3d.Buffer = function() { - this.fields_ = []; + this.fields = []; this.array_ = null; }; o3d.inherit('Buffer', 'NamedObject'); /** - * A private array to hold the fields. + * The fields currently set on the buffer. * @type {!Array.<o3d.Field>} */ -o3d.Buffer.prototype.fields_ = []; +o3d.Buffer.prototype.fields = []; /** @@ -75,6 +75,19 @@ o3d.Buffer.prototype.__defineGetter__('numElements', ); /** + * Computes and stores the correct total components from the + * fields so far. + */ +o3d.Buffer.prototype.updateTotalComponents_ = function() { + var total = 0; + for (var i = 0; i < this.fields.length; ++i) { + this.fields[i].offset_ = total; + total += this.fields[i].numComponents; + } + this.totalComponents = total; +}; + +/** * Allocates memory for the data to be stored in the buffer based on * the types of fields set on the buffer. * @@ -83,13 +96,7 @@ o3d.Buffer.prototype.__defineGetter__('numElements', */ o3d.Buffer.prototype.allocateElements = function(numElements) { - var total = 0; - for (var i = 0; i < this.fields_.length; ++i) { - this.fields_[i].offset_ = total; - total += this.fields_[i].numComponents; - } - this.totalComponents = total; - + this.updateTotalComponents_(); this.resize(numElements * this.totalComponents); }; @@ -126,8 +133,8 @@ o3d.Buffer.prototype.createField = // Make copies of the existing field data. if (alreadyAllocated) { - for (var i = 0; i < this.fields_.length; i++) { - savedData[i] = this.fields_[i].getAt(0, numElements); + for (var i = 0; i < this.fields.length; i++) { + savedData[i] = this.fields[i].getAt(0, numElements); } } @@ -136,15 +143,16 @@ o3d.Buffer.prototype.createField = f.buffer = this; f.numComponents = numComponents; f.size = numComponents * (fieldType=='UByteNField' ? 1 : 4); - this.fields_.push(f); + this.fields.push(f); + this.updateTotalComponents_(); // Resize the buffer with the new field, and replace data. if (alreadyAllocated) { this.allocateElements(numElements); - for (var i = 0; i < this.fields_.length; i++) { + for (var i = 0; i < this.fields.length; i++) { var fieldData = savedData[i]; if (fieldData) { - this.fields_[i].setAt(0, fieldData); + this.fields[i].setAt(0, fieldData); } } } @@ -164,18 +172,10 @@ o3d.Buffer.prototype.createField = */ o3d.Buffer.prototype.removeField = function(field) { - // TODO(luchen): Removing fields does not require a reshuffling, but may want - // to do it anyways to save space. - var i = 0; - for (var j = 0; j < this.fields_.length; ++j) { - if (this.fields_[i] == field) - j++; - this.fields_[j] = this.fields_[i]; - i++; - } - if (this.fields_.length > i) { - this.fields_.pop(); - } + o3d.removeFromArray(this.fields, field); + // TODO(petersont): Have this function actually shuffle the buffer around to + // remove the field properly. + this.updateTotalComponents_(); }; diff --git a/o3d/samples/o3d-webgl/client.js b/o3d/samples/o3d-webgl/client.js index 869320b..877bee4 100644 --- a/o3d/samples/o3d-webgl/client.js +++ b/o3d/samples/o3d-webgl/client.js @@ -225,6 +225,20 @@ o3d.Client = function() { o3d.Renderer.installRenderInterval(); o3d.Renderer.clients_.push(this); + + /** + * Stack of objects showing how the state has changed. + * @type {!Array.<!Object>} + * @private + */ + this.stateMapStack_ = []; + + /** + * An object containing an entry for each state variable that has changed. + * @type {Object} + * @private + */ + this.stateVariableStacks_ = {}; }; o3d.inherit('Client', 'NamedObject'); @@ -426,6 +440,8 @@ o3d.Client.prototype.render = function() { // Synthesize a render event. var render_event = new o3d.RenderEvent; + this.clearStateStack_(); + var now = (new Date()).getTime() * 0.001; if(this.then_ == 0.0) render_event.elapsedTime = 0.0; @@ -611,7 +627,9 @@ o3d.Client.prototype.initWithCanvas = function(canvas) { if (!canvas || !canvas.getContext) { return false; } - try {gl = canvas.getContext("experimental-webgl", standard_attributes) } catch(e) { } + try { + gl = canvas.getContext("experimental-webgl", standard_attributes) + } catch(e) { } if (!gl) { try { gl = canvas.getContext("moz-webgl") @@ -632,6 +650,7 @@ o3d.Client.prototype.initWithCanvas = function(canvas) { gl.client = this; gl.displayInfo = {width: canvas.width, height: canvas.height}; + o3d.State.createDefaultState_(gl).push_(); return true; }; @@ -762,78 +781,34 @@ o3d.Client.getEventInfo_ = function(event) { /** - * Returns the absolute position of an element for certain browsers. + * Returns the absolute position of an element. * @param {!HTMLElement} element The element to get a position for. * @return {!Object} An object containing x and y as the absolute position * of the given element. * @private */ o3d.Client.getAbsolutePosition_ = function(element) { - var r = { x: element.offsetLeft, y: element.offsetTop }; - if (element.offsetParent) { - var tmp = o3d.Client.getAbsolutePosition_(element.offsetParent); - r.x += tmp.x; - r.y += tmp.y; + var r = {x: 0, y: 0}; + for (var e = element; e; e = e.offsetParent) { + r.x += e.offsetLeft; + r.y += e.offsetTop; } return r; }; /** - * Retrieve the coordinates of the given event relative to the center - * of the widget. + * Compute the x and y of an event with respect to the element which received + * the event. * * @param {!Object} eventInfo As returned from * o3d.Client.getEventInfo. - * @param {HTMLElement} opt_reference A DOM element whose position we want - * to transform the mouse coordinates to. If it is not passed in the - * element in the eventInfo will be used. * @return {!Object} An object containing keys 'x' and 'y'. * @private */ -o3d.Client.getRelativeCoordinates_ = function(eventInfo, opt_reference) { - var x, y; +o3d.Client.getLocalXY_ = function(eventInfo) { var event = eventInfo.event; - var element = eventInfo.element; - var reference = opt_reference || eventInfo.element; - if (!window.opera && typeof event.offsetX != 'undefined') { - // Use offset coordinates and find common offsetParent - var pos = { x: event.offsetX, y: event.offsetY }; - // Send the coordinates upwards through the offsetParent chain. - var e = element; - while (e) { - e.mouseX_ = pos.x; - e.mouseY_ = pos.y; - pos.x += e.offsetLeft; - pos.y += e.offsetTop; - e = e.offsetParent; - } - // Look for the coordinates starting from the reference element. - var e = reference; - var offset = { x: 0, y: 0 } - while (e) { - if (typeof e.mouseX_ != 'undefined') { - x = e.mouseX_ - offset.x; - y = e.mouseY_ - offset.y; - break; - } - offset.x += e.offsetLeft; - offset.y += e.offsetTop; - e = e.offsetParent; - } - // Reset stored coordinates - e = element; - while (e) { - e.mouseX_ = undefined; - e.mouseY_ = undefined; - e = e.offsetParent; - } - } else { - // Use absolute coordinates - var pos = o3d.Client.getAbsolutePosition_(reference); - x = event.pageX - pos.x; - y = event.pageY - pos.y; - } - return { x: x, y: y }; + var p = o3d.Client.getAbsolutePosition_(eventInfo.element); + return {x: event.x - p.x, y: event.y - p.y}; }; @@ -849,7 +824,9 @@ o3d.Client.wrapEventCallback_ = function(handler, doCancelEvent) { return function(event) { event = o3d.Client.getEvent_(event); var info = o3d.Client.getEventInfo_(event); - var relativeCoords = o3d.Client.getRelativeCoordinates_(info); + var relativeCoords = o3d.Client.getLocalXY_(info); + // In a proper event, there are read only properties, so we clone it. + event = o3d.clone(event); event.x = relativeCoords.x; event.y = relativeCoords.y; // Invert value to meet contract for deltaY. @see event.js. @@ -1041,6 +1018,78 @@ o3d.Client.prototype.toDataURL = /** + * Saves data needed to return state to current, then sets the state according + * to the given object. + * @private + */ +o3d.Client.prototype.clearStateStack_ = function() { + this.stateMapStack_ = []; + for (var name in this.stateVariableStacks_) { + var l = this.stateVariableStacks_[name]; + // Recall there is a default value at the bottom of each of these stacks. + if (l.length != 1) { + this.stateVariableStacks_[name] = l.slice(0, 1); + } + } +}; + + +/** + * Saves data needed to return state to current, then sets the state according + * to the given object. + * @param {Object} variable_map A map linking names to values. + * @private + */ +o3d.Client.prototype.pushState_ = function(variable_map) { + // Save the variable map itself in a stack. + this.stateMapStack_.push(variable_map); + + // Save each of the state's variable's value in its own stack. + for (var name in variable_map) { + var value = variable_map[name]; + if (this.stateVariableStacks_[name] == undefined) { + this.stateVariableStacks_[name] = []; + } + this.stateVariableStacks_[name].push(value); + } + + // The value on the top of the stack in stateVariableStacks_ + // is current at this point. + o3d.State.setVariables_(this.gl, variable_map); +}; + + +/** + * Returns the state to the way it was before the current state + * @private + */ +o3d.Client.prototype.popState_ = function() { + var variable_map = this.stateMapStack_.pop(); + + for (var name in variable_map) { + var stack = this.stateVariableStacks_[name]; + stack.pop(); + variable_map[name] = stack.length ? stack[stack.length - 1] : + o3d.State.stateVariableInfos_[name]['defaultValue']; + } + + o3d.State.setVariables_(this.gl, variable_map); +}; + + +/** + * Gets the current value of the state variable with the give name. + * @param {string} name The name of the state variable in question. + * @private + */ +o3d.Client.prototype.getState_ = function(name) { + var stack = this.stateVariableStacks_[name]; + return stack.length ? stack[stack.length - 1] : + o3d.State.stateVariableInfos_[name]['defaultValue']; +}; + + +/** * Returns the status of initializing the renderer so we can display the * appropriate message. We require a certain minimum set of graphics * capabilities. If the user's computer does not have his minimum diff --git a/o3d/samples/o3d-webgl/draw_list.js b/o3d/samples/o3d-webgl/draw_list.js index a9fa9ee..2c79e12 100644 --- a/o3d/samples/o3d-webgl/draw_list.js +++ b/o3d/samples/o3d-webgl/draw_list.js @@ -127,9 +127,8 @@ o3d.DrawList.prototype.render = function() { var projection = drawElementInfo.projection; var transform = drawElementInfo.transform; var drawElement = drawElementInfo.drawElement; - var element = drawElementInfo.drawElement.owner; - var material = drawElementInfo.drawElement.material || - drawElementInfo.drawElement.owner.material; + var element = drawElement.owner; + var material = drawElement.material || element.material; var effect = material.effect; o3d.Param.SAS.setWorld(world); @@ -148,7 +147,15 @@ o3d.DrawList.prototype.render = function() { ]; effect.searchForParams_(paramObjects); + + var state_on = (material.state != undefined); + if (state_on) { + material.state.push_(); + } element.render(); + if (state_on) { + material.state.pop_(); + } } }; diff --git a/o3d/samples/o3d-webgl/element.js b/o3d/samples/o3d-webgl/element.js index d190780..bf9c6c9 100644 --- a/o3d/samples/o3d-webgl/element.js +++ b/o3d/samples/o3d-webgl/element.js @@ -147,17 +147,18 @@ o3d.Element.prototype.__defineGetter__('owner', * create more than one element for the same material. * * @param {!o3d.Pack} pack pack used to manage created DrawElement. - * @param {!o3d.Material} material material to use for DrawElement. If you - * pass null it will use the material on this Element. This allows you - * to easily setup the default (just draw as is) by passing null or - * setup a shadow pass by passing in a shadow material. + * @param {!o3d.Material} material material to use for DrawElement. + * Note: When a DrawElement with a material of null is rendered, the + * material on the corresponding Element will get used instead. + * This allows you to easily setup the default (just draw as is) by passing + * null or setup a shadow pass by passing in a shadow material. * @return {!o3d.DrawElement} The created draw element. */ o3d.Element.prototype.createDrawElement = function(pack, material) { drawElement = pack.createObject('DrawElement'); drawElement.owner = this; - drawElement.material = material || this.material; + drawElement.material = material; this.drawElements.push(drawElement); return drawElement; }; diff --git a/o3d/samples/o3d-webgl/object_base.js b/o3d/samples/o3d-webgl/object_base.js index a0f81b9..feb97bc 100644 --- a/o3d/samples/o3d-webgl/object_base.js +++ b/o3d/samples/o3d-webgl/object_base.js @@ -54,13 +54,14 @@ o3d.ObjectBase.prototype.superClass = null; /** * Traverses the current object's class and all its superclasses and * determines if any of them are of the given name. - * @param {string} className The name of a class. + * @param {string} class_type_name The name of a class. * @return {boolean} Whether this is counts as a className. */ -o3d.ObjectBase.prototype.isAClassName = function(className) { +o3d.ObjectBase.prototype.isAClassName = function(class_type_name) { + class_type_name = o3d.filterTypeName_(class_type_name); var object = this; while (object != undefined) { - if (object.className == className) { + if (object.className == class_type_name) { return true; } object = object.superClass && object.superClass.prototype; diff --git a/o3d/samples/o3d-webgl/pack.js b/o3d/samples/o3d-webgl/pack.js index 987fb9a..a168820 100644 --- a/o3d/samples/o3d-webgl/pack.js +++ b/o3d/samples/o3d-webgl/pack.js @@ -294,9 +294,7 @@ o3d.Pack.prototype.getObjects = */ o3d.Pack.prototype.getObjectsByClassName = function(class_type_name) { - if (class_type_name.substr(0, 4) == 'o3d.') { - class_type_name = class_type_name.substr(4); - } + class_type_name = o3d.filterTypeName_(class_type_name); var found = []; diff --git a/o3d/samples/o3d-webgl/param.js b/o3d/samples/o3d-webgl/param.js index a7b3e6f..59c0219 100644 --- a/o3d/samples/o3d-webgl/param.js +++ b/o3d/samples/o3d-webgl/param.js @@ -767,8 +767,29 @@ o3d.WorldViewProjectionInverseTransposeParamMatrix4 = function() { o3d.inherit('WorldViewProjectionInverseTransposeParamMatrix4', 'CompositionParamMatrix4'); + +/** + * Called to specify the value of a uniform variable. + * @param {WebGLContext} gl The current context. + * @param {WebGLUniformLocation} location The location to which to apply. + */ +o3d.ParamInteger.prototype.applyToLocation = function(gl, location) { + gl.uniform1i(location, this.value); +}; + +/** + * Called to specify the value of a uniform variable. + * @param {WebGLContext} gl The current context. + * @param {WebGLUniformLocation} location The location to which to apply. + */ +o3d.ParamBoolean.prototype.applyToLocation = function(gl, location) { + gl.uniform1i(location, this.value); +}; + /** * Called to specify the value of a uniform variable. + * @param {WebGLContext} gl The current context. + * @param {WebGLUniformLocation} location The location to which to apply. */ o3d.ParamFloat.prototype.applyToLocation = function(gl, location) { gl.uniform1f(location, this.value); @@ -776,6 +797,8 @@ o3d.ParamFloat.prototype.applyToLocation = function(gl, location) { /** * Called to specify the value of a uniform variable. + * @param {WebGLContext} gl The current context. + * @param {WebGLUniformLocation} location The location to which to apply. */ o3d.ParamFloat2.prototype.applyToLocation = function(gl, location) { gl.uniform2fv(location, this.value); @@ -783,6 +806,8 @@ o3d.ParamFloat2.prototype.applyToLocation = function(gl, location) { /** * Called to specify the value of a uniform variable. + * @param {WebGLContext} gl The current context. + * @param {WebGLUniformLocation} location The location to which to apply. */ o3d.ParamFloat3.prototype.applyToLocation = function(gl, location) { gl.uniform3fv(location, this.value); @@ -790,6 +815,8 @@ o3d.ParamFloat3.prototype.applyToLocation = function(gl, location) { /** * Called to specify the value of a uniform variable. + * @param {WebGLContext} gl The current context. + * @param {WebGLUniformLocation} location The location to which to apply. */ o3d.ParamFloat4.prototype.applyToLocation = function(gl, location) { gl.uniform4fv(location, this.value); @@ -797,6 +824,8 @@ o3d.ParamFloat4.prototype.applyToLocation = function(gl, location) { /** * Called to specify the value of a uniform variable. + * @param {WebGLContext} gl The current context. + * @param {WebGLUniformLocation} location The location to which to apply. */ o3d.ParamMatrix4.prototype.applyToLocation = function(gl, location) { gl.uniformMatrix4fv(location, @@ -812,6 +841,8 @@ o3d.Param.texture_index_ = 0; /** * Called to specify the value of a uniform variable. + * @param {WebGLContext} gl The current context. + * @param {WebGLUniformLocation} location The location to which to apply. */ o3d.ParamSampler.prototype.applyToLocation = function(gl, location) { // When before the effect object assigns values to parameters, diff --git a/o3d/samples/o3d-webgl/param_object.js b/o3d/samples/o3d-webgl/param_object.js index 253acde..78ee725 100644 --- a/o3d/samples/o3d-webgl/param_object.js +++ b/o3d/samples/o3d-webgl/param_object.js @@ -197,8 +197,10 @@ o3d.ParamObject.prototype.params_ = {}; */ o3d.ParamObject.prototype.copyParams = function(source_param_object) { - for (param in source_param_object.params_) { - this.createParam(param.name, param.className); + for (name in source_param_object.params_) { + var param = source_param_object.params_[name]; + this.createParam(name, param.className); + this.getParam(name).value = param.value; } }; diff --git a/o3d/samples/o3d-webgl/primitive.js b/o3d/samples/o3d-webgl/primitive.js index 5e8bcf3..d9f7bde 100644 --- a/o3d/samples/o3d-webgl/primitive.js +++ b/o3d/samples/o3d-webgl/primitive.js @@ -40,6 +40,13 @@ */ o3d.Primitive = function(opt_streamBank) { o3d.Element.call(this); + + /** + * The index buffer for the primitive. If null the primitive is non-indexed. + * @type {o3d.IndexBuffer} + */ + this.indexBuffer = null; + /** * The stream bank this primitive uses for vertices. * @type {o3d.StreamBank} @@ -78,11 +85,11 @@ o3d.Primitive = function(opt_streamBank) { this.startIndex = 0; /** - * The index buffer for the primitive. If null the primitive is non-indexed. + * The index buffer for the wireframe version of the primitive. * @type {o3d.IndexBuffer} + * @private */ - this.indexBuffer = null; - + this.wireframeIndexBuffer_ = null; }; o3d.inherit('Primitive', 'Element'); @@ -114,9 +121,9 @@ o3d.Primitive.prototype.render = function() { var enabled_attribs = []; for (var semantic = 0; - semantic < streamBank.vertexStreams.length; + semantic < streamBank.vertex_streams_.length; ++semantic) { - var streams = streamBank.vertexStreams[semantic]; + var streams = streamBank.vertex_streams_[semantic]; if (streams && streams.length) { for (var semantic_index = 0; semantic_index < streams.length; @@ -178,10 +185,43 @@ o3d.Primitive.prototype.render = function() { break; } + var use_wireframe_indices = false; + + if (this.gl.fillMode_ == o3d.State.POINT) { + // If the fill mode is points, then we just replace the gl primitive type + // with POINTS and let the (possibly redundant) list of points draw. + glMode = this.gl.POINTS; + } else if (this.gl.fillMode_ == o3d.State.WIREFRAME) { + // If the fill mode is lines, and the primitive type is some kind of + // triangle, then we need to reorder indices to draw the right thing. + + if (this.primitiveType == o3d.Primitive.TRIANGLELIST || + this.primitiveType == o3d.Primitive.TRIANGLEFAN || + this.primitiveType == o3d.Primitive.TRIANGLESTRIP) { + use_wireframe_indices = true; + glMode = this.gl.LINES; + this.computeWireframeIndices_(); + } + } + + if (use_wireframe_indices) { + indexBuffer = this.wireframeIndexBuffer_; + + switch(this.primitiveType) { + default: + case o3d.Primitive.TRIANGLELIST: + glNumElements = this.numberPrimitives * 6; + break; + case o3d.Primitive.TRIANGLESTRIP: + case o3d.Primitive.TRIANGLEFAN: + glNumElements = (this.numberPrimitives == 0) ? 0 : + this.numberPrimitives * 4 + 2; + break; + } + } + if (!indexBuffer) { - this.gl.drawArrays(glMode, - 0, - glNumElements); + this.gl.drawArrays(glMode, 0, glNumElements); } else { this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, indexBuffer.gl_buffer_); this.gl.drawElements(glMode, @@ -195,6 +235,252 @@ o3d.Primitive.prototype.render = function() { } }; + +/** + * Generates an index buffer for a wireframe outline of a triangle-based + * primitive. + * @private + */ +o3d.Primitive.prototype.computeWireframeIndices_ = function() { + this.wireframeIndexBuffer_ = new o3d.IndexBuffer; + this.wireframeIndexBuffer_.gl = this.gl; + var indices = this.indexBuffer.array_; + + // The current index in wireframeIndices. + var j = 0; + + switch (this.primitiveType) { + default: + case o3d.Primitive.TRIANGLELIST: { + this.wireframeIndexBuffer_.resize(2 * indices.length); + var wireframeIndices = this.wireframeIndexBuffer_.array_; + this.wireframeIndexBuffer_.lock(); + // Iterate through triangles. + for (var i = 0; i < indices.length / 3; ++i) { + // Indices the vertices of the triangle a, b, c. + var a = indices[3 * i]; + var b = indices[3 * i + 1]; + var c = indices[3 * i + 2]; + wireframeIndices[j++] = a; + wireframeIndices[j++] = b; + wireframeIndices[j++] = b; + wireframeIndices[j++] = c; + wireframeIndices[j++] = c; + wireframeIndices[j++] = a; + } + this.wireframeIndexBuffer_.unlock(); + } + break; + + case o3d.Primitive.TRIANGLEFAN: { + this.wireframeIndexBuffer_.resize(Math.max(0, 4 * indices.length - 4)); + var wireframeIndices = this.wireframeIndexBuffer_.array_; + this.wireframeIndexBuffer_.lock(); + // The first two points make a line. + var z; + if (indices.length > 1) { + z = indices[0]; + wireframeIndices[j++] = z; + wireframeIndices[j++] = indices[1]; + } + // Each additional point forms a new triangle by adding two lines. + for (var i = 2; i < indices.length; ++i) { + var a = indices[i]; + wireframeIndices[j++] = z; + wireframeIndices[j++] = a; + wireframeIndices[j++] = a; + wireframeIndices[j++] = indices[i - 1]; + } + this.wireframeIndexBuffer_.unlock(); + } + break; + + case o3d.Primitive.TRIANGLESTRIP: { + this.wireframeIndexBuffer_.resize(Math.max(0, 4 * indices.length - 4)); + var wireframeIndices = this.wireframeIndexBuffer_.array_; + this.wireframeIndexBuffer_.lock(); + // The frist two points make a line. + var a; + var b; + if (indices.length > 1) { + a = indices[0]; + b = indices[1]; + wireframeIndices[j++] = a; + wireframeIndices[j++] = b; + } + // Each additional point forms a new triangle by adding two lines. + for (var i = 2; i < indices.length; ++i) { + var c = indices[i]; + wireframeIndices[j++] = b; + wireframeIndices[j++] = c; + wireframeIndices[j++] = c; + wireframeIndices[j++] = a; + a = b; + b = c; + } + this.wireframeIndexBuffer_.unlock(); + } + break; + } +}; + + +/** + * Computes the intersection of a ray in the coordinate system of + * the specified POSITION stream. + * @param {number} position_stream_index Index of POSITION stream. + * @param {o3d.Cull} cull which side of the triangles to ignore. + * @param {!o3d.math.Point3} start position of start of ray in local space. + * @param {!o3d.math.Point3} end position of end of ray. in local space. + * @return {!o3d.RayIntersectionInfo} RayIntersectionInfo class. If valid() + * is false then something was wrong, Check GetLastError(). If + * intersected() is true then the ray intersected a something. position() + * is the exact point of intersection. + */ +o3d.Primitive.prototype.intersectRay = + function(position_stream_index, cull, start, end) { + var result = new o3d.RayIntersectionInfo; + result.valid = true; + + var streamBank = this.streamBank; + var indexBuffer = this.indexBuffer; + var stream = + this.streamBank.vertex_streams_[o3d.Stream.POSITION][position_stream_index]; + + var field = stream.field; + var buffer = field.buffer; + var numPoints = buffer.array_.length / buffer.totalComponents; + var elements = field.getAt(0, numPoints); + + // The direction of the vector of the ray. + var x = end[0] - start[0]; + var y = end[1] - start[1]; + var z = end[2] - start[2]; + + // Find two vectors orthogonal to direction for use in quickly eliminating + // triangles which can't possibly intersect the ray. + var direction = [x, y, z]; + + // Pick a vector orthogonal to direction called u. + var ux = -y; + var uy = x; + var uz = 0; + if (x * x + y * y < z * z) { + ux = -z; + uy = 0; + uz = x; + } + + // Cross product direction and u get a third orthogonal vector v. + var vx = y * uz - z * uy; + var vy = z * ux - x * uz; + var vz = x * uy - y * ux; + + var udotstart = ux * start[0] + uy * start[1] + uz * start[2]; + var vdotstart = vx * start[0] + vy * start[1] + vz * start[2]; + + // As we search for an intersection point, we keep track of how far out + // from the start the point with this variable. + var min_distance = 0; + + // Iterate through the indices three at a time. Each triple of indices + // defines a triangle. For each triangle, we test for intersection with + // the ray. We need to find the closest one to start, so we have to + // check them all. + var a = indexBuffer.array_; + for (var i = 0; i < a.length / 3; ++i) { + // Indices of the triangle. + var t = 3 * i; + var indices = [a[t], a[t + 1], a[t + 2]]; + + // Check if the current triangle is too far to one side of the ray + // to intersect at all. (This is what the orthogonal vectors are for) + var u_sides = [false, false, false]; + var v_sides = [false, false, false]; + for (var j = 0; j < 3; ++j) { + var t = 3 * indices[j]; + var r = elements.slice(t, t + 3); + u_sides[j] = ux * r[0] + uy * r[1] + uz * r[2] - udotstart > 0; + v_sides[j] = vx * r[0] + vy * r[1] + vz * r[2] - vdotstart > 0; + } + + // All vertices of the triangle are on the same side of the start point, + // the ray cannot intersect, so we move on. + if (((u_sides[0] == u_sides[1]) && (u_sides[0] == u_sides[2])) || + ((v_sides[0] == v_sides[1]) && (v_sides[0] == v_sides[2]))) { + continue; + } + + // Compute a matrix that transforms the unit triangle + // (1, 0, 0)..(0, 1, 0)..(0, 0, 1) into the current triangle. + var t; + t = 3 * indices[0]; + var m00 = elements[t] - start[0]; + var m01 = elements[t + 1] - start[1]; + var m02 = elements[t + 2] - start[2]; + t = 3 * indices[1]; + var m10 = elements[t] - start[0]; + var m11 = elements[t + 1] - start[1]; + var m12 = elements[t + 2] - start[2]; + t = 3 * indices[2]; + var m20 = elements[t] - start[0]; + var m21 = elements[t + 1] - start[1]; + var m22 = elements[t + 2] - start[2]; + + var t00 = m11 * m22 - m12 * m21; + var t10 = m01 * m22 - m02 * m21; + var t20 = m01 * m12 - m02 * m11; + + // Compute the determinant of the matrix. The sign (+/-) tells us + // if it's culled. + var d = m00 * t00 - m10 * t10 + m20 * t20; + + if ((cull == o3d.State.CULL_CW && d < 0) || + (cull == o3d.State.CULL_CCW && d > 0)) { + continue; + } + + // Transform the direction vector by the inverse of that matrix. + // If the end point is in the first octant, it's a hit. + var v0 = (t00 * x - + (m10 * m22 - m12 * m20) * y + + (m10 * m21 - m11 * m20) * z) / d; + var v1 = (-t10 * x + + (m00 * m22 - m02 * m20) * y - + (m00 * m21 - m01 * m20) * z) / d; + var v2 = (t20 * x - + (m00 * m12 - m02 * m10) * y + + (m00 * m11 - m01 * m10) * z) / d; + + if (v0 > 0 && v1 > 0 && v2 > 0) { + // Rescale by the one-norm to find the intersection of the transformed. + // ray with the unit triangle. + var one_norm = v0 + v1 + v2; + v0 /= one_norm; + v1 /= one_norm; + v2 /= one_norm; + // Multiply m to get back to the original triangle. + var px = m00 * v0 + m10 * v1 + m20 * v2; + var py = m01 * v0 + m11 * v1 + m21 * v2; + var pz = m02 * v0 + m12 * v1 + m22 * v2; + // Compute the distance (actually distance squared) from the start point + // to the intersection. + var distance = px * px + py * py + pz * pz; + if (!result.intersected || distance < min_distance) { + min_distance = distance; + result.position[0] = px + start[0]; + result.position[1] = py + start[1]; + result.position[2] = pz + start[2]; + result.primitiveIndex = i; + } + result.intersected = true; + } + } + + return result; +}; + + /** * Computes the bounding box in same coordinate system as the specified * POSITION stream. @@ -206,7 +492,7 @@ o3d.Primitive.prototype.getBoundingBox = var streamBank = this.streamBank; var indexBuffer = this.indexBuffer; var stream = - this.streamBank.vertexStreams[o3d.Stream.POSITION][position_stream_index]; + this.streamBank.getVertexStream(o3d.Stream.POSITION, position_stream_index); var points = []; var field = stream.field; diff --git a/o3d/samples/o3d-webgl/shape.js b/o3d/samples/o3d-webgl/shape.js index e58f1f4..a0ad16e 100644 --- a/o3d/samples/o3d-webgl/shape.js +++ b/o3d/samples/o3d-webgl/shape.js @@ -62,12 +62,30 @@ o3d.Shape.prototype.elements = []; /** + * Finds a draw element in the given list of draw elements that uses the given + * material if such a draw element exists. Returns null otherwise. + * @param {Array.<!o3d.DrawElements>} drawElements An array of draw elements. + * @param {o3d.Material} material A material to search for. + * @private + */ +o3d.Shape.findDrawElementWithMaterial_ = function(drawElements, material) { + for (var j = 0; j < drawElements.length; ++j) { + if (drawElements[j].material == material) { + return drawElements[j]; + } + } + return null; +}; + + +/** * Creates a DrawElement for each Element owned by this Shape. - * If an Element already has a DrawElement that uses \a material a new + * If an Element already has a DrawElement that uses material a new * DrawElement will not be created. * @param {o3d.Pack} pack pack used to manage created DrawElements. * @param {o3d.Material} material material to use for each DrawElement. - * If you pass null it will use the material on the corresponding Element. + * Note: When a DrawElement with a material of null is rendered, the + * material on the corresponding Element will get used instead. * This allows you to easily setup the default (just draw as is) by * passing null or setup a shadow pass by passing in a shadow material. */ @@ -75,7 +93,11 @@ o3d.Shape.prototype.createDrawElements = function(pack, material) { var elements = this.elements; for (var i = 0; i < elements.length; ++i) { - elements[i].createDrawElement(pack, material); + var element = elements[i]; + if (!o3d.Shape.findDrawElementWithMaterial_(element.drawElements, + material)) { + element.createDrawElement(pack, material); + } } }; diff --git a/o3d/samples/o3d-webgl/state.js b/o3d/samples/o3d-webgl/state.js index 632e88a..a09ab36 100644 --- a/o3d/samples/o3d-webgl/state.js +++ b/o3d/samples/o3d-webgl/state.js @@ -37,93 +37,91 @@ o3d.State = function() { o3d.ParamObject.call(this); - // TODO(petersont): Only some of these have been implemented. - var stateInfos = [ - {name: 'AlphaBlendEnable', paramType: 'ParamBoolean', - defaultValue: false}, - {name: 'AlphaComparisonFunction', paramType: 'ParamInteger', - defaultValue: o3d.State.CMP_ALWAYS}, - {name: 'AlphaReference', paramType: 'ParamFloat', - defaultValue: 0}, - {name: 'AlphaTestEnable', paramType: 'ParamBoolean', - defaultValue: false}, - {name: 'BlendAlphaEquation', paramType: 'ParamInteger', - defaultValue: o3d.State.BLEND_ADD}, - {name: 'BlendEquation', paramType: 'ParamInteger', - defaultValue: o3d.State.BLEND_ADD}, - {name: 'CCWStencilComparisonFunction', paramType: 'ParamInteger', - defaultValue: o3d.State.CMP_ALWAYS}, - {name: 'CCWStencilFailOperation', paramType: 'ParamInteger', - defaultValue: o3d.State.STENCIL_KEEP}, - {name: 'CCWStencilPassOperation', paramType: 'ParamInteger', - defaultValue: o3d.State.STENCIL_KEEP}, - {name: 'CCWStencilZFailOperation', paramType: 'ParamInteger', - defaultValue: o3d.State.STENCIL_KEEP}, - {name: 'ColorWriteEnable', paramType: 'ParamInteger', - defaultValue: 15}, - {name: 'CullMode', paramType: 'ParamInteger', - defaultValue: o3d.State.CULL_CW}, - {name: 'DestinationBlendAlphaFunction', paramType: 'ParamInteger', - defaultValue: o3d.State.BLENDFUNC_ZERO}, - {name: 'DestinationBlendFunction', paramType: 'ParamInteger', - defaultValue: o3d.State.BLENDFUNC_ZERO}, - {name: 'DitherEnable', paramType: 'ParamBoolean', - defaultValue: false}, - {name: 'FillMode', paramType: 'ParamInteger', - defaultValue: o3d.State.SOLID}, - {name: 'LineSmoothEnable', paramType: 'ParamBoolean', - defaultValue: false}, - {name: 'PointSize', paramType: 'ParamFloat', - defaultValue: 0}, - {name: 'PointSpriteEnable', paramType: 'ParamBoolean', - defaultValue: false}, - {name: 'PolygonOffset1', paramType: 'ParamFloat', - defaultValue: 0}, - {name: 'PolygonOffset2', paramType: 'ParamFloat', - defaultValue: 0}, - {name: 'SeparateAlphaBlendEnable', paramType: 'ParamBoolean', - defaultValue: false}, - {name: 'SourceBlendAlphaFunction', paramType: 'ParamInteger', - defaultValue: o3d.State.BLENDFUNC_ONE}, - {name: 'SourceBlendFunction', paramType: 'ParamInteger', - defaultValue: o3d.State.BLENDFUNC_ONE}, - {name: 'StencilComparisonFunction', paramType: 'ParamInteger', - defaultValue: o3d.State.CMP_ALWAYS}, - {name: 'StencilEnable', paramType: 'ParamBoolean', - defaultValue: false}, - {name: 'StencilFailOperation', paramType: 'ParamInteger', - defaultValue: o3d.State.STENCIL_KEEP}, - {name: 'StencilMask', paramType: 'ParamInteger', - defaultValue: 255}, - {name: 'StencilPassOperation', paramType: 'ParamInteger', - defaultValue: o3d.State.STENCIL_KEEP}, - {name: 'StencilReference', paramType: 'ParamInteger', - defaultValue: 0}, - {name: 'StencilWriteMask', paramType: 'ParamInteger', - defaultValue: 255}, - {name: 'StencilZFailOperation', paramType: 'ParamInteger', - defaultValue: o3d.State.STENCIL_KEEP}, - {name: 'TwoSidedStencilEnable', paramType: 'ParamBoolean', - defaultValue: false}, - {name: 'ZComparisonFunction', paramType: 'ParamInteger', - defaultValue: o3d.State.CMP_LESS}, - {name: 'ZEnable', paramType: 'ParamBoolean', - defaultValue: true}, - {name: 'ZWriteEnable', paramType: 'ParamBoolean', - defaultValue: true} - ]; - this.state_params_ = {}; - for (var i = 0; i < stateInfos.length; ++i) { - var info = stateInfos[i]; - var param = new o3d.global.o3d[info.paramType]; - param.value = info.defaultValue; - this.state_params_[info.name] = param; - } + /** + * The names types and default values of all the state variables. + * @type {Object} + * @private + */ + o3d.State.stateVariableInfos_ = o3d.State.stateVariableInfos_ || { + 'AlphaBlendEnable': + {paramType: 'ParamBoolean', defaultValue: false}, + 'AlphaComparisonFunction': + {paramType: 'ParamInteger', defaultValue: o3d.State.CMP_ALWAYS}, + 'AlphaReference': + {paramType: 'ParamFloat', defaultValue: 0}, + 'AlphaTestEnable': + {paramType: 'ParamBoolean', defaultValue: false}, + 'BlendAlphaEquation': + {paramType: 'ParamInteger', defaultValue: o3d.State.BLEND_ADD}, + 'BlendEquation': + {paramType: 'ParamInteger', defaultValue: o3d.State.BLEND_ADD}, + 'CCWStencilComparisonFunction': + {paramType: 'ParamInteger', defaultValue: o3d.State.CMP_ALWAYS}, + 'CCWStencilFailOperation': + {paramType: 'ParamInteger', defaultValue: o3d.State.STENCIL_KEEP}, + 'CCWStencilPassOperation': + {paramType: 'ParamInteger', defaultValue: o3d.State.STENCIL_KEEP}, + 'CCWStencilZFailOperation': + {paramType: 'ParamInteger', defaultValue: o3d.State.STENCIL_KEEP}, + 'ColorWriteEnable': + {paramType: 'ParamInteger', defaultValue: 15}, + 'CullMode': + {paramType: 'ParamInteger', defaultValue: o3d.State.CULL_CW}, + 'DestinationBlendAlphaFunction': + {paramType: 'ParamInteger', defaultValue: o3d.State.BLENDFUNC_ZERO}, + 'DestinationBlendFunction': + {paramType: 'ParamInteger', defaultValue: o3d.State.BLENDFUNC_ZERO}, + 'DitherEnable': + {paramType: 'ParamBoolean', defaultValue: false}, + 'FillMode': + {paramType: 'ParamInteger', defaultValue: o3d.State.SOLID}, + 'LineSmoothEnable': + {paramType: 'ParamBoolean', defaultValue: false}, + 'PointSize': + {paramType: 'ParamFloat', defaultValue: 0}, + 'PointSpriteEnable': + {paramType: 'ParamBoolean', defaultValue: false}, + 'PolygonOffset1': + {paramType: 'ParamFloat', defaultValue: 0}, + 'PolygonOffset2': + {paramType: 'ParamFloat', defaultValue: 0}, + 'SeparateAlphaBlendEnable': + {paramType: 'ParamBoolean', defaultValue: false}, + 'SourceBlendAlphaFunction': + {paramType: 'ParamInteger', defaultValue: o3d.State.BLENDFUNC_ONE}, + 'SourceBlendFunction': + {paramType: 'ParamInteger', defaultValue: o3d.State.BLENDFUNC_ONE}, + 'StencilComparisonFunction': + {paramType: 'ParamInteger', defaultValue: o3d.State.CMP_ALWAYS}, + 'StencilEnable': + {paramType: 'ParamBoolean', defaultValue: false}, + 'StencilFailOperation': + {paramType: 'ParamInteger', defaultValue: o3d.State.STENCIL_KEEP}, + 'StencilMask': + {paramType: 'ParamInteger', defaultValue: 255}, + 'StencilPassOperation': + {paramType: 'ParamInteger', defaultValue: o3d.State.STENCIL_KEEP}, + 'StencilReference': + {paramType: 'ParamInteger', defaultValue: 0}, + 'StencilWriteMask': + {paramType: 'ParamInteger', defaultValue: 255}, + 'StencilZFailOperation': + {paramType: 'ParamInteger', defaultValue: o3d.State.STENCIL_KEEP}, + 'TwoSidedStencilEnable': + {paramType: 'ParamBoolean', defaultValue: false}, + 'ZComparisonFunction': + {paramType: 'ParamInteger', defaultValue: o3d.State.CMP_LESS}, + 'ZEnable': + {paramType: 'ParamBoolean', defaultValue: true}, + 'ZWriteEnable': + {paramType: 'ParamBoolean', defaultValue: true} + }; }; o3d.inherit('State', 'ParamObject'); + /** * A private object containing the the state params by name. */ @@ -377,189 +375,326 @@ o3d.State.STENCIL_DECREMENT = 7; */ o3d.State.prototype.getStateParam = function(state_name) { + if (!this.state_params_[state_name]) { + var info = o3d.State.stateVariableInfos_[state_name]; + var param = new o3d.global.o3d[info.paramType]; + param.value = info.defaultValue; + this.state_params_[state_name] = param; + } return this.state_params_[state_name]; }; +/** + * Constructs a state to set all state variables to their default value. + * This is meant to be called once at client initialization. + * @param {WebGLContext} gl The context associated with the calling client. + * @return {o3d.State} A new state object. + * @private + */ +o3d.State.createDefaultState_ = function(gl) { + var state = new o3d.State; + state.gl = gl; + var infos = o3d.State.stateVariableInfos_; + for (name in infos) { + var info = infos[name]; + state.getStateParam(name).value = info.defaultValue; + } + return state; +}; -o3d.State.prototype.convertCmpFunc = function(cmp) { + +/** + * Converts a comparison function type constant from o3d to WebGL. + * @param {!WebGLContext} gl The current context. + * @param {number} blend_func The o3d constant. + * @return {number} The WebGL version of the constant. + * @private + */ +o3d.State.convertCmpFunc_ = function(gl, cmp) { switch(cmp) { case o3d.State.CMP_ALWAYS: - return this.gl.ALWAYS; + return gl.ALWAYS; case o3d.State.CMP_NEVER: - return this.gl.NEVER; + return gl.NEVER; case o3d.State.CMP_LESS: - return this.gl.LESS; + return gl.LESS; case o3d.State.CMP_GREATER: - return this.gl.GREATER; + return gl.GREATER; case o3d.State.CMP_LEQUAL: - return this.gl.LEQUAL; + return gl.LEQUAL; case o3d.State.CMP_GEQUAL: - return this.gl.GEQUAL; + return gl.GEQUAL; case o3d.State.CMP_EQUAL: - return this.gl.EQUAL; + return gl.EQUAL; case o3d.State.CMP_NOTEQUAL: - return this.gl.NOTEQUAL; + return gl.NOTEQUAL; default: break; } - return this.gl.ALWAYS; + return gl.ALWAYS; }; -o3d.State.prototype.convertFillMode = function(mode) { - switch (mode) { - case o3d.State.POINT: - return this.gl.POINT; - case o3d.State.WIREFRAME: - return this.gl.LINE; - case o3d.State.SOLID: - return this.gl.FILL; - default: - break; - } - return this.gl.FILL; -}; - - -o3d.State.prototype.convertBlendFunc = function(blend_func) { +/** + * Converts a blend function type constant from o3d to WebGL. + * @param {!WebGLContext} gl The current context. + * @param {number} blend_func The o3d constant. + * @return {number} The WebGL version of the constant. + * @private + */ +o3d.State.convertBlendFunc_ = function(gl, blend_func) { switch (blend_func) { case o3d.State.BLENDFUNC_ZERO: - return this.gl.ZERO; + return gl.ZERO; case o3d.State.BLENDFUNC_ONE: - return this.gl.ONE; + return gl.ONE; case o3d.State.BLENDFUNC_SOURCE_COLOR: - return this.gl.SRC_COLOR; + return gl.SRC_COLOR; case o3d.State.BLENDFUNC_INVERSE_SOURCE_COLOR: - return this.gl.ONE_MINUS_SRC_COLOR; + return gl.ONE_MINUS_SRC_COLOR; case o3d.State.BLENDFUNC_SOURCE_ALPHA: - return this.gl.SRC_ALPHA; + return gl.SRC_ALPHA; case o3d.State.BLENDFUNC_INVERSE_SOURCE_ALPHA: - return this.gl.ONE_MINUS_SRC_ALPHA; + return gl.ONE_MINUS_SRC_ALPHA; case o3d.State.BLENDFUNC_DESTINATION_ALPHA: - return this.gl.DST_ALPHA; + return gl.DST_ALPHA; case o3d.State.BLENDFUNC_INVERSE_DESTINATION_ALPHA: - return this.gl.ONE_MINUS_DST_ALPHA; + return gl.ONE_MINUS_DST_ALPHA; case o3d.State.BLENDFUNC_DESTINATION_COLOR: - return this.gl.DST_COLOR; + return gl.DST_COLOR; case o3d.State.BLENDFUNC_INVERSE_DESTINATION_COLOR: - return this.gl.ONE_MINUS_DST_COLOR; + return gl.ONE_MINUS_DST_COLOR; case o3d.State.BLENDFUNC_SOURCE_ALPHA_SATUTRATE: - return this.gl.SRC_ALPHA_SATURATE; + return gl.SRC_ALPHA_SATURATE; default: break; } - return this.gl.ONE; + return gl.ONE; }; -o3d.State.prototype.convertBlendEquation = function(blend_equation) { +/** + * Converts a stencil type constant from o3d to WebGL. + * @param {!WebGLContext} gl The current context. + * @param {number} stencil_func The o3d constant. + * @return {number} The WebGL version of the constant. + * @private + */ +o3d.State.convertBlendEquation_ = function(gl, blend_equation) { switch (blend_equation) { case o3d.State.BLEND_ADD: - return this.gl.FUNC_ADD; + return gl.FUNC_ADD; case o3d.State.BLEND_SUBTRACT: - return this.gl.FUNC_SUBTRACT; + return gl.FUNC_SUBTRACT; case o3d.State.BLEND_REVERSE_SUBTRACT: - return this.gl.FUNC_REVERSE_SUBTRACT; + return gl.FUNC_REVERSE_SUBTRACT; case o3d.State.BLEND_MIN: - return this.gl.MIN; + return gl.MIN; case o3d.State.BLEND_MAX: - return this.gl.MAX; + return gl.MAX; default: break; } - return this.gl.FUNC_ADD; + return gl.FUNC_ADD; }; -o3d.State.prototype.convertStencilOp = function(stencil_func) { - switch (stencil_func) { - case o3d.State.STENCIL_KEEP: - return this.gl.KEEP; - case o3d.State.STENCIL_ZERO: - return this.gl.ZERO; - case o3d.State.STENCIL_REPLACE: - return this.gl.REPLACE; - case o3d.State.STENCIL_INCREMENT_SATURATE: - return this.gl.INCR; - case o3d.State.STENCIL_DECREMENT_SATURATE: - return this.gl.DECR; - case o3d.State.STENCIL_INVERT: - return this.gl.INVERT; - case o3d.State.STENCIL_INCREMENT: - return this.gl.INCR_WRAP; - case o3d.State.STENCIL_DECREMENT: - return this.gl.DECR_WRAP; - default: +/** + * Sets the internal state to the this state. + * @private + */ +o3d.State.prototype.push_ = function() { + this.gl.client.pushState_(this.getVariableMap_()); +}; + + +/** + * Recovers the internal state prior to this state gettings set. + * @private + */ +o3d.State.prototype.pop_ = function() { + this.gl.client.popState_(); +}; + + +/** + * Returns a new javascript object of name value pairs indicating + * what values to set each of the (changing) state variables. + * @return {!Object} The variable map. + * @private + */ +o3d.State.prototype.getVariableMap_ = function() { + var m = {}; + var stateParams = this.state_params_; + for (var name in stateParams) { + m[name] = stateParams[name].value; + } + return m; +}; + + + +/** + * Helper function for setVariables_, looks for each of the given state + * variables' names in the given variable map. If any one of them is a key + * in the map, it fills in the rest in the target_map with the value either + * from the variable map or from the state if it isn't on the variable map. + * @param {Array.<!string>} names The names of the variables in question. + * @param {!Object} variable_map An object connecting names to values. + * @param {!Object} target_map An object to fill out with the variables from + * the given array of names. + * @return {boolean} True if any of the variable names in the given array were + * found in the variable_map + * @private + */ +o3d.State.relevantValues_ = + function(gl, names, variable_map, target_map) { + var found = false; + for (var i = 0; i < names.length; ++i) { + var name = names[i]; + if (variable_map[name] !== undefined) { + found = true; break; + } + } + + if (found) { + for (var i = 0; i < names.length; ++i) { + var name = names[i]; + var value = variable_map[name]; + if (value === undefined) { + value = gl.client.getState_(name); + } + target_map[name] = value; + } } - return this.gl.KEEP; + return found; }; -o3d.State.prototype.set = function() { - var stateParams = this.state_params_; +/** + * Sets the internal state according to the name value pairs in the given + * object. + * @param {WebGLContext} gl The gl context to use. + * @param {Object} variable_map A map linking state variable names to values. + * @private + */ +o3d.State.setVariables_ = function(gl, variable_map) { + // TODO(petersont): Only some of the state variables have been implemented. + // Others are unavailable in webgl. + + // Remember, any state variable might be missing from variable_map. When the + // key is not present, the state should be left alone. + + // Temporary map to hold name value pairs. + var v = {}; + + if (this.relevantValues_(gl, ['AlphaBlendEnable'], variable_map, v)) { + if (v['AlphaBlendEnable']) { + gl.enable(gl.BLEND); + } else { + gl.disable(gl.BLEND); + } + } - if (stateParams['AlphaBlendEnable'].value) { - this.gl.enable(this.gl.BLEND); - } else { - this.gl.disable(this.gl.BLEND); + if (this.relevantValues_(gl, ['SeparateAlphaBlendEnable', + 'SourceBlendFunction', + 'SourceBlendAlphaFunction', + 'DestinationBlendAlphaFunction', + 'BlendEquation', + 'BlendAlphaEquation'], variable_map, v)) { + if (v['SeparateAlphaBlendEnable']) { + gl.blendFuncSeparate( + o3d.State.convertBlendFunc_(gl, v['SourceBlendFunction']), + o3d.State.convertBlendFunc_(gl, v['DestinationBlendFunction']), + o3d.State.convertBlendFunc_(gl, v['SourceBlendAlphaFunction']), + o3d.State.convertBlendFunc_(gl, v['DestinationBlendAlphaFunction'])); + gl.blendEquationSeparate( + o3d.State.convertBlendEquation_(gl, v['BlendEquation']), + o3d.State.convertBlendEquation_(gl, v['BlendAlphaEquation'])); + } } - if (stateParams['SeparateAlphaBlendEnable'].value) { - this.gl.blendFuncSeparate( - this.convertBlendFunc(stateParams['SourceBlendFunction'].value), - this.convertBlendFunc(stateParams['DestinationBlendFunction'].value), - this.convertBlendFunc(stateParams['SourceBlendAlphaFunction'].value), - this.convertBlendFunc( - stateParams['DestinationBlendAlphaFunction'].value)); - this.gl.blendEquationSeparate( - this.convertBlendEquation(stateParams['BlendEquation'].value), - this.convertBlendEquation(stateParams['BlendAlphaEquation'].value)); - } else { - this.gl.blendFunc( - this.convertBlendFunc(stateParams['SourceBlendFunction'].value), - this.convertBlendFunc(stateParams['DestinationBlendFunction'].value)); - this.gl.blendEquation( - this.convertBlendEquation(stateParams['BlendEquation'].value)); + if (this.relevantValues_(gl, ['SourceBlendFunction', + 'DestinationBlendFunction'], variable_map, v)) { + gl.blendFunc( + o3d.State.convertBlendFunc_(gl, v['SourceBlendFunction']), + o3d.State.convertBlendFunc_(gl, v['DestinationBlendFunction'])); } - switch (stateParams['CullMode'].value) { - case o3d.State.CULL_CW: - this.gl.enable(this.gl.CULL_FACE); - this.gl.cullFace(this.gl.BACK); - break; - case o3d.State.CULL_CCW: - this.gl.enable(this.gl.CULL_FACE); - this.gl.cullFace(this.gl.FRONT); - break; - default: - this.gl.disable(this.gl.CULL_FACE); - break; + if (this.relevantValues_(gl, ['BlendEquation'], variable_map, v)) { + gl.blendEquation(o3d.State.convertBlendEquation_(gl, v['BlendEquation'])); + } + + if (this.relevantValues_(gl, ['CullMode'], variable_map, v)) { + switch (v['CullMode']) { + case o3d.State.CULL_CW: + gl.enable(gl.CULL_FACE); + gl.cullFace(gl.BACK); + break; + case o3d.State.CULL_CCW: + gl.enable(gl.CULL_FACE); + gl.cullFace(gl.FRONT); + break; + default: + gl.disable(gl.CULL_FACE); + break; + } + } + + if (this.relevantValues_(gl, ['DitherEnable'], variable_map, v)) { + if (v['DitherEnable']) { + gl.enable(gl.DITHER); + } else { + gl.disable(gl.DITHER); + } + } + + if (this.relevantValues_(gl, ['ZEnable', 'ZComparisonFunction'], + variable_map, v)) { + if (v['ZEnable']) { + gl.enable(gl.DEPTH_TEST); + gl.depthFunc( + this.convertCmpFunc_(gl, v['ZComparisonFunction'])); + } else { + gl.disable(gl.DEPTH_TEST); + } } - if (stateParams['DitherEnable'].value) { - this.gl.enable(this.gl.DITHER); - } else { - this.gl.disable(this.gl.DITHER); + if (this.relevantValues_(gl, ['ZWriteEnable'], variable_map, v)) { + gl.depthMask(v['ZWriteEnable']); } - this.gl.depthMask(stateParams['ZWriteEnable'].value); + if (this.relevantValues_(gl, ['StencilEnable', 'StencilComparisonFunction'], + variable_map, v)) { + if (v['StencilEnable']) { + gl.enable(gl.STENCIL_TEST); + gl.stencilFunc( + this.convertCmpFunc_(gl, v['StencilComparisonFunction'])); + } else { + gl.disable(gl.STENCIL_TEST); + } + } - if (stateParams['ZEnable'].value) { - this.gl.enable(this.gl.DEPTH_TEST); - this.gl.depthFunc( - this.convertCmpFunc(stateParams['ZComparisonFunction'].value)); - } else { - this.gl.disable(this.gl.DEPTH_TEST); + if (this.relevantValues_(gl, ['PolygonOffset1', + 'PolygonOffset2'], variable_map, v)) { + var polygon_offset_factor = v['PolygonOffset1'] || 0; + var polygon_offset_bias = v['PolygonOffset2'] || 0; + + if (polygon_offset_factor || polygon_offset_bias) { + gl.enable(gl.POLYGON_OFFSET_FILL); + gl.polygonOffset(polygon_offset_factor, polygon_offset_bias); + } else { + gl.disable(gl.POLYGON_OFFSET_FILL); + } } - if (stateParams['StencilEnable'].value) { - this.gl.enable(this.gl.STENCIL_TEST); - this.gl.stencilFunc( - this.convertCmpFunc(stateParams['StencilComparisonFunction'].value)); - } else { - this.gl.disable(this.gl.STENCIL_TEST); + if (this.relevantValues_(gl, ['FillMode'], variable_map, v)) { + // We emulate the behavior of the fill mode state in the primitive class. + gl.fillMode_ = v['FillMode']; } }; + + diff --git a/o3d/samples/o3d-webgl/state_set.js b/o3d/samples/o3d-webgl/state_set.js index e421622..a2b8bdd 100644 --- a/o3d/samples/o3d-webgl/state_set.js +++ b/o3d/samples/o3d-webgl/state_set.js @@ -34,7 +34,7 @@ * A StateSet is a render node that sets render states of all of its * children. You can make this a parent of a part of the render graph to set * render states in a more global way. - * + * * @param {o3d.State} opt_state The State the defines what states to set. * @constructor */ @@ -55,7 +55,16 @@ o3d.ParamObject.setUpO3DParam_(o3d.StateSet, 'state', 'ParamState'); * Sets the current state to the member state. */ o3d.StateSet.prototype.before = function() { - if (this.state) - this.state.set(); + if (this.state) { + this.state.push_(); + } }; +/** + * Sets the current state to the member state. + */ +o3d.StateSet.prototype.after = function() { + if (this.state) { + this.state.pop_(); + } +}; diff --git a/o3d/samples/o3d-webgl/stream_bank.js b/o3d/samples/o3d-webgl/stream_bank.js index 8329709..d0f1ca7 100644 --- a/o3d/samples/o3d-webgl/stream_bank.js +++ b/o3d/samples/o3d-webgl/stream_bank.js @@ -36,14 +36,32 @@ */ o3d.StreamBank = function() { o3d.NamedObject.call(this); - this.vertexStreams = []; + this.vertex_streams_ = []; }; o3d.inherit('StreamBank', 'NamedObject'); /** * Array of streams. */ -o3d.StreamBank.prototype.vertexStreams = []; +o3d.StreamBank.prototype.vertex_streams_ = []; + +o3d.StreamBank.prototype.__defineGetter__('vertexStreams', + function() { + result = []; + for (var i = 0; i < this.vertex_streams_.length; ++i) { + var stream_array = this.vertex_streams_[i]; + if (stream_array && stream_array.length) { + for (var j = 0; j < stream_array.length; ++j) { + var stream = stream_array[j]; + if (stream) { + result.push(stream); + } + } + } + } + return result; + } +); /** @@ -58,10 +76,10 @@ o3d.StreamBank.prototype.vertexStreams = []; */ o3d.StreamBank.prototype.setVertexStream = function(semantic, semantic_index, field, start_index) { - if (this.vertexStreams[semantic] == undefined) { - this.vertexStreams[semantic] = []; + if (this.vertex_streams_[semantic] == undefined) { + this.vertex_streams_[semantic] = []; } - this.vertexStreams[semantic][semantic_index] = new o3d.Stream( + this.vertex_streams_[semantic][semantic_index] = new o3d.Stream( semantic, semantic_index, field, start_index); }; @@ -76,10 +94,10 @@ o3d.StreamBank.prototype.setVertexStream = */ o3d.StreamBank.prototype.getVertexStream = function(semantic, semantic_index) { - if (this.vertexStreams[semantic] == undefined) { + if (this.vertex_streams_[semantic] == undefined) { return; } - return this.vertexStreams[semantic][semantic_index]; + return this.vertex_streams_[semantic][semantic_index]; }; @@ -92,10 +110,10 @@ o3d.StreamBank.prototype.getVertexStream = */ o3d.StreamBank.prototype.removeVertexStream = function(semantic, semantic_index) { - if (this.vertexStreams[semantic] == undefined) { + if (this.vertex_streams_[semantic] == undefined) { return false; } - this.vertexStreams[semantic][semantic_index] = null; + this.vertex_streams_[semantic][semantic_index] = null; return true; }; diff --git a/o3d/samples/o3d-webgl/transform.js b/o3d/samples/o3d-webgl/transform.js index 01231a1..640592c 100644 --- a/o3d/samples/o3d-webgl/transform.js +++ b/o3d/samples/o3d-webgl/transform.js @@ -257,7 +257,7 @@ o3d.Transform.prototype.addShape = */ o3d.Transform.prototype.removeShape = function(shape) { - o3d.notImplemented(); + o3d.removeFromArray(this.shapes, shape); }; @@ -601,7 +601,6 @@ o3d.Transform.prototype.rotateX = }; - /** * Takes a 4-by-4 matrix and a vector with 3 entries, * interprets the vector as a point, transforms that point by the matrix, and diff --git a/o3d/samples/o3djs/event.js b/o3d/samples/o3djs/event.js index f5de0d3..333aa0c 100644 --- a/o3d/samples/o3djs/event.js +++ b/o3d/samples/o3djs/event.js @@ -157,7 +157,7 @@ o3djs.event.getEventKeyChar = function(event) { event = window.event; } var charCode = 0; - if (event.keyIdentifier) + if (event.keyIdentifier) charCode = o3djs.event.keyIdentifierToChar(event.keyIdentifier); if (!charCode) charCode = (window.event) ? window.event.keyCode : event.charCode; |