topical media & game development
graphic-o3d-samples-o3djs-fps.js / js
/*
* Copyright 2009, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
@fileoverview This file contains a class for displaying frames per second.
o3djs.provide('o3djs.fps');
o3djs.require('o3djs.rendergraph');
o3djs.require('o3djs.canvas');
o3djs.require('o3djs.math');
o3djs.require('o3djs.primitives');
A Module with a fps class for helping to easily display frames per second.
@namespace
o3djs.fps = o3djs.fps || {};
Number of frames to average over for computing FPS.
@type {number}
o3djs.fps.NUM_FRAMES_TO_AVERAGE = 16;
Colors used for each second of the performance bar.
@type {!Array.<!o3djs.math.Vector4>}
o3djs.fps.PERF_BAR_COLORS = [
[0, 0, 1, 1],
[0, 1, 0, 1],
[1, 1, 0, 1],
[1, 0.5, 0, 1],
[1, 0, 0, 1]];
The shader code used by the pref quads.
@type {string}
o3djs.fps.CONST_COLOR_EFFECT =
'float4x4 worldViewProjection : WorldViewProjection;\n' +
'float4 color;\n' +
'struct a2v {\n' +
' float4 pos : POSITION;\n' +
'};\n'+
'struct v2f {\n' +
' float4 pos : POSITION;\n' +
'};\n' +
'v2f vsMain(a2v IN) {\n' +
' v2f OUT;\n' +
' OUT.pos = mul(IN.pos, worldViewProjection);\n' +
' return OUT;\n' +
'}\n' +
'float4 psMain(v2f IN): COLOR {\n' +
' return color;\n' +
'}\n' +
'// #o3d VertexShaderEntryPoint vsMain\n' +
'// #o3d PixelShaderEntryPoint psMain\n' +
'// #o3d MatrixLoadOrder RowMajor\n';
Creates an object for displaying frames per second.
You can use it like this.
<pre>
<html><body>
<script type="text/javascript" src="o3djs/base.js">
</script>
<script type="text/javascript">
o3djs.require('o3djs.util');
o3djs.require('o3djs.rendergraph');
o3djs.require('o3djs.fps');
window.onload = init;
window.onunload = uninit;
var g_client;
var g_fpsManager;
function init() {
o3djs.base.makeClients(initStep2);
}
function initStep2(clientElements) {
var clientElement = clientElements[0];
var g_client = clientElement.client;
var pack = g_client.createPack();
var viewInfo = o3djs.rendergraph.createBasicView(
pack,
g_client.root,
g_client.renderGraphRoot);
g_fpsManager = o3djs.fps.createFPSManager(pack,
g_client.width,
g_client.height,
g_client.renderGraphRoot);
g_client.setRenderCallback(onRender);
}
function onrender(renderEvent) {
g_fpsManager.update(renderEvent);
}
function uninit() {
if (g_client) {
g_client.cleanup();
}
}
</script>
<div id="o3d" style="width: 600px; height: 600px"></div>
</body></html>
</pre>
@constructor
parameter: {!o3d.Pack} pack Pack to create objects in.
parameter: {number} clientWidth width of client area.
parameter: {number} clientHeight Height of client area.
parameter: {!o3d.RenderNode} opt_parent RenderNode to use as parent for
ViewInfo that will be used to render the FPS with.
o3djs.fps.createFPSManager = function(pack,
clientWidth,
clientHeight,
opt_parent) {
return new o3djs.fps.FPSManager(pack, clientWidth, clientHeight, opt_parent);
};
A class for displaying frames per second.
@constructor
parameter: {!o3d.Pack} pack Pack to create objects in.
parameter: {number} clientWidth width of client area.
parameter: {number} clientHeight Height of client area.
parameter: {!o3d.RenderNode} opt_parent RenderNode to use as parent for
ViewInfo that will be used to render the FPS with.
o3djs.fps.FPSManager = function(pack, clientWidth, clientHeight, opt_parent) {
// total time spent for last N frames.
this.totalTime_ = 0.0;
// total active time for last N frames.
this.totalActiveTime_ = 0.0;
// elapsed time for last N frames.
this.timeTable_ = [];
// active time for last N frames.
this.activeTimeTable_ = [];
// where to record next elapsed time.
this.timeTableCursor_ = 0;
// Initialize the FPS elapsed time history table.
for (var tt = 0; tt < o3djs.fps.NUM_FRAMES_TO_AVERAGE; ++tt) {
this.timeTable_[tt] = 0.0;
this.activeTimeTable_[tt] = 0.0;
}
// The root transform for this sub graph.
this.root_ = pack.createObject('Transform');
The ViewInfo to display FPS.
@type {!o3djs.rendergraph.ViewInfo}
this.viewInfo = o3djs.rendergraph.createBasicView(pack,
this.root_,
opt_parent);
this.viewInfo.root.priority = 100000;
this.viewInfo.clearBuffer.clearColorFlag = false;
this.viewInfo.zOrderedState.getStateParam('CullMode').value =
g_o3d.State.CULL_NONE;
this.viewInfo.drawContext.view = g_math.matrix4.lookAt(
[0, 0, 1], // eye
[0, 0, 0], // target
[0, 1, 0]); // up
// create a view just for the FPS. That way it's indepdendent of other views.
this.canvasLib_ = o3djs.canvas.create(pack,
this.root_,
this.viewInfo);
this.paint_ = pack.createObject('CanvasPaint');
The quad used to display the FPS.
this.fpsQuad = this.canvasLib_.createXYQuad(0, 0, -1, 64, 32, true);
// create a unit plane with a const color effect we can use to draw
// rectangles.
this.colorEffect_ = pack.createObject('Effect');
this.colorEffect_.loadFromFXString(o3djs.fps.CONST_COLOR_EFFECT);
this.colorMaterial_ = pack.createObject('Material');
this.colorMaterial_.effect = this.colorEffect_;
this.colorMaterial_.drawList = this.viewInfo.zOrderedDrawList;
this.colorEffect_.createUniformParameters(this.colorMaterial_);
this.colorMaterial_.getParam('color').value = [1, 1, 1, 1];
this.colorQuadShape_ = o3djs.primitives.createPlane(
pack,
this.colorMaterial_,
1,
1,
1,
1,
[[1, 0, 0, 0],
[0, 0, 1, 0],
[0, -1, 0, 0],
[0.5, 0.5, 0, 1]]);
var barXOffset = 10;
var barYOffset = 2;
var barWidth = clientWidth - barXOffset * 2;
var barHeight = 7;
this.numPerfBars_ = o3djs.fps.PERF_BAR_COLORS.length - 1;
this.perfBarRoot_ = pack.createObject('Transform');
this.perfBarRoot_.parent = this.root_;
this.perfBarBack_ = new o3djs.fps.ColorRect(
pack, this.colorQuadShape_, this.perfBarRoot_,
barXOffset, barYOffset, -3, barWidth, barHeight,
[0, 0, 0, 1]);
this.perfMarker_ = [];
for (var ii = 0; ii < this.numPerfBars_; ++ii) {
this.perfMarker_[ii] = new o3djs.fps.ColorRect(
pack, this.colorQuadShape_, this.perfBarRoot_,
barXOffset + barWidth / (this.numPerfBars_ + 1) * (ii + 1),
barYOffset - 1, -1,
1, barHeight + 2,
[1, 1, 1, 1]);
}
this.perfBar_ = new o3djs.fps.ColorRect(
pack, this.colorQuadShape_, this.perfBarRoot_,
barXOffset + 1, barYOffset + 1, -2, 1, barHeight - 2,
[1, 1, 0, 1]);
this.perfBarWidth_ = barWidth - 2;
this.perfBarHeight_ = barHeight - 2;
this.perfBarXOffset_ = barXOffset;
this.perfBarYOffset_ = barYOffset;
// set the size and position.
this.resize(clientWidth, clientHeight);
this.setPosition(10, 10, -1);
};
Sets the position of the FPS display
The position is in pixels assuming the size of the client matches the size
last set either on creation or with FPSManager.resize.
parameter: {number} x The x position.
parameter: {number} y The y position.
o3djs.fps.FPSManager.prototype.setPosition = function(x, y) {
this.fpsQuad.transform.identity();
this.fpsQuad.transform.translate(x, y, -1);
};
Sets the visiblity of the fps display.
parameter: {boolean} visible true = visible.
o3djs.fps.FPSManager.prototype.setVisible = function(visible) {
this.viewInfo.root.active = visible;
};
Sets the visibility of the performance bar.
parameter: {boolean} visible true = visible.
o3djs.fps.FPSManager.prototype.setPerfVisible = function(visible) {
this.perfBarRoot_.visible = visible;
};
Resizes the area for the FPS display.
Note: you must call this if your client area changes size.
parameter: {number} clientWidth width of client area.
parameter: {number} clientHeight height of client area.
o3djs.fps.FPSManager.prototype.resize = function(clientWidth, clientHeight) {
this.viewInfo.drawContext.projection = o3djs.math.matrix4.orthographic(
0 + 0.5,
g_o3dWidth + 0.5,
g_o3dHeight + 0.5,
0 + 0.5,
0.001,
1000);
var barWidth = clientWidth - this.perfBarXOffset_ * 2;
this.perfBarBack_.setSize(barWidth, this.perfBarHeight_);
for (var ii = 0; ii < this.numPerfBars_; ++ii) {
this.perfMarker_[ii].setPosition(
this.perfBarXOffset_ + barWidth / (this.numPerfBars_ + 1) * (ii + 1),
this.perfBarYOffset_ - 1);
}
this.perfBarWidth_ = barWidth - 2;
};
Updates the fps display.
You must call this every frame to update the FPS display.
<pre>
...
client.setRenderCallback(onRender);
...
function onRender(renderEvent) {
myFpsManager.update(renderEvent);
}
</pre>
parameter: {!o3d.RenderEvent} renderEvent The object passed into the render
callback.
o3djs.fps.FPSManager.prototype.update = function(renderEvent) {
var elapsedTime = renderEvent.elapsedTime;
var activeTime = renderEvent.activeTime;
// Keep the total time and total active time for the last N frames.
this.totalTime_ += elapsedTime - this.timeTable_[this.timeTableCursor_];
this.totalActiveTime_ +=
activeTime - this.activeTimeTable_[this.timeTableCursor_];
// Save off the elapsed time for this frame so we can subtract it later.
this.timeTable_[this.timeTableCursor_] = elapsedTime;
this.activeTimeTable_[this.timeTableCursor_] = activeTime;
// Wrap the place to store the next time sample.
++this.timeTableCursor_;
if (this.timeTableCursor_ == o3djs.fps.NUM_FRAMES_TO_AVERAGE) {
this.timeTableCursor_ = 0;
}
// Print the average frame rate for the last N frames as well as the
// instantaneous frame rate.
var fps = '' +
Math.floor((1.0 / (this.totalTime_ /
o3djs.fps.NUM_FRAMES_TO_AVERAGE)) + 0.5) +
' : ' + Math.floor(1.0 / elapsedTime + 0.5);
var canvas = this.fpsQuad.canvas;
canvas.clear([0, 0, 0, 0]);
var paint = this.paint_;
canvas.saveMatrix();
paint.setOutline(3, [0, 0, 0, 1]);
paint.textAlign = o3djs.base.o3d.CanvasPaint.LEFT;
paint.textSize = 12;
paint.textTypeface = 'Arial';
paint.color = [1, 1, 0, 1];
canvas.drawText(fps, 2, 16, paint);
canvas.restoreMatrix();
this.fpsQuad.updateTexture();
var frames = this.totalActiveTime_ / o3djs.fps.NUM_FRAMES_TO_AVERAGE /
(1 / 60.0);
var colorIndex = Math.min(frames, o3djs.fps.PERF_BAR_COLORS.length - 1);
colorIndex = Math.floor(Math.max(colorIndex, 0));
if (!isNaN(colorIndex)) {
this.perfBar_.setColor(o3djs.fps.PERF_BAR_COLORS[colorIndex]);
this.perfBar_.setSize(frames * this.perfBarWidth_ / this.numPerfBars_,
this.perfBarHeight_);
}
};
A Class the manages a color rect.
parameter: {!o3d.Pack} pack Pack to create things in.
parameter: {!o3d.Shape} shape Shape to use for rectangle.
parameter: {!o3d.Transform} parent Transform to parent rect under.
parameter: {number} x initial x position.
parameter: {number} y initial y position.
parameter: {number} z initial z position.
parameter: {number} width initial width.
parameter: {number} height initial height.
parameter: {!o3djs.math.Vector4} color initial color.
o3djs.fps.ColorRect = function(pack, shape, parent,
x, y, z, width, height, color) {
this.transform_ = pack.createObject('Transform');
this.colorParam_ = this.transform_.createParam('color', 'ParamFloat4');
this.transform_.addShape(shape);
this.transform_.parent = parent;
this.width_ = 0;
this.height_ = 0;
this.x_ = 0;
this.y_ = 0;
this.z_ = z;
this.setPosition(x, y);
this.setSize(width, height);
this.setColor(color);
};
Updates the transform of this ColorRect
@private
o3djs.fps.ColorRect.prototype.updateTransform_ = function() {
this.transform_.identity();
this.transform_.translate(this.x_, this.y_, this.z_);
this.transform_.scale(this.width_, this.height_, 1);
};
Sets the position of this ColorRect.
parameter: {number} x x position.
parameter: {number} y y position.
o3djs.fps.ColorRect.prototype.setPosition = function(x, y) {
this.x_ = x;
this.y_ = y;
this.updateTransform_();
};
Sets the size of this ColorRect
parameter: {number} width width.
parameter: {number} height height.
o3djs.fps.ColorRect.prototype.setSize = function(width, height) {
this.width_ = width;
this.height_ = height;
this.updateTransform_();
};
Sets the color of this ColorRect.
parameter: {!o3djs.math.Vector4} color initial color.
o3djs.fps.ColorRect.prototype.setColor = function(color) {
this.colorParam_.value = color;
};
(C) Æliens
20/2/2008
You may not copy or print any of this material without explicit permission of the author or the publisher.
In case of other copyright issues, contact the author.