Search code examples
javascripttextureswebglmd2

WebGL - importing MD2 Model, problems with textures


I'm making a webgl demo with md2 models. I know that it is an old format but I need to use only md2.

I read the documentation for the md2 format.

I watched how this site works: and used part of it's sources in my program (i saved the author's name in the comments)

I made vertex loading, the model loads great!

But when I'm trying to map the texutre, something strange happens:

Robot

First, I thought that it's the problem of fragment shader, but I made an export by using assimp2json, used the data from it and it was drawn as it should draw.

The problem is that assimp2json changed the order vertices in triangles and uvs order, so I can't debug the program using it.

Maybe, someone could help to find the bug, to point to the error in my code?

P. S. since there is no animation, I only use the first frame

The most interesting is that if I will pass unindexed data (just the uvs from the file) it looks more right than the indexed textures:

enter image description here

The problem is that it is wrong in some places, like here:

enter image description here

Full source code and models:

ShaderProgram.js

class ShaderProgram
{
   constructor(gl, VSSource, FSSource) // VS - Vertex Shader, FS - Fragment 
Shader
       {
    this.gl = gl;
    let vertexShader = this.getShader(VSSource, gl.VERTEX_SHADER);
    let fragmentShader = this.getShader(FSSource, gl.FRAGMENT_SHADER);

    this.shaderProgram = gl.createProgram();

    gl.attachShader(this.shaderProgram, vertexShader);
    gl.attachShader(this.shaderProgram, fragmentShader);

    gl.linkProgram(this.shaderProgram);

    if (!gl.getProgramParameter(this.shaderProgram, gl.LINK_STATUS)) {
        alert("Can't load shaders");
    }

    this.enableAttributes();
    this.getUniforms();

}

getShader(source, type)
{
    let gl = this.gl;

    let shader = gl.createShader(type);
    gl.shaderSource(shader, source);
    gl.compileShader(shader);

    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
        alert("Error compilation: " + gl.getShaderInfoLog(shader));
        gl.deleteShader(shader);
        return null;
    }

    return shader;
}

getUniforms()
{
    this.model = gl.getUniformLocation(this.shaderProgram, "model");
    this.view = gl.getUniformLocation(this.shaderProgram, "view");
    this.projection = gl.getUniformLocation(this.shaderProgram, "projection");

}

enableAttributes()
{
    let gl = this.gl;
    let shaderProgram  = this.shaderProgram;


}

use()
{
    this.gl.useProgram(this.shaderProgram);
}


}

Textures.js

function initTexture(filename) {
let texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([255, 0, 0, 255]));

let image = new Image();
image.onload = function() { handleTextureLoaded(image, texture); }
image.src = filename;

return texture;
}

function handleTextureLoaded(image, texture) {
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
gl.generateMipmap(gl.TEXTURE_2D);
gl.bindTexture(gl.TEXTURE_2D, null);
}

MD2 Import:

// BinaryReader
// Refactored by Vjeux <[email protected]>
// http://blog.vjeux.com/2010/javascript/javascript-binary-reader.html

// Original
//+ Jonas Raoni Soares Silva
//@ http://jsfromhell.com/classes/binary-parser [rev. #1]

BinaryReader = function (data) {
this._buffer = data;
this._pos = 0;
};

