Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / mojo / apps / js / main.js
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.
4
5 define([
6     'console',
7     'monotonic_clock',
8     'timer',
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',
14     'mojom/shell',
15 ], function(console,
16             monotonicClock,
17             timer,
18             connector,
19             core,
20             gljs,
21             threading,
22             nativeViewport,
23             shell) {
24
25   const VERTEX_SHADER_SOURCE = [
26     'uniform mat4 u_mvpMatrix;',
27     'attribute vec4 a_position;',
28     'void main()',
29     '{',
30     '   gl_Position = u_mvpMatrix * a_position;',
31     '}'
32   ].join('\n');
33
34   const FRAGMENT_SHADER_SOURCE = [
35     'precision mediump float;',
36     'void main()',
37     '{',
38     '  gl_FragColor = vec4( 0.0, 1.0, 0.0, 1.0 );',
39     '}'
40   ].join('\n');
41
42   function ESMatrix() {
43     this.m = new Float32Array(16);
44   }
45
46   ESMatrix.prototype.getIndex = function(x, y) {
47     return x * 4 + y;
48   }
49
50   ESMatrix.prototype.set = function(x, y, v) {
51     this.m[this.getIndex(x, y)] = v;
52   };
53
54   ESMatrix.prototype.get = function(x, y) {
55     return this.m[this.getIndex(x, y)];
56   };
57
58   ESMatrix.prototype.loadZero = function() {
59     for (var i = 0; i < this.m.length; i++) {
60       this.m[i] = 0;
61     }
62   };
63
64   ESMatrix.prototype.loadIdentity = function() {
65     this.loadZero();
66     for (var i = 0; i < 4; i++) {
67       this.set(i, i, 1);
68     }
69   };
70
71   ESMatrix.prototype.multiply = function(a, b) {
72     var result = new ESMatrix();
73     for (var i = 0; i < 4; i++) {
74       result.set(i, 0,
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)));
79
80       result.set(i, 1,
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)));
85
86       result.set(i, 2,
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)));
91
92       result.set(i, 3,
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)));
97     }
98     for (var i = 0; i < result.m.length; i++) {
99       this.m[i] = result.m[i];
100     }
101   };
102
103   ESMatrix.prototype.frustrum = function(left, right, bottom, top, nearZ,
104                                          farZ) {
105     var deltaX = right - left;
106     var deltaY = top - bottom;
107     var deltaZ = farZ - nearZ;
108
109     if (nearZ < 0 || farZ < 0 || deltaZ < 0 || deltaY < 0 || deltaX < 0) {
110       return;
111     }
112
113     var frust = new ESMatrix();
114     frust.set(0, 0, 2 * nearZ / deltaX);
115
116     frust.set(1, 1, 2 * nearZ / deltaY);
117
118     frust.set(2, 0, (right + left) / deltaX);
119     frust.set(2, 1, (top + bottom) / deltaY);
120     frust.set(2, 2, -(nearZ + farZ) / deltaZ);
121     frust.set(2, 3, -1);
122
123     frust.set(3, 2, -2 * nearZ * farZ / deltaZ);
124
125     this.multiply(frust, this);
126   };
127
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);
132   };
133
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);
143   };
144
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);
149     if (mag <= 0) {
150       return;
151     }
152
153     var xx, yy, zz, xy, yz, zx, xs, ys, zs, oneMinusCos;
154     var rotation = new ESMatrix();
155
156     x /= mag;
157     y /= mag;
158     z /= mag;
159
160     xx = x * x;
161     yy = y * y;
162     zz = z * z;
163     xy = x * y;
164     yz = y * z;
165     zx = z * x;
166     xs = x * sinAngle;
167     ys = y * sinAngle;
168     zs = z * sinAngle;
169     oneMinusCos = 1 - cosAngle;
170
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);
175
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);
180
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);
185
186     rotation.set(3, 0, 0);
187     rotation.set(3, 1, 0);
188     rotation.set(3, 2, 0);
189     rotation.set(3, 3, 1);
190
191     this.multiply(rotation, this);
192   };
193
194   function loadProgram(gl) {
195     var vertexShader = gl.createShader(gl.VERTEX_SHADER);
196     gl.shaderSource(vertexShader, VERTEX_SHADER_SOURCE);
197     gl.compileShader(vertexShader);
198
199     var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
200     gl.shaderSource(fragmentShader, FRAGMENT_SHADER_SOURCE);
201     gl.compileShader(fragmentShader);
202
203     var program = gl.createProgram();
204     gl.attachShader(program, vertexShader);
205     gl.attachShader(program, fragmentShader);
206
207     gl.linkProgram(program);
208     // TODO(aa): Check for errors using getProgramiv and LINK_STATUS.
209
210     gl.deleteShader(vertexShader);
211     gl.deleteShader(fragmentShader);
212
213     return program;
214   }
215
216   var vboVertices;
217   var vboIndices;
218   function generateCube(gl) {
219     var numVertices = 24 * 3;
220     var numIndices = 12 * 3;
221
222     var cubeVertices = new Float32Array([
223       -0.5, -0.5, -0.5,
224       -0.5, -0.5,  0.5,
225       0.5, -0.5,  0.5,
226       0.5, -0.5, -0.5,
227       -0.5,  0.5, -0.5,
228       -0.5,  0.5,  0.5,
229       0.5,  0.5,  0.5,
230       0.5,  0.5, -0.5,
231       -0.5, -0.5, -0.5,
232       -0.5,  0.5, -0.5,
233       0.5,  0.5, -0.5,
234       0.5, -0.5, -0.5,
235       -0.5, -0.5, 0.5,
236       -0.5,  0.5, 0.5,
237       0.5,  0.5, 0.5,
238       0.5, -0.5, 0.5,
239       -0.5, -0.5, -0.5,
240       -0.5, -0.5,  0.5,
241       -0.5,  0.5,  0.5,
242       -0.5,  0.5, -0.5,
243       0.5, -0.5, -0.5,
244       0.5, -0.5,  0.5,
245       0.5,  0.5,  0.5,
246       0.5,  0.5, -0.5
247     ]);
248
249     var cubeIndices = new Uint16Array([
250       0, 2, 1,
251       0, 3, 2,
252       4, 5, 6,
253       4, 6, 7,
254       8, 9, 10,
255       8, 10, 11,
256       12, 15, 14,
257       12, 14, 13,
258       16, 17, 18,
259       16, 18, 19,
260       20, 23, 22,
261       20, 22, 21
262     ]);
263
264     // TODO(aa): The C++ program branches here on whether the pointer is
265     // non-NULL.
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);
270
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);
275
276     return cubeIndices.length;
277   }
278
279   function SampleApp(shell) {
280     this.shell_ = shell;
281
282     var pipe = new core.createMessagePipe();
283     new connector.Connection(pipe.handle0, NativeViewportClientImpl,
284                              nativeViewport.NativeViewportProxy);
285     this.shell_.connect('mojo:mojo_native_viewport_service', pipe.handle1);
286   }
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);
292
293
294   function NativeViewportClientImpl(remote) {
295     this.remote_ = remote;
296
297     var pipe = core.createMessagePipe();
298     new GLES2ClientImpl(pipe.handle0);
299
300     this.remote_.open();
301     this.remote_.createGLES2Context(pipe.handle1);
302   }
303   NativeViewportClientImpl.prototype =
304       Object.create(nativeViewport.NativeViewportClientStub.prototype);
305
306   NativeViewportClientImpl.prototype.onCreated = function() {
307     console.log('NativeViewportClientImpl.prototype.OnCreated');
308   };
309   NativeViewportClientImpl.prototype.didOpen = function() {
310     console.log('NativeViewportClientImpl.prototype.DidOpen');
311   };
312
313
314   function GLES2ClientImpl(remotePipe) {
315     this.gl_ = new gljs.Context(remotePipe, this.didCreateContext.bind(this));
316     this.lastTime_ = monotonicClock.seconds();
317     this.angle_ = 45;
318   }
319
320   GLES2ClientImpl.prototype.didCreateContext = function(width, height) {
321     this.width_ = width;
322     this.height_ = height;
323     this.program_ = loadProgram(this.gl_);
324     this.positionLocation_ =
325         this.gl_.getAttribLocation(this.program_, 'a_position');
326     this.mvpLocation_ =
327         this.gl_.getUniformLocation(this.program_, 'u_mvpMatrix');
328     this.numIndices_ = generateCube(this.gl_);
329     this.mvpMatrix_ = new ESMatrix();
330     this.mvpMatrix_.loadIdentity();
331
332     this.gl_.clearColor(0, 0, 0, 0);
333     this.timer_ = timer.createRepeating(16, this.handleTimer.bind(this));
334   };
335
336   GLES2ClientImpl.prototype.drawCube = function() {
337     this.gl_.viewport(0, 0, this.width_, this.height_);
338     this.gl_.clear(this.gl_.COLOR_BUFFER_BIT);
339     this.gl_.useProgram(this.program_);
340     this.gl_.bindBuffer(this.gl_.ARRAY_BUFFER, vboVertices);
341     this.gl_.bindBuffer(this.gl_.ELEMENT_ARRAY_BUFFER, vboIndices);
342     this.gl_.vertexAttribPointer(this.positionLocation_, 3, this.gl_.FLOAT,
343                                  false, 12, 0);
344     this.gl_.enableVertexAttribArray(this.positionLocation_);
345     this.gl_.uniformMatrix4fv(this.mvpLocation_, false, this.mvpMatrix_.m);
346     this.gl_.drawElements(this.gl_.TRIANGLES, this.numIndices_,
347                           this.gl_.UNSIGNED_SHORT, 0);
348     this.gl_.swapBuffers();
349   };
350
351   GLES2ClientImpl.prototype.handleTimer = function() {
352     var now = monotonicClock.seconds();
353     var secondsDelta = now - this.lastTime_;
354     this.lastTime_ = now;
355
356     this.angle_ += this.getRotationForTimeDelta(secondsDelta);
357     this.angle_ = this.angle_ % 360;
358
359     var aspect = this.width_ / this.height_;
360
361     var perspective = new ESMatrix();
362     perspective.loadIdentity();
363     perspective.perspective(60, aspect, 1, 20);
364
365     var modelView = new ESMatrix();
366     modelView.loadIdentity();
367     modelView.translate(0, 0, -2);
368     modelView.rotate(this.angle_, 1, 0, 1);
369
370     this.mvpMatrix_.multiply(modelView, perspective);
371
372     this.drawCube();
373   };
374
375   GLES2ClientImpl.prototype.getRotationForTimeDelta = function(secondsDelta) {
376     return secondsDelta * 40;
377   };
378
379   GLES2ClientImpl.prototype.contextLost = function() {
380     console.log('GLES2ClientImpl.prototype.contextLost');
381   };
382
383
384   return function(handle) {
385     new connector.Connection(handle, SampleApp, shell.ShellProxy);
386   };
387 });