more premake4 fun and tests

This commit is contained in:
erwin.coumans
2011-09-03 00:27:54 +00:00
parent 82234e6f33
commit c8b3c9e306
28 changed files with 2248 additions and 7 deletions

View File

@@ -0,0 +1,41 @@
hasDX11 = findDirectX11()
if (hasDX11) then
project "App_DX11ClothDemo"
initDirectX11()
language "C++"
defines { "UNICODE","_UNICODE"}
kind "WindowedApp"
flags { "WinMain" }
targetdir "../.."
includedirs {
"../../src",
"DXUT/Core",
"DXUT/Optional"
}
links {
"LinearMath","BulletCollision","BulletDynamics", "BulletSoftBody", "BulletSoftBodyDX11Solvers"
}
files {
"DXUT/Core/DXUT.cpp",
"DXUT/Optional/DXUTcamera.cpp",
"DXUT/Core/DXUTDevice11.cpp",
"DXUT/Core/DXUTDevice9.cpp",
"DXUT/Optional/DXUTgui.cpp",
"DXUT/Core/DXUTmisc.cpp",
"DXUT/Optional/DXUTres.cpp",
"DXUT/Optional/DXUTsettingsdlg.cpp",
"DXUT/Optional/SDKmesh.cpp",
"DXUT/Optional/SDKmisc.cpp",
"cloth_renderer.cpp"
}
end

View File

@@ -0,0 +1,22 @@
// Copyright (c) 2011 The Native Client Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview This class implements an extension to Function object that
* lets you bind a scope for |this| to a function.
*/
/**
* Bind a scope to a function. Used to bind an object to |this| for event
* handlers.
* @param {!Object} scope The scope in which the function executes. |scope|
* becomes |this| during function execution.
* @return {function} the bound version of the original function.
*/
Function.prototype.bind = function(scope) {
var boundContext = this;
return function() {
return boundContext.apply(scope, arguments);
}
}

View File

@@ -0,0 +1,134 @@
// Copyright (c) 2011 The Native Client Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview This class implements a mouse-drag event. It registers for
* mousedown events, and when it sees one, starts capturing mousemove events
* until it gets a mousup event. It manufactures three drag events: the
* DRAG_START, DRAG and DRAG_END.
*/
// Requires bind
/**
* Constructor for the Dragger. Register for mousedown events that happen on
* |opt_target|. If |opt_target| is null or undefined, then this object
* observes mousedown on the whole document.
* @param {?Element} opt_target The event target. Defaults to the whole
* document.
* @constructor
*/
tumbler.Dragger = function(opt_target) {
/**
* The event target.
* @type {Element}
* @private
*/
this.target_ = opt_target || document;
/**
* The array of objects that get notified of drag events. Each object in
* this array get sent a handleStartDrag(), handleDrag() and handleEndDrag()
* message.
* @type {Array.<Object>}
* @private
*/
this.listeners_ = [];
/**
* Flag to indicate whether the object is in a drag sequence or not.
* @type {boolean}
* @private
*/
this.isDragging_ = false;
/**
* The function objects that get attached as event handlers. These are
* cached so that they can be removed on mouse up.
* @type {function}
* @private
*/
this.boundMouseMove_ = null;
this.boundMouseUp_ = null;
this.target_.addEventListener('mousedown',
this.onMouseDown.bind(this),
false);
}
/**
* The ids used for drag event types.
* @enum {string}
*/
tumbler.Dragger.DragEvents = {
DRAG_START: 'dragstart', // Start a drag sequence
DRAG: 'drag', // Mouse moved during a drag sequence.
DRAG_END: 'dragend' // End a drag sewquence.
};
/**
* Add a drag listener. Each listener should respond to thhree methods:
* handleStartDrag(), handleDrag() and handleEndDrag(). This method assumes
* that |listener| does not already exist in the array of listeners.
* @param {!Object} listener The object that will listen to drag events.
*/
tumbler.Dragger.prototype.addDragListener = function(listener) {
this.listeners_.push(listener);
}
/**
* Handle a mousedown event: register for mousemove and mouseup, then tell
* the target that is has a DRAG_START event.
* @param {Event} event The mousedown event that triggered this method.
*/
tumbler.Dragger.prototype.onMouseDown = function(event) {
this.boundMouseMove_ = this.onMouseMove.bind(this);
this.boundMouseUp_ = this.onMouseUp.bind(this);
this.target_.addEventListener('mousemove', this.boundMouseMove_);
this.target_.addEventListener('mouseup', this.boundMouseUp_);
this.isDragging_ = true;
var dragStartEvent = { type: tumbler.Dragger.DragEvents.DRAG_START,
clientX: event.offsetX,
clientY: event.offsetY };
var i;
for (i = 0; i < this.listeners_.length; ++i) {
this.listeners_[i].handleStartDrag(this.target_, dragStartEvent);
}
}
/**
* Handle a mousemove event: tell the target that is has a DRAG event.
* @param {Event} event The mousemove event that triggered this method.
*/
tumbler.Dragger.prototype.onMouseMove = function(event) {
if (!this.isDragging_)
return;
var dragEvent = { type: tumbler.Dragger.DragEvents.DRAG,
clientX: event.offsetX,
clientY: event.offsetY};
var i;
for (i = 0; i < this.listeners_.length; ++i) {
this.listeners_[i].handleDrag(this.target_, dragEvent);
}
}
/**
* Handle a mouseup event: un-register for mousemove and mouseup, then tell
* the target that is has a DRAG_END event.
* @param {Event} event The mouseup event that triggered this method.
*/
tumbler.Dragger.prototype.onMouseUp = function(event) {
this.target_.removeEventListener('mouseup', this.boundMouseUp_, false);
this.target_.removeEventListener('mousemove', this.boundMouseMove_, false);
this.boundMouseUp_ = null;
this.boundMouseMove_ = null;
this.isDragging_ = false;
var dragEndEvent = { type: tumbler.Dragger.DragEvents.DRAG_END,
clientX: event.offsetX,
clientY: event.offsetY};
var i;
for (i = 0; i < this.listeners_.length; ++i) {
this.listeners_[i].handleEndDrag(this.target_, dragEndEvent);
}
}

View File

@@ -0,0 +1,9 @@
@echo off
setlocal
REM Relative path of CygWin
set CYGWIN=%~dp0%..\third_party\cygwin\bin
PATH=%CYGWIN%;%PATH%
python httpd.py

View File

@@ -0,0 +1,114 @@
#!/usr/bin/python
#
# Copyright (c) 2011, The Native Client Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
"""A tiny web server.
This is intended to be used for testing, and only run from within the examples
directory.
"""
import BaseHTTPServer
import logging
import os
import SimpleHTTPServer
import SocketServer
import sys
import urlparse
logging.getLogger().setLevel(logging.INFO)
# Using 'localhost' means that we only accept connections
# via the loop back interface.
SERVER_PORT = 5103
SERVER_HOST = ''
# We only run from the examples directory (the one that contains scons-out), so
# that not too much is exposed via this HTTP server. Everything in the
# directory is served, so there should never be anything potentially sensitive
# in the serving directory, especially if the machine might be a
# multi-user machine and not all users are trusted. We only serve via
# the loopback interface.
SAFE_DIR_COMPONENTS = ['bin_html']
SAFE_DIR_SUFFIX = apply(os.path.join, SAFE_DIR_COMPONENTS)
def SanityCheckDirectory():
if os.getcwd().endswith(SAFE_DIR_SUFFIX):
return
logging.error('httpd.py should only be run from the %s', SAFE_DIR_SUFFIX)
logging.error('directory for testing purposes.')
logging.error('We are currently in %s', os.getcwd())
sys.exit(1)
# An HTTP server that will quit when |is_running| is set to False. We also use
# SocketServer.ThreadingMixIn in order to handle requests asynchronously for
# faster responses.
class QuittableHTTPServer(SocketServer.ThreadingMixIn,
BaseHTTPServer.HTTPServer):
def serve_forever(self, timeout=0.5):
self.is_running = True
self.timeout = timeout
while self.is_running:
self.handle_request()
def shutdown(self):
self.is_running = False
return 1
# "Safely" split a string at |sep| into a [key, value] pair. If |sep| does not
# exist in |str|, then the entire |str| is the key and the value is set to an
# empty string.
def KeyValuePair(str, sep='='):
if sep in str:
return str.split(sep)
else:
return [str, '']
# A small handler that looks for '?quit=1' query in the path and shuts itself
# down if it finds that parameter.
class QuittableHTTPHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
def do_GET(self):
(_, _, _, query, _) = urlparse.urlsplit(self.path)
url_params = dict([KeyValuePair(key_value)
for key_value in query.split('&')])
if 'quit' in url_params and '1' in url_params['quit']:
self.send_response(200, 'OK')
self.send_header('Content-type', 'text/html')
self.send_header('Content-length', '0')
self.end_headers()
self.server.shutdown()
return
SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
def Run(server_address,
server_class=QuittableHTTPServer,
handler_class=QuittableHTTPHandler):
httpd = server_class(server_address, handler_class)
logging.info("Starting local server on port %d", server_address[1])
logging.info("To shut down send http://localhost:%d?quit=1",
server_address[1])
try:
httpd.serve_forever()
except KeyboardInterrupt:
logging.info("Received keyboard interrupt.")
httpd.server_close()
logging.info("Shutting down local server on port %d", server_address[1])
if __name__ == '__main__':
SanityCheckDirectory()
if len(sys.argv) > 1:
Run((SERVER_HOST, int(sys.argv[1])))
else:
Run((SERVER_HOST, SERVER_PORT))
sys.exit(0)

