The basics of displaying a HUD in a 3D application is simply to combine the 3D scene with a 2D overlay.
This can be accomplished easily by treating the scene we wish to render as two distinct parts, with the 3D and 2D components each having their own views.
In the sample code included with this tutorial, most of the code is simply the code from tutorial 2 and 3, which I have combined.
The biggest difference is that instead of using one view (g_viewInfo), we now create two, one each for the 3D and 2D components. The code referencing g_viewInfo is used to create the 3D scene, while g_hudViewInfo is used to create the HUD. I won’t go into the details of the setup, as all the code in the example has been covered in the last two tutorials, with one exception.
g_hudViewInfo.root.priority = g_viewInfo.root.priority + 1;
g_hudViewInfo.clearBuffer.clearColorFlag = false;
This sets the priority of the view so that the graphics are drawn in the right order. We want the 2D layer to get drawn last. We also turn off the flag for clearing the color of the hud, since we want to be able to see through it.
I also set the background color of the block in which I am displaying the text to grey with transparency set to 0.5, which demonstrates how the HUD can overlay the main scene.
Your imagination is your only limitation to how the HUD is designed but this is all there is to start building it.
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Tutorial 4: The beginnings of a HUD</title>
<script type="text/javascript" src="o3djs/base.js"></script>
<script type="text/javascript">
o3djs.require('o3djs.util');
o3djs.require('o3djs.math');
o3djs.require('o3djs.rendergraph');
o3djs.require('o3djs.canvas');
// Events
// Run the init() function once the page has finished loading.
// Run the uninit() function when the page has is unloaded.
window.onload = init;
window.onunload = uninit;
// global variables
var g_o3d;
var g_math;
var g_client;
var g_pack;
var g_clock = 0;
var g_timeMult = 1;
var g_cubeTransform;
var g_textCanvas;
var g_paint;
var g_canvasLib;
var g_3dRoot;
var g_hudRoot;
var g_viewInfo;
var g_hudViewInfo;
function createCube(material) {
var cubeShape = g_pack.createObject('Shape');
var cubePrimitive = g_pack.createObject('Primitive');
var streamBank = g_pack.createObject('StreamBank');
cubePrimitive.material = material;
cubePrimitive.owner = cubeShape;
cubePrimitive.streamBank = streamBank;
cubePrimitive.primitiveType = g_o3d.Primitive.TRIANGLELIST;
cubePrimitive.numberPrimitives = 12; // 12 triangles
cubePrimitive.numberVertices = 8; // 8 vertices in total
var positionArray = [
-0.5, -0.5, 0.5, // vertex 0
0.5, -0.5, 0.5, // vertex 1
-0.5, 0.5, 0.5, // vertex 2
0.5, 0.5, 0.5, // vertex 3
-0.5, 0.5, -0.5, // vertex 4
0.5, 0.5, -0.5, // vertex 5
-0.5, -0.5, -0.5, // vertex 6
0.5, -0.5, -0.5 // vertex 7
];
var indicesArray = [
0, 1, 2, // face 1
2, 1, 3,
2, 3, 4, // face 2
4, 3, 5,
4, 5, 6, // face 3
6, 5, 7,
6, 7, 0, // face 4
0, 7, 1,
1, 7, 3, // face 5
3, 7, 5,
6, 0, 4, // face 6
4, 0, 2
];
var positionsBuffer = g_pack.createObject('VertexBuffer');
var positionsField = positionsBuffer.createField('FloatField', 3);
positionsBuffer.set(positionArray);
var indexBuffer = g_pack.createObject('IndexBuffer');
indexBuffer.set(indicesArray);
streamBank.setVertexStream(
g_o3d.Stream.POSITION, // This stream stores vertex positions
0, // First (and only) position stream
positionsField, // field: the field this stream uses.
0); // start_index
// Associate the triangle indices Buffer with the primitive.
cubePrimitive.indexBuffer = indexBuffer;
return cubeShape;
}
function drawText(str) {
// Clear to completely transparent.
g_textCanvas.canvas.clear([0.5, 0.5, 0.5, 0.5]);
// Reuse the global paint object
var paint = g_paint;
paint.color = [1, 1, 1, 1];
paint.textSize = 12;
paint.textTypeface = 'Comic Sans MS';
paint.textAlign = g_o3d.CanvasPaint.LEFT;
paint.shader = null;
g_textCanvas.canvas.drawText(str, 10, 30, paint);
g_textCanvas.updateTexture();
}
/**
* This method gets called every time O3D renders a frame. Here's
* where we update the cube's transform to make it spin.
* @param {o3d.RenderEvent} renderEvent The render event object that
* gives us the elapsed time since the last time a frame was rendered
*/
function renderCallback(renderEvent) {
g_clock += renderEvent.elapsedTime * g_timeMult;
// Rotate the cube around the Y axis.
g_cubeTransform.identity();
g_cubeTransform.rotateY(2.0 * g_clock);
drawText("Hello world - " + (Math.round(g_clock * 100) / 100) + "s");
}
/**
* Creates the client area.
*/
function init() {
o3djs.util.makeClients(initStep2);
}
/**
* Initializes O3D.
* @param {Array} clientElements Array of o3d object elements.
*/
function initStep2(clientElements) {
// Initializes global variables and libraries.
var o3dElement = clientElements[0];
g_client = o3dElement.client;
g_o3d = o3dElement.o3d;
g_math = o3djs.math;
// Initialize O3D sample libraries.
o3djs.base.init(o3dElement);
// Create a pack to manage the objects created.
g_pack = g_client.createPack();
// Create 2 root transforms, one for the 3d parts and 2d parts.
// This is not strictly neccassary but it is helpful.
g_3dRoot = g_pack.createObject('Transform');
g_hudRoot = g_pack.createObject('Transform');
// Create the render graph for a view.
g_viewInfo = o3djs.rendergraph.createBasicView(
g_pack,
g_3dRoot,
g_client.renderGraphRoot);
// Set the background color to black.
g_viewInfo.clearBuffer.clearColor = [0, 0, 0, 1];
// Create a second view for the hud.
g_hudViewInfo = o3djs.rendergraph.createBasicView(
g_pack,
g_hudRoot,
g_client.renderGraphRoot);
// Make sure the hud gets drawn after the 3d stuff
g_hudViewInfo.root.priority = g_viewInfo.root.priority + 1;
// Turn off clearing color for the hud since it would erase the 3d
// parts but leave clearing the depth and stencil so the HUD is
// unaffected by anything done by the 3d parts.
g_hudViewInfo.clearBuffer.clearColorFlag = false;
// Set up a perspective view
g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
g_math.degToRad(30), // 30 degree fov.
g_client.width / g_client.height,
1, // Near plane.
5000); // Far plane.
// Set up our view transformation to look towards the world origin
// where the cube is located.
g_viewInfo.drawContext.view = g_math.matrix4.lookAt([0, 1, 5], //eye
[0, 0, 0], // target
[0, 1, 0]); // up
//Set up the 2d orthographic view
g_hudViewInfo.drawContext.projection = g_math.matrix4.orthographic(
0 + 0.5,
g_client.width + 0.5,
g_client.height + 0.5,
0 + 0.5,
0.001,
1000);
g_hudViewInfo.drawContext.view = g_math.matrix4.lookAt(
[0, 0, 1], // eye
[0, 0, 0], // target
[0, 1, 0]); // up
// Create an Effect object and initialize it using the shaders
// from the text area.
var redEffect = g_pack.createObject('Effect');
var shaderString = document.getElementById('effect').value;
redEffect.loadFromFXString(shaderString);
// Create a Material for the mesh.
var redMaterial = g_pack.createObject('Material');
// Set the material's drawList.
redMaterial.drawList = g_viewInfo.performanceDrawList;
// Apply our effect to this material. The effect tells the 3D
// hardware which shaders to use.
redMaterial.effect = redEffect;
// Create the Shape for the cube mesh and assign its material.
var cubeShape = createCube(redMaterial);
// Create a new transform and parent the Shape under it.
g_cubeTransform = g_pack.createObject('Transform');
g_cubeTransform.addShape(cubeShape);
// Parent the cube's transform to the client root.
g_cubeTransform.parent = g_3dRoot;
// Generate the draw elements for the cube shape.
cubeShape.createDrawElements(g_pack, null);
// Create the global paint object that's used by draw operations.
g_paint = g_pack.createObject('CanvasPaint');
// Creates an instance of the canvas utilities library.
g_canvasLib = o3djs.canvas.create(g_pack, g_hudRoot, g_hudViewInfo);
// Create a canvas that will be used to display the text.
g_textCanvas = g_canvasLib.createXYQuad(70, 70, 0, 100, 50, true);
// Set our render callback for animation.
// This sets a function to be executed every time frame is rendered.
g_client.setRenderCallback(renderCallback);
}
/**
* Removes callbacks so they aren't called after the page has unloaded.
*/
function uninit() {
if (g_client) {
g_client.cleanup();
}
}
</script>
</head>
<body>
<h1>Tutorial 4: The beginnings of a HUD</h1>
Every game has some form of HUD...Fortunately it is easy to do.
<br/>
<div id="o3d" style="width: 300px; height: 300px;"></div>
<div style="display:none">
<!-- Start of effect -->
<textarea id="effect">
// World View Projection matrix that will transform the input
// vertices to screen space.
float4x4 worldViewProjection : WorldViewProjection;
// input parameters for our vertex shader
struct VertexShaderInput {
float4 position : POSITION;
};
// input parameters for our pixel shader
struct PixelShaderInput {
float4 position : POSITION;
};
/**
* The vertex shader transforms the input vertices to screen space.
*/
PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
PixelShaderInput output;
// Multiply the vertex positions by the worldViewProjection
// matrix to transform them to screen space.
output.position = mul(input.position, worldViewProjection);
return output;
}
/**
* This pixel shader just returns the color red.
*/
float4 pixelShaderFunction(PixelShaderInput input): COLOR {
return float4(1, 0, 0, 1); // Red.
}
// Here we tell our effect file *which* functions are
// our vertex and pixel shaders.
// #o3d VertexShaderEntryPoint vertexShaderFunction
// #o3d PixelShaderEntryPoint pixelShaderFunction
// #o3d MatrixLoadOrder RowMajor
</textarea>
<!-- End of effect -->
</div>
</body>
</html>
No related posts.
Related posts brought to you by Yet Another Related Posts Plugin.
Serge Meunier is a software developer living in Cape Town, South Africa. He loves programming, fencing, philosophy, feeding his internet addiction, and, of course, dogs.
Comments