BinaryReader.prototype = {

/* Public */

readInt8:   function (){ return this._decodeInt(8, true); },
readUInt8:  function (){ return this._decodeInt(8, false); },
readInt16:  function (){ return this._decodeInt(16, true); },
readUInt16: function (){ return this._decodeInt(16, false); },
readInt32:  function (){ return this._decodeInt(32, true); },
readUInt32: function (){ return this._decodeInt(32, false); },

readFloat:  function (){ return this._decodeFloat(23, 8); },
readDouble: function (){ return this._decodeFloat(52, 11); },

readChar:   function () { return this.readString(1); },
readString: function (length) {
    this._checkSize(length * 8);
    var result = this._buffer.substr(this._pos, length);
    this._pos += length;
    return result;
},

seek: function (pos) {
    this._pos = pos;
    this._checkSize(0);
},

getPosition: function () {
    return this._pos;
},

getSize: function () {
    return this._buffer.length;
},


/* Private */

_decodeFloat: function(precisionBits, exponentBits)
{

    return this._decodeFloat2(precisionBits, exponentBits);


    var length = precisionBits + exponentBits + 1;
    var size = length >> 3;
    this._checkSize(length);

    var bias = Math.pow(2, exponentBits - 1) - 1;
    var signal = this._readBits(precisionBits + exponentBits, 1, size);
    var exponent = this._readBits(precisionBits, exponentBits, size);
    var significand = 0;
    var divisor = 2;
    var curByte = length + (-precisionBits >> 3) - 1;

    do
    {
        var byteValue = this._readByte(++curByte, size);
        var startBit = precisionBits % 8 || 8;
        var mask = 1 << startBit;

        while (mask >>= 1)
        {
            if (byteValue & mask)
            {
                significand += 1 / divisor;
            }
            divisor *= 2;
        }
    } while (precisionBits -= startBit);

    this._pos += size;

    return exponent == (bias << 1) + 1 ? significand ? NaN : signal ? -Infinity : +Infinity
        : (1 + signal * -2) * (exponent || significand ? !exponent ? Math.pow(2, -bias + 1) * significand
        : Math.pow(2, exponent - bias) * (1 + significand) : 0);
},

// I added this because _decodeFloat gave me some real inaccuarate results? -Terry Butler
_decodeFloat2: function(precisionBits, exponentBits)
{
    var length = precisionBits + exponentBits + 1;
    var value = this._decodeInt(length);

    var sign = (value >> 31) & 0x1;
    var allZero = 1;
    var mantissa = 0.0;
    var exponent = 0.0;

    // Mantissa
    for (var i = 22; i > -1; i--)
    {

        var test = 1.0 / Math.pow(2, 23-i);

        if ((value >> i & 0x1) == 1)
        {

            mantissa += test;

            allZero = 0;
        }

    }

    if (allZero == 0)
        mantissa += 1.0;

    // Exponent
    for (var i = 30; i > 22; i--)
    {

        var test = Math.pow(2, i - 23);

        if ((value >> i & 0x1) == 1)
        {
            exponent += test;
        }

    }

    exponent -= 127.0;

    //
    var total = Math.pow(2.0, exponent) * mantissa;

    //
    if (sign == 1)
    {
        total *= -1.0;
    }

    return total;

},

_decodeInt: function(bits, signed){
    var x = this._readBits(0, bits, bits / 8), max = Math.pow(2, bits);
    var result = signed && x >= max / 2 ? x - max : x;

    this._pos += bits / 8;
    return result;
},

//shl fix: Henri Torgemane ~1996 (compressed by Jonas Raoni)
_shl: function (a, b){
    for (++b; --b; a = ((a %= 0x7fffffff + 1) & 0x40000000) == 0x40000000 ? a * 2 : (a - 0x40000000) * 2 + 0x7fffffff + 1);
    return a;
},

_readByte: function (i, size) {
    return this._buffer.charCodeAt(this._pos + size - i - 1) & 0xff;
},

_readBits: function (start, length, size) {
    var offsetLeft = (start + length) % 8;
    var offsetRight = start % 8;
    var curByte = size - (start >> 3) - 1;
    var lastByte = size + (-(start + length) >> 3);
    var diff = curByte - lastByte;

    var sum = (this._readByte(curByte, size) >> offsetRight) & ((1 << (diff ? 8 - offsetRight : length)) - 1);

    if (diff && offsetLeft) {
        sum += (this._readByte(lastByte++, size) & ((1 << offsetLeft) - 1)) << (diff-- << 3) - offsetRight;
    }

    while (diff) {
        sum += this._shl(this._readByte(lastByte++, size), (diff-- << 3) - offsetRight);
    }

    return sum;
},

_checkSize: function (neededBits) {
    if (!(this._pos + Math.ceil(neededBits / 8) < this._buffer.length)) {
        throw new Error("Index out of bound");
    }
}
};


