1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
9 'mojo/apps/js/bindings/connector',
10 'mojo/apps/js/bindings/core',
11 'mojo/apps/js/bindings/gl',
12 'mojo/apps/js/bindings/threading',
13 'mojom/native_viewport',
25 const VERTEX_SHADER_SOURCE = [
26 'uniform mat4 u_mvpMatrix;',
27 'attribute vec4 a_position;',
30 ' gl_Position = u_mvpMatrix * a_position;',
34 const FRAGMENT_SHADER_SOURCE = [
35 'precision mediump float;',
38 ' gl_FragColor = vec4( 0.0, 1.0, 0.0, 1.0 );',
43 this.m = new Float32Array(16);
46 ESMatrix.prototype.getIndex = function(x, y) {
50 ESMatrix.prototype.set = function(x, y, v) {
51 this.m[this.getIndex(x, y)] = v;
54 ESMatrix.prototype.get = function(x, y) {
55 return this.m[this.getIndex(x, y)];
58 ESMatrix.prototype.loadZero = function() {
59 for (var i = 0; i < this.m.length; i++) {
64 ESMatrix.prototype.loadIdentity = function() {
66 for (var i = 0; i < 4; i++) {
71 ESMatrix.prototype.multiply = function(a, b) {
72 var result = new ESMatrix();
73 for (var i = 0; i < 4; i++) {
75 (a.get(i, 0) * b.get(0, 0)) +
76 (a.get(i, 1) * b.get(1, 0)) +
77 (a.get(i, 2) * b.get(2, 0)) +
78 (a.get(i, 3) * b.get(3, 0)));
81 (a.get(i, 0) * b.get(0, 1)) +
82 (a.get(i, 1) * b.get(1, 1)) +
83 (a.get(i, 2) * b.get(2, 1)) +
84 (a.get(i, 3) * b.get(3, 1)));
87 (a.get(i, 0) * b.get(0, 2)) +
88 (a.get(i, 1) * b.get(1, 2)) +
89 (a.get(i, 2) * b.get(2, 2)) +
90 (a.get(i, 3) * b.get(3, 2)));
93 (a.get(i, 0) * b.get(0, 3)) +
94 (a.get(i, 1) * b.get(1, 3)) +
95 (a.get(i, 2) * b.get(2, 3)) +
96 (a.get(i, 3) * b.get(3, 3)));
98 for (var i = 0; i < result.m.length; i++) {
99 this.m[i] = result.m[i];
103 ESMatrix.prototype.frustrum = function(left, right, bottom, top, nearZ,
105 var deltaX = right - left;
106 var deltaY = top - bottom;
107 var deltaZ = farZ - nearZ;
109 if (nearZ < 0 || farZ < 0 || deltaZ < 0 || deltaY < 0 || deltaX < 0) {
113 var frust = new ESMatrix();
114 frust.set(0, 0, 2 * nearZ / deltaX);
116 frust.set(1, 1, 2 * nearZ / deltaY);
118 frust.set(2, 0, (right + left) / deltaX);
119 frust.set(2, 1, (top + bottom) / deltaY);
120 frust.set(2, 2, -(nearZ + farZ) / deltaZ);
123 frust.set(3, 2, -2 * nearZ * farZ / deltaZ);
125 this.multiply(frust, this);
128 ESMatrix.prototype.perspective = function(fovY, aspect, nearZ, farZ) {
129 var frustrumH = Math.tan(fovY / 360 * Math.PI) * nearZ;
130 var frustrumW = frustrumH * aspect;
131 this.frustrum(-frustrumW, frustrumW, -frustrumH, frustrumH, nearZ, farZ);
134 ESMatrix.prototype.translate = function(tx, ty, tz) {
135 this.set(3, 0, this.get(3, 0) + this.get(0, 0) *
136 tx + this.get(1, 0) * ty + this.get(2, 0) * tz);
137 this.set(3, 1, this.get(3, 1) + this.get(0, 1) *
138 tx + this.get(1, 1) * ty + this.get(2, 1) * tz);
139 this.set(3, 2, this.get(3, 2) + this.get(0, 2) *
140 tx + this.get(1, 2) * ty + this.get(2, 2) * tz);
141 this.set(3, 3, this.get(3, 3) + this.get(0, 3) *
142 tx + this.get(1, 3) * ty + this.get(2, 3) * tz);
145 ESMatrix.prototype.rotate = function(angle, x, y, z) {
146 var mag = Math.sqrt(x * x + y * y + z * z);
147 var sinAngle = Math.sin(angle * Math.PI / 180);
148 var cosAngle = Math.cos(angle * Math.PI / 180);
153 var xx, yy, zz, xy, yz, zx, xs, ys, zs, oneMinusCos;
154 var rotation = new ESMatrix();
169 oneMinusCos = 1 - cosAngle;
171 rotation.set(0, 0, (oneMinusCos * xx) + cosAngle);
172 rotation.set(0, 1, (oneMinusCos * xy) - zs);
173 rotation.set(0, 2, (oneMinusCos * zx) + ys);
174 rotation.set(0, 3, 0);
176 rotation.set(1, 0, (oneMinusCos * xy) + zs);
177 rotation.set(1, 1, (oneMinusCos * yy) + cosAngle);
178 rotation.set(1, 2, (oneMinusCos * yz) - xs);
179 rotation.set(1, 3, 0);
181 rotation.set(2, 0, (oneMinusCos * zx) - ys);
182 rotation.set(2, 1, (oneMinusCos * yz) + xs);
183 rotation.set(2, 2, (oneMinusCos * zz) + cosAngle);
184 rotation.set(2, 3, 0);
186 rotation.set(3, 0, 0);
187 rotation.set(3, 1, 0);
188 rotation.set(3, 2, 0);
189 rotation.set(3, 3, 1);
191 this.multiply(rotation, this);
194 function loadProgram(gl) {
195 var vertexShader = gl.createShader(gl.VERTEX_SHADER);
196 gl.shaderSource(vertexShader, VERTEX_SHADER_SOURCE);
197 gl.compileShader(vertexShader);
199 var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
200 gl.shaderSource(fragmentShader, FRAGMENT_SHADER_SOURCE);
201 gl.compileShader(fragmentShader);
203 var program = gl.createProgram();
204 gl.attachShader(program, vertexShader);
205 gl.attachShader(program, fragmentShader);
207 gl.linkProgram(program);
208 // TODO(aa): Check for errors using getProgramiv and LINK_STATUS.
210 gl.deleteShader(vertexShader);
211 gl.deleteShader(fragmentShader);
218 function generateCube(gl) {
219 var numVertices = 24 * 3;
220 var numIndices = 12 * 3;
222 var cubeVertices = new Float32Array([
249 var cubeIndices = new Uint16Array([
264 // TODO(aa): The C++ program branches here on whether the pointer is
266 vboVertices = gl.createBuffer();
267 gl.bindBuffer(gl.ARRAY_BUFFER, vboVertices);
268 gl.bufferData(gl.ARRAY_BUFFER, cubeVertices, gl.STATIC_DRAW);
269 gl.bindBuffer(gl.ARRAY_BUFFER, 0);
271 vboIndices = gl.createBuffer();
272 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, vboIndices);
273 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, cubeIndices, gl.STATIC_DRAW);
274 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0);
276 return cubeIndices.length;
279 function SampleApp(shell) {
282 var pipe = new core.createMessagePipe();
283 this.shell_.connect('mojo:mojo_native_viewport_service', pipe.handle1);
284 new connector.Connection(pipe.handle0, NativeViewportClientImpl,
285 nativeViewport.NativeViewportProxy);
287 // TODO(aa): It is a bummer to need this stub object in JavaScript. We should
288 // have a 'client' object that contains both the sending and receiving bits of
289 // the client side of the interface. Since JS is loosely typed, we do not need
290 // a separate base class to inherit from to receive callbacks.
291 SampleApp.prototype = Object.create(shell.ShellClientStub.prototype);
294 function NativeViewportClientImpl(remote) {
295 this.remote_ = remote;
297 var pipe = core.createMessagePipe();
299 var rect = new nativeViewport.Rect;
300 rect.position = new nativeViewport.Point;
301 rect.size = new nativeViewport.Size;
302 rect.size.width = 800;
303 rect.size.height = 600;
304 this.remote_.create(rect);
306 this.remote_.createGLES2Context(pipe.handle1);
307 this.gles2_ = new GLES2ClientImpl(pipe.handle0);
309 NativeViewportClientImpl.prototype =
310 Object.create(nativeViewport.NativeViewportClientStub.prototype);
312 NativeViewportClientImpl.prototype.onCreated = function() {
313 console.log('NativeViewportClientImpl.prototype.OnCreated');
316 NativeViewportClientImpl.prototype.onBoundsChanged = function(bounds) {
317 console.log('NativeViewportClientImpl.prototype.OnBoundsChanged');
318 this.gles2_.setDimensions(bounds.size);
321 function GLES2ClientImpl(remotePipe) {
322 this.gl_ = new gljs.Context(remotePipe, this.contextLost.bind(this));
323 this.lastTime_ = monotonicClock.seconds();
326 this.program_ = loadProgram(this.gl_);
327 this.positionLocation_ =
328 this.gl_.getAttribLocation(this.program_, 'a_position');
330 this.gl_.getUniformLocation(this.program_, 'u_mvpMatrix');
331 this.numIndices_ = generateCube(this.gl_);
332 this.mvpMatrix_ = new ESMatrix();
333 this.mvpMatrix_.loadIdentity();
335 this.gl_.clearColor(0, 0, 0, 0);
338 GLES2ClientImpl.prototype.setDimensions = function(size) {
339 this.width_ = size.width;
340 this.height_ = size.height;
341 this.timer_ = timer.createRepeating(16, this.handleTimer.bind(this));
344 GLES2ClientImpl.prototype.drawCube = function() {
345 this.gl_.viewport(0, 0, this.width_, this.height_);
346 this.gl_.clear(this.gl_.COLOR_BUFFER_BIT);
347 this.gl_.useProgram(this.program_);
348 this.gl_.bindBuffer(this.gl_.ARRAY_BUFFER, vboVertices);
349 this.gl_.bindBuffer(this.gl_.ELEMENT_ARRAY_BUFFER, vboIndices);
350 this.gl_.vertexAttribPointer(this.positionLocation_, 3, this.gl_.FLOAT,
352 this.gl_.enableVertexAttribArray(this.positionLocation_);
353 this.gl_.uniformMatrix4fv(this.mvpLocation_, false, this.mvpMatrix_.m);
354 this.gl_.drawElements(this.gl_.TRIANGLES, this.numIndices_,
355 this.gl_.UNSIGNED_SHORT, 0);
356 this.gl_.swapBuffers();
359 GLES2ClientImpl.prototype.handleTimer = function() {
360 var now = monotonicClock.seconds();
361 var secondsDelta = now - this.lastTime_;
362 this.lastTime_ = now;
364 this.angle_ += this.getRotationForTimeDelta(secondsDelta);
365 this.angle_ = this.angle_ % 360;
367 var aspect = this.width_ / this.height_;
369 var perspective = new ESMatrix();
370 perspective.loadIdentity();
371 perspective.perspective(60, aspect, 1, 20);
373 var modelView = new ESMatrix();
374 modelView.loadIdentity();
375 modelView.translate(0, 0, -2);
376 modelView.rotate(this.angle_, 1, 0, 1);
378 this.mvpMatrix_.multiply(modelView, perspective);
383 GLES2ClientImpl.prototype.getRotationForTimeDelta = function(secondsDelta) {
384 return secondsDelta * 40;
387 GLES2ClientImpl.prototype.contextLost = function() {
388 console.log('GLES2ClientImpl.prototype.contextLost');
392 return function(handle) {
393 new connector.Connection(handle, SampleApp, shell.ShellProxy);