View File

@@ -0,0 +1,33 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<!--
Copyright (c) 2011 The Native Client Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<head>
<title>Interactive Cube Example</title>
<script type="text/javascript">
// Provide the tumbler namespace
tumbler = {};
</script>
<script type="text/javascript" src="bind.js"></script>
<script type="text/javascript" src="dragger.js"></script>
<script type="text/javascript" src="tumbler.js"></script>
<script type="text/javascript" src="vector3.js"></script>
<script type="text/javascript" src="trackball.js"></script>
</head>
<body id="bodyId">
<h1>Interactive Cube Example</h1>
<p>
The Native Client module executed in this page draws a 3D cube
and allows you to rotate it using a virtual trackball method.
</p>
<div id="tumbler_view"></div>
<script type="text/javascript">
tumbler.application = new tumbler.Application();
tumbler.application.run('tumbler_view');
</script>
</body>
</HTML>

View File

@@ -0,0 +1,296 @@
// Copyright (c) 2011 The Native Client Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Implement a virtual trackball in the tumbler.Trackball
* class. This class maps 2D mouse events to 3D rotations by simulating a
* trackball that you roll by dragging the mouse. There are two principle
* methods in the class: startAtPointInFrame which you use to begin a trackball
* simulation and rollToPoint, which you use while dragging the mouse. The
* rollToPoint method returns a rotation expressed as a quaternion.
*/
// Requires tumbler.Application
// Requires tumbler.DragEvent
// Requires tumbler.Vector3
/**
* Constructor for the Trackball object. This class maps 2D mouse drag events
* into 3D rotations by simulating a trackball. The idea is to simulate
* clicking on the trackball, and then rolling it as you drag the mouse.
* The math behind the trackball is simple: start with a vector from the first
* mouse-click on the ball to the center of the 3D view. At the same time, set
* the radius of the ball to be the smaller dimension of the 3D view. As you
* drag the mouse around in the 3D view, a second vector is computed from the
* surface of the ball to the center. The axis of rotation is the cross
* product of these two vectors, and the angle of rotation is the angle between
* the two vectors.
* @constructor
*/
tumbler.Trackball = function() {
/**
* The square of the trackball's radius. The math never looks at the radius,
* but looks at the radius squared.
* @type {number}
* @private
*/
this.sqrRadius_ = 0;
/**
* The 3D vector representing the point on the trackball where the mouse
* was clicked. Default is pointing stright through the center of the ball.
* @type {Object}
* @private
*/
this.rollStart_ = new tumbler.Vector3(0, 0, 1);
/**
* The 2D center of the frame that encloses the trackball.
* @type {!Object}
* @private
*/
this.center_ = { x: 0, y: 0 };
/**
* Cached camera orientation. When a drag START event happens this is set to
* the current orientation in the calling view's plugin. The default is the
* identity quaternion.
* @type {Array.<number>}
* @private
*/
this.cameraOrientation_ = [0, 0, 0, 1];
};
/**
* Compute the dimensions of the virtual trackball to fit inside |frameSize|.
* The radius of the trackball is set to be 1/2 of the smaller of the two frame
* dimensions, the center point is at the midpoint of each side.
* @param {!goog.math.Size} frameSize 2D-point representing the size of the
* element that encloses the virtual trackball.
* @private
*/
tumbler.Trackball.prototype.initInFrame_ = function(frameSize) {
// Compute the radius of the virtual trackball. This is 1/2 of the smaller
// of the frame's width and height.
var halfFrameSize = 0.5 * Math.min(frameSize.width, frameSize.height);
// Cache the square of the trackball's radius.
this.sqrRadius_ = halfFrameSize * halfFrameSize;
// Figure the center of the view.
this.center_.x = frameSize.width * 0.5;
this.center_.y = frameSize.height * 0.5;
};
/**
* Method to convert (by translation) a 2D client point from a coordinate space
* with origin in the lower-left corner of the client view to a space with
* origin in the center of the client view. Use this method before mapping the
* 2D point to he 3D tackball point (see also the projectOnTrackball_() method).
* Call the startAtPointInFrame before calling this method so that the
* |center_| property is correctly initialized.
* @param {!Object} clientPoint map this point to the coordinate space with
* origin in thecenter of the client view.
* @return {Object} the converted point.
* @private
*/
tumbler.Trackball.prototype.convertClientPoint_ = function(clientPoint) {
var difference = { x: clientPoint.x - this.center_.x,
y: clientPoint.y - this.center_.y }
return difference;
};
/**
* Method to map a 2D point to a 3D point on the virtual trackball that was set
* up using the startAtPointInFrame method. If the point lies outside of the
* radius of the virtual trackball, then the z-coordinate of the 3D point
* is set to 0.
* @param {!Object.<x, y>} point 2D-point in the coordinate space with origin
* in the center of the client view.
* @return {tumbler.Vector3} the 3D point on the virtual trackball.
* @private
*/
tumbler.Trackball.prototype.projectOnTrackball_ = function(point) {
var sqrRadius2D = point.x * point.x + point.y * point.y;
var zValue;
if (sqrRadius2D > this.sqrRadius_) {
// |point| lies outside the virtual trackball's sphere, so use a virtual
// z-value of 0. This is equivalent to clicking on the horizontal equator
// of the trackball.
zValue = 0;
} else {
// A sphere can be defined as: r^2 = x^2 + y^2 + z^2, so z =
// sqrt(r^2 - (x^2 + y^2)).
zValue = Math.sqrt(this.sqrRadius_ - sqrRadius2D);
}
var trackballPoint = new tumbler.Vector3(point.x, point.y, zValue);
return trackballPoint;
};
/**
* Method to start up the trackball. The trackball works by pretending that a
* ball encloses the 3D view. You roll this pretend ball with the mouse. For
* example, if you click on the center of the ball and move the mouse straight
* to the right, you roll the ball around its Y-axis. This produces a Y-axis
* rotation. You can click on the "edge" of the ball and roll it around
* in a circle to get a Z-axis rotation.
* @param {!Object.<x, y>} startPoint 2D-point, usually the mouse-down
* point.
* @param {!Object.<width, height>} frameSize 2D-point representing the size of
* the element that encloses the virtual trackball.
*/
tumbler.Trackball.prototype.startAtPointInFrame =
function(startPoint, frameSize) {
this.initInFrame_(frameSize);
// Compute the starting vector from the surface of the ball to its center.
this.rollStart_ = this.projectOnTrackball_(
this.convertClientPoint_(startPoint));
};
/**
* Method to roll the virtual trackball; call this in response to a mouseDrag
* event. Takes |dragPoint| and projects it from 2D mouse coordinates onto the
* virtual track ball that was set up in startAtPointInFrame method.
* Returns a quaternion that represents the rotation from |rollStart_| to
* |rollEnd_|.
* @param {!Object.<x, y>} dragPoint 2D-point representing the
* destination mouse point.
* @return {Array.<number>} a quaternion that represents the rotation from
* the point wnere the mouse was clicked on the trackball to this point.
* The quaternion looks like this: [[v], cos(angle/2)], where [v] is the
* imaginary part of the quaternion and is computed as [x, y, z] *
* sin(angle/2).
*/
tumbler.Trackball.prototype.rollToPoint = function(dragPoint) {
var rollTo = this.convertClientPoint_(dragPoint);
if ((Math.abs(this.rollStart_.x - rollTo.x) <
tumbler.Trackball.DOUBLE_EPSILON) &&
(Math.abs(this.rollStart_.y, rollTo.y) <
tumbler.Trackball.DOUBLE_EPSILON)) {
// Not enough change in the vectors to roll the ball, return the identity
// quaternion.
return [0, 0, 0, 1];
}
// Compute the ending vector from the surface of the ball to its center.
var rollEnd = this.projectOnTrackball_(rollTo);
// Take the cross product of the two vectors. r = s X e
var rollVector = this.rollStart_.cross(rollEnd);
var invStartMag = 1.0 / this.rollStart_.magnitude();
var invEndMag = 1.0 / rollEnd.magnitude();
// cos(a) = (s . e) / (||s|| ||e||)
var cosAng = this.rollStart_.dot(rollEnd) * invStartMag * invEndMag;
// sin(a) = ||(s X e)|| / (||s|| ||e||)
var sinAng = rollVector.magnitude() * invStartMag * invEndMag;
// Build a quaternion that represents the rotation about |rollVector|.
// Use atan2 for a better angle. If you use only cos or sin, you only get
// half the possible angles, and you can end up with rotations that flip
// around near the poles.
var rollHalfAngle = Math.atan2(sinAng, cosAng) * 0.5;
rollVector.normalize();
// The quaternion looks like this: [[v], cos(angle/2)], where [v] is the
// imaginary part of the quaternion and is computed as [x, y, z] *
// sin(angle/2).
rollVector.scale(Math.sin(rollHalfAngle));
var ballQuaternion = [rollVector.x,
rollVector.y,
rollVector.z,
Math.cos(rollHalfAngle)];
return ballQuaternion;
};
/**
* Handle the drag START event: grab the current camera orientation from the
* sending view and set up the virtual trackball.
* @param {!tumbler.Application} view The view controller that called this
* method.
* @param {!tumbler.DragEvent} dragStartEvent The DRAG_START event that
* triggered this handler.
*/
tumbler.Trackball.prototype.handleStartDrag =
function(controller, dragStartEvent) {
// Cache the camera orientation. The orientations from the trackball as it
// rolls are concatenated to this orientation and pushed back into the
// plugin on the other side of the JavaScript bridge.
controller.setCameraOrientation(this.cameraOrientation_);
// Invert the y-coordinate for the trackball computations.
var frameSize = { width: controller.offsetWidth,
height: controller.offsetHeight };
var flippedY = { x: dragStartEvent.clientX,
y: frameSize.height - dragStartEvent.clientY };
this.startAtPointInFrame(flippedY, frameSize);
};
/**
* Handle the drag DRAG event: concatenate the current orientation to the
* cached orientation. Send this final value through to the GSPlugin via the
* setValueForKey() method.
* @param {!tumbler.Application} view The view controller that called this
* method.
* @param {!tumbler.DragEvent} dragEvent The DRAG event that triggered this
* handler.
*/
tumbler.Trackball.prototype.handleDrag =
function(controller, dragEvent) {
// Flip the y-coordinate so that the 2D origin is in the lower-left corner.
var frameSize = { width: controller.offsetWidth,
height: controller.offsetHeight };
var flippedY = { x: dragEvent.clientX,
y: frameSize.height - dragEvent.clientY };
controller.setCameraOrientation(
tumbler.multQuaternions(this.rollToPoint(flippedY),
this.cameraOrientation_));
};
/**
* Handle the drag END event: get the final orientation and concatenate it to
* the cached orientation.
* @param {!tumbler.Application} view The view controller that called this
* method.
* @param {!tumbler.DragEvent} dragEndEvent The DRAG_END event that triggered
* this handler.
*/
tumbler.Trackball.prototype.handleEndDrag =
function(controller, dragEndEvent) {
// Flip the y-coordinate so that the 2D origin is in the lower-left corner.
var frameSize = { width: controller.offsetWidth,
height: controller.offsetHeight };
var flippedY = { x: dragEndEvent.clientX,
y: frameSize.height - dragEndEvent.clientY };
this.cameraOrientation_ = tumbler.multQuaternions(this.rollToPoint(flippedY),
this.cameraOrientation_);
controller.setCameraOrientation(this.cameraOrientation_);
};
/**
* A utility function to multiply two quaterions. Returns the product q0 * q1.
* This is effectively the same thing as concatenating the two rotations
* represented in each quaternion together. Note that quaternion multiplication
* is NOT commutative: q0 * q1 != q1 * q0.
* @param {!Array.<number>} q0 A 4-element array representing the first
* quaternion.
* @param {!Array.<number>} q1 A 4-element array representing the second
* quaternion.
* @return {Array.<number>} A 4-element array representing the product q0 * q1.
*/
tumbler.multQuaternions = function(q0, q1) {
// Return q0 * q1 (note the order).
var qMult = [
q0[3] * q1[0] + q0[0] * q1[3] + q0[1] * q1[2] - q0[2] * q1[1],
q0[3] * q1[1] - q0[0] * q1[2] + q0[1] * q1[3] + q0[2] * q1[0],
q0[3] * q1[2] + q0[0] * q1[1] - q0[1] * q1[0] + q0[2] * q1[3],
q0[3] * q1[3] - q0[0] * q1[0] - q0[1] * q1[1] - q0[2] * q1[2]
];
return qMult;
};
/**
* Real numbers that are less than this distance apart are considered
* equivalent.
* TODO(dspringer): It seems as though there should be a const like this
* in Closure somewhere (goog.math?).
* @type {number}
*/
tumbler.Trackball.DOUBLE_EPSILON = 1.0e-16;

