Quote from WebGL - General/Working Group Info:
I’d love to see the results![/quote]
I didn’t really implemented several ways to load meshes, but I thought about some time. The idea was to include meshes and shader data into one single library file. An ResourceManager class does load one or more of this library files, e.g. one default file and some specific files for the current map or level. After that you can request resources by ID, for example “defaultshader.vs”.
XML
First thing I tried was to load data from an XML file like this:
<library>
<vertexshader id="defaultshader.vs">
uniform mat4 uModelViewProjection;
uniform mat4 uModelView;
...
void main() {
...
}
</vertexshader>
<fragmentshader id="defaultshader.fs">
varying vec3 vPosition;
varying vec3 vNormal;
varying vec2 vTexCoord;
...
void main()
{
gl_FragColor = ...
}
</fragmentshader>
<mesh id="spheres.mesh">
<vertices>
0,-1,2,-2.94008e-07,-1,3.2738e-07,0.0,0.0,
0.7236,-0.447215,2.52572,0.723607,-0.447219,0.525726,0.0,0.0,
-0.276385,-0.447215,2.85064,-0.276387,-0.44722,0.850649,0.0,0.0,
-0.894425,-0.447215,2,-0.894426,-0.447216,1.49148e-09,0.0,0.0,
...
0.528274,0.628275,-0.571137,0.528362,0.628139,-0.571205,0.0,0.0,
0.589185,0.578092,-0.564509,0.589041,0.578138,-0.564612,0.0,0.0
</vertices>
<indices>
1602,1123,402,1123,1602,885,283,885,1602,885,102,1123,1602,1603,283,1603,1602,1604,402,1604,1602,
1604,172,1603,663,1122,47,1122,663,1604,172,1604,663,1604,402,1122,662,1603,172,1603,662,884,14,884,662,
...
20894,21610,23057,23057,21332,20755,21611,20894,23056,23056,22094,21611,20656,21611,22094,22094,
23056,21136
</indices>
</mesh>
</library>
This is pretty fast, because browsers are designed to parse XML data. To parse the meshdata itself I put brackets (“[…]”) around the string and use the eval function, which does return an array. This is also fast, because eval is implemented in native code. I didn’t try an JavaScript implementation, because even if eval is slow, it should be at least as fast as a JS implementation.
However, the problem with XML files is that getElementById() does not work out of the box. You need to specify the DTD. Because Firefox (didn’t try other browsers) does not support external DTD you need to include the DTD definition in every library file. The DTD for the document above looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE library [
<!ELEMENT library (vertexshader|fragmentshader|mesh)*>
<!ELEMENT vertexshader (#PCDATA)>
<!ATTLIST vertexshader id ID #REQUIRED>
<!ELEMENT fragmentshader (#PCDATA)>
<!ATTLIST fragmentshader id ID #REQUIRED>
<!ELEMENT mesh (vertices, indices?)>
<!ATTLIST mesh id ID #REQUIRED>
<!ELEMENT vertices (#PCDATA)>
<!ATTLIST vertices type CDATA #REQUIRED>
<!ELEMENT indices (#PCDATA)>
]>
JSON
Because this DTD stuff is annoying, I tried to use JSON. Actually this was the idea of Jeff Chimene at GWT groups. JSON is a really simple format, it’s just JavaScript code. You can parse the complete file at once with the eval function. As result you will get an JavaScript object structure.
The library file above does look like this:
{
"defaultshader.fs" : "varying vec3 vPosition;
varying vec3 vNormal;
varying vec2 vTexCoord;
uniform vec3 uLightPos;
const float cShininess = 100.0;
const vec4 cAmbient = vec4(0.2,0.2,0.2,1);
const vec4 cDiffuse = vec4(0.5,0.5,0.5,0);
const vec4 cSpecular = vec4(1,1,1,0);
void main() {
vec3 lightDir = normalize(uLightPos-vPosition);
vec3 normal = normalize(vNormal);
gl_FragColor = cAmbient;
float diffuse = max(dot(lightDir, normal), 0.0);
gl_FragColor += cDiffuse * diffuse;
//gl_FragColor = vec4(lightDir,1.0);
if (diffuse > 0.0) {
vec3 r = normalize( (2.0 * dot(normal, lightDir) * normal) - lightDir );
float f = dot(r, normalize(-vPosition));
float specular = pow(max(f, 0.0), cShininess);
gl_FragColor += cSpecular * specular;
}
}
",
"defaultshader.vs" : "uniform mat4 uModelViewProjection;
uniform mat4 uModelView;
uniform mat4 uNormalMatrix;
attribute vec3 aPosition;
attribute vec3 aNormal;
attribute vec3 aTexCoord;
varying vec3 vPosition;
varying vec3 vNormal;
varying vec2 vTexCoord;
void main() {
gl_Position = uModelViewProjection * vec4(aPosition, 1.0);
vPosition = uModelView * vec4(aPosition, 1.0);
vTexCoord = aTexCoord;
//vNormal = normalize(uNormalMatrix * aNormal);
vNormal = normalize(uNormalMatrix * vec4(aNormal, 0.0)).xyz;
}
",
"spheres.mesh" : {
"vertices" : [
0,-1,2,-2.94008e-07,-1,3.2738e-07,0.0,0.0,
0.7236,-0.447215,2.52572,0.723607,-0.447219,0.525726,0.0,0.0,
...
0.528274,0.628275,-0.571137,0.528362,0.628139,-0.571205,0.0,0.0,
0.589185,0.578092,-0.564509,0.589041,0.578138,-0.564612,0.0,0.0],
"indices" : [
1602,1123,402,1123,1602,885,283,885,1602,885,102,1123,1602,1603,283,1603,1602,1604,402,1604,1602,
...
21332,21332,23057,21610,20894,21610,23057,23057,21332,20755,21611,20894,23056,23056,22094,21611,20656,21611,22094,
22094,23056,21136]
}
}
=> result: JSON is fast and also easy to implement, since you just need to use eval which does the parsing for you. The only drawback is you need some kind of tool which does convert shader files into this format since JSON does not support multi-line strings. However, a tool that does convert newlines into "
" is not that hard to write.
In both cases you need a tool that converts your mesh data into the required format. I wrote such a tool based on lib3ds for Linux x86_64, it does also convert shader files. I will provide the source code if some one asks for it.
If have also implemented that ResourceManager stuff, but it is integrated into an complete WebGL engine. This engine is an module for GoogleWebToolkit and provides an WebGL wrapper, the ResourceManager, some Math classes and classes for comfortable use of Shaders, Textures, Meshes and so on. Since GWT does compile Java 1.5 code into JavaScript, you don’t have to bother about JavaScript. Coding in Java is much better However, it will take some weeks until I can release that engine.