vif/vif.js

262 lines
8.6 KiB
JavaScript

/* JVIV - JavaScript Voxel Image Viewer */
JVIV = function() {
var state = 0;
var vif_loader;
var display;
var boxels = []; // array of voxels represented by CBDL.Box(es)
var events;
var loop;
var default_font;
var debug_text;
var debug_string = '';
var help_string = "left/right to rotate, up/down to pitch, z/x to zoom, q to quit";
this.Main = function() {
viewport = CBDL.Graphics.getViewport();
display = new CBDL.Graphics.Display(document.body, new CBDL.Graphics.VideoMode(viewport.width, viewport.height, CBDL.Graphics.VM.FILL|CBDL.Graphics.VM.ABSOLUTE), CBDL.Graphics.BACKEND.DIV);
display.Init();
display.Fill(25, 75, 25);
default_font = new display.Font('courier');
debug_text = new display.String(debug_string, default_font, 9);
debug_text.setColor("#FFFFFF");
help_text = new display.String(help_string, default_font, 9);
help_text.setColor("#FFFFFF");
events = new CBDL.Event(["keydown", "keyup"], document.body);
events.addEvent(["resize"], window);
vif_loader = new VL.Loader('test.vif');
(loop = new CBDL.Loop(this, onLoop)).start();
};
var version = 0;
var type = 0;
var width = 0;
var height = 0;
var depth = 0;
var palette_size = 0;
var palettes = []; // array of palettes, ex.: palettes[1] = [0=red,1=green,2=blue,3=alpha]
var voxels = []; // array of voxels, ex.: voxels[1] = [0=x,1=y,2=z,3=palette_entry]
var scale_x = 1;
var scale_y = 1;
var camera_x = 0;
var camera_y = 0;
var current_red = 0; // for pointless background
var current_green = 0; // ^
var current_blue = 0; // ^
var current_mod = 1; // ^
function onLoop(loop_time) {
if (state == 0) { // start state
if (vif_loader.state == 1) {
display.Fill(100, 50, 50);
loadVoxel(vif_loader.raw_data);
// We're done with the vif_loader, remove it
vif_loader = null;
// create our voxels as CBDL.Box(es)
for (var i=0;i<voxels.length;i++) {
boxels[i] = new display.Box({width: 1, height: 1}, {x: 1*scale_x, y: 1*scale_y}, {x:0, y:0});
boxels[i].x = voxels[i][0];
boxels[i].y = voxels[i][1];
boxels[i].z = voxels[i][2];
palette = palettes[voxels[i][3]];
// convert our rgb byte values to a hexadecimal string
boxels[i].setColor('#'+((1 << 24) + (palette[0] << 16) + (palette[1] << 8) + palette[2]).toString(16).substr(1));
// opacity is handled from 0-1, convert our 0-255 value accordingly
boxels[i].opacity = palettes[voxels[i][3]][3]/255;
}
state = 1;
}
} else if (state == 1) { // display/control state
text_color = '#'+((1 << 24) + (255-current_green << 16) + (255-current_blue << 8) + 255-current_red).toString(16).substr(1);
debug_text.setColor(text_color);
help_text.setColor(text_color);
if (current_mod > 0) {
if (current_red < 100) {
current_red += current_mod;
} else if (current_green < 100) {
current_green += current_mod;
} else if (current_blue < 100) {
current_blue += current_mod;
} else {
current_mod *= -1
}
} else if (current_mod < 0) {
if (current_red > 0) {
current_red += current_mod;
} else if (current_green > 0) {
current_green += current_mod;
} else if (current_blue > 0) {
current_blue += current_mod;
} else {
current_mod *= -1
}
}
display.Fill(current_red, current_green, current_blue);
drawVoxels();
// handle our input events
while(event = events.pollEvent()) {
if (event.type == "keydown") {
switch(event.keyCode) {
case 81: // q
loop.stop();
/* The following line enables window.close() in Chrome and < FF2.0
FF above that require explicit user action to enable:
about:config -> dom.allow_scripts_to_close_windows=true
*/
window.open('', '_self', ''); // enables window.close() in Chrome and FF1.5 to enable FF1.5
window.close();
break;
case 37: // left
camera_x-=10;
break;
case 39: // right
camera_x+=10;
break;
case 38: // up
break;
case 40: // down
break;
default:
console.log(event.keyCode);
break;
}
} else if (event.type == "resize") {
viewport = CBDL.Graphics.getViewport();
display.setWidth(viewport.width);
display.setHeight(viewport.height);
scale_x = viewport.width/width;
scale_y = viewport.height/height;
for(var i=0;i<boxels.length;i++) {
boxels[i].setScale({x: 1*scale_x, y: 1*scale_y});
}
}
}
}
display.draw(debug_text, {x: 1, y: 1}, {x: 1, y: 1});
display.draw(help_text, {x: 1, y: 1}, {x: 1, y: 11});
return 25;
};
drawVoxels = function() {
for(var i=0;i < boxels.length;i++) {
display.draw(boxels[i], {x: 0, y: 0}, {x: boxels[i].x*scale_x+camera_x, y: boxels[i].y*scale_y});
}
};
/** void loadVoxel(byte_array)
Info:
reads in the voxel byte array and populates the following JVIV variables:
name, version, type, palette_size, palettes[], width, height, depth,
scale_x, scale_y, voxels[]
**/
loadVoxel = function(buffer) {
var offset = 0;
console.log(buffer);
name = String.fromCharCode(buffer[0], buffer[1], buffer[2]);
offset = 3;
if (name == "VIF") {
debug_string += name+":";
}
version = buffer[offset++];
debug_string += version+":";
type = buffer[offset++];
debug_string += type+" ";
var palette_size = 0;
for ( var i = 3; i >= 0; i--) {
palette_size = (palette_size * 256) + buffer[offset+i];
}
palette_size += 1; // TODO: palette size in test.vif should be changed to 00 00 00 01, NOT absolutely 0
offset += 4; // move past pallete byte range
debug_string += "p:"+palette_size+" ";
palettes = [];
for (var i=0;i<palette_size;i++) {
palettes[i] = [];
palettes[i][0] = buffer[offset++]; // red
palettes[i][1] = buffer[offset++]; // green
palettes[i][2] = buffer[offset++]; // blue
palettes[i][3] = buffer[offset++]; // alpha
console.log("palette entry("+i+"): "+palettes[i]);
}
// TODO: width, height, and depth should be prior to palettes in the .vif
width = buffer[offset++];
scale_x = display.video_mode.width/width;
height = buffer[offset++];
scale_y = display.video_mode.height/height;
depth = buffer[offset++];
debug_string += width+"x"+height+"x"+depth+" ";
var i = 0;
var voxel_count = 0;
while(offset < buffer.length) {
voxels[i] = [];
voxels[i][0] = buffer[offset++]; // x
voxels[i][1] = buffer[offset++]; // y
voxels[i][2] = buffer[offset++]; // z
voxels[i][3] = 0;
for ( var j = 3; j >= 0; j--) {
voxels[i][3] = (voxels[i][3] * 256) + buffer[offset+j]; // pallete reference
}
offset += 4; // move past pallete byte range
console.log("voxel("+i+"): "+voxels[i]);
i++;
voxel_count++;
}
debug_string += "v:"+voxel_count+" ";
debug_text.setText(debug_string);
};
}; CBDL.extend(CBDL.App, JVIV);
/* VIF Library */
var VL = VL || {};
/** void Loader(vif_file)
Properties:
state: the loading state, 0=not loaded, 1=loaded
data: the responseText of the XMLHttpRequest
raw_data: the formatted byte array - USE THIS.
Info:
Requests the specified vif_file with an asynchronous XMLHttpRequest.
The Loader's state property must be polled to test for completion
**/
VL.Loader = function(vif_file) {
this.state = 0;
this.raw_data = [];
// TODO: Chrome does not support local file requests per default - create an HTML5 FileAPI backend as well
var req = new XMLHttpRequest();
req.open('GET', vif_file, true);
req.overrideMimeType('text\/plain; charset=x-user-defined')
CBDL.addEvent(req, 'load', (function(req, scope) {
return function() {
data = this.responseText;
for(var i=0;i<data.length;i++) {
scope.raw_data[i] = data.charCodeAt(i) & 0xff;
}
scope.state = 1;
}
})(req, this));
req.send(null);
// Older iframe-based loading - does not work for non-server local file requests on non-Safari browsers
/*var iframe = document.createElement('iframe');
iframe.content = 'text\/plain; charset=x-user-defined'
iframe.style.display = 'none';
document.body.appendChild(iframe);
CBDL.addEvent(iframe, 'load', (function(iframe, scope) {
return function() {
var doc = (iframe.contentDocument || iframe.contentWindow.document || window[iframe.id].document);
var pre = doc.body.getElementsByTagName("pre");
if (typeof pre[0] === 'undefined') {
data = doc.body.innerHTML;
} else {
data = pre[0].innerHTML;
}
for(var i=0;i<data.length;i++) {
scope.raw_data[i] = data.charCodeAt(i) & 0xff;
}
scope.state = 1;
// remove self w/ "delay" to prevent resource cancelled msg in Safari
setTimeout((function(iframe) {
return function() {
document.body.removeChild(iframe);
}
})(iframe), 0);
}
})(iframe, this));
iframe.src = vif_file;*/
};