View File

@@ -0,0 +1,133 @@
// Copyright (c) 2011 The Native Client Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview The tumbler Application object. This object instantiates a
* Trackball object and connects it to the element named |tumbler_content|.
* It also conditionally embeds a debuggable module or a release module into
* the |tumbler_content| element.
*/
// Requires tumbler
// Requires tumbler.Dragger
// Requires tumbler.Trackball
/**
* Constructor for the Application class. Use the run() method to populate
* the object with controllers and wire up the events.
* @constructor
*/
tumbler.Application = function() {
/**
* The native module for the application. This refers to the module loaded
* via the <embed> tag.
* @type {Element}
* @private
*/
this.module_ = null;
/**
* The trackball object.
* @type {tumbler.Trackball}
* @private
*/
this.trackball_ = null;
/**
* The mouse-drag event object.
* @type {tumbler.Dragger}
* @private
*/
this.dragger_ = null;
/**
* The function objects that get attached as event handlers. These are
* cached so that they can be removed when they are no longer needed.
* @type {function}
* @private
*/
this.boundModuleDidLoad_ = null;
}
/**
* The ids used for elements in the DOM. The Tumlber Application expects these
* elements to exist.
* @enum {string}
* @private
*/
tumbler.Application.DomIds_ = {
MODULE: 'tumbler', // The <embed> element representing the NaCl module
VIEW: 'tumbler_view' // The <div> containing the NaCl element.
}
/**
* Called by the module loading function once the module has been loaded.
* @param {?Element} nativeModule The instance of the native module.
*/
tumbler.Application.prototype.moduleDidLoad = function() {
this.module_ = document.getElementById(tumbler.Application.DomIds_.MODULE);
// Unbind the load function.
this.boundModuleDidLoad_ = null;
/**
* Set the camera orientation property on the NaCl module.
* @param {Array.<number>} orientation A 4-element array representing the
* camera orientation as a quaternion.
*/
this.module_.setCameraOrientation = function(orientation) {
var methodString = 'setCameraOrientation ' +
'orientation:' +
JSON.stringify(orientation);
this.postMessage(methodString);
}
this.trackball_ = new tumbler.Trackball();
this.dragger_ = new tumbler.Dragger(this.module_);
this.dragger_.addDragListener(this.trackball_);
}
/**
* Asserts that cond is true; issues an alert and throws an Error otherwise.
* @param {bool} cond The condition.
* @param {String} message The error message issued if cond is false.
*/
tumbler.Application.prototype.assert = function(cond, message) {
if (!cond) {
message = "Assertion failed: " + message;
alert(message);
throw new Error(message);
}
}
/**
* The run() method starts and 'runs' the application. The trackball object
* is allocated and all the events get wired up.
* @param {?String} opt_contentDivName The id of a DOM element in which to
* embed the Native Client module. If unspecified, defaults to
* VIEW. The DOM element must exist.
*/
tumbler.Application.prototype.run = function(opt_contentDivName) {
contentDivName = opt_contentDivName || tumbler.Application.DomIds_.VIEW;
var contentDiv = document.getElementById(contentDivName);
this.assert(contentDiv, "Missing DOM element '" + contentDivName + "'");
// Note that the <EMBED> element is wrapped inside a <DIV>, which has a 'load'
// event listener attached. This method is used instead of attaching the
// 'load' event listener directly to the <EMBED> element to ensure that the
// listener is active before the NaCl module 'load' event fires.
this.boundModuleDidLoad_ = this.moduleDidLoad.bind(this);
contentDiv.addEventListener('load', this.boundModuleDidLoad_, true);
// Load the published .nexe. This includes the 'nacl' attribute which
// shows how to load multi-architecture modules. Each entry in the "nexes"
// object in the .nmf manifest file is a key-value pair: the key is the
// runtime ('x86-32', 'x86-64', etc.); the value is a URL for the desired
// NaCl module. To load the debug versions of your .nexes, set the 'nacl'
// attribute to the _dbg.nmf version of the manifest file.
contentDiv.innerHTML = '<embed id="'
+ tumbler.Application.DomIds_.MODULE + '" '
+ 'src=tumbler.nmf '
+ 'type="application/x-nacl" '
+ 'width="480" height="480" />'
}

View File

@@ -0,0 +1,6 @@
{
"program": {
"x86-64": {"url": "NativeClientTumbler_x64.exe"},
"x86-32": {"url": "NativeClientTumbler.exe"}
}
}

View File

