mirror of
https://github.com/GTAmodding/modelviewjs.git
synced 2025-07-09 10:30:14 +02:00
first commit
This commit is contained in:
parent
2d2119a869
commit
bea1a1c2a0
2
README.md
Normal file
2
README.md
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Does not come with data.
|
||||||
|
See it in action [here](http://gta.rockstarvision.com/vehicleviewer/).
|
6888
gl-matrix.js
Normal file
6888
gl-matrix.js
Normal file
File diff suppressed because it is too large
Load Diff
105
gl-util.js
Normal file
105
gl-util.js
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
var gl;
|
||||||
|
|
||||||
|
const ATTRIB_POS = 0;
|
||||||
|
const ATTRIB_NORMAL = 1;
|
||||||
|
const ATTRIB_COLOR = 2;
|
||||||
|
const ATTRIB_TEXCOORDS0 = 3;
|
||||||
|
const ATTRIB_TEXCOORDS1 = 4;
|
||||||
|
|
||||||
|
function
|
||||||
|
loadTexture(url)
|
||||||
|
{
|
||||||
|
if(gl === undefined)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
const texid = gl.createTexture();
|
||||||
|
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, texid);
|
||||||
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,
|
||||||
|
1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
|
||||||
|
new Uint8Array([255, 255, 255, 255]));
|
||||||
|
|
||||||
|
const image = new Image();
|
||||||
|
image.onload = function() {
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, texid);
|
||||||
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,
|
||||||
|
gl.RGBA, gl.UNSIGNED_BYTE, image);
|
||||||
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
|
||||||
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
|
||||||
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||||
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||||
|
};
|
||||||
|
image.src = url;
|
||||||
|
|
||||||
|
return texid;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
loadShaders(vs, fs)
|
||||||
|
{
|
||||||
|
const shaderProgram = initShaderProgram(vs, fs);
|
||||||
|
|
||||||
|
programInfo = {
|
||||||
|
program: shaderProgram,
|
||||||
|
a: [
|
||||||
|
gl.getAttribLocation(shaderProgram, 'in_pos'),
|
||||||
|
gl.getAttribLocation(shaderProgram, 'in_normal'),
|
||||||
|
gl.getAttribLocation(shaderProgram, 'in_color'),
|
||||||
|
gl.getAttribLocation(shaderProgram, 'in_tex0'),
|
||||||
|
gl.getAttribLocation(shaderProgram, 'in_tex1'),
|
||||||
|
],
|
||||||
|
u: {
|
||||||
|
u_proj: gl.getUniformLocation(shaderProgram, 'u_proj'),
|
||||||
|
u_view: gl.getUniformLocation(shaderProgram, 'u_view'),
|
||||||
|
u_world: gl.getUniformLocation(shaderProgram, 'u_world'),
|
||||||
|
u_env: gl.getUniformLocation(shaderProgram, 'u_env'),
|
||||||
|
u_matColor: gl.getUniformLocation(shaderProgram, 'u_matColor'),
|
||||||
|
u_surfaceProps: gl.getUniformLocation(shaderProgram, 'u_surfaceProps'),
|
||||||
|
u_ambLight: gl.getUniformLocation(shaderProgram, 'u_ambLight'),
|
||||||
|
u_lightDir: gl.getUniformLocation(shaderProgram, 'u_lightDir'),
|
||||||
|
u_lightCol: gl.getUniformLocation(shaderProgram, 'u_lightCol'),
|
||||||
|
u_alphaRef: gl.getUniformLocation(shaderProgram, 'u_alphaRef'),
|
||||||
|
u_tex0: gl.getUniformLocation(shaderProgram, 'tex0'),
|
||||||
|
u_tex1: gl.getUniformLocation(shaderProgram, 'tex1'),
|
||||||
|
u_tex2: gl.getUniformLocation(shaderProgram, 'tex2'),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
console.log(programInfo.u.u_alphaRef);
|
||||||
|
return programInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
initShaderProgram(vsSource, fsSource)
|
||||||
|
{
|
||||||
|
const vertexShader = loadShader(gl.VERTEX_SHADER, vsSource);
|
||||||
|
const fragmentShader = loadShader(gl.FRAGMENT_SHADER, fsSource);
|
||||||
|
|
||||||
|
const shaderProgram = gl.createProgram();
|
||||||
|
gl.attachShader(shaderProgram, vertexShader);
|
||||||
|
gl.attachShader(shaderProgram, fragmentShader);
|
||||||
|
gl.linkProgram(shaderProgram);
|
||||||
|
|
||||||
|
if(!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)){
|
||||||
|
alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return shaderProgram;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
loadShader(type, source)
|
||||||
|
{
|
||||||
|
const shader = gl.createShader(type);
|
||||||
|
|
||||||
|
gl.shaderSource(shader, source);
|
||||||
|
gl.compileShader(shader);
|
||||||
|
|
||||||
|
if(!gl.getShaderParameter(shader, gl.COMPILE_STATUS)){
|
||||||
|
alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));
|
||||||
|
gl.deleteShader(shader);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return shader;
|
||||||
|
}
|
83
index.html
Normal file
83
index.html
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>WebGL</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<link rel="stylesheet" href="webgl.css" type="text/css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="control">
|
||||||
|
<a href="javascript: startVehicleViewerIII();">III</a>
|
||||||
|
<a href="javascript: startVehicleViewerVC();">VC</a>
|
||||||
|
<a href="javascript: startVehicleViewerSA();">SA</a>
|
||||||
|
</div>
|
||||||
|
<canvas id="glcanvas" width="640" height="480" style="float:left"></canvas>
|
||||||
|
<div style="float:left">
|
||||||
|
<select id="objects" size="25">
|
||||||
|
<!-- JS inserts models here -->
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<table id="colors" style="float:left">
|
||||||
|
<!-- JS inserts colors here -->
|
||||||
|
</table>
|
||||||
|
<div style="display:table;">
|
||||||
|
<ul id="frames">
|
||||||
|
<!-- JS inserts frames here -->
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div style="clear:both;"></div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="gl-matrix.js"></script>
|
||||||
|
<script src="gl-util.js"></script>
|
||||||
|
<script src="rw.js"></script>
|
||||||
|
<script src="rwrender.js"></script>
|
||||||
|
<script src="shaders.js"></script>
|
||||||
|
<script src="rwstream.js"></script>
|
||||||
|
<script src="main.js"></script>
|
||||||
|
<script src="loaddata.js"></script>
|
||||||
|
<script>
|
||||||
|
InitRW();
|
||||||
|
|
||||||
|
function
|
||||||
|
startVehicleViewerIII()
|
||||||
|
{
|
||||||
|
DataDirPath = "iii/data";
|
||||||
|
ModelsDirPath = "iii/models";
|
||||||
|
TexturesDirPath = "iii/textures";
|
||||||
|
loadCar = loadCarIII;
|
||||||
|
loadVehicleViewer("default.ide", function() {
|
||||||
|
SelectModel("cheetah");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
startVehicleViewerVC()
|
||||||
|
{
|
||||||
|
DataDirPath = "vc/data";
|
||||||
|
ModelsDirPath = "vc/models";
|
||||||
|
TexturesDirPath = "vc/textures";
|
||||||
|
loadCar = loadCarVC;
|
||||||
|
loadVehicleViewer("default.ide", function() {
|
||||||
|
SelectModel("cheetah");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
startVehicleViewerSA()
|
||||||
|
{
|
||||||
|
DataDirPath = "sa/data";
|
||||||
|
ModelsDirPath = "sa/models";
|
||||||
|
TexturesDirPath = "sa/textures";
|
||||||
|
loadCar = loadCarSA;
|
||||||
|
loadVehicleViewer("vehicles.ide", function() {
|
||||||
|
SelectModel("cheetah");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
startVehicleViewerIII();
|
||||||
|
</script>
|
||||||
|
</html>
|
213
loaddata.js
Normal file
213
loaddata.js
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
var VehicleColours = [];
|
||||||
|
var ModelInfos = {};
|
||||||
|
var ModelInfosName = {};
|
||||||
|
var CurrentModel = null;
|
||||||
|
|
||||||
|
function
|
||||||
|
SetCarColors(cols)
|
||||||
|
{
|
||||||
|
carColors = [
|
||||||
|
[ 0, 0, 0, 255 ],
|
||||||
|
[ 0, 0, 0, 255 ],
|
||||||
|
[ 0, 0, 0, 255 ],
|
||||||
|
[ 0, 0, 0, 255 ]
|
||||||
|
];
|
||||||
|
if(cols !== undefined)
|
||||||
|
for(let i = 0; i < cols.length; i++){
|
||||||
|
let col = VehicleColours[cols[i]];
|
||||||
|
carColors[i][0] = col[0];
|
||||||
|
carColors[i][1] = col[1];
|
||||||
|
carColors[i][2] = col[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
LoadColors(cols)
|
||||||
|
{
|
||||||
|
SetCarColors(cols);
|
||||||
|
setVehicleColors(modelinfo, carColors[0], carColors[1], carColors[2], carColors[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
SelectModel(model)
|
||||||
|
{
|
||||||
|
let colortable = document.getElementById('colors');
|
||||||
|
removeChildren(colortable);
|
||||||
|
|
||||||
|
CurrentModel = ModelInfosName[model];
|
||||||
|
let col1 = [ 0, 0, 0, 255 ];
|
||||||
|
let col2 = [ 0, 0, 0, 255 ];
|
||||||
|
for(let i = 0; i < CurrentModel.colors.length; i++){
|
||||||
|
let c = CurrentModel.colors[i];
|
||||||
|
let c1 = VehicleColours[c[0]];
|
||||||
|
let c2 = VehicleColours[c[1]];
|
||||||
|
if(i == 0){
|
||||||
|
col1[0] = c1[0];
|
||||||
|
col1[1] = c1[1];
|
||||||
|
col1[2] = c1[2];
|
||||||
|
col2[0] = c2[0];
|
||||||
|
col2[1] = c2[1];
|
||||||
|
col2[2] = c2[2];
|
||||||
|
}
|
||||||
|
let tr = document.createElement('tr');
|
||||||
|
for(let j = 0; j < c.length; j++){
|
||||||
|
let td = document.createElement('td');
|
||||||
|
td.width = "16px";
|
||||||
|
td.height = "16px";
|
||||||
|
let col = VehicleColours[c[j]];
|
||||||
|
td.style = "background-color: rgb("+col[0]+","+col[1]+","+col[2]+")";
|
||||||
|
tr.appendChild(td);
|
||||||
|
}
|
||||||
|
tr.onclick = function() { LoadColors(c); };
|
||||||
|
colortable.appendChild(tr);
|
||||||
|
}
|
||||||
|
|
||||||
|
camDist = 5.0;
|
||||||
|
camPitch = 0.3;
|
||||||
|
camYaw = 1.0;
|
||||||
|
SetCarColors(CurrentModel.colors[0]);
|
||||||
|
|
||||||
|
loadCar(model + ".dff");
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
StartUI()
|
||||||
|
{
|
||||||
|
let objects = document.getElementById('objects');
|
||||||
|
removeChildren(objects);
|
||||||
|
for(let model in ModelInfosName){
|
||||||
|
let option = document.createElement('option');
|
||||||
|
option.innerHTML = model;
|
||||||
|
option.onclick = function(){ SelectModel(model); };
|
||||||
|
objects.appendChild(option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
LoadVehicle(fields)
|
||||||
|
{
|
||||||
|
let id = Number(fields[0]);
|
||||||
|
let mi = {};
|
||||||
|
mi.id = id;
|
||||||
|
mi.type = "vehicle";
|
||||||
|
mi.model = fields[1];
|
||||||
|
mi.txd = fields[2];
|
||||||
|
mi.vehtype = fields[3];
|
||||||
|
mi.handling = fields[4];
|
||||||
|
if(mi.vehtype == "car"){
|
||||||
|
// TODO: check SA
|
||||||
|
mi.wheelId = Number(fields[11]);
|
||||||
|
mi.wheelScale = Number(fields[12]);
|
||||||
|
}
|
||||||
|
mi.colors = [];
|
||||||
|
ModelInfos[mi.id] = mi;
|
||||||
|
ModelInfosName[mi.model] = mi;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
LoadColour(fields)
|
||||||
|
{
|
||||||
|
let r = Number(fields[0]);
|
||||||
|
let g = Number(fields[1]);
|
||||||
|
let b = Number(fields[2]);
|
||||||
|
VehicleColours.push([r, g, b]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
LoadVehicleColour(fields)
|
||||||
|
{
|
||||||
|
let mi = ModelInfosName[fields[0]];
|
||||||
|
for(let i = 1; i < fields.length; i += 2){
|
||||||
|
let c1 = Number(fields[i]);
|
||||||
|
let c2 = Number(fields[i+1]);
|
||||||
|
mi.colors.push([c1, c2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
LoadVehicleColour4(fields)
|
||||||
|
{
|
||||||
|
let mi = ModelInfosName[fields[0]];
|
||||||
|
for(let i = 1; i < fields.length; i += 4){
|
||||||
|
let c1 = Number(fields[i]);
|
||||||
|
let c2 = Number(fields[i+1]);
|
||||||
|
let c3 = Number(fields[i+2]);
|
||||||
|
let c4 = Number(fields[i+3]);
|
||||||
|
mi.colors.push([c1, c2, c3, c4]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
LoadObjectTypes(text)
|
||||||
|
{
|
||||||
|
LoadSectionedFile(text, {
|
||||||
|
"cars": LoadVehicle
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
LoadVehicleColours(text)
|
||||||
|
{
|
||||||
|
LoadSectionedFile(text, {
|
||||||
|
"col": LoadColour,
|
||||||
|
"car": LoadVehicleColour,
|
||||||
|
"car4": LoadVehicleColour4
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
LoadSectionedFile(text, sections)
|
||||||
|
{
|
||||||
|
let section = "end";
|
||||||
|
let lines = text.split("\n");
|
||||||
|
for(let i = 0; i < lines.length; i++){
|
||||||
|
let line = lines[i].replace(/,/g, " ").replace(/#.*/g, "").trim().toLowerCase();
|
||||||
|
if(line.length == 0)
|
||||||
|
continue;
|
||||||
|
let fields = line.split(/[\t ]+/);
|
||||||
|
|
||||||
|
if(section == "end"){
|
||||||
|
section = fields[0];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(fields[0] == "end"){
|
||||||
|
section = "end";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(section in sections)
|
||||||
|
sections[section](fields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
loadText(filename, cb)
|
||||||
|
{
|
||||||
|
let req = new XMLHttpRequest();
|
||||||
|
req.open("GET", DataDirPath + "/" + filename, true);
|
||||||
|
req.responseType = "text";
|
||||||
|
|
||||||
|
req.onload = function(oEvent){
|
||||||
|
cb(req.response);
|
||||||
|
};
|
||||||
|
|
||||||
|
req.send(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
loadVehicleViewer(idefile, CB)
|
||||||
|
{
|
||||||
|
VehicleColours = [];
|
||||||
|
ModelInfos = {};
|
||||||
|
ModelInfosName = {};
|
||||||
|
CurrentModel = null;
|
||||||
|
|
||||||
|
loadText(idefile, function(text){
|
||||||
|
LoadObjectTypes(text);
|
||||||
|
loadText("carcols.dat", function(text){
|
||||||
|
LoadVehicleColours(text);
|
||||||
|
StartUI();
|
||||||
|
CB();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
430
main.js
Normal file
430
main.js
Normal file
@ -0,0 +1,430 @@
|
|||||||
|
var DataDirPath;
|
||||||
|
var ModelsDirPath;
|
||||||
|
var TexturesDirPath;
|
||||||
|
|
||||||
|
// init info
|
||||||
|
var isIIICar;
|
||||||
|
var isSACar;
|
||||||
|
var carColors;
|
||||||
|
|
||||||
|
// the scene
|
||||||
|
var running = false;
|
||||||
|
var myclump;
|
||||||
|
var modelinfo;
|
||||||
|
var camPitch;
|
||||||
|
var camYaw;
|
||||||
|
var camDist;
|
||||||
|
|
||||||
|
// gl things
|
||||||
|
var state = {};
|
||||||
|
var whitetex;
|
||||||
|
var camera;
|
||||||
|
|
||||||
|
var envFrame;
|
||||||
|
var defaultProgram;
|
||||||
|
var envMapProgram;
|
||||||
|
var carPS2Program;
|
||||||
|
|
||||||
|
function deg2rad(d) { return d / 180.0 * Math.PI; }
|
||||||
|
|
||||||
|
var rotating, zooming;
|
||||||
|
|
||||||
|
function
|
||||||
|
mouseDown(e)
|
||||||
|
{
|
||||||
|
if(e.button == 0)
|
||||||
|
rotating = true;
|
||||||
|
else if(e.button == 1)
|
||||||
|
zooming = true;
|
||||||
|
old_x = e.pageX;
|
||||||
|
old_y = e.pageY;
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
mouseUp(e)
|
||||||
|
{
|
||||||
|
rotating = false;
|
||||||
|
zooming = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
mouseMove(e)
|
||||||
|
{
|
||||||
|
let dX, dY;
|
||||||
|
if(rotating){
|
||||||
|
dX = (e.pageX-old_x)*2*Math.PI/gl.canvas.width,
|
||||||
|
dY = (e.pageY-old_y)*2*Math.PI/gl.canvas.height;
|
||||||
|
|
||||||
|
camYaw -= dX;
|
||||||
|
camPitch += dY;
|
||||||
|
if(camPitch > Math.PI/2 - 0.01) camPitch = Math.PI/2 - 0.01
|
||||||
|
if(camPitch < -Math.PI/2 + 0.01) camPitch = -Math.PI/2 + 0.01
|
||||||
|
|
||||||
|
old_x = e.pageX;
|
||||||
|
old_y = e.pageY;
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
if(zooming){
|
||||||
|
dY = (e.pageY-old_y)/gl.canvas.height;
|
||||||
|
|
||||||
|
camDist += dY;
|
||||||
|
if(camDist < 0.1) camDist = 0.1;
|
||||||
|
|
||||||
|
old_x = e.pageX;
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function
|
||||||
|
InitRW()
|
||||||
|
{
|
||||||
|
console.log("InitRW()");
|
||||||
|
let canvas = document.querySelector('#glcanvas');
|
||||||
|
gl = canvas.getContext('webgl');
|
||||||
|
|
||||||
|
if(!gl){
|
||||||
|
alert('Unable to initialize WebGL. Your browser or machine may not support it.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.addEventListener("mousedown", mouseDown, false);
|
||||||
|
canvas.addEventListener("mouseup", mouseUp, false);
|
||||||
|
canvas.addEventListener("mouseout", mouseUp, false);
|
||||||
|
canvas.addEventListener("mousemove", mouseMove, false);
|
||||||
|
|
||||||
|
whitetex = loadTexture("textures/white.png");
|
||||||
|
|
||||||
|
defaultProgram = loadShaders(defaultVS, defaultFS);
|
||||||
|
envMapProgram = loadShaders(envVS, envFS);
|
||||||
|
carPS2Program = loadShaders(carPS2VS, carPS2FS);
|
||||||
|
|
||||||
|
state.alphaRef = 0.1;
|
||||||
|
state.projectionMatrix = mat4.create();
|
||||||
|
state.viewMatrix = mat4.create();
|
||||||
|
state.worldMatrix = mat4.create();
|
||||||
|
state.envMatrix = mat4.create();
|
||||||
|
state.matColor = vec4.create();
|
||||||
|
state.surfaceProps = vec4.create();
|
||||||
|
state.ambLight = vec3.fromValues(0.4, 0.4, 0.4);
|
||||||
|
const alpha = 45;
|
||||||
|
const beta = 45;
|
||||||
|
state.lightDir = vec3.fromValues(
|
||||||
|
-Math.cos(deg2rad(beta))*Math.cos(deg2rad(alpha)),
|
||||||
|
-Math.sin(deg2rad(beta))*Math.cos(deg2rad(alpha)),
|
||||||
|
-Math.sin(deg2rad(alpha))
|
||||||
|
);
|
||||||
|
state.lightCol = vec3.fromValues(1.0, 1.0, 1.0);
|
||||||
|
|
||||||
|
|
||||||
|
AttachPlugins();
|
||||||
|
|
||||||
|
camera = RwCameraCreate();
|
||||||
|
camera.nearPlane = 0.1;
|
||||||
|
camera.farPlane = 100.0;
|
||||||
|
let frm = RwFrameCreate();
|
||||||
|
RwCameraSetFrame(camera, frm);
|
||||||
|
|
||||||
|
const fov = deg2rad(70);
|
||||||
|
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
|
||||||
|
camera.viewWindow[1] = Math.tan(fov / 2);
|
||||||
|
camera.viewWindow[0] = camera.viewWindow[1]*aspect;
|
||||||
|
|
||||||
|
envFrame = RwFrameCreate();
|
||||||
|
mat4.rotateX(envFrame.matrix, envFrame.matrix, deg2rad(60));
|
||||||
|
rwFrameSynchLTM(envFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
displayFrames(frame, parelem)
|
||||||
|
{
|
||||||
|
let li = document.createElement('li');
|
||||||
|
li.innerHTML = frame.name;
|
||||||
|
for(let i = 0; i < frame.objects.length; i++){
|
||||||
|
let o = frame.objects[i];
|
||||||
|
if(o.type == rwID_ATOMIC){
|
||||||
|
let checkbox = document.createElement('input');
|
||||||
|
checkbox.type = "checkbox";
|
||||||
|
checkbox.onclick = function() { o.visible = checkbox.checked; };
|
||||||
|
checkbox.checked = o.visible;
|
||||||
|
li.appendChild(checkbox);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parelem.appendChild(li);
|
||||||
|
if(frame.child){
|
||||||
|
let ul = document.createElement('ul');
|
||||||
|
parelem.appendChild(ul);
|
||||||
|
for(let c = frame.child; c != null; c = c.next)
|
||||||
|
displayFrames(c, ul);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
putControl()
|
||||||
|
{
|
||||||
|
// let ctl = document.getElementById('control');
|
||||||
|
// let reloadbtn = document.createElement('input');
|
||||||
|
// reloadbtn.type = "button";
|
||||||
|
// reloadbtn.value = "reload";
|
||||||
|
// reloadbtn.onclick = reload;
|
||||||
|
// ctl.appendChild(reloadbtn);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function
|
||||||
|
loadCarIII(filename)
|
||||||
|
{
|
||||||
|
loadDFF(filename, function(clump){
|
||||||
|
myclump = clump;
|
||||||
|
modelinfo = processVehicle(myclump);
|
||||||
|
setupIIICar(myclump);
|
||||||
|
setVehicleColors(modelinfo, carColors[0], carColors[1]);
|
||||||
|
main();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
loadCarVC(filename)
|
||||||
|
{
|
||||||
|
loadDFF(filename, function(clump){
|
||||||
|
myclump = clump;
|
||||||
|
modelinfo = processVehicle(myclump);
|
||||||
|
setVehicleColors(modelinfo, carColors[0], carColors[1]);
|
||||||
|
main();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
loadCarSA(filename)
|
||||||
|
{
|
||||||
|
loadDFF(filename, function(clump){
|
||||||
|
myclump = clump;
|
||||||
|
modelinfo = processVehicle(myclump);
|
||||||
|
setupSACar(myclump);
|
||||||
|
setVehicleColors(modelinfo,
|
||||||
|
carColors[0], carColors[1], carColors[2], carColors[3]);
|
||||||
|
// setVehicleLightColors(modelinfo,
|
||||||
|
// [ 128, 0, 0, 255 ],
|
||||||
|
// [ 128, 0, 0, 255 ],
|
||||||
|
// [ 128, 0, 0, 255 ],
|
||||||
|
// [ 128, 0, 0, 255 ]);
|
||||||
|
main();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
loadModel(filename)
|
||||||
|
{
|
||||||
|
loadDFF(filename, function(clump){
|
||||||
|
myclump = clump;
|
||||||
|
main();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
removeChildren(x)
|
||||||
|
{
|
||||||
|
while(x.firstChild)
|
||||||
|
x.removeChild(x.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
let ul = document.getElementById('frames');
|
||||||
|
removeChildren(ul);
|
||||||
|
displayFrames(myclump.frame, ul);
|
||||||
|
|
||||||
|
if(!running){
|
||||||
|
running = true;
|
||||||
|
|
||||||
|
putControl();
|
||||||
|
|
||||||
|
let then = 0;
|
||||||
|
function render(now){
|
||||||
|
now *= 0.001; // convert to seconds
|
||||||
|
const deltaTime = now - then;
|
||||||
|
then = now;
|
||||||
|
|
||||||
|
drawScene(deltaTime);
|
||||||
|
|
||||||
|
requestAnimationFrame(render);
|
||||||
|
}
|
||||||
|
requestAnimationFrame(render);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
setupIIICar(clump)
|
||||||
|
{
|
||||||
|
for(let i = 0; i < clump.atomics.length; i++){
|
||||||
|
let a = clump.atomics[i];
|
||||||
|
a.pipeline = matFXPipe;
|
||||||
|
for(let j = 0; j < a.geometry.materials.length; j++){
|
||||||
|
m = a.geometry.materials[j];
|
||||||
|
if(m.surfaceProperties[1] <= 0.0)
|
||||||
|
continue;
|
||||||
|
m.matfx = {
|
||||||
|
type: 2,
|
||||||
|
envCoefficient: m.surfaceProperties[1],
|
||||||
|
envTex: RwTextureRead("reflection01", "")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
setupSACar(clump)
|
||||||
|
{
|
||||||
|
for(let i = 0; i < clump.atomics.length; i++){
|
||||||
|
let a = clump.atomics[i];
|
||||||
|
a.pipeline = carPipe;
|
||||||
|
for(let j = 0; j < a.geometry.materials.length; j++){
|
||||||
|
m = a.geometry.materials[j];
|
||||||
|
m.fxFlags = 0;
|
||||||
|
if(!m.matfx || m.matfx.type != 2) continue;
|
||||||
|
|
||||||
|
if(m.matfx.envTex && m.envMap && m.envMap.shininess != 0){
|
||||||
|
m.envMap.texture = m.matfx.envTex;
|
||||||
|
if(m.envMap.texture.name[0] == 'x')
|
||||||
|
m.fxFlags |= 2;
|
||||||
|
else
|
||||||
|
m.fxFlags |= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(m.specMap && m.specMap.specularity != 0)
|
||||||
|
m.fxFlags |= 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
setVehicleColors(vehinfo, c1, c2, c3, c4)
|
||||||
|
{
|
||||||
|
for(let i = 0; i < vehinfo.firstMaterials.length; i++)
|
||||||
|
vehinfo.firstMaterials[i].color = c1;
|
||||||
|
for(let i = 0; i < vehinfo.secondMaterials.length; i++)
|
||||||
|
vehinfo.secondMaterials[i].color = c2;
|
||||||
|
for(let i = 0; i < vehinfo.thirdMaterials.length; i++)
|
||||||
|
vehinfo.thirdMaterials[i].color = c3;
|
||||||
|
for(let i = 0; i < vehinfo.fourthMaterials.length; i++)
|
||||||
|
vehinfo.fourthMaterials[i].color = c4;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
setVehicleLightColors(vehinfo, c1, c2, c3, c4)
|
||||||
|
{
|
||||||
|
for(let i = 0; i < vehinfo.firstLightMaterials.length; i++)
|
||||||
|
vehinfo.firstLightMaterials[i].color = c1;
|
||||||
|
for(let i = 0; i < vehinfo.secondLightMaterials.length; i++)
|
||||||
|
vehinfo.secondLightMaterials[i].color = c2;
|
||||||
|
for(let i = 0; i < vehinfo.thirdLightMaterials.length; i++)
|
||||||
|
vehinfo.thirdLightMaterials[i].color = c3;
|
||||||
|
for(let i = 0; i < vehinfo.fourthLightMaterials.length; i++)
|
||||||
|
vehinfo.fourthLightMaterials[i].color = c4;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
findEditableMaterials(geo, vehinfo)
|
||||||
|
{
|
||||||
|
for(let i = 0; i < geo.materials.length; i++){
|
||||||
|
m = geo.materials[i];
|
||||||
|
if(m.color[0] == 0x3C && m.color[1] == 0xFF && m.color[2] == 0)
|
||||||
|
vehinfo.firstMaterials.push(m);
|
||||||
|
else if(m.color[0] == 0xFF && m.color[1] == 0 && m.color[2] == 0xAF)
|
||||||
|
vehinfo.secondMaterials.push(m);
|
||||||
|
else if(m.color[0] == 0 && m.color[1] == 0xFF && m.color[2] == 0xFF)
|
||||||
|
vehinfo.thirdMaterials.push(m);
|
||||||
|
else if(m.color[0] == 0xFF && m.color[1] == 0x00 && m.color[2] == 0xFF)
|
||||||
|
vehinfo.fourthMaterials.push(m);
|
||||||
|
else if(m.color[0] == 0xFF && m.color[1] == 0xAF && m.color[2] == 0)
|
||||||
|
vehinfo.firstLightMaterials.push(m);
|
||||||
|
else if(m.color[0] == 0 && m.color[1] == 0xFF && m.color[2] == 0xC8)
|
||||||
|
vehinfo.secondLightMaterials.push(m);
|
||||||
|
else if(m.color[0] == 0xB9 && m.color[1] == 0xFF && m.color[2] == 0)
|
||||||
|
vehinfo.thirdLightMaterials.push(m);
|
||||||
|
else if(m.color[0] == 0xFF && m.color[1] == 0x3C && m.color[2] == 0)
|
||||||
|
vehinfo.fourthLightMaterials.push(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
processVehicle(clump)
|
||||||
|
{
|
||||||
|
let vehicleInfo = {
|
||||||
|
firstMaterials: [],
|
||||||
|
secondMaterials: [],
|
||||||
|
thirdMaterials: [],
|
||||||
|
fourthMaterials: [],
|
||||||
|
firstLightMaterials: [], // front left
|
||||||
|
secondLightMaterials: [], // front right
|
||||||
|
thirdLightMaterials: [], // back left
|
||||||
|
fourthLightMaterials: [], // back right
|
||||||
|
clump: clump
|
||||||
|
};
|
||||||
|
for(let i = 0; i < clump.atomics.length; i++){
|
||||||
|
a = clump.atomics[i];
|
||||||
|
f = a.frame;
|
||||||
|
if(f.name.endsWith("_dam") ||
|
||||||
|
f.name.endsWith("_lo") ||
|
||||||
|
f.name.endsWith("_vlo"))
|
||||||
|
a.visible = false;
|
||||||
|
findEditableMaterials(a.geometry, vehicleInfo);
|
||||||
|
}
|
||||||
|
return vehicleInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
drawScene(deltaTime)
|
||||||
|
{
|
||||||
|
// camYaw += deltaTime;
|
||||||
|
|
||||||
|
gl.clearColor(0.5, 0.5, 0.5, 1.0);
|
||||||
|
gl.clearDepth(1.0);
|
||||||
|
gl.enable(gl.DEPTH_TEST);
|
||||||
|
gl.depthFunc(gl.LEQUAL);
|
||||||
|
|
||||||
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
let x = camDist * Math.cos(camYaw)* Math.cos(camPitch);
|
||||||
|
let y = camDist * Math.sin(camYaw)* Math.cos(camPitch);
|
||||||
|
let z = camDist * Math.sin(camPitch);
|
||||||
|
RwFrameLookAt(camera.frame,
|
||||||
|
[ x, y, z ],
|
||||||
|
[ 0.0, 0.0, 0.0 ],
|
||||||
|
[ 0.0, 0.0, 1.0 ]);
|
||||||
|
rwFrameSynchLTM(camera.frame);
|
||||||
|
|
||||||
|
RwCameraBeginUpdate(camera);
|
||||||
|
|
||||||
|
RenderPass = 0;
|
||||||
|
RpClumpRender(myclump);
|
||||||
|
RenderPass = 1;
|
||||||
|
RpClumpRender(myclump);
|
||||||
|
RenderPass = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function
|
||||||
|
loadDFF(filename, cb)
|
||||||
|
{
|
||||||
|
let req = new XMLHttpRequest();
|
||||||
|
req.open("GET", ModelsDirPath + "/" + filename, true);
|
||||||
|
req.responseType = "arraybuffer";
|
||||||
|
|
||||||
|
req.onload = function(oEvent){
|
||||||
|
let arrayBuffer = req.response;
|
||||||
|
if(arrayBuffer){
|
||||||
|
stream = RwStreamCreate(arrayBuffer);
|
||||||
|
|
||||||
|
if(RwStreamFindChunk(stream, rwID_CLUMP)){
|
||||||
|
let c = RpClumpStreamRead(stream);
|
||||||
|
if(c != null)
|
||||||
|
cb(c);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
req.send(null);
|
||||||
|
}
|
556
rw.js
Normal file
556
rw.js
Normal file
@ -0,0 +1,556 @@
|
|||||||
|
// core
|
||||||
|
var rwID_STRUCT = 0x01;
|
||||||
|
var rwID_STRING = 0x02;
|
||||||
|
var rwID_EXTENSION = 0x03;
|
||||||
|
var rwID_CAMERA = 0x05;
|
||||||
|
var rwID_TEXTURE = 0x06;
|
||||||
|
var rwID_MATERIAL = 0x07;
|
||||||
|
var rwID_MATLIST = 0x08;
|
||||||
|
var rwID_FRAMELIST = 0x0E;
|
||||||
|
var rwID_GEOMETRY = 0x0F;
|
||||||
|
var rwID_CLUMP = 0x10;
|
||||||
|
var rwID_LIGHT = 0x12;
|
||||||
|
var rwID_ATOMIC = 0x14;
|
||||||
|
var rwID_GEOMETRYLIST = 0x1A;
|
||||||
|
// tk
|
||||||
|
var rwID_HANIMPLUGIN = 0x11E;
|
||||||
|
var rwID_MATERIALEFFECTSPLUGIN = 0x120;
|
||||||
|
// world
|
||||||
|
var rwID_BINMESHPLUGIN = 0x50E;
|
||||||
|
// R*
|
||||||
|
var rwID_NODENAME = 0x0253F2FE;
|
||||||
|
var rwID_ENVMAT = 0x0253F2FC;
|
||||||
|
var rwID_SPECMAT = 0x0253F2F6;
|
||||||
|
|
||||||
|
|
||||||
|
var frameTKList = {};
|
||||||
|
var textureTKList = {};
|
||||||
|
var materialTKList = {};
|
||||||
|
var geometryTKList = {};
|
||||||
|
var atomicTKList = {};
|
||||||
|
var clumpTKList = {};
|
||||||
|
|
||||||
|
/* RwFrame */
|
||||||
|
|
||||||
|
function
|
||||||
|
rwSetHierarchyRoot(frame, root)
|
||||||
|
{
|
||||||
|
frame.root = root;
|
||||||
|
for(frame = frame.child; frame != null; frame = frame.next)
|
||||||
|
rwSetHierarchyRoot(frame, root);
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
RwFrameRemoveChild(c)
|
||||||
|
{
|
||||||
|
let f = c.parent.child;
|
||||||
|
// remove as child
|
||||||
|
if(f == c)
|
||||||
|
c.parent.child = c.next;
|
||||||
|
else{
|
||||||
|
while(f.next != c)
|
||||||
|
f = f.next;
|
||||||
|
f.next = c.next;
|
||||||
|
}
|
||||||
|
// now make this the root of a new hierarchy
|
||||||
|
c.parent = null;
|
||||||
|
c.next = null;
|
||||||
|
rwSetHierarchyRoot(c, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
RwFrameAddChild(p, child)
|
||||||
|
{
|
||||||
|
if(child.parent != null)
|
||||||
|
RwFrameRemoveChild(child);
|
||||||
|
// append as child of p
|
||||||
|
if(p.child == null)
|
||||||
|
p.child = child;
|
||||||
|
else{
|
||||||
|
let c;
|
||||||
|
for(c = p.child; c.next != null; c = c.next);
|
||||||
|
c.next = child;
|
||||||
|
}
|
||||||
|
child.next = null;
|
||||||
|
|
||||||
|
child.parent = p;
|
||||||
|
rwSetHierarchyRoot(child, p.root);
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
rwFrameSynchLTM(f)
|
||||||
|
{
|
||||||
|
if(f.parent == null)
|
||||||
|
mat4.copy(f.ltm, f.matrix);
|
||||||
|
else
|
||||||
|
mat4.multiply(f.ltm, f.matrix, f.parent.ltm);
|
||||||
|
for(let c = f.child; c != null; c = c.next)
|
||||||
|
rwFrameSynchLTM(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
RwFrameLookAt(frm, pos, target, up)
|
||||||
|
{
|
||||||
|
let at = vec3.create();
|
||||||
|
vec3.subtract(at, target, pos);
|
||||||
|
vec3.normalize(at, at);
|
||||||
|
let left = vec3.create();
|
||||||
|
vec3.cross(left, up, at);
|
||||||
|
vec3.normalize(left, left);
|
||||||
|
vec3.cross(up, at, left);
|
||||||
|
m = frm.matrix;
|
||||||
|
m[0] = left[0];
|
||||||
|
m[1] = left[1];
|
||||||
|
m[2] = left[2];
|
||||||
|
m[4] = up[0];
|
||||||
|
m[5] = up[1];
|
||||||
|
m[6] = up[2];
|
||||||
|
m[8] = at[0];
|
||||||
|
m[9] = at[1];
|
||||||
|
m[10] = at[2];
|
||||||
|
m[12] = pos[0];
|
||||||
|
m[13] = pos[1];
|
||||||
|
m[14] = pos[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
RwFrameCreate()
|
||||||
|
{
|
||||||
|
let f = {
|
||||||
|
parent: null,
|
||||||
|
root: null,
|
||||||
|
child: null,
|
||||||
|
next: null,
|
||||||
|
matrix: mat4.create(),
|
||||||
|
ltm: mat4.create(),
|
||||||
|
objects: [],
|
||||||
|
name: ""
|
||||||
|
};
|
||||||
|
f.root = f;
|
||||||
|
mat4.copy(f.ltm, f.matrix);
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RwCamera */
|
||||||
|
|
||||||
|
function
|
||||||
|
RwCameraCreate()
|
||||||
|
{
|
||||||
|
cam = {
|
||||||
|
type: rwID_CAMERA,
|
||||||
|
frame: null,
|
||||||
|
viewWindow: [ 1.0, 1.0 ],
|
||||||
|
viewOffset: [ 0.0, 0.0 ],
|
||||||
|
nearPlane: [ 0.5 ],
|
||||||
|
farPlane: [ 10.0 ],
|
||||||
|
fogPlane: [ 5.0 ],
|
||||||
|
projmat: mat4.create()
|
||||||
|
};
|
||||||
|
return cam;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
RwCameraSetFrame(c, f)
|
||||||
|
{
|
||||||
|
c.frame = f;
|
||||||
|
f.objects.push(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
RwCameraBeginUpdate(cam)
|
||||||
|
{
|
||||||
|
mat4.invert(state.viewMatrix, camera.frame.ltm);
|
||||||
|
state.viewMatrix[0] = -state.viewMatrix[0];
|
||||||
|
state.viewMatrix[4] = -state.viewMatrix[4];
|
||||||
|
state.viewMatrix[8] = -state.viewMatrix[8];
|
||||||
|
state.viewMatrix[12] = -state.viewMatrix[12];
|
||||||
|
|
||||||
|
p = cam.projmat;
|
||||||
|
let xscl = 1.0/cam.viewWindow[0];
|
||||||
|
let yscl = 1.0/cam.viewWindow[1];
|
||||||
|
let zscl = 1.0/(cam.farPlane-cam.nearPlane);
|
||||||
|
|
||||||
|
p[0] = xscl;
|
||||||
|
p[1] = 0;
|
||||||
|
p[2] = 0;
|
||||||
|
p[3] = 0;
|
||||||
|
|
||||||
|
p[4] = 0;
|
||||||
|
p[5] = yscl;
|
||||||
|
p[6] = 0;
|
||||||
|
p[7] = 0;
|
||||||
|
|
||||||
|
p[8] = cam.viewOffset[0]*xscl;
|
||||||
|
p[9] = cam.viewOffset[1]*yscl;
|
||||||
|
p[12] = -p[8];
|
||||||
|
p[13] = -p[9];
|
||||||
|
|
||||||
|
p[10] = (cam.farPlane+cam.nearPlane)*zscl;
|
||||||
|
p[11] = 1.0;
|
||||||
|
p[14] = -2.0*cam.nearPlane*cam.farPlane*zscl;
|
||||||
|
p[15] = 0.0;
|
||||||
|
|
||||||
|
mat4.copy(state.projectionMatrix, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RwTexture */
|
||||||
|
|
||||||
|
function
|
||||||
|
RwTextureRead(name, mask)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
name: name,
|
||||||
|
mask: mask,
|
||||||
|
tex: loadTexture(TexturesDirPath + "/" + name + ".png")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RpMaterial */
|
||||||
|
|
||||||
|
function
|
||||||
|
RpMaterialCreate()
|
||||||
|
{
|
||||||
|
let mat = {
|
||||||
|
color: [ 255, 255, 255, 255 ],
|
||||||
|
surfaceProperties: [ 1.0, 1.0, 1.0 ]
|
||||||
|
};
|
||||||
|
return mat;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RpGeometry */
|
||||||
|
|
||||||
|
function
|
||||||
|
RpGeometryCreate(flags, numMorphTargets)
|
||||||
|
{
|
||||||
|
let geo = {
|
||||||
|
numVertices: 0, // used for instancing
|
||||||
|
triangles: [],
|
||||||
|
morphTargets: [],
|
||||||
|
materials: [],
|
||||||
|
meshtype: 0,
|
||||||
|
meshes: [],
|
||||||
|
totalMeshIndices: 0 // used for instancing
|
||||||
|
};
|
||||||
|
|
||||||
|
let numTexCoords = (flags >> 16) & 0xFF;
|
||||||
|
if(numTexCoords == 0){
|
||||||
|
if(flags & 0x04) numTexCoords = 1;
|
||||||
|
if(flags & 0x80) numTexCoords = 2;
|
||||||
|
}
|
||||||
|
geo.texCoords = [];
|
||||||
|
if(numTexCoords > 0)
|
||||||
|
while(numTexCoords--)
|
||||||
|
geo.texCoords.push([]);
|
||||||
|
|
||||||
|
if(flags & 0x08)
|
||||||
|
geo.prelit = [];
|
||||||
|
|
||||||
|
while(numMorphTargets--){
|
||||||
|
let mt = { vertices: [] };
|
||||||
|
if(flags & 0x10)
|
||||||
|
mt.normals = [];
|
||||||
|
geo.morphTargets.push(mt);
|
||||||
|
}
|
||||||
|
|
||||||
|
return geo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RpAtomic */
|
||||||
|
|
||||||
|
function
|
||||||
|
RpAtomicCreate()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
type: rwID_ATOMIC,
|
||||||
|
frame: null,
|
||||||
|
geometry: null,
|
||||||
|
visible: true,
|
||||||
|
pipeline: defaultPipe
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
RpAtomicSetFrame(a, f)
|
||||||
|
{
|
||||||
|
a.frame = f;
|
||||||
|
f.objects.push(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
RpAtomicRender(atomic)
|
||||||
|
{
|
||||||
|
if(atomic.geometry.instData === undefined)
|
||||||
|
instanceGeo(atomic.geometry);
|
||||||
|
atomic.pipeline.renderCB(atomic);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RpClump */
|
||||||
|
|
||||||
|
function
|
||||||
|
RpClumpCreate()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
type: rwID_CLUMP,
|
||||||
|
frame: null,
|
||||||
|
atomics: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function RpClumpSetFrame(c, f) { c.frame = f; }
|
||||||
|
|
||||||
|
function
|
||||||
|
RpClumpRender(clump)
|
||||||
|
{
|
||||||
|
for(let i = 0; i < clump.atomics.length; i++)
|
||||||
|
RpAtomicRender(clump.atomics[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Instancing */
|
||||||
|
|
||||||
|
function
|
||||||
|
instanceGeo(geo)
|
||||||
|
{
|
||||||
|
let header = {
|
||||||
|
prim: gl.TRIANGLES,
|
||||||
|
totalNumVertices: geo.numVertices,
|
||||||
|
totalNumIndices: geo.totalMeshIndices,
|
||||||
|
vbo: gl.createBuffer(),
|
||||||
|
ibo: gl.createBuffer(),
|
||||||
|
inst: [],
|
||||||
|
attribs: []
|
||||||
|
};
|
||||||
|
if(geo.meshtype == 1)
|
||||||
|
header.prim = gl.TRIANGLE_STRIP;
|
||||||
|
|
||||||
|
// instance indices
|
||||||
|
let buffer = new ArrayBuffer(header.totalNumIndices*2);
|
||||||
|
offset = 0;
|
||||||
|
for(let i = 0; i < geo.meshes.length; i++){
|
||||||
|
m = geo.meshes[i];
|
||||||
|
inst = {
|
||||||
|
material: m.material,
|
||||||
|
numIndices: m.indices.length,
|
||||||
|
offset: offset
|
||||||
|
};
|
||||||
|
let indices = new Uint16Array(buffer, inst.offset, inst.numIndices);
|
||||||
|
indices.set(m.indices);
|
||||||
|
offset += inst.numIndices*2;
|
||||||
|
header.inst.push(inst);
|
||||||
|
}
|
||||||
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, header.ibo);
|
||||||
|
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, buffer, gl.STATIC_DRAW);
|
||||||
|
geo.instData = header;
|
||||||
|
|
||||||
|
instanceVertices(geo);
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
instanceVertices(geo)
|
||||||
|
{
|
||||||
|
let i;
|
||||||
|
let stride = 0;
|
||||||
|
let attribs = [];
|
||||||
|
|
||||||
|
attribs.push({
|
||||||
|
index: ATTRIB_POS,
|
||||||
|
size: 3,
|
||||||
|
type: gl.FLOAT,
|
||||||
|
normalized: false,
|
||||||
|
offset: stride
|
||||||
|
});
|
||||||
|
stride += 12;
|
||||||
|
|
||||||
|
if(geo.morphTargets[0].normals){
|
||||||
|
attribs.push({
|
||||||
|
index: ATTRIB_NORMAL,
|
||||||
|
size: 3,
|
||||||
|
type: gl.FLOAT,
|
||||||
|
normalized: false,
|
||||||
|
offset: stride
|
||||||
|
});
|
||||||
|
stride += 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(geo.prelit){
|
||||||
|
attribs.push({
|
||||||
|
index: ATTRIB_COLOR,
|
||||||
|
size: 4,
|
||||||
|
type: gl.UNSIGNED_BYTE,
|
||||||
|
normalized: true,
|
||||||
|
offset: stride
|
||||||
|
});
|
||||||
|
stride += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0; i < geo.texCoords.length; i++){
|
||||||
|
attribs.push({
|
||||||
|
index: ATTRIB_TEXCOORDS0 + i,
|
||||||
|
size: 2,
|
||||||
|
type: gl.FLOAT,
|
||||||
|
normalized: false,
|
||||||
|
offset: stride
|
||||||
|
});
|
||||||
|
stride += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0; i < attribs.length; i++)
|
||||||
|
attribs[i].stride = stride;
|
||||||
|
|
||||||
|
header = geo.instData;
|
||||||
|
header.attribs = attribs;
|
||||||
|
let buffer = new ArrayBuffer(header.totalNumVertices*stride);
|
||||||
|
|
||||||
|
// instance verts
|
||||||
|
for(i = 0; attribs[i].index != ATTRIB_POS; i++);
|
||||||
|
let a = attribs[i];
|
||||||
|
instV3d(buffer, a.offset, a.stride, geo.morphTargets[0].vertices, header.totalNumVertices);
|
||||||
|
|
||||||
|
if(geo.morphTargets[0].normals){
|
||||||
|
for(i = 0; attribs[i].index != ATTRIB_NORMAL; i++);
|
||||||
|
let a = attribs[i];
|
||||||
|
instV3d(buffer, a.offset, a.stride, geo.morphTargets[0].normals, header.totalNumVertices);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(geo.prelit){
|
||||||
|
for(i = 0; attribs[i].index != ATTRIB_COLOR; i++);
|
||||||
|
let a = attribs[i];
|
||||||
|
instRGBA(buffer, a.offset, a.stride, geo.prelit, header.totalNumVertices);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0; i < geo.texCoords.length; i++){
|
||||||
|
for(j = 0; attribs[j].index != ATTRIB_TEXCOORDS0 + i; j++);
|
||||||
|
let a = attribs[j];
|
||||||
|
instV2d(buffer, a.offset, a.stride, geo.texCoords[i], header.totalNumVertices);
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.bindBuffer(gl.ARRAY_BUFFER, header.vbo);
|
||||||
|
gl.bufferData(gl.ARRAY_BUFFER, buffer, gl.STATIC_DRAW);
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
instV3d(buffer, offset, stride, src, n)
|
||||||
|
{
|
||||||
|
let view = new DataView(buffer);
|
||||||
|
let o = offset;
|
||||||
|
for(let i = 0; i < n; i++){
|
||||||
|
view.setFloat32(o+0, src[i][0], true);
|
||||||
|
view.setFloat32(o+4, src[i][1], true);
|
||||||
|
view.setFloat32(o+8, src[i][2], true);
|
||||||
|
o += stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
instV2d(buffer, offset, stride, src, n)
|
||||||
|
{
|
||||||
|
let view = new DataView(buffer);
|
||||||
|
let o = offset;
|
||||||
|
for(let i = 0; i < n; i++){
|
||||||
|
view.setFloat32(o+0, src[i][0], true);
|
||||||
|
view.setFloat32(o+4, src[i][1], true);
|
||||||
|
o += stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
instRGBA(buffer, offset, stride, src, n)
|
||||||
|
{
|
||||||
|
let view = new DataView(buffer);
|
||||||
|
let o = offset;
|
||||||
|
for(let i = 0; i < n; i++){
|
||||||
|
view.setUint8(o+0, src[i][0]);
|
||||||
|
view.setUint8(o+1, src[i][1]);
|
||||||
|
view.setUint8(o+2, src[i][2]);
|
||||||
|
view.setUint8(o+3, src[i][3]);
|
||||||
|
o += stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The Init functions are for when we
|
||||||
|
* read a json structure produced by convdff */
|
||||||
|
|
||||||
|
function
|
||||||
|
rwFrameInit(f)
|
||||||
|
{
|
||||||
|
f.parent = null;
|
||||||
|
f.root = f;
|
||||||
|
f.child = null;
|
||||||
|
f.next = null;
|
||||||
|
f.objects = [];
|
||||||
|
m = f.matrix;
|
||||||
|
f.matrix = mat4.fromValues(
|
||||||
|
m[0], m[1], m[2], 0,
|
||||||
|
m[3], m[4], m[5], 0,
|
||||||
|
m[6], m[7], m[8], 0,
|
||||||
|
m[9], m[10], m[11], 1);
|
||||||
|
f.ltm = mat4.create();
|
||||||
|
mat4.copy(f.ltm, f.matrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
rpMaterialInit(m)
|
||||||
|
{
|
||||||
|
if(m.texture)
|
||||||
|
m.texture = RwTextureRead(m.texture.name, m.texture.mask);
|
||||||
|
if(m.matfx && m.matfx.envTex)
|
||||||
|
m.matfx.envTex = RwTextureRead(m.matfx.envTex.name, m.matfx.envTex.mask);
|
||||||
|
if(m.specMat)
|
||||||
|
m.specMat.texture = RwTextureRead(m.specMat.texture, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
rpGeometryInit(g)
|
||||||
|
{
|
||||||
|
for(let i = 0; i < g.materials.length; i++){
|
||||||
|
m = g.materials[i];
|
||||||
|
rpMaterialInit(m);
|
||||||
|
}
|
||||||
|
for(let i = 0; i < g.meshes.length; i++){
|
||||||
|
g.meshes[i].material = g.materials[g.meshes[i].matId];
|
||||||
|
delete g.meshes[i].matId;
|
||||||
|
}
|
||||||
|
g.numVertices = g.morphTargets[0].vertices.length;
|
||||||
|
g.totalMeshIndices = 0;
|
||||||
|
for(let i = 0; i < g.meshes.length; i++)
|
||||||
|
g.totalMeshIndices += g.meshes[i].indices.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
rpAtomicInit(atomic)
|
||||||
|
{
|
||||||
|
atomic.type = rwID_ATOMIC;
|
||||||
|
atomic.frame = null;
|
||||||
|
atomic.visible = true;
|
||||||
|
atomic.pipeline = defaultPipe;
|
||||||
|
atomic.objects = [];
|
||||||
|
if(atomic.matfx)
|
||||||
|
atomic.pipeline = matFXPipe;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
rpClumpInit(clump)
|
||||||
|
{
|
||||||
|
for(let i = 0; i < clump.atomics.length; i++){
|
||||||
|
let a = clump.atomics[i];
|
||||||
|
let f = clump.frames[a.frame];
|
||||||
|
rpAtomicInit(a);
|
||||||
|
RpAtomicSetFrame(a, f);
|
||||||
|
|
||||||
|
rpGeometryInit(a.geometry);
|
||||||
|
instanceGeo(a.geometry)
|
||||||
|
}
|
||||||
|
clump.frame = null;
|
||||||
|
for(let i = 0; i < clump.frames.length; i++){
|
||||||
|
let f = clump.frames[i];
|
||||||
|
p = f.parent;
|
||||||
|
rwFrameInit(f);
|
||||||
|
if(p >= 0)
|
||||||
|
RwFrameAddChild(clump.frames[p], f);
|
||||||
|
else
|
||||||
|
RpClumpSetFrame(clump, f);
|
||||||
|
}
|
||||||
|
delete clump.frames;
|
||||||
|
|
||||||
|
rwFrameSynchLTM(clump.frame);
|
||||||
|
}
|
260
rwrender.js
Normal file
260
rwrender.js
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
var RenderPass = -1;
|
||||||
|
|
||||||
|
var defaultPipe = {
|
||||||
|
renderCB: defaultRenderCB
|
||||||
|
};
|
||||||
|
var matFXPipe = {
|
||||||
|
renderCB: matfxRenderCB
|
||||||
|
};
|
||||||
|
var carPipe = {
|
||||||
|
renderCB: carRenderCB
|
||||||
|
};
|
||||||
|
|
||||||
|
function
|
||||||
|
RenderThisPass(mat)
|
||||||
|
{
|
||||||
|
switch(RenderPass){
|
||||||
|
case 0: // opaque
|
||||||
|
return mat.color[3] == 255;
|
||||||
|
case 1: // transparent
|
||||||
|
return mat.color[3] != 255;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function
|
||||||
|
uploadState(proginfo)
|
||||||
|
{
|
||||||
|
gl.uniformMatrix4fv(proginfo.u.u_proj, false, state.projectionMatrix);
|
||||||
|
gl.uniformMatrix4fv(proginfo.u.u_view, false, state.viewMatrix);
|
||||||
|
gl.uniformMatrix4fv(proginfo.u.u_world, false, state.worldMatrix);
|
||||||
|
if(proginfo.u.u_env)
|
||||||
|
gl.uniformMatrix4fv(proginfo.u.u_env, false, state.envMatrix);
|
||||||
|
|
||||||
|
gl.uniform3fv(proginfo.u.u_ambLight, state.ambLight);
|
||||||
|
gl.uniform3fv(proginfo.u.u_lightDir, state.lightDir);
|
||||||
|
gl.uniform3fv(proginfo.u.u_lightCol, state.lightCol);
|
||||||
|
|
||||||
|
gl.uniform1i(proginfo.u.u_tex0, 0);
|
||||||
|
gl.uniform1i(proginfo.u.u_tex1, 1);
|
||||||
|
gl.uniform1i(proginfo.u.u_tex2, 2);
|
||||||
|
|
||||||
|
gl.uniform1f(proginfo.u.u_alphaRef, state.alphaRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
setAttributes(attribs, proginfo)
|
||||||
|
{
|
||||||
|
for(let i = 0; i < attribs.length; i++){
|
||||||
|
a = attribs[i];
|
||||||
|
if(proginfo.a[a.index] < 0)
|
||||||
|
continue;
|
||||||
|
gl.vertexAttribPointer(proginfo.a[a.index],
|
||||||
|
a.size, a.type,
|
||||||
|
a.normalized,
|
||||||
|
a.stride, a.offset);
|
||||||
|
gl.enableVertexAttribArray(proginfo.a[a.index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
resetAttributes(attribs, proginfo)
|
||||||
|
{
|
||||||
|
for(let i = 0; i < attribs.length; i++){
|
||||||
|
a = attribs[i];
|
||||||
|
if(proginfo.a[a.index] >= 0)
|
||||||
|
gl.disableVertexAttribArray(proginfo.a[a.index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
defaultRenderCB(atomic)
|
||||||
|
{
|
||||||
|
if(!atomic.visible)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mat4.copy(state.worldMatrix, atomic.frame.ltm);
|
||||||
|
|
||||||
|
let header = atomic.geometry.instData;
|
||||||
|
gl.bindBuffer(gl.ARRAY_BUFFER, header.vbo);
|
||||||
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, header.ibo);
|
||||||
|
|
||||||
|
let prg = defaultProgram;
|
||||||
|
gl.useProgram(prg.program);
|
||||||
|
|
||||||
|
setAttributes(header.attribs, prg);
|
||||||
|
uploadState(prg);
|
||||||
|
|
||||||
|
for(let i = 0; i < header.inst.length; i++){
|
||||||
|
inst = header.inst[i];
|
||||||
|
m = inst.material;
|
||||||
|
|
||||||
|
if(!RenderThisPass(m))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
gl.activeTexture(gl.TEXTURE0);
|
||||||
|
if(m.texture)
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, m.texture.tex);
|
||||||
|
else
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, whitetex);
|
||||||
|
|
||||||
|
vec4.scale(state.matColor, m.color, 1.0/255.0);
|
||||||
|
state.surfaceProps[0] = m.surfaceProperties[0];
|
||||||
|
state.surfaceProps[1] = m.surfaceProperties[1];
|
||||||
|
state.surfaceProps[2] = m.surfaceProperties[2];
|
||||||
|
|
||||||
|
gl.uniform4fv(prg.u.u_matColor, state.matColor);
|
||||||
|
gl.uniform4fv(prg.u.u_surfaceProps, state.surfaceProps);
|
||||||
|
|
||||||
|
if(m.color[3] != 255 || m.texture){
|
||||||
|
gl.enable(gl.BLEND);
|
||||||
|
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
||||||
|
}else
|
||||||
|
gl.disable(gl.BLEND);
|
||||||
|
|
||||||
|
gl.drawElements(header.prim, inst.numIndices, gl.UNSIGNED_SHORT, inst.offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
resetAttributes(header.attribs, programInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
var envMatScale = mat4.fromValues(
|
||||||
|
-0.5, 0.0, 0.0, 0.0,
|
||||||
|
0.0, -0.5, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 1.0, 0.0,
|
||||||
|
0.5, 0.5, 0.0, 1.0
|
||||||
|
);
|
||||||
|
|
||||||
|
function
|
||||||
|
matfxRenderCB(atomic)
|
||||||
|
{
|
||||||
|
if(!atomic.visible)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mat4.copy(state.worldMatrix, atomic.frame.ltm);
|
||||||
|
|
||||||
|
let tmp = mat4.create();
|
||||||
|
mat4.invert(tmp, envFrame.ltm);
|
||||||
|
mat4.multiply(tmp, tmp, state.viewMatrix);
|
||||||
|
tmp[12] = tmp[13] = tmp[14] = 0.0;
|
||||||
|
|
||||||
|
mat4.multiply(state.envMatrix, envMatScale, tmp);
|
||||||
|
|
||||||
|
let header = atomic.geometry.instData;
|
||||||
|
gl.bindBuffer(gl.ARRAY_BUFFER, header.vbo);
|
||||||
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, header.ibo);
|
||||||
|
|
||||||
|
let prg = envMapProgram;
|
||||||
|
gl.useProgram(prg.program);
|
||||||
|
|
||||||
|
setAttributes(header.attribs, prg);
|
||||||
|
uploadState(prg);
|
||||||
|
|
||||||
|
for(let i = 0; i < header.inst.length; i++){
|
||||||
|
inst = header.inst[i];
|
||||||
|
m = inst.material;
|
||||||
|
|
||||||
|
if(!RenderThisPass(m))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
gl.activeTexture(gl.TEXTURE0);
|
||||||
|
if(m.texture)
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, m.texture.tex);
|
||||||
|
else
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, whitetex);
|
||||||
|
|
||||||
|
gl.activeTexture(gl.TEXTURE1);
|
||||||
|
envcoef = 0.0;
|
||||||
|
if(m.matfx && m.matfx.envTex){
|
||||||
|
envcoef = m.matfx.envCoefficient;
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, m.matfx.envTex.tex);
|
||||||
|
}else
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, null);
|
||||||
|
|
||||||
|
vec4.scale(state.matColor, m.color, 1.0/255.0);
|
||||||
|
state.surfaceProps[0] = m.surfaceProperties[0];
|
||||||
|
state.surfaceProps[1] = envcoef;
|
||||||
|
state.surfaceProps[2] = m.surfaceProperties[2];
|
||||||
|
|
||||||
|
gl.uniform4fv(prg.u.u_matColor, state.matColor);
|
||||||
|
gl.uniform4fv(prg.u.u_surfaceProps, state.surfaceProps);
|
||||||
|
|
||||||
|
if(m.color[3] != 255 || m.texture){
|
||||||
|
gl.enable(gl.BLEND);
|
||||||
|
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
||||||
|
}else
|
||||||
|
gl.disable(gl.BLEND);
|
||||||
|
|
||||||
|
gl.drawElements(header.prim, inst.numIndices, gl.UNSIGNED_SHORT, inst.offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
resetAttributes(header.attribs, programInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
carRenderCB(atomic)
|
||||||
|
{
|
||||||
|
if(!atomic.visible)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mat4.copy(state.worldMatrix, atomic.frame.ltm);
|
||||||
|
|
||||||
|
let header = atomic.geometry.instData;
|
||||||
|
gl.bindBuffer(gl.ARRAY_BUFFER, header.vbo);
|
||||||
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, header.ibo);
|
||||||
|
|
||||||
|
let prg = carPS2Program;
|
||||||
|
gl.useProgram(prg.program);
|
||||||
|
|
||||||
|
setAttributes(header.attribs, prg);
|
||||||
|
uploadState(prg);
|
||||||
|
|
||||||
|
for(let i = 0; i < header.inst.length; i++){
|
||||||
|
inst = header.inst[i];
|
||||||
|
m = inst.material;
|
||||||
|
|
||||||
|
if(!RenderThisPass(m))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
gl.activeTexture(gl.TEXTURE0);
|
||||||
|
if(m.texture)
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, m.texture.tex);
|
||||||
|
else
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, whitetex);
|
||||||
|
|
||||||
|
let shininess = 0.0;
|
||||||
|
let specularity = 0.0;
|
||||||
|
|
||||||
|
if(m.fxFlags & 3){
|
||||||
|
shininess = m.envMap.shininess;
|
||||||
|
gl.activeTexture(gl.TEXTURE1);
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, m.envMap.texture.tex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(m.fxFlags & 4){
|
||||||
|
specularity = m.specMap.specularity;
|
||||||
|
gl.activeTexture(gl.TEXTURE2);
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, m.specMap.texture.tex);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4.scale(state.matColor, m.color, 1.0/255.0);
|
||||||
|
state.surfaceProps[0] = m.surfaceProperties[0];
|
||||||
|
state.surfaceProps[1] = specularity;
|
||||||
|
state.surfaceProps[2] = m.surfaceProperties[2];
|
||||||
|
state.surfaceProps[3] = shininess;
|
||||||
|
|
||||||
|
gl.uniform4fv(prg.u.u_matColor, state.matColor);
|
||||||
|
gl.uniform4fv(prg.u.u_surfaceProps, state.surfaceProps);
|
||||||
|
|
||||||
|
if(m.color[3] != 255 || m.texture){
|
||||||
|
gl.enable(gl.BLEND);
|
||||||
|
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
||||||
|
}else
|
||||||
|
gl.disable(gl.BLEND);
|
||||||
|
|
||||||
|
gl.drawElements(header.prim, inst.numIndices, gl.UNSIGNED_SHORT, inst.offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
resetAttributes(header.attribs, programInfo);
|
||||||
|
}
|
623
rwstream.js
Normal file
623
rwstream.js
Normal file
@ -0,0 +1,623 @@
|
|||||||
|
function
|
||||||
|
AttachPlugins()
|
||||||
|
{
|
||||||
|
frameTKList[rwID_NODENAME] = { streamRead: NodeNameStreamRead };
|
||||||
|
geometryTKList[rwID_BINMESHPLUGIN] = { streamRead: rpMeshRead };
|
||||||
|
materialTKList[rwID_MATERIALEFFECTSPLUGIN] = { streamRead: rpMatfxMaterialStreamRead };
|
||||||
|
materialTKList[rwID_ENVMAT] = { streamRead: envMatStreamRead };
|
||||||
|
materialTKList[rwID_SPECMAT] = { streamRead: specMatStreamRead };
|
||||||
|
atomicTKList[rwID_MATERIALEFFECTSPLUGIN] = { streamRead: rpMatfxAtomicStreamRead };
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
RwStreamCreate(buffer)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
buffer: buffer,
|
||||||
|
view: new DataView(buffer),
|
||||||
|
offset: 0,
|
||||||
|
eof: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
RwStreamSkip(stream, len)
|
||||||
|
{
|
||||||
|
stream.offset += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
RwStreamReadUInt8(stream)
|
||||||
|
{
|
||||||
|
if(stream.offset >= stream.buffer.byteLength){
|
||||||
|
stream.eof = true;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let v = stream.view.getUint8(stream.offset, true);
|
||||||
|
stream.offset += 1;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
RwStreamReadUInt16(stream)
|
||||||
|
{
|
||||||
|
if(stream.offset >= stream.buffer.byteLength){
|
||||||
|
stream.eof = true;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let v = stream.view.getUint16(stream.offset, true);
|
||||||
|
stream.offset += 2;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
RwStreamReadUInt32(stream)
|
||||||
|
{
|
||||||
|
if(stream.offset >= stream.buffer.byteLength){
|
||||||
|
stream.eof = true;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let v = stream.view.getUint32(stream.offset, true);
|
||||||
|
stream.offset += 4;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
RwStreamReadInt32(stream)
|
||||||
|
{
|
||||||
|
if(stream.offset >= stream.buffer.byteLength){
|
||||||
|
stream.eof = true;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let v = stream.view.getInt32(stream.offset, true);
|
||||||
|
stream.offset += 4;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
RwStreamReadReal(stream)
|
||||||
|
{
|
||||||
|
if(stream.offset >= stream.buffer.byteLength){
|
||||||
|
stream.eof = true;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let v = stream.view.getFloat32(stream.offset, true);
|
||||||
|
stream.offset += 4;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
RwStreamReadString(stream, length)
|
||||||
|
{
|
||||||
|
if(stream.offset >= stream.buffer.byteLength){
|
||||||
|
stream.eof = true;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let a = new Uint8Array(stream.buffer, stream.offset, length);
|
||||||
|
let s = "";
|
||||||
|
for(let i = 0; i < length && a[i] != 0; i++)
|
||||||
|
s += String.fromCharCode(a[i]);
|
||||||
|
stream.offset += length;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
rwStreamReadChunkHeader(stream)
|
||||||
|
{
|
||||||
|
let t = RwStreamReadUInt32(stream);
|
||||||
|
let l = RwStreamReadUInt32(stream);
|
||||||
|
let id = RwStreamReadUInt32(stream);
|
||||||
|
if(stream.eof)
|
||||||
|
return null;
|
||||||
|
let version = 0;
|
||||||
|
let build = 0;
|
||||||
|
if((id & 0xFFFF0000) == 0){
|
||||||
|
version = id<<8;
|
||||||
|
build = 0;
|
||||||
|
}else{
|
||||||
|
version = ((id>>14) & 0x3FF00) + 0x30000 |
|
||||||
|
((id>>16) & 3);
|
||||||
|
build = id & 0xFFFF;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: t,
|
||||||
|
length: l,
|
||||||
|
version: version,
|
||||||
|
build: build,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
RwStreamFindChunk(stream, type)
|
||||||
|
{
|
||||||
|
let header;
|
||||||
|
while(header = rwStreamReadChunkHeader(stream)){
|
||||||
|
if(header.type == type)
|
||||||
|
return header;
|
||||||
|
RwStreamSkip(stream, header.length);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
rwPluginRegistryReadDataChunks(tklist, stream, object)
|
||||||
|
{
|
||||||
|
let header;
|
||||||
|
if((header = RwStreamFindChunk(stream, rwID_EXTENSION)) == null)
|
||||||
|
return null;
|
||||||
|
let end = stream.offset + header.length;
|
||||||
|
while(stream.offset < end){
|
||||||
|
header = rwStreamReadChunkHeader(stream);
|
||||||
|
if(header.type in tklist && tklist[header.type].streamRead){
|
||||||
|
if(!tklist[header.type].streamRead(stream, object, header.length))
|
||||||
|
return null;
|
||||||
|
}else
|
||||||
|
RwStreamSkip(stream, header.length);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
rwStringStreamFindAndRead(stream)
|
||||||
|
{
|
||||||
|
let header;
|
||||||
|
if((header = RwStreamFindChunk(stream, rwID_STRING)) == null)
|
||||||
|
return null;
|
||||||
|
return RwStreamReadString(stream, header.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
rwFrameListStreamRead(stream)
|
||||||
|
{
|
||||||
|
let header;
|
||||||
|
if((header = RwStreamFindChunk(stream, rwID_STRUCT)) == null)
|
||||||
|
return null;
|
||||||
|
let numFrames = RwStreamReadInt32(stream);
|
||||||
|
let frames = [];
|
||||||
|
for(let i = 0; i < numFrames; i++){
|
||||||
|
let xx = RwStreamReadReal(stream);
|
||||||
|
let xy = RwStreamReadReal(stream);
|
||||||
|
let xz = RwStreamReadReal(stream);
|
||||||
|
let yx = RwStreamReadReal(stream);
|
||||||
|
let yy = RwStreamReadReal(stream);
|
||||||
|
let yz = RwStreamReadReal(stream);
|
||||||
|
let zx = RwStreamReadReal(stream);
|
||||||
|
let zy = RwStreamReadReal(stream);
|
||||||
|
let zz = RwStreamReadReal(stream);
|
||||||
|
let wx = RwStreamReadReal(stream);
|
||||||
|
let wy = RwStreamReadReal(stream);
|
||||||
|
let wz = RwStreamReadReal(stream);
|
||||||
|
|
||||||
|
frame = RwFrameCreate();
|
||||||
|
mat4.set(frame.matrix,
|
||||||
|
xx, xy, xz, 0,
|
||||||
|
yx, yy, yz, 0,
|
||||||
|
zx, zy, zz, 0,
|
||||||
|
wx, wy, wz, 1);
|
||||||
|
frames.push(frame);
|
||||||
|
let parent = RwStreamReadInt32(stream);
|
||||||
|
RwStreamReadInt32(stream); // unused
|
||||||
|
if(parent >= 0)
|
||||||
|
RwFrameAddChild(frames[parent], frame);
|
||||||
|
}
|
||||||
|
for(let i = 0; i < numFrames; i++)
|
||||||
|
if(!rwPluginRegistryReadDataChunks(frameTKList, stream, frames[i]))
|
||||||
|
return null;
|
||||||
|
return frames;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
RwTextureStreamRead(stream)
|
||||||
|
{
|
||||||
|
let header;
|
||||||
|
if((header = RwStreamFindChunk(stream, rwID_STRUCT)) == null)
|
||||||
|
return null;
|
||||||
|
let flags = RwStreamReadUInt32(stream); // we ignore this
|
||||||
|
let name = rwStringStreamFindAndRead(stream);
|
||||||
|
if(name == null) return null;
|
||||||
|
let mask = rwStringStreamFindAndRead(stream);
|
||||||
|
if(mask == null) return null;
|
||||||
|
let tex = RwTextureRead(name, mask);
|
||||||
|
if(!rwPluginRegistryReadDataChunks(textureTKList, stream, tex))
|
||||||
|
return null;
|
||||||
|
return tex;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
RpMaterialStreamRead(stream)
|
||||||
|
{
|
||||||
|
let header;
|
||||||
|
if((header = RwStreamFindChunk(stream, rwID_STRUCT)) == null)
|
||||||
|
return null;
|
||||||
|
let mat = RpMaterialCreate();
|
||||||
|
RwStreamReadInt32(stream); // flags, unused
|
||||||
|
mat.color[0] = RwStreamReadUInt8(stream);
|
||||||
|
mat.color[1] = RwStreamReadUInt8(stream);
|
||||||
|
mat.color[2] = RwStreamReadUInt8(stream);
|
||||||
|
mat.color[3] = RwStreamReadUInt8(stream);
|
||||||
|
RwStreamReadInt32(stream); // unused
|
||||||
|
let textured = RwStreamReadInt32(stream);
|
||||||
|
mat.surfaceProperties[0] = RwStreamReadReal(stream);
|
||||||
|
mat.surfaceProperties[1] = RwStreamReadReal(stream);
|
||||||
|
mat.surfaceProperties[2] = RwStreamReadReal(stream);
|
||||||
|
if(textured){
|
||||||
|
if((header = RwStreamFindChunk(stream, rwID_TEXTURE)) == null)
|
||||||
|
return null;
|
||||||
|
mat.texture = RwTextureStreamRead(stream);
|
||||||
|
if(mat.texture == null)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if(!rwPluginRegistryReadDataChunks(materialTKList, stream, mat))
|
||||||
|
return null;
|
||||||
|
return mat;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
rpMaterialListStreamRead(stream)
|
||||||
|
{
|
||||||
|
let header;
|
||||||
|
if((header = RwStreamFindChunk(stream, rwID_STRUCT)) == null)
|
||||||
|
return null;
|
||||||
|
let numMaterials = RwStreamReadInt32(stream);
|
||||||
|
let indices = [];
|
||||||
|
while(numMaterials--)
|
||||||
|
indices.push(RwStreamReadInt32(stream));
|
||||||
|
let materials = []
|
||||||
|
for(let i = 0; i < indices.length; i++){
|
||||||
|
if(indices[i] >= 0)
|
||||||
|
materials.push(materials[indices[i]]);
|
||||||
|
else{
|
||||||
|
if((header = RwStreamFindChunk(stream, rwID_MATERIAL)) == null)
|
||||||
|
return null;
|
||||||
|
let m = RpMaterialStreamRead(stream);
|
||||||
|
if(m == null)
|
||||||
|
return null;
|
||||||
|
materials.push(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return materials;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
RpGeometryStreamRead(stream)
|
||||||
|
{
|
||||||
|
let header;
|
||||||
|
if((header = RwStreamFindChunk(stream, rwID_STRUCT)) == null)
|
||||||
|
return null;
|
||||||
|
let flags = RwStreamReadUInt32(stream);
|
||||||
|
let numTriangles = RwStreamReadInt32(stream);
|
||||||
|
let numVertices = RwStreamReadInt32(stream);
|
||||||
|
let numMorphTargets = RwStreamReadInt32(stream);
|
||||||
|
if(header.version < 0x34000)
|
||||||
|
RwStreamSkip(stream, 12);
|
||||||
|
if(flags & 0x01000000) return null; // native geometry not supported
|
||||||
|
|
||||||
|
let geo = RpGeometryCreate(flags, numMorphTargets);
|
||||||
|
geo.numVertices = numVertices;
|
||||||
|
|
||||||
|
if(geo.prelit)
|
||||||
|
for(let i = 0; i < numVertices; i++){
|
||||||
|
let r = RwStreamReadUInt8(stream);
|
||||||
|
let g = RwStreamReadUInt8(stream);
|
||||||
|
let b = RwStreamReadUInt8(stream);
|
||||||
|
let a = RwStreamReadUInt8(stream);
|
||||||
|
geo.prelit.push([r, g, b, a]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(let i = 0; i < geo.texCoords.length; i++){
|
||||||
|
let texCoords = geo.texCoords[i];
|
||||||
|
for(let j = 0; j < numVertices; j++){
|
||||||
|
let u = RwStreamReadReal(stream);
|
||||||
|
let v = RwStreamReadReal(stream);
|
||||||
|
texCoords.push([u, v]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(let i = 0; i < numTriangles; i++){
|
||||||
|
let w1 = RwStreamReadUInt32(stream);
|
||||||
|
let w2 = RwStreamReadUInt32(stream);
|
||||||
|
let v1 = w1>>16 & 0xFFFF;
|
||||||
|
let v2 = w1 & 0xFFFF;
|
||||||
|
let v3 = w2>>16 & 0xFFFF;
|
||||||
|
let matid = w2 & 0xFFFF;
|
||||||
|
geo.triangles.push([v1, v2, v3, matid]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(let i = 0; i < numMorphTargets; i++){
|
||||||
|
let mt = geo.morphTargets[i];
|
||||||
|
|
||||||
|
RwStreamSkip(stream, 4*4 + 4 + 4); // ignore bounding sphere and flags
|
||||||
|
for(let j = 0; j < numVertices; j++){
|
||||||
|
let x = RwStreamReadReal(stream);
|
||||||
|
let y = RwStreamReadReal(stream);
|
||||||
|
let z = RwStreamReadReal(stream);
|
||||||
|
mt.vertices.push([x, y, z]);
|
||||||
|
}
|
||||||
|
if(mt.normals)
|
||||||
|
for(let j = 0; j < numVertices; j++){
|
||||||
|
let x = RwStreamReadReal(stream);
|
||||||
|
let y = RwStreamReadReal(stream);
|
||||||
|
let z = RwStreamReadReal(stream);
|
||||||
|
mt.normals.push([x, y, z]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if((header = RwStreamFindChunk(stream, rwID_MATLIST)) == null)
|
||||||
|
return null;
|
||||||
|
geo.materials = rpMaterialListStreamRead(stream);
|
||||||
|
if(geo.materials == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if(!rwPluginRegistryReadDataChunks(geometryTKList, stream, geo))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return geo;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
rpMeshRead(stream, geo, length)
|
||||||
|
{
|
||||||
|
geo.meshtype = RwStreamReadInt32(stream);
|
||||||
|
let numMeshes = RwStreamReadInt32(stream);
|
||||||
|
geo.totalMeshIndices = RwStreamReadInt32(stream);
|
||||||
|
while(numMeshes--){
|
||||||
|
let numIndices = RwStreamReadInt32(stream);
|
||||||
|
let matid = RwStreamReadInt32(stream);
|
||||||
|
let m = {
|
||||||
|
indices: [],
|
||||||
|
material: geo.materials[matid]
|
||||||
|
};
|
||||||
|
while(numIndices--)
|
||||||
|
m.indices.push(RwStreamReadInt32(stream));
|
||||||
|
geo.meshes.push(m);
|
||||||
|
}
|
||||||
|
return geo;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
rpGeometryListStreamRead(stream)
|
||||||
|
{
|
||||||
|
let header;
|
||||||
|
if((header = RwStreamFindChunk(stream, rwID_STRUCT)) == null)
|
||||||
|
return null;
|
||||||
|
let numGeoms = RwStreamReadInt32(stream);
|
||||||
|
let geoms = []
|
||||||
|
while(numGeoms--){
|
||||||
|
if((header = RwStreamFindChunk(stream, rwID_GEOMETRY)) == null)
|
||||||
|
return null;
|
||||||
|
let g = RpGeometryStreamRead(stream);
|
||||||
|
if(g == null)
|
||||||
|
return null;
|
||||||
|
geoms.push(g);
|
||||||
|
}
|
||||||
|
return geoms;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
rpClumpAtomicStreamRead(stream, frames, geos)
|
||||||
|
{
|
||||||
|
let header;
|
||||||
|
if((header = RwStreamFindChunk(stream, rwID_STRUCT)) == null)
|
||||||
|
return null;
|
||||||
|
let atomic = RpAtomicCreate();
|
||||||
|
let frame = RwStreamReadInt32(stream);
|
||||||
|
let geometry = RwStreamReadInt32(stream);
|
||||||
|
let flags = RwStreamReadInt32(stream); // ignored
|
||||||
|
RwStreamReadInt32(stream); // unused
|
||||||
|
RpAtomicSetFrame(atomic, frames[frame]);
|
||||||
|
atomic.geometry = geos[geometry];
|
||||||
|
|
||||||
|
if(!rwPluginRegistryReadDataChunks(atomicTKList, stream, atomic))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return atomic;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
RpClumpStreamRead(stream)
|
||||||
|
{
|
||||||
|
let header;
|
||||||
|
if((header = RwStreamFindChunk(stream, rwID_STRUCT)) == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
let numAtomics = RwStreamReadInt32(stream);
|
||||||
|
let numLights = 0;
|
||||||
|
let numCameras = 0;
|
||||||
|
if(header.version > 0x33000){
|
||||||
|
numLights = RwStreamReadInt32(stream);
|
||||||
|
numCameras = RwStreamReadInt32(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
if((header = RwStreamFindChunk(stream, rwID_FRAMELIST)) == null)
|
||||||
|
return null;
|
||||||
|
frames = rwFrameListStreamRead(stream);
|
||||||
|
if(frames == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
clump = RpClumpCreate();
|
||||||
|
RpClumpSetFrame(clump, frames[0]);
|
||||||
|
|
||||||
|
if((header = RwStreamFindChunk(stream, rwID_GEOMETRYLIST)) == null)
|
||||||
|
return null;
|
||||||
|
geos = rpGeometryListStreamRead(stream);
|
||||||
|
if(geos == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
while(numAtomics--){
|
||||||
|
if((header = RwStreamFindChunk(stream, rwID_ATOMIC)) == null)
|
||||||
|
return null;
|
||||||
|
let a = rpClumpAtomicStreamRead(stream, frames, geos);
|
||||||
|
if(a == null)
|
||||||
|
return null;
|
||||||
|
clump.atomics.push(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!rwPluginRegistryReadDataChunks(clumpTKList, stream, clump))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
rwFrameSynchLTM(clump.frame);
|
||||||
|
|
||||||
|
// TODO? lights, cameras
|
||||||
|
|
||||||
|
return clump;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Plugins
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* MatFX */
|
||||||
|
|
||||||
|
var rpMATFXEFFECTBUMPMAP = 1;
|
||||||
|
var rpMATFXEFFECTENVMAP = 2;
|
||||||
|
var rpMATFXEFFECTBUMPENVMAP = 3;
|
||||||
|
var rpMATFXEFFECTDUAL = 4;
|
||||||
|
var rpMATFXEFFECTUVTRANSFORM = 5;
|
||||||
|
var rpMATFXEFFECTDUALUVTRANSFORM = 6;
|
||||||
|
|
||||||
|
function
|
||||||
|
RpMatFXMaterialSetEffects(mat, effects)
|
||||||
|
{
|
||||||
|
mat.matfx = {
|
||||||
|
type: effects,
|
||||||
|
bump: false,
|
||||||
|
env: false,
|
||||||
|
dual: false,
|
||||||
|
uvxform: false
|
||||||
|
};
|
||||||
|
// TODO: init the relevant fields here
|
||||||
|
switch(effects){
|
||||||
|
case rpMATFXEFFECTBUMPMAP:
|
||||||
|
mat.matfx.bump = true;
|
||||||
|
break;
|
||||||
|
case rpMATFXEFFECTENVMAP:
|
||||||
|
mat.matfx.env = true;
|
||||||
|
break;
|
||||||
|
case rpMATFXEFFECTBUMPENVMAP:
|
||||||
|
mat.matfx.bump = true;
|
||||||
|
mat.matfx.env = true;
|
||||||
|
break;
|
||||||
|
case rpMATFXEFFECTDUAL:
|
||||||
|
mat.matfx.dual = true;
|
||||||
|
break;
|
||||||
|
case rpMATFXEFFECTUVTRANSFORM:
|
||||||
|
mat.matfx.uvxform = true;
|
||||||
|
break;
|
||||||
|
case rpMATFXEFFECTDUALUVTRANSFORM:
|
||||||
|
mat.matfx.dual = true;
|
||||||
|
mat.matfx.uvxform = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
rpMatfxMaterialStreamRead(stream, mat, length)
|
||||||
|
{
|
||||||
|
let header;
|
||||||
|
|
||||||
|
let effects = RwStreamReadInt32(stream);
|
||||||
|
RpMatFXMaterialSetEffects(mat, effects);
|
||||||
|
let mfx = mat.matfx;
|
||||||
|
|
||||||
|
for(let i = 0; i < 2; i++){
|
||||||
|
let type = RwStreamReadInt32(stream);
|
||||||
|
switch(type){
|
||||||
|
case rpMATFXEFFECTBUMPMAP:
|
||||||
|
mfx.bumpCoefficient = RwStreamReadReal(stream);
|
||||||
|
if(RwStreamReadInt32(stream)){
|
||||||
|
if((header = RwStreamFindChunk(stream, rwID_TEXTURE)) == null)
|
||||||
|
return null;
|
||||||
|
mfx.bumpedTex = RwTextureStreamRead(stream);
|
||||||
|
if(mfx.bumpedTex == null)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if(RwStreamReadInt32(stream)){
|
||||||
|
if((header = RwStreamFindChunk(stream, rwID_TEXTURE)) == null)
|
||||||
|
return null;
|
||||||
|
mfx.bumpTex = RwTextureStreamRead(stream);
|
||||||
|
if(mfx.bumpTex == null)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case rpMATFXEFFECTENVMAP:
|
||||||
|
mfx.envCoefficient = RwStreamReadReal(stream);
|
||||||
|
mfx.envFBalpha = RwStreamReadInt32(stream);
|
||||||
|
if(RwStreamReadInt32(stream)){
|
||||||
|
if((header = RwStreamFindChunk(stream, rwID_TEXTURE)) == null)
|
||||||
|
return null;
|
||||||
|
mfx.envTex = RwTextureStreamRead(stream);
|
||||||
|
if(mfx.envTex == null)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case rpMATFXEFFECTDUAL:
|
||||||
|
mfs.srcBlend = RwStreamReadInt32(stream);
|
||||||
|
mfs.dstBlend = RwStreamReadInt32(stream);
|
||||||
|
if(RwStreamReadInt32(stream)){
|
||||||
|
if((header = RwStreamFindChunk(stream, rwID_TEXTURE)) == null)
|
||||||
|
return null;
|
||||||
|
mfx.dualTex = RwTextureStreamRead(stream);
|
||||||
|
if(mfx.dualTex == null)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mat;
|
||||||
|
}
|
||||||
|
|
||||||
|
function
|
||||||
|
rpMatfxAtomicStreamRead(stream, atomic, length)
|
||||||
|
{
|
||||||
|
atomic.matfx = RwStreamReadInt32(stream);
|
||||||
|
if(atomic.matfx)
|
||||||
|
atomic.pipeline = matFXPipe;
|
||||||
|
return atomic;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* GTA Node Name */
|
||||||
|
|
||||||
|
function
|
||||||
|
NodeNameStreamRead(stream, frame, length)
|
||||||
|
{
|
||||||
|
frame.name = RwStreamReadString(stream, length);
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GTA Env Map */
|
||||||
|
|
||||||
|
function
|
||||||
|
envMatStreamRead(stream, mat, length)
|
||||||
|
{
|
||||||
|
let sclX = RwStreamReadReal(stream);
|
||||||
|
let sclY = RwStreamReadReal(stream);
|
||||||
|
let transSclX = RwStreamReadReal(stream);
|
||||||
|
let transSclY = RwStreamReadReal(stream);
|
||||||
|
let shininess = RwStreamReadReal(stream);
|
||||||
|
RwStreamReadInt32(stream); // ignore
|
||||||
|
|
||||||
|
mat.envMap = {
|
||||||
|
scale: [ sclX, sclY ],
|
||||||
|
transScale: [ transSclX, transSclY ],
|
||||||
|
shininess: shininess
|
||||||
|
};
|
||||||
|
return mat;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GTA Spec Map */
|
||||||
|
|
||||||
|
function
|
||||||
|
specMatStreamRead(stream, mat, length)
|
||||||
|
{
|
||||||
|
let specularity = RwStreamReadReal(stream);
|
||||||
|
let texname = RwStreamReadString(stream, 24);
|
||||||
|
|
||||||
|
mat.specMap = {
|
||||||
|
specularity: specularity,
|
||||||
|
texture: RwTextureRead(texname, "")
|
||||||
|
};
|
||||||
|
return mat;
|
||||||
|
}
|
189
shaders.js
Normal file
189
shaders.js
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
const defaultVS = `
|
||||||
|
attribute vec3 in_pos;
|
||||||
|
attribute vec3 in_normal;
|
||||||
|
attribute vec4 in_color;
|
||||||
|
attribute vec2 in_tex0;
|
||||||
|
|
||||||
|
uniform mat4 u_world;
|
||||||
|
uniform mat4 u_view;
|
||||||
|
uniform mat4 u_proj;
|
||||||
|
|
||||||
|
uniform vec4 u_matColor;
|
||||||
|
uniform vec4 u_surfaceProps;
|
||||||
|
|
||||||
|
uniform vec3 u_ambLight;
|
||||||
|
uniform vec3 u_lightDir;
|
||||||
|
uniform vec3 u_lightCol;
|
||||||
|
|
||||||
|
varying highp vec4 v_color;
|
||||||
|
varying highp vec2 v_tex0;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = u_proj * u_view * u_world * vec4(in_pos, 1.0);
|
||||||
|
v_tex0 = in_tex0;
|
||||||
|
|
||||||
|
v_color = in_color;
|
||||||
|
|
||||||
|
v_color.rgb += u_ambLight*u_surfaceProps.x;
|
||||||
|
vec3 N = mat3(u_world) * in_normal;
|
||||||
|
float L = max(0.0, dot(N, -normalize(u_lightDir)));
|
||||||
|
v_color.rgb += L*u_lightCol*u_surfaceProps.z;
|
||||||
|
v_color = clamp(v_color, 0.0, 1.0);
|
||||||
|
v_color *= u_matColor;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const defaultFS = `
|
||||||
|
uniform sampler2D tex;
|
||||||
|
|
||||||
|
uniform highp float u_alphaRef;
|
||||||
|
|
||||||
|
varying highp vec4 v_color;
|
||||||
|
varying highp vec2 v_tex0;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_FragColor = v_color*texture2D(tex, v_tex0);
|
||||||
|
if(gl_FragColor.a < u_alphaRef)
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
|
||||||
|
const envVS = `
|
||||||
|
attribute vec3 in_pos;
|
||||||
|
attribute vec3 in_normal;
|
||||||
|
attribute vec4 in_color;
|
||||||
|
attribute vec2 in_tex0;
|
||||||
|
|
||||||
|
uniform mat4 u_world;
|
||||||
|
uniform mat4 u_view;
|
||||||
|
uniform mat4 u_proj;
|
||||||
|
uniform mat4 u_env;
|
||||||
|
|
||||||
|
uniform vec4 u_matColor;
|
||||||
|
uniform vec4 u_surfaceProps;
|
||||||
|
|
||||||
|
uniform vec3 u_ambLight;
|
||||||
|
uniform vec3 u_lightDir;
|
||||||
|
uniform vec3 u_lightCol;
|
||||||
|
|
||||||
|
varying highp vec4 v_color0;
|
||||||
|
varying highp vec4 v_color1;
|
||||||
|
varying highp vec2 v_tex0;
|
||||||
|
varying highp vec2 v_tex1;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = u_proj * u_view * u_world * vec4(in_pos, 1.0);
|
||||||
|
v_tex0 = in_tex0;
|
||||||
|
|
||||||
|
v_color0 = in_color;
|
||||||
|
|
||||||
|
v_color0.rgb += u_ambLight*u_surfaceProps.x;
|
||||||
|
vec3 N = mat3(u_world) * in_normal;
|
||||||
|
float L = max(0.0, dot(N, -normalize(u_lightDir)));
|
||||||
|
v_color0.rgb += L*u_lightCol*u_surfaceProps.z;
|
||||||
|
v_color0 = clamp(v_color0, 0.0, 1.0);
|
||||||
|
v_color0 *= u_matColor;
|
||||||
|
|
||||||
|
v_color1 = v_color0*u_surfaceProps.y;
|
||||||
|
|
||||||
|
v_tex1 = (u_env*vec4(N, 1.0)).xy;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const envFS = `
|
||||||
|
uniform sampler2D tex0;
|
||||||
|
uniform sampler2D tex1;
|
||||||
|
|
||||||
|
uniform highp float u_alphaRef;
|
||||||
|
|
||||||
|
varying highp vec4 v_color0;
|
||||||
|
varying highp vec4 v_color1;
|
||||||
|
varying highp vec2 v_tex0;
|
||||||
|
varying highp vec2 v_tex1;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_FragColor = v_color0*texture2D(tex0, v_tex0);
|
||||||
|
if(gl_FragColor.a < u_alphaRef)
|
||||||
|
discard;
|
||||||
|
gl_FragColor.rgb += (v_color1*texture2D(tex1, v_tex1)).rgb;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
|
||||||
|
const carPS2VS = `
|
||||||
|
attribute vec3 in_pos;
|
||||||
|
attribute vec3 in_normal;
|
||||||
|
attribute vec4 in_color;
|
||||||
|
attribute vec2 in_tex0;
|
||||||
|
attribute vec2 in_tex1;
|
||||||
|
|
||||||
|
uniform mat4 u_world;
|
||||||
|
uniform mat4 u_view;
|
||||||
|
uniform mat4 u_proj;
|
||||||
|
uniform mat4 u_env;
|
||||||
|
|
||||||
|
uniform vec4 u_matColor;
|
||||||
|
uniform vec4 u_surfaceProps;
|
||||||
|
|
||||||
|
uniform vec3 u_ambLight;
|
||||||
|
uniform vec3 u_lightDir;
|
||||||
|
uniform vec3 u_lightCol;
|
||||||
|
|
||||||
|
varying highp vec4 v_color0;
|
||||||
|
varying highp vec4 v_color1;
|
||||||
|
varying highp vec4 v_color2;
|
||||||
|
varying highp vec2 v_tex0;
|
||||||
|
varying highp vec2 v_tex1;
|
||||||
|
varying highp vec2 v_tex2;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = u_proj * u_view * u_world * vec4(in_pos, 1.0);
|
||||||
|
v_tex0 = in_tex0;
|
||||||
|
|
||||||
|
v_color0 = in_color;
|
||||||
|
|
||||||
|
v_color0.rgb += u_ambLight*u_surfaceProps.x;
|
||||||
|
vec3 N = mat3(u_world) * in_normal;
|
||||||
|
float L = max(0.0, dot(N, -normalize(u_lightDir)));
|
||||||
|
v_color0.rgb += L*u_lightCol*u_surfaceProps.z;
|
||||||
|
v_color0 = clamp(v_color0, 0.0, 1.0);
|
||||||
|
v_color0 *= u_matColor;
|
||||||
|
|
||||||
|
v_tex1 = in_tex1;
|
||||||
|
v_color1 = vec4(1.5*u_surfaceProps.w);
|
||||||
|
|
||||||
|
|
||||||
|
N = mat3(u_view) * N;
|
||||||
|
vec3 D = mat3(u_view) * u_lightDir;
|
||||||
|
N = D - 2.0*N*dot(N, D);
|
||||||
|
v_tex2 = (N.xy + vec2(1.0, 1.0))/2.0;
|
||||||
|
if(N.z < 0.0)
|
||||||
|
v_color2 = vec4(0.75*u_surfaceProps.y);
|
||||||
|
else
|
||||||
|
v_color2 = vec4(0.0);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const carPS2FS = `
|
||||||
|
uniform sampler2D tex0;
|
||||||
|
uniform sampler2D tex1;
|
||||||
|
uniform sampler2D tex2;
|
||||||
|
|
||||||
|
uniform highp float u_alphaRef;
|
||||||
|
|
||||||
|
varying highp vec4 v_color0;
|
||||||
|
varying highp vec4 v_color1;
|
||||||
|
varying highp vec4 v_color2;
|
||||||
|
varying highp vec2 v_tex0;
|
||||||
|
varying highp vec2 v_tex1;
|
||||||
|
varying highp vec2 v_tex2;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_FragColor = v_color0*texture2D(tex0, v_tex0);
|
||||||
|
if(gl_FragColor.a < u_alphaRef)
|
||||||
|
discard;
|
||||||
|
gl_FragColor.rgb += (v_color1*texture2D(tex1, v_tex1)).rgb;
|
||||||
|
gl_FragColor.rgb += (v_color2*texture2D(tex2, v_tex2)).rgb;
|
||||||
|
}
|
||||||
|
`;
|
Loading…
x
Reference in New Issue
Block a user