function setupCanvas() {
    var canvas = document.getElementById("canvas");
    var gl = null;
    var program = null;
    var obj = null;
    var mvIndex = null;
    var normalMatrixUniformIndex = null;
    var boneUniformIndex = null;
    var lightPosIndex = null;
    var fpsTime = new Date().getTime() / 1000;
    var frames = 0;
    var models = 0;
    var hax = -1;
    var countSelect = document.getElementById("count-select");
    var instances = [];
    var slog = document.getElementById("logdiv");
    function alog(s) {
        slog.innerHTML += s + "<br>";
    };
    alog("get context");
    gl = getContext(canvas);
    var w = canvas.clientWidth;
    var h = canvas.clientHeight;
    gl.viewport(0, 0, w, h);
    gl.clearColor(0.2, 0.2, 0.2, 0.5);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    gl.enable(gl.DEPTH_TEST);
    program = createProgramFromElements(gl, "vert", "frag", ["vertex", "normal", "tangent", "bitangent", "uv0", "boneWeights", "boneIndices"], alog);
    gl.useProgram(program);
    mvIndex = gl.getUniformLocation(program, "mvp");
    boneUniformIndex = gl.getUniformLocation(program, "boneMatrices");
    normalMatrixUniformIndex = gl.getUniformLocation(program, "normalmatrix");
    lightPosIndex = gl.getUniformLocation(program, "lightPos");
    alog("bone uniform location = " + boneUniformIndex);
    for (var i = 0; i < 32; i += 1) {
        alog(" bone[" + i + "] uniform location = " + gl.getUniformLocation(program, "boneMatrices[" + i + "]"));
    };
    alog("active uniforms = " + gl.getProgrami(program, gl.ACTIVE_UNIFORMS));
    if (gl.getActiveUniform) {
        for (var i = 0; i < gl.getProgrami(program, gl.ACTIVE_UNIFORMS); i += 1) {
            var u = gl.getActiveUniform(program, i);
            alog("uniform " + i + " @" + gl.getUniformLocation(program, u["name"]) + " = " + JSON.stringify(u));
        };
    };
    alog("active attributes = " + gl.getProgrami(program, gl.ACTIVE_ATTRIBUTES));
    if (gl.getActiveAttrib) {
        for (var i = 0; i < gl.getProgrami(program, gl.ACTIVE_ATTRIBUTES); i += 1) {
            var a = gl.getActiveAttrib(program, i);
            alog("attrib " + i + "@ " + gl.getAttribLocation(program, a["name"]) + "= " + JSON.stringify(a));
        };
    };
    alog("used prog");
    simpleVbo(gl, new CanvasUnsignedShortArray([0, 1, 2, 3]), [{ "data" : new CanvasFloatArray([-1, -1, -1, 1, 1, -1, 1, 1]), "size" : 2 }])(gl);
    alog("created buffers");
    function clearBones() {
        if (boneUniformIndex > -1) {
            for (var i = 0; i < 32; i += 1) {
                gl.uniformMatrix4fv(i + boneUniformIndex, false, new CanvasFloatArray([1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]));
            };
        };
    };
    function draw() {
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        ++frames;
        if (boneUniformIndex > -1) {
            gl.uniformMatrix4fv(boneUniformIndex, false, new CanvasFloatArray([1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]));
        };
        var time = new Date().getTime() / 1000;
        var sdiv = document.getElementById("status");
        var la = lookAt(-200, 100, -450, 0, 0, 0, 0, 1, 0).elements;
        var p2 = frustum(-1, 1, -1, 1, 2, 1000).elements;
        var d = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
        var d2 = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
        var r = rotation(time / 5, 0, 1, 0).elements;
        if (time - fpsTime > 2) {
            document.getElementById("fps").innerHTML = "fps: " + (frames / (time - fpsTime)).toPrecision(4) + "  mps: " + (models / (time - fpsTime)).toPrecision(5);
            frames = 0;
            models = 0;
            fpsTime = time;
            if (hax) {
                --hax;
            };
        };
        _matx(p2, d2, d2);
        _matx(d2, la, d2);
        _matx(d2, r, d2);
        var drawCount = instances.length;
        var count = countSelect.selectedIndex + 1;
        if (countSelect) {
            drawCount = Math.min(count * count, drawCount);
        };
        models += drawCount;
        for (var x = 0; x < drawCount; x += 1) {
            var stat = function (a) {
            };
            var instance = instances[x];
            var pos3649 = instance.pos;
            var xx = translation(pos3649[0] - count * 20, pos3649[1], count * 20 - pos3649[2]).elements;
            if (1 == drawCount) {
                _matx(d2, [6, 0, 0, 0, 0, 6, 0, 0, 0, 0, 6, 0, 0, -200, 0, 1], d);
            } else {
                _matx(d2, xx, d);
            };
            gl.uniformMatrix4fv(mvIndex, false, new CanvasFloatArray(d));
            if (!obj) {
                gl.drawElements(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_SHORT, 0);
            };
            if (obj) {
                var animNodes = null;
                if (hax && obj.animate) {
                    obj.animate(instance.anim, 1.23 * x + time * 4 * instance.speed, stat);
                };
                animNodes = obj.animNodes;
                if (instance.anim == 1 || instance.anim == 2) {
                    (instance.pos)[2] += instance.anim * instance.speed;
                };
                if ((instance.pos)[2] > 400) {
                    (instance.pos)[2] = 0;
                };
                for (var m = null, _js_arrvar3651 = obj.meshes, _js_idx3650 = 0; _js_idx3650 < _js_arrvar3651.length; _js_idx3650 += 1) {
                    m = _js_arrvar3651[_js_idx3650];
                    stat("id" + m.id);
                    if (boneUniformIndex > -1) {
                        if (m.uploadBones) {
                            m.uploadBones(gl, boneUniformIndex, animNodes, stat);
                        } else {
                            clearBones();
                        };
                    };
                    if (m.bind) {
                        stat("+[" + m["index-count"] + "]");
                        m.bind(gl, stat);
                        gl.drawElements(gl.TRIANGLES, m["index-count"], gl.UNSIGNED_SHORT, 0);
                    };
                };
            };
        };
        var err = gl.getError();
        if (err) {
            sdiv.innerHTML = "gl error : " + err;
        };
        gl.flush();
    };
    canvas.addEventListener("mousedown", function (ev) {
        gl.clearColor(1.0, 1.2, 1.2, 1.5);
        return true;
    }, false);
    canvas.addEventListener("mousemove", function (ev) {
        gl.clearColor(ev.screenY / 500.0, Math.abs(Math.sin(ev.screenX / 64.0)), 0.2, 1.0);
        return true;
    }, false);
    canvas.addEventListener("mouseup", function (ev) {
        gl.clearColor(0.0, 1.2, 0.2, 0.5);
        return true;
    }, false);
    alog("setup finished..");
    loadModel("test.model", function (a) {
        obj = a;
        alog("got obj");
        vboFromModel(gl, obj, program, alog);
        alog("made VBOs");
        for (var i = 0; i < 100; i += 1) {
            var k = [1, 2, 5, 10, 17, 26, 37, 50, 65, 82, 3, 4, 6, 11, 18, 27, 38, 51, 66, 83, 7, 8, 9, 12, 19, 28, 39, 52, 67, 84, 13, 14, 15, 16, 20, 29, 40, 53, 68, 85, 21, 22, 23, 24, 25, 30, 41, 54, 69, 86, 31, 32, 33, 34, 35, 36, 42, 55, 70, 87, 43, 44, 45, 46, 47, 48, 49, 56, 71, 88, 57, 58, 59, 60, 61, 62, 63, 64, 72, 89, 73, 74, 75, 76, 77, 78, 79, 80, 81, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100][i] - 1;
            var n = 10;
            var spacing = 40;
            var ofs = 0;
            var ia = makeAnimInfo(obj);
            instances[k] = { "obj" : obj, "pos" : [Math.floor(i / n) * spacing - ofs, -60, (i % n) * spacing - ofs], "anim" : i % obj.anims.length, "speed" : 1.1 + Math.sin(i) * 0.2, "instance-anim" : ia };
        };
    }, alog);
    alog("<br>....");
    setInterval(draw, 1);
};
function handleLoad() {
    setupCanvas();
};
window.addEventListener("load", handleLoad, false);
