topical media & game development

talk show tell print

mobile-query-three-vendor-three.js-loaders-ctm-ctm.js / js



  
  var CTM = CTM || {};
  
  CTM.CompressionMethod = {
    RAW: 0x00574152,
    MG1: 0x0031474d,
    MG2: 0x0032474d
  };
  
  CTM.Flags = {
    NORMALS: 0x00000001
  };
  
  CTM.File = function(stream){
    this.load(stream);
  };
  
  CTM.File.prototype.load = function(stream){
    this.header = new CTM.FileHeader(stream);
  
    this.body = new CTM.FileBody(this.header);
    
    this.getReader().read(stream, this.body);
  };
  
  CTM.File.prototype.getReader = function(){
    var reader;
  
    switch(this.header.compressionMethod){
      case CTM.CompressionMethod.RAW:
        reader = new CTM.ReaderRAW();
        break;
      case CTM.CompressionMethod.MG1:
        reader = new CTM.ReaderMG1();
        break;
      case CTM.CompressionMethod.MG2:
        reader = new CTM.ReaderMG2();
        break;
    }
  
    return reader;
  };
  
  CTM.FileHeader = function(stream){
    stream.readInt32(); //magic "OCTM"
    this.fileFormat = stream.readInt32();
    this.compressionMethod = stream.readInt32();
    this.vertexCount = stream.readInt32();
    this.triangleCount = stream.readInt32();
    this.uvMapCount = stream.readInt32();
    this.attrMapCount = stream.readInt32();
    this.flags = stream.readInt32();
    this.comment = stream.readString();
  };
  
  CTM.FileHeader.prototype.hasNormals = function(){
    return this.flags & CTM.Flags.NORMALS;
  };
  
  CTM.FileBody = function(header){
    var i = header.triangleCount * 3,
        v = header.vertexCount * 3,
        n = header.hasNormals()? header.vertexCount * 3: 0,
        u = header.vertexCount * 2,
        a = header.vertexCount * 4,
        j = 0;
  
    var data = new ArrayBuffer(
      (i + v + n + (u * header.uvMapCount) + (a * header.attrMapCount) ) * 4);
  
    this.indices = new Uint32Array(data, 0, i);
  
    this.vertices = new Float32Array(data, i * 4, v);
  
    if ( header.hasNormals() ){
      this.normals = new Float32Array(data, (i + v) * 4, n);
    }
    
    if (header.uvMapCount){
      this.uvMaps = [];
      for (j = 0; j < header.uvMapCount; ++ j){
        this.uvMaps[j] = {uv: new Float32Array(data,
          (i + v + n + (j * u) ) * 4, u) };
      }
    }
    
    if (header.attrMapCount){
      this.attrMaps = [];
      for (j = 0; j < header.attrMapCount; ++ j){
        this.attrMaps[j] = {attr: new Float32Array(data,
          (i + v + n + (u * header.uvMapCount) + (j * a) ) * 4, a) };
      }
    }
  };
  
  CTM.FileMG2Header = function(stream){
    stream.readInt32(); //magic "MG2H"
    this.vertexPrecision = stream.readFloat32();
    this.normalPrecision = stream.readFloat32();
    this.lowerBoundx = stream.readFloat32();
    this.lowerBoundy = stream.readFloat32();
    this.lowerBoundz = stream.readFloat32();
    this.higherBoundx = stream.readFloat32();
    this.higherBoundy = stream.readFloat32();
    this.higherBoundz = stream.readFloat32();
    this.divx = stream.readInt32();
    this.divy = stream.readInt32();
    this.divz = stream.readInt32();
    
    this.sizex = (this.higherBoundx - this.lowerBoundx) / this.divx;
    this.sizey = (this.higherBoundy - this.lowerBoundy) / this.divy;
    this.sizez = (this.higherBoundz - this.lowerBoundz) / this.divz;
  };
  
  CTM.ReaderRAW = function(){
  };
  
  CTM.ReaderRAW.prototype.read = function(stream, body){
    this.readIndices(stream, body.indices);
    this.readVertices(stream, body.vertices);
    
    if (body.normals){
      this.readNormals(stream, body.normals);
    }
    if (body.uvMaps){
      this.readUVMaps(stream, body.uvMaps);
    }
    if (body.attrMaps){
      this.readAttrMaps(stream, body.attrMaps);
    }
  };
  
  CTM.ReaderRAW.prototype.readIndices = function(stream, indices){
    stream.readInt32(); //magic "INDX"
    stream.readArrayInt32(indices);
  };
  
  CTM.ReaderRAW.prototype.readVertices = function(stream, vertices){
    stream.readInt32(); //magic "VERT"
    stream.readArrayFloat32(vertices);
  };
  
  CTM.ReaderRAW.prototype.readNormals = function(stream, normals){
    stream.readInt32(); //magic "NORM"
    stream.readArrayFloat32(normals);
  };
  
  CTM.ReaderRAW.prototype.readUVMaps = function(stream, uvMaps){
    var i = 0;
    for (; i < uvMaps.length; ++ i){
      stream.readInt32(); //magic "TEXC"
  
      uvMaps[i].name = stream.readString();
      uvMaps[i].filename = stream.readString();
      stream.readArrayFloat32(uvMaps[i].uv);
    }
  };
  
  CTM.ReaderRAW.prototype.readAttrMaps = function(stream, attrMaps){
    var i = 0;
    for (; i < attrMaps.length; ++ i){
      stream.readInt32(); //magic "ATTR"
  
      attrMaps[i].name = stream.readString();
      stream.readArrayFloat32(attrMaps[i].attr);
    }
  };
  
  CTM.ReaderMG1 = function(){
  };
  
  CTM.ReaderMG1.prototype.read = function(stream, body){
    this.readIndices(stream, body.indices);
    this.readVertices(stream, body.vertices);
    
    if (body.normals){
      this.readNormals(stream, body.normals);
    }
    if (body.uvMaps){
      this.readUVMaps(stream, body.uvMaps);
    }
    if (body.attrMaps){
      this.readAttrMaps(stream, body.attrMaps);
    }
  };
  
  CTM.ReaderMG1.prototype.readIndices = function(stream, indices){
    stream.readInt32(); //magic "INDX"
    stream.readInt32(); //packed size
    
    var interleaved = new CTM.InterleavedStream(indices, 3);
    LZMA.decompress(stream, stream, interleaved, interleaved.data.length);
  
    CTM.restoreIndices(indices, indices.length);
  };
  
  CTM.ReaderMG1.prototype.readVertices = function(stream, vertices){
    stream.readInt32(); //magic "VERT"
    stream.readInt32(); //packed size
    
    var interleaved = new CTM.InterleavedStream(vertices, 1);
    LZMA.decompress(stream, stream, interleaved, interleaved.data.length);
  };
  
  CTM.ReaderMG1.prototype.readNormals = function(stream, normals){
    stream.readInt32(); //magic "NORM"
    stream.readInt32(); //packed size
  
    var interleaved = new CTM.InterleavedStream(normals, 3);
    LZMA.decompress(stream, stream, interleaved, interleaved.data.length);
  };
  
  CTM.ReaderMG1.prototype.readUVMaps = function(stream, uvMaps){
    var i = 0;
    for (; i < uvMaps.length; ++ i){
      stream.readInt32(); //magic "TEXC"
  
      uvMaps[i].name = stream.readString();
      uvMaps[i].filename = stream.readString();
      
      stream.readInt32(); //packed size
  
      var interleaved = new CTM.InterleavedStream(uvMaps[i].uv, 2);
      LZMA.decompress(stream, stream, interleaved, interleaved.data.length);
    }
  };
  
  CTM.ReaderMG1.prototype.readAttrMaps = function(stream, attrMaps){
    var i = 0;
    for (; i < attrMaps.length; ++ i){
      stream.readInt32(); //magic "ATTR"
  
      attrMaps[i].name = stream.readString();
      
      stream.readInt32(); //packed size
  
      var interleaved = new CTM.InterleavedStream(attrMaps[i].attr, 4);
      LZMA.decompress(stream, stream, interleaved, interleaved.data.length);
    }
  };
  
  CTM.ReaderMG2 = function(){
  };
  
  CTM.ReaderMG2.prototype.read = function(stream, body){
    this.MG2Header = new CTM.FileMG2Header(stream);
    
    this.readVertices(stream, body.vertices);
    this.readIndices(stream, body.indices);
    
    if (body.normals){
      this.readNormals(stream, body);
    }
    if (body.uvMaps){
      this.readUVMaps(stream, body.uvMaps);
    }
    if (body.attrMaps){
      this.readAttrMaps(stream, body.attrMaps);
    }
  };
  
  CTM.ReaderMG2.prototype.readVertices = function(stream, vertices){
    stream.readInt32(); //magic "VERT"
    stream.readInt32(); //packed size
  
    var interleaved = new CTM.InterleavedStream(vertices, 3);
    LZMA.decompress(stream, stream, interleaved, interleaved.data.length);
    
    var gridIndices = this.readGridIndices(stream, vertices);
    
    CTM.restoreVertices(vertices, this.MG2Header, gridIndices, this.MG2Header.vertexPrecision);
  };
  
  CTM.ReaderMG2.prototype.readGridIndices = function(stream, vertices){
    stream.readInt32(); //magic "GIDX"
    stream.readInt32(); //packed size
    
    var gridIndices = new Uint32Array(vertices.length / 3);
    
    var interleaved = new CTM.InterleavedStream(gridIndices, 1);
    LZMA.decompress(stream, stream, interleaved, interleaved.data.length);
    
    CTM.restoreGridIndices(gridIndices, gridIndices.length);
    
    return gridIndices;
  };
  
  CTM.ReaderMG2.prototype.readIndices = function(stream, indices){
    stream.readInt32(); //magic "INDX"
    stream.readInt32(); //packed size
  
    var interleaved = new CTM.InterleavedStream(indices, 3);
    LZMA.decompress(stream, stream, interleaved, interleaved.data.length);
  
    CTM.restoreIndices(indices, indices.length);
  };
  
  CTM.ReaderMG2.prototype.readNormals = function(stream, body){
    stream.readInt32(); //magic "NORM"
    stream.readInt32(); //packed size
  
    var interleaved = new CTM.InterleavedStream(body.normals, 3);
    LZMA.decompress(stream, stream, interleaved, interleaved.data.length);
  
    var smooth = CTM.calcSmoothNormals(body.indices, body.vertices);
  
    CTM.restoreNormals(body.normals, smooth, this.MG2Header.normalPrecision);
  };
  
  CTM.ReaderMG2.prototype.readUVMaps = function(stream, uvMaps){
    var i = 0;
    for (; i < uvMaps.length; ++ i){
      stream.readInt32(); //magic "TEXC"
  
      uvMaps[i].name = stream.readString();
      uvMaps[i].filename = stream.readString();
      
      var precision = stream.readFloat32();
      
      stream.readInt32(); //packed size
  
      var interleaved = new CTM.InterleavedStream(uvMaps[i].uv, 2);
      LZMA.decompress(stream, stream, interleaved, interleaved.data.length);
      
      CTM.restoreMap(uvMaps[i].uv, 2, precision);
    }
  };
  
  CTM.ReaderMG2.prototype.readAttrMaps = function(stream, attrMaps){
    var i = 0;
    for (; i < attrMaps.length; ++ i){
      stream.readInt32(); //magic "ATTR"
  
      attrMaps[i].name = stream.readString();
      
      var precision = stream.readFloat32();
      
      stream.readInt32(); //packed size
  
      var interleaved = new CTM.InterleavedStream(attrMaps[i].attr, 4);
      LZMA.decompress(stream, stream, interleaved, interleaved.data.length);
      
      CTM.restoreMap(attrMaps[i].attr, 4, precision);
    }
  };
  
  CTM.restoreIndices = function(indices, len){
    var i = 3;
    if (len > 0){
      indices[2] += indices[0];
    }
    for (; i < len; i += 3){
      indices[i] += indices[i - 3];
      
      if (indices[i] === indices[i - 3]){
        indices[i + 1] += indices[i - 2];
      }else{
        indices[i + 1] += indices[i];
      }
  
      indices[i + 2] += indices[i];
    }
  };
  
  CTM.restoreGridIndices = function(gridIndices, len){
    var i = 1;
    for (; i < len; ++ i){
      gridIndices[i] += gridIndices[i - 1];
    }
  };
  
  CTM.restoreVertices = function(vertices, grid, gridIndices, precision){
    var gridIdx, delta, x, y, z,
        intVertices = new Uint32Array(vertices.buffer, vertices.byteOffset, vertices.length),
        ydiv = grid.divx, zdiv = ydiv * grid.divy,
        prevGridIdx = 0x7fffffff, prevDelta = 0,
        i = 0, j = 0, len = gridIndices.length;
  
    for (; i < len; j += 3){
      x = gridIdx = gridIndices[i ++];
      
      z = ~~(x / zdiv);
      x -= ~~(z * zdiv);
      y = ~~(x / ydiv);
      x -= ~~(y * ydiv);
  
      delta = intVertices[j];
      if (gridIdx === prevGridIdx){
        delta += prevDelta;
      }
  
      vertices[j]     = grid.lowerBoundx +
        x * grid.sizex + precision * delta;
      vertices[j + 1] = grid.lowerBoundy +
        y * grid.sizey + precision * intVertices[j + 1];
      vertices[j + 2] = grid.lowerBoundz +
        z * grid.sizez + precision * intVertices[j + 2];
  
      prevGridIdx = gridIdx;
      prevDelta = delta;
    }
  };
  
  CTM.restoreNormals = function(normals, smooth, precision){
    var ro, phi, theta, sinPhi,
        nx, ny, nz, by, bz, len,
        intNormals = new Uint32Array(normals.buffer, normals.byteOffset, normals.length),
        i = 0, k = normals.length,
        PI_DIV_2 = 3.141592653589793238462643 * 0.5;
  
    for (; i < k; i += 3){
      ro = intNormals[i] * precision;
      phi = intNormals[i + 1];
  
      if (phi === 0){
        normals[i]     = smooth[i]     * ro;
        normals[i + 1] = smooth[i + 1] * ro;
        normals[i + 2] = smooth[i + 2] * ro;
      }else{
        
        if (phi <= 4){
          theta = (intNormals[i + 2] - 2) * PI_DIV_2;
        }else{
          theta = ( (intNormals[i + 2] * 4 / phi) - 2) * PI_DIV_2;
        }
        
        phi *= precision * PI_DIV_2;
        sinPhi = ro * Math.sin(phi);
  
        nx = sinPhi * Math.cos(theta);
        ny = sinPhi * Math.sin(theta);
        nz = ro * Math.cos(phi);
  
        bz = smooth[i + 1];
        by = smooth[i] - smooth[i + 2];
  
        len = Math.sqrt(2 * bz * bz + by * by);
        if (len > 1e-20){
          by /= len;
          bz /= len;
        }
  
        normals[i]     = smooth[i]     * nz +
          (smooth[i + 1] * bz - smooth[i + 2] * by) * ny - bz * nx;
        normals[i + 1] = smooth[i + 1] * nz -
          (smooth[i + 2]      + smooth[i]   ) * bz  * ny + by * nx;
        normals[i + 2] = smooth[i + 2] * nz +
          (smooth[i]     * by + smooth[i + 1] * bz) * ny + bz * nx;
      }
    }
  };
  
  CTM.restoreMap = function(map, count, precision){
    var delta, value,
        intMap = new Uint32Array(map.buffer, map.byteOffset, map.length),
        i = 0, j, len = map.length;
  
    for (; i < count; ++ i){
      delta = 0;
  
      for (j = i; j < len; j += count){
        value = intMap[j];
        
        delta += value & 1? -( (value + 1) >> 1): value >> 1;
        
        map[j] = delta * precision;
      }
    }
  };
  
  CTM.calcSmoothNormals = function(indices, vertices){
    var smooth = new Float32Array(vertices.length),
        indx, indy, indz, nx, ny, nz,
        v1x, v1y, v1z, v2x, v2y, v2z, len,
        i, k;
  
    for (i = 0, k = indices.length; i < k;){
      indx = indices[i ++] * 3;
      indy = indices[i ++] * 3;
      indz = indices[i ++] * 3;
  
      v1x = vertices[indy]     - vertices[indx];
      v2x = vertices[indz]     - vertices[indx];
      v1y = vertices[indy + 1] - vertices[indx + 1];
      v2y = vertices[indz + 1] - vertices[indx + 1];
      v1z = vertices[indy + 2] - vertices[indx + 2];
      v2z = vertices[indz + 2] - vertices[indx + 2];
      
      nx = v1y * v2z - v1z * v2y;
      ny = v1z * v2x - v1x * v2z;
      nz = v1x * v2y - v1y * v2x;
      
      len = Math.sqrt(nx * nx + ny * ny + nz * nz);
      if (len > 1e-10){
        nx /= len;
        ny /= len;
        nz /= len;
      }
      
      smooth[indx]     += nx;
      smooth[indx + 1] += ny;
      smooth[indx + 2] += nz;
      smooth[indy]     += nx;
      smooth[indy + 1] += ny;
      smooth[indy + 2] += nz;
      smooth[indz]     += nx;
      smooth[indz + 1] += ny;
      smooth[indz + 2] += nz;
    }
  
    for (i = 0, k = smooth.length; i < k; i += 3){
      len = Math.sqrt(smooth[i] * smooth[i] + 
        smooth[i + 1] * smooth[i + 1] +
        smooth[i + 2] * smooth[i + 2]);
  
      if(len > 1e-10){
        smooth[i]     /= len;
        smooth[i + 1] /= len;
        smooth[i + 2] /= len;
      }
    }
  
    return smooth;
  };
  
  CTM.isLittleEndian = (function(){
    var buffer = new ArrayBuffer(2),
        bytes = new Uint8Array(buffer),
        ints = new Uint16Array(buffer);
  
    bytes[0] = 1;
  
    return ints[0] === 1;
  }());
  
  CTM.InterleavedStream = function(data, count){
    this.data = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
    this.offset = CTM.isLittleEndian? 3: 0;
    this.count = count * 4;
    this.len = this.data.length;
  };
  
  CTM.InterleavedStream.prototype.writeByte = function(value){
    this.data[this.offset] = value;
    
    this.offset += this.count;
    if (this.offset >= this.len){
    
      this.offset -= this.len - 4;
      if (this.offset >= this.count){
      
        this.offset -= this.count + (CTM.isLittleEndian? 1: -1);
      }
    }
  };
  
  CTM.Stream = function(data){
    this.data = data;
    this.offset = 0;
  };
  
  CTM.Stream.prototype.TWO_POW_MINUS23 = Math.pow(2, -23);
  
  CTM.Stream.prototype.TWO_POW_MINUS126 = Math.pow(2, -126);
  
  CTM.Stream.prototype.readByte = function(){
    return this.data.charCodeAt(this.offset ++) & 0xff;
  };
  
  CTM.Stream.prototype.readInt32 = function(){
    var i = this.readByte();
    i |= this.readByte() << 8;
    i |= this.readByte() << 16;
    return i | (this.readByte() << 24);
  };
  
  CTM.Stream.prototype.readFloat32 = function(){
    var m = this.readByte();
    m += this.readByte() << 8;
  
    var b1 = this.readByte();
    var b2 = this.readByte();
  
    m += (b1 & 0x7f) << 16; 
    var e = ( (b2 & 0x7f) << 1) | ( (b1 & 0x80) >>> 7);
    var s = b2 & 0x80? -1: 1;
  
    if (e === 255){
      return m !== 0? NaN: s * Infinity;
    }
    if (e > 0){
      return s * (1 + (m * this.TWO_POW_MINUS23) ) * Math.pow(2, e - 127);
    }
    if (m !== 0){
      return s * m * this.TWO_POW_MINUS126;
    }
    return s * 0;
  };
  
  CTM.Stream.prototype.readString = function(){
    var len = this.readInt32();
  
    this.offset += len;
  
    return this.data.substr(this.offset - len, len);
  };
  
  CTM.Stream.prototype.readArrayInt32 = function(array){
    var i = 0, len = array.length;
    
    while(i < len){
      array[i ++] = this.readInt32();
    }
  
    return array;
  };
  
  CTM.Stream.prototype.readArrayFloat32 = function(array){
    var i = 0, len = array.length;
  
    while(i < len){
      array[i ++] = this.readFloat32();
    }
  
    return array;
  };
  


(C) Æliens 04/09/2009

You may not copy or print any of this material without explicit permission of the author or the publisher. In case of other copyright issues, contact the author.