@@ -0,0 +1,91 @@
// Copyright (c) 2011 The Native Client Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview A 3D vector class. Proviudes some utility functions on
* 3-dimentional vectors.
*/
// Requires tumbler
/**
* Constructor for the Vector3 object. This class contains a 3-tuple that
* represents a vector in 3D space.
* @param {?number} opt_x The x-coordinate for this vector. If null or
* undefined, the x-coordinate value is set to 0.
* @param {?number} opt_y The y-coordinate for this vector. If null or
* undefined, the y-coordinate value is set to 0.
* @param {?number} opt_z The z-coordinate for this vector. If null or
* undefined, the z-coordinate value is set to 0.
* @constructor
*/
tumbler.Vector3 = function(opt_x, opt_y, opt_z) {
/**
* The vector's 3-tuple.
* @type {number}
*/
this.x = opt_x || 0;
this.y = opt_y || 0;
this.z = opt_z || 0;
}
/**
* Method to return the magnitude of a Vector3.
* @return {number} the magnitude of the vector.
*/
tumbler.Vector3.prototype.magnitude = function() {
return Math.sqrt(this.dot(this));
}
/**
* Normalize the vector in-place.
* @return {number} the magnitude of the vector.
*/
tumbler.Vector3.prototype.normalize = function() {
var mag = this.magnitude();
if (mag < tumbler.Vector3.DOUBLE_EPSILON)
return 0.0; // |this| is equivalent to the 0-vector, don't normalize.
this.scale(1.0 / mag);
return mag;
}
/**
* Scale the vector in-place by |s|.
* @param {!number} s The scale factor.
*/
tumbler.Vector3.prototype.scale = function(s) {
this.x *= s;
this.y *= s;
this.z *= s;
}
/**
* Compute the dot product: |this| . v.
* @param {!tumbler.Vector3} v The vector to dot.
* @return {number} the result of |this| . v.
*/
tumbler.Vector3.prototype.dot = function(v) {
return this.x * v.x + this.y * v.y + this.z * v.z;
}
/**
* Compute the cross product: |this| X v.
* @param {!tumbler.Vector3} v The vector to cross with.
* @return {tumbler.Vector3} the result of |this| X v.
*/
tumbler.Vector3.prototype.cross = function(v) {
var vCross = new tumbler.Vector3(this.y * v.z - this.z * v.y,
this.z * v.x - this.x * v.z,
this.x * v.y - this.y * v.x);
return vCross;
}
/**
* Real numbers that are less than this distance apart are considered
* equivalent.
* TODO(dspringer): It seems as though there should be a const like this
* in generally available somewhere.
* @type {number}
*/
tumbler.Vector3.DOUBLE_EPSILON = 1.0e-16;

View File

@@ -0,0 +1,92 @@
// Copyright (c) 2011 The Native Client Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef EXAMPLES_TUMBLER_CALLBACK_H_
#define EXAMPLES_TUMBLER_CALLBACK_H_
#include <map>
#include <string>
#include <vector>
namespace tumbler {
class ScriptingBridge;
// Templates used to support method call-backs when a method or property is
// accessed from the browser code.
// Class suite used to publish a method name to Javascript. Typical use is
// like this:
// photo::MethodCallback<Calculator>* calculate_callback_;
// calculate_callback_ =
// new scripting::MethodCallback<Calculator>(this,
// &Calculator::Calculate);
// bridge->AddMethodNamed("calculate", calculate_callback_);
// ...
// delete calculate_callback_;
//
// The caller must delete the callback.
// Methods get parameters as a dictionary that maps parameter names to values.
typedef std::map<std::string, std::string> MethodParameter;
// Pure virtual class used in STL containers.
class MethodCallbackExecutor {
public:
virtual ~MethodCallbackExecutor() {}
virtual void Execute(
const ScriptingBridge& bridge,
const MethodParameter& parameters) = 0;
};
template <class T>
class MethodCallback : public MethodCallbackExecutor {
public:
typedef void (T::*Method)(
const ScriptingBridge& bridge,
const MethodParameter& parameters);
MethodCallback(T* instance, Method method)
: instance_(instance), method_(method) {}
virtual ~MethodCallback() {}
virtual void Execute(
const ScriptingBridge& bridge,
const MethodParameter& parameters) {
// Use "this->" to force C++ to look inside our templatized base class; see
// Effective C++, 3rd Ed, item 43, p210 for details.
((this->instance_)->*(this->method_))(bridge, parameters);
}
private:
T* instance_;
Method method_;
};
template <class T>
class ConstMethodCallback : public MethodCallbackExecutor {
public:
typedef void (T::*ConstMethod)(
const ScriptingBridge& bridge,
const MethodParameter& parameters) const;
ConstMethodCallback(const T* instance, ConstMethod method)
: instance_(instance), const_method_(method) {}
virtual ~ConstMethodCallback() {}
virtual void Execute(
const ScriptingBridge& bridge,
const MethodParameter& parameters) {
// Use "this->" to force C++ to look inside our templatized base class; see
// Effective C++, 3rd Ed, item 43, p210 for details.
((this->instance_)->*(this->const_method_))(bridge, parameters);
}
private:
const T* instance_;
ConstMethod const_method_;
};
} // namespace tumbler
#endif // EXAMPLES_TUMBLER_CALLBACK_H_

267
Demos/NativeClient/cube.cc Normal file
View File

