Upstream version 5.34.104.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     this.shell_.connect('mojo:mojo_native_viewport_service', pipe.handle1);
284     new connector.Connection(pipe.handle0, NativeViewportClientImpl,
285                              nativeViewport.NativeViewportProxy);
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
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);
305     this.remote_.show();
306     this.remote_.createGLES2Context(pipe.handle1);
307     this.gles2_ = new GLES2ClientImpl(pipe.handle0);
308   }
309   NativeViewportClientImpl.prototype =
310       Object.create(nativeViewport.NativeViewportClientStub.prototype);
311
312   NativeViewportClientImpl.prototype.onCreated = function() {
313     console.log('NativeViewportClientImpl.prototype.OnCreated');
314   };
315
316   NativeViewportClientImpl.prototype.onBoundsChanged = function(bounds) {
317     console.log('NativeViewportClientImpl.prototype.OnBoundsChanged');
318     this.gles2_.setDimensions(bounds.size);
319   }
320
321   function GLES2ClientImpl(remotePipe) {
322     this.gl_ = new gljs.Context(remotePipe, this.contextLost.bind(this));
323     this.lastTime_ = monotonicClock.seconds();
324     this.angle_ = 45;
325
326     this.program_ = loadProgram(this.gl_);
327     this.positionLocation_ =
328         this.gl_.getAttribLocation(this.program_, 'a_position');
329     this.mvpLocation_ =
330         this.gl_.getUniformLocation(this.program_, 'u_mvpMatrix');
331     this.numIndices_ = generateCube(this.gl_);
332     this.mvpMatrix_ = new ESMatrix();
333     this.mvpMatrix_.loadIdentity();
334
335     this.gl_.clearColor(0, 0, 0, 0);
336   }
337
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));
342   }
343
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,
351                                  false, 12, 0);
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();
357   };
358
359   GLES2ClientImpl.prototype.handleTimer = function() {
360     var now = monotonicClock.seconds();
361     var secondsDelta = now - this.lastTime_;
362     this.lastTime_ = now;
363
364     this.angle_ += this.getRotationForTimeDelta(secondsDelta);
365     this.angle_ = this.angle_ % 360;
366
367     var aspect = this.width_ / this.height_;
368
369     var perspective = new ESMatrix();
370     perspective.loadIdentity();
371     perspective.perspective(60, aspect, 1, 20);
372
373     var modelView = new ESMatrix();
374     modelView.loadIdentity();
375     modelView.translate(0, 0, -2);
376     modelView.rotate(this.angle_, 1, 0, 1);
377
378     this.mvpMatrix_.multiply(modelView, perspective);
379
380     this.drawCube();
381   };
382
383   GLES2ClientImpl.prototype.getRotationForTimeDelta = function(secondsDelta) {
384     return secondsDelta * 40;
385   };
386
387   GLES2ClientImpl.prototype.contextLost = function() {
388     console.log('GLES2ClientImpl.prototype.contextLost');
389   };
390
391
392   return function(handle) {
393     new connector.Connection(handle, SampleApp, shell.ShellProxy);
394   };
395 });