/**
 * @author oosmoxiecode
 * based on http://www.terrybutler.co.uk/web-development/html5-canvas-md2-    renderer/
 * and
 * http://tfc.duke.free.fr/coding/md2-specs-en.html
 *
 * dependant on binaryReader.js
 *
 * Returns a object like: {string: json_string, info: {status: "Success",     faces: 10, vertices: 10, frames: 5 }}
 *
 **/

// Library is modified for this program by me

function MD2_converter (file) {

    var scope = this;

    // Create the Binary Reader
    var reader = new BinaryReader(file);

    // Setup
    var header = {};
    var frames = [];
    var st = [];
    var triag = [];

    var string = "";
    var info = {};
    var returnObject = {string: string, info: info};

// Ident and version
header.ident = reader.readString(4);
header.version = reader.readInt32();

// Valid MD2 file?
if (header.ident != "IDP2" || header.version != 8) {
    info.status = "Not a valid MD2 file";
    return returnObject;
}

// header
header.skinwidth        = reader.readInt32(); // texture width
header.skinheight       = reader.readInt32(); // texture height

header.framesize        = reader.readInt32(); // size in bytes of a frame

header.num_skins        = reader.readInt32(); // number of skins
header.num_vertices     = reader.readInt32(); // number of vertices per frame
header.num_st           = reader.readInt32(); // number of texture coordinates
header.num_tris         = reader.readInt32(); // number of triangles
header.num_glcmds       = reader.readInt32(); // number of opengl commands
header.num_frames       = reader.readInt32(); // number of frames

header.offset_skins     = reader.readInt32(); // offset skin data
header.offset_st        = reader.readInt32(); // offset texture coordinate data
header.offset_tris      = reader.readInt32(); // offset triangle data
header.offset_frames    = reader.readInt32(); // offset frame data
header.offset_glcmds    = reader.readInt32(); // offset OpenGL command data
header.offset_end       = reader.readInt32(); // offset end of file

// faulty size
if (reader.getSize() != header.offset_end) {
    info.status = "Corrupted MD2 file";
    return returnObject;
}

// texture coordinates
let count = 0;
reader.seek(header.offset_st);
for (var i = 0; i < header.num_st; i++) {
    var s = reader.readInt16();
    var t = reader.readInt16();

    st[i] = {
        s: s / header.skinwidth,
        t: t / header.skinheight
    };
}

reader.seek(header.offset_tris);
for (var i = 0; i < header.num_tris; i++) {
    var a = reader.readInt16();
    var b = reader.readInt16();
    var c = reader.readInt16();

    var uva_i = reader.readUInt16();
    var uvb_i = reader.readUInt16();
    var uvc_i = reader.readUInt16();

    triag[i] = {};
    triag[i].vertex = [];
    triag[i].st = [];

    triag[i].vertex[0] = a;
    triag[i].vertex[1] = b;
    triag[i].vertex[2] = c;

    triag[i].st[0] = uva_i;
    triag[i].st[1] = uvb_i;
    triag[i].st[2] = uvc_i;
}

// frames
reader.seek(header.offset_frames);
for (var f = 0; f < header.num_frames; f++) {
    var frame = {};

    frame.name = "";
    frame.scale = {};
    frame.translate = {};

    frame.scale.x = reader.readFloat();
    frame.scale.y = reader.readFloat();
    frame.scale.z = reader.readFloat();

    frame.translate.x = reader.readFloat();
    frame.translate.y = reader.readFloat();
    frame.translate.z = reader.readFloat();

    frame.vertices = [];

    frame.name = reader.readString(16).replace(/[^a-z0-9]/gi,''); // 4+4+4 4+4+4 (12 + 12) = 24 + 16 = 40

    for (var v = 0; v < header.num_vertices; v++) {
        var tempX = reader.readUInt8();
        var tempY = reader.readUInt8();
        var tempZ = reader.readUInt8();
        var normal = reader.readUInt8();

        var xx = frame.scale.x * tempX + frame.translate.x;
        var yy = frame.scale.z * tempZ + frame.translate.z;
        var zz = frame.scale.y * tempY + frame.translate.y;

        let vertex = [];

        vertex[0] = xx;
        vertex[1] = yy;
        vertex[2] = zz;

        frame.vertices.push(vertex);
    }

    frames.push(frame);
}

let res = {};

res.st = st;
res.triag = triag;

res.frame = [];
for (var i=0; i<frames.length; ++i )
    res.frame[i]=frames[i];


res.faces_count = header.num_tris;
res.vertices_count = header.num_vertices;
res.frames_count = header.num_frames;

return res;

}