@@ -0,0 +1,267 @@
// Copyright (c) 2011 The Native Client Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cube.h"
#include <algorithm>
#include "shader_util.h"
#include "transforms.h"
namespace tumbler {
static const size_t kVertexCount = 24;
static const int kIndexCount = 36;
Cube::Cube(SharedOpenGLContext opengl_context)
: opengl_context_(opengl_context),
width_(1),
height_(1) {
eye_[0] = eye_[1] = 0.0f;
eye_[2] = 2.0f;
orientation_[0] = 0.0f;
orientation_[1] = 0.0f;
orientation_[2] = 0.0f;
orientation_[3] = 1.0f;
}
Cube::~Cube() {
glDeleteBuffers(3, cube_vbos_);
glDeleteProgram(shader_program_object_);
}
void Cube::PrepareOpenGL() {
CreateShaders();
CreateCube();
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glEnable(GL_DEPTH_TEST);
}
void Cube::Resize(int width, int height) {
width_ = std::max(width, 1);
height_ = std::max(height, 1);
// Set the viewport
glViewport(0, 0, width_, height_);
// Compute the perspective projection matrix with a 60 degree FOV.
GLfloat aspect = static_cast<GLfloat>(width_) / static_cast<GLfloat>(height_);
transform_4x4::LoadIdentity(perspective_proj_);
transform_4x4::Perspective(perspective_proj_, 60.0f, aspect, 1.0f, 20.0f);
}
void Cube::Draw() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Compute a new model-view matrix, then use that to make the composite
// model-view-projection matrix: MVP = MV . P.
GLfloat model_view[16];
ComputeModelViewTransform(model_view);
transform_4x4::Multiply(mvp_matrix_, model_view, perspective_proj_);
glBindBuffer(GL_ARRAY_BUFFER, cube_vbos_[0]);
glUseProgram(shader_program_object_);
glEnableVertexAttribArray(position_location_);
glVertexAttribPointer(position_location_,
3,
GL_FLOAT,
GL_FALSE,
3 * sizeof(GLfloat),
NULL);
glEnableVertexAttribArray(color_location_);
glBindBuffer(GL_ARRAY_BUFFER, cube_vbos_[1]);
glVertexAttribPointer(color_location_,
3,
GL_FLOAT,
GL_FALSE,
3 * sizeof(GLfloat),
NULL);
glUniformMatrix4fv(mvp_location_, 1, GL_FALSE, mvp_matrix_);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cube_vbos_[2]);
glDrawElements(GL_TRIANGLES, kIndexCount, GL_UNSIGNED_SHORT, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
bool Cube::CreateShaders() {
const char vertex_shader_src[] =
"uniform mat4 u_mvpMatrix; \n"
"attribute vec4 a_position; \n"
"attribute vec3 a_color; \n"
"varying lowp vec4 v_color; \n"
"void main() \n"
"{ \n"
" v_color.xyz = a_color; \n"
" v_color.w = 1.0; \n"
" gl_Position = u_mvpMatrix * a_position; \n"
"} \n";
const char fragment_shader_src[] =
"varying lowp vec4 v_color; \n"
"void main() \n"
"{ \n"
" gl_FragColor = v_color; \n"
"} \n";
// Load the shaders and get a linked program object
shader_program_object_ =
shader_util::CreateProgramFromVertexAndFragmentShaders(
vertex_shader_src, fragment_shader_src);
if (shader_program_object_ == 0)
return false;
position_location_ = glGetAttribLocation(shader_program_object_,
"a_position");
color_location_ = glGetAttribLocation(shader_program_object_, "a_color");
mvp_location_ = glGetUniformLocation(shader_program_object_, "u_mvpMatrix");
return true;
}
void Cube::CreateCube() {
static const GLfloat cube_vertices[] = {
// Vertex coordinates interleaved with color values
// Bottom
-0.5f, -0.5f, -0.5f,
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
0.5f, -0.5f, -0.5f,
// Top
-0.5f, 0.5f, -0.5f,
-0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
0.5f, 0.5f, -0.5f,
// Back
-0.5f, -0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
// Front
-0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
// Left
-0.5f, -0.5f, -0.5f,
-0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, -0.5f,
// Right
0.5f, -0.5f, -0.5f,
0.5f, -0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
0.5f, 0.5f, -0.5f
};
static const GLfloat cube_colors[] = {
// Vertex coordinates interleaved with color values
// Bottom
1.0, 0.0, 0.0,
1.0, 0.0, 0.0,
1.0, 0.0, 0.0,
1.0, 0.0, 0.0,
// Top
0.0, 1.0, 0.0,
0.0, 1.0, 0.0,
0.0, 1.0, 0.0,
0.0, 1.0, 0.0,
// Back
0.0, 0.0, 1.0,
0.0, 0.0, 1.0,
0.0, 0.0, 1.0,
0.0, 0.0, 1.0,
// Front
1.0, 0.0, 1.0,
1.0, 0.0, 1.0,
1.0, 0.0, 1.0,
1.0, 0.0, 1.0,
// Left
1.0, 1.0, 0.0,
1.0, 1.0, 0.0,
1.0, 1.0, 0.0,
1.0, 1.0, 0.0,
// Right
0.0, 1.0, 1.0,
0.0, 1.0, 1.0,
0.0, 1.0, 1.0,
0.0, 1.0, 1.0
};
static const GLushort cube_indices[] = {
// Bottom
0, 2, 1,
0, 3, 2,
// Top
4, 5, 6,
4, 6, 7,
// Back
8, 9, 10,
8, 10, 11,
// Front
12, 15, 14,
12, 14, 13,
// Left
16, 17, 18,
16, 18, 19,
// Right
20, 23, 22,
20, 22, 21
};
// Generate the VBOs and upload them to the graphics context.
glGenBuffers(3, cube_vbos_);
glBindBuffer(GL_ARRAY_BUFFER, cube_vbos_[0]);
glBufferData(GL_ARRAY_BUFFER,
kVertexCount * sizeof(GLfloat) * 3,
cube_vertices,
GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, cube_vbos_[1]);
glBufferData(GL_ARRAY_BUFFER,
kVertexCount * sizeof(GLfloat) * 3,
cube_colors,
GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cube_vbos_[2]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
kIndexCount * sizeof(GL_UNSIGNED_SHORT),
cube_indices,
GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
void Cube::ComputeModelViewTransform(GLfloat* model_view) {
// This method takes into account the possiblity that |orientation_|
// might not be normalized.
double sqrx = orientation_[0] * orientation_[0];
double sqry = orientation_[1] * orientation_[1];
double sqrz = orientation_[2] * orientation_[2];
double sqrw = orientation_[3] * orientation_[3];
double sqrLength = 1.0 / (sqrx + sqry + sqrz + sqrw);
transform_4x4::LoadIdentity(model_view);
model_view[0] = (sqrx - sqry - sqrz + sqrw) * sqrLength;
model_view[5] = (-sqrx + sqry - sqrz + sqrw) * sqrLength;
model_view[10] = (-sqrx - sqry + sqrz + sqrw) * sqrLength;
double temp1 = orientation_[0] * orientation_[1];
double temp2 = orientation_[2] * orientation_[3];
model_view[1] = 2.0 * (temp1 + temp2) * sqrLength;
model_view[4] = 2.0 * (temp1 - temp2) * sqrLength;
temp1 = orientation_[0] * orientation_[2];
temp2 = orientation_[1] * orientation_[3];
model_view[2] = 2.0 * (temp1 - temp2) * sqrLength;
model_view[8] = 2.0 * (temp1 + temp2) * sqrLength;
temp1 = orientation_[1] * orientation_[2];
temp2 = orientation_[0] * orientation_[3];
model_view[6] = 2.0 * (temp1 + temp2) * sqrLength;
model_view[9] = 2.0 * (temp1 - temp2) * sqrLength;
model_view[3] = 0.0;
model_view[7] = 0.0;
model_view[11] = 0.0;
// Concatenate the translation to the eye point.
model_view[12] = -eye_[0];
model_view[13] = -eye_[1];
model_view[14] = -eye_[2];
model_view[15] = 1.0;
}
} // namespace tumbler

97
Demos/NativeClient/cube.h Normal file
View File

@@ -0,0 +1,97 @@
// Copyright (c) 2011 The Native Client Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef EXAMPLES_TUMBLER_CUBE_H_
#define EXAMPLES_TUMBLER_CUBE_H_
#include <GLES2/gl2.h>
#include <vector>
#include "opengl_context.h"
#include "opengl_context_ptrs.h"
namespace tumbler {
// The Cube class provides a place to implement 3D rendering. It has a
// frame that it occupies in a browser window.
class Cube {
public:
explicit Cube(SharedOpenGLContext opengl_context);
~Cube();
// Called once when a new RenderContext is first bound to the view. The
// bound context is guaranteed to be current and valid before calling this
// method.
void PrepareOpenGL();
// Called whenever the size of the browser view changes. This method is
// called at least once when the view is first made visible. Clamps the
// sizes to 1.
void Resize(int width, int height);
// Called every time the view need to be drawn. The bound context is
// guaranteed to be current and valid before this method is called. The
// visible portion of the context is flushed to the browser after this
// method returns.
void Draw();
// Accessor for width and height. To change these, call Resize.
const int width() const {
return width_;
}
const int height() const {
return height_;
}
// Accessor/mutator for the camera orientation.
void GetOrientation(std::vector<float>* orientation) const {
if (!orientation)
return;
(*orientation)[0] = static_cast<float>(orientation_[0]);
(*orientation)[1] = static_cast<float>(orientation_[1]);
(*orientation)[2] = static_cast<float>(orientation_[2]);
(*orientation)[3] = static_cast<float>(orientation_[3]);
}
void SetOrientation(const std::vector<float>& orientation) {
orientation_[0] = static_cast<GLfloat>(orientation[0]);
orientation_[1] = static_cast<GLfloat>(orientation[1]);
orientation_[2] = static_cast<GLfloat>(orientation[2]);
orientation_[3] = static_cast<GLfloat>(orientation[3]);
}
private:
// Create the shaders used to draw the cube, and link them into a program.
// Initializes |shader_progam_object_|, |position_loction_| and
// |mvp_location_|.
bool CreateShaders();
// Generates a cube as a series of GL_TRIANGLE_STRIPs, and initializes
// |index_count_| to the number of indices in the index list used as a VBO.
// Creates the |vbo_ids_| required for the vertex and index data and uploads
// the the VBO data.
void CreateCube();
// Build up the model-view transform from the eye and orienation properties.
// Assumes that |model_view| is a 4x4 matrix.
void ComputeModelViewTransform(GLfloat* model_view);
SharedOpenGLContext opengl_context_;
int width_;
int height_;
GLuint shader_program_object_; // The compiled shaders.
GLint position_location_; // The position attribute location.
GLint color_location_; // The color attribute location.
GLint mvp_location_; // The Model-View-Projection composite matrix.
GLuint cube_vbos_[3];
GLfloat eye_[3]; // The eye point of the virtual camera.
// The orientation of the virtual camera stored as a quaternion. The
// quaternion is laid out as {{x, y, z}, w}.
GLfloat orientation_[4];
GLfloat perspective_proj_[16];
GLfloat mvp_matrix_[16];
};
} // namespace tumbler
#endif // EXAMPLES_TUMBLER_CUBE_H_

View File

@@ -0,0 +1,72 @@
// Copyright (c) 2011 The Native Client Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "opengl_context.h"
#include <pthread.h>
#include "ppapi/gles2/gl2ext_ppapi.h"
namespace {
// This is called by the brower when the 3D context has been flushed to the
// browser window.
void FlushCallback(void* data, int32_t result) {
static_cast<tumbler::OpenGLContext*>(data)->set_flush_pending(false);
}
} // namespace
namespace tumbler {
OpenGLContext::OpenGLContext(pp::Instance* instance)
: pp::Graphics3DClient_Dev(instance),
flush_pending_(false) {
pp::Module* module = pp::Module::Get();
assert(module);
gles2_interface_ = static_cast<const struct PPB_OpenGLES2_Dev*>(
module->GetBrowserInterface(PPB_OPENGLES2_DEV_INTERFACE));
assert(gles2_interface_);
}
OpenGLContext::~OpenGLContext() {
glSetCurrentContextPPAPI(0);
}
bool OpenGLContext::MakeContextCurrent(pp::Instance* instance) {
if (instance == NULL) {
glSetCurrentContextPPAPI(0);
return false;
}
// Lazily create the Pepper context.
if (context_.is_null()) {
context_ = pp::Context3D_Dev(*instance, 0, pp::Context3D_Dev(), NULL);
if (context_.is_null()) {
glSetCurrentContextPPAPI(0);
return false;
}
surface_ = pp::Surface3D_Dev(*instance, 0, NULL);
context_.BindSurfaces(surface_, surface_);
instance->BindGraphics(surface_);
}
glSetCurrentContextPPAPI(context_.pp_resource());
return true;
}
void OpenGLContext::InvalidateContext(pp::Instance* instance) {
if (instance == NULL)
return;
// Unbind the existing surface and re-bind to null surfaces.
instance->BindGraphics(pp::Surface3D_Dev());
context_.BindSurfaces(pp::Surface3D_Dev(), pp::Surface3D_Dev());
glSetCurrentContextPPAPI(0);
}
void OpenGLContext::FlushContext() {
if (flush_pending()) {
// A flush is pending so do nothing; just drop this flush on the floor.
return;
}
set_flush_pending(true);
surface_.SwapBuffers(pp::CompletionCallback(&FlushCallback, this));
}
} // namespace tumbler

View File

@@ -0,0 +1,90 @@
// Copyright (c) 2011 The Native Client Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef EXAMPLES_TUMBLER_OPENGL_CONTEXT_H_
#define EXAMPLES_TUMBLER_OPENGL_CONTEXT_H_
///
/// @file
/// OpenGLContext manages the OpenGL context in the browser that is associated
/// with a @a pp::Instance instance.
///
#include <pthread.h>
#include <algorithm>
#include <string>
#include "opengl_context_ptrs.h"
#include "ppapi/c/dev/ppb_opengles_dev.h"
#include "ppapi/cpp/dev/context_3d_dev.h"
#include "ppapi/cpp/dev/graphics_3d_client_dev.h"
#include "ppapi/cpp/dev/graphics_3d_dev.h"
#include "ppapi/cpp/dev/surface_3d_dev.h"
#include "ppapi/cpp/instance.h"
namespace tumbler {
/// OpenGLContext manages an OpenGL rendering context in the browser.
///
class OpenGLContext : public pp::Graphics3DClient_Dev {
public:
explicit OpenGLContext(pp::Instance* instance);
/// Release all the in-browser resources used by this context, and make this
/// context invalid.
virtual ~OpenGLContext();
/// The Graphics3DClient interfcace.
virtual void Graphics3DContextLost() {
assert(!"Unexpectedly lost graphics context");
}
/// Make @a this the current 3D context in @a instance.
/// @param instance The instance of the NaCl module that will receive the
/// the current 3D context.
/// @return success.
bool MakeContextCurrent(pp::Instance* instance);
/// Flush the contents of this context to the browser's 3D device.
void FlushContext();
/// Make the underlying 3D device invalid, so that any subsequent rendering
/// commands will have no effect. The next call to MakeContextCurrent() will
/// cause the underlying 3D device to get rebound and start receiving
/// receiving rendering commands again. Use InvalidateContext(), for
/// example, when resizing the context's viewing area.
void InvalidateContext(pp::Instance* instance);
/// The OpenGL ES 2.0 interface.
const struct PPB_OpenGLES2_Dev* gles2() const {
return gles2_interface_;
}
/// The PP_Resource needed to make GLES2 calls through the Pepper interface.
const PP_Resource gl_context() const {
return context_.pp_resource();
}
/// Indicate whether a flush is pending. This can only be called from the
/// main thread; it is not thread safe.
bool flush_pending() const {
return flush_pending_;
}
void set_flush_pending(bool flag) {
flush_pending_ = flag;
}
private:
pp::Context3D_Dev context_;
pp::Surface3D_Dev surface_;
bool flush_pending_;
const struct PPB_OpenGLES2_Dev* gles2_interface_;
};
} // namespace tumbler
#endif // EXAMPLES_TUMBLER_OPENGL_CONTEXT_H_

View File

@@ -0,0 +1,22 @@
// Copyright (c) 2011 The Native Client Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef EXAMPLES_TUMBLER_OPENGL_CONTEXT_PTRS_H_
#define EXAMPLES_TUMBLER_OPENGL_CONTEXT_PTRS_H_
// A convenience wrapper for a shared OpenGLContext pointer type. As other
// smart pointer types are needed, add them here.
#include <tr1/memory>
namespace tumbler {
class OpenGLContext;
typedef std::tr1::shared_ptr<OpenGLContext> SharedOpenGLContext;
} // namespace tumbler
#endif // EXAMPLES_TUMBLER_OPENGL_CONTEXT_PTRS_H_

View File

@@ -0,0 +1,27 @@
project "NativeClientTumbler"
kind "ConsoleApp"
targetdir "bin_html"
includedirs { "." }
--libdirs {}
links {
"ppapi_gles2",
"ppapi",
"ppapi_cpp",
"ppruntime"
}
files {
"cube.cc",
"opengl_context.cc",
"scripting_bridge.cc",
"shader_util.cc",
"transforms.cc",
"tumbler.cc",
"tumbler_module.cc"
}

View File

@@ -0,0 +1,95 @@
// Copyright (c) 2011 The Native Client Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "scripting_bridge.h"
namespace {
const char* const kWhiteSpaceCharacters = " \t";
// Helper function to pull out the next token in |token_string|. A token is
// delimited by whitespace. Scanning begins at |*pos|, if pos goes beyond the
// end of |token_string|, it is set to std::string::npos and an empty string
// is returned. On return, |*pos| will point to the beginning of the next
// token. |pos| must not be NULL.
const std::string ScanToken(const std::string& token_string, size_t* pos) {
std::string token;
if (*pos == std::string::npos) {
return token;
}
size_t token_start_pos = token_string.find_first_not_of(kWhiteSpaceCharacters,
*pos);
size_t token_end_pos = token_string.find_first_of(kWhiteSpaceCharacters,
token_start_pos);
if (token_start_pos != std::string::npos) {
token = token_string.substr(token_start_pos, token_end_pos);
}
*pos = token_end_pos;
return token;
}
// Take a string of the form 'name:value' and split it into two strings, one
// containing 'name' and the other 'value'. If the ':' separator is missing,
// or is the last character in |parameter|, |parameter| is copied to
// |param_name|, |param_value| is left unchanged and false is returned.
bool ParseParameter(const std::string& parameter,
std::string* param_name,
std::string* param_value) {
bool success = false;
size_t sep_pos = parameter.find_first_of(':');
if (sep_pos != std::string::npos) {
*param_name = parameter.substr(0, sep_pos);
if (sep_pos < parameter.length() - 1) {
*param_value = parameter.substr(sep_pos + 1);
success = true;
} else {
success = false;
}
} else {
*param_name = parameter;
success = false;
}
return success;
}
} // namespace
namespace tumbler {
bool ScriptingBridge::AddMethodNamed(const std::string& method_name,
SharedMethodCallbackExecutor method) {
if (method_name.size() == 0 || method == NULL)
return false;
method_dictionary_.insert(
std::pair<std::string, SharedMethodCallbackExecutor>(method_name,
method));
return true;
}
bool ScriptingBridge::InvokeMethod(const std::string& method) {
size_t current_pos = 0;
const std::string method_name = ScanToken(method, &current_pos);
MethodDictionary::iterator method_iter;
method_iter = method_dictionary_.find(method_name);
if (method_iter != method_dictionary_.end()) {
// Pull out the method parameters and build a dictionary that maps
// parameter names to values.
std::map<std::string, std::string> param_dict;
while (current_pos != std::string::npos) {
const std::string parameter = ScanToken(method, &current_pos);
if (parameter.length()) {
std::string param_name;
std::string param_value;
if (ParseParameter(parameter, &param_name, &param_value)) {
// Note that duplicate parameter names will override each other. The
// last one in the method string will be used.
param_dict[param_name] = param_value;
}
}
}
(*method_iter->second).Execute(*this, param_dict);
return true;
}
return false;
}
} // namespace tumbler

View File

@@ -0,0 +1,52 @@
// Copyright (c) 2011 The Native Client Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef EXAMPLES_TUMBLER_SCRIPTING_BRIDGE_H_
#define EXAMPLES_TUMBLER_SCRIPTING_BRIDGE_H_
#include <map>
#include <string>
#include <tr1/memory>
#include <vector>
#include "callback.h"
#include "ppapi/cpp/var.h"
namespace tumbler {
class MethodCallbackExecutor;
// This class handles the interface between the browser and the NaCl module.
// There is a single point of entry from the browser: postMessage(). The
// string passed to postMessage() has this format:
// 'function_name arg_name0:arg_0 arg_name1:arg1 ...'
// The arguments have undetermined type; they are placed in a map of argument
// names and values. Values are all strings, it is up to the target code to
// do any type coercion.
// Methods called by the scripting bridge must have a signature like this:
// void Method(const ScriptingBridge& bridge,
// const ParameterDictionary&);
class ScriptingBridge {
public:
// Shared pointer type used in the method map.
typedef std::tr1::shared_ptr<MethodCallbackExecutor>
SharedMethodCallbackExecutor;
virtual ~ScriptingBridge() {}
// Causes |method_name| to be published as a method that can be called via
// postMessage() from the browser. Associates this method with |method|.
bool AddMethodNamed(const std::string& method_name,
SharedMethodCallbackExecutor method);
bool InvokeMethod(const std::string& method);
private:
typedef std::map<std::string, SharedMethodCallbackExecutor> MethodDictionary;
MethodDictionary method_dictionary_;
};
} // namespace tumbler
#endif // EXAMPLES_TUMBLER_SCRIPTING_BRIDGE_H_

View File

@@ -0,0 +1,95 @@
// Copyright (c) 2011 The Native Client Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "shader_util.h"
#include <stdlib.h>
#include <stdio.h>
namespace shader_util {
GLuint CreateShaderOfType(GLenum type, const char *shader_src) {
GLuint shader;
GLint compiled;
// Create the shader object
shader = glCreateShader(type);
if (shader == 0)
return 0;
// Load and compile the shader source
glShaderSource(shader, 1, &shader_src, NULL);
glCompileShader(shader);
// Check the compile status
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
if (compiled == 0) {
GLint info_len = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_len);
if (info_len > 1) {
char* info_log = reinterpret_cast<char*>(malloc(sizeof(char) * info_len));
glGetShaderInfoLog(shader, info_len, NULL, info_log);
// TODO(dspringer): We could really use a logging API.
printf("Error compiling shader:\n%s\n", info_log);
free(info_log);
}
glDeleteShader(shader);
return 0;
}
return shader;
}
GLuint CreateProgramFromVertexAndFragmentShaders(
const char *vertex_shader_src, const char *fragment_shader_src) {
GLuint vertex_shader;
GLuint fragment_shader;
GLuint program_object;
GLint linked;
// Load the vertex/fragment shaders
vertex_shader = CreateShaderOfType(GL_VERTEX_SHADER, vertex_shader_src);
if (vertex_shader == 0)
return 0;
fragment_shader = CreateShaderOfType(GL_FRAGMENT_SHADER, fragment_shader_src);
if (fragment_shader == 0) {
glDeleteShader(vertex_shader);
return 0;
}
// Create the program object and attach the shaders.
program_object = glCreateProgram();
if (program_object == 0)
return 0;
glAttachShader(program_object, vertex_shader);
glAttachShader(program_object, fragment_shader);
// Link the program
glLinkProgram(program_object);
// Check the link status
glGetProgramiv(program_object, GL_LINK_STATUS, &linked);
if (linked == 0) {
GLint info_len = 0;
glGetProgramiv(program_object, GL_INFO_LOG_LENGTH, &info_len);
if (info_len > 1) {
char* info_log = reinterpret_cast<char*>(malloc(info_len));
glGetProgramInfoLog(program_object, info_len, NULL, info_log);
// TODO(dspringer): We could really use a logging API.
printf("Error linking program:\n%s\n", info_log);
free(info_log);
}
glDeleteProgram(program_object);
return 0;
}
// Delete these here because they are attached to the program object.
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
return program_object;
}
} // namespace shader_util

View File

@@ -0,0 +1,29 @@
// Copyright (c) 2011 The Native Client Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Some simple helper functions that load shaders and create program objects.
#ifndef EXAMPLES_TUMBLER_SHADER_UTIL_H_
#define EXAMPLES_TUMBLER_SHADER_UTIL_H_
#include <GLES2/gl2.h>
namespace shader_util {
// Load and compile a shader. |type| can be one of GL_VERTEX_SHADER or
// GL_FRAGMENT_SHADER. Returns a non-0 value representing the compiled
// shader on success, 0 on failure. The caller is responsible for deleting
// the returned shader using glDeleteShader().
GLuint CreateShaderOfType(GLenum type, const char *shader_src);
// Load and compile the vertex and fragment shaders, then link these together
// into a complete program. Returns a non-0 value representing the program on,
// success or 0 on failure. The caller is responsible for deleting the
// returned program using glDeleteProgram().
GLuint CreateProgramFromVertexAndFragmentShaders(
const char *vertex_shader_src, const char *fragment_shader_src);
} // namespace shader_util
#endif // EXAMPLES_TUMBLER_SHADER_UTIL_H_

View File

@@ -0,0 +1,116 @@
// Copyright (c) 2011 The Native Client Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "transforms.h"
#include <math.h>
#include <string.h>
#include <GLES2/gl2.h>
namespace transform_4x4 {
static const GLfloat kPI = 3.1415926535897932384626433832795f;
void Translate(GLfloat* m, GLfloat tx, GLfloat ty, GLfloat tz) {
m[12] += (m[0] * tx + m[4] * ty + m[8] * tz);
m[13] += (m[1] * tx + m[5] * ty + m[9] * tz);
m[14] += (m[2] * tx + m[6] * ty + m[10] * tz);
m[15] += (m[3] * tx + m[7] * ty + m[11] * tz);
}
void Frustum(GLfloat* m,
GLfloat left,
GLfloat right,
GLfloat bottom,
GLfloat top,
GLfloat near_z,
GLfloat far_z) {
GLfloat delta_x = right - left;
GLfloat delta_y = top - bottom;
GLfloat delta_z = far_z - near_z;
GLfloat frustum[16];
if ((near_z <= 0.0f) || (far_z <= 0.0f) ||
(delta_x <= 0.0f) || (delta_y <= 0.0f) || (delta_z <= 0.0f))
return;
frustum[0] = 2.0f * near_z / delta_x;
frustum[1] = frustum[2] = frustum[3] = 0.0f;
frustum[5] = 2.0f * near_z / delta_y;
frustum[4] = frustum[6] = frustum[7] = 0.0f;
frustum[8] = (right + left) / delta_x;
frustum[9] = (top + bottom) / delta_y;
frustum[10] = -(near_z + far_z) / delta_z;
frustum[11] = -1.0f;
frustum[14] = -2.0f * near_z * far_z / delta_z;
frustum[12] = frustum[13] = frustum[15] = 0.0f;
transform_4x4::Multiply(m, frustum, m);
}
void Perspective(GLfloat* m,
GLfloat fovy,
GLfloat aspect,
GLfloat near_z,
GLfloat far_z) {
GLfloat frustum_w, frustum_h;
frustum_h = tanf((fovy * 0.5f) / 180.0f * kPI) * near_z;
frustum_w = frustum_h * aspect;
transform_4x4::Frustum(m, -frustum_w, frustum_w, -frustum_h, frustum_h,
near_z, far_z);
}
void Multiply(GLfloat *m, GLfloat *a, GLfloat* b) {
GLfloat tmp[16];
// tmp = a . b
GLfloat a0, a1, a2, a3;
a0 = a[0];
a1 = a[1];
a2 = a[2];
a3 = a[3];
tmp[0] = a0 * b[0] + a1 * b[4] + a2 * b[8] + a3 * b[12];
tmp[1] = a0 * b[1] + a1 * b[5] + a2 * b[9] + a3 * b[13];
tmp[2] = a0 * b[2] + a1 * b[6] + a2 * b[10] + a3 * b[14];
tmp[3] = a0 * b[3] + a1 * b[7] + a2 * b[11] + a3 * b[15];
a0 = a[4];
a1 = a[5];
a2 = a[6];
a3 = a[7];
tmp[4] = a0 * b[0] + a1 * b[4] + a2 * b[8] + a3 * b[12];
tmp[5] = a0 * b[1] + a1 * b[5] + a2 * b[9] + a3 * b[13];
tmp[6] = a0 * b[2] + a1 * b[6] + a2 * b[10] + a3 * b[14];
tmp[7] = a0 * b[3] + a1 * b[7] + a2 * b[11] + a3 * b[15];
a0 = a[8];
a1 = a[9];
a2 = a[10];
a3 = a[11];
tmp[8] = a0 * b[0] + a1 * b[4] + a2 * b[8] + a3 * b[12];
tmp[9] = a0 * b[1] + a1 * b[5] + a2 * b[9] + a3 * b[13];
tmp[10] = a0 * b[2] + a1 * b[6] + a2 * b[10] + a3 * b[14];
tmp[11] = a0 * b[3] + a1 * b[7] + a2 * b[11] + a3 * b[15];
a0 = a[12];
a1 = a[13];
a2 = a[14];
a3 = a[15];
tmp[12] = a0 * b[0] + a1 * b[4] + a2 * b[8] + a3 * b[12];
tmp[13] = a0 * b[1] + a1 * b[5] + a2 * b[9] + a3 * b[13];
tmp[14] = a0 * b[2] + a1 * b[6] + a2 * b[10] + a3 * b[14];
tmp[15] = a0 * b[3] + a1 * b[7] + a2 * b[11] + a3 * b[15];
memcpy(m, tmp, sizeof(GLfloat) * 4 * 4);
}
void LoadIdentity(GLfloat* m) {
memset(m, 0, sizeof(GLfloat) * 4 * 4);
m[0] = m[5] = m[10] = m[15] = 1.0f;
}
} // namespace transform_4x4

View File

@@ -0,0 +1,45 @@
// Copyright (c) 2011 The Native Client Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef EXAMPLES_TUMBLER_TRANSFORMS_H_
#define EXAMPLES_TUMBLER_TRANSFORMS_H_
#include <GLES2/gl2.h>
// A very simple set of 4x4 matrix routines. In all these routines, the input
// matrix is assumed to be a 4x4 of GLfloats.
namespace transform_4x4 {
// Pre-multply |m| with a projection transformation 4x4 matrix from a
// truncated pyramid viewing frustum.
void Frustum(GLfloat* m,
GLfloat left,
GLfloat right,
GLfloat bottom,
GLfloat top,
GLfloat near_z,
GLfloat far_z);
// Replace |m| with the 4x4 identity matrix.
void LoadIdentity(GLfloat* m);
// |m| <- |a| . |b|. |m| can point at the same memory as either |a| or |b|.
void Multiply(GLfloat *m, GLfloat *a, GLfloat* b);
// Pre-multiply |m| with a single-point perspective matrix based on the viewing
// frustum whose view angle is |fovy|.
void Perspective(GLfloat* m,
GLfloat fovy,
GLfloat aspect,
GLfloat near_z,
GLfloat far_z);
// Pre-multiply |m| with a matrix that represents a translation by |tx|, |ty|,
// |tz|.
void Translate(GLfloat* m, GLfloat tx, GLfloat ty, GLfloat tz);
} // namespace transform_4x4
#endif // EXAMPLES_TUMBLER_TRANSFORMS_H_

View File

@@ -0,0 +1,136 @@
// Copyright (c) 2011 The Native Client Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "tumbler.h"
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#include "cube.h"
#include "opengl_context.h"
#include "scripting_bridge.h"
#include "ppapi/cpp/rect.h"
#include "ppapi/cpp/size.h"
#include "ppapi/cpp/var.h"
namespace {
const size_t kQuaternionElementCount = 4;
const char* const kArrayStartCharacter = "[";
const char* const kArrayEndCharacter = "]";
const char* const kArrayDelimiter = ",";
// Return the value of parameter named |param_name| from |parameters|. If
// |param_name| doesn't exist, then return an empty string.
std::string GetParameterNamed(
const std::string& param_name,
const tumbler::MethodParameter& parameters) {
tumbler::MethodParameter::const_iterator i =
parameters.find(param_name);
if (i == parameters.end()) {
return "";
}
return i->second;
}
// Convert the JSON string |array| into a vector of floats. |array| is
// expected to be a string bounded by '[' and ']', and a comma-delimited list
// of numbers. Any errors result in the return of an empty array.
std::vector<float> CreateArrayFromJSON(const std::string& json_array) {
std::vector<float> float_array;
size_t array_start_pos = json_array.find_first_of(kArrayStartCharacter);
size_t array_end_pos = json_array.find_last_of(kArrayEndCharacter);
if (array_start_pos == std::string::npos ||
array_end_pos == std::string::npos)
return float_array; // Malformed JSON: missing '[' or ']'.
// Pull out the array elements.
size_t token_pos = array_start_pos + 1;
while (token_pos < array_end_pos) {
float_array.push_back(strtof(json_array.data() + token_pos, NULL));
size_t delim_pos = json_array.find_first_of(kArrayDelimiter, token_pos);
if (delim_pos == std::string::npos)
break;
token_pos = delim_pos + 1;
}
return float_array;
}
} // namespace
namespace tumbler {
Tumbler::Tumbler(PP_Instance instance)
: pp::Instance(instance),
cube_(NULL) {
}
Tumbler::~Tumbler() {
// Destroy the cube view while GL context is current.
opengl_context_->MakeContextCurrent(this);
delete cube_;
}
bool Tumbler::Init(uint32_t /* argc */,
const char* /* argn */[],
const char* /* argv */[]) {
// Add all the methods to the scripting bridge.
ScriptingBridge::SharedMethodCallbackExecutor set_orientation_method(
new tumbler::MethodCallback<Tumbler>(
this, &Tumbler::SetCameraOrientation));
scripting_bridge_.AddMethodNamed("setCameraOrientation",
set_orientation_method);
return true;
}
void Tumbler::HandleMessage(const pp::Var& message) {
if (!message.is_string())
return;
scripting_bridge_.InvokeMethod(message.AsString());
}
void Tumbler::DidChangeView(const pp::Rect& position, const pp::Rect& clip) {
int cube_width = cube_ ? cube_->width() : 0;
int cube_height = cube_ ? cube_->height() : 0;
if (position.size().width() == cube_width &&
position.size().height() == cube_height)
return; // Size didn't change, no need to update anything.
if (opengl_context_ == NULL)
opengl_context_.reset(new OpenGLContext(this));
opengl_context_->InvalidateContext(this);
if (!opengl_context_->MakeContextCurrent(this))
return;
if (cube_ == NULL) {
cube_ = new Cube(opengl_context_);
cube_->PrepareOpenGL();
}
cube_->Resize(position.size().width(), position.size().height());
DrawSelf();
}
void Tumbler::DrawSelf() {
if (cube_ == NULL || opengl_context_ == NULL)
return;
opengl_context_->MakeContextCurrent(this);
cube_->Draw();
opengl_context_->FlushContext();
}
void Tumbler::SetCameraOrientation(
const tumbler::ScriptingBridge& bridge,
const tumbler::MethodParameter& parameters) {
// |parameters| is expected to contain one object named "orientation", whose
// value is a JSON string that represents an array of four floats.
if (parameters.size() != 1 || cube_ == NULL)
return;
std::string orientation_desc = GetParameterNamed("orientation", parameters);
std::vector<float> orientation = CreateArrayFromJSON(orientation_desc);
if (orientation.size() != kQuaternionElementCount) {
return;
}
cube_->SetOrientation(orientation);
DrawSelf();
}
} // namespace tumbler

View File

@@ -0,0 +1,64 @@
// Copyright (c) 2011 The Native Client Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef EXAMPLES_TUMBLER_TUMBLER_H_
#define EXAMPLES_TUMBLER_TUMBLER_H_
#include <pthread.h>
#include <map>
#include <vector>
#include "cube.h"
#include "opengl_context.h"
#include "opengl_context_ptrs.h"
#include "scripting_bridge.h"
#include "ppapi/cpp/instance.h"
namespace tumbler {
class Tumbler : public pp::Instance {
public:
explicit Tumbler(PP_Instance instance);
// The dtor makes the 3D context current before deleting the cube view, then
// destroys the 3D context both in the module and in the browser.
virtual ~Tumbler();
// Called by the browser when the NaCl module is loaded and all ready to go.
virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]);
// Called whenever the in-browser window changes size.
virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip);
// Called by the browser to handle the postMessage() call in Javascript.
virtual void HandleMessage(const pp::Var& message);
// Bind and publish the module's methods to JavaScript.
void InitializeMethods(ScriptingBridge* bridge);
// Set the camera orientation to the quaternion in |args[0]|. |args| must
// have length at least 1; the first element is expeted to be an Array
// object containing 4 floating point number elements (the quaternion).
// This method is bound to the JavaScript "setCameraOrientation" method and
// is called like this:
// module.setCameraOrientation([0.0, 1.0, 0.0, 0.0]);
void SetCameraOrientation(
const tumbler::ScriptingBridge& bridge,
const tumbler::MethodParameter& parameters);
// Called to draw the contents of the module's browser area.
void DrawSelf();
private:
// Browser connectivity and scripting support.
ScriptingBridge scripting_bridge_;
SharedOpenGLContext opengl_context_;
// Wouldn't it be awesome if we had boost::scoped_ptr<>?
Cube* cube_;
};
} // namespace tumbler
#endif // EXAMPLES_TUMBLER_TUMBLER_H_

