more premake4 fun and tests
This commit is contained in:
41
Demos/DX11ClothDemo/premake4.lua
Normal file
41
Demos/DX11ClothDemo/premake4.lua
Normal 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
|
||||||
22
Demos/NativeClient/bin_html/bind.js
Normal file
22
Demos/NativeClient/bin_html/bind.js
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
134
Demos/NativeClient/bin_html/dragger.js
Normal file
134
Demos/NativeClient/bin_html/dragger.js
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
9
Demos/NativeClient/bin_html/httpd.cmd
Normal file
9
Demos/NativeClient/bin_html/httpd.cmd
Normal 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
|
||||||
114
Demos/NativeClient/bin_html/httpd.py
Normal file
114
Demos/NativeClient/bin_html/httpd.py
Normal 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)
|
||||||
33
Demos/NativeClient/bin_html/index.html
Normal file
33
Demos/NativeClient/bin_html/index.html
Normal 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>
|
||||||
296
Demos/NativeClient/bin_html/trackball.js
Normal file
296
Demos/NativeClient/bin_html/trackball.js
Normal 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;
|
||||||
133
Demos/NativeClient/bin_html/tumbler.js
Normal file
133
Demos/NativeClient/bin_html/tumbler.js
Normal 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" />'
|
||||||
|
}
|
||||||
6
Demos/NativeClient/bin_html/tumbler.nmf
Normal file
6
Demos/NativeClient/bin_html/tumbler.nmf
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"program": {
|
||||||
|
"x86-64": {"url": "NativeClientTumbler_x64.exe"},
|
||||||
|
"x86-32": {"url": "NativeClientTumbler.exe"}
|
||||||
|
}
|
||||||
|
}
|
||||||
91
Demos/NativeClient/bin_html/vector3.js
Normal file
91
Demos/NativeClient/bin_html/vector3.js
Normal 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;
|
||||||
92
Demos/NativeClient/callback.h
Normal file
92
Demos/NativeClient/callback.h
Normal 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
267
Demos/NativeClient/cube.cc
Normal 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
97
Demos/NativeClient/cube.h
Normal 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_
|
||||||
72
Demos/NativeClient/opengl_context.cc
Normal file
72
Demos/NativeClient/opengl_context.cc
Normal 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
|
||||||
|
|
||||||
90
Demos/NativeClient/opengl_context.h
Normal file
90
Demos/NativeClient/opengl_context.h
Normal 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_
|
||||||
|
|
||||||
22
Demos/NativeClient/opengl_context_ptrs.h
Normal file
22
Demos/NativeClient/opengl_context_ptrs.h
Normal 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_
|
||||||
|
|
||||||
27
Demos/NativeClient/premake4.lua
Normal file
27
Demos/NativeClient/premake4.lua
Normal 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"
|
||||||
|
}
|
||||||
95
Demos/NativeClient/scripting_bridge.cc
Normal file
95
Demos/NativeClient/scripting_bridge.cc
Normal 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, ¤t_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, ¤t_pos);
|
||||||
|
if (parameter.length()) {
|
||||||
|
std::string param_name;
|
||||||
|
std::string param_value;
|
||||||
|
if (ParseParameter(parameter, ¶m_name, ¶m_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
|
||||||
52
Demos/NativeClient/scripting_bridge.h
Normal file
52
Demos/NativeClient/scripting_bridge.h
Normal 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_
|
||||||
95
Demos/NativeClient/shader_util.cc
Normal file
95
Demos/NativeClient/shader_util.cc
Normal 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
|
||||||
29
Demos/NativeClient/shader_util.h
Normal file
29
Demos/NativeClient/shader_util.h
Normal 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_
|
||||||
116
Demos/NativeClient/transforms.cc
Normal file
116
Demos/NativeClient/transforms.cc
Normal 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
|
||||||
45
Demos/NativeClient/transforms.h
Normal file
45
Demos/NativeClient/transforms.h
Normal 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_
|
||||||
|
|
||||||
136
Demos/NativeClient/tumbler.cc
Normal file
136
Demos/NativeClient/tumbler.cc
Normal 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
|
||||||
|
|
||||||
64
Demos/NativeClient/tumbler.h
Normal file
64
Demos/NativeClient/tumbler.h
Normal 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_
|
||||||
45
Demos/NativeClient/tumbler_module.cc
Normal file
45
Demos/NativeClient/tumbler_module.cc
Normal 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
15
msvc/nacl.sh
Normal 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
|
||||||
|
|
||||||
@@ -63,15 +63,18 @@ end
|
|||||||
include "../src/BulletMultiThreaded/GpuSoftBodySolvers/DX11"
|
include "../src/BulletMultiThreaded/GpuSoftBodySolvers/DX11"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if not _OPTIONS["with-dx11"] and not _OPTIONS["with-nacl"] then
|
if not _OPTIONS["with-dx11"] and not _OPTIONS["with-nacl"] then
|
||||||
include "../Demos"
|
include "../Demos"
|
||||||
include "../Extras"
|
include "../Extras"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if _OPTIONS["with-nacl"] then
|
||||||
|
include "../Demos/NativeClient"
|
||||||
|
else
|
||||||
include "../src/LinearMath"
|
include "../src/LinearMath"
|
||||||
include "../src/BulletCollision"
|
include "../src/BulletCollision"
|
||||||
include "../src/BulletDynamics"
|
include "../src/BulletDynamics"
|
||||||
include "../src/BulletSoftBody"
|
include "../src/BulletSoftBody"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user