main.js

let gl;
let shaderProgram;

let firstMouse = true;
let mouseDown = false;

let lastTime = 0;
let deltaTime;

let test;

let lastX= 0;
let lastY = 0;


let DIRS = {
    Forward: 0,
    Backward: 1,
    Left: 2,
    Right: 3
};

let KeyCodes = 
{
    Up: 38,
    Down : 40,
    Left : 37,
    Right : 39,

    W : 87,
    S : 83,
    A : 65,
    D : 68,

    Q : 81,
    E : 69,

    PageDown : 34,
    PageUp : 33
};

let pressedKeys = [];


let vertexShaderSource = `
precision highp float;

attribute vec3 aPos;
attribute vec2 aTexCoord;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

varying vec2 vTexCoord;


void main(void)
{
     gl_Position = projection * view * model * vec4(aPos, 1.0);
     vTexCoord = aTexCoord;
}
`;

let fragmentShaderSource = `
precision highp float;

varying highp vec2 vTexCoord;
uniform sampler2D uSampler;
void main(void)
{
  gl_FragColor = texture2D(uSampler, vec2(vTexCoord.s,1.0-vTexCoord.t));
}
`;

let camera;
function initGL()
{
let canvas = document.getElementById("Canvas3D");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
try
{
    gl = canvas.getContext("webgl2") || canvas.getContext("experimental-webgl2");
}
catch(e)
{
    alert("Your browser don't support WebGL");
}

gl.viewportWidth = canvas.width;
gl.viewportHeight = canvas.height;
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);

gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LESS);

shaderProgram = new     ShaderProgram(gl,vertexShaderSource,fragmentShaderSource);
shaderProgram.use();

camera = {
    Init(shaderProgram)
    {
        this.shaderProgram = shaderProgram;
        this.pos = vec3.create();
        this.up = vec3.create();
        this.front = vec3.create();
        this.right = vec3.create();

        this.WorldUp = vec3.create();

        this.pos = [51.656294013839215, 36.9293564686086, -28.23351748054847];
        this.front = [-0.7667674915573891, -0.6401096994849556, 0.04824092159224934];
        this.up = [-0.6388465891741784, 0.7682835235935235, 0.040192821190338686];

        this.WorldUp = [0,1,0];

        this.yaw = 176.39999999999998;
        this.pitch = -39.8;

        this.speed = 25;
        this.sensivity = 0.1;
        this.zoom = 100;

        this.view = mat4.create();
        this.projection = mat4.create();
        mat4.perspective(this.projection, Math.PI/180.0*this.zoom,     gl.viewportWidth/gl.viewportHeight, 0.1, 200);
            this.shaderProgram.gl.uniformMatrix4fv(this.shaderProgram.projection, false,     this.projection);

        this.updateCameraVectors();
        this.updateView();

    },

    updateView: function()
    {
        let tmp = vec3.create();
        vec3.add(tmp, this.pos, this.front);
        mat4.lookAt(this.view, this.pos, tmp, this.up);
        this.shaderProgram.gl.uniformMatrix4fv(this.shaderProgram.view, false, this.view);
    },

    processKeyboard(deltaTime)
    {
        let velocity = this.speed * deltaTime;
        let tmp = vec3.create();

        if (pressedKeys[KeyCodes.W] || pressedKeys[KeyCodes.Up]) {
            vec3.scale(tmp, this.front, velocity);
            vec3.add(this.pos,this.pos,tmp);
        }
        if (pressedKeys[KeyCodes.S] || pressedKeys[KeyCodes.Down]) {
            velocity *=(-1);
            vec3.scale(tmp, this.front, velocity);
            vec3.add(this.pos,this.pos,tmp);
        }
        if (pressedKeys[KeyCodes.A] || pressedKeys[KeyCodes.Left]) {
            velocity *=(-1);
            vec3.scale(tmp, this.right, velocity);
            vec3.add(this.pos,this.pos,tmp);
        }
        if (pressedKeys[KeyCodes.D] || pressedKeys[KeyCodes.Right]) {
            vec3.scale(tmp, this.right, velocity);
            vec3.add(this.pos,this.pos,tmp);
        }
    },

    processMouseMovement(xoffset, yoffset)
    {
        this.yaw   += (xoffset*this.sensivity);
        this.pitch += (yoffset*this.sensivity);

        if (this.pitch > 89.0)
        this.pitch = 89.0;
        if (this.pitch < -89.0)
        this.pitch = -89.0;

        this.updateCameraVectors();
    },

    updateCameraVectors()
    {
        let ToRads = Math.PI/180;
        let yaw = this.yaw;
        let pitch = this.pitch;
        this.front[0] = Math.cos(ToRads*yaw) * Math.cos(ToRads*pitch);
        this.front[1] = Math.sin(ToRads*pitch);
        this.front[2] = Math.sin(ToRads*yaw) * Math.cos(ToRads*pitch);
        vec3.normalize(this.front, this.front);


        vec3.cross(this.right, this.front, this.WorldUp);
        vec3.normalize(this.right, this.right);

        vec3.cross(this.up, this.right, this.front);
        vec3.normalize(this.up, this.up);
    }
};

