Results 1 to 5 of 5

Thread: I need help! Transform objects with single drawarrays call

  1. #1

    I need help! Transform objects with single drawarrays call

    I need to know how to do per object transforms (for animation) with a single drawArrays call - in js, in the shader? And how?

    I have a game engine I am intending to use to prototype iOS and Android games with. I started from a webgl lesson and it has been coming along nicely. Get the source here:http://github.com/johnnyscott/soft-voxels (I'll also copy pasta it below)

    (Use Chrome)
    See it here (until my dynamic IP shuffles):
    http://208.126.167.117/webgl/soft-voxels/

    You fly around with WASD and look around with the mouse. Also you can hit ~ for the console and type ? for a very sparse list of commands ;D.

    At first I was doing a call to drawArrays for every object in the scene per frame and found around 100-1000x performance increase by instead doing a single drawArrays call per frame for all objects. I went from having around 2000 objects at 60fps to easily supporting about 100,000+ at 60fps (actually found no limit)

    Clearly a single call to drawArrays is the way to go for performance reasons.

    I accomplished this by creating a scene graph 'class' that builds a vertex array once before the render loop begins (scene.rebuildBuffers()). This function \ traverses the graph recursively and adds each node's world coords to its local coords and stuffs the result into the end of this massive array (along with texture coords into another array).

    The issue is
    1) I am only doing translation (no rotation or scale) - should have a translation matrix per object probably
    2) I have no idea how I ought to do the transformations per object now. Right now rebuilding the buffers for 40k objects takes a couple hundred ms and I can't do that once per frame and stay at 60fps.

    I thought 'perhaps I only update the objects that have changed' but still this requires some kind of monkey tricks and it's in javascript so it'll be slow doing matrix multiplication and then updating the vertex positions at some offset.

    My other thought was is there some way to maybe have another uniform or something in my shader that holds the transform matrices per object and then update that for the object that has changed and do the transform matrix multiplication in the vertex shader.

    I am pasting code below or you can see it at github it below so you can see.

    THANKS SO MUCH FOR YOUR HELP!!!

    (ps: the code is messy still have unused functions from the example I'm cleaning as I go)


    <!DOCTYPE html>
    <html>

    <head>
    <title>Soft Voxels</title>
    <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
    <style>
    html,body{ overflow:hidden;background:black; cursor:text;}

    canvas {
    position: absolute;
    top: 0;
    left: 0;
    background:black;
    }

    .console{
    position:absolute;
    z-index:1000;
    top:-400px;
    left:0;
    background:transparent;
    height:400px;
    width:33%;
    opacity:0.90;
    }
    .console span{
    display:block;
    position:absolute;
    top:0px;
    bottom:32px;
    left:0;
    right:0;
    width:auto;
    height:auto;
    background:#343942;
    font-family:'Dina', 'Courier New', 'Courier', monospace;
    font-size:15px;
    color:white;
    overflow-y:scroll;
    overflow-x:hidden;
    }
    .console input{
    position:absolute;
    bottom:3px;
    width:auto;
    left:0;
    right:0;
    height:20px;
    }
    </style>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
    <script type="text/javascript" src="glMatrix-0.9.5.min.js"></script>
    <script type="text/javascript" src="webgl-utils.js"></script>
    /* star shader
    <script id="shader-fs" type="x-shader/x-fragment">
    #ifdef GL_ES
    precision highp float;
    #endif

    varying vec2 vTextureCoord;

    uniform sampler2D uSampler;


    void main(void) {
    vec4 textureColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
    if ( textureColor.a < 0.4 ) discard ;
    gl_FragColor = textureColor;// * vec4(uColor, 1.0);
    }
    </script>

    <script id="shader-vs" type="x-shader/x-vertex">
    attribute vec3 aVertexPosition;
    attribute vec2 aTextureCoord;

    uniform mat4 uMVMatrix;
    uniform mat4 uPMatrix;

    varying vec2 vTextureCoord;

    void main(void) {
    gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
    vTextureCoord = aTextureCoord;
    }
    </script>


    <script type="text/javascript">

    var bL = true; // logging on or off?
    var aCommandLog = []; // stack of commands entered
    var iCommandLogIndex = -1; // position in command stack
    // console comamnd names
    var CC_CLEAR = 'clear';
    var CC_HELP = '?';

    var LOOK_ANGLE_RANGE = 540;
    var VIEW_VOLUME_SIZE = 1000;
    var KEY_W = 87;
    var KEY_A = 65;
    var KEY_S = 83;
    var KEY_D = 68;

    var KEY_TILDE = 192;
    var KEY_ENTER = 13;
    var KEY_UP = 38;
    var KEY_DOWN = 40;










    var gl;
    var fFOV = 45.0 // field of view


    var consoleLineNumber = 0;


    var zoom = -15;

    var tilt = 10;
    var spin = 0;
    var pan = 0;

    var runSpeed = 0;
    var runStep = 0.001;

    var strafeSpeed = 0;
    var pX = 0;
    var pY =0;
    var pZ = 0;

    var friction = .0007;





    // fit canvas to window
    function fit(){ $('canvas').css({width:$(window).width()+'px'});}


    function handleKeys()
    {

    // RUN FORWARD
    if (currentlyPressedKeys[KEY_W]) { //w
    // Page Down
    zoom += 0.1;
    runSpeed += runStep;

    }
    // STRAFE LEFT
    if (currentlyPressedKeys[KEY_A]) { //a
    // Up cursor key
    pan += 0.1;
    strafeSpeed +=runStep;
    }
    // RUN BACKWARDS
    if (currentlyPressedKeys[KEY_S]) { //s
    // Page Up
    zoom -= 0.1;
    runSpeed -=runStep;

    }
    // STRAFE RIGHT
    if (currentlyPressedKeys[KEY_D]) { //d
    // Down cursor key
    pan -= 0.1;
    strafeSpeed -= runStep;
    }
    // TOGGLE CONSOLE
    if (currentlyPressedKeys[KEY_TILDE] ) { // ~ `
    // Down cursor key
    toggleConsole();
    }



    }




    // browser console
    function lC(s){ if(bL){console.log(s);}else{}}

    // engine console
    function l(s)
    {
    if(bL){
    consoleLineNumber++; var oConsole = getConsoleLog();
    oConsole.html(
    oConsole.html()
    + consoleLineNumber
    + ''
    + s
    + '
    '
    );
    oConsole[0].scrollTop = oConsole[0].scrollHeight;
    }else{}
    }
    function getConsoleLog(){ return getConsole().find('span'); }
    function getConsole(){ return $('.console');}
    function getConsoleInput(){ return $('.console input');}
    function setConsoleInput(sCmd){ getConsoleInput().val(sCmd); }
    function clearConsoleInput(){ getConsoleInput().val('');}
    function clearConsole(){ getConsoleLog().html(''); }
    var bInConsoleToggle = false;
    function toggleConsole()
    {
    if(bInConsoleToggle){return false;}else{}
    bInConsoleToggle = true;


    var o = getConsole();

    var iTop = 0;
    if(o.offset().top > -1){
    iTop = -o.height();
    getConsoleInput().blur();
    }else{ getConsoleInput().focus(); }
    o.animate(
    {
    top: iTop + 'px'
    }
    ,150
    ,'swing'
    ,function(){ clearConsoleInput(); bInConsoleToggle = false; }
    );
    }
    function consoleSaveCommand(sCmd)
    {
    aCommandLog.push(sCmd);
    iCommandLogIndex = aCommandLog.length -1;
    }

    // run console command and log command and output
    function consoleCommand(sCmd)
    {
    consoleSaveCommand(sCmd);
    clearConsoleInput();

    var sOutput = sCmd;
    switch(sCmd){

    case CC_CLEAR: CC_clear(); return /* special case - don't log clear */; break;

    case CC_HELP: sOutput += CC_help(); break;

    default: sOutput += CC_bad();
    }

    l(sOutput);
    }

    function consoleInputHandler(e)
    {
    var iKeyCode = e.keyCode;
    if(iKeyCode == KEY_ENTER){
    getConsole();
    consoleCommand(e.srcElement.value);
    }else{}

    if(iKeyCode == KEY_UP){ // backup command
    setConsoleInput(aCommandLog[iCommandLogIndex]);
    iCommandLogIndex--; if(iCommsdddddddddandLogIndex < 0 ){iCommandLogIndex = 0; }else{}

    }
    if(iKeyCode == KEY_DOWN){ // next command
    setConsoleInput(aCommandLog[iCommandLogIndex]);
    iCommandLogIndex++; if(iCommandLogIndex >aCommandLog.length-1){iCommandLogIndex = aCommandLog.length-1; }else{}
    }
    }
    // actual commands for engine console
    function CC_bad(){ return '
    command doesn\'t exist or command malformed'; }
    function CC_clear(){ clearConsole(); return ''; }
    function CC_help(){ return '
    Commands
    * clear - clear console
    * ? - this help message'; }






























    function initGL(canvas) {
    try {
    gl = canvas.getContext("experimental-webgl");
    gl.viewportWidth = canvas.width;
    gl.viewportHeight = canvas.height;

    // perspective
    mat4.perspective(fFOV, gl.viewportWidth / gl.viewportHeight, 0.1, VIEW_VOLUME_SIZE, pMatrix);

    // when we want transparency we can do the following:
    // gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
    // gl.enable(gl.BLEND);

    // we moved this so we don't do it every frame
    gl.enable (gl.DEPTH_TEST);
    } catch (e) {}

    if (!gl) {
    alert("Could not initialise WebGL: try get.webgl.org");
    }
    }


    function getShader(gl, id) {
    var shaderScript = document.getElementById(id);
    if (!shaderScript) {
    return null;
    }

    var str = "";
    var k = shaderScript.firstChild;
    while (k) {
    if (k.nodeType == 3) {
    str += k.textContent;
    }
    k = k.nextSibling;
    }

    var shader;
    if (shaderScript.type == "x-shader/x-fragment") {
    shader = gl.createShader(gl.FRAGMENT_SHADER);
    } else if (shaderScript.type == "x-shader/x-vertex") {
    shader = gl.createShader(gl.VERTEX_SHADER);
    } else {
    return null;
    }

    gl.shaderSource(shader, str);
    gl.compileShader(shader);

    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    alert(gl.getShaderInfoLog(shader));
    return null;
    }

    return shader;
    }


    var shaderProgram;

    function initShaders() {
    var fragmentShader = getShader(gl, "shader-fs");
    var vertexShader = getShader(gl, "shader-vs");

    shaderProgram = gl.createProgram();
    gl.attachShader(shaderProgram, vertexShader);
    gl.attachShader(shaderProgram, fragmentShader);
    gl.linkProgram(shaderProgram);

    if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
    alert("Could not initialise shaders");
    }

    gl.useProgram(shaderProgram);

    shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); // gets an integer, on inspection this is 0
    gl.enableVertexAttribArray(shaderProgram.vertexPos itionAttribute);

    shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); // gets an integer, on inspection this is 1
    gl.enableVertexAttribArray(shaderProgram.textureCo ordAttribute);

    shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix"); // projection matrix -> clipping and field of view, perspective
    shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); // modelview matrix -> puts camera in scene
    shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler");

    }


    function handleLoadedTexture(texture) {
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
    gl.generateMipmap(gl.TEXTURE_2D);

    gl.bindTexture(gl.TEXTURE_2D, null);
    }


    var starTexture;

    function initTexture() {
    starTexture = gl.createTexture();
    starTexture.image = new Image();
    starTexture.image.onload = function () {
    handleLoadedTexture(starTexture)
    }

    starTexture.image.src = "grass-billboard.png";
    }


    var mvMatrix = mat4.create();
    var mvMatrixStack = [];
    var pMatrix = mat4.create();

    function mvPushMatrix() {
    var copy = mat4.create();
    mat4.set(mvMatrix, copy);
    mvMatrixStack.push(copy);
    }

    function mvPopMatrix() {
    if (mvMatrixStack.length == 0) {
    throw "Invalid popMatrix!";
    }
    mvMatrix = mvMatrixStack.pop();
    }


    function setMatrixUniforms() {
    gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
    gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
    }


    function degToRad(degrees) {
    return degrees * Math.PI / 180;
    }


    var currentlyPressedKeys = {};

    function handleKeyDown(event) {
    currentlyPressedKeys[event.keyCode] = true;
    }


    function handleKeyUp(event) {
    currentlyPressedKeys[event.keyCode] = false;
    }


    var starVertexPositionBuffer;
    var starVertexTextureCoordBuffer;

    function initBuffers() {
    starVertexPositionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, starVertexPositionBuffer); // gives us a pointer into a store

    vertices = scene.aVertexPositions;

    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); // actually allocates stoe and pushes data up
    starVertexPositionBuffer.itemSize = 3;
    starVertexPositionBuffer.numItems = vertices.length / starVertexPositionBuffer.itemSize;

    starVertexTextureCoordBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, starVertexTextureCoordBuffer);

    var textureCoords = scene.aTextureCoords;

    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW);
    starVertexTextureCoordBuffer.itemSize = 2;
    starVertexTextureCoordBuffer.numItems = textureCoords.length/starVertexTextureCoordBuffer.itemSize;

    return;
    }


    function drawStar() {
    // gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, starTexture);
    gl.uniform1i(shaderProgram.samplerUniform, 0);

    gl.bindBuffer(gl.ARRAY_BUFFER, starVertexTextureCoordBuffer);
    gl.vertexAttribPointer(shaderProgram.textureCoordA ttribute, starVertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);

    gl.bindBuffer(gl.ARRAY_BUFFER, starVertexPositionBuffer);
    gl.vertexAttribPointer(shaderProgram.vertexPositio nAttribute, starVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

    setMatrixUniforms();
    gl.drawArrays(gl.TRIANGLES, 0, starVertexPositionBuffer.numItems);
    }



    function Star(startingDistance, rotationSpeed, position) {
    this['x'] = position[0];
    this['y'] = position[1];
    this['z'] = position[2];

    this.angle = 0;
    this.dist = startingDistance;
    this.rotationSpeed = rotationSpeed;

    }

    Star.prototype.draw = function () {
    mvPushMatrix();

    // Move to the star's positionw
    mat4.translate(mvMatrix, [this.x, this.y, this.z]);

    // Draw the star in its main color
    drawStar()

    mvPopMatrix();
    };


    var effectiveFPMS = 60 / 1000;
    Star.prototype.animate = function (elapsedTime) {


    };



    var stars = [];

    // random half negative
    function rndHN(iRange){
    return (Math.random() * iRange) - iRange/2;
    }

    var scene = null;
    // star constructor: function Star(startingDistance, rotationSpeed) {
    function initWorldObjects() {
    scene = new sceneGraph();
    var numBushes = 38530;
    for (var i=0; i < numBushes; i++) {
    scene.addChild(
    new sceneNode(
    new obj_quad({ position:
    [rndHN(133)
    ,rndHN(1.1)
    ,rndHN(203)]
    })
    )
    , 0);

    }
    var i = new Date().getTime();
    scene.rebuildBuffers(); // generate nice arrays of vertices
    // array concat is slow that's the problem traversing only takes like 4 ms
    console.log(new Date().getTime() - i);
    }


    function drawScene() {
    // is this needed? I commented it out and behavior is the same ??
    //gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);

    gl.clear(gl.COLOR_BUFFER_BIT );

    // THIS IS DONE IN INIT NOW since it need only be done once
    // mat4.perspective(fFOV, gl.viewportWidth / gl.viewportHeight, 0.1, VIEW_VOLUME_SIZE, pMatrix);



    mat4.identity(mvMatrix);

    mat4.rotate(mvMatrix, degToRad(oMouse.angleY), [1.0, 0.0, 0.0]);
    mat4.rotate(mvMatrix, degToRad(oMouse.angleX), [0.0, 1.0, 0.0]);
    mat4.translate(mvMatrix, [pX, pY, pZ]);

    mvPushMatrix();

    // Move to the star's positionw
    // mat4.translate(mvMatrix, [0, 0, 0]);

    // Draw the star in its main color
    gl.bindTexture(gl.TEXTURE_2D, starTexture);
    gl.uniform1i(shaderProgram.samplerUniform, 0);

    gl.bindBuffer(gl.ARRAY_BUFFER, starVertexTextureCoordBuffer);
    gl.vertexAttribPointer(shaderProgram.textureCoordA ttribute, starVertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);

    gl.bindBuffer(gl.ARRAY_BUFFER, starVertexPositionBuffer);
    gl.vertexAttribPointer(shaderProgram.vertexPositio nAttribute, starVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

    setMatrixUniforms();
    gl.drawArrays(gl.TRIANGLES, 0, starVertexPositionBuffer.numItems);

    mvPopMatrix();

    }


    var lastTime = 0;

    function animate() {
    var timeNow = new Date().getTime();
    if (lastTime != 0) {
    var elapsed = timeNow - lastTime;

    // apply friction to speeds
    if( runSpeed < 0){
    runSpeed += friction;
    if(runSpeed > 0) {runSpeed=0;}else{}
    }else{
    runSpeed -= friction;
    if(runSpeed < 0) {runSpeed=0;}else{}
    }

    // apply friction to speeds
    if( strafeSpeed < 0){
    strafeSpeed += friction;
    if(strafeSpeed > 0) {strafeSpeed=0;}else{}
    }else{
    strafeSpeed -= friction;
    if(strafeSpeed < 0) {strafeSpeed=0;}else{}
    }

    pX += Math.cos(degToRad(90+oMouse.angleX)) * runSpeed * elapsed;
    pZ += Math.sin(degToRad(90+oMouse.angleX)) * runSpeed * elapsed;

    pX += Math.cos(degToRad(oMouse.angleX)) * strafeSpeed * elapsed;
    pZ += Math.sin(degToRad(oMouse.angleX)) * strafeSpeed * elapsed;

    pY += Math.sin(degToRad(oMouse.angleY)) * runSpeed * elapsed;



    }
    lastTime = timeNow;

    }


    function tick() {
    requestAnimFrame(tick);
    handleKeys();
    drawScene();
    animate();
    }



    function webGLStart() {
    var canvas = document.getElementById("lesson09-canvas");
    initGL(canvas);
    initShaders();
    initWorldObjects();
    initBuffers();
    initTexture();


    gl.clearColor(0.0, 0.0, 0.0, 1.0);

    document.onkeydown = handleKeyDown;
    document.onkeyup = handleKeyUp;

    tick();
    }


    var oMouse = {x:0,y:0,angleY:0.0,angleX:0.0}
    function mouseMoveHandler(e)
    {
    oMouse.x = e.pageX;
    oMouse.y = e.pageY;

    // calculate angle as percentage of window height/width
    var iH = $(window).height();
    var iW = $(window).width();
    var x = oMouse.x;
    var y = oMouse.y;


    var oldAX = oMouse.angleX;
    var oldAY = oMouse.angleY;

    oMouse.angleX = (x/iW) * LOOK_ANGLE_RANGE;
    oMouse.angleY = (y/iH) * LOOK_ANGLE_RANGE;
    oMouse.angleY -= LOOK_ANGLE_RANGE/2;
    oMouse.angleX -= LOOK_ANGLE_RANGE/2;

    }


















    $(document).ready(
    function()
    {

    fit(); $(window).resize( fit ); // fit canvas to window handler
    $(document).mousemove( mouseMoveHandler ); // mouse move handler
    getConsoleInput().keyup( consoleInputHandler ); // console command handler

    }
    );
















    // SCENE GRAPH


    // BASE OBJECT //////////////////////////////
    var obj_base = {};
    obj_base.aDefaultProperties = {
    position: [0.0, 0.0, 0.0] // world position
    };




    // QUAD OBJECT /////////////////////////////
    function obj_quad(aProperties)
    {
    // get defaults from base object, overwrite with arguments
    for(var k in obj_base.aDefaultProperties){ this[k] = obj_base.aDefaultProperties[k]; }
    for(var k in aProperties){ this[k] = aProperties[k]; }

    this.aVertexPositions =
    [-1.0, -1.0, 0.0,
    1.0, -1.0, 0.0,
    -1.0, 1.0, 0.0,

    -1.0, 1.0, 0.0,
    1.0, -1.0, 0.0,
    1.0, 1.0, 0.0
    ];

    this.iTriangleCount = this.aVertexPositions.length / 9;

    this.aTextureCoords =
    [0.0, 0.0,
    1.0, 0.0,
    0.0, 1.0,

    0.0, 1.0,
    1.0, 0.0,
    1.0, 1.0
    ];
    }

    obj_quad.prototype.getVertexPositions = function() // applies world position to local vertex positions
    {
    var aReturnVertexArray = [];
    var a = this.aVertexPositions;
    var p = this.position;
    for(var i = 0; i < this.iTriangleCount; i++){
    aReturnVertexArray = aReturnVertexArray.concat(
    [ a[(i*9)] + p[0], a[(i*9)+1] + p[1], a[(i*9)+2] + p[2] ] // vertex 1
    ,[ a[(i*9)+3] + p[0], a[(i*9)+4] + p[1], a[(i*9)+5] + p[2] ] // vertex 2
    ,[ a[(i*9)+6] + p[0], a[(i*9)+7] + p[1], a[(i*9)+8] + p[2] ] // vertex 3

    );
    }
    return aReturnVertexArray;
    };

    obj_quad.prototype.getTextureCoords = function(){ return this.aTextureCoords; };




    // SCENE GRAPH /////////////////////////////

    // NODE oooo
    function sceneNode(geometry){
    this.iParentIndex = -1;
    this.aChildrenIndices= [];
    this.geometry = geometry;
    }

    sceneNode.prototype.setParent = function(index){
    var oldParent = this.iParentIndex;
    this.iParentIndex = index;
    return oldParent;
    };

    sceneNode.prototype.hasChildren = function(){ return this.aChildrenIndices.length > 0; };



    // GRAPH oooo
    function sceneGraph(){
    // vertex data
    this.aVertexPositions = [];
    this.aTextureCoords = [];

    // graph
    this.graph = [];

    // create root node
    this.rootIndex = this.addChild(new sceneNode(null), null);
    }

    sceneGraph.prototype.addChild = function(node, parentIndex)
    {
    var iNodeIndex = this.graph.push(node) - 1;
    if(parentIndex != null){
    node.setParent(parentIndex);
    this.getNode(parentIndex).aChildrenIndices.push(iN odeIndex);
    }else{}
    return iNodeIndex;
    };

    sceneGraph.prototype.getRoot = function(){ return this.getNode(this.rootIndex); };
    sceneGraph.prototype.getNode = function(index){ return this.graph[index]; };

    sceneGraph.prototype.clearBuffers = function(){ this.aVertexPositions = []; this.aTextureCoords = []; };

    sceneGraph.prototype.rebuildBuffers = function(){
    this.clearBuffers();
    this.rebuildBuffersAtNode( this.getRoot() );
    };
    sceneGraph.prototype.rebuildBuffersAtNode = function(node)
    {

    // PROCESS SELF
    // concat geometry and texture coords to list

    if(node.geometry != null){
    // this.aVertexPositions = this.aVertexPositions.concat(node.geometry.getVert exPositions());
    // this.aTextureCoords = this.aTextureCoords.concat(node.geometry.getTextur eCoords());

    var aPos = node.geometry.getVertexPositions();
    var aPosL = aPos.length;
    var aTex = node.geometry.getTextureCoords();
    var aTexL = aTex.length;
    for(var i = 0; i< aPosL; i++){
    this.aVertexPositions.push(aPos[i]);
    }
    for(var i = 0; i< aTexL; i++){
    this.aTextureCoords.push(aTex[i]);
    }
    }else{}

    // PROCESS CHILDREN
    // no children? do nothing, else rebuild foreach child
    if(!node.hasChildren()){ return; }
    else{
    var children = node.aChildrenIndices;
    for(var i = 0; i<children.length; i++){
    this.rebuildBuffersAtNode(this.getNode( children[i] ));
    }
    }
    };























    </script>


    </head>


    <body onload="webGLStart();">

    <canvas id="lesson09-canvas" style="border: none;" width="569" height="320"></canvas>





    <div class="console"><span></span><input type="text" /></div>









    </body>

    </html>

  2. #2

    Re: I need help! Transform objects with single drawarrays ca

    OK that first post is so long Here's a picture to pay you guys back of what my neo-natal engine is doing. I can't get this to drop below 60fps so I love a single call to drawArrays per frame


    The question is simpified as this

    1) my sceneGraph builds geometry array once then just drawArrays it over and over

    2) nodes are quads look like this:
    Code :
     function obj_quad(aProperties)
         {
          // get defaults from base object, overwrite with arguments
          for(var k in obj_base.aDefaultProperties){ this[k] = obj_base.aDefaultProperties[k]; }  
          for(var k in aProperties){ this[k] = aProperties[k]; }   
     
          this.aVertexPositions = 
            [-1.0, -1.0, 0.0,
              1.0, -1.0, 0.0,
             -1.0,  1.0, 0.0,
     
             -1.0,  1.0, 0.0,
              1.0, -1.0, 0.0,
              1.0,  1.0, 0.0
             ]; 
     
          this.iTriangleCount = this.aVertexPositions.length / 9;
     
          this.aTextureCoords = 
             [0.0, 0.0,
              1.0, 0.0,
              0.0, 1.0,
     
              0.0, 1.0,
              1.0, 0.0,
              1.0, 1.0
            ]; 
         }
     
         obj_quad.prototype.getVertexPositions = function() // applies world position to local vertex positions
         {
          var aReturnVertexArray = [];
          var a = this.aVertexPositions;
          var p = this.position;
          for(var i = 0; i < this.iTriangleCount;  i++){
            aReturnVertexArray = aReturnVertexArray.concat( 
              [ a[(i*9)]   + p[0], a[(i*9)+1] + p[1], a[(i*9)+2] + p[2] ] // vertex 1
             ,[ a[(i*9)+3] + p[0], a[(i*9)+4] + p[1], a[(i*9)+5] + p[2] ] // vertex 2
             ,[ a[(i*9)+6] + p[0], a[(i*9)+7] + p[1], a[(i*9)+8] + p[2] ] // vertex 3
     
            );
          }
          return aReturnVertexArray;
         };
     
         obj_quad.prototype.getTextureCoords = function(){ return this.aTextureCoords; };


    3) QUESTION: what is the best way to do per object transforms when you have a single call to drawarrays?
    a) in javascript per object via transform matrices in obj_quad's

    b) in shader by some kind of massive array of transform matrices (one for each object) that paralles the single vertex array I have?

    c) none of the above - I'm crazy.


    COMPLETE CODE AGAIN PASTED CORRECTLY (also see github above)
    Code :
     
    <!DOCTYPE html> 
    <html> 
     
    <head> 
    <title>Soft Voxels</title> 
    <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1"> 
    <style> 
    html,body{ overflow:hidden;background:black; cursor:text;}
     
    canvas {
      position: absolute;
      top: 0;
      left: 0;
      background:black;
    }
     
    .console{
        position:absolute;
        z-index:1000;
        top:-400px;
        left:0;
        background:transparent;
        height:400px;
        width:33%;
        opacity:0.90;
    }
    .console span{
        display:block;
        position:absolute;
        top:0px;
        bottom:32px;
        left:0;
        right:0;
        width:auto;
        height:auto;
        background:#343942;
        font-family:'Dina', 'Courier New', 'Courier', monospace;
        font-size:15px;    
        color:white;
        overflow-y:scroll;
        overflow-x:hidden;
    }
    .console input{
        position:absolute;
        bottom:3px;
        width:auto;
        left:0;
        right:0;
        height:20px;
    }
    </style> 
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script> 
    <script type="text/javascript" src="glMatrix-0.9.5.min.js"></script> 
    <script type="text/javascript" src="webgl-utils.js"></script> 
    /* star shader
    <script id="shader-fs" type="x-shader/x-fragment"> 
        #ifdef GL_ES
        precision highp float;
        #endif
     
        varying vec2 vTextureCoord;
     
        uniform sampler2D uSampler;
     
     
        void main(void) {
            vec4 textureColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
         if ( textureColor.a < 0.4 ) discard ;
            gl_FragColor = textureColor;// * vec4(uColor, 1.0);
        }
    </script> 
     
    <script id="shader-vs" type="x-shader/x-vertex"> 
        attribute vec3 aVertexPosition;
        attribute vec2 aTextureCoord;
     
        uniform mat4 uMVMatrix;
        uniform mat4 uPMatrix;
     
        varying vec2 vTextureCoord;
     
        void main(void) {
            gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
            vTextureCoord = aTextureCoord;
        }
    </script> 
     
     
    <script type="text/javascript"> 
     
        var bL = true; // logging on or off?
        var aCommandLog = []; // stack of commands entered
        var iCommandLogIndex = -1; // position in command stack
        // console comamnd names
        var CC_CLEAR = 'clear';
        var CC_HELP  = '?';
     
        var LOOK_ANGLE_RANGE = 540;
        var VIEW_VOLUME_SIZE = 1000;
        var KEY_W = 87;
        var KEY_A = 65;
        var KEY_S = 83;
        var KEY_D = 68;
     
        var KEY_TILDE        = 192;
        var KEY_ENTER        = 13;
        var KEY_UP           = 38;
        var KEY_DOWN         = 40;
     
     
     
     
     
     
     
     
     
     
        var gl;
        var fFOV = 45.0 // field of view
     
     
        var consoleLineNumber = 0;
     
     
        var zoom = -15;
     
        var tilt = 10;
        var spin = 0;
        var pan = 0;
     
        var runSpeed = 0;
        var runStep = 0.001;
     
        var strafeSpeed = 0;
        var pX = 0;
        var pY =0;
        var pZ = 0;
     
        var friction = .0007;
     
     
     
     
     
        // fit canvas to window
        function fit(){ $('canvas').css({width:$(window).width()+'px'});}
     
     
        function handleKeys() 
        {
     
            // RUN FORWARD
            if (currentlyPressedKeys[KEY_W]) { //w
                // Page Down
                zoom += 0.1;
                runSpeed += runStep;
     
            }
            // STRAFE LEFT 
            if (currentlyPressedKeys[KEY_A]) { //a
                // Up cursor key
                pan += 0.1;
                strafeSpeed +=runStep;
            }
            // RUN BACKWARDS
            if (currentlyPressedKeys[KEY_S]) { //s
                // Page Up
                zoom -= 0.1;
                runSpeed -=runStep;
     
            }
            // STRAFE RIGHT 
            if (currentlyPressedKeys[KEY_D]) { //d
                // Down cursor key
                pan -= 0.1;
                strafeSpeed -= runStep;
            }
            // TOGGLE CONSOLE
            if (currentlyPressedKeys[KEY_TILDE] ) { // ~ `
                // Down cursor key
               toggleConsole();
            }
     
     
     
        }
     
     
     
     
        // browser console    
        function lC(s){ if(bL){console.log(s);}else{}}    
     
        // engine  console
        function l(s)
        {     
         if(bL){ 
          consoleLineNumber++; var oConsole =   getConsoleLog();       
          oConsole.html( 
           oConsole.html() 
           + consoleLineNumber 
           + ''
           + s 
           + '
    '
          );  
          oConsole[0].scrollTop = oConsole[0].scrollHeight;      
         }else{}      
        }  
        function getConsoleLog(){ return getConsole().find('span'); }
        function getConsole(){ return $('.console');}
        function getConsoleInput(){ return $('.console input');}
        function setConsoleInput(sCmd){ getConsoleInput().val(sCmd); }
        function clearConsoleInput(){ getConsoleInput().val('');}
        function clearConsole(){ getConsoleLog().html(''); }
        var bInConsoleToggle = false;
        function toggleConsole()
        {
            if(bInConsoleToggle){return false;}else{}
            bInConsoleToggle = true;
     
     
            var o = getConsole();
     
            var iTop = 0;
            if(o.offset().top > -1){
                iTop = -o.height();
                getConsoleInput().blur();
            }else{ getConsoleInput().focus(); }
            o.animate(
            {
                top: iTop + 'px'
            }
            ,150
            ,'swing'
            ,function(){ clearConsoleInput(); bInConsoleToggle = false;  }
            );        
        }
        function consoleSaveCommand(sCmd)
        {
          aCommandLog.push(sCmd);
          iCommandLogIndex = aCommandLog.length -1;
        }
     
        // run console command and log command and output
        function consoleCommand(sCmd) 
        {   
         consoleSaveCommand(sCmd);
         clearConsoleInput();
     
         var sOutput = sCmd; 
         switch(sCmd){
     
          case CC_CLEAR: CC_clear(); return /* special case - don't log clear */; break;
     
          case CC_HELP: sOutput += CC_help(); break;
     
          default: sOutput += CC_bad();
         }
     
         l(sOutput);
        }
     
        function consoleInputHandler(e)
        {
          var iKeyCode = e.keyCode;
          if(iKeyCode == KEY_ENTER){ 
            getConsole();
            consoleCommand(e.srcElement.value);
          }else{}
     
          if(iKeyCode == KEY_UP){ // backup command
            setConsoleInput(aCommandLog[iCommandLogIndex]);
            iCommandLogIndex--; if(iCommsdddddddddandLogIndex < 0 ){iCommandLogIndex = 0; }else{}
     
          }
          if(iKeyCode == KEY_DOWN){ // next command
            setConsoleInput(aCommandLog[iCommandLogIndex]);
            iCommandLogIndex++; if(iCommandLogIndex >aCommandLog.length-1){iCommandLogIndex = aCommandLog.length-1; }else{}
          }
        }
        // actual commands for engine console
        function CC_bad(){ return '
    command doesn\'t exist or command malformed'; }
        function CC_clear(){ clearConsole(); return ''; }
        function CC_help(){ return '
    Commands
    * clear - clear console
    * ? - this help message'; }
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
        function initGL(canvas) {
          try {
            gl = canvas.getContext("experimental-webgl");
            gl.viewportWidth = canvas.width;
            gl.viewportHeight = canvas.height;
     
           // perspective
            mat4.perspective(fFOV, gl.viewportWidth / gl.viewportHeight, 0.1, VIEW_VOLUME_SIZE, pMatrix);
     
           // when we want transparency we can do the following:
           //   gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
           //   gl.enable(gl.BLEND);
     
           // we moved this so we don't do it every frame 
              gl.enable (gl.DEPTH_TEST);
          } catch (e) {}
     
          if (!gl) {
              alert("Could not initialise WebGL: try get.webgl.org");
          }
        }
     
     
        function getShader(gl, id) {
            var shaderScript = document.getElementById(id);
            if (!shaderScript) {
                return null;
            }
     
            var str = "";
            var k = shaderScript.firstChild;
            while (k) {
                if (k.nodeType == 3) {
                    str += k.textContent;
                }
                k = k.nextSibling;
            }
     
            var shader;
            if (shaderScript.type == "x-shader/x-fragment") {
                shader = gl.createShader(gl.FRAGMENT_SHADER);
            } else if (shaderScript.type == "x-shader/x-vertex") {
                shader = gl.createShader(gl.VERTEX_SHADER);
            } else {
                return null;
            }
     
            gl.shaderSource(shader, str);
            gl.compileShader(shader);
     
            if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
                alert(gl.getShaderInfoLog(shader));
                return null;
            }
     
            return shader;
        }
     
     
        var shaderProgram;
     
        function initShaders() {
            var fragmentShader = getShader(gl, "shader-fs");
            var vertexShader = getShader(gl, "shader-vs");
     
            shaderProgram = gl.createProgram();
            gl.attachShader(shaderProgram, vertexShader);
            gl.attachShader(shaderProgram, fragmentShader);
            gl.linkProgram(shaderProgram);
     
            if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
                alert("Could not initialise shaders");
            }
     
            gl.useProgram(shaderProgram);
     
            shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); // gets an integer, on inspection this is 0
            gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
     
            shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); // gets an integer, on inspection this is 1
            gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute);
     
            shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix"); // projection matrix -> clipping and field of view, perspective
            shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); // modelview matrix -> puts camera in scene
            shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler");
     
        }
     
     
        function handleLoadedTexture(texture) {
            gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
            gl.bindTexture(gl.TEXTURE_2D, texture);
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
            gl.generateMipmap(gl.TEXTURE_2D);
     
            gl.bindTexture(gl.TEXTURE_2D, null);
        }
     
     
        var starTexture;
     
        function initTexture() {
            starTexture = gl.createTexture();
            starTexture.image = new Image();
            starTexture.image.onload = function () {
                handleLoadedTexture(starTexture)
            }
     
            starTexture.image.src = "grass-billboard.png";
        }
     
     
        var mvMatrix = mat4.create();
        var mvMatrixStack = [];
        var pMatrix = mat4.create();
     
        function mvPushMatrix() {
            var copy = mat4.create();
            mat4.set(mvMatrix, copy);
            mvMatrixStack.push(copy);
        }
     
        function mvPopMatrix() {
            if (mvMatrixStack.length == 0) {
                throw "Invalid popMatrix!";
            }
            mvMatrix = mvMatrixStack.pop();
        }
     
     
        function setMatrixUniforms() {
            gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
            gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
        }
     
     
        function degToRad(degrees) {
            return degrees * Math.PI / 180;
        }
     
     
        var currentlyPressedKeys = {};
     
        function handleKeyDown(event) {
            currentlyPressedKeys[event.keyCode] = true;
        }
     
     
        function handleKeyUp(event) {
            currentlyPressedKeys[event.keyCode] = false;
        }
     
     
        var starVertexPositionBuffer;
        var starVertexTextureCoordBuffer;
     
        function initBuffers() {
            starVertexPositionBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, starVertexPositionBuffer); // gives us a pointer into a store
     
            vertices = scene.aVertexPositions;
     
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); // actually allocates stoe and pushes data up
            starVertexPositionBuffer.itemSize = 3;
            starVertexPositionBuffer.numItems = vertices.length / starVertexPositionBuffer.itemSize;
     
            starVertexTextureCoordBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, starVertexTextureCoordBuffer);
     
            var textureCoords = scene.aTextureCoords;
     
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW);
            starVertexTextureCoordBuffer.itemSize = 2;
            starVertexTextureCoordBuffer.numItems = textureCoords.length/starVertexTextureCoordBuffer.itemSize;
     
            return;
        }
     
     
        function drawStar() {
         //   gl.activeTexture(gl.TEXTURE0);
            gl.bindTexture(gl.TEXTURE_2D, starTexture);
            gl.uniform1i(shaderProgram.samplerUniform, 0);
     
            gl.bindBuffer(gl.ARRAY_BUFFER, starVertexTextureCoordBuffer);
            gl.vertexAttribPointer(shaderProgram.textureCoordAttribute, starVertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);
     
            gl.bindBuffer(gl.ARRAY_BUFFER, starVertexPositionBuffer);
            gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, starVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
     
            setMatrixUniforms();
            gl.drawArrays(gl.TRIANGLES, 0, starVertexPositionBuffer.numItems);
        }
     
     
     
        function Star(startingDistance, rotationSpeed, position) {
            this['x'] = position[0];
            this['y'] = position[1];
            this['z'] = position[2];
     
            this.angle = 0;
            this.dist = startingDistance;
            this.rotationSpeed = rotationSpeed;
     
        }
     
        Star.prototype.draw = function () {
            mvPushMatrix();
     
            // Move to the star's positionw  
            mat4.translate(mvMatrix, [this.x, this.y, this.z]);
     
            // Draw the star in its main color
            drawStar()
     
            mvPopMatrix();
        };
     
     
        var effectiveFPMS = 60 / 1000;
        Star.prototype.animate = function (elapsedTime) {
     
     
        };
     
     
     
        var stars = [];
     
        // random half negative
        function rndHN(iRange){
            return (Math.random() * iRange) - iRange/2;
        }
     
        var scene = null;
        // star constructor:   function Star(startingDistance, rotationSpeed) {
        function initWorldObjects() {
            scene = new sceneGraph();
            var numBushes = 78530;
            for (var i=0; i < numBushes; i++) {
              scene.addChild(
                new sceneNode(
                  new obj_quad({ position:
                                  [rndHN(333)
                                  ,rndHN(1.1)
                                  ,rndHN(303)]
                                })
                )
                , 0);
     
            }
           var i = new Date().getTime();
            scene.rebuildBuffers(); // generate nice arrays of vertices
            // array concat is slow  that's the problem traversing only takes like 4 ms
              console.log(new Date().getTime() - i);
        }
     
     
        function drawScene() {
            // is this needed? I commented it out and behavior is the same ??
            //gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
     
            gl.clear(gl.COLOR_BUFFER_BIT );
     
            // THIS IS DONE IN INIT NOW since it need only be done once
            //    mat4.perspective(fFOV, gl.viewportWidth / gl.viewportHeight, 0.1, VIEW_VOLUME_SIZE, pMatrix);
     
     
     
            mat4.identity(mvMatrix);
     
            mat4.rotate(mvMatrix, degToRad(oMouse.angleY), [1.0, 0.0, 0.0]);
            mat4.rotate(mvMatrix, degToRad(oMouse.angleX), [0.0, 1.0, 0.0]);
            mat4.translate(mvMatrix, [pX, pY, pZ]);
     
            mvPushMatrix();
     
            // Move to the star's positionw  
           // mat4.translate(mvMatrix, [0, 0, 0]);
     
            // Draw the star in its main color
            gl.bindTexture(gl.TEXTURE_2D, starTexture);
            gl.uniform1i(shaderProgram.samplerUniform, 0);
     
            gl.bindBuffer(gl.ARRAY_BUFFER, starVertexTextureCoordBuffer);
            gl.vertexAttribPointer(shaderProgram.textureCoordAttribute, starVertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);
     
            gl.bindBuffer(gl.ARRAY_BUFFER, starVertexPositionBuffer);
            gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, starVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
     
            setMatrixUniforms();
            gl.drawArrays(gl.TRIANGLES, 0, starVertexPositionBuffer.numItems);
     
            mvPopMatrix();
     
        }
     
     
        var lastTime = 0;
     
        function animate() {
            var timeNow = new Date().getTime();
            if (lastTime != 0) {
                var elapsed = timeNow - lastTime;
     
                // apply friction to speeds
                if( runSpeed < 0){ 
                    runSpeed += friction;
                    if(runSpeed > 0) {runSpeed=0;}else{}
                }else{
                    runSpeed -= friction;
                    if(runSpeed < 0) {runSpeed=0;}else{}
                }
     
                // apply friction to speeds
                if( strafeSpeed < 0){ 
                    strafeSpeed += friction;
                    if(strafeSpeed > 0) {strafeSpeed=0;}else{}
                }else{
                    strafeSpeed -= friction;
                    if(strafeSpeed < 0) {strafeSpeed=0;}else{}
                }
     
                pX += Math.cos(degToRad(90+oMouse.angleX)) * runSpeed * elapsed;                                     
                pZ +=  Math.sin(degToRad(90+oMouse.angleX)) * runSpeed * elapsed; 
     
                pX += Math.cos(degToRad(oMouse.angleX)) * strafeSpeed * elapsed;                                     
                pZ +=  Math.sin(degToRad(oMouse.angleX)) * strafeSpeed * elapsed; 
     
                pY += Math.sin(degToRad(oMouse.angleY)) * runSpeed * elapsed;
     
     
     
            }
            lastTime = timeNow;
     
        }
     
     
        function tick() {
            requestAnimFrame(tick);
            handleKeys();
            drawScene();
            animate();
        }
     
     
     
        function webGLStart() {
            var canvas = document.getElementById("lesson09-canvas");
            initGL(canvas);
            initShaders();
            initWorldObjects();
            initBuffers();
            initTexture();
     
     
            gl.clearColor(0.0, 0.0, 0.0, 1.0);
     
            document.onkeydown = handleKeyDown;
            document.onkeyup = handleKeyUp;
     
            tick();
        }
     
     
        var oMouse = {x:0,y:0,angleY:0.0,angleX:0.0}
        function mouseMoveHandler(e)
        {
         oMouse.x = e.pageX;
         oMouse.y = e.pageY;
     
         // calculate angle as percentage of window height/width
         var iH = $(window).height();
         var iW = $(window).width();
         var x = oMouse.x;
         var y = oMouse.y;
     
     
         var oldAX = oMouse.angleX;
         var oldAY = oMouse.angleY;
     
         oMouse.angleX = (x/iW) * LOOK_ANGLE_RANGE;
         oMouse.angleY = (y/iH) * LOOK_ANGLE_RANGE;
         oMouse.angleY -= LOOK_ANGLE_RANGE/2;
         oMouse.angleX -= LOOK_ANGLE_RANGE/2;
     
        }
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
        $(document).ready(
        function()
        {
     
          fit(); $(window).resize( fit ); // fit canvas to window handler
          $(document).mousemove( mouseMoveHandler ); // mouse move handler
          getConsoleInput().keyup( consoleInputHandler ); // console command handler
     
        }
        );
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
    // SCENE GRAPH    
     
     
        // BASE OBJECT //////////////////////////////
        var obj_base = {};
        obj_base.aDefaultProperties = { 
           position: [0.0, 0.0, 0.0] // world position
        };
     
     
     
     
        // QUAD OBJECT /////////////////////////////
         function obj_quad(aProperties)
         {
          // get defaults from base object, overwrite with arguments
          for(var k in obj_base.aDefaultProperties){ this[k] = obj_base.aDefaultProperties[k]; }  
          for(var k in aProperties){ this[k] = aProperties[k]; }   
     
          this.aVertexPositions = 
            [-1.0, -1.0, 0.0,
              1.0, -1.0, 0.0,
             -1.0,  1.0, 0.0,
     
             -1.0,  1.0, 0.0,
              1.0, -1.0, 0.0,
              1.0,  1.0, 0.0
             ]; 
     
          this.iTriangleCount = this.aVertexPositions.length / 9;
     
          this.aTextureCoords = 
             [0.0, 0.0,
              1.0, 0.0,
              0.0, 1.0,
     
              0.0, 1.0,
              1.0, 0.0,
              1.0, 1.0
            ]; 
         }
     
         obj_quad.prototype.getVertexPositions = function() // applies world position to local vertex positions
         {
          var aReturnVertexArray = [];
          var a = this.aVertexPositions;
          var p = this.position;
          for(var i = 0; i < this.iTriangleCount;  i++){
            aReturnVertexArray = aReturnVertexArray.concat( 
              [ a[(i*9)]   + p[0], a[(i*9)+1] + p[1], a[(i*9)+2] + p[2] ] // vertex 1
             ,[ a[(i*9)+3] + p[0], a[(i*9)+4] + p[1], a[(i*9)+5] + p[2] ] // vertex 2
             ,[ a[(i*9)+6] + p[0], a[(i*9)+7] + p[1], a[(i*9)+8] + p[2] ] // vertex 3
     
            );
          }
          return aReturnVertexArray;
         };
     
         obj_quad.prototype.getTextureCoords = function(){ return this.aTextureCoords; };
     
     
     
     
        // SCENE GRAPH /////////////////////////////
     
        // NODE oooo
        function sceneNode(geometry){
          this.iParentIndex = -1;
          this.aChildrenIndices= [];
          this.geometry = geometry;
        }
     
        sceneNode.prototype.setParent = function(index){ 
          var oldParent = this.iParentIndex; 
          this.iParentIndex = index;
          return oldParent;
        };
     
        sceneNode.prototype.hasChildren = function(){ return this.aChildrenIndices.length > 0; };
     
     
     
        // GRAPH oooo
        function sceneGraph(){
          // vertex data
          this.aVertexPositions = [];
          this.aTextureCoords = [];
     
          // graph
          this.graph = [];
     
          // create root node  
          this.rootIndex = this.addChild(new sceneNode(null), null);
        }
     
        sceneGraph.prototype.addChild = function(node, parentIndex)
        {
          var iNodeIndex = this.graph.push(node) - 1;
          if(parentIndex != null){
            node.setParent(parentIndex);
            this.getNode(parentIndex).aChildrenIndices.push(iNodeIndex);
          }else{}
          return iNodeIndex;
        };
     
        sceneGraph.prototype.getRoot = function(){ return this.getNode(this.rootIndex); };
        sceneGraph.prototype.getNode = function(index){ return this.graph[index]; };
     
        sceneGraph.prototype.clearBuffers = function(){ this.aVertexPositions = []; this.aTextureCoords = []; };
     
        sceneGraph.prototype.rebuildBuffers = function(){
          this.clearBuffers();
          this.rebuildBuffersAtNode( this.getRoot() );
        };
        sceneGraph.prototype.rebuildBuffersAtNode = function(node) 
        {      
     
          // PROCESS SELF
          //  concat geometry and texture coords to list
     
          if(node.geometry != null){
         //  this.aVertexPositions = this.aVertexPositions.concat(node.geometry.getVertexPositions());
         //  this.aTextureCoords = this.aTextureCoords.concat(node.geometry.getTextureCoords());
     
           var aPos = node.geometry.getVertexPositions();
           var aPosL = aPos.length;
           var aTex = node.geometry.getTextureCoords();
           var aTexL = aTex.length;
           for(var i = 0; i< aPosL; i++){
            this.aVertexPositions.push(aPos[i]);
           }
           for(var i = 0; i< aTexL; i++){
            this.aTextureCoords.push(aTex[i]);
           }
          }else{}
     
          // PROCESS CHILDREN
          //  no children? do nothing, else rebuild foreach child 
          if(!node.hasChildren()){ return; }
          else{
            var children = node.aChildrenIndices;
            for(var i = 0; i<children.length; i++){
              this.rebuildBuffersAtNode(this.getNode( children[i] ));
            }      
          }
        };
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
    </script> 
     
     
    </head> 
     
     
    <body onload="webGLStart();"> 
     
        <canvas id="lesson09-canvas" style="border: none;" width="569" height="320"></canvas> 
     
     
     
     
     
        <div class="console"><span></span><input type="text" /></div> 
     
     
     
     
     
     
     
     
     
    </body> 
     
    </html>

  3. #3

    I think I've got it figured out

    OK so my solution to using a single call to drawArrays with lots of animated objects is to use a glMatrix transform matrix against the animated objects vertices and bufferSubData() the objects new vertex positions into the single vertexbuffer

    1. When I traverse the scene graph before the render loop, store the objects index into the single vertex buffer for use as the offset in bufferSubData(), also add the item to an 'animatable' list[/*:m:3gks6bm8]
    2. on each frame, for each item in the 'animatable' list, apply the objects transforms to its vertices in js [/*:m:3gks6bm8]
    3. bufferSubData() to the gpu the result[/*:m:3gks6bm8]
    4. Profit[/*:m:3gks6bm8]


    This will work I'm sure of it. I did a test and managed to get some nice sine wave action. The texture coords seem to be screwed which makes no sense but I'll figure that out later.

  4. #4

    Re: I need help! Transform objects with single drawarrays ca

    Quote Originally Posted by judylugan
    Quote Originally Posted by judylugan
    Whoah..! It is pretty long.. But, I'll try to see what i can do ..
    I got this technique I propose above of doing bufferSubData() working in super simplified fashion . I found I could do several THOUSAND buffersubdata's before fps had any hit which means this is the right way to go.

    For now from github you'll see I'm doing the calculation in JS via glMatrix.

    My thought is to have a three new VBOs for position, rotation, and scale and instead of doing the transform math in JS to just buffersubdata() the position,rotation,scale values from JS and do the actual transform math in the vertex shader.

    This should be amazingly fast and give me around 10,000+ animated objects per frame in 1,000,000+ polygons with framerate locked at 60fps.

    Once my engine can do this reliably I'll make a basic game to demonstrate it works. My only game I did last year an HTML5 2d canvas tetris clone if anyone is curious It's awesome. Download it : https://github.com/johnnyscott/Johnn...L5-Tetrominoes

    Here's a screen shot:



    Also I'm a fantastic artist and a great programmer so by the time I figure this out I ought to be a great indie game dev LOL Here's a sketch of my dog I did:



  5. #5

    Re: I need help! Transform objects with single drawarrays ca


Similar Threads

  1. Affine transform, floatnxm
    By Budric in forum OpenCL
    Replies: 5
    Last Post: 07-21-2010, 02:25 PM
  2. Why does EGL call glGetIntegerv when I call wglSwapBuffer?
    By teriba in forum Cross API and window system integration
    Replies: 0
    Last Post: 07-23-2009, 04:50 AM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •