Skip to content
Browse files

Lots and lots of optimizations, better material caching/handling

  • Loading branch information...
1 parent 550c6b7 commit 4c485bdb75eb4178bd407adf79ee4651033ba4e0 @toji toji committed Sep 17, 2011
Showing with 521 additions and 104 deletions.
  1. +2 −3 index.html
  2. +61 −6 js/js-struct.js
  3. +15 −1 js/source-bsp-struct.js
  4. +243 −22 js/source-bsp.js
  5. +74 −41 js/source-mdl.js
  6. +114 −30 js/source-vmt.js
  7. +1 −1 js/webgl-demo-camera.js
  8. +11 −0 js/webgl-util.js
View
5 index.html
@@ -38,7 +38,6 @@
gl.enable(gl.CULL_FACE);
gl.cullFace(gl.FRONT);
- gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
@@ -106,8 +105,8 @@
var canvas = document.getElementById('viewport');
// Fit the canvas to the document
- canvas.width = 854; //document.width;
- canvas.height = 480; //document.height;
+ canvas.width = document.width; //854;
+ canvas.height = document.height; //480;
// Get the GL Context
var gl = glUtil.getContext(canvas);
View
67 js/js-struct.js
@@ -30,9 +30,6 @@
// TODO: Ugh, this is messy. Do it differentely soon, please!
var nextStructId = 0;
-/**
-*
-*/
var Struct = Object.create(Object, {
/**
* Defines a single byte integer value (byte/char).
@@ -259,7 +256,17 @@ var Struct = Object.create(Object, {
return struct;
}
},
-
+
+ //
+ // Utility functions to read simple arrays of data from a buffer
+ //
+
+ /**
+ * Read an ASCII string from an ArrayBuffer
+ * @param buffer Buffer to read from
+ * @param offset Offset in bytes to start reading at
+ * @param length Optional, number of characters to read. If not given will read until a NULL char is reached
+ */
readString: {
value: function(buffer, offset, length) {
var str = "", charBuffer;
@@ -276,7 +283,6 @@ var Struct = Object.create(Object, {
str += String.fromCharCode(char);
}
} else {
- // If no length is specified, read till we hit a NULL char
charBuffer = new Uint8Array(buffer, offset);
var i = 0;
@@ -290,7 +296,14 @@ var Struct = Object.create(Object, {
}
},
- // I wonder if there's a more efficent way to do this that doesn't run afoul the offset restrictions of TypedArrays
+ // I wonder if there's a more efficent way to do these that doesn't run afoul the offset restrictions of TypedArrays
+
+ /**
+ * Read an array of 8 bit integers
+ * @param buffer Buffer to read from
+ * @param offset Offset in bytes to start reading at
+ * @param elements Number of integers to read
+ */
readInt8Array: {
value: function(buffer, offset, elements) {
var array = new Int8Array(elements);
@@ -302,6 +315,12 @@ var Struct = Object.create(Object, {
}
},
+ /**
+ * Read an array of 8 bit unsigned integers
+ * @param buffer Buffer to read from
+ * @param offset Offset in bytes to start reading at
+ * @param elements Number of integers to read
+ */
readUint8Array: {
value: function(buffer, offset, elements) {
var array = new Uint8Array(elements);
@@ -313,6 +332,12 @@ var Struct = Object.create(Object, {
}
},
+ /**
+ * Read an array of 16 bit integers
+ * @param buffer Buffer to read from
+ * @param offset Offset in bytes to start reading at
+ * @param elements Number of integers to read
+ */
readInt16Array: {
value: function(buffer, offset, elements) {
var array = new Int16Array(elements);
@@ -324,6 +349,12 @@ var Struct = Object.create(Object, {
}
},
+ /**
+ * Read an array of 16 bit unsigned integers
+ * @param buffer Buffer to read from
+ * @param offset Offset in bytes to start reading at
+ * @param elements Number of integers to read
+ */
readUint16Array: {
value: function(buffer, offset, elements) {
var array = new Uint16Array(elements);
@@ -335,6 +366,12 @@ var Struct = Object.create(Object, {
}
},
+ /**
+ * Read an array of 32 bit integers
+ * @param buffer Buffer to read from
+ * @param offset Offset in bytes to start reading at
+ * @param elements Number of integers to read
+ */
readInt32Array: {
value: function(buffer, offset, elements) {
var array = new Int32Array(elements);
@@ -346,6 +383,12 @@ var Struct = Object.create(Object, {
}
},
+ /**
+ * Read an array of 32 bit unsigned integers
+ * @param buffer Buffer to read from
+ * @param offset Offset in bytes to start reading at
+ * @param elements Number of integers to read
+ */
readUint32Array: {
value: function(buffer, offset, elements) {
var array = new Uint32Array(elements);
@@ -357,6 +400,12 @@ var Struct = Object.create(Object, {
}
},
+ /**
+ * Read an array of 32 bit floats
+ * @param buffer Buffer to read from
+ * @param offset Offset in bytes to start reading at
+ * @param elements Number of floats to read
+ */
readFloat32Array: {
value: function(buffer, offset, elements) {
var array = new Float32Array(elements);
@@ -368,6 +417,12 @@ var Struct = Object.create(Object, {
}
},
+ /**
+ * Read an array of 64 bit floats
+ * @param buffer Buffer to read from
+ * @param offset Offset in bytes to start reading at
+ * @param elements Number of floats to read
+ */
readFloat64Array: {
value: function(buffer, offset, elements) {
var array = new Float64Array(elements);
View
16 js/source-bsp-struct.js
@@ -325,7 +325,21 @@ var StaticPropDictLumpHeader_t = Struct.create(
);
var StaticPropDictLump_t = Struct.create(
- Struct.string("m_Name", STATIC_PROP_NAME_LENGTH)
+ Struct.string("m_Name", STATIC_PROP_NAME_LENGTH),
+ {
+ props: {
+ value: null
+ },
+
+ addProp: {
+ value: function(prop) {
+ if(!this.props) {
+ this.props = [];
+ }
+ this.props.push(prop);
+ }
+ }
+ }
);
var StaticPropLeafLumpHeader_t = Struct.create(
View
265 js/source-bsp.js
@@ -63,6 +63,8 @@ mapFS += " gl_FragColor = vec4(color.rgb * (light.rgb + ambient), color.a);\n";
//mapFS += " gl_FragColor = color;\n";
mapFS += "}";
+var sourceBspShader = null;
+
var SourceBspTree = Object.create(Object, {
planes: {
value: null
@@ -247,6 +249,14 @@ var SourceBsp = Object.create(Object, {
value: null
},
+ propVertBuffer: {
+ value: null
+ },
+
+ propIndexBuffer: {
+ value: null
+ },
+
staticPropDict: {
value: null
},
@@ -282,6 +292,7 @@ var SourceBsp = Object.create(Object, {
load: {
value: function(gl, url, callback) {
this._initializeShaders(gl);
+ SourceModel.initializeShaders(gl);
var self = this;
this.complete = false;
@@ -459,19 +470,31 @@ var SourceBsp = Object.create(Object, {
_loadStaticProps: {
value: function(gl, bspData) {
+ var self = this;
this.staticPropDict = bspData.staticPropDict;
this.staticProps = bspData.staticProps;
+ var staticPropCount = bspData.staticPropDict.length;
+ var staticPropsLoaded = 0;
+
for(var propId in bspData.staticPropDict) {
var propDict = bspData.staticPropDict[propId];
- propDict.model = Object.create(SourceModel).load(gl, "root/tf/" + propDict.m_Name);
+ propDict.model = Object.create(SourceModel).load(null, "root/tf/" + propDict.m_Name, function(model) {
+ staticPropsLoaded++;
+ if(staticPropsLoaded == staticPropCount) {
+ self._staticPropsLoaded(gl, bspData.staticPropDict);
+ }
+ });
}
for(var propId = 0; propId < bspData.staticProps.length; ++propId) {
var prop = bspData.staticProps[propId];
var origin = prop.m_Origin;
var angle = prop.m_Angles;
+ var propDict = bspData.staticPropDict[prop.m_PropType];
+ propDict.addProp(prop);
+
var modelMat = mat4.create();
mat4.identity(modelMat);
mat4.translate(modelMat, [origin.x, origin.y, origin.z]);
@@ -490,6 +513,67 @@ var SourceBsp = Object.create(Object, {
}
},
+ /**
+ * Upload all of the static props into a single, shared buffer for faster rendering
+ **/
+ _staticPropsLoaded: {
+ value: function(gl, props) {
+ console.log("All props loaded");
+
+ var self = this;
+ materialManager.onMaterialsCompleted = function() {
+ console.log("All materials loaded");
+ self._staticPropMaterialsLoaded(gl, props);
+ };
+
+ for(var propId in this.staticPropDict) {
+ var propDict = this.staticPropDict[propId];
+ propDict.model.loadSkin(gl, 0);
+ }
+ }
+ },
+
+ // Props are loaded, materials are loaded, now we sort and build the buffers
+ _staticPropMaterialsLoaded: {
+ value: function(gl, props) {
+ var vertexArraySize = 0;
+ var indexArraySize = 0;
+
+ for(var propId in this.staticPropDict) {
+ var model = this.staticPropDict[propId].model;
+ vertexArraySize += model.vertArray.length;
+ indexArraySize += model.indexArray.length;
+ }
+
+ var vertexArray = new Uint8Array(vertexArraySize);
+ var indexArray = new Uint16Array(indexArraySize);
+
+ var vertexArrayOffset = 0;
+ var indexArrayOffset = 0;
+
+ for(var propId in this.staticPropDict) {
+ var prop = this.staticPropDict[propId];
+ var model = prop.model;
+ prop.vertexOffset = vertexArrayOffset;
+ prop.indexOffset = indexArrayOffset * 2;
+
+ vertexArray.set(model.vertArray, vertexArrayOffset);
+ indexArray.set(model.indexArray, indexArrayOffset);
+
+ vertexArrayOffset += model.vertArray.length;
+ indexArrayOffset += model.indexArray.length;
+ }
+
+ this.propVertBuffer = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.propVertBuffer);
+ gl.bufferData(gl.ARRAY_BUFFER, vertexArray, gl.STATIC_DRAW);
+
+ this.propIndexBuffer = gl.createBuffer();
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.propIndexBuffer);
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexArray, gl.STATIC_DRAW);
+ }
+ },
+
_processFaces: {
value: function(gl, bspData) {
var vertices = [];
@@ -562,6 +646,7 @@ var SourceBsp = Object.create(Object, {
texData: texData,
lightmap: lightmap,
renderFrame: -1,
+ translucent: false,
};
for(var faceId in texData.faces) {
@@ -575,7 +660,6 @@ var SourceBsp = Object.create(Object, {
face.indexOffset = lockGroup.indexCount*2;
face.indexCount = 0;
face.lightmap = lightmap;
- face.triPatch = triPatch;
if(face.lightofs != -1) {
// Load the lighting for this face
@@ -596,10 +680,17 @@ var SourceBsp = Object.create(Object, {
texData: texData,
lightmap: lightmap,
renderFrame: -1,
+ translucent: false,
};
}
}
+ face.triPatch = triPatch;
+
+ if(texInfo.flags & SURF_TRANS) {
+ triPatch.translucent = true; // Flag transparent patches
+ }
+
// Just... ugh :(
var vertLookupTable = {};
@@ -667,10 +758,12 @@ var SourceBsp = Object.create(Object, {
_initializeShaders: {
value: function(gl) {
- this.shader = glUtil.createShaderProgram(gl, mapVS, mapFS,
- ['position', 'texture', 'light'],
- ['viewMat', 'projectionMat', 'diffuse', 'lightmap']
- );
+ if(!sourceBspShader) {
+ sourceBspShader = glUtil.createShaderProgram(gl, mapVS, mapFS,
+ ['position', 'texture', 'light'],
+ ['viewMat', 'projectionMat', 'diffuse', 'lightmap']
+ );
+ }
}
},
@@ -683,12 +776,20 @@ var SourceBsp = Object.create(Object, {
// Only load materials that will have visible faces
// Note: This must run after _processFaces
if(texData.faces) {
- texData.material = Object.create(SourceMaterial).load(gl, "root/tf/materials/", materialName);
+ this._loadMaterial(gl, texData, materialName);
}
}
}
},
+ _loadMaterial: {
+ value: function(gl, texData, materialName) {
+ materialManager.loadMaterial(gl, "root/tf/materials/", null, materialName, function(material) {
+ texData.material = material;
+ });
+ }
+ },
+
_compileBuffers: {
value: function(gl, bspData) {
this.vertBuffer = gl.createBuffer();
@@ -758,8 +859,28 @@ var SourceBsp = Object.create(Object, {
this._flagVisibleTriPatches(leafId, frameCount);
}
- var shader = this.shader;
+ // Render opaque geometry
+ gl.disable(gl.BLEND);
+ var numSkippedBrushes = this._drawBrushes(gl, viewMat, projectionMat, frameCount, cullFrame, false);
+ var numSkippedProps = this._drawProps(gl, viewMat, projectionMat, frameCount, cullFrame, false);
+
+ // Render translucent geometry
+ gl.enable(gl.BLEND);
+ if(numSkippedBrushes > 0) {
+ this._drawBrushes(gl, viewMat, projectionMat, frameCount, cullFrame, true);
+ }
+
+ if(numSkippedProps > 0) {
+ this._drawProps(gl, viewMat, projectionMat, frameCount, cullFrame, true);
+ }
+ }
+ },
+
+ _drawBrushes: {
+ value: function(gl, viewMat, projectionMat, frameCount, cullFrame, translucent) {
+ var shader = sourceBspShader;
var lastLightmap = null;
+ var numSkippedSurfaces = 0;
// Now we get down to the rendering loop
gl.useProgram(shader);
@@ -792,22 +913,31 @@ var SourceBsp = Object.create(Object, {
for(var triPatchId in lockGroup.triPatches) {
var triPatch = lockGroup.triPatches[triPatchId];
if(cullFrame && triPatch.renderFrame != frameCount) { continue; }
-
- if(triPatch.lightmap !== lastLightmap) {
- gl.activeTexture(gl.TEXTURE1);
- gl.bindTexture(gl.TEXTURE_2D, triPatch.lightmap.texture);
- gl.uniform1i(shader.uniform.lightmap, 1);
- lastLightmap = triPatch.lightmap;
+
+ if(triPatch.texData && triPatch.texData.material) {
+ var material = triPatch.texData.material;
+ if(material.translucent != translucent) {
+ numSkippedSurfaces++;
+ continue;
+ }
+ texture = triPatch.texData.material.texture;
}
-
+
var texture = null;
if(triPatch.texData && triPatch.texData.material) {
texture = triPatch.texData.material.texture;
}
if(!texture) {
texture = glUtil.defaultTexture;
}
-
+
+ if(triPatch.lightmap !== lastLightmap) {
+ gl.activeTexture(gl.TEXTURE1);
+ gl.bindTexture(gl.TEXTURE_2D, triPatch.lightmap.texture);
+ gl.uniform1i(shader.uniform.lightmap, 1);
+ lastLightmap = triPatch.lightmap;
+ }
+
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.uniform1i(shader.uniform.diffuse, 0);
@@ -816,14 +946,103 @@ var SourceBsp = Object.create(Object, {
}
}
- for(var propId in this.staticProps) {
- var prop = this.staticProps[propId];
- if(!cullFrame || prop.renderFrame != frameCount) { continue; }
- var propDict = this.staticPropDict[prop.m_PropType];
- if(propDict.model) {
- propDict.model.draw(gl, viewMat, projectionMat, prop.modelMat);
+ return numSkippedSurfaces;
+ }
+ },
+
+ _drawProps: {
+ value: function(gl, viewMat, projectionMat, frameCount, cullFrame, translucent) {
+ if(!cullFrame || !this.propVertBuffer) { return 0; } // Don't render props when we step outside the world geometry.
+
+ var shader = sourceMdlShader;
+ var numSkippedSurfaces = 0;
+
+ //
+ // Render static props
+ //
+
+ // Bind the common shader that they all use
+ shader = sourceMdlShader;
+ gl.useProgram(shader);
+
+ gl.uniformMatrix4fv(shader.uniform.projectionMat, false, projectionMat);
+ gl.uniformMatrix4fv(shader.uniform.viewMat, false, viewMat);
+
+ // Enable vertex arrays
+ gl.enableVertexAttribArray(shader.attribute.position);
+ gl.enableVertexAttribArray(shader.attribute.texture);
+ gl.enableVertexAttribArray(shader.attribute.normal);
+ gl.enableVertexAttribArray(shader.attribute.tangent);
+
+ gl.uniform1i(shader.uniform.diffuse, 0);
+ gl.uniform1i(shader.uniform.bump, 1);
+
+ // Bind the appropriate buffers
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.propVertBuffer);
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.propIndexBuffer);
+
+ var vertexOffset, indexOffset;
+
+ // Loop through all prop types
+ for(var propDictId in this.staticPropDict) {
+ var propDict = this.staticPropDict[propDictId];
+ if(propDict.renderFrame != frameCount) { continue; } // This prop type is not visible, skip
+
+ vertexOffset = propDict.vertexOffset;
+
+ // Setup the vertex layout
+ gl.vertexAttribPointer(shader.attribute.position, 3, gl.FLOAT, false, 64, vertexOffset + 16);
+ gl.vertexAttribPointer(shader.attribute.normal, 3, gl.FLOAT, true, 64, vertexOffset + 28);
+ gl.vertexAttribPointer(shader.attribute.texture, 2, gl.FLOAT, false, 64, vertexOffset + 40);
+ gl.vertexAttribPointer(shader.attribute.tangent, 4, gl.FLOAT, false, 64, vertexOffset + 48);
+
+ // Loop through all instances of this prop and draw them
+ for(var propId in propDict.props) {
+ var prop = propDict.props[propId];
+ if(prop.renderFrame != frameCount) { continue; }
+
+ // Set up per-prop shader uniforms
+ gl.uniform3f(shader.uniform.lightPos, prop.m_LightingOrigin.x, prop.m_LightingOrigin.y, prop.m_LightingOrigin.z);
+ gl.uniformMatrix4fv(shader.uniform.modelMat, false, prop.modelMat);
+
+ // It's too bad we can't do this in the shader
+ mat4.multiply(viewMat, prop.modelMat, modelViewMat);
+ mat4.toInverseMat3(modelViewMat, modelViewInvMat);
+ gl.uniformMatrix3fv(shader.uniform.normalMat, false, modelViewInvMat);
+
+ // Draw the mesh
+ var propModel = propDict.model;
+ var lastTexture = null;
+ var lastBump = null;
+ propModel._iterateStripGroups(function(stripGroup, mesh, model, bodyPart) {
+ var materialId = mesh.material + (propModel.numSkinRef * propModel.skin);
+ var material = propModel.textures[propModel.skinTable[materialId]].material;
+
+ if(material && material.translucent != translucent) { numSkippedSurfaces++; return; }
+
+ var texture = material ? material.texture : null;
+ if(!texture) { texture = glUtil.defaultTexture; }
+
+ var bump = material ? material.bump : null;
+ if(!bump) { bump = glUtil.defaultBumpTexture; }
+
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+
+ gl.activeTexture(gl.TEXTURE1);
+ gl.bindTexture(gl.TEXTURE_2D, bump);
+
+ for(var stripId in stripGroup.strips) {
+ var strip = stripGroup.strips[stripId];
+ gl.drawElements(gl.TRIANGLES, strip.numIndices, gl.UNSIGNED_SHORT, propDict.indexOffset + ((stripGroup.indexOffset + strip.indexOffset) * 2));
+ }
+ }, propModel.lod);
+
+ //gl.drawElements(gl.TRIANGLES, triPatch.indexCount, gl.UNSIGNED_SHORT, propDict.indexOffset);
}
}
+
+ return numSkippedSurfaces;
}
},
@@ -842,6 +1061,8 @@ var SourceBsp = Object.create(Object, {
for(var i = 0; i < leafPropCount; ++i) {
var prop = this.staticProps[leafProps[i]];
prop.renderFrame = frame;
+
+ this.staticPropDict[prop.m_PropType].renderFrame = frame;
}
}
View
115 js/source-mdl.js
@@ -37,16 +37,19 @@ meshVS += "attribute vec2 texture;\n";
meshVS += "attribute vec3 normal;\n";
meshVS += "attribute vec4 tangent;\n";
-meshVS += "uniform mat4 modelViewMat;\n";
+meshVS += "uniform mat4 viewMat;\n";
+meshVS += "uniform mat4 modelMat;\n";
meshVS += "uniform mat3 normalMat;\n";
meshVS += "uniform mat4 projectionMat;\n";
+meshVS += "uniform vec3 lightPos;\n";
+
meshVS += "varying vec2 vTexCoord;\n";
meshVS += "varying vec3 tangentLightDir;\n";
meshVS += "varying vec3 tangentEyeDir;\n";
meshVS += "void main(void) {\n";
-meshVS += " vec3 lightPos = (modelViewMat * vec4(100.0, 100.0, 100.0, 1.0)).xyz;\n";
+meshVS += " mat4 modelViewMat = viewMat * modelMat;\n";
meshVS += " vec4 vPosition = modelViewMat * vec4(position, 1.0);\n";
meshVS += " gl_Position = projectionMat * vPosition;\n";
@@ -56,7 +59,8 @@ meshVS += " vec3 n = normalize(normal * normalMat);\n";
meshVS += " vec3 t = normalize(tangent.xyz * normalMat);\n";
meshVS += " vec3 b = cross (n, t) * tangent.w;\n";
-meshVS += " vec3 lightDir = lightPos - vPosition.xyz;\n";
+meshVS += " vec3 vlightPos = (viewMat * vec4(lightPos, 1.0)).xyz;\n";
+meshVS += " vec3 lightDir = vlightPos - vPosition.xyz;\n";
meshVS += " tangentLightDir.x = dot(lightDir, t);\n";
meshVS += " tangentLightDir.y = dot(lightDir, b);\n";
meshVS += " tangentLightDir.z = dot(lightDir, n);\n";
@@ -92,9 +96,11 @@ meshFS += " float specularFactor = pow(clamp(dot(reflectDir, eyeDir), 0.0, 1.0),
meshFS += " float lightFactor = max(dot(lightDir, normal), 0.0);\n";
meshFS += " vec3 lightValue = ambientLight + (lightColor * lightFactor) + (specularColor * specularFactor);\n";
-meshFS += " gl_FragColor = vec4(diffuseColor.rgb * lightValue, 1.0);\n";
+meshFS += " gl_FragColor = vec4(diffuseColor.rgb * lightValue, diffuseColor.a);\n";
meshFS += "}";
+var sourceMdlShader = null;
+
var modelIdentityMat = mat4.create();
mat4.identity(modelIdentityMat);
@@ -114,19 +120,19 @@ var SourceModel = Object.create(Object, {
value: null
},
- indexBuffer: {
+ indexArray: {
value: null
},
- bodyParts: {
+ indexBuffer: {
value: null
},
- mdlBodyParts: {
+ bodyParts: {
value: null
},
- shader: {
+ mdlBodyParts: {
value: null
},
@@ -154,12 +160,16 @@ var SourceModel = Object.create(Object, {
value: 0
},
+ /**
+ * Load the .mdl and other associated files.
+ * @param gl WebGL context used to initialize the data. If not provided, the model will parse all relevent data but will not upload to the GPU
+ * @param url Location to load the model from. Do not pass an extension. Will load url.mdl, url.vvd, and url.vtx
+ * @param callback Function to call when load is complete
+ **/
load: {
- value: function(gl, url, lod) {
- this._initializeShaders(gl);
-
- if(typeof(lod) != "undefined") {
- this.lod = lod;
+ value: function(gl, url, callback) {
+ if(gl) {
+ this.initializeShaders(gl);
}
url = url.replace(".mdl", ""); // Strip off .mdl extension if it was provided
@@ -172,7 +182,9 @@ var SourceModel = Object.create(Object, {
mdlXhr.addEventListener("load", function() {
self._parseMdl(this.response);
- self.loadSkin(gl, 0);
+ if(gl) {
+ self.loadSkin(gl, 0);
+ }
var vvdXhr = new XMLHttpRequest();
vvdXhr.open('GET', url + ".vvd", true);
@@ -185,7 +197,12 @@ var SourceModel = Object.create(Object, {
vtxXhr.responseType = "arraybuffer";
vtxXhr.addEventListener("load", function() {
self._parseVtx(this.response, self.lod);
- self._initializeBuffers(gl);
+
+ if(gl) {
+ self._initializeBuffers(gl);
+ }
+
+ if(callback) { callback(self); }
});
vtxXhr.send(null);
@@ -209,19 +226,29 @@ var SourceModel = Object.create(Object, {
var textureId = this.skinTable[skinTableOffset + i];
var texture = this.textures[textureId];
if(!texture.material) {
- var materialName = texture.textureName;
- texture.material = Object.create(SourceMaterial).load(gl, "root/tf/materials/", this.textureDirs, materialName);
+ this._loadMaterial(gl, texture);
}
}
}
},
- _initializeShaders: {
+ _loadMaterial: {
+ value: function(gl, texture) {
+ var materialName = texture.textureName;
+ materialManager.loadMaterial(gl, "root/tf/materials/", this.textureDirs, materialName, function(material) {
+ texture.material = material;
+ });
+ }
+ },
+
+ initializeShaders: {
value: function(gl) {
- this.shader = glUtil.createShaderProgram(gl, meshVS, meshFS,
- ['position', 'texture', 'normal', 'tangent'],
- ['modelViewMat', 'projectionMat', 'normalMat', 'diffuse', 'bump']
- );
+ if(!sourceMdlShader) {
+ sourceMdlShader = glUtil.createShaderProgram(gl, meshVS, meshFS,
+ ['position', 'texture', 'normal', 'tangent'],
+ ['modelMat', 'viewMat', 'projectionMat', 'normalMat', 'diffuse', 'bump', 'lightPos']
+ );
+ }
}
},
@@ -374,7 +401,6 @@ var SourceModel = Object.create(Object, {
var header = VtxHeader_t.readStructs(buffer, 0, 1)[0];
// Nested struct parsing loop of DOOM!
-
this.bodyParts = BodyPartHeader_t.readStructs(buffer, header.bodyPartOffset, header.numBodyParts, function(bodyPart, offset) {
bodyPart.models = ModelHeader_t.readStructs(buffer, offset + bodyPart.modelOffset, bodyPart.numModels, function(model, offset) {
model.lods = ModelLODHeader_t.readStructs(buffer, offset + model.lodOffset, model.numLODs, function(lod, offset) {
@@ -388,6 +414,8 @@ var SourceModel = Object.create(Object, {
});
});
});
+
+ this.indexArray = this._buildIndices();
}
},
@@ -417,15 +445,13 @@ var SourceModel = Object.create(Object, {
_initializeBuffers: {
value: function(gl) {
- var indexArray = this._buildIndices();
-
this.vertBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertBuffer);
gl.bufferData(gl.ARRAY_BUFFER, this.vertArray, gl.STATIC_DRAW);
this.indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
- gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexArray, gl.STATIC_DRAW);
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indexArray, gl.STATIC_DRAW);
}
},
@@ -484,27 +510,34 @@ var SourceModel = Object.create(Object, {
*/
draw: {
- value: function(gl, viewMat, projectionMat, modelMat) {
- var shader = this.shader;
-
- if(!shader || !this.vertBuffer) { return; }
+ value: function(gl, viewMat, projectionMat, modelMat, shader) {
+ if(!this.vertBuffer) { return; }
- gl.useProgram(shader);
+ if(!shader) {
+ if(!sourceMdlShader) { return; }
+ shader = sourceMdlShader;
+
+ gl.useProgram(shader);
+
+ gl.uniformMatrix4fv(shader.uniform.projectionMat, false, projectionMat);
+
+ // Enable vertex arrays
+ gl.enableVertexAttribArray(shader.attribute.position);
+ gl.enableVertexAttribArray(shader.attribute.texture);
+ gl.enableVertexAttribArray(shader.attribute.normal);
+ gl.enableVertexAttribArray(shader.attribute.tangent);
+
+ gl.uniform3f(shader.uniform.lightPos, 100, 100, 100);
+ gl.uniformMatrix4fv(shader.uniform.viewMat, false, viewMat);
+ };
- gl.uniformMatrix4fv(shader.uniform.projectionMat, false, projectionMat);
+ gl.uniformMatrix4fv(shader.uniform.modelMat, false, modelMat || modelIdentityMat);
+ // It's too bad we can't do this in the shader
mat4.multiply(viewMat, modelMat || modelIdentityMat, modelViewMat);
- gl.uniformMatrix4fv(shader.uniform.modelViewMat, false, modelViewMat);
-
mat4.toInverseMat3(modelViewMat, modelViewInvMat);
gl.uniformMatrix3fv(shader.uniform.normalMat, false, modelViewInvMat);
- // Enable vertex arrays
- gl.enableVertexAttribArray(shader.attribute.position);
- gl.enableVertexAttribArray(shader.attribute.texture);
- gl.enableVertexAttribArray(shader.attribute.normal);
- gl.enableVertexAttribArray(shader.attribute.tangent);
-
// Bind the appropriate buffers
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertBuffer);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
@@ -544,5 +577,5 @@ var SourceModel = Object.create(Object, {
}
}, this.lod);
}
- }
+ },
});
View
144 js/source-vmt.js
@@ -80,37 +80,18 @@ var SourceMaterial = Object.create(Object, {
value: null
},
+ bump: {
+ value: null
+ },
+
+ translucent: {
+ value: false
+ },
+
load: {
- value: function(gl, rootUrl, searchDirs, url) {
- var self = this;
-
- if(!url) {
- url = searchDirs;
- searchDirs = [""];
- }
-
- function tryDir(searchDirId) {
- if(searchDirId >= searchDirs.length) { return; }
-
- var searchDir = searchDirs[searchDirId];
- var vmtXhr = new XMLHttpRequest();
- vmtXhr.open('GET', rootUrl + searchDir + url + ".vmt", true);
- vmtXhr.addEventListener("load", function() {
- if (vmtXhr.status == 200) {
- var material = self._parseVmt(this.responseText);
- self._compileMaterial(gl, rootUrl, material);
- } else {
- tryDir(searchDirId+1);
- }
- });
- vmtXhr.addEventListener("error", function() {
- tryDir(searchDirId++);
- });
- vmtXhr.send(null);
- }
- tryDir(0);
-
- return this;
+ value: function(gl, rootUrl, buffer) {
+ var material = this._parseVmt(buffer);
+ this._compileMaterial(gl, rootUrl, material);
}
},
@@ -145,6 +126,12 @@ var SourceMaterial = Object.create(Object, {
case "$bumpmap": {
material.bumpmap = tokens.next();
} break;
+
+ case "$alphatest":
+ case "$translucent": {
+ var trans = tokens.next();
+ this.translucent = trans == "1";
+ } break;
}
}
@@ -191,3 +178,100 @@ var SourceMaterial = Object.create(Object, {
}
});
+//
+// Material management
+//
+
+var SourceMaterialManager = Object.create(Object, {
+ materials: {
+ value: null
+ },
+
+ materialCount: {
+ value: 0
+ },
+
+ materialsComplete: {
+ value: 0
+ },
+
+ onMaterialsCompleted: {
+ value: null
+ },
+
+ init: {
+ value: function() {
+ this.materials = {};
+ this.textures = {};
+
+ return this;
+ }
+ },
+
+ loadMaterial: {
+ value: function(gl, rootUrl, searchDirs, url, callback) {
+ var self = this;
+
+ this.materialCount++;
+
+ if(!searchDirs) {
+ searchDirs = [""];
+ }
+
+ var material;
+ function tryDir(searchDirId) {
+ if(searchDirId >= searchDirs.length) {
+ if(callback) { callback(null); } // Not found
+ self._materialCompleted();
+ return;
+ }
+
+ var searchDir = searchDirs[searchDirId];
+
+ var path = rootUrl + searchDir + url + ".vmt";
+
+ material = self.materials[path];
+ if(material) {
+ if(callback) { callback(material); }
+ self._materialCompleted();
+ return;
+ }
+
+ var vmtXhr = new XMLHttpRequest();
+ vmtXhr.open('GET', rootUrl + searchDir + url + ".vmt", true);
+ vmtXhr.addEventListener("load", function() {
+ if (vmtXhr.status == 200) {
+ material = Object.create(SourceMaterial);
+ material.load(gl, rootUrl, this.responseText);
+ if(callback) { callback(material); }
+ self._materialCompleted();
+ } else {
+ tryDir(searchDirId+1);
+ }
+ });
+ vmtXhr.addEventListener("error", function() {
+ tryDir(searchDirId++);
+ });
+ vmtXhr.send(null);
+ }
+ tryDir(0);
+ }
+ },
+
+ _materialCompleted: {
+ value: function() {
+ this.materialsComplete++;
+ if(this.materialsComplete == this.materialCount) {
+ if(this.onMaterialsCompleted) { this.onMaterialsCompleted(); }
+ }
+ }
+ },
+
+ areMaterialsComplete: {
+ get: function() {
+ return this.materialsComplete == this.materialCount;
+ }
+ },
+});
+
+var materialManager = Object.create(SourceMaterialManager).init();
View
2 js/webgl-demo-camera.js
@@ -213,7 +213,7 @@ var FlyingDemoCamera = Object.create(Object, {
mat4.rotateX(mv, this.angles[0]-Math.PI/2.0);
mat4.rotateZ(mv, this.angles[1]);
mat4.rotateY(mv, this.angles[2]);
- mat4.translate(mv, [-this.position[0], -this.position[1], - this.position[2]]);
+ mat4.translate(mv, [-this.position[0], -this.position[1], -this.position[2]]);
this._dirty = false;
}
View
11 js/webgl-util.js
@@ -121,6 +121,15 @@ var glUtil = Object.create(Object, {
loadTexture: {
value: function(gl, src, callback) {
+ if(!this.textures) { this.textures = {}; }
+
+ // Check to see if that URL has already been loaded
+ var texture = this.textures[src];
+ if(texture) {
+ if(callback) { callback(texture); }
+ return texture;
+ }
+
var texture = gl.createTexture();
var image = new Image();
image.addEventListener("load", function() {
@@ -133,6 +142,8 @@ var glUtil = Object.create(Object, {
if(callback) { callback(texture); }
});
image.src = src;
+
+ this.textures[src] = texture;
return texture;
}
},

0 comments on commit 4c485bd

Please sign in to comment.
Something went wrong with that request. Please try again.