function load_binary_resource(url) {
    let req = new XMLHttpRequest();
    req.open('GET', url, false);
    req.overrideMimeType('text/plain; charset=x-user-defined'); // No unicode data
    req.send(null);
    if (req.status != 200) return '';
    return req.responseText;
}

let file = load_binary_resource('../models/rhino/Tris.md2');
let res = MD2_converter(file);

let texture = initTexture("../models/rhino/rhino.png");

let verts = [];
let inds = [];

let uvs = [];

for (let i =0;i<res.frame[0].vertices.length;i++)
{
    let vert = res.frame[0].vertices[i];
    verts.push(vert[0]);
    verts.push(vert[1]);
    verts.push(vert[2]);
}

for (let i =0;i<res.triag.length;i++)
{
    let triag = res.triag[i];
    inds.push(triag.vertex[0]);
    inds.push(triag.vertex[1]);
    inds.push(triag.vertex[2]);

    uvs.push(res.st[triag.st[0]].s);
    uvs.push(res.st[triag.st[0]].t);

    uvs.push(res.st[triag.st[1]].s);
    uvs.push(res.st[triag.st[1]].t);

    uvs.push(res.st[triag.st[2]].s);
    uvs.push(res.st[triag.st[2]].t);
}

test = new Drawable(shaderProgram,  verts, inds, uvs, texture);

camera.Init(shaderProgram);

requestAnimationFrame(gameCycle);
}

function recalculateFPS(gotTime)
{
    deltaTime = (gotTime - lastTime)/1000;
lastTime = gotTime;
}
function gameCycle(gotTime)
{
    recalculateFPS(gotTime);
    gl.clearColor(0,0,0,1);
    gl.clear(gl.COLOR_BUFFER_BIT);
    camera.processKeyboard(deltaTime);
    camera.updateView();
    test.draw();


    requestAnimationFrame(gameCycle);
}

window.onload=function()
{
    initGL();
};


document.onkeydown = function(e)
{
    pressedKeys[e.keyCode]=true;
};

document.onkeyup = function(e)
{
    pressedKeys[e.keyCode]=false;
};

document.body.onmousedown = function(event)
{
    mouseDown = true;
    lastX = event.clientX;
    lastY = event.clientY;
};

document.body.onmouseup = function(event)
{
    mouseDown = false;
};

document.body.onmouseout = function(event)
{
    mouseDown = false;
};