View File

@@ -0,0 +1,45 @@
// Copyright (c) 2011 The Native Client Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "tumbler.h"
#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/module.h"
#include "ppapi/gles2/gl2ext_ppapi.h"
/// The Module class. The browser calls the CreateInstance() method to create
/// an instance of your NaCl module on the web page. The browser creates a new
/// instance for each <embed> tag with type="application/x-nacl".
class TumberModule : public pp::Module {
public:
TumberModule() : pp::Module() {}
virtual ~TumberModule() {
glTerminatePPAPI();
}
/// Called by the browser when the module is first loaded and ready to run.
/// This is called once per module, not once per instance of the module on
/// the page.
virtual bool Init() {
return glInitializePPAPI(get_browser_interface()) == GL_TRUE;
}
/// Create and return a Tumbler instance object.
/// @param[in] instance The browser-side instance.
/// @return the plugin-side instance.
virtual pp::Instance* CreateInstance(PP_Instance instance) {
return new tumbler::Tumbler(instance);
}
};
namespace pp {
/// Factory function called by the browser when the module is first loaded.
/// The browser keeps a singleton of this module. It calls the
/// CreateInstance() method on the object you return to make instances. There
/// is one instance per <embed> tag on the page. This is the main binding
/// point for your NaCl module with the browser.
Module* CreateModule() {
return new TumberModule();
}
} // namespace pp

15
msvc/nacl.sh Normal file
View File

@@ -0,0 +1,15 @@
echo Only tested using a Cygwin Bash Shell with make.exe available
export AR=/cygdrive/f/sdks/native_client_sdk_0_5_984/toolchain/win_x86/bin/nacl-ar.exe
export CC=/cygdrive/f/sdks/native_client_sdk_0_5_984/toolchain/win_x86/bin/nacl-gcc.exe
export CXX=/cygdrive/f/sdks/native_client_sdk_0_5_984/toolchain/win_x86/bin/nacl-g++.exe
./premake4 --with-nacl gmake
cd gmake
export config=release32
make
export config=release64
make

View File

@@ -63,15 +63,18 @@ end
include "../src/BulletMultiThreaded/GpuSoftBodySolvers/DX11"
end
if not _OPTIONS["with-dx11"] and not _OPTIONS["with-nacl"] then
include "../Demos"
include "../Extras"
end
if _OPTIONS["with-nacl"] then
include "../Demos/NativeClient"
else
include "../src/LinearMath"
include "../src/BulletCollision"
include "../src/BulletDynamics"
include "../src/BulletSoftBody"
end