document.body.onmousemove = function (e)
{
if (!mouseDown)
    return;
let xpos = e.clientX;
let ypos = e.clientY;
if(firstMouse) {
    lastX = xpos;
    lastY = ypos;
    firstMouse = false;
}

    let xoffset = xpos - lastX;
    let yoffset = lastY - ypos;

lastX = xpos;
lastY = ypos;

camera.processMouseMovement(xoffset,yoffset);


}

main.html

    <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body scroll="no" style="overflow: hidden">
<canvas id="Canvas3D"></canvas>
<script src="../js/glmatrix/dist/gl-matrix.js"></script>

<script src="../js/MD2Import.js"></script>
<script src="../js/ShaderProgram.js"></script>
<script src="../js/Textures.js"></script>
<script src="../js/Drawable.js"></script>

<script src="../js/main.js"></script>
</body>
</html>

Drawable.js

class Drawable {
constructor(shaderProgram, vertices, indices, texCoords, texture) {
    this.gl = shaderProgram.gl;
    let gl = shaderProgram.gl;
    this.shaderProgram = shaderProgram;

    shaderProgram.aTexCoord = gl.getAttribLocation(shaderProgram.shaderProgram, "aTexCoord");
    shaderProgram.aPos = gl.getAttribLocation(shaderProgram.shaderProgram, "aPos");

    this.VAO = gl.createVertexArray();
    this.texture = texture;
    gl.bindVertexArray(this.VAO);

    this.vertexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
    gl.vertexAttribPointer(shaderProgram.aPos, 3, gl.FLOAT, false, 12, 0);
    gl.enableVertexAttribArray(shaderProgram.aPos);

    this.EBO = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.EBO);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);

    this.VBOTexCoords = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, this.VBOTexCoords);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(texCoords),gl.STATIC_DRAW);
    gl.vertexAttribPointer(shaderProgram.aTexCoord, 2, gl.FLOAT, false, 8, 0);
    gl.enableVertexAttribArray(shaderProgram.aTexCoord);








    gl.bindVertexArray(null);

    this.pos = vec3.create();
    this.rot = vec3.create(); // в Градусах
    this.scale = vec3.create();

    this.pos = [0,0,0];
    this.rot = [0,0,0];
    this.scale=[1,1,1];

    this.vertCount = indices.length;

    this.model = mat4.create();
}

updateModel() {
    mat4.identity(this.model);
    mat4.translate(this.model, this.model, this.pos);

    mat4.rotateX(this.model, this.model, Math.PI / 180 * this.rot[0]);
    mat4.rotateY(this.model, this.model, Math.PI / 180 * this.rot[1]);
    mat4.rotateZ(this.model, this.model, Math.PI / 180 * this.rot[2]);

    mat4.scale(this.model, this.model, this.scale);

}

translate(transX, transY, transZ)
{
    this.pos[0] += transX;
    this.pos[1] += transY;
    this.pos[2] += transZ;

}

rotate(rotX, rotY, rotZ)
{
    this.rot[0] += rotX;
    this.rot[1] += rotY;
    this.rot[2] += rotZ;
}

draw() {
    this.updateModel();
    let gl =this.gl;

    gl.uniformMatrix4fv(this.shaderProgram.model, false, this.model);

    gl.bindVertexArray(this.VAO);

    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, this.texture);
    gl.uniform1i(gl.getUniformLocation(shaderProgram.shaderProgram, "uSampler"), 0);
    //gl.drawArrays(gl.TRIANGLES, 0, this.vertCount);
    gl.drawElements(gl.TRIANGLES, this.vertCount,gl.UNSIGNED_SHORT,0);
}
}

Tried using gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true), result: Picture with flip

By the way, it shouldn't work, because I already flipped the textures in the shader:

gl_FragColor = texture2D(uSampler, vec2(vTexCoord.s,1.0 - vTexCoord.t));

rhino.png (textures)textures

The md2 file: http://dropmefiles.com/zhQFU

The problem solved when I deleted the index buffer and placed the vertices and uvs in one huge buffer. What it could be?


Solution

  • The solution was to delete index buffer and place everything in the vertex buffer. It worked for me