All courses now render properly. WIP items (shell, banana, fake item)
parent
e010627b5d
commit
ff21142744
|
@ -26,7 +26,7 @@
|
||||||
play.addEventListener('click', function() {
|
play.addEventListener('click', function() {
|
||||||
if (last != null) nitroAudio.instaKill(last);
|
if (last != null) nitroAudio.instaKill(last);
|
||||||
document.getElementById('seq').innerText = "Current SSEQ: "+i;
|
document.getElementById('seq').innerText = "Current SSEQ: "+i;
|
||||||
last = nitroAudio.playSound(i++, {}, 2);
|
last = nitroAudio.playSound(i++, {}, 0);
|
||||||
})
|
})
|
||||||
/*
|
/*
|
||||||
var ctx = new AudioContext();
|
var ctx = new AudioContext();
|
||||||
|
|
|
@ -18,6 +18,7 @@ window.nitroAudio = new (function() {
|
||||||
t.kill = kill;
|
t.kill = kill;
|
||||||
t.init = init;
|
t.init = init;
|
||||||
t.instaKill = instaKill;
|
t.instaKill = instaKill;
|
||||||
|
t.updateListener = updateListener;
|
||||||
|
|
||||||
t.sdat = null;
|
t.sdat = null;
|
||||||
|
|
||||||
|
@ -33,6 +34,19 @@ window.nitroAudio = new (function() {
|
||||||
t.sdat = sdat;
|
t.sdat = sdat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateListener(pos, view) {
|
||||||
|
var listener = ctx.listener;
|
||||||
|
listener.positionX.value = pos[0];
|
||||||
|
listener.positionY.value = pos[1];
|
||||||
|
listener.positionZ.value = pos[2];
|
||||||
|
listener.forwardX.value = view[8];
|
||||||
|
listener.forwardY.value = -view[9];
|
||||||
|
listener.forwardZ.value = -view[10];
|
||||||
|
listener.upX.value = view[4];
|
||||||
|
listener.upY.value = view[5];
|
||||||
|
listener.upZ.value = view[6];
|
||||||
|
}
|
||||||
|
|
||||||
function tick() {
|
function tick() {
|
||||||
for (var i=0; i<t.sounds.length; i++) {
|
for (var i=0; i<t.sounds.length; i++) {
|
||||||
var snd = t.sounds[i];
|
var snd = t.sounds[i];
|
||||||
|
@ -76,6 +90,7 @@ window.nitroAudio = new (function() {
|
||||||
output.connect(sound.gainN);
|
output.connect(sound.gainN);
|
||||||
sound.panner = output;
|
sound.panner = output;
|
||||||
|
|
||||||
|
if (sound.obj.soundProps == null) sound.obj.soundProps = obj;
|
||||||
updatePanner(sound.panner, sound.obj.soundProps);
|
updatePanner(sound.panner, sound.obj.soundProps);
|
||||||
} else {
|
} else {
|
||||||
output = ctx.createGain();
|
output = ctx.createGain();
|
||||||
|
@ -105,9 +120,10 @@ window.nitroAudio = new (function() {
|
||||||
if (panner == null || soundProps == null) return;
|
if (panner == null || soundProps == null) return;
|
||||||
if (soundProps.pos != null) panner.setPosition(soundProps.pos[0], soundProps.pos[1], soundProps.pos[2]);
|
if (soundProps.pos != null) panner.setPosition(soundProps.pos[0], soundProps.pos[1], soundProps.pos[2]);
|
||||||
//if (soundProps.vel != null) panner.setVelocity(soundProps.vel[0], soundProps.vel[1], soundProps.vel[2]);
|
//if (soundProps.vel != null) panner.setVelocity(soundProps.vel[0], soundProps.vel[1], soundProps.vel[2]);
|
||||||
if (soundProps.refDistance != null) panner.refDistance = soundProps.refDistance;
|
|
||||||
|
panner.refDistance = soundProps.refDistance || 192;
|
||||||
if (soundProps.panningModel != null) panner.panningModel = soundProps.panningModel;
|
if (soundProps.panningModel != null) panner.panningModel = soundProps.panningModel;
|
||||||
if (soundProps.rolloffFactor != null) panner.rolloffFactor = soundProps.rolloffFactor;
|
panner.rolloffFactor = soundProps.rolloffFactor || 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
})();
|
})();
|
|
@ -0,0 +1,166 @@
|
||||||
|
//
|
||||||
|
// tileFlattener.js
|
||||||
|
//--------------------
|
||||||
|
// Renders screens or cells to 2d canvas. Useful for drawing UI elements from the ROM.
|
||||||
|
// by RHY3756547
|
||||||
|
//
|
||||||
|
// includes: main.js
|
||||||
|
//
|
||||||
|
|
||||||
|
window.TileFlattener = function(palette, tiles, map) {
|
||||||
|
|
||||||
|
this.palette = palette;
|
||||||
|
this.tiles = tiles;
|
||||||
|
this.map = map;
|
||||||
|
this.toCanvas = toCanvas;
|
||||||
|
|
||||||
|
this.cellMode = map.cebk != null;
|
||||||
|
|
||||||
|
var tileCache = {};
|
||||||
|
var zero = [0, 0, 0, 0];
|
||||||
|
var emptyTile = new Uint8ClampedArray(64);
|
||||||
|
|
||||||
|
function getTileImg(pal0trans, tile, pal) {
|
||||||
|
var cacheID = tile + ":" + pal;
|
||||||
|
var item = tileCache[cacheID];
|
||||||
|
if (item != null) return item;
|
||||||
|
//make the tile
|
||||||
|
var canvas = document.createElement("canvas");
|
||||||
|
canvas.width = 8;
|
||||||
|
canvas.height = 8;
|
||||||
|
var ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
|
var d = new Uint8ClampedArray(8*8*4);
|
||||||
|
var data = new ImageData(d, 8, 8);
|
||||||
|
var targ = 0;
|
||||||
|
var colors = palette.pltt.palettes[pal] || [];
|
||||||
|
var tileData = tiles.char.tiles[tile] || emptyTile;
|
||||||
|
|
||||||
|
for (var i=0; i<64; i++) {
|
||||||
|
var colID = tileData[i];
|
||||||
|
var col = (pal0trans && colID == 0) ? zero : (colors[colID] || zero);
|
||||||
|
d[targ++] = col[0];
|
||||||
|
d[targ++] = col[1];
|
||||||
|
d[targ++] = col[2];
|
||||||
|
d[targ++] = col[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.putImageData(data, 0, 0);
|
||||||
|
tileCache[cacheID] = canvas;
|
||||||
|
return canvas;
|
||||||
|
}
|
||||||
|
|
||||||
|
function calcImageSize(image) {
|
||||||
|
var xMin = 65536;
|
||||||
|
var yMin = 65536;
|
||||||
|
var xMax = 0;
|
||||||
|
var yMax = 0;
|
||||||
|
for (var i=0; i<image.cells.length; i++) {
|
||||||
|
var cell = image.cells[i];
|
||||||
|
var size = cell.size;
|
||||||
|
var x = cell.x + size[0];
|
||||||
|
if (x > xMax) xMax = x;
|
||||||
|
x -= size[0];
|
||||||
|
if (x < xMin) xMin = x;
|
||||||
|
var y = cell.y + size[1];
|
||||||
|
if (y > yMax) yMax = y;
|
||||||
|
y -= size[1];
|
||||||
|
if (y < yMin) yMin = y;
|
||||||
|
}
|
||||||
|
return [xMin, yMin, xMax, yMax];
|
||||||
|
}
|
||||||
|
|
||||||
|
function toCanvas(pal0trans, imageInd, palInd) {
|
||||||
|
var canvas = document.createElement("canvas");
|
||||||
|
if (this.cellMode) {
|
||||||
|
//essentially a collection of ds sprites
|
||||||
|
//render out the image the user has requested
|
||||||
|
var image = map.cebk.images[imageInd];
|
||||||
|
var isize = calcImageSize(image);
|
||||||
|
|
||||||
|
canvas.width = isize[2] - isize[0];
|
||||||
|
canvas.height = isize[3] - isize[1];
|
||||||
|
var ctx = canvas.getContext("2d");
|
||||||
|
var tileWidth = this.tiles.char.tilesX;
|
||||||
|
image.cells.sort(function(a, b){return b.priority - a.priority});
|
||||||
|
|
||||||
|
for (var i=image.cells.length-1; i>=0; i--) {
|
||||||
|
var cell = image.cells[i];
|
||||||
|
|
||||||
|
var size = cell.size;
|
||||||
|
var sx2 = size[0]/2;
|
||||||
|
var sy2 = size[1]/2;
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(cell.x + sx2 - isize[0], cell.y + sy2 - isize[1]);
|
||||||
|
ctx.scale(cell.xFlip?(-1):1, cell.yFlip?(-1):1);
|
||||||
|
|
||||||
|
var tile = cell.tileOffset;
|
||||||
|
var pal = cell.pal;
|
||||||
|
ctx.strokeStyle = "white";
|
||||||
|
ctx.strokeWidth = 1;
|
||||||
|
if (cell.disable) continue;
|
||||||
|
|
||||||
|
//draw oam sprite
|
||||||
|
var base = tile;
|
||||||
|
for (var y=0; y<size[1]; y+=8) {
|
||||||
|
for (var x=0; x<size[0]; x+=8) {
|
||||||
|
var img = getTileImg(pal0trans, tile++, pal);
|
||||||
|
ctx.drawImage(img, x-sx2, y-sy2);
|
||||||
|
}
|
||||||
|
if (tileWidth != 65535) { //when defined, wrap to the next row when drawing a lower portion of the sprite
|
||||||
|
base += tileWidth;
|
||||||
|
tile = base;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//screen render, very simple
|
||||||
|
var screen = map.scrn;
|
||||||
|
canvas.width = screen.screenWidth;
|
||||||
|
canvas.height = screen.screenHeight;
|
||||||
|
var ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
|
var data = screen.data;
|
||||||
|
var tileWidth = (screen.screenWidth / 8);
|
||||||
|
var tileHeight = (screen.screenHeight / 8);
|
||||||
|
var x = 0;
|
||||||
|
var y = 0;
|
||||||
|
for (var i=0; i<data.length; i++) {
|
||||||
|
var info = data[i];
|
||||||
|
|
||||||
|
/*
|
||||||
|
Format is (YYYYXXNNNNNNNNNN)
|
||||||
|
Y4 Palette Number
|
||||||
|
X2 Transformation (YFlip/XFlip)
|
||||||
|
N10 Tile Number
|
||||||
|
*/
|
||||||
|
var pal = info >> 12;
|
||||||
|
var trans = (info >> 10) & 3;
|
||||||
|
var tile = info & 0x3FF;
|
||||||
|
|
||||||
|
var img = getTileImg(pal0trans, tile, pal);
|
||||||
|
var xf = (trans&1) > 0;
|
||||||
|
var yf = (trans&2) > 0;
|
||||||
|
|
||||||
|
if (xf || yf) {
|
||||||
|
//transform
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(x + 4, y + 4);
|
||||||
|
ctx.scale(xf?(-1):1, yf?(-1):1);
|
||||||
|
ctx.drawImage(img, -4, -4);
|
||||||
|
ctx.restore();
|
||||||
|
} else {
|
||||||
|
ctx.drawImage(img, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
x += 8;
|
||||||
|
if (x >= screen.screenWidth) {
|
||||||
|
x -= screen.screenWidth;
|
||||||
|
y += 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return canvas;
|
||||||
|
}
|
||||||
|
}
|
|
@ -99,13 +99,15 @@ window.MKDS_COLTYPE = new (function(){
|
||||||
this.STICKY = 0x0D; //sets gravity to negative this plane's normal until the object hasn't collided for a few frames.
|
this.STICKY = 0x0D; //sets gravity to negative this plane's normal until the object hasn't collided for a few frames.
|
||||||
this.SMALLJUMP = 0x0E; //choco island 2's disaster ramps
|
this.SMALLJUMP = 0x0E; //choco island 2's disaster ramps
|
||||||
this.CANNON = 0x0F; //activates cannon. basic effect id is the cannon to use.
|
this.CANNON = 0x0F; //activates cannon. basic effect id is the cannon to use.
|
||||||
this.UNKNOWN = 0x10; //it is a mystery...
|
this.WALLOOB = 0x10; //like a wall. normally only appears oob, but doesn't seem to have any special behaviour apart from maybe slowing you down more.
|
||||||
this.FALLSWATER = 0x11; //points to falls object in nkm, gets motion parameters from there.
|
this.FALLSWATER = 0x11; //points to falls object in nkm, gets motion parameters from there.
|
||||||
this.BOOST2 = 0x12;
|
this.BOOST2 = 0x12;
|
||||||
this.LOOP = 0x13; //like sticky but with boost applied. see rainbow road ds
|
this.LOOP = 0x13; //like sticky but with boost applied. see rainbow road ds
|
||||||
this.SOUNDROAD = 0x14;
|
this.SOUNDROAD = 0x14;
|
||||||
this.RR_SPECIAL_WALL = 0x15;
|
this.RR_SPECIAL_WALL = 0x15;
|
||||||
|
|
||||||
|
this.KNOCKBACK_DAMAGE = 0x1F;
|
||||||
|
|
||||||
this.GROUP_ROAD = [
|
this.GROUP_ROAD = [
|
||||||
this.ROAD, this.OFFROAD1, this.OFFROAD2, this.OFFROAD3, this.OFFROAD4, this.SLIPPERY, this.BOOST,
|
this.ROAD, this.OFFROAD1, this.OFFROAD2, this.OFFROAD3, this.OFFROAD4, this.SLIPPERY, this.BOOST,
|
||||||
this.JUMP_PAD, this.STICKY, this.SMALLJUMP, this.FALLSWATER, this.BOOST2, this.LOOP, this.SOUNDROAD,
|
this.JUMP_PAD, this.STICKY, this.SMALLJUMP, this.FALLSWATER, this.BOOST2, this.LOOP, this.SOUNDROAD,
|
||||||
|
@ -117,17 +119,23 @@ window.MKDS_COLTYPE = new (function(){
|
||||||
this.JUMP_PAD, this.STICKY, this.SMALLJUMP, this.FALLSWATER, this.BOOST2, this.LOOP, this.SOUNDROAD,
|
this.JUMP_PAD, this.STICKY, this.SMALLJUMP, this.FALLSWATER, this.BOOST2, this.LOOP, this.SOUNDROAD,
|
||||||
this.OOB, this.OFFROADMAIN,
|
this.OOB, this.OFFROADMAIN,
|
||||||
|
|
||||||
this.WALL, this.WALL2, this.RR_SPECIAL_WALL
|
this.WALL, this.WALL2, this.WALLOOB, this.RR_SPECIAL_WALL,
|
||||||
|
|
||||||
|
this.KNOCKBACK_DAMAGE
|
||||||
]
|
]
|
||||||
|
|
||||||
this.GROUP_WALL = [
|
this.GROUP_WALL = [
|
||||||
this.WALL, this.WALL2, this.RR_SPECIAL_WALL
|
this.WALL, this.WALL2, this.WALLOOB, this.RR_SPECIAL_WALL, this.KNOCKBACK_DAMAGE
|
||||||
]
|
]
|
||||||
|
|
||||||
this.GROUP_BOOST = [
|
this.GROUP_BOOST = [
|
||||||
this.BOOST, this.BOOST2, this.LOOP
|
this.BOOST, this.BOOST2, this.LOOP
|
||||||
]
|
]
|
||||||
|
|
||||||
|
this.GROUP_OOB = [
|
||||||
|
this.OOB, this.FALL
|
||||||
|
]
|
||||||
|
|
||||||
this.PHYS_MAP = new Array(31);
|
this.PHYS_MAP = new Array(31);
|
||||||
this.PHYS_MAP[this.ROAD] = 0;
|
this.PHYS_MAP[this.ROAD] = 0;
|
||||||
this.PHYS_MAP[this.OFFROAD3] = 2;
|
this.PHYS_MAP[this.OFFROAD3] = 2;
|
||||||
|
@ -194,7 +202,7 @@ window.MKDS_COLTYPE = new (function(){
|
||||||
|
|
||||||
{drift: MKDS_COLSOUNDS.DRIFT_ASPHALT, brake: MKDS_COLSOUNDS.BRAKE, land: MKDS_COLSOUNDS.LAND_GRASS, drive: MKDS_COLSOUNDS.DRIVE_GRASS, particle: 32},
|
{drift: MKDS_COLSOUNDS.DRIFT_ASPHALT, brake: MKDS_COLSOUNDS.BRAKE, land: MKDS_COLSOUNDS.LAND_GRASS, drive: MKDS_COLSOUNDS.DRIVE_GRASS, particle: 32},
|
||||||
|
|
||||||
{drift: MKDS_COLSOUNDS.DRIFT_SAND, brake: MKDS_COLSOUNDS.BRAKE_SAND, land: MKDS_COLSOUNDS.LAND_SAND, drive: MKDS_COLSOUNDS.DRIVE_SAND, particle: 28},
|
{drift: MKDS_COLSOUNDS.DRIFT_SAND, brake: MKDS_COLSOUNDS.BRAKE_SAND, land: MKDS_COLSOUNDS.LAND_SAND, drive: MKDS_COLSOUNDS.DRIVE_SAND, particle: 40}, //sky garden cloud
|
||||||
{drift: MKDS_COLSOUNDS.DRIFT_SAND, brake: MKDS_COLSOUNDS.BRAKE_SAND, land: MKDS_COLSOUNDS.LAND_SAND, drive: MKDS_COLSOUNDS.DRIVE_SAND, particle: 28},
|
{drift: MKDS_COLSOUNDS.DRIFT_SAND, brake: MKDS_COLSOUNDS.BRAKE_SAND, land: MKDS_COLSOUNDS.LAND_SAND, drive: MKDS_COLSOUNDS.DRIVE_SAND, particle: 28},
|
||||||
{drift: MKDS_COLSOUNDS.DRIFT_ASPHALT, brake: MKDS_COLSOUNDS.BRAKE, land: MKDS_COLSOUNDS.LAND_SNOW, particle:112}, //snow
|
{drift: MKDS_COLSOUNDS.DRIFT_ASPHALT, brake: MKDS_COLSOUNDS.BRAKE, land: MKDS_COLSOUNDS.LAND_SNOW, particle:112}, //snow
|
||||||
{},
|
{},
|
||||||
|
@ -263,7 +271,7 @@ window.MKDS_COLTYPE = new (function(){
|
||||||
{hit: MKDS_COLSOUNDS.HIT_ICE},
|
{hit: MKDS_COLSOUNDS.HIT_ICE},
|
||||||
],
|
],
|
||||||
|
|
||||||
0x10: //wall 3
|
0x10: //wall oob
|
||||||
[
|
[
|
||||||
{hit: MKDS_COLSOUNDS.HIT_CONCRETE},
|
{hit: MKDS_COLSOUNDS.HIT_CONCRETE},
|
||||||
{},
|
{},
|
||||||
|
|
|
@ -45,6 +45,8 @@ window.controlRaceCPU = function(nkm) {
|
||||||
var destConst;
|
var destConst;
|
||||||
var destPoint;
|
var destPoint;
|
||||||
|
|
||||||
|
var item = false;
|
||||||
|
|
||||||
function fetchInput() {
|
function fetchInput() {
|
||||||
//basically as a cpu, we're really dumb and need a constant supply of points to drive to.
|
//basically as a cpu, we're really dumb and need a constant supply of points to drive to.
|
||||||
//battle mode AI is a lot more complex, but since we're only going in one direction it can be kept simple.
|
//battle mode AI is a lot more complex, but since we're only going in one direction it can be kept simple.
|
||||||
|
@ -83,6 +85,7 @@ window.controlRaceCPU = function(nkm) {
|
||||||
offTrans += 1/240;
|
offTrans += 1/240;
|
||||||
|
|
||||||
if (offTrans >= 1) chooseNewOff();
|
if (offTrans >= 1) chooseNewOff();
|
||||||
|
item = !item;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
accel: accel, //x
|
accel: accel, //x
|
||||||
|
|
|
@ -5,6 +5,137 @@
|
||||||
// by RHY3756547
|
// by RHY3756547
|
||||||
//
|
//
|
||||||
|
|
||||||
|
window.GraphicTester = function(rom) {
|
||||||
|
|
||||||
|
findGraphicsRecursive(rom);
|
||||||
|
function listRecursive(resource, path) {
|
||||||
|
path = path || "";
|
||||||
|
var files = resource.list();
|
||||||
|
for (var i=0; i<files.length; i++) {
|
||||||
|
var file = files[i];
|
||||||
|
console.log(path + file);
|
||||||
|
if (file.toLowerCase().endsWith(".carc")) {
|
||||||
|
listRecursive(new narc(lz77.decompress(resource.getFile(file))), path + file);
|
||||||
|
}
|
||||||
|
if (file.toLowerCase().endsWith(".nftr")) {
|
||||||
|
testFont(new nftr(resource.getFile(file)), 0, path + file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function findGraphicsRecursive(resource, path) {
|
||||||
|
path = path || "";
|
||||||
|
var files = resource.list();
|
||||||
|
var pals = files.filter(x => x.toLowerCase().endsWith(".nclr"));
|
||||||
|
var graphics = files.filter(x => x.toLowerCase().endsWith(".ncgr"));
|
||||||
|
for (var i=0; i<files.length; i++) {
|
||||||
|
var file = files[i];
|
||||||
|
console.log(path + file);
|
||||||
|
if (file.toLowerCase().endsWith(".carc")) {
|
||||||
|
if (/\_..\./.exec(file) != null) continue; //a localization carc (format _us.carc). only scan the main ones. (+us)
|
||||||
|
var mainCarc = new narc(lz77.decompress(resource.getFile(file)));
|
||||||
|
var locCarc = resource.getFile(file.replace(".carc", "_us.carc"));
|
||||||
|
if (locCarc != null) {
|
||||||
|
//create a combo
|
||||||
|
mainCarc = new narcGroup([mainCarc, new narc(lz77.decompress(locCarc))]);
|
||||||
|
}
|
||||||
|
findGraphicsRecursive(mainCarc, path + file);
|
||||||
|
}
|
||||||
|
if (file.toLowerCase().endsWith(".nscr")) {
|
||||||
|
//screen
|
||||||
|
//try to find a pal
|
||||||
|
//...not a friend, like a color palette
|
||||||
|
var palFile = mostSimilarString(file, pals, "b");
|
||||||
|
var grFile = mostSimilarString(file, graphics.filter(x => !x.endsWith(".nce.ncgr")), "b");
|
||||||
|
if (palFile != null && grFile != null) {
|
||||||
|
var scr = new nscr(resource.getFile(file));
|
||||||
|
var pal = new nclr(resource.getFile(palFile));
|
||||||
|
var graphic = new ncgr(resource.getFile(grFile));
|
||||||
|
|
||||||
|
var flattener = new TileFlattener(pal, graphic, scr);
|
||||||
|
|
||||||
|
var render = flattener.toCanvas(true, 0, 0);
|
||||||
|
|
||||||
|
var split = document.createElement("h3");
|
||||||
|
split.innerText = path + file;
|
||||||
|
document.body.appendChild(split);
|
||||||
|
split = document.createElement("h4");
|
||||||
|
split.innerText = path + palFile + " " + path + grFile;
|
||||||
|
document.body.appendChild(split);
|
||||||
|
|
||||||
|
document.body.appendChild(render);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file.toLowerCase().endsWith(".ncer")) {
|
||||||
|
//cell resource
|
||||||
|
//try to find a pal
|
||||||
|
//...not a friend, like a color palette
|
||||||
|
var palFile = mostSimilarString(file, pals, "o");
|
||||||
|
var grFile = mostSimilarString(file, graphics, "o");
|
||||||
|
if (palFile != null && grFile != null) {
|
||||||
|
var cer = new ncer(resource.getFile(file));
|
||||||
|
var pal = new nclr(resource.getFile(palFile));
|
||||||
|
var graphic = new ncgr(resource.getFile(grFile));
|
||||||
|
|
||||||
|
var flattener = new TileFlattener(pal, graphic, cer);
|
||||||
|
|
||||||
|
var split = document.createElement("h3");
|
||||||
|
split.innerText = path + file;
|
||||||
|
document.body.appendChild(split);
|
||||||
|
split = document.createElement("h4");
|
||||||
|
split.innerText = path + palFile + " " + path + grFile;
|
||||||
|
document.body.appendChild(split);
|
||||||
|
|
||||||
|
//render all images
|
||||||
|
var imageCount = cer.cebk.imageCount;
|
||||||
|
for (var j=0; j<imageCount; j++) {
|
||||||
|
var render = flattener.toCanvas(true, j, 0);
|
||||||
|
document.body.appendChild(render);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mostSimilarString(text, list, pref) {
|
||||||
|
var bestString = null;
|
||||||
|
var bestScore = 0;
|
||||||
|
for (var i=0; i<list.length; i++) {
|
||||||
|
var score = startSimilarity(text, list[i], pref);
|
||||||
|
if (score > bestScore) {
|
||||||
|
bestScore = score;
|
||||||
|
bestString = list[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bestString;
|
||||||
|
}
|
||||||
|
|
||||||
|
function countStr(text, char) {
|
||||||
|
var count = 0;
|
||||||
|
for (var i=0; i<text.length; i++) {
|
||||||
|
if (text[i] == char) count++;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
function startSimilarity(text1, text2, pref) {
|
||||||
|
var min = Math.min(text1.length, text2.length);
|
||||||
|
var score = 0;
|
||||||
|
for (var i=0; i<min; i++) {
|
||||||
|
if (text1[i] != text2[i]) {
|
||||||
|
if (pref != null) {
|
||||||
|
score += countStr(text2.substr(i), pref) / 10;
|
||||||
|
}
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
score++;
|
||||||
|
}
|
||||||
|
return score; //as similar as possible
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
window.IngameRes = function(rom) {
|
window.IngameRes = function(rom) {
|
||||||
var r = this;
|
var r = this;
|
||||||
this.kartPhys = new kartphysicalparam(rom.getFile("/data/KartModelMenu/kartphysicalparam.bin"));
|
this.kartPhys = new kartphysicalparam(rom.getFile("/data/KartModelMenu/kartphysicalparam.bin"));
|
||||||
|
@ -21,6 +152,56 @@ window.IngameRes = function(rom) {
|
||||||
this.RaceEffect = new spa(r.MainEffect.getFile("RaceEffect.spa"));
|
this.RaceEffect = new spa(r.MainEffect.getFile("RaceEffect.spa"));
|
||||||
|
|
||||||
this.MainFont = new nftr(r.Main2D.getFile("marioFont.NFTR"));
|
this.MainFont = new nftr(r.Main2D.getFile("marioFont.NFTR"));
|
||||||
|
this.MFont = new nftr(r.Main2D.getFile("LC_Font_m.NFTR"));
|
||||||
|
this.SFont = new nftr(r.Main2D.getFile("LC_Font_s.NFTR"));
|
||||||
|
|
||||||
|
//testFont(this.MainFont, 0);
|
||||||
|
//testFont(this.MFont, 16*4);
|
||||||
|
//testFont(this.SFont, 32*4);
|
||||||
|
|
||||||
|
/*
|
||||||
|
var test = new GraphicTester(rom);
|
||||||
|
listRecursive(rom);
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
function testFont(font, off, name) {
|
||||||
|
var all = Object.keys(font.charMap).join("");
|
||||||
|
var split = document.createElement("h3");
|
||||||
|
split.innerText = name;
|
||||||
|
document.body.appendChild(split);
|
||||||
|
|
||||||
|
for (var i=0; i<4; i++) {
|
||||||
|
var sliceF = Math.floor((all.length * i) / 4);
|
||||||
|
var sliceT = Math.floor((all.length * (i+1)) / 4);
|
||||||
|
|
||||||
|
var canvas = font.drawToCanvas(all.substring(sliceF, sliceT), [[0, 0, 0, 0], [255, 0, 0, 255], [255, 255, 255, 255],
|
||||||
|
[32, 0, 0, 255], [64, 0, 0, 255], [96, 0, 0, 255], [128, 0, 0, 255]]);
|
||||||
|
document.body.appendChild(canvas);
|
||||||
|
//canvas.style.position = "absolute";
|
||||||
|
//canvas.style.left = 0;
|
||||||
|
//canvas.style.top = off + "px";
|
||||||
|
|
||||||
|
//off += 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function listRecursive(resource, path) {
|
||||||
|
path = path || "";
|
||||||
|
var files = resource.list();
|
||||||
|
for (var i=0; i<files.length; i++) {
|
||||||
|
var file = files[i];
|
||||||
|
console.log(path + file);
|
||||||
|
if (file.toLowerCase().endsWith(".carc")) {
|
||||||
|
listRecursive(new narc(lz77.decompress(resource.getFile(file))), path + file);
|
||||||
|
}
|
||||||
|
if (file.toLowerCase().endsWith(".nftr")) {
|
||||||
|
if (file == "/selectFont.NFTR") debugger;
|
||||||
|
testFont(new nftr(resource.getFile(file)), 0, path + file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//debugger;
|
//debugger;
|
||||||
|
|
||||||
this.getChar = getChar;
|
this.getChar = getChar;
|
||||||
|
|
|
@ -19,6 +19,8 @@ window.ItemController = function(scene) {
|
||||||
t.changeItem = changeItem;
|
t.changeItem = changeItem;
|
||||||
t.update = update;
|
t.update = update;
|
||||||
t.draw = draw;
|
t.draw = draw;
|
||||||
|
t.createItem = createItem;
|
||||||
|
t.removeItem = removeItem;
|
||||||
|
|
||||||
var RedShell, Banana, Bomb, BlueShell, Star, MultiItem, Shroom, TripleShroom, QueenShroom, Bullet, Ghost, Squid //these are all null
|
var RedShell, Banana, Bomb, BlueShell, Star, MultiItem, Shroom, TripleShroom, QueenShroom, Bullet, Ghost, Squid //these are all null
|
||||||
|
|
||||||
|
@ -47,10 +49,25 @@ window.ItemController = function(scene) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function draw(mvMatrix, pMatrix, gl) {
|
function draw(mvMatrix, pMatrix, gl) {
|
||||||
|
nitroRender.setShadBias(0.001);
|
||||||
for (var i=0; i<t.items.length; i++) {
|
for (var i=0; i<t.items.length; i++) {
|
||||||
var e = t.items[i];
|
var e = t.items[i];
|
||||||
t.items[i].draw(mvMatrix, pMatrix, gl);
|
t.items[i].draw(mvMatrix, pMatrix, gl);
|
||||||
}
|
}
|
||||||
|
nitroRender.resetShadOff();
|
||||||
|
}
|
||||||
|
|
||||||
|
function createItem(type, kart) {
|
||||||
|
var item = new Item(scene, kart, type, t.curInd++);
|
||||||
|
t.items.push(item);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeItem(item) {
|
||||||
|
var ind = t.items.indexOf(item);
|
||||||
|
if (ind !== -1) {
|
||||||
|
t.items.splice(ind, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addItem(type, ownerKart, params) {
|
function addItem(type, ownerKart, params) {
|
||||||
|
|
|
@ -14,12 +14,28 @@ window.lsc = new (function() {
|
||||||
this.sweepEllipse = sweepEllipse;
|
this.sweepEllipse = sweepEllipse;
|
||||||
this.pointInTriangle = pointInTriangle; //expose this because its kinda useful
|
this.pointInTriangle = pointInTriangle; //expose this because its kinda useful
|
||||||
|
|
||||||
function raycast(pos, dir, kclO, error, ignoreList) { //used for shells, bananas and spammable items. Much faster than sphere sweep. Error used to avoid falling through really small seams between tris.
|
var t, colPlane, colPoint, emb, edge, colO, planeNormal;
|
||||||
|
|
||||||
|
function raycast(pos, dir, scn, error, ignoreList) { //used for shells, bananas and spammable items. Much faster than sphere sweep. Error used to avoid falling through really small seams between tris.
|
||||||
var error = (error==null)?0:error;
|
var error = (error==null)?0:error;
|
||||||
var t=1;
|
t=1;
|
||||||
var tris = getTriList(pos, dir, kclO);
|
var tris = getTriList(pos, dir, scn.kcl);
|
||||||
var colPlane = null;
|
colPlane = null;
|
||||||
var colPoint = null; //can be calculated from t, but we calculate it anyway so why not include
|
colPoint = null; //can be calculated from t, but we calculate it anyway so why not include
|
||||||
|
colO = null;
|
||||||
|
|
||||||
|
rayVTris(pos, dir, tris, null, ignoreList, null, error);
|
||||||
|
|
||||||
|
for (var i=0; i<scn.colEnt.length; i++) {
|
||||||
|
var c = scn.colEnt[i];
|
||||||
|
var col = c.getCollision();
|
||||||
|
|
||||||
|
if (vec3.distance(pos, c.pos) < c.colRad) {
|
||||||
|
rayVTris(pos, dir, col.tris, col.mat, ignoreList, c, error, col.frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
for (var i=0; i<tris.length; i++) {
|
for (var i=0; i<tris.length; i++) {
|
||||||
//first, check if we intersect the plane within reasonable t.
|
//first, check if we intersect the plane within reasonable t.
|
||||||
//only if this happens do we check if the point is in the triangle.
|
//only if this happens do we check if the point is in the triangle.
|
||||||
|
@ -29,7 +45,7 @@ window.lsc = new (function() {
|
||||||
|
|
||||||
if (ignoreList.indexOf(tri) != -1) continue;
|
if (ignoreList.indexOf(tri) != -1) continue;
|
||||||
|
|
||||||
var planeConst = -vec3.dot(tri.Normal, tri.Vertex1);
|
var planeConst = -vec3.dot(tri.Normal, tri.Vertices[0]);
|
||||||
var dist = vec3.dot(tri.Normal, pos) + planeConst;
|
var dist = vec3.dot(tri.Normal, pos) + planeConst;
|
||||||
var modDir = vec3.dot(tri.Normal, dir);
|
var modDir = vec3.dot(tri.Normal, dir);
|
||||||
if (dist < 0 || modDir == 0) continue; //can't collide with back side of polygons! also can't intersect plane with ray perpendicular to plane
|
if (dist < 0 || modDir == 0) continue; //can't collide with back side of polygons! also can't intersect plane with ray perpendicular to plane
|
||||||
|
@ -44,24 +60,72 @@ window.lsc = new (function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
if (colPlane != null) {
|
if (colPlane != null) {
|
||||||
return {
|
return {
|
||||||
t: t,
|
t: t,
|
||||||
plane: colPlane,
|
plane: colPlane,
|
||||||
colPoint: colPoint,
|
colPoint: colPoint,
|
||||||
|
object: colO,
|
||||||
normal: colPlane.Normal
|
normal: colPlane.Normal
|
||||||
}
|
}
|
||||||
} else return null;
|
} else return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function rayVTris(pos, dir, tris, mat, ignoreList, targ, error, colFrame) {
|
||||||
|
for (var i=0; i<tris.length; i++) {
|
||||||
|
//first, check if we intersect the plane within reasonable t.
|
||||||
|
//only if this happens do we check if the point is in the triangle.
|
||||||
|
//we would also only do sphere sweep if this happens.
|
||||||
|
var tri = tris[i];
|
||||||
|
if (mat != null) {
|
||||||
|
if (tri.colFrame === colFrame && tri.cache) {
|
||||||
|
tri = tri.cache;
|
||||||
|
} else {
|
||||||
|
var oT = tri;
|
||||||
|
tri = modTri(tris[i], mat);
|
||||||
|
oT.cache = tri;
|
||||||
|
oT.colFrame = colFrame;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ignoreList.indexOf(tri) != -1) continue;
|
||||||
|
|
||||||
|
var planeConst = -vec3.dot(tri.Normal, tri.Vertices[0]);
|
||||||
|
var dist = vec3.dot(tri.Normal, pos) + planeConst;
|
||||||
|
var modDir = vec3.dot(tri.Normal, dir);
|
||||||
|
if (dist < 0 || modDir == 0) continue; //can't collide with back side of polygons! also can't intersect plane with ray perpendicular to plane
|
||||||
|
var newT = -dist/modDir;
|
||||||
|
if (newT>0 && newT<t) {
|
||||||
|
//we have a winner! check if the plane intersecion point is in the triangle.
|
||||||
|
var pt = vec3.add([], pos, vec3.scale([], dir, newT))
|
||||||
|
if (pointInTriangle(tri, pt, error)) {
|
||||||
|
t = newT;
|
||||||
|
colPlane = tri;
|
||||||
|
colPoint = pt; //result!
|
||||||
|
colO = targ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function transformMat3Normal(out, a, m) {
|
||||||
|
var x = a[0], y = a[1], z = a[2];
|
||||||
|
out[0] = x * m[0] + y * m[4] + z * m[8];
|
||||||
|
out[1] = x * m[1] + y * m[5] + z * m[9];
|
||||||
|
out[2] = x * m[2] + y * m[6] + z * m[10];
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
function modTri(tri, mat) {
|
function modTri(tri, mat) {
|
||||||
var obj = {};
|
var obj = {};
|
||||||
obj.Vertex1 = vec3.transformMat4([], tri.Vertex1, mat);
|
obj.Vertices = [];
|
||||||
obj.Vertex2 = vec3.transformMat4([], tri.Vertex2, mat);
|
obj.Vertices[0] = vec3.transformMat4([], tri.Vertices[0], mat);
|
||||||
obj.Vertex3 = vec3.transformMat4([], tri.Vertex3, mat);
|
obj.Vertices[1] = vec3.transformMat4([], tri.Vertices[1], mat);
|
||||||
|
obj.Vertices[2] = vec3.transformMat4([], tri.Vertices[2], mat);
|
||||||
|
|
||||||
obj.Normal = vec3.transformMat3([], tri.Normal, mat3.fromMat4([], mat));
|
obj.Normal = transformMat3Normal([], tri.Normal, mat);
|
||||||
vec3.normalize(obj.Normal, obj.Normal);
|
vec3.normalize(obj.Normal, obj.Normal);
|
||||||
obj.CollisionType = tri.CollisionType;
|
obj.CollisionType = tri.CollisionType;
|
||||||
return obj;
|
return obj;
|
||||||
|
@ -69,17 +133,16 @@ window.lsc = new (function() {
|
||||||
|
|
||||||
function scaleTri(tri, eDim) {
|
function scaleTri(tri, eDim) {
|
||||||
var obj = {};
|
var obj = {};
|
||||||
obj.Vertex1 = vec3.divide([], tri.Vertex1, eDim);
|
obj.Vertices = [];
|
||||||
obj.Vertex2 = vec3.divide([], tri.Vertex2, eDim);
|
obj.Vertices[0] = vec3.divide([], tri.Vertices[0], eDim);
|
||||||
obj.Vertex3 = vec3.divide([], tri.Vertex3, eDim);
|
obj.Vertices[1] = vec3.divide([], tri.Vertices[1], eDim);
|
||||||
|
obj.Vertices[2] = vec3.divide([], tri.Vertices[2], eDim);
|
||||||
|
|
||||||
obj.Normal = tri.Normal
|
obj.Normal = tri.Normal
|
||||||
obj.CollisionType = tri.CollisionType;
|
obj.CollisionType = tri.CollisionType;
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
var t, colPlane, colPoint, emb, edge, colO, planeNormal;
|
|
||||||
|
|
||||||
function sweepEllipse(pos, dir, scn, eDimensions, ignoreList) { //used for karts or things that need to occupy physical space.
|
function sweepEllipse(pos, dir, scn, eDimensions, ignoreList) { //used for karts or things that need to occupy physical space.
|
||||||
t=1;
|
t=1;
|
||||||
|
|
||||||
|
@ -140,7 +203,7 @@ window.lsc = new (function() {
|
||||||
if (ignoreList.indexOf(oTri) != -1) continue;
|
if (ignoreList.indexOf(oTri) != -1) continue;
|
||||||
|
|
||||||
var tri = (eDims)?scaleTri(tris[i], mat):modTri(tris[i], mat);
|
var tri = (eDims)?scaleTri(tris[i], mat):modTri(tris[i], mat);
|
||||||
var planeConst = -vec3.dot(tri.Normal, tri.Vertex1);
|
var planeConst = -vec3.dot(tri.Normal, tri.Vertices[0]);
|
||||||
var dist = vec3.dot(tri.Normal, pos) + planeConst;
|
var dist = vec3.dot(tri.Normal, pos) + planeConst;
|
||||||
var modDir = vec3.dot(tri.Normal, dir);
|
var modDir = vec3.dot(tri.Normal, dir);
|
||||||
|
|
||||||
|
@ -200,14 +263,14 @@ window.lsc = new (function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
//no inside intersection check vertices:
|
//no inside intersection check vertices:
|
||||||
for (var j=1; j<=3; j++) {
|
for (var j=0; j<=2; j++) {
|
||||||
var vert = vec3.sub([], pos, tri["Vertex"+j]);
|
var vert = vec3.sub([], pos, tri.Vertices[j]);
|
||||||
var root = getSmallestRoot(vec3.dot(dir, dir), 2*vec3.dot(dir, vert), vec3.dot(vert, vert)-1, t);
|
var root = getSmallestRoot(vec3.dot(dir, dir), 2*vec3.dot(dir, vert), vec3.dot(vert, vert)-1, t);
|
||||||
if (root != null) {
|
if (root != null) {
|
||||||
t = root;
|
t = root;
|
||||||
colPlane = oTri;
|
colPlane = oTri;
|
||||||
colO = targ;
|
colO = targ;
|
||||||
colPoint = vec3.clone(tri["Vertex"+j]); //result!
|
colPoint = vec3.clone(tri.Vertices[j]); //result!
|
||||||
planeNormal = tri.Normal;
|
planeNormal = tri.Normal;
|
||||||
edge = false;
|
edge = false;
|
||||||
}
|
}
|
||||||
|
@ -215,9 +278,9 @@ window.lsc = new (function() {
|
||||||
|
|
||||||
//... and lines
|
//... and lines
|
||||||
|
|
||||||
for (var j=1; j<=3; j++) {
|
for (var j=0; j<=2; j++) {
|
||||||
var vert = tri["Vertex"+j];
|
var vert = tri.Vertices[j];
|
||||||
var nextV = tri["Vertex"+((j%3)+1)];
|
var nextV = tri.Vertices[(j+1)%3];
|
||||||
|
|
||||||
var distVert = vec3.sub([], vert, pos);
|
var distVert = vec3.sub([], vert, pos);
|
||||||
var distLine = vec3.sub([], nextV, vert);
|
var distLine = vec3.sub([], nextV, vert);
|
||||||
|
@ -277,9 +340,9 @@ window.lsc = new (function() {
|
||||||
|
|
||||||
function pointInTriangle(tri, point, error) { //barycentric check
|
function pointInTriangle(tri, point, error) { //barycentric check
|
||||||
//compute direction vectors to the other verts and the point
|
//compute direction vectors to the other verts and the point
|
||||||
var v0 = vec3.sub([], tri.Vertex3, tri.Vertex1);
|
var v0 = vec3.sub([], tri.Vertices[2], tri.Vertices[0]);
|
||||||
var v1 = vec3.sub([], tri.Vertex2, tri.Vertex1);
|
var v1 = vec3.sub([], tri.Vertices[1], tri.Vertices[0]);
|
||||||
var v2 = vec3.sub([], point, tri.Vertex1);
|
var v2 = vec3.sub([], point, tri.Vertices[0]);
|
||||||
|
|
||||||
//we need to find u and v across the two vectors v0 and v1 such that adding them will result in our point's position
|
//we need to find u and v across the two vectors v0 and v1 such that adding them will result in our point's position
|
||||||
//where the unit length of both vectors v0 and v1 is 1, the sum of both u and v should not exceed 1 and neither should be negative
|
//where the unit length of both vectors v0 and v1 is 1, the sum of both u and v should not exceed 1 and neither should be negative
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
window.MKDSCONST = new (function() {
|
window.MKDSCONST = new (function() {
|
||||||
|
|
||||||
this.DAMAGE_SPIN = 0;
|
this.DAMAGE_SPIN = 0;
|
||||||
this.DAMAGE_FLIP = 0;
|
this.DAMAGE_FLIP = 1;
|
||||||
this.DAMAGE_EXPLODE = 0;
|
this.DAMAGE_EXPLODE = 2;
|
||||||
|
|
||||||
this.COURSEDIR = "/data/Course/";
|
this.COURSEDIR = "/data/Course/";
|
||||||
|
|
||||||
|
|
|
@ -207,6 +207,8 @@ window.courseScene = function(mainNarc, texNarc, music, chars, options, gameRes)
|
||||||
}
|
}
|
||||||
entsToRemove = [];
|
entsToRemove = [];
|
||||||
var mat = scn.camera.getView(scn, nitroRender.getViewWidth(), nitroRender.getViewHeight());
|
var mat = scn.camera.getView(scn, nitroRender.getViewWidth(), nitroRender.getViewHeight());
|
||||||
|
|
||||||
|
nitroAudio.updateListener(mat.pos, mat.mv);
|
||||||
frame++;
|
frame++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ window.fileStore = new (function(){
|
||||||
|| window.mozIndexedDB
|
|| window.mozIndexedDB
|
||||||
|| window.shimIndexedDB;
|
|| window.shimIndexedDB;
|
||||||
|
|
||||||
var request = indexedDB.open("MKJS_DB", 1);
|
var request = indexedDB.open("MKJS-DB", 1);
|
||||||
request.onerror = window.onerror;
|
request.onerror = window.onerror;
|
||||||
|
|
||||||
request.onsuccess = function(event) {
|
request.onsuccess = function(event) {
|
||||||
|
@ -40,6 +40,18 @@ window.fileStore = new (function(){
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function validateFiles() {
|
||||||
|
var transaction = db.transaction(["files"]);
|
||||||
|
var objectStore = transaction.objectStore("files");
|
||||||
|
var request = objectStore.get("mkds.nds");
|
||||||
|
request.onerror = function(event) {
|
||||||
|
alert("Fatal database error!");
|
||||||
|
};
|
||||||
|
request.onsuccess = function(event) {
|
||||||
|
if (request.result == null) alert("Locally storing files failed!");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function downloadGame(url, callback) {
|
function downloadGame(url, callback) {
|
||||||
if (typeof url == "string") {
|
if (typeof url == "string") {
|
||||||
var xml = new XMLHttpRequest();
|
var xml = new XMLHttpRequest();
|
||||||
|
@ -77,6 +89,7 @@ window.fileStore = new (function(){
|
||||||
callback(dat);
|
callback(dat);
|
||||||
};
|
};
|
||||||
request.onsuccess = function(event) {
|
request.onsuccess = function(event) {
|
||||||
|
validateFiles();
|
||||||
callback(dat);
|
callback(dat);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//
|
//
|
||||||
// bowserPlatforms.js
|
// bowserPlatforms.js
|
||||||
//--------------------
|
//--------------------
|
||||||
// Provides platforms for Bowser's Castle
|
// Provides moving platforms for Bowser's Castle and Delfino
|
||||||
// by RHY3756547
|
// by RHY3756547
|
||||||
//
|
//
|
||||||
// includes:
|
// includes:
|
||||||
|
@ -78,6 +78,133 @@ window.ObjRotaryRoom = function(obji, scene) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.ObjBridge = function(obji, scene) {
|
||||||
|
var obji = obji;
|
||||||
|
var res = [];
|
||||||
|
|
||||||
|
var t = this;
|
||||||
|
|
||||||
|
t.collidable = true;
|
||||||
|
t.colMode = 0;
|
||||||
|
t.colRad = 512;
|
||||||
|
t.getCollision = getCollision;
|
||||||
|
t.moveWith = moveWith;
|
||||||
|
|
||||||
|
t.pos = vec3.clone(obji.pos);
|
||||||
|
t.angle = vec3.clone(obji.angle);
|
||||||
|
t.scale = vec3.clone(obji.scale);
|
||||||
|
|
||||||
|
t.requireRes = requireRes;
|
||||||
|
t.provideRes = provideRes;
|
||||||
|
t.update = update;
|
||||||
|
t.draw = draw;
|
||||||
|
|
||||||
|
t.largerUpAngle = obji.setting1&0xFFFF;
|
||||||
|
t.upDuration = obji.setting1>>16;
|
||||||
|
t.statDuration = obji.setting2&0xFFFF;
|
||||||
|
t.downDuration = obji.setting2>>16;
|
||||||
|
t.upAngle = obji.setting3&0xFFFF;
|
||||||
|
t.unknown = obji.setting4>>16; //10001
|
||||||
|
|
||||||
|
t.obji = obji;
|
||||||
|
t.colFrame = 0;
|
||||||
|
|
||||||
|
var dirVel = 0;
|
||||||
|
var genCol;
|
||||||
|
|
||||||
|
var prevMat;
|
||||||
|
var curMat;
|
||||||
|
var colMat = mat4.create();
|
||||||
|
prevMat = curMat;
|
||||||
|
|
||||||
|
var anim;
|
||||||
|
var animMat;
|
||||||
|
|
||||||
|
var frame = 0;
|
||||||
|
var mode = 0; //going up, stationary, going down, stationary
|
||||||
|
|
||||||
|
function setMat() {
|
||||||
|
prevMat = curMat;
|
||||||
|
var mat = mat4.create();
|
||||||
|
mat4.translate(mat, mat, t.pos);
|
||||||
|
|
||||||
|
if (t.angle[2] != 0) mat4.rotateZ(mat, mat, t.angle[2]*(Math.PI/180));
|
||||||
|
if (t.angle[1] != 0) mat4.rotateY(mat, mat, t.angle[1]*(Math.PI/180));
|
||||||
|
if (t.angle[0] != 0) mat4.rotateX(mat, mat, t.angle[0]*(Math.PI/180));
|
||||||
|
|
||||||
|
mat4.scale(mat, mat, vec3.scale([], t.scale, 16));
|
||||||
|
mat4.scale(colMat, mat, [genCol.scale, genCol.scale, genCol.scale]);
|
||||||
|
t.colFrame++;
|
||||||
|
curMat = mat;
|
||||||
|
}
|
||||||
|
|
||||||
|
function update(scene) {
|
||||||
|
var angle = 0;
|
||||||
|
frame++;
|
||||||
|
switch (mode) {
|
||||||
|
case 0:
|
||||||
|
var p = frame / t.upDuration;
|
||||||
|
angle = (0.5 - Math.cos(p * Math.PI) / 2) * t.largerUpAngle;
|
||||||
|
if (frame >= t.upDuration) {
|
||||||
|
mode = 1;
|
||||||
|
frame = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
case 3:
|
||||||
|
angle = (mode == 1) ? t.largerUpAngle : 0;
|
||||||
|
if (frame >= t.statDuration) {
|
||||||
|
mode = (mode + 1) % 4;
|
||||||
|
frame = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
var p = 1 - frame / t.downDuration;
|
||||||
|
angle = (0.5 - Math.cos(p * Math.PI) / 2) * t.largerUpAngle;
|
||||||
|
if (frame >= t.downDuration) {
|
||||||
|
mode = 3;
|
||||||
|
frame = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
t.angle[0] = -angle;
|
||||||
|
animMat = anim.setFrame(0, 0, angle*1.5);
|
||||||
|
setMat();
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw(view, pMatrix) {
|
||||||
|
var mat = mat4.create();
|
||||||
|
mat4.mul(mat, view, curMat);
|
||||||
|
|
||||||
|
res.mdl[0].draw(mat, pMatrix, animMat);
|
||||||
|
}
|
||||||
|
|
||||||
|
function requireRes() { //scene asks what resources to load
|
||||||
|
return {mdl:[{nsbmd:"bridge.nsbmd"}], other:[null, "bridge.nsbca"]};
|
||||||
|
}
|
||||||
|
|
||||||
|
function provideRes(r) {
|
||||||
|
res = r; //...and gives them to us. :)
|
||||||
|
var inf = res.mdl[0].getCollisionModel(0, 1, 7<<8); //dash
|
||||||
|
var inf2 = res.mdl[0].getCollisionModel(0, 0, 0); //regular
|
||||||
|
anim = new nitroAnimator(r.mdl[0].bmd, r.other[1]);
|
||||||
|
|
||||||
|
genCol = {dat:JSON.parse(JSON.stringify(inf.dat.concat(inf2.dat))), scale:inf.scale};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCollision() {
|
||||||
|
return { tris: genCol.dat, mat: colMat, frame: t.colFrame };
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveWith(obj) { //used for collidable objects that move.
|
||||||
|
//the most general way to move something with an object is to multiply its position by the inverse mv matrix of that object, and then the new mv matrix.
|
||||||
|
vec3.transformMat4(obj.pos, obj.pos, mat4.invert([], prevMat))
|
||||||
|
vec3.transformMat4(obj.pos, obj.pos, curMat)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
window.ObjRoutePlatform = function(obji, scene) {
|
window.ObjRoutePlatform = function(obji, scene) {
|
||||||
var obji = obji;
|
var obji = obji;
|
||||||
var res = [];
|
var res = [];
|
||||||
|
@ -111,12 +238,14 @@ window.ObjRoutePlatform = function(obji, scene) {
|
||||||
t.elapsedTime = 0;
|
t.elapsedTime = 0;
|
||||||
|
|
||||||
t.mode = 0;
|
t.mode = 0;
|
||||||
|
t.colFrame = 0;
|
||||||
|
|
||||||
var movVel;
|
var movVel;
|
||||||
|
|
||||||
//t.speed = (obji.setting1&0xFFFF)/8192;
|
//t.speed = (obji.setting1&0xFFFF)/8192;
|
||||||
|
|
||||||
function update(scene) {
|
function update(scene) {
|
||||||
|
t.colFrame++;
|
||||||
if (t.mode == 0) {
|
if (t.mode == 0) {
|
||||||
t.elapsedTime += t.routeSpeed;
|
t.elapsedTime += t.routeSpeed;
|
||||||
movVel = vec3.sub([], t.nextNode.pos, t.prevPos);
|
movVel = vec3.sub([], t.nextNode.pos, t.prevPos);
|
||||||
|
@ -159,15 +288,11 @@ window.ObjRoutePlatform = function(obji, scene) {
|
||||||
function generateCol() {
|
function generateCol() {
|
||||||
genCol = {dat: [
|
genCol = {dat: [
|
||||||
{
|
{
|
||||||
Vertex1: [25, 0, 11],
|
Vertices: [[25, 0, 11], [25, 0, -11], [-25, 0, -11]],
|
||||||
Vertex2: [25, 0, -11],
|
|
||||||
Vertex3: [-25, 0, -11],
|
|
||||||
Normal: [0, 1, 0]
|
Normal: [0, 1, 0]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Vertex1: [-25, 0, -11],
|
Vertices: [[-25, 0, -11], [-25, 0, 11], [25, 0, 11]],
|
||||||
Vertex2: [-25, 0, 11],
|
|
||||||
Vertex3: [25, 0, 11],
|
|
||||||
Normal: [0, 1, 0]
|
Normal: [0, 1, 0]
|
||||||
},
|
},
|
||||||
], scale: 1};
|
], scale: 1};
|
||||||
|
@ -182,6 +307,7 @@ window.ObjRoutePlatform = function(obji, scene) {
|
||||||
mat4.scale(mat, mat, vec3.mul([], [16*inf.scale, 16*inf.scale, 16*inf.scale], t.scale));
|
mat4.scale(mat, mat, vec3.mul([], [16*inf.scale, 16*inf.scale, 16*inf.scale], t.scale));
|
||||||
|
|
||||||
obj.mat = mat;
|
obj.mat = mat;
|
||||||
|
obj.frame = t.colFrame;
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,34 +32,44 @@ var itemTypes = {
|
||||||
'$koura_w': BlueShellC
|
'$koura_w': BlueShellC
|
||||||
}
|
}
|
||||||
|
|
||||||
window.Item = function(scene, owner, type) {
|
window.Item = function(scene, owner, type, id) {
|
||||||
var t = this;
|
var t = this;
|
||||||
var minimumMove = 0.01;
|
var minimumMove = 0.01;
|
||||||
|
|
||||||
this.id = 0;
|
this.id = id;
|
||||||
|
|
||||||
this.pos = vec3.transformMat4([], [0, (-owner.params.colRadius)+1, 16], owner.mat);
|
this.pos = vec3.transformMat4([], [0, (-owner.params.colRadius)+1, 16], owner.mat);
|
||||||
this.vel = vec3.create();
|
this.vel = vec3.create();
|
||||||
this.gravity = [0, -0.17, 0]; //100% confirmed by me messing around with the gravity value in mkds
|
this.gravity = [0, -0.17, 0]; //100% confirmed by me messing around with the gravity value in mkds
|
||||||
this.minBounceVel = 0.5;
|
this.minBounceVel = 0.5;
|
||||||
this.airResist = 0.95;
|
this.airResist = 0.99;
|
||||||
this.enablePhysics = true;
|
this.enablePhysics = true;
|
||||||
this.floorBounce = 0.5;
|
this.floorBounce = 0.5;
|
||||||
this.held = true;
|
this.held = true;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
|
this.holdTime = 20;
|
||||||
|
this.dead = false;
|
||||||
|
|
||||||
this.angle = owner.angle;
|
this.angle = owner.angle;
|
||||||
this.speed = 10;
|
this.speed = 10;
|
||||||
this.yvel = 0;
|
this.yvel = 0;
|
||||||
|
this.xyScale = [1, 1];
|
||||||
|
|
||||||
this.colRadius = 4;
|
this.colRadius = 4;
|
||||||
this.holdDist = 16;
|
this.holdDist = 2;
|
||||||
this.safeKart = owner;
|
this.safeKart = owner;
|
||||||
|
var safeTimeMax = 4;
|
||||||
|
this.safeTime = safeTimeMax; //time the object needs to not be colliding with the source to start considering collisions with it
|
||||||
|
this.stuckTo = null;
|
||||||
|
|
||||||
var deadTimerLength = 20;
|
this.groundTime = 0;
|
||||||
var throwVelocity = 16;
|
|
||||||
var throwAngle = (Math.PI / 3) * 2;
|
|
||||||
|
var deadTimerLength = 30;
|
||||||
|
var throwVelocity = 7; //xz velocity for throw. angle adds a y component
|
||||||
|
var throwAngle = (Math.PI / 10) * 2;
|
||||||
|
var working = vec3.create();
|
||||||
this.deadTimer = 0; //animates death. goes to 20, then deletes for real. dead objects can't run update or otherwise
|
this.deadTimer = 0; //animates death. goes to 20, then deletes for real. dead objects can't run update or otherwise
|
||||||
|
|
||||||
//a controller makes this item what it is...
|
//a controller makes this item what it is...
|
||||||
|
@ -69,6 +79,8 @@ window.Item = function(scene, owner, type) {
|
||||||
// update?: (scene: CourseScene) => void
|
// update?: (scene: CourseScene) => void
|
||||||
// draw?: (mvMatrix, pMatrix) => void // OVERRIDES NORMAL DRAW FUNCTION!
|
// draw?: (mvMatrix, pMatrix) => void // OVERRIDES NORMAL DRAW FUNCTION!
|
||||||
// release?: (direction: number) => boolean //direction is 1 for forward, -1 for back. returns if the item has more uses
|
// release?: (direction: number) => boolean //direction is 1 for forward, -1 for back. returns if the item has more uses
|
||||||
|
// onRest?: (normal: vec3) => void //when the object comes to a rest (first time, or after leaving the ground for a while)
|
||||||
|
// onDie?: (final: boolean) => void //when the object dies
|
||||||
// collide?: (item: Item | Kart)
|
// collide?: (item: Item | Kart)
|
||||||
// collideKart?: (item: Kart)
|
// collideKart?: (item: Kart)
|
||||||
var subtypeInd = type.indexOf('-');
|
var subtypeInd = type.indexOf('-');
|
||||||
|
@ -91,10 +103,16 @@ window.Item = function(scene, owner, type) {
|
||||||
function updateHold(kart) {
|
function updateHold(kart) {
|
||||||
//move the object behind the kart (physical direction without drift off)
|
//move the object behind the kart (physical direction without drift off)
|
||||||
//assuming this will only be called for something that can be held
|
//assuming this will only be called for something that can be held
|
||||||
var dir = -kart.driftOff;
|
var dir = kart.driftOff / 4;
|
||||||
|
|
||||||
//offset the kart's drift offset (on direction)
|
//offset the kart's drift offset (on direction)
|
||||||
var pos = [Math.sin(dir)*t.holdDist, 0, -Math.cos(dir)*t.holdDist];
|
var pos;
|
||||||
|
if (t.holdPos != null) {
|
||||||
|
pos = vec3.clone(t.holdPos);
|
||||||
|
} else {
|
||||||
|
var dist = t.colRadius + kart.params.colRadius + t.holdDist;
|
||||||
|
var pos = [Math.sin(dir)*dist, -kart.params.colRadius, -Math.cos(dir)*dist];
|
||||||
|
}
|
||||||
|
|
||||||
//make relative to the kart's position
|
//make relative to the kart's position
|
||||||
vec3.transformMat4(pos, pos, kart.mat);
|
vec3.transformMat4(pos, pos, kart.mat);
|
||||||
|
@ -105,18 +123,37 @@ window.Item = function(scene, owner, type) {
|
||||||
|
|
||||||
function release(forward) {
|
function release(forward) {
|
||||||
//release the item, either forward or back
|
//release the item, either forward or back
|
||||||
if (t.canBeHeld()) t.updateHold(owner);
|
t.holdTime = 0;
|
||||||
|
if (t.canBeHeld()) {
|
||||||
|
t.updateHold(owner);
|
||||||
|
updateCollision(scene);
|
||||||
|
}
|
||||||
|
t.enablePhysics = true;
|
||||||
if (t.controller.release) t.controller.release(forward);
|
if (t.controller.release) t.controller.release(forward);
|
||||||
else {
|
else {
|
||||||
//default drop and throw. just here for template purposes
|
//default drop and throw. just here for template purposes
|
||||||
if (forward >= 0) {
|
if (forward > 0) {
|
||||||
var dir = owner.physicalDir;
|
nitroAudio.playSound(218, {volume: 2}, 0, owner);
|
||||||
vec3.zero(t.vel);
|
var dir = owner.driftOff / 4;
|
||||||
|
|
||||||
|
//offset the kart's drift offset (on direction). add y component
|
||||||
|
var vel = [-Math.sin(dir)*throwVelocity, Math.tan(throwAngle) * throwVelocity, Math.cos(dir)*throwVelocity];
|
||||||
|
var z = [0, 0, 0];
|
||||||
|
|
||||||
|
//make relative to the kart's orientation
|
||||||
|
vec3.transformMat4(vel, vel, owner.mat);
|
||||||
|
vec3.transformMat4(z, z, owner.mat);
|
||||||
|
vec3.sub(vel, vel, z);
|
||||||
|
var v2 = vec3.scale([], owner.vel, 2);
|
||||||
|
vec3.add(vel, vel, v2);
|
||||||
|
|
||||||
|
t.vel = vel;
|
||||||
} else {
|
} else {
|
||||||
vec3.zero(t.vel);
|
t.vel = vec3.create();
|
||||||
|
t.safeKart = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.held = false;
|
t.held = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function canBeHeld() {
|
function canBeHeld() {
|
||||||
|
@ -124,7 +161,8 @@ window.Item = function(scene, owner, type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function canBeDropped() {
|
function canBeDropped() {
|
||||||
return t.controller.canBeDropped || true;
|
if (t.controller.canBeDropped == null) return true;
|
||||||
|
return t.controller.canBeDropped;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isDestructive() {
|
function isDestructive() {
|
||||||
|
@ -132,13 +170,22 @@ window.Item = function(scene, owner, type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function isSolid() {
|
function isSolid() {
|
||||||
return t.controller.isSolid || true;
|
if (t.controller.isSolid == null) return true;
|
||||||
|
return t.controller.isSolid;
|
||||||
}
|
}
|
||||||
|
|
||||||
function finalize() {
|
function finalize() {
|
||||||
//kill instantly
|
//kill instantly
|
||||||
|
if (t.controller.onDie) t.controller.onDie(true);
|
||||||
t.deadTimer = deadTimerLength;
|
t.deadTimer = deadTimerLength;
|
||||||
scene.items.removeItem(t);
|
scene.items.removeItem(t);
|
||||||
|
t.dead = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function intensityMax(targ, vec) {
|
||||||
|
if (Math.abs(vec[0]) > Math.abs(targ[0])*0.5) targ[0] = vec[0];
|
||||||
|
if (Math.abs(vec[1]) > Math.abs(targ[1])*0.5) targ[1] = vec[1];
|
||||||
|
if (Math.abs(vec[2]) > Math.abs(targ[2])*0.5) targ[2] = vec[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
function collide(item) {
|
function collide(item) {
|
||||||
|
@ -152,15 +199,19 @@ window.Item = function(scene, owner, type) {
|
||||||
if (item.isDestructive() || t.isDestructive()) {
|
if (item.isDestructive() || t.isDestructive()) {
|
||||||
//mutual destruction. other side will deal with how they handle the collision
|
//mutual destruction. other side will deal with how they handle the collision
|
||||||
t.deadTimer++;
|
t.deadTimer++;
|
||||||
|
item.deadTimer++;
|
||||||
} else if (item.isSolid() && t.isSolid()) {
|
} else if (item.isSolid() && t.isSolid()) {
|
||||||
//bounce off other items that are not destructive
|
//bounce off other items that are not destructive
|
||||||
//set our velocity to move away (not too intensely)
|
//set our velocity to move away (not too intensely)
|
||||||
//(only apply if our id is before, to avoid double adding the velocity)
|
//(only apply if our id is before, to avoid double adding the velocity)
|
||||||
if (t.id < item.id) {
|
if (t.id < item.id) {
|
||||||
var diff = vec3.sub([], t.pos, item.pos);
|
var diff = vec3.sub(working, t.pos, item.pos);
|
||||||
vec3.scale(diff, diff, 0.5);
|
vec3.scale(diff, diff, 0.33);
|
||||||
vec3.add(t.vel, t.vel, diff);
|
intensityMax(t.vel, diff);
|
||||||
vec3.sub(item.vel, item.vel, diff);
|
vec3.scale(diff, diff, -1);
|
||||||
|
intensityMax(item.vel, diff);
|
||||||
|
//vec3.add(t.vel, t.vel, diff);
|
||||||
|
//vec3.sub(item.vel, item.vel, diff);
|
||||||
t.enablePhysics = true;
|
t.enablePhysics = true;
|
||||||
item.enablePhysics = true;
|
item.enablePhysics = true;
|
||||||
}
|
}
|
||||||
|
@ -175,77 +226,131 @@ window.Item = function(scene, owner, type) {
|
||||||
|
|
||||||
function update(scene) {
|
function update(scene) {
|
||||||
if (t.controller.update) t.controller.update(scene);
|
if (t.controller.update) t.controller.update(scene);
|
||||||
|
if (t.holdTime > 0 && t.holdTime-- > 7) {
|
||||||
|
if (t.holdTime == 7) {
|
||||||
|
nitroAudio.playSound(231, {volume: 2}, 0, owner);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (t.pos[2] < -10000) finalize(); //out of bounds failsafe
|
||||||
|
|
||||||
if (t.deadTimer > 0) {
|
if (t.deadTimer > 0) {
|
||||||
|
if (t.deadTimer == 1 && t.controller.onDie) t.controller.onDie(false);
|
||||||
t.deadTimer++;
|
t.deadTimer++;
|
||||||
if (t.deadTimer >= 20) t.finalize();
|
t.sprMat = mat4.create();
|
||||||
|
mat4.translate(t.sprMat, t.sprMat, [t.deadTimer/50, Math.sin((t.deadTimer/30) * Math.PI) * 0.5, 0]);
|
||||||
|
mat4.rotateZ(t.sprMat, t.sprMat, (t.deadTimer/-15) * Math.PI);
|
||||||
|
if (t.deadTimer >= 30) t.finalize();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (t.held) {
|
||||||
|
t.updateHold(owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
var hitSafe = false;
|
||||||
//search for player collisions, collisions with other items
|
//search for player collisions, collisions with other items
|
||||||
for (var i=0; i<scene.karts.length; i++) {
|
for (var i=0; i<scene.karts.length; i++) {
|
||||||
var ok = scene.karts[i];
|
var ok = scene.karts[i];
|
||||||
var dist = vec3.dist(vec3.add([], t.pos, [0,1,0]), ok.pos);
|
|
||||||
if (dist < t.colRadius + 12) {
|
var dist = vec3.dist(vec3.add(working, t.pos, [0,t.colRadius/2,0]), ok.pos);
|
||||||
|
if (dist < t.colRadius + ok.params.colRadius) {
|
||||||
//colliding with a kart.
|
//colliding with a kart.
|
||||||
//do we need to do something?
|
//do we need to do something?
|
||||||
|
if (ok === t.safeKart) {
|
||||||
|
hitSafe = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
t.collide(ok);
|
t.collide(ok);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i=0; i<scene.items.length; i++) {
|
if (t.safeKart && !hitSafe && !t.held) {
|
||||||
var ot = scene.items[i];
|
t.safeTime--;
|
||||||
var dist = vec3.dist(t.pos, ot.pos);
|
if (t.safeTime <= 0) {
|
||||||
if (dist < t.colRadius + ot.colRadius) {
|
t.safeKart = null;
|
||||||
//two items are colliding.
|
|
||||||
t.collide(ot);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (t.holdTime == 0) { //avoid mutual item destruction on the first frame
|
||||||
if (t.enablePhysics) {
|
for (var i=0; i<scene.items.items.length; i++) {
|
||||||
if (!t.held) {
|
var ot = scene.items.items[i];
|
||||||
vec3.add(t.vel, t.vel, t.gravity);
|
if (ot == t || (t.held && ot.held)) continue;
|
||||||
vec3.scale(t.vel, t.vel, t.airResist);
|
var dist = vec3.dist(t.pos, ot.pos);
|
||||||
}
|
if (dist < t.colRadius + ot.colRadius && ot.holdTime <= 7 && ot.deadTimer == 0) {
|
||||||
|
//two items are colliding.
|
||||||
//by default, items use raycast collision against the world (rather than ellipse)
|
t.collide(ot);
|
||||||
//this speeds things up considerably
|
|
||||||
|
|
||||||
var steps = 0;
|
|
||||||
var remainingT = 1;
|
|
||||||
var velSeg = vec3.clone(t.vel);
|
|
||||||
var posSeg = vec3.clone(t.pos);
|
|
||||||
var ignoreList = [];
|
|
||||||
while (steps++ < 10 && remainingT > 0.01) {
|
|
||||||
var result = lsc.raycast(posSeg, velSeg, scene.kcl, 0.05, ignoreList);
|
|
||||||
if (result != null) {
|
|
||||||
if (t.controller.colResponse) t.controller.colResponse(posSeg, velSeg, result, ignoreList)
|
|
||||||
else colResponse(posSeg, velSeg, result, ignoreList)
|
|
||||||
remainingT -= result.t;
|
|
||||||
if (remainingT > 0.01) {
|
|
||||||
velSeg = vec3.scale(vec3.create(), t.vel, remainingT);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
vec3.add(posSeg, posSeg, velSeg);
|
|
||||||
remainingT = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.pos = posSeg;
|
}
|
||||||
|
|
||||||
|
if (t.groundTime > 0) t.groundTime++;
|
||||||
|
|
||||||
|
if (t.stuckTo != null) {
|
||||||
|
if (t.stuckTo.moveWith != null) t.stuckTo.moveWith(t);
|
||||||
|
t.enablePhysics = true;
|
||||||
|
t.stuckTo = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.enablePhysics) {
|
||||||
|
updateCollision(scene);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateCollision(scene) {
|
||||||
|
if (!t.held) {
|
||||||
|
vec3.add(t.vel, t.vel, t.gravity);
|
||||||
|
vec3.scale(t.vel, t.vel, t.airResist);
|
||||||
|
}
|
||||||
|
|
||||||
|
//by default, items use raycast collision against the world (rather than ellipse)
|
||||||
|
//this speeds things up considerably
|
||||||
|
|
||||||
|
var steps = 0;
|
||||||
|
var remainingT = 1;
|
||||||
|
var velSeg = vec3.clone(t.vel);
|
||||||
|
var posSeg = vec3.clone(t.pos);
|
||||||
|
var ignoreList = [];
|
||||||
|
while (steps++ < 10 && remainingT > 0.01) {
|
||||||
|
var result = lsc.raycast(posSeg, velSeg, scene, 0.05, ignoreList);
|
||||||
|
if (result != null) {
|
||||||
|
if (t.controller.colResponse && !t.held) t.controller.colResponse(posSeg, velSeg, result, ignoreList)
|
||||||
|
else colResponse(posSeg, velSeg, result, ignoreList)
|
||||||
|
remainingT -= result.t;
|
||||||
|
if (remainingT > 0.01) {
|
||||||
|
velSeg = vec3.scale(velSeg, t.vel, remainingT);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vec3.add(posSeg, posSeg, velSeg);
|
||||||
|
remainingT = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.pos = posSeg;
|
||||||
|
}
|
||||||
|
|
||||||
function draw(mvMatrix, pMatrix) {
|
function draw(mvMatrix, pMatrix) {
|
||||||
|
if (t.holdTime > 7) return;
|
||||||
if (t.deadTimer > 0) nitroRender.setColMult([1, 1, 1, 1-(t.deadTimer/deadTimerLength)]); //fade out
|
if (t.deadTimer > 0) nitroRender.setColMult([1, 1, 1, 1-(t.deadTimer/deadTimerLength)]); //fade out
|
||||||
if (t.controller.draw) {
|
if (t.controller.draw) {
|
||||||
t.controller.draw(mvMatrix, pMatrix);
|
t.controller.draw(mvMatrix, pMatrix);
|
||||||
} else {
|
} else {
|
||||||
var mat = mat4.translate(mat4.create(), mvMatrix, vec3.add(vec3.create(), t.pos, [0, 3, 0]));
|
var mat = mat4.translate(mat4.create(), mvMatrix, vec3.add(vec3.create(), t.pos, [0, t.colRadius * t.xyScale[1], 0]));
|
||||||
|
|
||||||
spritify(mat);
|
spritify(mat);
|
||||||
mat4.scale(mat, mat, [16, 16, 16]);
|
var scale = 6*t.colRadius * (1 - t.holdTime/7);
|
||||||
|
mat4.scale(mat, mat, [scale, scale, scale]);
|
||||||
|
|
||||||
scene.gameRes.items[type].draw(mat, pMatrix);
|
var mdl = scene.gameRes.items[type];
|
||||||
|
//apply our custom mat (in sprite space), if it exists
|
||||||
|
//used for destruction animation, scaling
|
||||||
|
if (t.sprMat) {
|
||||||
|
var oldMat = mdl.baseMat;
|
||||||
|
mdl.setBaseMat(t.sprMat);
|
||||||
|
mdl.draw(mat, pMatrix);
|
||||||
|
mdl.setBaseMat(oldMat);
|
||||||
|
} else {
|
||||||
|
mdl.draw(mat, pMatrix);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (t.deadTimer > 0) nitroRender.setColMult([1, 1, 1, 1]);
|
if (t.deadTimer > 0) nitroRender.setColMult([1, 1, 1, 1]);
|
||||||
}
|
}
|
||||||
|
@ -272,13 +377,28 @@ window.Item = function(scene, owner, type) {
|
||||||
//normally, item collision with a wall cause a perfect reflection of the velocity.
|
//normally, item collision with a wall cause a perfect reflection of the velocity.
|
||||||
var proj = vec3.dot(t.vel, n) * 2;
|
var proj = vec3.dot(t.vel, n) * 2;
|
||||||
vec3.sub(t.vel, t.vel, vec3.scale(vec3.create(), n, proj));
|
vec3.sub(t.vel, t.vel, vec3.scale(vec3.create(), n, proj));
|
||||||
|
t.safeKart = null;
|
||||||
|
} else if (colType == MKDS_COLTYPE.OOB || colType == MKDS_COLTYPE.FALL) {
|
||||||
|
if (t.deadTimer == 0) t.deadTimer++;
|
||||||
} else if (MKDS_COLTYPE.GROUP_ROAD.indexOf(colType) != -1) {
|
} else if (MKDS_COLTYPE.GROUP_ROAD.indexOf(colType) != -1) {
|
||||||
//sliding plane
|
//sliding plane
|
||||||
var proj = vec3.dot(t.vel, n) * (1 + t.floorBounce);
|
var bounce = t.held ? 0 : t.floorBounce;
|
||||||
|
var proj = vec3.dot(t.vel, n) * (1 + bounce);
|
||||||
vec3.sub(t.vel, t.vel, vec3.scale(vec3.create(), n, proj));
|
vec3.sub(t.vel, t.vel, vec3.scale(vec3.create(), n, proj));
|
||||||
|
|
||||||
if (t.floorBounce == 0 || Math.abs(proj) < t.minBounceVel) t.enablePhysics = false;
|
if (!t.held && (t.floorBounce == 0 || Math.abs(proj) < t.minBounceVel)) {
|
||||||
|
t.vel[0] = 0;
|
||||||
|
t.vel[1] = 0;
|
||||||
|
t.vel[2] = 0;
|
||||||
|
t.enablePhysics = false;
|
||||||
|
if (t.groundTime == 0) {
|
||||||
|
t.groundTime = 1;
|
||||||
|
if (t.controller.onRest) {
|
||||||
|
t.controller.onRest(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.stuckTo = dat.object;
|
||||||
} else {
|
} else {
|
||||||
adjustPos = false;
|
adjustPos = false;
|
||||||
ignoreList.push(plane);
|
ignoreList.push(plane);
|
||||||
|
|
|
@ -98,12 +98,15 @@ window.ItemBox = function(obji, scene) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function sndUpdate(view) {
|
function sndUpdate(view) {
|
||||||
|
/*
|
||||||
t.soundProps.pos = vec3.transformMat4([], t.pos, view);
|
t.soundProps.pos = vec3.transformMat4([], t.pos, view);
|
||||||
if (t.soundProps.lastPos != null) t.soundProps.vel = vec3.sub([], t.soundProps.pos, t.soundProps.lastPos);
|
if (t.soundProps.lastPos != null) t.soundProps.vel = vec3.sub([], t.soundProps.pos, t.soundProps.lastPos);
|
||||||
else t.soundProps.vel = [0, 0, 0];
|
else t.soundProps.vel = [0, 0, 0];
|
||||||
|
*/
|
||||||
t.soundProps.lastPos = t.soundProps.pos;
|
t.soundProps.lastPos = t.soundProps.pos;
|
||||||
|
t.soundProps.pos = t.pos; //todo: reintroduce doppler via emulation
|
||||||
|
|
||||||
t.soundProps.refDistance = 192/1024;
|
t.soundProps.refDistance = 192;
|
||||||
t.soundProps.rolloffFactor = 1;
|
t.soundProps.rolloffFactor = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,42 @@
|
||||||
window.BananaC = function(item, scene, type) {
|
window.BananaC = function(item, scene, type) {
|
||||||
|
var t = this;
|
||||||
this.canBeHeld = true;
|
this.canBeHeld = true;
|
||||||
this.canBeDropped = true;
|
this.canBeDropped = true;
|
||||||
this.isDestructive = false;
|
this.isDestructive = false;
|
||||||
item.minBounceVel = 0;
|
item.floorBounce = 0;
|
||||||
|
|
||||||
this.collideKart = collideKart;
|
this.collideKart = collideKart;
|
||||||
|
this.onRest = onRest;
|
||||||
|
this.update = update;
|
||||||
|
|
||||||
function collideKart(kart) {
|
function collideKart(kart) {
|
||||||
item.deadTimerLength = 20;
|
item.deadTimer = 1;
|
||||||
kart.damage(MKDSCONST.DAMAGE_SPIN);
|
kart.damage(MKDSCONST.DAMAGE_SPIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onRest(normal) {
|
||||||
|
nitroAudio.playSound(219, {volume: 2}, 0, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
function update(argument) {
|
||||||
|
if (!item.held && item.colRadius < 6) {
|
||||||
|
item.colRadius += 0.2;
|
||||||
|
if (item.colRadius > 6) item.colRadius = 6;
|
||||||
|
}
|
||||||
|
if (item.groundTime < 30) {
|
||||||
|
var t = (1-item.groundTime/29);
|
||||||
|
var s = Math.sin(item.groundTime * Math.PI/14);
|
||||||
|
|
||||||
|
var sprMat = mat4.create();
|
||||||
|
mat4.translate(sprMat, sprMat, [0, -1/6, 0]);
|
||||||
|
mat4.scale(sprMat, sprMat, [1 + s * 0.6 * t, 1 - s * 0.6 * t, 1]);
|
||||||
|
mat4.translate(sprMat, sprMat, [0, 1/6, 0]);
|
||||||
|
|
||||||
|
item.sprMat = sprMat;
|
||||||
|
} else {
|
||||||
|
item.sprMat = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.BananaGroupC = function(item, scene, type) {
|
window.BananaGroupC = function(item, scene, type) {
|
||||||
|
@ -28,27 +55,89 @@ window.BananaGroupC = function(item, scene, type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
window.FakeBoxC = function(item, scene, type) {
|
window.FakeBoxC = function(item, scene, type) {
|
||||||
|
var t = this;
|
||||||
this.canBeHeld = true;
|
this.canBeHeld = true;
|
||||||
this.canBeDropped = true;
|
this.canBeDropped = true;
|
||||||
this.isDestructive = false;
|
this.isDestructive = false;
|
||||||
this.isSolid = true;
|
this.isSolid = false;
|
||||||
var model = scene.gameRes.fakeBox;
|
item.floorBounce = 0;
|
||||||
|
item.airResist = 0.98;
|
||||||
|
|
||||||
|
this.collideKart = collideKart;
|
||||||
|
this.onRest = onRest;
|
||||||
|
this.update = update;
|
||||||
this.draw = draw;
|
this.draw = draw;
|
||||||
|
|
||||||
function draw(view, pMatrix) {
|
this.xyScale = [1,1];
|
||||||
mat4.translate(mat, view, t.pos);
|
this.dir = 0;
|
||||||
mat4.translate(mat, view, [0, 16, 0]);
|
|
||||||
|
|
||||||
/* adjust to make it rest on a corner
|
function collideKart(kart) {
|
||||||
if (t.angle[2] != 0) mat4.rotateZ(mat, mat, t.angle[2]*(Math.PI/180));
|
item.deadTimer = 1;
|
||||||
if (t.angle[1] != 0) mat4.rotateY(mat, mat, t.angle[1]*(Math.PI/180));
|
nitroAudio.playSound(250, {volume: 2}, 0, item);
|
||||||
if (t.angle[0] != 0) mat4.rotateX(mat, mat, t.angle[0]*(Math.PI/180));
|
kart.damage(MKDSCONST.DAMAGE_FLIP);
|
||||||
*/
|
}
|
||||||
|
|
||||||
mat4.scale(mat, mat, vec3.scale([], t.scale, 16));
|
function onRest(normal) {
|
||||||
model.draw(mat, pMatrix, animMat);
|
nitroAudio.playSound(251, {volume: 2}, 0, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
function update(argument) {
|
||||||
|
if (item.held) {
|
||||||
|
t.dir = -(item.owner.physicalDir + item.owner.driftOff / 4);
|
||||||
|
}
|
||||||
|
if (!item.held && item.colRadius < 8) {
|
||||||
|
item.colRadius += 0.2;
|
||||||
|
if (item.colRadius > 8) item.colRadius = 8;
|
||||||
|
}
|
||||||
|
if (item.groundTime < 20) {
|
||||||
|
var linear = (1-item.groundTime/19);
|
||||||
|
var s = Math.sin(item.groundTime * Math.PI/8);
|
||||||
|
|
||||||
|
t.xyScale = [1 + s * 0.25 * linear, 1 - s * 0.25 * linear];
|
||||||
|
} else {
|
||||||
|
t.xyScale = [1,1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw(mvMatrix, pMatrix) {
|
||||||
|
var mat = mat4.translate(mat4.create(), mvMatrix, vec3.add(vec3.create(), item.pos, [0, item.colRadius*1.5 * t.xyScale[1], 0]));
|
||||||
|
|
||||||
|
var scale = 2*item.colRadius * (1 - item.holdTime/7);
|
||||||
|
mat4.scale(mat, mat, [scale*t.xyScale[0], scale*t.xyScale[1], scale*t.xyScale[0]]);
|
||||||
|
mat4.rotateY(mat, mat, t.dir);
|
||||||
|
mat4.rotateZ(mat, mat, Math.PI/-6);
|
||||||
|
mat4.rotateY(mat, mat, Math.PI/6);
|
||||||
|
mat4.rotateX(mat, mat, Math.PI/-6);
|
||||||
|
|
||||||
|
var mdl = scene.gameRes.items.fakeBox;
|
||||||
|
mdl.draw(mat, pMatrix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.BombC = null;
|
window.BombC = function(item, scene, type) {
|
||||||
|
var t = this;
|
||||||
|
this.canBeHeld = true;
|
||||||
|
this.canBeDropped = true;
|
||||||
|
this.isDestructive = true;
|
||||||
|
|
||||||
|
this.isExploding = false;
|
||||||
|
|
||||||
|
this.collideKart = collideKart;
|
||||||
|
this.onRest = onRest;
|
||||||
|
this.update = update;
|
||||||
|
|
||||||
|
function collideKart(kart) {
|
||||||
|
item.deadTimer = 1;
|
||||||
|
kart.damage(MKDSCONST.DAMAGE_EXPLODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onRest(normal) {
|
||||||
|
}
|
||||||
|
|
||||||
|
function update(argument) {
|
||||||
|
if (!item.held && item.colRadius < 6) {
|
||||||
|
item.colRadius += 0.2;
|
||||||
|
if (item.colRadius > 6) item.colRadius = 6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,97 @@
|
||||||
window.GreenShellC = function(item, scene) {
|
window.GreenShellC = function(item, scene) {
|
||||||
|
var t = this;
|
||||||
this.canBeHeld = true;
|
this.canBeHeld = true;
|
||||||
this.canBeDropped = true;
|
this.canBeDropped = true;
|
||||||
this.isDestructive = true;
|
this.isDestructive = true;
|
||||||
|
this.angle = 0;
|
||||||
|
this.speed = 6; //base speed + kart speed
|
||||||
|
this.sound = null;
|
||||||
|
this.soundCooldown = 0;
|
||||||
|
item.colRadius = 3;
|
||||||
|
|
||||||
|
var minimumMove = 0.17;
|
||||||
|
this.gravity = [0, -0.17, 0]; //100% confirmed by me messing around with the gravity value in mkds
|
||||||
|
|
||||||
|
this.collideKart = collideKart;
|
||||||
|
this.update = update;
|
||||||
|
this.release = release;
|
||||||
|
this.onDie = onDie;
|
||||||
|
this.colResponse = colResponse;
|
||||||
|
|
||||||
|
function release(forward) {
|
||||||
|
t.sound = nitroAudio.playSound(215, {volume: 1.5}, 0, item);
|
||||||
|
t.speed = 6;
|
||||||
|
t.angle = item.owner.physicalDir;
|
||||||
|
if (forward < 0) {
|
||||||
|
t.angle += Math.PI;
|
||||||
|
t.angle %= Math.PI*2;
|
||||||
|
} else {
|
||||||
|
t.speed += item.owner.speed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDie(final) {
|
||||||
|
if (!final) {
|
||||||
|
nitroAudio.playSound(214, {volume: 2}, 0, item);
|
||||||
|
}
|
||||||
|
if (t.sound) {
|
||||||
|
nitroAudio.instaKill(t.sound);
|
||||||
|
t.sound = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function collideKart(kart) {
|
||||||
|
item.deadTimer = 1;
|
||||||
|
kart.damage(MKDSCONST.DAMAGE_FLIP);
|
||||||
|
}
|
||||||
|
|
||||||
|
function update(scene) {
|
||||||
|
item.vel = [Math.sin(t.angle)*t.speed, item.vel[1], -Math.cos(t.angle)*t.speed]
|
||||||
|
vec3.add(item.vel, item.vel, t.gravity);
|
||||||
|
if (this.soundCooldown > 0) this.soundCooldown--;
|
||||||
|
}
|
||||||
|
|
||||||
|
function colResponse(pos, pvel, dat, ignoreList) {
|
||||||
|
var plane = dat.plane;
|
||||||
|
var colType = (plane.CollisionType>>8)&31;
|
||||||
|
vec3.add(pos, pos, vec3.scale(vec3.create(), pvel, dat.t));
|
||||||
|
|
||||||
|
var n = dat.normal;
|
||||||
|
vec3.normalize(n, n);
|
||||||
|
var gravS = Math.sqrt(vec3.dot(t.gravity, t.gravity));
|
||||||
|
var angle = Math.acos(vec3.dot(vec3.scale(vec3.create(), t.gravity, -1/gravS), n));
|
||||||
|
var adjustPos = true
|
||||||
|
|
||||||
|
if (MKDS_COLTYPE.GROUP_WALL.indexOf(colType) != -1) { //wall
|
||||||
|
//shell reflection code - slide y vel across plane, bounce on xz
|
||||||
|
if (this.soundCooldown <= 0) {
|
||||||
|
nitroAudio.playSound(213, {volume: 2.5}, 0, item);
|
||||||
|
this.soundCooldown = 30;
|
||||||
|
}
|
||||||
|
vec3.add(item.vel, vec3.scale(vec3.create(), n, -2*(vec3.dot(item.vel, n)/vec3.dot(n,n))), item.vel);
|
||||||
|
item.vel[1] = 0;
|
||||||
|
|
||||||
|
var v = item.vel;
|
||||||
|
t.angle = Math.atan2(v[0], -v[2]);
|
||||||
|
} else if (colType == MKDS_COLTYPE.OOB || colType == MKDS_COLTYPE.FALL) {
|
||||||
|
if (item.deadTimer == 0) item.deadTimer++;
|
||||||
|
} else if (MKDS_COLTYPE.GROUP_ROAD.indexOf(colType) != -1) {
|
||||||
|
//sliding plane
|
||||||
|
var proj = vec3.dot(item.vel, n);
|
||||||
|
vec3.sub(item.vel, item.vel, vec3.scale(vec3.create(), n, proj));
|
||||||
|
} else {
|
||||||
|
adjustPos = false;
|
||||||
|
ignoreList.push(plane);
|
||||||
|
}
|
||||||
|
|
||||||
|
var rVelMag = Math.sqrt(vec3.dot(item.vel, item.vel));
|
||||||
|
vec3.scale(item.vel, item.vel, t.speed/rVelMag); //force speed to shell speed for green shells.
|
||||||
|
|
||||||
|
if (adjustPos) { //move back from plane slightly
|
||||||
|
vec3.add(pos, pos, vec3.scale(vec3.create(), n, minimumMove));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.RedShellC = function(item, scene) {
|
window.RedShellC = function(item, scene) {
|
||||||
|
@ -11,11 +101,100 @@ window.RedShellC = function(item, scene) {
|
||||||
}
|
}
|
||||||
|
|
||||||
window.ShellGroupC = function(item, scene, type) {
|
window.ShellGroupC = function(item, scene, type) {
|
||||||
this.canBeHeld = false;
|
var t = this;
|
||||||
this.canBeDropped = 'func';
|
this.canBeHeld = "func";
|
||||||
|
this.canBeDropped = "func";
|
||||||
this.rotationPeriod = 45;
|
this.rotationPeriod = 45;
|
||||||
|
item.colRadius = -Infinity;
|
||||||
|
|
||||||
this.draw = draw;
|
this.draw = draw;
|
||||||
|
this.update = update;
|
||||||
|
this.release = release;
|
||||||
|
this.onDie = onDie;
|
||||||
|
|
||||||
|
this.children = [];
|
||||||
|
|
||||||
|
var itemType = "koura_g";
|
||||||
|
var itemCount = 3;
|
||||||
|
|
||||||
|
if (type.length > 0) {
|
||||||
|
var typeParse = type.split("-");
|
||||||
|
if (typeParse.length == 1) {
|
||||||
|
itemType = type;
|
||||||
|
} else if (typeParse.length == 2 && !isNaN(typeParse[1]-0)) {
|
||||||
|
itemType = typeParse[0];
|
||||||
|
itemCount = typeParse[1]-0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.phase = 0;
|
||||||
|
var spinDist = 6;
|
||||||
|
|
||||||
|
init();
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
t.remaining = itemCount;
|
||||||
|
item.holdPos = [0, 0, 0];
|
||||||
|
//create children
|
||||||
|
for (var i=0; i<itemCount; i++) {
|
||||||
|
var sub = scene.items.createItem(itemType, item.owner);
|
||||||
|
sub.holdTime = 7;
|
||||||
|
t.children.push(sub);
|
||||||
|
}
|
||||||
|
nitroAudio.playSound(231, {volume: 2}, 0, item);
|
||||||
|
this.sound = nitroAudio.playSound(227, {volume: 1.5}, 0, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDie(final) {
|
||||||
|
if (t.sound) {
|
||||||
|
nitroAudio.instaKill(t.sound);
|
||||||
|
t.sound = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function update(scene) {
|
||||||
|
for (var i=0; i<t.children.length; i++) {
|
||||||
|
var child = t.children[i];
|
||||||
|
if (child == null) continue;
|
||||||
|
if (child.deadTimer > 0) {
|
||||||
|
t.children[i] = null;
|
||||||
|
t.remaining--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var angle = ((i / itemCount + t.phase / t.rotationPeriod) * Math.PI * 2);
|
||||||
|
var rad = item.owner.params.colRadius;
|
||||||
|
var dist = spinDist + rad;
|
||||||
|
child.holdPos = [-Math.sin(angle) * dist, -item.owner.params.colRadius, Math.cos(angle) * dist];
|
||||||
|
}
|
||||||
|
t.phase++;
|
||||||
|
t.phase %= t.rotationPeriod;
|
||||||
|
}
|
||||||
|
|
||||||
|
function release(forward) {
|
||||||
|
//forward the release to our last child
|
||||||
|
var toUse;
|
||||||
|
|
||||||
|
for (var i=0; i<t.children.length; i++) {
|
||||||
|
var child = t.children[i];
|
||||||
|
if (child == null) continue;
|
||||||
|
if (child.deadTimer > 0) {
|
||||||
|
t.children[i] = null;
|
||||||
|
t.remaining--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
toUse = child;
|
||||||
|
t.children[i] = null;
|
||||||
|
t.remaining--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toUse != null) {
|
||||||
|
toUse.release(forward);
|
||||||
|
}
|
||||||
|
if (t.remaining == 0) {
|
||||||
|
item.finalize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function draw(mvMatrix, pMatrix) {
|
function draw(mvMatrix, pMatrix) {
|
||||||
//the group itself is invisible - the shells draw individually
|
//the group itself is invisible - the shells draw individually
|
||||||
|
|
|
@ -73,6 +73,11 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
||||||
this.getPosition = getPosition;
|
this.getPosition = getPosition;
|
||||||
this.playCharacterSound = playCharacterSound;
|
this.playCharacterSound = playCharacterSound;
|
||||||
|
|
||||||
|
//functions for external objects to trigger
|
||||||
|
this.damage = damage;
|
||||||
|
this.damageTime = 0;
|
||||||
|
this.damageType = -1;
|
||||||
|
|
||||||
this.trackAttach = null; //a normal for the kart to attach to (loop)
|
this.trackAttach = null; //a normal for the kart to attach to (loop)
|
||||||
this.boostMT = 0;
|
this.boostMT = 0;
|
||||||
this.boostNorm = 0;
|
this.boostNorm = 0;
|
||||||
|
@ -399,7 +404,8 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
||||||
mat4.rotateX(mat, mat, c.angle[0]*(-Math.PI/180));
|
mat4.rotateX(mat, mat, c.angle[0]*(-Math.PI/180));
|
||||||
|
|
||||||
k.pos = vec3.clone(c2.pos);
|
k.pos = vec3.clone(c2.pos);
|
||||||
vec3.add(k.pos, k.pos, vec3.transformMat4([], [0,16,16], mat));
|
vec3.add(k.pos, k.pos, vec3.transformMat4([], [0,16,32], mat));
|
||||||
|
k.airTime = 4;
|
||||||
|
|
||||||
k.physicalDir = (180-c2.angle[1])*(Math.PI/180);
|
k.physicalDir = (180-c2.angle[1])*(Math.PI/180);
|
||||||
k.angle = k.physicalDir;
|
k.angle = k.physicalDir;
|
||||||
|
@ -438,7 +444,6 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else { //default kart mode
|
} else { //default kart mode
|
||||||
|
|
||||||
if (k.OOB > 0) {
|
if (k.OOB > 0) {
|
||||||
playCharacterSound(0);
|
playCharacterSound(0);
|
||||||
var current = checkpoints[k.checkPointNumber];
|
var current = checkpoints[k.checkPointNumber];
|
||||||
|
@ -466,180 +471,176 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
||||||
var top = params.topSpeed*effect.topSpeedMul; //if you let go of accel, drift ends anyway, so always accel in drift.
|
var top = params.topSpeed*effect.topSpeedMul; //if you let go of accel, drift ends anyway, so always accel in drift.
|
||||||
|
|
||||||
var boosting = (k.boostNorm + k.boostMT)>0;
|
var boosting = (k.boostNorm + k.boostMT)>0;
|
||||||
|
if (k.specialControlHandler != null) k.specialControlHandler();
|
||||||
if (boosting) {
|
else {
|
||||||
var top2
|
if (boosting) {
|
||||||
if (k.boostNorm>0){
|
var top2
|
||||||
top2 = params.topSpeed*1.3;
|
if (k.boostNorm>0){
|
||||||
k.boostNorm--;
|
top2 = params.topSpeed*1.3;
|
||||||
} else {
|
k.boostNorm--;
|
||||||
top2 = params.topSpeed*((effect.topSpeedMul >= 1)?1.3:effect.topSpeedMul);
|
} else {
|
||||||
}
|
top2 = params.topSpeed*((effect.topSpeedMul >= 1)?1.3:effect.topSpeedMul);
|
||||||
|
|
||||||
if (k.boostMT>0) {
|
|
||||||
k.boostMT--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (k.speed <= top2) {
|
|
||||||
k.speed += 1;
|
|
||||||
if (k.speed > top2) k.speed = top2;
|
|
||||||
} else {
|
|
||||||
k.speed *= 0.95;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//kart controls
|
|
||||||
if (k.drifting) {
|
|
||||||
if ((onGround) && !(input.accel && input.drift && (k.speed > 2 || !k.driftLanded))) {
|
|
||||||
//end drift, execute miniturbo
|
|
||||||
k.drifting = false;
|
|
||||||
clearWheelParticles();
|
|
||||||
if (sounds.powerslide != null) {
|
|
||||||
nitroAudio.instaKill(sounds.powerslide);
|
|
||||||
sounds.powerslide = null;
|
|
||||||
}
|
|
||||||
if (k.driftPSMode == 3) {
|
|
||||||
k.boostMT = params.miniTurbo;
|
|
||||||
}
|
|
||||||
k.driftPSMode = 0;
|
|
||||||
k.driftPSTick = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (k.driftMode == 0) {
|
|
||||||
if (input.turn > 0.30) {
|
|
||||||
k.driftMode = 2;
|
|
||||||
} else if (input.turn < -0.30) {
|
|
||||||
k.driftMode = 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (k.driftLanded) {
|
|
||||||
var change = (((k.driftMode-1.5)*Math.PI/1.5)-k.driftOff)*0.05;
|
|
||||||
k.driftOff += change;
|
|
||||||
k.physicalDir -= change;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//if we're above the initial y position, add a constant turn with a period of 180 frames.
|
if (k.boostMT>0) {
|
||||||
if (!k.driftLanded && k.ylock>=0) {
|
k.boostMT--;
|
||||||
k.physicalDir += (Math.PI*2/180)*(k.driftMode*2-3);
|
}
|
||||||
|
|
||||||
|
if (k.speed <= top2) {
|
||||||
|
k.speed += 1;
|
||||||
|
if (k.speed > top2) k.speed = top2;
|
||||||
|
} else {
|
||||||
|
k.speed *= 0.95;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (onGround) {
|
//kart controls
|
||||||
if (!k.driftLanded) {
|
if (k.drifting) {
|
||||||
if (k.driftMode == 0) {
|
if ((onGround) && !(input.accel && input.drift && (k.speed > 2 || !k.driftLanded))) {
|
||||||
k.drifting = false;
|
//end drift, execute miniturbo
|
||||||
clearWheelParticles();
|
endDrift();
|
||||||
|
if (k.driftPSMode == 3) {
|
||||||
|
k.boostMT = params.miniTurbo;
|
||||||
}
|
}
|
||||||
else {
|
k.driftPSMode = 0;
|
||||||
k.driftPSMode = 0;
|
k.driftPSTick = 0;
|
||||||
k.driftPSTick = 0;
|
}
|
||||||
k.driftLanded = true;
|
|
||||||
if (k.drifting) setWheelParticles(20, 1); //20 = smoke, 1 = drift priority
|
if (k.driftMode == 0) {
|
||||||
|
if (input.turn > 0.30) {
|
||||||
|
k.driftMode = 2;
|
||||||
|
} else if (input.turn < -0.30) {
|
||||||
|
k.driftMode = 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (k.driftLanded) {
|
||||||
|
var change = (((k.driftMode-1.5)*Math.PI/1.5)-k.driftOff)*0.05;
|
||||||
|
k.driftOff += change;
|
||||||
|
k.physicalDir -= change;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if we're above the initial y position, add a constant turn with a period of 180 frames.
|
||||||
|
if (!k.driftLanded && k.ylock>=0) {
|
||||||
|
k.physicalDir += (Math.PI*2/180)*(k.driftMode*2-3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (k.drifting) {
|
|
||||||
|
|
||||||
|
if (onGround) {
|
||||||
|
if (!k.driftLanded) {
|
||||||
|
if (k.driftMode == 0) {
|
||||||
|
endDrift();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
k.driftPSMode = 0;
|
||||||
|
k.driftPSTick = 0;
|
||||||
|
k.driftLanded = true;
|
||||||
|
if (k.drifting) setWheelParticles(20, 1); //20 = smoke, 1 = drift priority
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (k.drifting) {
|
||||||
|
|
||||||
|
if (!boosting) {
|
||||||
|
if (k.speed <= top) {
|
||||||
|
k.speed += (k.speed/top > params.driftAccelSwitch)?params.driftAccel2:params.driftAccel1;
|
||||||
|
if (k.speed > top) k.speed = top;
|
||||||
|
} else {
|
||||||
|
k.speed *= 0.95;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var turn = ((k.driftMode == 1)?(input.turn-1):(input.turn+1))/2;
|
||||||
|
|
||||||
|
k.physicalDir += params.driftTurnRate*turn+((k.driftMode == 1)?-1:1)*(50/32768)*Math.PI; //what is this mystery number i hear you ask? well my friend, this is the turn rate for outward drift.
|
||||||
|
|
||||||
|
//miniturbo code
|
||||||
|
if (input.turn != 0) {
|
||||||
|
var inward = ((input.turn>0) == k.driftMode-1); //if we're turning
|
||||||
|
|
||||||
|
switch (k.driftPSMode) {
|
||||||
|
case 0: //dpad away from direction for 10 frames
|
||||||
|
if (!inward) k.driftPSTick++;
|
||||||
|
else if (k.driftPSTick > 9) {
|
||||||
|
k.driftPSMode++;
|
||||||
|
k.driftPSTick = 1;
|
||||||
|
|
||||||
|
//play blue spark sound, flare
|
||||||
|
setWheelParticles(126, 2); //126 = blue flare, 2 = flare priority
|
||||||
|
var blue = nitroAudio.playSound(210, {}, 0, k);
|
||||||
|
blue.gainN.gain.value = 2;
|
||||||
|
|
||||||
|
} else k.driftPSTick = 0;
|
||||||
|
break;
|
||||||
|
case 1: //dpad toward direction for 10 frames
|
||||||
|
if (inward) k.driftPSTick++;
|
||||||
|
else if (k.driftPSTick > 9) {
|
||||||
|
k.driftPSMode++;
|
||||||
|
k.driftPSTick = 1;
|
||||||
|
|
||||||
|
} else k.driftPSTick = 0;
|
||||||
|
break;
|
||||||
|
case 2: //dpad away from direction for 10 frames
|
||||||
|
if (!inward) k.driftPSTick++;
|
||||||
|
else if (k.driftPSTick > 9) {
|
||||||
|
k.driftPSMode++;
|
||||||
|
k.driftPSTick = 1;
|
||||||
|
//play red sparks sound, full MT!
|
||||||
|
setWheelParticles(22, 2); //22 = red flare, 2 = flare priority
|
||||||
|
setWheelParticles(17, 1); //17 = red mt, 1 = drift priority ... 18 is sparks that come out - but their mode is not working yet (spark mode)
|
||||||
|
sounds.powerslide = nitroAudio.playSound(209, {}, 0, k);
|
||||||
|
sounds.powerslide.gainN.gain.value = 2;
|
||||||
|
} else k.driftPSTick = 0;
|
||||||
|
break;
|
||||||
|
case 3: //turbo charged
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!k.drifting) {
|
||||||
|
if (onGround) {
|
||||||
|
var effect = params.colParam[groundEffect];
|
||||||
if (!boosting) {
|
if (!boosting) {
|
||||||
if (k.speed <= top) {
|
if (input.accel) {
|
||||||
k.speed += (k.speed/top > params.driftAccelSwitch)?params.driftAccel2:params.driftAccel1;
|
if (k.speed <= top) {
|
||||||
if (k.speed > top) k.speed = top;
|
k.speed += (k.speed/top > params.accelSwitch)?params.accel2:params.accel1;
|
||||||
|
if (k.speed > top) k.speed = top;
|
||||||
|
} else {
|
||||||
|
k.speed *= 0.95;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
k.speed *= 0.95;
|
k.speed *= params.decel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var turn = ((k.driftMode == 1)?(input.turn-1):(input.turn+1))/2;
|
if ((input.accel && k.speed >= 0) || (k.speed > 0.1)) {
|
||||||
|
k.physicalDir += params.turnRate*input.turn;
|
||||||
k.physicalDir += params.driftTurnRate*turn+((k.driftMode == 1)?-1:1)*(50/32768)*Math.PI; //what is this mystery number i hear you ask? well my friend, this is the turn rate for outward drift.
|
} else if ( k.speed < -0.1) {
|
||||||
|
k.physicalDir -= params.turnRate*input.turn;
|
||||||
//miniturbo code
|
|
||||||
if (input.turn != 0) {
|
|
||||||
var inward = ((input.turn>0) == k.driftMode-1); //if we're turning
|
|
||||||
|
|
||||||
switch (k.driftPSMode) {
|
|
||||||
case 0: //dpad away from direction for 10 frames
|
|
||||||
if (!inward) k.driftPSTick++;
|
|
||||||
else if (k.driftPSTick > 9) {
|
|
||||||
k.driftPSMode++;
|
|
||||||
k.driftPSTick = 1;
|
|
||||||
|
|
||||||
//play blue spark sound, flare
|
|
||||||
setWheelParticles(126, 2); //126 = blue flare, 2 = flare priority
|
|
||||||
var blue = nitroAudio.playSound(210, {}, 0, k);
|
|
||||||
blue.gainN.gain.value = 2;
|
|
||||||
|
|
||||||
} else k.driftPSTick = 0;
|
|
||||||
break;
|
|
||||||
case 1: //dpad toward direction for 10 frames
|
|
||||||
if (inward) k.driftPSTick++;
|
|
||||||
else if (k.driftPSTick > 9) {
|
|
||||||
k.driftPSMode++;
|
|
||||||
k.driftPSTick = 1;
|
|
||||||
|
|
||||||
} else k.driftPSTick = 0;
|
|
||||||
break;
|
|
||||||
case 2: //dpad away from direction for 10 frames
|
|
||||||
if (!inward) k.driftPSTick++;
|
|
||||||
else if (k.driftPSTick > 9) {
|
|
||||||
k.driftPSMode++;
|
|
||||||
k.driftPSTick = 1;
|
|
||||||
//play red sparks sound, full MT!
|
|
||||||
setWheelParticles(22, 2); //22 = red flare, 2 = flare priority
|
|
||||||
setWheelParticles(17, 1); //17 = red mt, 1 = drift priority
|
|
||||||
sounds.powerslide = nitroAudio.playSound(209, {}, 0, k);
|
|
||||||
sounds.powerslide.gainN.gain.value = 2;
|
|
||||||
} else k.driftPSTick = 0;
|
|
||||||
break;
|
|
||||||
case 3: //turbo charged
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!k.drifting) {
|
if (input.drift) {
|
||||||
if (onGround) {
|
ylvel = 1.25;
|
||||||
var effect = params.colParam[groundEffect];
|
k.vel[1] += 1.25;
|
||||||
if (!boosting) {
|
k.airTime = 4;
|
||||||
if (input.accel) {
|
k.drifting = true;
|
||||||
if (k.speed <= top) {
|
k.driftLanded = false;
|
||||||
k.speed += (k.speed/top > params.accelSwitch)?params.accel2:params.accel1;
|
k.driftMode = 0;
|
||||||
if (k.speed > top) k.speed = top;
|
k.ylock = 0;
|
||||||
} else {
|
onGround = false;
|
||||||
k.speed *= 0.95;
|
|
||||||
}
|
var boing = nitroAudio.playSound(207, {transpose: -4}, 0, k);
|
||||||
} else {
|
boing.gainN.gain.value = 2;
|
||||||
k.speed *= params.decel;
|
}
|
||||||
|
} else {
|
||||||
|
if (input.drift) {
|
||||||
|
ylvel = 0;
|
||||||
|
k.drifting = true;
|
||||||
|
k.driftLanded = false;
|
||||||
|
k.driftMode = 0;
|
||||||
|
k.ylock = -0.001;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if ((input.accel && k.speed >= 0) || (k.speed > 0.1)) {
|
|
||||||
k.physicalDir += params.turnRate*input.turn;
|
|
||||||
} else if ( k.speed < -0.1) {
|
|
||||||
k.physicalDir -= params.turnRate*input.turn;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.drift) {
|
|
||||||
ylvel = 1.25;
|
|
||||||
k.vel[1] += 1.25;
|
|
||||||
k.airTime = 4;
|
|
||||||
k.drifting = true;
|
|
||||||
k.driftLanded = false;
|
|
||||||
k.driftMode = 0;
|
|
||||||
k.ylock = 0;
|
|
||||||
onGround = false;
|
|
||||||
|
|
||||||
var boing = nitroAudio.playSound(207, {transpose: -4}, 0, k);
|
|
||||||
boing.gainN.gain.value = 2;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (input.drift) {
|
|
||||||
ylvel = 0;
|
|
||||||
k.drifting = true;
|
|
||||||
k.driftLanded = false;
|
|
||||||
k.driftMode = 0;
|
|
||||||
k.ylock = -0.001;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -708,8 +709,6 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
||||||
|
|
||||||
//move kart.
|
//move kart.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var steps = 0;
|
var steps = 0;
|
||||||
var remainingT = 1;
|
var remainingT = 1;
|
||||||
var baseVel = k.vel;
|
var baseVel = k.vel;
|
||||||
|
@ -754,6 +753,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
||||||
var mat = mat4.create();
|
var mat = mat4.create();
|
||||||
mat4.translate(mat, mat, k.pos);
|
mat4.translate(mat, mat, k.pos);
|
||||||
k.mat = mat4.mul(mat, mat, k.basis);
|
k.mat = mat4.mul(mat, mat, k.basis);
|
||||||
|
if (k.damageMat != null) mat4.mul(mat, mat, k.damageMat);
|
||||||
|
|
||||||
if (input.item) {
|
if (input.item) {
|
||||||
scene.items.addItem(0, scene.karts.indexOf(k), {})
|
scene.items.addItem(0, scene.karts.indexOf(k), {})
|
||||||
|
@ -763,6 +763,83 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
||||||
positionChanged(lastPos, k.pos);
|
positionChanged(lastPos, k.pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function endDrift() {
|
||||||
|
k.drifting = false;
|
||||||
|
clearWheelParticles();
|
||||||
|
if (sounds.powerslide != null) {
|
||||||
|
nitroAudio.instaKill(sounds.powerslide);
|
||||||
|
sounds.powerslide = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function damage(damageType) {
|
||||||
|
if (k.damageType >= damageType) {
|
||||||
|
return; //we are already damaged
|
||||||
|
}
|
||||||
|
//TODO: check invuln state
|
||||||
|
k.specialControlHandler = damagedControls;
|
||||||
|
playCharacterSound((damageType == 0) ? 1 : 0);
|
||||||
|
k.damageType = damageType;
|
||||||
|
k.ylock = 0;
|
||||||
|
|
||||||
|
k.anim.setAnim(k.charRes.spinA);
|
||||||
|
k.animMode = "spin";
|
||||||
|
|
||||||
|
if (k.drifting) {
|
||||||
|
endDrift();
|
||||||
|
}
|
||||||
|
k.boostMT = 0;
|
||||||
|
k.boostNorm = 0;
|
||||||
|
|
||||||
|
switch (damageType) {
|
||||||
|
case 0:
|
||||||
|
k.damageTime = 40;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
k.damageTime = 80;
|
||||||
|
k.vel[1] += 3;
|
||||||
|
ylvel = 3;
|
||||||
|
k.airTime = 4;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
k.damageTime = 105;
|
||||||
|
k.vel[1] += 8;
|
||||||
|
ylvel = 8;
|
||||||
|
k.airTime = 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function damagedControls(kart) {
|
||||||
|
if (--k.damageTime == 0) {
|
||||||
|
k.anim.setAnim(k.charRes.driveA);
|
||||||
|
k.animMode = "drive";
|
||||||
|
k.specialControlHandler = null;
|
||||||
|
k.damageType = -1;
|
||||||
|
k.damageMat = null;
|
||||||
|
}
|
||||||
|
vec3.scale(k.vel, k.vel, 0.98);
|
||||||
|
k.speed *= 0.98;
|
||||||
|
|
||||||
|
var total = 40;
|
||||||
|
switch (k.damageType) {
|
||||||
|
case 1:
|
||||||
|
total = 80;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
total = 105;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var anim = 1 - (k.damageTime / total);
|
||||||
|
|
||||||
|
k.damageMat = mat4.create();
|
||||||
|
var flip = ((k.damageType%2) == 1)? 1 : -1;
|
||||||
|
var animOff = Math.min(1, anim*1.75);
|
||||||
|
mat4.rotateX(k.damageMat, k.damageMat, Math.PI*2 * animOff * k.damageType * flip);
|
||||||
|
if (k.damageType == 0) mat4.rotateY(k.damageMat, k.damageMat, Math.PI*-2 * anim);
|
||||||
|
else mat4.rotateY(k.damageMat, k.damageMat, Math.PI/12 * Math.sin(animOff*Math.PI));
|
||||||
|
}
|
||||||
|
|
||||||
function triggerCannon(id) {
|
function triggerCannon(id) {
|
||||||
if (k.cannon != null) return;
|
if (k.cannon != null) return;
|
||||||
k.cannon = id;
|
k.cannon = id;
|
||||||
|
@ -923,7 +1000,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
||||||
|
|
||||||
k.kartColVel[1] = 0;
|
k.kartColVel[1] = 0;
|
||||||
//play this kart's horn
|
//play this kart's horn
|
||||||
nitroAudio.playSound(192 + charRes.sndOff/14, { volume: 2 }, 0, k);
|
nitroAudio.playSound(193 + charRes.sndOff/14, { volume: 2 }, 0, k);
|
||||||
}
|
}
|
||||||
|
|
||||||
function fixDir(dir) {
|
function fixDir(dir) {
|
||||||
|
@ -940,6 +1017,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateKartSound(mode, input) {
|
function updateKartSound(mode, input) {
|
||||||
|
if (!k.local) return; //for now, don't play kart sounds from other racers.
|
||||||
var turn = (onGround && !k.drifting)?(1-Math.abs(input.turn)/11):1;
|
var turn = (onGround && !k.drifting)?(1-Math.abs(input.turn)/11):1;
|
||||||
var transpose = (mode == 0)?0:(22*turn*Math.min(1.3, k.speed/params.topSpeed));
|
var transpose = (mode == 0)?0:(22*turn*Math.min(1.3, k.speed/params.topSpeed));
|
||||||
|
|
||||||
|
@ -1028,12 +1106,15 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function sndUpdate(view) {
|
function sndUpdate(view) {
|
||||||
|
/*
|
||||||
k.soundProps.pos = vec3.transformMat4([], k.pos, view);
|
k.soundProps.pos = vec3.transformMat4([], k.pos, view);
|
||||||
if (k.soundProps.lastPos != null) k.soundProps.vel = vec3.sub([], k.soundProps.pos, k.soundProps.lastPos);
|
if (k.soundProps.lastPos != null) k.soundProps.vel = vec3.sub([], k.soundProps.pos, k.soundProps.lastPos);
|
||||||
else k.soundProps.vel = [0, 0, 0];
|
else k.soundProps.vel = [0, 0, 0];
|
||||||
|
*/
|
||||||
k.soundProps.lastPos = k.soundProps.pos;
|
k.soundProps.lastPos = k.soundProps.pos;
|
||||||
|
k.soundProps.pos = k.pos; //todo: reintroduce doppler via emulation
|
||||||
|
|
||||||
k.soundProps.refDistance = 192/1024;
|
k.soundProps.refDistance = 192;
|
||||||
k.soundProps.rolloffFactor = 1;
|
k.soundProps.rolloffFactor = 1;
|
||||||
|
|
||||||
var calcVol = (k.soundProps.refDistance / (k.soundProps.refDistance + k.soundProps.rolloffFactor * (Math.sqrt(vec3.dot(k.soundProps.pos, k.soundProps.pos)) - k.soundProps.refDistance)));
|
var calcVol = (k.soundProps.refDistance / (k.soundProps.refDistance + k.soundProps.rolloffFactor * (Math.sqrt(vec3.dot(k.soundProps.pos, k.soundProps.pos)) - k.soundProps.refDistance)));
|
||||||
|
@ -1080,7 +1161,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
||||||
var angle = Math.acos(vec3.dot(vec3.scale(vec3.create(), k.gravity, -1/gravS), n));
|
var angle = Math.acos(vec3.dot(vec3.scale(vec3.create(), k.gravity, -1/gravS), n));
|
||||||
var adjustPos = true;
|
var adjustPos = true;
|
||||||
|
|
||||||
if (colType == MKDS_COLTYPE.OOB || colType == MKDS_COLTYPE.FALL) {
|
if (MKDS_COLTYPE.GROUP_OOB.indexOf(colType) != -1) {
|
||||||
k.OOB = 1;
|
k.OOB = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1101,12 +1182,18 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
||||||
}
|
}
|
||||||
vec3.sub(k.vel, k.vel, vec3.scale(vec3.create(), adjN, proj));
|
vec3.sub(k.vel, k.vel, vec3.scale(vec3.create(), adjN, proj));
|
||||||
|
|
||||||
|
if (colType == MKDS_COLTYPE.KNOCKBACK_DAMAGE && k.damageType == -1) {
|
||||||
|
if (dat.object.vel) vec3.add(k.vel, k.vel, dat.object.vel);
|
||||||
|
vec3.add(k.vel, k.vel, vec3.scale(vec3.create(), adjN, 1.25));
|
||||||
|
k.damage(MKDSCONST.DAMAGE_FLIP);
|
||||||
|
}
|
||||||
|
|
||||||
//convert back to angle + speed to keep change to kart vel
|
//convert back to angle + speed to keep change to kart vel
|
||||||
|
|
||||||
var v = k.vel;
|
var v = k.vel;
|
||||||
k.speed = Math.sqrt(v[0]*v[0]+v[2]*v[2]);
|
k.speed = Math.sqrt(v[0]*v[0]+v[2]*v[2]);
|
||||||
k.angle = Math.atan2(v[0], -v[2]);
|
k.angle = Math.atan2(v[0], -v[2]);
|
||||||
|
stuckTo = dat.object;
|
||||||
} else if (MKDS_COLTYPE.GROUP_ROAD.indexOf(colType) != -1) {
|
} else if (MKDS_COLTYPE.GROUP_ROAD.indexOf(colType) != -1) {
|
||||||
//sliding plane
|
//sliding plane
|
||||||
if (MKDS_COLTYPE.GROUP_BOOST.indexOf(colType) != -1) {
|
if (MKDS_COLTYPE.GROUP_BOOST.indexOf(colType) != -1) {
|
||||||
|
@ -1117,7 +1204,8 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
||||||
|
|
||||||
if (k.vel[1] > 0) k.vel[1] = 0;
|
if (k.vel[1] > 0) k.vel[1] = 0;
|
||||||
var proj = vec3.dot(k.vel, an);
|
var proj = vec3.dot(k.vel, an);
|
||||||
if (!stick && proj < -4 && k.vel[1] < -2) { proj -= 1.5; }
|
if (k.damageType > 0) proj *= 1.7;
|
||||||
|
else if (!stick && proj < -4 && k.vel[1] < -2) { proj -= 1.5; }
|
||||||
vec3.sub(k.vel, k.vel, vec3.scale(vec3.create(), an, proj));
|
vec3.sub(k.vel, k.vel, vec3.scale(vec3.create(), an, proj));
|
||||||
|
|
||||||
if (stick) {
|
if (stick) {
|
||||||
|
|
|
@ -2,19 +2,20 @@
|
||||||
|
|
||||||
window.KartItems = function(kart, scene) {
|
window.KartItems = function(kart, scene) {
|
||||||
var t = this;
|
var t = this;
|
||||||
t.heldItem = null; //of type Item
|
t.heldItem = null; //held item, or item that is bound to us. (bound items have hold type 'func', eg. triple shell)
|
||||||
t.currentItem = null; //string name for item
|
t.currentItem = null; //string name for item
|
||||||
t.specificItem = null;
|
t.specificItem = null;
|
||||||
t.empty = true;
|
t.empty = true;
|
||||||
t.cycleTime = 0;
|
t.cycleTime = 0;
|
||||||
t.totalTime = 230;
|
t.totalTime = 230;
|
||||||
|
|
||||||
var maxItemTime = 230;
|
var maxItemTime = 230;
|
||||||
var minItemTime = 80;
|
var minItemTime = 80;
|
||||||
var carouselSfx = null;
|
var carouselSfx = null;
|
||||||
var lastItemState = false;
|
var lastItemState = false;
|
||||||
var holdAppearDelay = 15;
|
var holdAppearDelay = 15;
|
||||||
|
|
||||||
var hurtExplodeDelay = 105 //turn right slightly, huge double backflip, small bounces.
|
var hurtExplodeDelay = 105; //turn right slightly, huge double backflip, small bounces.
|
||||||
var hurtFlipDelay = 80; //turn right slightly, bounce twice, forward flip
|
var hurtFlipDelay = 80; //turn right slightly, bounce twice, forward flip
|
||||||
var hurtSpinDelay = 40; //counter clockwise spin
|
var hurtSpinDelay = 40; //counter clockwise spin
|
||||||
|
|
||||||
|
@ -43,8 +44,22 @@ window.KartItems = function(kart, scene) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createItem() {
|
||||||
|
var item = scene.items.createItem(t.currentItem, kart);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
function release(input) {
|
||||||
|
if (t.heldItem != null) {
|
||||||
|
t.heldItem.release(input.airTurn);
|
||||||
|
}
|
||||||
|
t.heldItem = null;
|
||||||
|
kart.playCharacterSound(7);
|
||||||
|
}
|
||||||
|
|
||||||
function update(input) {
|
function update(input) {
|
||||||
var pressed = (input.item && !lastItemState);
|
var pressed = (input.item && !lastItemState);
|
||||||
|
var released = (lastItemState && !input.item);
|
||||||
if (!t.empty) {
|
if (!t.empty) {
|
||||||
if (t.currentItem == null) {
|
if (t.currentItem == null) {
|
||||||
//carousel
|
//carousel
|
||||||
|
@ -53,24 +68,48 @@ window.KartItems = function(kart, scene) {
|
||||||
if (carouselSfx != null) nitroAudio.kill(carouselSfx);
|
if (carouselSfx != null) nitroAudio.kill(carouselSfx);
|
||||||
|
|
||||||
//decide on an item
|
//decide on an item
|
||||||
var item = "koura_g";
|
var item = "banana"; //koura_g, banana, f_box, koura_group, koura_group-bomb-7
|
||||||
sfx((specialItems.indexOf(item) == -1) ? 63 : 64);
|
sfx((specialItems.indexOf(item) == -1) ? 63 : 64);
|
||||||
t.currentItem = item;
|
t.currentItem = item;
|
||||||
} else {
|
} else {
|
||||||
//if item button is pressed, we speed up the carousel
|
//if item button is pressed, we speed up the carousel
|
||||||
if (pressed) {
|
if (pressed && t.heldItem == null) {
|
||||||
t.totalTime = Math.max(minItemTime, t.totalTime - 20);
|
t.totalTime = Math.max(minItemTime, t.totalTime - 20);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if (t.heldItem == null) {
|
||||||
if (pressed) {
|
if (pressed) {
|
||||||
//fire?
|
//fire?
|
||||||
t.currentItem = null;
|
t.heldItem = createItem();
|
||||||
t.empty = true;
|
//t.currentItem = null;
|
||||||
kart.playCharacterSound(7);
|
//t.empty = true;
|
||||||
|
|
||||||
|
if (t.heldItem.canBeHeld()) {
|
||||||
|
//begin holding
|
||||||
|
} else {
|
||||||
|
release(input);
|
||||||
|
}
|
||||||
|
pressed = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//todo: if held item has been destroyed, stop holding it.
|
||||||
|
|
||||||
|
if (t.heldItem != null) {
|
||||||
|
if (t.heldItem.dead) {
|
||||||
|
t.heldItem = null;
|
||||||
|
} else {
|
||||||
|
//t.heldItem.updateHold(kart);
|
||||||
|
if (released) {
|
||||||
|
if (t.heldItem.canBeHeld() !== 'func') release(input);
|
||||||
|
} else if (pressed) {
|
||||||
|
//special release: triple shells, bananas. object stays bound when released
|
||||||
|
t.heldItem.release(input.airTurn);
|
||||||
|
kart.playCharacterSound(7);
|
||||||
|
if (t.heldItem.dead) t.heldItem = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
lastItemState = input.item;
|
lastItemState = input.item;
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ window.objDatabase = new (function(){
|
||||||
t[0x0195] = ObjBus;
|
t[0x0195] = ObjBus;
|
||||||
|
|
||||||
|
|
||||||
t[0x00CC] = ObjDecor; //DEBUG: pianta bridge
|
t[0x00CC] = ObjBridge; //DEBUG: pianta bridge
|
||||||
t[0x000D] = ObjDecor; //DEBUG: puddle
|
t[0x000D] = ObjDecor; //DEBUG: puddle
|
||||||
|
|
||||||
t[0x0158] = ObjDecor; //DEBUG: airship (routed)
|
t[0x0158] = ObjDecor; //DEBUG: airship (routed)
|
||||||
|
|
|
@ -41,11 +41,15 @@ window.ObjGear = function(obji, scene) {
|
||||||
t.angle = 0;
|
t.angle = 0;
|
||||||
t.dir = (t.wB1 == 0)
|
t.dir = (t.wB1 == 0)
|
||||||
|
|
||||||
|
t.colFrame = 0;
|
||||||
|
|
||||||
|
var colRes;
|
||||||
|
|
||||||
var dirVel = 0;
|
var dirVel = 0;
|
||||||
|
|
||||||
var prevMat;
|
var prevMat;
|
||||||
var curMat;
|
var curMat;
|
||||||
setMat();
|
var colMat = mat4.create();
|
||||||
prevMat = curMat;
|
prevMat = curMat;
|
||||||
|
|
||||||
function setMat() {
|
function setMat() {
|
||||||
|
@ -59,6 +63,8 @@ window.ObjGear = function(obji, scene) {
|
||||||
mat4.rotateX(mat, mat, obji.angle[0]*(Math.PI/180));
|
mat4.rotateX(mat, mat, obji.angle[0]*(Math.PI/180));
|
||||||
|
|
||||||
mat4.rotateY(mat, mat, t.angle);
|
mat4.rotateY(mat, mat, t.angle);
|
||||||
|
mat4.scale(colMat, mat, [colRes.scale, colRes.scale, colRes.scale]);
|
||||||
|
t.colFrame++;
|
||||||
curMat = mat;
|
curMat = mat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,43 +125,24 @@ window.ObjGear = function(obji, scene) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cloneKCL(kcl) {
|
||||||
|
return JSON.parse(JSON.stringify(kcl));
|
||||||
|
}
|
||||||
|
|
||||||
function provideRes(r) {
|
function provideRes(r) {
|
||||||
res = r; //...and gives them to us. :)
|
res = r; //...and gives them to us. :)
|
||||||
|
colRes = cloneKCL(res.mdl[0].getCollisionModel(0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCollision() {
|
function getCollision() {
|
||||||
var obj = {};
|
return { tris: colRes.dat, mat: colMat, frame: t.colFrame };
|
||||||
var inf = res.mdl[0].getCollisionModel(0, 0);
|
|
||||||
obj.tris = inf.dat;
|
|
||||||
|
|
||||||
var mat = mat4.translate([], mat4.create(), t.pos);
|
|
||||||
mat4.scale(mat, mat, vec3.mul([], [16*inf.scale, 16*inf.scale, 16*inf.scale], t.scale));
|
|
||||||
|
|
||||||
mat4.rotateY(mat, mat, obji.angle[1]*(Math.PI/180));
|
|
||||||
mat4.rotateX(mat, mat, obji.angle[0]*(Math.PI/180));
|
|
||||||
mat4.rotateY(mat, mat, t.angle);
|
|
||||||
|
|
||||||
obj.mat = mat;
|
|
||||||
return obj;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function moveWith(obj) { //used for collidable objects that move.
|
function moveWith(obj) { //used for collidable objects that move.
|
||||||
|
|
||||||
//the most general way to move something with an object is to multiply its position by the inverse mv matrix of that object, and then the new mv matrix.
|
//the most general way to move something with an object is to multiply its position by the inverse mv matrix of that object, and then the new mv matrix.
|
||||||
|
|
||||||
vec3.transformMat4(obj.pos, obj.pos, mat4.invert([], prevMat))
|
vec3.transformMat4(obj.pos, obj.pos, mat4.invert([], prevMat))
|
||||||
vec3.transformMat4(obj.pos, obj.pos, curMat)
|
vec3.transformMat4(obj.pos, obj.pos, curMat)
|
||||||
|
|
||||||
/*var p = vec3.sub([], obj.pos, t.pos);
|
|
||||||
|
|
||||||
if (obji.ID == 0x00D1) { //todo: maybe something more general
|
|
||||||
vec3.transformMat4(p, p, mat4.rotateX([], mat4.create(), dirVel));
|
|
||||||
vec3.add(obj.pos, t.pos, p);
|
|
||||||
} else {
|
|
||||||
vec3.transformMat4(p, p, mat4.rotateY([], mat4.create(), dirVel));
|
|
||||||
vec3.add(obj.pos, t.pos, p);
|
|
||||||
obj.physicalDir -= dirVel;
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -34,7 +34,7 @@ window.GreenShell = function(scene, owner, time, itemID, cliID, params) {
|
||||||
var posSeg = vec3.clone(t.pos);
|
var posSeg = vec3.clone(t.pos);
|
||||||
var ignoreList = [];
|
var ignoreList = [];
|
||||||
while (steps++ < 10 && remainingT > 0.01) {
|
while (steps++ < 10 && remainingT > 0.01) {
|
||||||
var result = lsc.raycast(posSeg, velSeg, scene.kcl, 0.05, ignoreList);
|
var result = lsc.raycast(posSeg, velSeg, kcl, 0.05, ignoreList);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
colResponse(posSeg, velSeg, result, ignoreList)
|
colResponse(posSeg, velSeg, result, ignoreList)
|
||||||
remainingT -= result.t;
|
remainingT -= result.t;
|
||||||
|
@ -87,7 +87,9 @@ window.GreenShell = function(scene, owner, time, itemID, cliID, params) {
|
||||||
|
|
||||||
var v = t.vel;
|
var v = t.vel;
|
||||||
t.angle = Math.atan2(v[0], -v[2]);
|
t.angle = Math.atan2(v[0], -v[2]);
|
||||||
|
item.safeKart = null;
|
||||||
|
} else if (colType == MKDS_COLTYPE.OOB || colType == MKDS_COLTYPE.FALL) {
|
||||||
|
if (item.deadTimer == 0) item.deadTimer++;
|
||||||
} else if (MKDS_COLTYPE.GROUP_ROAD.indexOf(colType) != -1) {
|
} else if (MKDS_COLTYPE.GROUP_ROAD.indexOf(colType) != -1) {
|
||||||
//sliding plane
|
//sliding plane
|
||||||
var proj = vec3.dot(t.vel, n);
|
var proj = vec3.dot(t.vel, n);
|
||||||
|
|
|
@ -44,16 +44,20 @@ window.ObjSoundMaker = function(obji, scene) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function sndUpdate(view) {
|
function sndUpdate(view) {
|
||||||
t.soundProps.pos = vec3.transformMat4([], t.pos, view);
|
//t.soundProps.pos = vec3.transformMat4([], t.pos, view);
|
||||||
t.soundProps.pos = [0, 0, Math.sqrt(vec3.dot(t.soundProps.pos, t.soundProps.pos))]
|
//t.soundProps.pos = [0, 0, Math.sqrt(vec3.dot(t.soundProps.pos, t.soundProps.pos))]
|
||||||
//if (t.soundProps.lastPos != null) t.soundProps.vel = vec3.sub([], t.soundProps.pos, t.soundProps.lastPos);
|
//if (t.soundProps.lastPos != null) t.soundProps.vel = vec3.sub([], t.soundProps.pos, t.soundProps.lastPos);
|
||||||
//else t.soundProps.vel = [0, 0, 0];
|
//else t.soundProps.vel = [0, 0, 0];
|
||||||
//t.soundProps.lastPos = t.soundProps.pos;
|
//t.soundProps.lastPos = t.soundProps.pos;
|
||||||
|
|
||||||
t.soundProps.refDistance = 1024/1024;
|
t.soundProps.pos = t.pos; //todo: when reintroducing doppler, disable it on this source
|
||||||
|
|
||||||
|
t.soundProps.refDistance = 1024;
|
||||||
//t.soundProps.rolloffFactor = 1;
|
//t.soundProps.rolloffFactor = 1;
|
||||||
|
|
||||||
var calcVol = (t.soundProps.refDistance / (t.soundProps.refDistance + t.soundProps.rolloffFactor * (t.soundProps.pos[2] - t.soundProps.refDistance)));
|
var relPos = vec3.transformMat4([], t.pos, view);
|
||||||
|
|
||||||
|
var calcVol = (t.soundProps.refDistance / (t.soundProps.refDistance + t.soundProps.rolloffFactor * (relPos[2] - t.soundProps.refDistance)));
|
||||||
|
|
||||||
if (calcVol<threshold) {
|
if (calcVol<threshold) {
|
||||||
if (sound != null) {
|
if (sound != null) {
|
||||||
|
|
|
@ -17,6 +17,7 @@ window.ObjTruck = function(obji, scene) {
|
||||||
t.pos = vec3.clone(obji.pos);
|
t.pos = vec3.clone(obji.pos);
|
||||||
//t.angle = vec3.clone(obji.angle);
|
//t.angle = vec3.clone(obji.angle);
|
||||||
t.scale = vec3.clone(obji.scale);
|
t.scale = vec3.clone(obji.scale);
|
||||||
|
t.vel = vec3.create();
|
||||||
|
|
||||||
t.requireRes = requireRes;
|
t.requireRes = requireRes;
|
||||||
t.provideRes = provideRes;
|
t.provideRes = provideRes;
|
||||||
|
@ -24,45 +25,138 @@ window.ObjTruck = function(obji, scene) {
|
||||||
t.draw = draw;
|
t.draw = draw;
|
||||||
|
|
||||||
t.route = scene.paths[obji.routeID];
|
t.route = scene.paths[obji.routeID];
|
||||||
t.routeSpeed = (obji.setting1>>16)/100;
|
t.routeSpeed = 0.01;
|
||||||
t.routePos = (obji.setting1&0xFFFF)%t.route.length;
|
t.unknown = (obji.setting1&0xFFFF);
|
||||||
|
t.routePos = (obji.setting1>>16); //(obji.setting1&0xFFFF)%t.route.length;
|
||||||
|
t.variant = (obji.setting2&0xFFFF); //sets design for this car (from nsbtp)
|
||||||
|
t.variant2 = (obji.setting2>>16); //0 or 1. unknown purpose
|
||||||
|
|
||||||
|
t.routePos = (t.routePos+1)%t.route.length;
|
||||||
|
|
||||||
|
t.nodes = [
|
||||||
|
t.route[(t.routePos+t.route.length-2)%t.route.length].pos,
|
||||||
|
t.route[(t.routePos+t.route.length-1)%t.route.length].pos,
|
||||||
|
t.route[t.routePos].pos,
|
||||||
|
t.route[(t.routePos+1)%t.route.length].pos
|
||||||
|
];
|
||||||
|
|
||||||
t.nextNode = t.route[t.routePos];
|
t.nextNode = t.route[t.routePos];
|
||||||
t.prevPos = t.pos;
|
t.prevPos = t.pos;
|
||||||
t.elapsedTime = 0;
|
t.elapsedTime = 0;
|
||||||
|
|
||||||
var facingNormal = [0, 1, 0];
|
|
||||||
var curNormal = [0, 1, 0];
|
var curNormal = [0, 1, 0];
|
||||||
|
var curFloorNormal = [0, 1, 0];
|
||||||
var floorNormal = [0, 1, 0];
|
var floorNormal = [0, 1, 0];
|
||||||
|
|
||||||
|
//collision stuff
|
||||||
|
t.collidable = true;
|
||||||
|
t.colMode = 0;
|
||||||
|
t.colRad = 512;
|
||||||
|
t.colFrame = 0;
|
||||||
|
t.moveWith = moveWith;
|
||||||
|
t.getCollision = getCollision;
|
||||||
|
|
||||||
|
var colRes;
|
||||||
|
|
||||||
|
var dirVel = 0;
|
||||||
|
|
||||||
|
var prevMat;
|
||||||
|
var curMat;
|
||||||
|
var colMat = mat4.create();
|
||||||
|
prevMat = curMat;
|
||||||
|
|
||||||
|
function setMat() {
|
||||||
|
prevMat = curMat;
|
||||||
|
var mat = mat4.create();
|
||||||
|
mat4.translate(mat, mat, t.pos);
|
||||||
|
|
||||||
|
mat4.scale(mat, mat, vec3.scale([], t.scale, 16));
|
||||||
|
|
||||||
|
mat4.mul(mat, mat, mat4.invert([], mat4.lookAt([], [0, 0, 0], curNormal, curFloorNormal)));
|
||||||
|
mat4.scale(colMat, mat, [colRes.scale, colRes.scale, colRes.scale]);
|
||||||
|
t.colFrame++;
|
||||||
|
curMat = mat;
|
||||||
|
}
|
||||||
|
|
||||||
|
//end collision stuff
|
||||||
|
|
||||||
|
function cubic1D(y0, y1, y2, y3, i) {
|
||||||
|
var a0, a1, a2, a3, i2;
|
||||||
|
|
||||||
|
i2 = i*i;
|
||||||
|
a0 = -0.5*y0 + 1.5*y1 - 1.5*y2 + 0.5*y3;
|
||||||
|
a1 = y0 - 2.5*y1 + 2*y2 - 0.5*y3;
|
||||||
|
a2 = -0.5*y0 + 0.5*y2;
|
||||||
|
a3 = y1;
|
||||||
|
|
||||||
|
return(a0 * i * i2 + a1 * i2 + a2 * i + a3);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cubic3D(points, i) { //note: i is 0-1 between point 1 and 2. (0 and 3 are used to better define the curve)
|
||||||
|
var p0 = points[0];
|
||||||
|
var p1 = points[1];
|
||||||
|
var p2 = points[2];
|
||||||
|
var p3 = points[3];
|
||||||
|
return [
|
||||||
|
cubic1D(p0[0], p1[0], p2[0], p3[0], i),
|
||||||
|
cubic1D(p0[1], p1[1], p2[1], p3[1], i),
|
||||||
|
cubic1D(p0[2], p1[2], p2[2], p3[2], i)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
function update(scene) {
|
function update(scene) {
|
||||||
//simple behaviour, just follow the path! piece of cake.
|
//simple behaviour, just follow the path! piece of cake.
|
||||||
|
|
||||||
|
//recalculate our route speed using a target real world speed.
|
||||||
|
var nextTime = t.elapsedTime + t.routeSpeed;
|
||||||
|
for (var i=0; i<((t.elapsedTime == 0)?3:1); i++) {
|
||||||
|
if (nextTime < 1) {
|
||||||
|
var targSpeed = 2;
|
||||||
|
var estimate = cubic3D(t.nodes, nextTime);
|
||||||
|
var estDistance = vec3.dist(estimate, t.pos);
|
||||||
|
t.routeSpeed *= targSpeed/estDistance; //correct
|
||||||
|
if (t.routeSpeed > 0.2) t.routeSpeed = 0.2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (t.routeSpeed <= 0) t.routeSpeed = 0.01;
|
||||||
|
|
||||||
t.elapsedTime += t.routeSpeed;
|
t.elapsedTime += t.routeSpeed;
|
||||||
t.pos = vec3.lerp([], t.prevPos, t.nextNode.pos, t.elapsedTime/t.nextNode.duration);
|
var i = t.elapsedTime;
|
||||||
if (t.elapsedTime >= t.nextNode.duration) {
|
|
||||||
t.elapsedTime = 0;
|
var newPos = cubic3D(t.nodes, i); //vec3.lerp([], t.prevPos, t.nextNode.pos, i);
|
||||||
t.prevPos = t.nextNode.pos;
|
vec3.sub(t.vel, newPos, t.pos);
|
||||||
|
t.pos = newPos;
|
||||||
|
|
||||||
|
if (t.elapsedTime >= 1) {
|
||||||
|
t.elapsedTime -= 1;
|
||||||
|
|
||||||
t.routePos = (t.routePos+1)%t.route.length;
|
t.routePos = (t.routePos+1)%t.route.length;
|
||||||
t.nextNode = t.route[t.routePos];
|
t.nextNode = t.route[t.routePos];
|
||||||
|
t.nodes.splice(0, 1);
|
||||||
|
t.nodes.push(t.route[(t.routePos+1)%t.route.length].pos);
|
||||||
|
t.routeSpeed = 0.25;
|
||||||
}
|
}
|
||||||
|
|
||||||
facingNormal = vec3.sub([], t.prevPos, t.nextNode.pos)
|
curNormal = vec3.sub([], t.prevPos, t.pos)
|
||||||
vec3.normalize(facingNormal, facingNormal);
|
t.prevPos = vec3.clone(t.pos);
|
||||||
|
|
||||||
var rate = 0.025
|
|
||||||
curNormal[0] += (facingNormal[0]-curNormal[0])*rate;
|
|
||||||
curNormal[1] += (facingNormal[1]-curNormal[1])*rate;
|
|
||||||
curNormal[2] += (facingNormal[2]-curNormal[2])*rate;
|
|
||||||
vec3.normalize(curNormal, curNormal);
|
vec3.normalize(curNormal, curNormal);
|
||||||
|
if (isNaN(curNormal[0])) curNormal = [0, 0, 1];
|
||||||
|
|
||||||
var spos = vec3.clone(t.pos);
|
var spos = vec3.clone(t.pos);
|
||||||
spos[1] += 32;
|
spos[1] += 32;
|
||||||
var result = lsc.raycast(spos, [0, -100, 0], scene.kcl, 0.05, []);
|
var result = lsc.raycast(spos, [0, -100, 0], scene, 0.05, []);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
floorNormal = result.normal;
|
floorNormal = result.normal;
|
||||||
} else {
|
} else {
|
||||||
floorNormal = [0,1,0];
|
floorNormal = [0,1,0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var rate = 0.025;
|
||||||
|
curFloorNormal[0] += (floorNormal[0]-curFloorNormal[0])*rate;
|
||||||
|
curFloorNormal[1] += (floorNormal[1]-curFloorNormal[1])*rate;
|
||||||
|
curFloorNormal[2] += (floorNormal[2]-curFloorNormal[2])*rate;
|
||||||
|
vec3.normalize(curFloorNormal, curFloorNormal);
|
||||||
|
setMat();
|
||||||
}
|
}
|
||||||
|
|
||||||
function draw(view, pMatrix) {
|
function draw(view, pMatrix) {
|
||||||
|
@ -70,23 +164,44 @@ window.ObjTruck = function(obji, scene) {
|
||||||
|
|
||||||
mat4.scale(mat, mat, vec3.scale([], t.scale, 16));
|
mat4.scale(mat, mat, vec3.scale([], t.scale, 16));
|
||||||
|
|
||||||
mat4.mul(mat, mat, mat4.invert([], mat4.lookAt([], [0, 0, 0], curNormal, floorNormal)));
|
mat4.mul(mat, mat, mat4.invert([], mat4.lookAt([], [0, 0, 0], curNormal, curFloorNormal)));
|
||||||
|
res.mdl[0].setFrame(t.variant);
|
||||||
res.mdl[0].draw(mat, pMatrix);
|
res.mdl[0].draw(mat, pMatrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
function requireRes() { //scene asks what resources to load
|
function requireRes() { //scene asks what resources to load
|
||||||
switch (obji.ID) {
|
switch (obji.ID) {
|
||||||
case 0x019A:
|
case 0x019A:
|
||||||
return {mdl:[{nsbmd:"car_a.nsbmd"}]}; //one model, car
|
return {mdl:[{nsbmd:"car_a.nsbmd"}], other:["car_a.nsbtp"]}; //one model, car
|
||||||
case 0x019C:
|
case 0x019C:
|
||||||
return {mdl:[{nsbmd:"truck_a.nsbmd"}]}; //one model, truck
|
return {mdl:[{nsbmd:"truck_a.nsbmd"}], other:["truck_a.nsbtp"]}; //one model, truck
|
||||||
case 0x0195:
|
case 0x0195:
|
||||||
return {mdl:[{nsbmd:"bus_a.nsbmd"}]}; //one model, bus
|
return {mdl:[{nsbmd:"bus_a.nsbmd"}]}; //one model, bus
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function moveWith(obj) { //used for collidable objects that move.
|
||||||
|
//the most general way to move something with an object is to multiply its position by the inverse mv matrix of that object, and then the new mv matrix.
|
||||||
|
|
||||||
|
vec3.transformMat4(obj.pos, obj.pos, mat4.invert([], prevMat))
|
||||||
|
vec3.transformMat4(obj.pos, obj.pos, curMat);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCollision() {
|
||||||
|
return { tris: colRes.dat, mat: colMat, frame: t.colFrame };
|
||||||
|
}
|
||||||
|
|
||||||
function provideRes(r) {
|
function provideRes(r) {
|
||||||
res = r; //...and gives them to us. :)
|
res = r; //...and gives them to us. :)
|
||||||
|
if (r.other != null) {
|
||||||
|
if (r.other.length > 0 && r.other[0] != null) {
|
||||||
|
res.mdl[0].loadTexPAnim(r.other[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
colRes = res.mdl[0].getBoundingCollisionModel(0, 0);
|
||||||
|
for (var i=0; i<colRes.dat.length; i++) {
|
||||||
|
colRes.dat[i].CollisionType = MKDS_COLTYPE.KNOCKBACK_DAMAGE << 8;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,10 @@ window.ObjWater = function(obji, scene) {
|
||||||
t.update = update;
|
t.update = update;
|
||||||
t.draw = draw;
|
t.draw = draw;
|
||||||
var frame = 0;
|
var frame = 0;
|
||||||
|
var wheight = 6.144;
|
||||||
|
var wosc = 12.288;
|
||||||
|
var wstay = 5*60;
|
||||||
|
var wchange = 4*60;
|
||||||
|
|
||||||
function draw(view, pMatrix) {
|
function draw(view, pMatrix) {
|
||||||
if (nitroRender.flagShadow) return;
|
if (nitroRender.flagShadow) return;
|
||||||
|
@ -34,34 +38,35 @@ window.ObjWater = function(obji, scene) {
|
||||||
gl.stencilFunc(gl.ALWAYS, 1, 0xFF);
|
gl.stencilFunc(gl.ALWAYS, 1, 0xFF);
|
||||||
gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); //when depth test passes for water lower layer, pixel is already drawn, do not cover it with the white overlay (set stencil bit)
|
gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); //when depth test passes for water lower layer, pixel is already drawn, do not cover it with the white overlay (set stencil bit)
|
||||||
|
|
||||||
var height = (t.pos[1])+6.144+Math.sin(frame/150)*12.288 //0.106
|
var height = (t.pos[1])+wheight+Math.sin(frame/150)*wosc //0.106
|
||||||
|
|
||||||
mat4.translate(waterM, view, [Math.sin(frame/180)*96, height-3.072, Math.cos(frame/146)*96])
|
mat4.translate(waterM, view, [Math.sin(frame/180)*96, height, Math.cos(frame/146)*96])
|
||||||
nitroRender.setAlpha(0x0A/31);
|
nitroRender.setColMult([1, 1, 1, 0x0A/31]);
|
||||||
res.mdl[0].drawPoly(mat4.scale([], waterM, [16, 16, 16]), pMatrix, 0, 0); //water
|
res.mdl[0].drawPoly(mat4.scale([], waterM, [16, 16, 16]), pMatrix, 0, 0); //water
|
||||||
|
|
||||||
if (res.mdl[1] != null) {
|
|
||||||
mat4.translate(waterM, view, [-Math.sin((frame+30)/180)*96, height-3, Math.cos((frame+100)/146)*96])
|
|
||||||
nitroRender.setAlpha(0x02/31);
|
|
||||||
res.mdl[1].draw(mat4.scale([], waterM, [16, 16, 16]), pMatrix); //water white detail part. stencil should do nothing here, since it's in the same position as the above.
|
|
||||||
}
|
|
||||||
|
|
||||||
gl.stencilFunc(gl.EQUAL, 0, 0xFF);
|
gl.stencilFunc(gl.EQUAL, 0, 0xFF);
|
||||||
gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
|
gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
|
||||||
|
|
||||||
if (!obji.ID == 9) {
|
if (obji.ID != 9) {
|
||||||
mat4.translate(waterM, view, [0, height, 0])
|
mat4.translate(waterM, view, [0, height, 0])
|
||||||
nitroRender.setAlpha(0x10/31);
|
nitroRender.setColMult([1, 1, 1, 0x10/31]);
|
||||||
res.mdl[0].drawPoly(mat4.scale([], waterM, [16, 16, 16]), pMatrix, 0, 1); //white shore wash part, water is stencil masked out
|
res.mdl[0].drawPoly(mat4.scale([], waterM, [16, 16, 16]), pMatrix, 0, 1); //white shore wash part, water is stencil masked out
|
||||||
}
|
}
|
||||||
|
|
||||||
gl.disable(gl.STENCIL_TEST);
|
gl.disable(gl.STENCIL_TEST);
|
||||||
|
|
||||||
nitroRender.setAlpha(1);
|
if (res.mdl[1] != null) {
|
||||||
|
mat4.translate(waterM, view, [-Math.sin((frame+30)/180)*96, height, Math.cos((frame+100)/146)*96])
|
||||||
|
nitroRender.setColMult([1, 1, 1, 0x04/31]);
|
||||||
|
res.mdl[1].draw(mat4.scale([], waterM, [16, 16, 16]), pMatrix); //water white detail part. stencil should do nothing here, since it's in the same position as the above.
|
||||||
|
}
|
||||||
|
|
||||||
|
nitroRender.setColMult([1, 1, 1, 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function update() {
|
function update() {
|
||||||
frame = (frame+1)%197100; //it's a big number but yolo... we have the technology...
|
frame = (frame+1)%197100; //it's a big number but yolo... we have the technology...
|
||||||
|
//TODO: physics and void-out for karts
|
||||||
}
|
}
|
||||||
|
|
||||||
function requireRes() { //scene asks what resources to load
|
function requireRes() { //scene asks what resources to load
|
||||||
|
@ -75,6 +80,8 @@ window.ObjWater = function(obji, scene) {
|
||||||
case 0x0009:
|
case 0x0009:
|
||||||
return {mdl:[{nsbmd:"hyudoro_waterC.nsbmd"}, {nsbmd:"hyudoro_waterA.nsbmd"}]};
|
return {mdl:[{nsbmd:"hyudoro_waterC.nsbmd"}, {nsbmd:"hyudoro_waterA.nsbmd"}]};
|
||||||
case 0x000C:
|
case 0x000C:
|
||||||
|
wheight = 38;
|
||||||
|
wosc = 16;
|
||||||
return {mdl:[{nsbmd:"mini_stage3_waterC.nsbmd"}, {nsbmd:"mini_stage3_waterA.nsbmd"}]};
|
return {mdl:[{nsbmd:"mini_stage3_waterC.nsbmd"}, {nsbmd:"mini_stage3_waterA.nsbmd"}]};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,156 @@
|
||||||
|
//
|
||||||
|
// ncer.js
|
||||||
|
//--------------------
|
||||||
|
// Loads ncer files and provides a variety of functions for accessing and using the data.
|
||||||
|
// Cell data for nitro 2d graphics. Multiple images made out of multiple cells, sharing an input palette and ncgr.
|
||||||
|
// by RHY3756547
|
||||||
|
//
|
||||||
|
|
||||||
|
window.ncer = function(input) {
|
||||||
|
var dimensions = [ //indexed by width, then height
|
||||||
|
[[8,8], [16,8], [8,16]],
|
||||||
|
[[16,16], [32,8], [8,32]],
|
||||||
|
[[32,32], [32,16], [16,32]],
|
||||||
|
[[64,64], [64,32], [32,64]],
|
||||||
|
]
|
||||||
|
|
||||||
|
var mainOff;
|
||||||
|
var t = this;
|
||||||
|
if (input != null) {
|
||||||
|
load(input);
|
||||||
|
}
|
||||||
|
this.load = load;
|
||||||
|
|
||||||
|
function load(input) {
|
||||||
|
var view = new DataView(input);
|
||||||
|
var header = null;
|
||||||
|
var offset = 0;
|
||||||
|
var tex;
|
||||||
|
|
||||||
|
//nitro 3d header
|
||||||
|
header = nitro.readHeader(view);
|
||||||
|
if (header.stamp != "RECN") throw "NCER invalid. Expected RECN, found "+header.stamp;
|
||||||
|
if (header.numSections < 1 || header.numSections > 3) throw "NCER invalid. Too many sections - should have 1-3.";
|
||||||
|
//end nitro
|
||||||
|
t.sectionOffsets = header.sectionOffsets;
|
||||||
|
t.sectionOffsets[0] = 0x18;
|
||||||
|
|
||||||
|
mainOff = offset;
|
||||||
|
|
||||||
|
t.cebk = loadCEBK(view);
|
||||||
|
/* ignore for now
|
||||||
|
t.labl = loadLABL(view);
|
||||||
|
t.uext = loadUEXT(view); //external data?
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSize(shape, size, double) {
|
||||||
|
var dim = dimensions[size][shape];
|
||||||
|
if (double) return [dim[0]*2, dim[1]*2];
|
||||||
|
return [dim[0], dim[1]];
|
||||||
|
}
|
||||||
|
|
||||||
|
function readOAM(view, offset) {
|
||||||
|
//see ds docs. really, any of them?
|
||||||
|
var obj0 = view.getUint16(offset, true);
|
||||||
|
var obj1 = view.getUint16(offset + 2, true);
|
||||||
|
var obj2 = view.getUint16(offset + 4, true);
|
||||||
|
|
||||||
|
var rsFlag = (obj0 & 0x100) > 0;
|
||||||
|
var x = obj1 & 0x01FF;
|
||||||
|
var y = obj0 & 0xFF;
|
||||||
|
var result = {
|
||||||
|
y: (y > 0x80) ? y-0x100 : y,
|
||||||
|
rsFlag: rsFlag,
|
||||||
|
disable: (!rsFlag && (obj0 & 0x200) > 0),
|
||||||
|
doubleSize: (rsFlag && (obj0 & 0x200) > 0),
|
||||||
|
objMode: (obj0 >> 10) & 0x3,
|
||||||
|
mosaic: (obj0 & 0x1000) > 0,
|
||||||
|
depth: (obj0 & 0x2000) > 0,
|
||||||
|
shape: (obj0 >> 14) & 0x3, //used in combination with size to determine final x+y tile size.
|
||||||
|
|
||||||
|
x: (x > 0x100) ? x-0x200 : x,
|
||||||
|
|
||||||
|
xFlip: (!rsFlag && (obj1 & 0x1000) > 0),
|
||||||
|
yFlip: (!rsFlag && (obj1 & 0x2000) > 0),
|
||||||
|
|
||||||
|
selectParam: rsFlag ? ((obj1 >> 9) & 0x1F) : 0,
|
||||||
|
|
||||||
|
size: (obj1 >> 14) & 0x3,
|
||||||
|
|
||||||
|
tileOffset: obj2 & 0x03FF,
|
||||||
|
priority: (obj2 >> 10) & 3,
|
||||||
|
pal: (obj2 >> 12) & 0xF,
|
||||||
|
}
|
||||||
|
result.size = getSize(result.shape, result.size, result.doubleSize);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadCEBK(view) { //cell bank
|
||||||
|
var offset = t.sectionOffsets[0] - 8;
|
||||||
|
|
||||||
|
var cebk = {};
|
||||||
|
cebk.type = readChar(view, offset+0x0)+readChar(view, offset+0x1)+readChar(view, offset+0x2)+readChar(view, offset+0x3);
|
||||||
|
if (cebk.type != "KBEC") throw "NCER invalid. Expected KBEC, found "+cebk.type;
|
||||||
|
cebk.blockSize = view.getUint32(offset+0x4, true);
|
||||||
|
cebk.imageCount = view.getUint16(offset+0x8, true);
|
||||||
|
cebk.bankType = view.getUint16(offset+0xA, true); //type 1 has additional fields
|
||||||
|
cebk.unknown = view.getUint32(offset+0xC, true); //always 0x12
|
||||||
|
cebk.boundarySize = view.getUint32(offset+0x10, true) * 64; //area in which the image can be drawn (pixel height AND width)
|
||||||
|
cebk.partitionDataOffset = view.getUint32(offset+0x14, true);
|
||||||
|
//pad 0x18
|
||||||
|
//pad 0x1C
|
||||||
|
|
||||||
|
cebk.images = [];
|
||||||
|
|
||||||
|
offset += 0x20;
|
||||||
|
var tableEnd = offset + cebk.imageCount * (8 + cebk.bankType * 8);
|
||||||
|
for (var i=0; i<cebk.imageCount; i++) {
|
||||||
|
var image = {};
|
||||||
|
image.numCells = view.getUint16(offset, true);
|
||||||
|
image.readOnlyInfo = view.getUint16(offset + 0x2, true);
|
||||||
|
image.offset = view.getInt32(offset + 0x4, true);
|
||||||
|
offset += 0x8;
|
||||||
|
if (cebk.bankType == 1) {
|
||||||
|
image.xMax = view.getInt16(offset, true);
|
||||||
|
image.yMax = view.getInt16(offset+2, true);
|
||||||
|
image.xMin = view.getInt16(offset+4, true);
|
||||||
|
image.yMin = view.getInt16(offset+6, true);
|
||||||
|
offset += 0x8;
|
||||||
|
}
|
||||||
|
|
||||||
|
var offset2 = tableEnd + image.offset;
|
||||||
|
image.cells = [];
|
||||||
|
for (var j=0; j<image.numCells; j++) {
|
||||||
|
var cell = readOAM(view, offset2);
|
||||||
|
offset2 += 6;
|
||||||
|
image.cells.push(cell);
|
||||||
|
}
|
||||||
|
cebk.images.push(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cebk.partitionDataOffset != 0) { //not sure what this does, just that it's here
|
||||||
|
var pOff = t.sectionOffsets[0] + cebk.partitionDataOffset;
|
||||||
|
cebk.maxPartitionSize = view.getUint32(pOff, true);
|
||||||
|
cebk.firstOffset = view.getUint32(pOff+4, true);
|
||||||
|
pOff += cebk.firstOffset;
|
||||||
|
for (var i=0; i<cebk.imageCount; i++) {
|
||||||
|
cebk.images[i].partitionOffset = view.getUint32(pOff, true);
|
||||||
|
cebk.images[i].partitionSize = view.getUint32(pOff+4, true);
|
||||||
|
pOff += 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cebk;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readPalColour(view, ind) {
|
||||||
|
var col = view.getUint16(ind, true);
|
||||||
|
var f = 255/31;
|
||||||
|
return [Math.round((col&31)*f), Math.round(((col>>5)&31)*f), Math.round(((col>>10)&31)*f), 255];
|
||||||
|
}
|
||||||
|
|
||||||
|
function readChar(view, offset) {
|
||||||
|
return String.fromCharCode(view.getUint8(offset));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
//
|
||||||
|
// ncgr.js
|
||||||
|
//--------------------
|
||||||
|
// Loads ncgr files and provides a variety of functions for accessing and using the data.
|
||||||
|
// "Graphics Resource", as in tile data. Usually rendered in conjunction with Palette (nclr) and Cell (ncer) / screen (nscr) data.
|
||||||
|
// by RHY3756547
|
||||||
|
//
|
||||||
|
|
||||||
|
window.ncgr = function(input) {
|
||||||
|
|
||||||
|
var mainOff;
|
||||||
|
var t = this;
|
||||||
|
if (input != null) {
|
||||||
|
load(input);
|
||||||
|
}
|
||||||
|
this.load = load;
|
||||||
|
|
||||||
|
function load(input) {
|
||||||
|
var view = new DataView(input);
|
||||||
|
var header = null;
|
||||||
|
var offset = 0;
|
||||||
|
var tex;
|
||||||
|
|
||||||
|
//nitro 3d header
|
||||||
|
header = nitro.readHeader(view);
|
||||||
|
if (header.stamp != "RGCN") throw "NCGR invalid. Expected RGCN, found "+header.stamp;
|
||||||
|
if (header.numSections < 1 || header.numSections > 2) throw "NCGR invalid. Too many sections - should have 2.";
|
||||||
|
offset = header.sectionOffsets[0];
|
||||||
|
//end nitro
|
||||||
|
t.sectionOffsets = header.sectionOffsets;
|
||||||
|
t.sectionOffsets[0] = 0x18;
|
||||||
|
|
||||||
|
mainOff = offset;
|
||||||
|
|
||||||
|
t.char = loadCHAR(view);
|
||||||
|
if (header.numSections > 1) t.cpos = loadCPOS(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadCHAR(view) {
|
||||||
|
var offset = t.sectionOffsets[0] - 8;
|
||||||
|
|
||||||
|
var char = {};
|
||||||
|
char.type = readChar(view, offset+0x0)+readChar(view, offset+0x1)+readChar(view, offset+0x2)+readChar(view, offset+0x3);
|
||||||
|
if (char.type != "RAHC") throw "NCGR invalid. Expected RAHC, found "+char.type;
|
||||||
|
char.blockSize = view.getUint32(offset+0x4, true);
|
||||||
|
t.sectionOffsets[1] = t.sectionOffsets[0] + char.blockSize;
|
||||||
|
char.tilesY = view.getUint16(offset+0x8, true); //(tiles y)
|
||||||
|
char.tilesX = view.getUint16(offset+0xA, true); //(tiles x)
|
||||||
|
char.bitDepth = view.getUint32(offset+0xC, true); //3 - 4bits, 4 - 8bits
|
||||||
|
//pad 0x10
|
||||||
|
char.tiledFlag = view.getUint32(offset+0x14, true);
|
||||||
|
char.tileDataSize = view.getUint32(offset+0x18, true);
|
||||||
|
char.unknown = view.getUint32(offset+0x1C, true); //usually 24
|
||||||
|
offset += 0x20;
|
||||||
|
|
||||||
|
//tiles are 8 or 4 bit index to pal data
|
||||||
|
//64 pixels per tile (8*8)
|
||||||
|
var tileCount = (char.blockSize-0x20) / ((char.bitDepth == 4) ? 64 : 32);
|
||||||
|
char.tiles = [];
|
||||||
|
for (var i=0; i<tileCount; i++) {
|
||||||
|
var tile = [];
|
||||||
|
if (char.bitDepth == 4) {
|
||||||
|
//easy, just read 1024 bytes
|
||||||
|
for (var j=0; j<64; j++) tile.push(view.getUint8(offset++));
|
||||||
|
} else {
|
||||||
|
for (var j=0; j<32; j++) {
|
||||||
|
var dat = view.getUint8(offset++);
|
||||||
|
tile.push(dat&0xF);
|
||||||
|
tile.push(dat>>4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
char.tiles.push(tile);
|
||||||
|
}
|
||||||
|
return char;
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadCPOS(view) { //palette count map, supposedly. maps each palette to an ID
|
||||||
|
var offset = t.sectionOffsets[1] - 8;
|
||||||
|
|
||||||
|
var cpos = {};
|
||||||
|
cpos.type = readChar(view, offset+0x0)+readChar(view, offset+0x1)+readChar(view, offset+0x2)+readChar(view, offset+0x3);
|
||||||
|
if (cpos.type != "SOPC") throw "NCLR invalid. Expected SOPC, found "+stamp;
|
||||||
|
cpos.blockSize = view.getUint32(offset+0x4, true);
|
||||||
|
//padding 0x8
|
||||||
|
|
||||||
|
cpos.tileSize = view.getUint16(offset+0xC, true); //always 32
|
||||||
|
cpos.tileCount = view.getUint16(offset+0xE, true);
|
||||||
|
return cpos;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readPalColour(view, ind) {
|
||||||
|
var col = view.getUint16(ind, true);
|
||||||
|
var f = 255/31;
|
||||||
|
return [Math.round((col&31)*f), Math.round(((col>>5)&31)*f), Math.round(((col>>10)&31)*f), 255];
|
||||||
|
}
|
||||||
|
|
||||||
|
function readChar(view, offset) {
|
||||||
|
return String.fromCharCode(view.getUint8(offset));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
//
|
||||||
|
// nclr.js
|
||||||
|
//--------------------
|
||||||
|
// Loads nclr files and provides a variety of functions for accessing and using the data.
|
||||||
|
// Palette information for nitro 2d graphics.
|
||||||
|
// by RHY3756547
|
||||||
|
//
|
||||||
|
|
||||||
|
window.nclr = function(input) {
|
||||||
|
|
||||||
|
var mainOff;
|
||||||
|
var t = this;
|
||||||
|
if (input != null) {
|
||||||
|
load(input);
|
||||||
|
}
|
||||||
|
this.load = load;
|
||||||
|
|
||||||
|
function load(input) {
|
||||||
|
var view = new DataView(input);
|
||||||
|
var header = null;
|
||||||
|
var offset = 0;
|
||||||
|
var tex;
|
||||||
|
|
||||||
|
//nitro 3d header
|
||||||
|
header = nitro.readHeader(view);
|
||||||
|
if (header.stamp != "RLCN") throw "NCLR invalid. Expected RLCN, found "+header.stamp;
|
||||||
|
if (header.numSections < 1 || header.numSections > 2) throw "NCLR invalid. Too many sections - should have 2.";
|
||||||
|
offset = header.sectionOffsets[0];
|
||||||
|
//end nitro
|
||||||
|
t.sectionOffsets = header.sectionOffsets;
|
||||||
|
t.sectionOffsets[0] = 0x18;
|
||||||
|
|
||||||
|
mainOff = offset;
|
||||||
|
|
||||||
|
t.pltt = loadPLTT(view);
|
||||||
|
if (header.numSections > 1) t.pcmp = loadPCMP(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadPLTT(view) {
|
||||||
|
var offset = t.sectionOffsets[0] - 8;
|
||||||
|
|
||||||
|
var pltt = {};
|
||||||
|
pltt.type = readChar(view, offset+0x0)+readChar(view, offset+0x1)+readChar(view, offset+0x2)+readChar(view, offset+0x3);
|
||||||
|
if (pltt.type != "TTLP") throw "NCLR invalid. Expected TTLP, found "+pltt.type;
|
||||||
|
pltt.blockSize = view.getUint32(offset+0x4, true);
|
||||||
|
t.sectionOffsets[1] = t.sectionOffsets[0] + pltt.blockSize;
|
||||||
|
pltt.bitDepth = view.getUint32(offset+0x8, true); //3 -> 4bit, 4 -> 8bit
|
||||||
|
pltt.padding = view.getUint32(offset+0xC, true);
|
||||||
|
pltt.palEntries = view.getUint32(offset+0x10, true) / 2; //stored in bytes, 2 bytes per col. seems to be wrong sometimes? (8bit mode, padding as 1)
|
||||||
|
pltt.colorsPerPal = view.getUint32(offset+0x14, true); //usually 16
|
||||||
|
|
||||||
|
//16-bit pallete data
|
||||||
|
//XBBBBBGGGGGRRRRR
|
||||||
|
|
||||||
|
var colsPerPal = (pltt.bitDepth == 4)?256:16;
|
||||||
|
var realPalCount = (pltt.blockSize - 0x18) / 2;
|
||||||
|
|
||||||
|
offset += 0x18;
|
||||||
|
pltt.palettes = [];
|
||||||
|
var curPal = [];
|
||||||
|
for (var i=0; i<realPalCount; i++) {
|
||||||
|
curPal.push(readPalColour(view, offset));
|
||||||
|
if (curPal.length >= colsPerPal) {
|
||||||
|
pltt.palettes.push(curPal);
|
||||||
|
curPal = [];
|
||||||
|
}
|
||||||
|
offset += 2;
|
||||||
|
}
|
||||||
|
if (curPal.length > 0) pltt.palettes.push(curPal);
|
||||||
|
return pltt;
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadPCMP(view) { //palette count map, supposedly. maps each palette to an ID
|
||||||
|
var offset = t.sectionOffsets[1] - 8;
|
||||||
|
|
||||||
|
var pcmp = {};
|
||||||
|
pcmp.type = readChar(view, offset+0x0)+readChar(view, offset+0x1)+readChar(view, offset+0x2)+readChar(view, offset+0x3);
|
||||||
|
if (pcmp.type != "PMCP") throw "NCLR invalid. Expected PMCP, found "+stamp;
|
||||||
|
pcmp.blockSize = view.getUint32(offset+0x4, true);
|
||||||
|
pcmp.palCount = view.getUint16(offset+0x8, true);
|
||||||
|
//unknown 16: 0?
|
||||||
|
pcmp.unknown = view.getUint32(offset+0xC, true);
|
||||||
|
|
||||||
|
offset += 0x10;
|
||||||
|
var palIDs = [];
|
||||||
|
for (var i=0; i<pcmp.palCount; i++) {
|
||||||
|
palIDs.push(view.getUint16(offset, true));
|
||||||
|
offset += 2;
|
||||||
|
}
|
||||||
|
return pcmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readPalColour(view, ind) {
|
||||||
|
var col = view.getUint16(ind, true);
|
||||||
|
var f = 255/31;
|
||||||
|
return [Math.round((col&31)*f), Math.round(((col>>5)&31)*f), Math.round(((col>>10)&31)*f), 255];
|
||||||
|
}
|
||||||
|
|
||||||
|
function readChar(view, offset) {
|
||||||
|
return String.fromCharCode(view.getUint8(offset));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
//
|
||||||
|
// nscr.js
|
||||||
|
//--------------------
|
||||||
|
// Loads nscr files and provides a variety of functions for accessing and using the data.
|
||||||
|
// Screen data for nitro 2d graphics. Each cell references a graphic (ncgr) and palette (nclr).
|
||||||
|
// by RHY3756547
|
||||||
|
//
|
||||||
|
|
||||||
|
window.nscr = function(input) {
|
||||||
|
|
||||||
|
var mainOff;
|
||||||
|
var t = this;
|
||||||
|
if (input != null) {
|
||||||
|
load(input);
|
||||||
|
}
|
||||||
|
this.load = load;
|
||||||
|
|
||||||
|
function load(input) {
|
||||||
|
var view = new DataView(input);
|
||||||
|
var header = null;
|
||||||
|
var offset = 0;
|
||||||
|
var tex;
|
||||||
|
|
||||||
|
//nitro 3d header
|
||||||
|
header = nitro.readHeader(view);
|
||||||
|
if (header.stamp != "RCSN") throw "NSCR invalid. Expected RCSN, found "+header.stamp;
|
||||||
|
if (header.numSections != 1) throw "NSCR invalid. Too many sections - should have 1.";
|
||||||
|
offset = header.sectionOffsets[0];
|
||||||
|
//end nitro
|
||||||
|
t.sectionOffsets = header.sectionOffsets;
|
||||||
|
t.sectionOffsets[0] = 0x18;
|
||||||
|
|
||||||
|
mainOff = offset;
|
||||||
|
|
||||||
|
t.scrn = loadSCRN(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadSCRN(view) {
|
||||||
|
var offset = t.sectionOffsets[0] - 8;
|
||||||
|
|
||||||
|
var scrn = {};
|
||||||
|
scrn.type = readChar(view, offset+0x0)+readChar(view, offset+0x1)+readChar(view, offset+0x2)+readChar(view, offset+0x3);
|
||||||
|
if (scrn.type != "NRCS") throw "SCRN invalid. Expected NRCS, found "+scrn.type;
|
||||||
|
scrn.blockSize = view.getUint32(offset+0x4, true);
|
||||||
|
t.sectionOffsets[1] = t.sectionOffsets[0] + scrn.blockSize;
|
||||||
|
scrn.screenWidth = view.getUint16(offset+0x8, true); //in pixels
|
||||||
|
scrn.screenHeight = view.getUint16(offset+0xA, true);
|
||||||
|
scrn.padding = view.getUint32(offset+0xC, true); //always 0
|
||||||
|
scrn.screenDataSize = view.getUint32(offset+0x10, true);
|
||||||
|
offset += 0x14;
|
||||||
|
|
||||||
|
var entries = (scrn.blockSize - 0x14)/2;
|
||||||
|
scrn.data = [];
|
||||||
|
|
||||||
|
for (var i=0; i<entries; i++) {
|
||||||
|
scrn.data.push(view.getUint16(offset, true));
|
||||||
|
offset += 2;
|
||||||
|
}
|
||||||
|
return scrn;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Format is (YYYYXXNNNNNNNNNN)
|
||||||
|
Y4 Palette Number
|
||||||
|
X2 Transformation (YFlip/XFlip)
|
||||||
|
N10 Tile Number
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
function readPalColour(view, ind) {
|
||||||
|
var col = view.getUint16(ind, true);
|
||||||
|
var f = 255/31;
|
||||||
|
return [Math.round((col&31)*f), Math.round(((col>>5)&31)*f), Math.round(((col>>10)&31)*f), 255];
|
||||||
|
}
|
||||||
|
|
||||||
|
function readChar(view, offset) {
|
||||||
|
return String.fromCharCode(view.getUint8(offset));
|
||||||
|
}
|
||||||
|
}
|
|
@ -42,7 +42,6 @@ window.kartoffsetdata = function(input) {
|
||||||
var pos = vec3.create();
|
var pos = vec3.create();
|
||||||
pos[0] = view.getInt32(off, true)/4096;
|
pos[0] = view.getInt32(off, true)/4096;
|
||||||
pos[1] = view.getInt32(off+4, true)/4096;
|
pos[1] = view.getInt32(off+4, true)/4096;
|
||||||
console.log("charPos: "+pos[1]);
|
|
||||||
pos[2] = view.getInt32(off+8, true)/4096;
|
pos[2] = view.getInt32(off+8, true)/4096;
|
||||||
off += 12;
|
off += 12;
|
||||||
chars.push(pos);
|
chars.push(pos);
|
||||||
|
|
|
@ -84,7 +84,7 @@ window.kcl = function(input, mkwii) {
|
||||||
while (offset < octreeOffset) {
|
while (offset < octreeOffset) {
|
||||||
planes.push(new Plane(view, offset));
|
planes.push(new Plane(view, offset));
|
||||||
offset += 0x10;
|
offset += 0x10;
|
||||||
var vert = planes[planes.length-1].Vertex1;
|
var vert = planes[planes.length-1].Vertices[0];
|
||||||
if (vert[0] < minx) minx=vert[0];
|
if (vert[0] < minx) minx=vert[0];
|
||||||
if (vert[0] > maxx) maxx=vert[0];
|
if (vert[0] > maxx) maxx=vert[0];
|
||||||
if (vert[2] < minz) minz=vert[2];
|
if (vert[2] < minz) minz=vert[2];
|
||||||
|
@ -177,9 +177,9 @@ window.kcl = function(input, mkwii) {
|
||||||
var plane = planes[i];
|
var plane = planes[i];
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
|
|
||||||
ctx.moveTo(plane.Vertex1[0], plane.Vertex1[2]);
|
ctx.moveTo(plane.Vertices[0][0], plane.Vertices[0][2]);
|
||||||
ctx.lineTo(plane.Vertex2[0], plane.Vertex2[2]);
|
ctx.lineTo(plane.Vertices[1][0], plane.Vertices[1][2]);
|
||||||
ctx.lineTo(plane.Vertex3[0], plane.Vertex3[2]);
|
ctx.lineTo(plane.Vertices[2][0], plane.Vertices[2][2]);
|
||||||
|
|
||||||
ctx.closePath();
|
ctx.closePath();
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
|
@ -253,7 +253,8 @@ window.kcl = function(input, mkwii) {
|
||||||
|
|
||||||
function Plane(view, offset) {
|
function Plane(view, offset) {
|
||||||
this.Len = readBigDec(view, offset, mkwiiMode);
|
this.Len = readBigDec(view, offset, mkwiiMode);
|
||||||
this.Vertex1 = readVert(view.getUint16(offset+0x4, end), view);
|
this.Vertices = [];
|
||||||
|
this.Vertices[0] = readVert(view.getUint16(offset+0x4, end), view);
|
||||||
this.Normal = readNormal(view.getUint16(offset+0x6, end), view);
|
this.Normal = readNormal(view.getUint16(offset+0x6, end), view);
|
||||||
this.NormalA = readNormal(view.getUint16(offset+0x8, end), view);
|
this.NormalA = readNormal(view.getUint16(offset+0x8, end), view);
|
||||||
this.NormalB = readNormal(view.getUint16(offset+0xA, end), view);
|
this.NormalB = readNormal(view.getUint16(offset+0xA, end), view);
|
||||||
|
@ -263,8 +264,8 @@ window.kcl = function(input, mkwii) {
|
||||||
var crossA = vec3.cross(vec3.create(), this.NormalA, this.Normal);
|
var crossA = vec3.cross(vec3.create(), this.NormalA, this.Normal);
|
||||||
var crossB = vec3.cross(vec3.create(), this.NormalB, this.Normal);
|
var crossB = vec3.cross(vec3.create(), this.NormalB, this.Normal);
|
||||||
|
|
||||||
this.Vertex2 = vec3.scaleAndAdd(vec3.create(), this.Vertex1, crossB, (this.Len/vec3.dot(crossB, this.NormalC)));
|
this.Vertices[1] = vec3.scaleAndAdd(vec3.create(), this.Vertices[0], crossB, (this.Len/vec3.dot(crossB, this.NormalC)));
|
||||||
this.Vertex3 = vec3.scaleAndAdd(vec3.create(), this.Vertex1, crossA, (this.Len/vec3.dot(crossA, this.NormalC)));
|
this.Vertices[2] = vec3.scaleAndAdd(vec3.create(), this.Vertices[0], crossA, (this.Len/vec3.dot(crossA, this.NormalC)));
|
||||||
}
|
}
|
||||||
|
|
||||||
function readVert(num, view) {
|
function readVert(num, view) {
|
||||||
|
|
|
@ -5,10 +5,34 @@
|
||||||
// by RHY3756547
|
// by RHY3756547
|
||||||
//
|
//
|
||||||
|
|
||||||
|
// for reading from multiple narcs as one. (eg. Race.narc, Race_us.narc)
|
||||||
|
window.narcGroup = function(files) {
|
||||||
|
this.getFile = getFile;
|
||||||
|
this.list = list;
|
||||||
|
|
||||||
|
function getFile(name) {
|
||||||
|
for (var i=0; i<files.length; i++) {
|
||||||
|
var file = files[i].getFile(name);
|
||||||
|
if (file != null) return file;
|
||||||
|
}
|
||||||
|
console.error("File not found: "+name);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function list() {
|
||||||
|
var result = [];
|
||||||
|
for (var i=0; i<files.length; i++) {
|
||||||
|
files[i].list(result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
window.narc = function(input) {
|
window.narc = function(input) {
|
||||||
|
|
||||||
this.load = load;
|
this.load = load;
|
||||||
this.getFile = getFile;
|
this.getFile = getFile;
|
||||||
|
this.list = list;
|
||||||
|
|
||||||
var arc = this;
|
var arc = this;
|
||||||
var handlers = [];
|
var handlers = [];
|
||||||
|
@ -18,8 +42,6 @@ window.narc = function(input) {
|
||||||
mouseY = evt.pageY;
|
mouseY = evt.pageY;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.scopeEval = function(code) {return eval(code)} //for debug purposes
|
|
||||||
|
|
||||||
function load(buffer) {
|
function load(buffer) {
|
||||||
arc.buffer = buffer; //we will use this data in the future.
|
arc.buffer = buffer; //we will use this data in the future.
|
||||||
|
|
||||||
|
@ -84,6 +106,22 @@ window.narc = function(input) {
|
||||||
return null; //incomplete path; we ended on a directory, not a file!
|
return null; //incomplete path; we ended on a directory, not a file!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function list(files, curDir, path) {
|
||||||
|
path = path || "/";
|
||||||
|
files = files || [];
|
||||||
|
var table = arc.sections["BTNF"].directories;
|
||||||
|
curDir = curDir || table[0].entries; //root
|
||||||
|
|
||||||
|
for (var i=0; i<curDir.length; i++) {
|
||||||
|
if (curDir[i].dir) {
|
||||||
|
list(files, table[curDir[i].id-0xF000].entries, path+curDir[i].name+"/");
|
||||||
|
} else {
|
||||||
|
files.push(path + curDir[i].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
function readFileWithID(id) {
|
function readFileWithID(id) {
|
||||||
var table = arc.sections["BTAF"].files;
|
var table = arc.sections["BTAF"].files;
|
||||||
var file = table[id];
|
var file = table[id];
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
window.ndsFS = function(input) {
|
window.ndsFS = function(input) {
|
||||||
this.load = load;
|
this.load = load;
|
||||||
this.getFile = getFile;
|
this.getFile = getFile;
|
||||||
|
this.list = list;
|
||||||
|
|
||||||
var arc = this;
|
var arc = this;
|
||||||
var handlers = [];
|
var handlers = [];
|
||||||
|
@ -65,6 +66,22 @@ window.ndsFS = function(input) {
|
||||||
return null; //incomplete path; we ended on a directory, not a file!
|
return null; //incomplete path; we ended on a directory, not a file!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function list(files, curDir, path) {
|
||||||
|
path = path || "/";
|
||||||
|
files = files || [];
|
||||||
|
var table = arc.sections["BTNF"].directories;
|
||||||
|
curDir = curDir || table[0].entries; //root
|
||||||
|
|
||||||
|
for (var i=0; i<curDir.length; i++) {
|
||||||
|
if (curDir[i].dir) {
|
||||||
|
list(files, table[curDir[i].id-0xF000].entries, path+curDir[i].name+"/");
|
||||||
|
} else {
|
||||||
|
files.push(path + curDir[i].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
function readFileWithID(id) {
|
function readFileWithID(id) {
|
||||||
var off = arc.fileOff+id*8;
|
var off = arc.fileOff+id*8;
|
||||||
/*var table = arc.sections["BTAF"].files;
|
/*var table = arc.sections["BTAF"].files;
|
||||||
|
|
|
@ -11,13 +11,17 @@
|
||||||
window.nftr = function(input) {
|
window.nftr = function(input) {
|
||||||
|
|
||||||
var mainOff;
|
var mainOff;
|
||||||
var mainObj = this;
|
var t = this;
|
||||||
|
this.info = {};
|
||||||
|
|
||||||
if (input != null) {
|
if (input != null) {
|
||||||
load(input);
|
load(input);
|
||||||
}
|
}
|
||||||
this.load = load;
|
this.load = load;
|
||||||
|
this.drawToCanvas = drawToCanvas;
|
||||||
|
this.measureText = measureText;
|
||||||
|
this.measureMapped = measureMapped;
|
||||||
|
this.mapText = mapText;
|
||||||
|
|
||||||
function load(input) {
|
function load(input) {
|
||||||
var view = new DataView(input);
|
var view = new DataView(input);
|
||||||
|
@ -29,9 +33,249 @@ window.nftr = function(input) {
|
||||||
header = nitro.readHeader(view);
|
header = nitro.readHeader(view);
|
||||||
//debugger;
|
//debugger;
|
||||||
if (header.stamp != "RTFN") throw "NFTR invalid. Expected RTFN, found "+header.stamp;
|
if (header.stamp != "RTFN") throw "NFTR invalid. Expected RTFN, found "+header.stamp;
|
||||||
offset = header.sectionOffsets[0];
|
offset = 0x10; //nitro header for nftr doesn't have section offsets - they are in order
|
||||||
//end nitro
|
//end nitro
|
||||||
|
|
||||||
|
var info = t.info;
|
||||||
|
info.type = readChar(view, offset+0x0)+readChar(view, offset+0x1)+readChar(view, offset+0x2)+readChar(view, offset+0x3);
|
||||||
|
info.blockSize = view.getUint32(offset+0x4, true);
|
||||||
|
|
||||||
|
info.unknown1 = view.getUint8(offset+0x8);
|
||||||
|
info.height = view.getUint8(offset+0x9);
|
||||||
|
info.nullCharIndex = view.getUint16(offset+0xA, true);
|
||||||
|
info.unknown2 = view.getUint8(offset+0xC);
|
||||||
|
info.width = view.getUint8(offset+0xD);
|
||||||
|
info.widthBis = view.getUint8(offset+0xE);
|
||||||
|
info.encoding = view.getUint8(offset+0xF);
|
||||||
|
|
||||||
|
info.offsetCGLP = view.getUint32(offset+0x10, true); //character graphics
|
||||||
|
info.offsetCWDH = view.getUint32(offset+0x14, true); //character width
|
||||||
|
info.offsetCMAP = view.getUint32(offset+0x18, true); //character map
|
||||||
|
|
||||||
|
if (info.blockSize == 0x20) {
|
||||||
|
//extra info
|
||||||
|
info.fontHeight = view.getUint8(offset+0x1C);
|
||||||
|
info.fontWidth = view.getUint8(offset+0x1D);
|
||||||
|
info.bearingX = view.getUint8(offset+0x1E);
|
||||||
|
info.bearingY = view.getUint8(offset+0x1F);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadCGLP(view);
|
||||||
|
loadCWDH(view);
|
||||||
|
loadCMAP(view);
|
||||||
|
|
||||||
mainOff = offset;
|
mainOff = offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function loadCGLP(view) {
|
||||||
|
var offset = t.info.offsetCGLP - 8;
|
||||||
|
|
||||||
|
var cglp = {};
|
||||||
|
cglp.type = readChar(view, offset+0x0)+readChar(view, offset+0x1)+readChar(view, offset+0x2)+readChar(view, offset+0x3);
|
||||||
|
cglp.blockSize = view.getUint32(offset+0x4, true);
|
||||||
|
cglp.tileWidth = view.getUint8(offset+0x8);
|
||||||
|
cglp.tileHeight = view.getUint8(offset+0x9);
|
||||||
|
cglp.tileLength = view.getUint16(offset+0xA, true);
|
||||||
|
cglp.unknown = view.getUint16(offset+0xC, true);
|
||||||
|
cglp.depth = view.getUint8(offset+0xE);
|
||||||
|
cglp.rotateMode = view.getUint8(offset+0xF);
|
||||||
|
|
||||||
|
offset += 0x10;
|
||||||
|
cglp.tiles = [];
|
||||||
|
var total = (cglp.blockSize - 0x10) / cglp.tileLength;
|
||||||
|
for (var i=0; i<total; i++) {
|
||||||
|
cglp.tiles.push(new Uint8Array(view.buffer.slice(offset, offset+cglp.tileLength)));
|
||||||
|
offset += cglp.tileLength;
|
||||||
|
}
|
||||||
|
t.cglp = cglp;
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadCWDH(view) {
|
||||||
|
var offset = t.info.offsetCWDH - 8;
|
||||||
|
|
||||||
|
var cwdh = {};
|
||||||
|
cwdh.type = readChar(view, offset+0x0)+readChar(view, offset+0x1)+readChar(view, offset+0x2)+readChar(view, offset+0x3);
|
||||||
|
cwdh.blockSize = view.getUint32(offset+0x4, true);
|
||||||
|
cwdh.firstCode = view.getUint16(offset+0x8, true);
|
||||||
|
cwdh.lastCode = view.getUint16(offset+0xA, true);
|
||||||
|
|
||||||
|
cwdh.unknown = view.getUint32(offset+0xC, true);
|
||||||
|
|
||||||
|
cwdh.info = [];
|
||||||
|
offset += 0x10;
|
||||||
|
for (var i=0; i<t.cglp.tiles.length; i++) {
|
||||||
|
var info = {};
|
||||||
|
info.pixelStart = view.getInt8(offset);
|
||||||
|
info.pixelWidth = view.getUint8(offset+1);
|
||||||
|
info.pixelLength = view.getUint8(offset+2);
|
||||||
|
cwdh.info.push(info);
|
||||||
|
offset += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
t.cwdh = cwdh;
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadCMAP(view) {
|
||||||
|
var offset = t.info.offsetCMAP - 8;
|
||||||
|
var cmaps = [];
|
||||||
|
var charMap = {};
|
||||||
|
while (offset > 0 && offset < view.byteLength) {
|
||||||
|
var cmap = {};
|
||||||
|
cmap.type = readChar(view, offset+0x0)+readChar(view, offset+0x1)+readChar(view, offset+0x2)+readChar(view, offset+0x3);
|
||||||
|
cmap.blockSize = view.getUint32(offset+0x4, true);
|
||||||
|
cmap.firstChar = view.getUint16(offset+0x8, true);
|
||||||
|
cmap.lastChar = view.getUint16(offset+0xA, true);
|
||||||
|
|
||||||
|
cmap.typeSection = view.getUint32(offset+0xC, true);
|
||||||
|
cmap.nextOffset = view.getUint32(offset+0x10, true);
|
||||||
|
|
||||||
|
offset += 0x14;
|
||||||
|
switch (cmap.typeSection & 0xFFFF) {
|
||||||
|
case 1: //char code list (first to last)
|
||||||
|
cmap.charCodes = [];
|
||||||
|
var total = (cmap.lastChar - cmap.firstChar) + 1;
|
||||||
|
var charCode = cmap.firstChar;
|
||||||
|
for (var i=0; i<total; i++) {
|
||||||
|
var char = view.getUint16(offset, true);
|
||||||
|
cmap.charCodes.push(char);
|
||||||
|
if (char != 65535) {
|
||||||
|
charMap[String.fromCharCode(charCode)] = char;
|
||||||
|
}
|
||||||
|
charCode++;
|
||||||
|
offset += 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2: //char code map
|
||||||
|
cmap.numChars = view.getUint16(offset, true);
|
||||||
|
offset += 2;
|
||||||
|
cmap.charMap = [];
|
||||||
|
for (var i=0; i<cmap.numChars; i++) {
|
||||||
|
var charCode = view.getUint16(offset, true);
|
||||||
|
var char = view.getUint16(offset+2, true);
|
||||||
|
cmap.charMap.push([charCode, char]);
|
||||||
|
charMap[String.fromCharCode(charCode)] = char;
|
||||||
|
offset += 4;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
cmap.firstCharCode = view.getUint16(offset, true);
|
||||||
|
var total = (cmap.lastChar - cmap.firstChar) + 1;
|
||||||
|
var charCode = cmap.firstChar;
|
||||||
|
var char = cmap.firstCharCode;
|
||||||
|
|
||||||
|
for (var i=0; i<total; i++) {
|
||||||
|
charMap[String.fromCharCode(charCode++)] = char++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
cmaps.push(cmap);
|
||||||
|
offset = cmap.nextOffset - 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
t.charMap = charMap;
|
||||||
|
t.cmaps = cmaps;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readChar(view, offset) {
|
||||||
|
return String.fromCharCode(view.getUint8(offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
// RENDERING FUNCTIONS
|
||||||
|
|
||||||
|
function mapText(text, missing) {
|
||||||
|
if (missing == null) missing = "*";
|
||||||
|
var map = t.charMap;
|
||||||
|
var result = [];
|
||||||
|
for (var i=0; i<text.length; i++) {
|
||||||
|
var code = text[i];
|
||||||
|
var mapped = map[code];
|
||||||
|
if (mapped == null) mapped = map[missing];
|
||||||
|
result.push(mapped);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function measureText(text, missing, spacing) {
|
||||||
|
return measureMapped(mapText(text, missing), spacing);
|
||||||
|
}
|
||||||
|
|
||||||
|
function measureMapped(mapped, spacing) {
|
||||||
|
if (spacing == null) spacing = 1;
|
||||||
|
var width = 0;
|
||||||
|
var widths = t.cwdh.info;
|
||||||
|
|
||||||
|
for (var i=0; i<mapped.length; i++) {
|
||||||
|
width += widths[mapped[i]].pixelLength + spacing; // pixelWidth is the width of drawn section - length is how wide the char is
|
||||||
|
}
|
||||||
|
|
||||||
|
return [width, t.info.height];
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawToCanvas(text, palette, spacing) {
|
||||||
|
if (spacing == null) spacing = 1;
|
||||||
|
var mapped = mapText(text, "");
|
||||||
|
var size = measureMapped(mapped, spacing);
|
||||||
|
|
||||||
|
var canvas = document.createElement("canvas");
|
||||||
|
canvas.width = size[0];
|
||||||
|
canvas.height = size[1];
|
||||||
|
var ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
|
//draw chars
|
||||||
|
var widths = t.cwdh.info;
|
||||||
|
var position = 0;
|
||||||
|
|
||||||
|
for (var i=0; i<mapped.length; i++) {
|
||||||
|
var c = mapped[i];
|
||||||
|
var cinfo = widths[c];
|
||||||
|
|
||||||
|
var data = getCharData(c, palette);
|
||||||
|
ctx.putImageData(data, position + cinfo.pixelStart, 0, 0, 0, cinfo.pixelWidth, data.height);
|
||||||
|
|
||||||
|
position += cinfo.pixelLength + spacing;
|
||||||
|
}
|
||||||
|
return canvas;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCharData(id, pal) {
|
||||||
|
//todo: cache?
|
||||||
|
var cglp = t.cglp;
|
||||||
|
var tile = cglp.tiles[id];
|
||||||
|
var pixels = cglp.tileWidth*cglp.tileHeight;
|
||||||
|
var d = new Uint8ClampedArray(pixels*4);
|
||||||
|
var data = new ImageData(d, cglp.tileWidth, cglp.tileHeight);
|
||||||
|
var depth = t.cglp.depth;
|
||||||
|
var mask = (1<<depth)-1;
|
||||||
|
|
||||||
|
var bit = 8;
|
||||||
|
var byte = 0;
|
||||||
|
var curByte = tile[byte];
|
||||||
|
var ind = 0;
|
||||||
|
for (var i=0; i<pixels; i++) {
|
||||||
|
bit -= depth;
|
||||||
|
var pind = 0;
|
||||||
|
if (bit < 0) {
|
||||||
|
//overlap into next
|
||||||
|
var end = bit + 8;
|
||||||
|
if (end < 8) {
|
||||||
|
//still some left in this byte
|
||||||
|
pind = (curByte << (-bit)) & mask;
|
||||||
|
}
|
||||||
|
curByte = tile[++byte];
|
||||||
|
bit += 8;
|
||||||
|
pind |= (curByte >> (bit)) & mask;
|
||||||
|
} else {
|
||||||
|
pind = (curByte >> (bit)) & mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
var col = pal[pind];
|
||||||
|
d[ind++] = col[0];
|
||||||
|
d[ind++] = col[1];
|
||||||
|
d[ind++] = col[2];
|
||||||
|
d[ind++] = col[3];
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -66,6 +66,7 @@ window.nitro = new function() {
|
||||||
for (var j=0; j<16; j++) {
|
for (var j=0; j<16; j++) {
|
||||||
name += readChar(view, offset++)
|
name += readChar(view, offset++)
|
||||||
}
|
}
|
||||||
|
objectData[i].name = name;
|
||||||
names.push(name);
|
names.push(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ window.nsbca = function(input) {
|
||||||
|
|
||||||
var mainOff;
|
var mainOff;
|
||||||
var animData;
|
var animData;
|
||||||
var speeds = [1.0, 0.5, 1/3];
|
var speeds = [1.0, 0.5, 1/4];
|
||||||
var mainObj = this;
|
var mainObj = this;
|
||||||
|
|
||||||
if (input != null) {
|
if (input != null) {
|
||||||
|
@ -72,10 +72,10 @@ window.nsbca = function(input) {
|
||||||
|
|
||||||
function readTrans(view, off, obj) {
|
function readTrans(view, off, obj) {
|
||||||
var flag = view.getUint16(off, true); //--zyx-Sr-RZYX-T-
|
var flag = view.getUint16(off, true); //--zyx-Sr-RZYX-T-
|
||||||
|
|
||||||
off += 4;
|
off += 4;
|
||||||
|
|
||||||
var transform = {};
|
var transform = {};
|
||||||
|
transform.flag = flag;
|
||||||
|
|
||||||
if (!((flag>>1) & 1)) { //T: translation
|
if (!((flag>>1) & 1)) { //T: translation
|
||||||
var translate = [[], [], []]; //store translations in x,y,z arrays
|
var translate = [[], [], []]; //store translations in x,y,z arrays
|
||||||
|
@ -96,7 +96,7 @@ window.nsbca = function(input) {
|
||||||
inf.off = view.getUint32(off+4, true);
|
inf.off = view.getUint32(off+4, true);
|
||||||
|
|
||||||
var extra = (obj.unknown != 3)?0:(obj.frames-inf.endFrame);
|
var extra = (obj.unknown != 3)?0:(obj.frames-inf.endFrame);
|
||||||
var length = Math.floor((obj.frames+extra)*inf.speed);
|
var length = Math.ceil((inf.endFrame-inf.startFrame)*inf.speed);
|
||||||
var w = (inf.halfSize)?2:4;
|
var w = (inf.halfSize)?2:4;
|
||||||
|
|
||||||
var off2 = obj.baseOff+inf.off;
|
var off2 = obj.baseOff+inf.off;
|
||||||
|
@ -132,7 +132,7 @@ window.nsbca = function(input) {
|
||||||
inf.off = view.getUint32(off+4, true);
|
inf.off = view.getUint32(off+4, true);
|
||||||
var extra = (obj.unknown != 3)?0:(obj.frames-inf.endFrame);
|
var extra = (obj.unknown != 3)?0:(obj.frames-inf.endFrame);
|
||||||
//florian's rotate code seems to ignore this extra value. I'll need more examples of nsbca to test this more thoroughly.
|
//florian's rotate code seems to ignore this extra value. I'll need more examples of nsbca to test this more thoroughly.
|
||||||
var length = Math.floor((obj.frames+extra)*inf.speed);
|
var length = Math.ceil((inf.endFrame-inf.startFrame)*inf.speed);
|
||||||
|
|
||||||
var off2 = obj.baseOff+inf.off;
|
var off2 = obj.baseOff+inf.off;
|
||||||
try {
|
try {
|
||||||
|
@ -173,7 +173,7 @@ window.nsbca = function(input) {
|
||||||
inf.off = view.getUint32(off+4, true);
|
inf.off = view.getUint32(off+4, true);
|
||||||
|
|
||||||
var extra = (obj.unknown != 3)?0:(obj.frames-inf.endFrame);
|
var extra = (obj.unknown != 3)?0:(obj.frames-inf.endFrame);
|
||||||
var length = Math.ceil((obj.frames+extra)*inf.speed);
|
var length = Math.ceil((inf.endFrame-inf.startFrame)*inf.speed);
|
||||||
var w = ((inf.halfSize)?2:4);
|
var w = ((inf.halfSize)?2:4);
|
||||||
|
|
||||||
var off2 = obj.baseOff+inf.off;
|
var off2 = obj.baseOff+inf.off;
|
||||||
|
@ -211,21 +211,26 @@ window.nsbca = function(input) {
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
var off2 = obj.baseOff+obj.off2+ind*10; //jump to rotation data
|
var off2 = obj.baseOff+obj.off2+ind*10; //jump to rotation data
|
||||||
var d1 = view.getInt16(off2, true);
|
var d1 = view.getUint16(off2, true);
|
||||||
var d2 = view.getInt16(off2+2, true);
|
var d2 = view.getUint16(off2+2, true);
|
||||||
var d3 = view.getInt16(off2+4, true);
|
var d3 = view.getUint16(off2+4, true);
|
||||||
var d4 = view.getInt16(off2+6, true);
|
var d4 = view.getUint16(off2+6, true);
|
||||||
var d5 = view.getInt16(off2+8, true);
|
var d5 = view.getUint16(off2+8, true);
|
||||||
|
|
||||||
var i6 = ((d5&7)<<12) | ((d1&7)<<9) | ((d2&7)<<6) | ((d3&7)<<3) | ((d4&7));
|
var i6 = ((d5&7)<<12) | ((d1&7)<<9) | ((d2&7)<<6) | ((d3&7)<<3) | ((d4&7));
|
||||||
if (i6&4096) i6 = (-8192)+i6;
|
//if (i6&4096) i6 = (-8192)+i6;
|
||||||
|
|
||||||
var v1 = [d1>>3, d2>>3, d3>>3]
|
var v1 = [d1>>3, d2>>3, d3>>3];
|
||||||
var v2 = [d4>>3, d5>>3, i6]
|
var v2 = [d4>>3, d5>>3, i6];
|
||||||
|
|
||||||
|
for (var i=0; i<3; i++) {
|
||||||
|
if (v1[i] & 4096) v1[i] -= 8192;
|
||||||
|
if (v2[i] & 4096) v2[i] -= 8192;
|
||||||
|
}
|
||||||
|
|
||||||
vec3.scale(v1, v1, 1/4096);
|
vec3.scale(v1, v1, 1/4096);
|
||||||
vec3.scale(v2, v2, 1/4096);
|
vec3.scale(v2, v2, 1/4096);
|
||||||
var v3 = vec3.cross([], v1, v2)
|
var v3 = vec3.cross([], v1, v2);
|
||||||
|
|
||||||
var mat = [
|
var mat = [
|
||||||
v1[0], v1[1], v1[2],
|
v1[0], v1[1], v1[2],
|
||||||
|
|
|
@ -70,6 +70,7 @@ window.nsbmd = function(input) {
|
||||||
head.maxStack = view.getUint8(offset+0x1A);
|
head.maxStack = view.getUint8(offset+0x1A);
|
||||||
|
|
||||||
head.scale = view.getInt32(offset+0x1C, true)/4096;
|
head.scale = view.getInt32(offset+0x1C, true)/4096;
|
||||||
|
head.downScale = view.getInt32(offset+0x20, true)/4096; //usually inverse of the above
|
||||||
|
|
||||||
head.numVerts = view.getUint16(offset+0x24, true);
|
head.numVerts = view.getUint16(offset+0x24, true);
|
||||||
head.numSurfaces = view.getUint16(offset+0x26, true);
|
head.numSurfaces = view.getUint16(offset+0x26, true);
|
||||||
|
@ -93,6 +94,12 @@ window.nsbmd = function(input) {
|
||||||
var tex = nitro.read3dInfo(view, head.materialsOffset+view.getUint16(head.materialsOffset, true), texInfoHandler);
|
var tex = nitro.read3dInfo(view, head.materialsOffset+view.getUint16(head.materialsOffset, true), texInfoHandler);
|
||||||
var palt = nitro.read3dInfo(view, head.materialsOffset+view.getUint16(head.materialsOffset+2, true), palInfoHandler);
|
var palt = nitro.read3dInfo(view, head.materialsOffset+view.getUint16(head.materialsOffset+2, true), palInfoHandler);
|
||||||
|
|
||||||
|
//bind tex and palt names to their materials
|
||||||
|
for (var i=0; i<materials.objectData.length; i++) {
|
||||||
|
materials.objectData[i].texName = tex.names[materials.objectData[i].tex];
|
||||||
|
materials.objectData[i].palName = palt.names[materials.objectData[i].pal];
|
||||||
|
}
|
||||||
|
|
||||||
var commands = parseBones(head.bonesOffset, view, polys, materials, objects, head.maxStack);
|
var commands = parseBones(head.bonesOffset, view, polys, materials, objects, head.maxStack);
|
||||||
|
|
||||||
return {head: head, objects: objects, polys: polys, materials: materials, tex:tex, palt:palt, commands:commands}
|
return {head: head, objects: objects, polys: polys, materials: materials, tex:tex, palt:palt, commands:commands}
|
||||||
|
@ -101,14 +108,18 @@ window.nsbmd = function(input) {
|
||||||
function parseBones(offset, view, polys, materials, objects, maxStack) {
|
function parseBones(offset, view, polys, materials, objects, maxStack) {
|
||||||
var last;
|
var last;
|
||||||
var commands = [];
|
var commands = [];
|
||||||
|
var debug = true;
|
||||||
|
if (debug) console.log("== Begin Parse Bones ==");
|
||||||
|
|
||||||
var freeStack = maxStack;
|
var freeStack = maxStack;
|
||||||
var forceID=null;
|
var forceID=null;
|
||||||
var lastMat = null;
|
var lastMat = null;
|
||||||
|
var bound = [];
|
||||||
|
|
||||||
|
var matMap = [];
|
||||||
|
|
||||||
while (offset<texPalOff) { //bones
|
while (offset<texPalOff) { //bones
|
||||||
last = view.getUint8(offset++);
|
last = view.getUint8(offset++);
|
||||||
console.log("bone cmd: 0x"+last.toString(16));
|
|
||||||
switch (last) {
|
switch (last) {
|
||||||
case 0x06: //bind object transforms to parent. bone exists but is not placed in the stack
|
case 0x06: //bind object transforms to parent. bone exists but is not placed in the stack
|
||||||
var obj = view.getUint8(offset++);
|
var obj = view.getUint8(offset++);
|
||||||
|
@ -117,21 +128,50 @@ window.nsbmd = function(input) {
|
||||||
|
|
||||||
var object = objects.objectData[obj];
|
var object = objects.objectData[obj];
|
||||||
object.parent = parent;
|
object.parent = parent;
|
||||||
|
if (debug) console.log("[0x"+last.toString(16)+"] Multiply stack with object " + obj + " bound to parent " + parent);
|
||||||
|
|
||||||
commands.push({obj:obj, parent:parent, stackID:freeStack++});
|
commands.push({obj:obj, parent:parent, stackID:freeStack++});
|
||||||
break;
|
break;
|
||||||
case 0x26:
|
case 0x26:
|
||||||
case 0x46: //placed in the stack at stack id
|
case 0x46: //placed in the stack at stack id
|
||||||
|
case 0x66:
|
||||||
var obj = view.getUint8(offset++);
|
var obj = view.getUint8(offset++);
|
||||||
var parent = view.getUint8(offset++);
|
var parent = view.getUint8(offset++);
|
||||||
var zero = view.getUint8(offset++);
|
var zero = view.getUint8(offset++);
|
||||||
var stackID = view.getUint8(offset++);
|
var stackID = view.getUint8(offset++);
|
||||||
|
var restoreID = null;
|
||||||
|
if (last == 0x66) restoreID = view.getUint8(offset++);
|
||||||
|
if (last == 0x46) {
|
||||||
|
restoreID = stackID;
|
||||||
|
stackID = freeStack++;
|
||||||
|
}
|
||||||
|
|
||||||
var object = objects.objectData[obj];
|
var object = objects.objectData[obj];
|
||||||
object.parent = parent;
|
object.parent = parent;
|
||||||
|
|
||||||
commands.push({obj:obj, parent:parent, stackID:((last == 0x26)?stackID:freeStack++), restoreID:((last == 0x46)?stackID:null)});
|
if (debug) {
|
||||||
|
var debugMessage = "[0x"+last.toString(16)+"] ";
|
||||||
|
if (restoreID != null) debugMessage += "Restore matrix at " + stackID + ", ";
|
||||||
|
debugMessage += "Multiply stack with object " + obj + " bound to parent " + parent;
|
||||||
|
if (stackID != null) debugMessage += ", store in " + stackID;
|
||||||
|
console.log(debugMessage);
|
||||||
|
}
|
||||||
|
var item = {obj:obj, parent:parent, stackID:stackID, restoreID:restoreID};
|
||||||
|
if (bound[stackID]) {
|
||||||
|
//we're updating a matrix that is already bound...
|
||||||
|
//we must move copy the old value of the matrix to another place, and update the polys that point to it to the new location.
|
||||||
|
//(does not play well with skinned meshes (they don't do this anyways), but fixes lots of "multiple object" meshes.)
|
||||||
|
console.log("!! Already bound !! Moving old matrix at " + stackID + " to " + freeStack);
|
||||||
|
var poly = polys.objectData;
|
||||||
|
for (var i=0; i<poly.length; i++) {
|
||||||
|
if (poly[i].stackID == stackID) poly[i].stackID = freeStack;
|
||||||
|
}
|
||||||
|
commands.push({copy: stackID, dest: freeStack++})
|
||||||
|
}
|
||||||
|
|
||||||
|
commands.push(item);
|
||||||
break;
|
break;
|
||||||
|
/*
|
||||||
case 0x66: //has ability to restore to another stack id. no idea how this works
|
case 0x66: //has ability to restore to another stack id. no idea how this works
|
||||||
var obj = view.getUint8(offset++);
|
var obj = view.getUint8(offset++);
|
||||||
var parent = view.getUint8(offset++);
|
var parent = view.getUint8(offset++);
|
||||||
|
@ -142,8 +182,11 @@ window.nsbmd = function(input) {
|
||||||
var object = objects.objectData[obj];
|
var object = objects.objectData[obj];
|
||||||
object.parent = parent;
|
object.parent = parent;
|
||||||
|
|
||||||
|
if (debug) console.log("[0x"+last.toString(16)+"] Restore matrix at " + restoreID + ", multiply stack with object " + obj + " bound to parent " + parent + ", store in " + stackID);
|
||||||
|
|
||||||
commands.push({obj:obj, parent:parent, stackID:stackID, restoreID:restoreID});
|
commands.push({obj:obj, parent:parent, stackID:stackID, restoreID:restoreID});
|
||||||
break;
|
break;
|
||||||
|
*/
|
||||||
case 0x04:
|
case 0x04:
|
||||||
case 0x24:
|
case 0x24:
|
||||||
case 0x44: //bind material to polygon: matID, 5, polyID
|
case 0x44: //bind material to polygon: matID, 5, polyID
|
||||||
|
@ -151,54 +194,69 @@ window.nsbmd = function(input) {
|
||||||
lastMat = mat;
|
lastMat = mat;
|
||||||
var five = view.getUint8(offset++); //???
|
var five = view.getUint8(offset++); //???
|
||||||
var poly = view.getUint8(offset++);
|
var poly = view.getUint8(offset++);
|
||||||
polys.objectData[poly].stackID = (forceID == null)?(commands[commands.length-1].stackID):forceID;
|
var bindID = (forceID == null)?(commands[commands.length-1].stackID):forceID;
|
||||||
|
bound[bindID] = true;
|
||||||
|
polys.objectData[poly].stackID = bindID;
|
||||||
polys.objectData[poly].mat = mat;
|
polys.objectData[poly].mat = mat;
|
||||||
|
|
||||||
|
if (debug) console.log("[0x"+last.toString(16)+"] Bind material " + mat + " to poly " + poly + " (with stack id " + polys.objectData[poly].stackID + ")");
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
|
//end of all
|
||||||
|
if (debug) console.log("[0x"+last.toString(16)+"] END OF BONES");
|
||||||
break;
|
break;
|
||||||
case 2: //node visibility (maybe to implement this set matrix to 0)
|
case 2: //node visibility (maybe to implement this set matrix to 0)
|
||||||
|
|
||||||
var node = view.getUint8(offset++);
|
var node = view.getUint8(offset++);
|
||||||
var vis = view.getUint8(offset++);
|
var vis = view.getUint8(offset++);
|
||||||
objects.objectData[node].vis = vis;
|
objects.objectData[node].vis = vis;
|
||||||
console.log("visibility thing "+node);
|
if (debug) console.log("[0x"+last.toString(16)+"] Set object " + node + " visibility: " + vis);
|
||||||
if (node > 10) debugger;
|
if (node > 10) debugger;
|
||||||
break;
|
break;
|
||||||
case 3: //stack id for poly (wit)
|
case 3: //stack id for poly (wit)
|
||||||
forceID = view.getUint8(offset++);
|
forceID = view.getUint8(offset++);
|
||||||
console.log("stackid is "+forceID);
|
if (debug) console.log("[0x"+last.toString(16)+"] Force stack id to " + forceID);
|
||||||
case 0:
|
case 0:
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5: //"draw a mesh" supposedly. might require getting a snapshot of the matrices at this point
|
||||||
//i don't... what??
|
|
||||||
//holy shp!
|
|
||||||
var poly = view.getUint8(offset++);
|
var poly = view.getUint8(offset++);
|
||||||
polys.objectData[poly].stackID = (stackID == null)?(commands[commands.length-1].forceID):forceID;
|
var bindID = (forceID == null)?(commands[commands.length-1].stackID):forceID;
|
||||||
|
bound[bindID] = true;
|
||||||
|
polys.objectData[poly].stackID = bindID;
|
||||||
polys.objectData[poly].mat = lastMat;
|
polys.objectData[poly].mat = lastMat;
|
||||||
|
if (debug) console.log("[0x"+last.toString(16)+"] Draw " + poly + "(stack id " + polys.objectData[poly].stackID + ")");
|
||||||
break;
|
break;
|
||||||
case 7:
|
case 7:
|
||||||
//sets object to be billboard
|
//sets object to be billboard
|
||||||
var obj = view.getUint8(offset++);
|
var obj = view.getUint8(offset++);
|
||||||
objects.objectData[obj].billboardMode = 1;
|
objects.objectData[obj].billboardMode = 1;
|
||||||
|
if (debug) console.log("[0x"+last.toString(16)+"] Object " + obj + " set to full billboard mode.");
|
||||||
mainObj.hasBillboards = true;
|
mainObj.hasBillboards = true;
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 8:
|
||||||
//sets object to be billboard around only y axis. (xz remain unchanged)
|
//sets object to be billboard around only y axis. (xz remain unchanged)
|
||||||
var obj = view.getUint8(offset++);
|
var obj = view.getUint8(offset++);
|
||||||
objects.objectData[obj].billboardMode = 2;
|
objects.objectData[obj].billboardMode = 2;
|
||||||
|
if (debug) console.log("[0x"+last.toString(16)+"] Object " + obj + " set to Y billboard mode.");
|
||||||
mainObj.hasBillboards = true;
|
mainObj.hasBillboards = true;
|
||||||
break;
|
break;
|
||||||
|
case 9: //skinning equ. not used?
|
||||||
|
if (debug) console.log("[0x"+last.toString(16)+"] Skinning Equation (UNIMPLEMENTED)");
|
||||||
|
debugger;
|
||||||
|
break;
|
||||||
case 0x0b:
|
case 0x0b:
|
||||||
break; //begin, not quite sure what of. doesn't seem to change anything
|
if (debug) console.log("[0x"+last.toString(16)+"] BEGIN PAIRING.");
|
||||||
|
break; //begin polygon material paring (scale up? using scale value in model..)
|
||||||
case 0x2b:
|
case 0x2b:
|
||||||
break; //end
|
if (debug) console.log("[0x"+last.toString(16)+"] END PAIRING.");
|
||||||
|
break; //end polygon material pairing (scale down? using scale(down) value in model..)
|
||||||
default:
|
default:
|
||||||
console.log("bone transform unknown: "+last);
|
console.log("bone transform unknown: 0x"+last.toString(16));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//if (window.throwWhatever) debugger;
|
|
||||||
|
if (debug) console.log("== End Parse Bones ==");
|
||||||
return commands;
|
return commands;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,7 +271,6 @@ window.nsbmd = function(input) {
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
var polyAttrib = view.getUint16(offset+12, true);
|
var polyAttrib = view.getUint16(offset+12, true);
|
||||||
console.log(polyAttrib);
|
|
||||||
|
|
||||||
var flags = view.getUint16(offset+22, true); //other info in here is specular data etc.
|
var flags = view.getUint16(offset+22, true); //other info in here is specular data etc.
|
||||||
|
|
||||||
|
|
|
@ -120,6 +120,7 @@ window.nsbta = function(input) {
|
||||||
if (i == 2) obj[prop[i]].push(data2);
|
if (i == 2) obj[prop[i]].push(data2);
|
||||||
} else { //data is found at offset
|
} else { //data is found at offset
|
||||||
frames = frames>>obj.frameStep[prop[i]];
|
frames = frames>>obj.frameStep[prop[i]];
|
||||||
|
if (obj.frameStep[prop[i]] > 0) frames++; //one extra frame, for interpolation
|
||||||
//frames -= 1;
|
//frames -= 1;
|
||||||
var off = base + value-8;
|
var off = base + value-8;
|
||||||
for (var j=0; j<frames*((i==2)?2:1); j++) {
|
for (var j=0; j<frames*((i==2)?2:1); j++) {
|
||||||
|
|
|
@ -38,7 +38,11 @@ window.nsbtp = function(input) {
|
||||||
|
|
||||||
var texTotal;
|
var texTotal;
|
||||||
var palTotal;
|
var palTotal;
|
||||||
|
var texNamesOff;
|
||||||
|
var palNamesOff;
|
||||||
|
|
||||||
|
var texNames;
|
||||||
|
var palNames;
|
||||||
|
|
||||||
function load(input) {
|
function load(input) {
|
||||||
var view = new DataView(input);
|
var view = new DataView(input);
|
||||||
|
@ -96,13 +100,35 @@ window.nsbtp = function(input) {
|
||||||
var duration = view.getUint16(offset, true);
|
var duration = view.getUint16(offset, true);
|
||||||
texTotal = view.getUint8(offset+2);
|
texTotal = view.getUint8(offset+2);
|
||||||
palTotal = view.getUint8(offset+3);
|
palTotal = view.getUint8(offset+3);
|
||||||
var unknown = view.getUint16(offset+4, true);
|
texNamesOff = view.getUint16(offset+4, true);
|
||||||
var unknown2 = view.getUint16(offset+6, true);
|
palNamesOff = view.getUint16(offset+6, true);
|
||||||
|
|
||||||
|
var nameOffset = matOff + texNamesOff;
|
||||||
|
texNames = [];
|
||||||
|
//read 16char tex names
|
||||||
|
for (var i=0; i<texTotal; i++) {
|
||||||
|
var name = "";
|
||||||
|
for (var j=0; j<16; j++) {
|
||||||
|
name += readChar(view, nameOffset++)
|
||||||
|
}
|
||||||
|
texNames[i] = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
nameOffset = matOff + palNamesOff;
|
||||||
|
palNames = [];
|
||||||
|
//read 16char pal names
|
||||||
|
for (var i=0; i<palTotal; i++) {
|
||||||
|
var name = "";
|
||||||
|
for (var j=0; j<16; j++) {
|
||||||
|
name += readChar(view, nameOffset++)
|
||||||
|
}
|
||||||
|
palNames[i] = name;
|
||||||
|
}
|
||||||
|
|
||||||
//...then another nitro
|
//...then another nitro
|
||||||
var data = nitro.read3dInfo(view, offset+8, matInfoHandler);
|
var data = nitro.read3dInfo(view, offset+8, matInfoHandler);
|
||||||
|
|
||||||
return {data: data, nextoff: data.nextoff, texTotal:texTotal, palTotal:palTotal, unknown:unknown, unknown2:unknown2, duration:duration};
|
return {data: data, nextoff: data.nextoff, texTotal:texTotal, palTotal:palTotal, duration:duration, texNames: texNames, palNames: palNames};
|
||||||
}
|
}
|
||||||
|
|
||||||
function matInfoHandler(view, offset, base) {
|
function matInfoHandler(view, offset, base) {
|
||||||
|
@ -124,9 +150,9 @@ window.nsbtp = function(input) {
|
||||||
// 16char texname
|
// 16char texname
|
||||||
//then (frame of these)
|
//then (frame of these)
|
||||||
// 16char palname
|
// 16char palname
|
||||||
|
// texture animations are bound to the material via the name of this block.
|
||||||
|
|
||||||
var frames = view.getUint16(offset, true);
|
var frames = view.getUint32(offset, true);
|
||||||
obj.matinfo = view.getUint16(offset+2, true);
|
|
||||||
obj.flags = view.getUint16(offset+4, true);
|
obj.flags = view.getUint16(offset+4, true);
|
||||||
var offset2 = view.getUint16(offset+6, true);
|
var offset2 = view.getUint16(offset+6, true);
|
||||||
offset += 8;
|
offset += 8;
|
||||||
|
@ -135,33 +161,16 @@ window.nsbtp = function(input) {
|
||||||
offset = matOff + offset2;
|
offset = matOff + offset2;
|
||||||
//info and timing for each frame
|
//info and timing for each frame
|
||||||
for (var i=0; i<frames; i++) {
|
for (var i=0; i<frames; i++) {
|
||||||
obj.frames[i] = {
|
var entry = {
|
||||||
time: view.getUint16(offset, true),
|
time: view.getUint16(offset, true),
|
||||||
tex: view.getUint8(offset+2),
|
tex: view.getUint8(offset+2), //index into names?
|
||||||
mat: view.getUint8(offset+3),
|
pal: view.getUint8(offset+3), //index into pal names?
|
||||||
}
|
}
|
||||||
|
entry.texName = texNames[entry.tex];
|
||||||
|
entry.palName = palNames[entry.pal];
|
||||||
|
obj.frames.push(entry);
|
||||||
offset += 4;
|
offset += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.texNames = [];
|
|
||||||
//read 16char tex names
|
|
||||||
for (var i=0; i<texTotal; i++) {
|
|
||||||
var name = "";
|
|
||||||
for (var j=0; j<16; j++) {
|
|
||||||
name += readChar(view, offset++)
|
|
||||||
}
|
|
||||||
obj.texNames[i] = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
obj.palNames = [];
|
|
||||||
//read 16char pal names
|
|
||||||
for (var i=0; i<palTotal; i++) {
|
|
||||||
var name = "";
|
|
||||||
for (var j=0; j<16; j++) {
|
|
||||||
name += readChar(view, offset++)
|
|
||||||
}
|
|
||||||
obj.palNames[i] = name;
|
|
||||||
}
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,10 +74,21 @@ window.nsbtx = function(input, tex0) {
|
||||||
paletteInfo = nitro.read3dInfo(view, mainOff + palInfoOff, palInfoHandler);
|
paletteInfo = nitro.read3dInfo(view, mainOff + palInfoOff, palInfoHandler);
|
||||||
textureInfo = nitro.read3dInfo(view, mainOff + texInfoOff, texInfoHandler);
|
textureInfo = nitro.read3dInfo(view, mainOff + texInfoOff, texInfoHandler);
|
||||||
|
|
||||||
|
buildNameToIndex(paletteInfo);
|
||||||
|
buildNameToIndex(textureInfo);
|
||||||
|
|
||||||
thisObj.paletteInfo = paletteInfo;
|
thisObj.paletteInfo = paletteInfo;
|
||||||
thisObj.textureInfo = textureInfo;
|
thisObj.textureInfo = textureInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildNameToIndex(info) {
|
||||||
|
var nameToIndex = {};
|
||||||
|
for (var i=0; i<info.names.length; i++) {
|
||||||
|
nameToIndex["$" + info.names[i]] = i;
|
||||||
|
}
|
||||||
|
info.nameToIndex = nameToIndex;
|
||||||
|
}
|
||||||
|
|
||||||
function readTexWithPal(textureId, palId) {
|
function readTexWithPal(textureId, palId) {
|
||||||
var tex = textureInfo.objectData[textureId];
|
var tex = textureInfo.objectData[textureId];
|
||||||
var pal = paletteInfo.objectData[palId];
|
var pal = paletteInfo.objectData[palId];
|
||||||
|
@ -106,6 +117,7 @@ window.nsbtx = function(input, tex0) {
|
||||||
var dat = texView.getUint8(off++)
|
var dat = texView.getUint8(off++)
|
||||||
col = readPalColour(palView, palOff, dat&31, trans);
|
col = readPalColour(palView, palOff, dat&31, trans);
|
||||||
col[3] = (dat>>5)*(255/7);
|
col[3] = (dat>>5)*(255/7);
|
||||||
|
premultiply(col);
|
||||||
|
|
||||||
} else if (format == 2) { //2 bit pal
|
} else if (format == 2) { //2 bit pal
|
||||||
if (i%4 == 0) databuf = texView.getUint8(off++);
|
if (i%4 == 0) databuf = texView.getUint8(off++);
|
||||||
|
@ -126,6 +138,7 @@ window.nsbtx = function(input, tex0) {
|
||||||
var dat = texView.getUint8(off++)
|
var dat = texView.getUint8(off++)
|
||||||
col = readPalColour(palView, palOff, dat&7, trans);
|
col = readPalColour(palView, palOff, dat&7, trans);
|
||||||
col[3] = (dat>>3)*(255/31);
|
col[3] = (dat>>3)*(255/31);
|
||||||
|
premultiply(col);
|
||||||
|
|
||||||
} else if (format == 7) { //raw color data
|
} else if (format == 7) { //raw color data
|
||||||
col = texView.getUint16(off, true);
|
col = texView.getUint16(off, true);
|
||||||
|
@ -134,6 +147,7 @@ window.nsbtx = function(input, tex0) {
|
||||||
colourBuffer[2] = Math.round((((col>>10)&31)/31)*255)
|
colourBuffer[2] = Math.round((((col>>10)&31)/31)*255)
|
||||||
colourBuffer[3] = Math.round((col>>15)*255);
|
colourBuffer[3] = Math.round((col>>15)*255);
|
||||||
col = colourBuffer;
|
col = colourBuffer;
|
||||||
|
premultiply(col);
|
||||||
off += 2;
|
off += 2;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -146,6 +160,12 @@ window.nsbtx = function(input, tex0) {
|
||||||
return canvas;
|
return canvas;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function premultiply(col) {
|
||||||
|
col[0] *= col[3]/255;
|
||||||
|
col[1] *= col[3]/255;
|
||||||
|
col[2] *= col[3]/255;
|
||||||
|
}
|
||||||
|
|
||||||
function readCompressedTex(tex, pal) { //format 5, 4x4 texels. I'll keep this well documented so it's easy to understand.
|
function readCompressedTex(tex, pal) { //format 5, 4x4 texels. I'll keep this well documented so it's easy to understand.
|
||||||
var off = tex.texOffset;
|
var off = tex.texOffset;
|
||||||
var texView = new DataView(compData); //real texture data - 32 bits per 4x4 block (one byte per 4px horizontal line, each descending 1px)
|
var texView = new DataView(compData); //real texture data - 32 bits per 4x4 block (one byte per 4px horizontal line, each descending 1px)
|
||||||
|
|
|
@ -406,6 +406,7 @@ window.spa = function(input) {
|
||||||
var dat = texView.getUint8(off++)
|
var dat = texView.getUint8(off++)
|
||||||
col = readPalColour(palView, palOff, dat&31, trans);
|
col = readPalColour(palView, palOff, dat&31, trans);
|
||||||
col[3] = (dat>>5)*(255/7);
|
col[3] = (dat>>5)*(255/7);
|
||||||
|
premultiply(col);
|
||||||
|
|
||||||
} else if (format == 2) { //2 bit pal
|
} else if (format == 2) { //2 bit pal
|
||||||
if (i%4 == 0) databuf = texView.getUint8(off++);
|
if (i%4 == 0) databuf = texView.getUint8(off++);
|
||||||
|
@ -426,6 +427,7 @@ window.spa = function(input) {
|
||||||
var dat = texView.getUint8(off++)
|
var dat = texView.getUint8(off++)
|
||||||
col = readPalColour(palView, palOff, dat&7, trans);
|
col = readPalColour(palView, palOff, dat&7, trans);
|
||||||
col[3] = (dat>>3)*(255/31);
|
col[3] = (dat>>3)*(255/31);
|
||||||
|
premultiply(col);
|
||||||
|
|
||||||
} else if (format == 7) { //raw color data
|
} else if (format == 7) { //raw color data
|
||||||
col = texView.getUint16(off, true);
|
col = texView.getUint16(off, true);
|
||||||
|
@ -434,6 +436,7 @@ window.spa = function(input) {
|
||||||
colourBuffer[2] = Math.round((((col>>10)&31)/31)*255)
|
colourBuffer[2] = Math.round((((col>>10)&31)/31)*255)
|
||||||
colourBuffer[3] = Math.round((col>>15)*255);
|
colourBuffer[3] = Math.round((col>>15)*255);
|
||||||
col = colourBuffer;
|
col = colourBuffer;
|
||||||
|
premultiply(col);
|
||||||
off += 2;
|
off += 2;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -446,6 +449,12 @@ window.spa = function(input) {
|
||||||
return canvas;
|
return canvas;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function premultiply(col) {
|
||||||
|
col[0] *= col[3]/255;
|
||||||
|
col[1] *= col[3]/255;
|
||||||
|
col[2] *= col[3]/255;
|
||||||
|
}
|
||||||
|
|
||||||
function readCompressedTex(tex) { //format 5, 4x4 texels. I'll keep this well documented so it's easy to understand.
|
function readCompressedTex(tex) { //format 5, 4x4 texels. I'll keep this well documented so it's easy to understand.
|
||||||
throw "compressed tex not supported for particles! (unknowns for tex data offsets and lengths?)";
|
throw "compressed tex not supported for particles! (unknowns for tex data offsets and lengths?)";
|
||||||
var off = 0;//tex.texOffset;
|
var off = 0;//tex.texOffset;
|
||||||
|
|
|
@ -42,10 +42,25 @@ window.nitroAnimator = function(bmd, bca) {
|
||||||
return bca.animData.objectData[anim].frames;
|
return bca.animData.objectData[anim].frames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getFrames(anim, length, frame, totalLength) {
|
||||||
|
//totalLength (realtime)
|
||||||
|
//startFrame (realtime)
|
||||||
|
//endFrame (realtime)
|
||||||
|
//frame is between 0 and totalLength
|
||||||
|
|
||||||
|
var f = Math.max(0, (Math.min(frame, anim.endFrame) - anim.startFrame) * anim.speed); //speed relative time
|
||||||
|
var end = Math.floor((anim.endFrame - anim.startFrame) * anim.speed);
|
||||||
|
var realEnd = Math.min(length-1, end);
|
||||||
|
return [Math.min(realEnd, Math.floor(f)), Math.min(realEnd, Math.ceil(f)), f%1];
|
||||||
|
}
|
||||||
|
|
||||||
function setFrame(anim, modelind, frame) {
|
function setFrame(anim, modelind, frame) {
|
||||||
|
|
||||||
var b = bca.animData.objectData[anim];
|
var b = bca.animData.objectData[anim];
|
||||||
|
|
||||||
|
var totalLength = getLength(anim);
|
||||||
|
frame %= getLength(anim);
|
||||||
|
|
||||||
var fLow = Math.floor(frame);
|
var fLow = Math.floor(frame);
|
||||||
var fHigh = Math.ceil(frame);
|
var fHigh = Math.ceil(frame);
|
||||||
var iterp = frame%1;
|
var iterp = frame%1;
|
||||||
|
@ -64,27 +79,21 @@ window.nitroAnimator = function(bmd, bca) {
|
||||||
if (a.translate != null) {
|
if (a.translate != null) {
|
||||||
translate = [];
|
translate = [];
|
||||||
if (a.tlExtra[0] != null) {
|
if (a.tlExtra[0] != null) {
|
||||||
var f = frame * a.tlExtra[0].speed;
|
var f = getFrames(a.tlExtra[0], a.translate[0].length, frame, totalLength);
|
||||||
var fLow = Math.floor(f)%a.translate[0].length;
|
var p = f[2];
|
||||||
var fHigh = Math.ceil(f)%a.translate[0].length;
|
translate[0] = a.translate[0][f[0]]*(1-p) + a.translate[0][f[1]]*(p);
|
||||||
var p = f%1;
|
|
||||||
translate[0] = a.translate[0][fHigh]*(p) + a.translate[0][fLow]*(1-p);
|
|
||||||
} else translate[0] = a.translate[0][0];
|
} else translate[0] = a.translate[0][0];
|
||||||
|
|
||||||
if (a.tlExtra[1] != null) {
|
if (a.tlExtra[1] != null) {
|
||||||
var f = frame * a.tlExtra[1].speed;
|
var f = getFrames(a.tlExtra[1], a.translate[1].length, frame, totalLength);
|
||||||
var fLow = Math.floor(f)%a.translate[1].length;
|
var p = f[2];
|
||||||
var fHigh = Math.ceil(f)%a.translate[1].length;
|
translate[1] = a.translate[1][f[0]]*(1-p) + a.translate[1][f[1]]*(p);
|
||||||
var p = f%1;
|
|
||||||
translate[1] = a.translate[1][fHigh]*(p) + a.translate[1][fLow]*(1-p);
|
|
||||||
} else translate[1] = a.translate[1][0];
|
} else translate[1] = a.translate[1][0];
|
||||||
|
|
||||||
if (a.tlExtra[2] != null) {
|
if (a.tlExtra[2] != null) {
|
||||||
var f = frame * a.tlExtra[2].speed;
|
var f = getFrames(a.tlExtra[2], a.translate[2].length, frame, totalLength);
|
||||||
var fLow = Math.floor(f)%a.translate[2].length;
|
var p = f[2];
|
||||||
var fHigh = Math.ceil(f)%a.translate[2].length;
|
translate[2] = a.translate[2][f[0]]*(1-p) + a.translate[2][f[1]]*(p);
|
||||||
var p = f%1;
|
|
||||||
translate[2] = a.translate[2][fHigh]*(p) + a.translate[2][fLow]*(1-p);
|
|
||||||
} else translate[2] = a.translate[2][0];
|
} else translate[2] = a.translate[2][0];
|
||||||
} else {
|
} else {
|
||||||
translate = fa.translate;
|
translate = fa.translate;
|
||||||
|
@ -93,13 +102,11 @@ window.nitroAnimator = function(bmd, bca) {
|
||||||
var rotate;
|
var rotate;
|
||||||
if (a.rotate != null) {
|
if (a.rotate != null) {
|
||||||
if (a.rotExtra != null) {
|
if (a.rotExtra != null) {
|
||||||
var f = frame * a.rotExtra.speed;
|
var f = getFrames(a.rotExtra, a.rotate.length, frame, totalLength);
|
||||||
var fLow = Math.floor(f)%a.rotate.length;
|
var p = f[2];
|
||||||
var fHigh = Math.ceil(f)%a.rotate.length;
|
|
||||||
var p = f%1;
|
|
||||||
|
|
||||||
var r1 = parseRotation(a.rotate[fLow]);
|
var r1 = parseRotation(a.rotate[f[0]]);
|
||||||
var r2 = parseRotation(a.rotate[fHigh]);
|
var r2 = parseRotation(a.rotate[f[1]]);
|
||||||
rotate = lerpMat3(r1, r2, p);
|
rotate = lerpMat3(r1, r2, p);
|
||||||
} else {
|
} else {
|
||||||
rotate = parseRotation(a.rotate[0]);
|
rotate = parseRotation(a.rotate[0]);
|
||||||
|
@ -112,27 +119,21 @@ window.nitroAnimator = function(bmd, bca) {
|
||||||
if (a.scale != null) {
|
if (a.scale != null) {
|
||||||
scale = [];
|
scale = [];
|
||||||
if (a.scExtra[0] != null) {
|
if (a.scExtra[0] != null) {
|
||||||
var f = frame * a.scExtra[0].speed;
|
var f = getFrames(a.scExtra[0], a.scale[0].length, frame, totalLength);
|
||||||
var fLow = Math.floor(f)%a.scale[0].length;
|
var p = f[2];
|
||||||
var fHigh = Math.ceil(f)%a.scale[0].length;
|
scale[0] = a.scale[0][f[0]].s1*(1-p) + a.scale[0][f[1]].s1*(p);
|
||||||
var p = f%1;
|
|
||||||
scale[0] = a.scale[0][fHigh].s1*(p) + a.scale[0][fLow].s1*(1-p);
|
|
||||||
} else scale[0] = a.scale[0][0].s1;
|
} else scale[0] = a.scale[0][0].s1;
|
||||||
|
|
||||||
if (a.scExtra[1] != null) {
|
if (a.scExtra[1] != null) {
|
||||||
var f = frame * a.scExtra[1].speed;
|
var f = getFrames(a.scExtra[1], a.scale[1].length, frame, totalLength);
|
||||||
var fLow = Math.floor(f)%a.scale[1].length;
|
var p = f[2];
|
||||||
var fHigh = Math.ceil(f)%a.scale[1].length;
|
scale[1] = a.scale[1][f[0]].s1*(1-p) + a.scale[1][f[1]].s1*(p);
|
||||||
var p = f%1;
|
|
||||||
scale[1] = a.scale[1][fHigh].s1*(p) + a.scale[1][fLow].s1*(1-p);
|
|
||||||
} else scale[1] = a.scale[1][0].s1;
|
} else scale[1] = a.scale[1][0].s1;
|
||||||
|
|
||||||
if (a.scExtra[2] != null) {
|
if (a.scExtra[2] != null) {
|
||||||
var f = frame * a.scExtra[2].speed;
|
var f = getFrames(a.scExtra[2], a.scale[2].length, frame, totalLength);
|
||||||
var fLow = Math.floor(f)%a.scale[2].length;
|
var p = f[2];
|
||||||
var fHigh = Math.ceil(f)%a.scale[2].length;
|
scale[2] = a.scale[2][f[0]].s1*(1-p) + a.scale[2][f[1]].s1*(p);
|
||||||
var p = f%1;
|
|
||||||
scale[2] = a.scale[2][fHigh].s1*(p) + a.scale[2][fLow].s1*(1-p);
|
|
||||||
} else scale[2] = a.scale[2][0].s1;
|
} else scale[2] = a.scale[2][0].s1;
|
||||||
} else {
|
} else {
|
||||||
scale = fa.scale;
|
scale = fa.scale;
|
||||||
|
@ -154,9 +155,15 @@ window.nitroAnimator = function(bmd, bca) {
|
||||||
var cmds = model.commands;
|
var cmds = model.commands;
|
||||||
var curMat = mat4.create();
|
var curMat = mat4.create();
|
||||||
var lastStackID = 0;
|
var lastStackID = 0;
|
||||||
|
var highestUsed = -1;
|
||||||
|
|
||||||
for (var i=0; i<cmds.length; i++) {
|
for (var i=0; i<cmds.length; i++) {
|
||||||
var cmd = cmds[i];
|
var cmd = cmds[i];
|
||||||
|
if (cmd.copy != null) {
|
||||||
|
//copy this matrix to somewhere else, because it's bound and is going to be overwritten.
|
||||||
|
matrices[cmd.dest] = mat4.clone(matrices[cmd.copy]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (cmd.restoreID != null) curMat = mat4.clone(matrices[cmd.restoreID]);
|
if (cmd.restoreID != null) curMat = mat4.clone(matrices[cmd.restoreID]);
|
||||||
var o = objs[cmd.obj];
|
var o = objs[cmd.obj];
|
||||||
mat4.multiply(curMat, curMat, objMats[cmd.obj]);
|
mat4.multiply(curMat, curMat, objMats[cmd.obj]);
|
||||||
|
@ -165,6 +172,7 @@ window.nitroAnimator = function(bmd, bca) {
|
||||||
if (cmd.stackID != null) {
|
if (cmd.stackID != null) {
|
||||||
matrices[cmd.stackID] = mat4.clone(curMat);
|
matrices[cmd.stackID] = mat4.clone(curMat);
|
||||||
lastStackID = cmd.stackID;
|
lastStackID = cmd.stackID;
|
||||||
|
if (lastStackID > highestUsed) highestUsed = lastStackID;
|
||||||
} else {
|
} else {
|
||||||
matrices[lastStackID] = mat4.clone(curMat);
|
matrices[lastStackID] = mat4.clone(curMat);
|
||||||
}
|
}
|
||||||
|
@ -172,10 +180,14 @@ window.nitroAnimator = function(bmd, bca) {
|
||||||
|
|
||||||
model.lastStackID = lastStackID;
|
model.lastStackID = lastStackID;
|
||||||
|
|
||||||
|
var scale = [model.head.scale, model.head.scale, model.head.scale];
|
||||||
targ.set(matBufEmpty);
|
targ.set(matBufEmpty);
|
||||||
var off=0;
|
var off=0;
|
||||||
for (var i=0; i<31; i++) {
|
for (var i=0; i<=highestUsed; i++) {
|
||||||
if (matrices[i] != null) targ.set(matrices[i], off);
|
if (matrices[i] != null) {
|
||||||
|
mat4.scale(matrices[i], matrices[i], scale);
|
||||||
|
targ.set(matrices[i], off);
|
||||||
|
}
|
||||||
off += 16;
|
off += 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -223,6 +223,8 @@ window.nitroRender = new function() {
|
||||||
this.init = function(ctx) {
|
this.init = function(ctx) {
|
||||||
gl = ctx;
|
gl = ctx;
|
||||||
this.gl = gl;
|
this.gl = gl;
|
||||||
|
this.billboardMat = mat4.create();
|
||||||
|
this.yBillboardMat = mat4.create();
|
||||||
|
|
||||||
shaders = nitroShaders.compileShaders(gl);
|
shaders = nitroShaders.compileShaders(gl);
|
||||||
|
|
||||||
|
@ -233,7 +235,7 @@ window.nitroRender = new function() {
|
||||||
this.prepareShader = function() {
|
this.prepareShader = function() {
|
||||||
//prepares the shader so no redundant calls have to be made. Should be called upon every program change.
|
//prepares the shader so no redundant calls have to be made. Should be called upon every program change.
|
||||||
gl.enable(gl.BLEND);
|
gl.enable(gl.BLEND);
|
||||||
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); //gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
||||||
this.last = {};
|
this.last = {};
|
||||||
gl.activeTexture(gl.TEXTURE0);
|
gl.activeTexture(gl.TEXTURE0);
|
||||||
gl.uniform1i(this.nitroShader.samplerUniform, 0);
|
gl.uniform1i(this.nitroShader.samplerUniform, 0);
|
||||||
|
@ -261,6 +263,12 @@ window.nitroRender = new function() {
|
||||||
this.prepareShader();
|
this.prepareShader();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setShadBias = function(bias) {
|
||||||
|
var shader = shaders[1];
|
||||||
|
gl.uniform1f(shader.shadOffUniform, bias);
|
||||||
|
gl.uniform1f(shader.farShadOffUniform, bias);
|
||||||
|
}
|
||||||
|
|
||||||
this.resetShadOff = function() {
|
this.resetShadOff = function() {
|
||||||
var shader = shaders[1];
|
var shader = shaders[1];
|
||||||
gl.uniform1f(shader.shadOffUniform, 0.00005+((mobile)?0.0005:0));
|
gl.uniform1f(shader.shadOffUniform, 0.00005+((mobile)?0.0005:0));
|
||||||
|
@ -372,7 +380,7 @@ window.nitroRender = new function() {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function nitroModel(bmd, btx, remap) {
|
function nitroModel(bmd, btx) {
|
||||||
var bmd = bmd;
|
var bmd = bmd;
|
||||||
this.bmd = bmd;
|
this.bmd = bmd;
|
||||||
var thisObj = this;
|
var thisObj = this;
|
||||||
|
@ -399,7 +407,9 @@ function nitroModel(bmd, btx, remap) {
|
||||||
this.draw = draw;
|
this.draw = draw;
|
||||||
this.drawPoly = externDrawPoly;
|
this.drawPoly = externDrawPoly;
|
||||||
this.drawModel = externDrawModel;
|
this.drawModel = externDrawModel;
|
||||||
|
this.getBoundingCollisionModel = getBoundingCollisionModel;
|
||||||
this.getCollisionModel = getCollisionModel;
|
this.getCollisionModel = getCollisionModel;
|
||||||
|
this.baseMat = mat4.create();
|
||||||
|
|
||||||
modelBuffers = []
|
modelBuffers = []
|
||||||
this.modelBuffers = modelBuffers;
|
this.modelBuffers = modelBuffers;
|
||||||
|
@ -409,10 +419,6 @@ function nitroModel(bmd, btx, remap) {
|
||||||
matBuf.push({built: false, dat: new Float32Array(31*16)});
|
matBuf.push({built: false, dat: new Float32Array(31*16)});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (remap != null) {
|
|
||||||
setTextureRemap(remap);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (btx != null) {
|
if (btx != null) {
|
||||||
loadTexture(btx);
|
loadTexture(btx);
|
||||||
} else if (bmd.tex != null) {
|
} else if (bmd.tex != null) {
|
||||||
|
@ -461,41 +467,47 @@ function nitroModel(bmd, btx, remap) {
|
||||||
var model = models[j];
|
var model = models[j];
|
||||||
var mat = model.materials.objectData
|
var mat = model.materials.objectData
|
||||||
for (var i=0; i<mat.length; i++) {
|
for (var i=0; i<mat.length; i++) {
|
||||||
var m = mat[i];
|
mat[i].texInd = tex.length;
|
||||||
var texI = mat[i].tex;
|
loadMatTex(mat[i], btx);
|
||||||
var palI = mat[i].pal;
|
|
||||||
//remap below
|
|
||||||
var nTex = texMap.tex[texI];
|
|
||||||
var nPal = texMap.pal[palI];
|
|
||||||
if ((texI == null && nTex == null) || (palI == null && nPal == null)) {
|
|
||||||
debugger;
|
|
||||||
console.warn("WARNING: material "+i+" in model could not be assigned a texture.")
|
|
||||||
|
|
||||||
var fC = document.createElement("canvas");
|
|
||||||
fC.width = 2;
|
|
||||||
fC.height = 2;
|
|
||||||
var ctx = fC.getContext("2d")
|
|
||||||
ctx.fillStyle = "white";
|
|
||||||
ctx.fillRect(0,0,2,2);
|
|
||||||
texCanvas.push(fC);
|
|
||||||
var t = loadTex(fC, gl, !m.repeatX, !m.repeatY);
|
|
||||||
t.realWidth = 2;
|
|
||||||
t.realHeight = 2;
|
|
||||||
tex.push(t);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var truetex = (nTex==null)?texI:nTex;
|
|
||||||
var truepal = (nPal==null)?palI:nPal;
|
|
||||||
var cacheID = truetex+":"+truepal;
|
|
||||||
var cached = btx.cache[cacheID];
|
|
||||||
|
|
||||||
tex.push(cacheTex(btx, truetex, truepal, m));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function loadMatTex(mat, btx, matReplace) {
|
||||||
|
var m = mat;
|
||||||
|
if (matReplace) m = matReplace;
|
||||||
|
var texI = m.texName;
|
||||||
|
var palI = m.palName;
|
||||||
|
|
||||||
|
if (texI == null || palI == null) {
|
||||||
|
debugger;
|
||||||
|
console.warn("WARNING: material "+i+" in model could not be assigned a texture.");
|
||||||
|
/*
|
||||||
|
|
||||||
|
var fC = document.createElement("canvas");
|
||||||
|
fC.width = 2;
|
||||||
|
fC.height = 2;
|
||||||
|
var ctx = fC.getContext("2d")
|
||||||
|
ctx.fillStyle = "white";
|
||||||
|
ctx.fillRect(0,0,2,2);
|
||||||
|
texCanvas.push(fC);
|
||||||
|
var t = loadTex(fC, gl, !mat.repeatX, !mat.repeatY);
|
||||||
|
t.realWidth = 2;
|
||||||
|
t.realHeight = 2;
|
||||||
|
tex.push(t);
|
||||||
|
*/
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var truetex = loadedTex.textureInfo.nameToIndex["$" + texI] || 0;
|
||||||
|
var truepal = loadedTex.paletteInfo.nameToIndex["$" + palI] || 0;
|
||||||
|
var cacheID = truetex+":"+truepal;
|
||||||
|
var cached = btx.cache[cacheID];
|
||||||
|
|
||||||
|
tex[mat.texInd] = cacheTex(btx, truetex, truepal, mat);
|
||||||
|
}
|
||||||
|
|
||||||
function cacheTex(btx, truetex, truepal, m) {
|
function cacheTex(btx, truetex, truepal, m) {
|
||||||
var cacheID = truetex+":"+truepal;
|
var cacheID = truetex+":"+truepal;
|
||||||
var cached = btx.cache[cacheID];
|
var cached = btx.cache[cacheID];
|
||||||
|
@ -542,11 +554,6 @@ function nitroModel(bmd, btx, remap) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setTextureRemap(remap) {
|
|
||||||
texMap = remap;
|
|
||||||
if (loadedTex != null) loadTexture(loadedTex)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loadTexAnim = function(bta) {
|
this.loadTexAnim = function(bta) {
|
||||||
texAnim = bta;
|
texAnim = bta;
|
||||||
texFrame = 0;
|
texFrame = 0;
|
||||||
|
@ -560,6 +567,11 @@ function nitroModel(bmd, btx, remap) {
|
||||||
texFrame = frame;
|
texFrame = frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setBaseMat = function(mat) {
|
||||||
|
thisObj.baseMat = mat;
|
||||||
|
thisObj.billboardID = -1;
|
||||||
|
}
|
||||||
|
|
||||||
function externDrawModel(mv, project, mdl) {
|
function externDrawModel(mv, project, mdl) {
|
||||||
var models = bmd.modelData.objectData;
|
var models = bmd.modelData.objectData;
|
||||||
drawModel(models[mdl], mv, project, mdl);
|
drawModel(models[mdl], mv, project, mdl);
|
||||||
|
@ -584,7 +596,7 @@ function nitroModel(bmd, btx, remap) {
|
||||||
|
|
||||||
var shader = nitroRender.nitroShader;
|
var shader = nitroRender.nitroShader;
|
||||||
|
|
||||||
var mv = mat4.scale([], mv, [model.head.scale, model.head.scale, model.head.scale]);
|
//var mv = mat4.scale([], mv, [model.head.scale, model.head.scale, model.head.scale]);
|
||||||
|
|
||||||
gl.uniformMatrix4fv(shader.mvMatrixUniform, false, mv);
|
gl.uniformMatrix4fv(shader.mvMatrixUniform, false, mv);
|
||||||
gl.uniformMatrix4fv(shader.pMatrixUniform, false, project);
|
gl.uniformMatrix4fv(shader.pMatrixUniform, false, project);
|
||||||
|
@ -603,7 +615,85 @@ function nitroModel(bmd, btx, remap) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCollisionModel(modelind, polyind) { //simple func to get collision model for a model. used when I'm too lazy to define my own... REQUIRES TRI MODE ACTIVE!
|
function getBoundingCollisionModel(modelind, polyind) { //simple func to get collision model for a model. used when I'm too lazy to define my own... REQUIRES TRI MODE ACTIVE!
|
||||||
|
var model = bmd.modelData.objectData[modelind];
|
||||||
|
var poly = model.polys.objectData[polyind];
|
||||||
|
if (modelBuffers[modelind][polyind] == null) modelBuffers[modelind][polyind] = nitroRender.renderDispList(poly.disp, tex[poly.mat], (poly.stackID == null)?model.lastStackID:poly.stackID);
|
||||||
|
|
||||||
|
var tris = modelBuffers[modelind][polyind].strips[0].posArray;
|
||||||
|
|
||||||
|
var tC = tris.length/3;
|
||||||
|
var off = 0;
|
||||||
|
var min = [Infinity, Infinity, Infinity];
|
||||||
|
var max = [-Infinity, -Infinity, -Infinity];
|
||||||
|
for (var i=0; i<tC; i++) {
|
||||||
|
var tri = [tris[off++], tris[off++], tris[off++]];
|
||||||
|
for (var j=0; j<3; j++) {
|
||||||
|
if (tri[j] < min[j]) min[j] = tri[j];
|
||||||
|
if (tri[j] > max[j]) max[j] = tri[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//create the bounding box
|
||||||
|
out = [
|
||||||
|
{ //top
|
||||||
|
Vertices: [[max[0], max[1], max[2]], [max[0], max[1], min[2]], [min[0], max[1], min[2]]],
|
||||||
|
Normal: [0, 1, 0]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vertices: [[min[0], max[1], min[2]], [min[0], max[1], max[2]], [max[0], max[1], max[2]]],
|
||||||
|
Normal: [0, 1, 0]
|
||||||
|
},
|
||||||
|
|
||||||
|
{ //bottom
|
||||||
|
Vertices: [[min[0], min[1], min[2]], [max[0], min[1], min[2]], [max[0], min[1], max[2]] ],
|
||||||
|
Normal: [0, -1, 0]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vertices: [[max[0], min[1], max[2]], [min[0], min[1], max[2]], [min[0], min[1], min[2]] ],
|
||||||
|
Normal: [0, -1, 0]
|
||||||
|
},
|
||||||
|
|
||||||
|
{ //back (farthest z)
|
||||||
|
Vertices: [[max[0], max[1], max[2]], [max[0], min[1], max[2]], [min[0], min[1], max[2]]],
|
||||||
|
Normal: [0, 0, 1]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vertices: [[min[0], min[1], max[2]], [min[0], max[1], max[2]], [max[0], max[1], max[2]]],
|
||||||
|
Normal: [0, 0, 1]
|
||||||
|
},
|
||||||
|
|
||||||
|
{ //front (closest z)
|
||||||
|
Vertices: [[min[0], min[1], min[2]], [max[0], min[1], min[2]], [max[0], max[1], min[2]]],
|
||||||
|
Normal: [0, 0, -1]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vertices: [[max[0], max[1], min[2]], [min[0], max[1], min[2]], [min[0], min[1], min[2]]],
|
||||||
|
Normal: [0, 0, -1]
|
||||||
|
},
|
||||||
|
|
||||||
|
{ //right (pos x)
|
||||||
|
Vertices: [[max[0], max[1], max[2]], [max[0], min[1], max[2]], [max[0], min[1], min[2]]],
|
||||||
|
Normal: [1, 0, 0]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vertices: [[max[0], min[1], min[2]], [max[0], max[1], min[2]], [max[0], max[1], max[2]]],
|
||||||
|
Normal: [1, 0, 0]
|
||||||
|
},
|
||||||
|
|
||||||
|
{ //left (neg x)
|
||||||
|
Vertices: [[-max[0], min[1], min[2]], [-max[0], min[1], max[2]], [-max[0], max[1], max[2]]],
|
||||||
|
Normal: [-1, 0, 0]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vertices: [[-max[0], max[1], max[2]], [-max[0], max[1], min[2]], [-max[0], min[1], min[2]]],
|
||||||
|
Normal: [-1, 0, 0]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
out.push()
|
||||||
|
return {dat:out, scale:model.head.scale};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCollisionModel(modelind, polyind, colType) { //simple func to get collision model for a model. used when I'm too lazy to define my own... REQUIRES TRI MODE ACTIVE!
|
||||||
if (collisionModel[modelind] == null) collisionModel[modelind] = [];
|
if (collisionModel[modelind] == null) collisionModel[modelind] = [];
|
||||||
if (collisionModel[modelind][polyind] != null) return collisionModel[modelind][polyind];
|
if (collisionModel[modelind][polyind] != null) return collisionModel[modelind][polyind];
|
||||||
var model = bmd.modelData.objectData[modelind];
|
var model = bmd.modelData.objectData[modelind];
|
||||||
|
@ -617,14 +707,16 @@ function nitroModel(bmd, btx, remap) {
|
||||||
var off = 0;
|
var off = 0;
|
||||||
for (var i=0; i<tC; i++) {
|
for (var i=0; i<tC; i++) {
|
||||||
var t = {}
|
var t = {}
|
||||||
t.Vertex1 = [tris[off++], tris[off++], tris[off++]];
|
t.Vertices = [];
|
||||||
t.Vertex2 = [tris[off++], tris[off++], tris[off++]];
|
t.Vertices[0] = [tris[off++], tris[off++], tris[off++]];
|
||||||
t.Vertex3 = [tris[off++], tris[off++], tris[off++]];
|
t.Vertices[1] = [tris[off++], tris[off++], tris[off++]];
|
||||||
|
t.Vertices[2] = [tris[off++], tris[off++], tris[off++]];
|
||||||
|
|
||||||
//calculate normal
|
//calculate normal
|
||||||
var v = vec3.sub([], t.Vertex2, t.Vertex1);
|
var v = vec3.sub([], t.Vertices[1], t.Vertices[0]);
|
||||||
var w = vec3.sub([], t.Vertex3, t.Vertex1);
|
var w = vec3.sub([], t.Vertices[2], t.Vertices[0]);
|
||||||
t.Normal = vec3.cross([], v, w)
|
t.Normal = vec3.cross([], v, w);
|
||||||
|
t.CollisionType = colType;
|
||||||
vec3.normalize(t.Normal, t.Normal);
|
vec3.normalize(t.Normal, t.Normal);
|
||||||
out.push(t);
|
out.push(t);
|
||||||
}
|
}
|
||||||
|
@ -647,7 +739,7 @@ function nitroModel(bmd, btx, remap) {
|
||||||
}
|
}
|
||||||
var shader = nitroRender.nitroShader;
|
var shader = nitroRender.nitroShader;
|
||||||
|
|
||||||
var mv = mat4.scale([], mv, [model.head.scale, model.head.scale, model.head.scale]);
|
//var mv = mat4.scale([], mv, [model.head.scale, model.head.scale, model.head.scale]);
|
||||||
|
|
||||||
gl.uniformMatrix4fv(shader.mvMatrixUniform, false, mv);
|
gl.uniformMatrix4fv(shader.mvMatrixUniform, false, mv);
|
||||||
gl.uniformMatrix4fv(shader.pMatrixUniform, false, project);
|
gl.uniformMatrix4fv(shader.pMatrixUniform, false, project);
|
||||||
|
@ -678,9 +770,13 @@ function nitroModel(bmd, btx, remap) {
|
||||||
//we got a match! it's wonderful :')
|
//we got a match! it's wonderful :')
|
||||||
var anim = anims.objectData[animNum];
|
var anim = anims.objectData[animNum];
|
||||||
//look thru frames for the approprate point in the animation
|
//look thru frames for the approprate point in the animation
|
||||||
for (var i=0; i<anim.frames.length; i++) {
|
for (var i=anim.frames.length-1; i>=0; i--) {
|
||||||
if (offFrame >= anim.frames[i].time) {
|
if (offFrame >= anim.frames[i].time) {
|
||||||
|
loadMatTex(model.materials.objectData[pmat], btx == null ? bmd.tex : btx, anim.frames[i]);
|
||||||
|
/*
|
||||||
tex[pmat] = cacheTex(btx == null ? bmd.tex : btx, anim.frames[i].tex, anim.frames[i].mat, model.materials.objectData[pmat]);
|
tex[pmat] = cacheTex(btx == null ? bmd.tex : btx, anim.frames[i].tex, anim.frames[i].mat, model.materials.objectData[pmat]);
|
||||||
|
*/
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -692,7 +788,7 @@ function nitroModel(bmd, btx, remap) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var material = model.materials.objectData[pmat];
|
var material = model.materials.objectData[pmat];
|
||||||
nitroRender.setAlpha(material.alpha)
|
nitroRender.setAlpha(material.alpha);
|
||||||
|
|
||||||
if (texAnim != null) {
|
if (texAnim != null) {
|
||||||
//generate and send texture matrix from data
|
//generate and send texture matrix from data
|
||||||
|
@ -703,9 +799,7 @@ function nitroModel(bmd, btx, remap) {
|
||||||
if (animNum != -1) {
|
if (animNum != -1) {
|
||||||
//we got a match! it's wonderful :')
|
//we got a match! it's wonderful :')
|
||||||
var anim = anims.objectData[animNum];
|
var anim = anims.objectData[animNum];
|
||||||
var mat = mat3.create(); //material texture mat is ignored
|
var mat = matAtFrame(texFrame, anim);
|
||||||
mat3.scale(mat, mat, [anim.scaleS[(texFrame>>anim.frameStep.scaleS)%anim.scaleS.length], anim.scaleT[(texFrame>>anim.frameStep.scaleT)%anim.scaleT.length]]);
|
|
||||||
mat3.translate(mat, mat, [-anim.translateS[(texFrame>>anim.frameStep.translateS)%anim.translateS.length], anim.translateT[(texFrame>>anim.frameStep.translateT)%anim.translateT.length]]) //for some mystery reason I need to negate the S translation
|
|
||||||
gl.uniformMatrix3fv(shader.texMatrixUniform, false, mat);
|
gl.uniformMatrix3fv(shader.texMatrixUniform, false, mat);
|
||||||
} else {
|
} else {
|
||||||
gl.uniformMatrix3fv(shader.texMatrixUniform, false, material.texMat);
|
gl.uniformMatrix3fv(shader.texMatrixUniform, false, material.texMat);
|
||||||
|
@ -717,24 +811,52 @@ function nitroModel(bmd, btx, remap) {
|
||||||
drawModelBuffer(modelBuffers[modelind][polyind], gl, shader);
|
drawModelBuffer(modelBuffers[modelind][polyind], gl, shader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function frameLerp(frame, step, values) {
|
||||||
|
if (values.length == 1) return values[0];
|
||||||
|
var i = (frame / (1 << step)) % 1;
|
||||||
|
var len = values.length
|
||||||
|
if (step > 0) len -= 1;
|
||||||
|
var frame1 = (frame>>step)%len;
|
||||||
|
var from = values[frame1];
|
||||||
|
var to = values[frame1+1] || values[frame1];
|
||||||
|
return to * i + from * (1-i);
|
||||||
|
}
|
||||||
|
|
||||||
|
function matAtFrame(frame, anim) {
|
||||||
|
var mat = mat3.create(); //material texture mat is ignored
|
||||||
|
|
||||||
|
mat3.scale(mat, mat, [frameLerp(frame, anim.frameStep.scaleS, anim.scaleS), frameLerp(frame, anim.frameStep.scaleT, anim.scaleT)]);
|
||||||
|
mat3.translate(mat, mat, [-frameLerp(frame, anim.frameStep.translateS, anim.translateS), frameLerp(frame, anim.frameStep.translateT, anim.translateT)]);
|
||||||
|
|
||||||
|
return mat;
|
||||||
|
}
|
||||||
|
|
||||||
function generateMatrixStack(model, targ) { //this generates a matrix stack with the default bones. use nitroAnimator to pass custom matrix stacks using nsbca animations.
|
function generateMatrixStack(model, targ) { //this generates a matrix stack with the default bones. use nitroAnimator to pass custom matrix stacks using nsbca animations.
|
||||||
var matrices = [];
|
var matrices = [];
|
||||||
|
|
||||||
var objs = model.objects.objectData;
|
var objs = model.objects.objectData;
|
||||||
var cmds = model.commands;
|
var cmds = model.commands;
|
||||||
var curMat = mat4.create();
|
var curMat = mat4.clone(thisObj.baseMat);
|
||||||
var lastStackID = 0;
|
var lastStackID = 0;
|
||||||
|
var highestUsed = -1;
|
||||||
|
|
||||||
for (var i=0; i<cmds.length; i++) {
|
for (var i=0; i<cmds.length; i++) {
|
||||||
var cmd = cmds[i];
|
var cmd = cmds[i];
|
||||||
|
if (cmd.copy != null) {
|
||||||
|
//copy this matrix to somewhere else, because it's bound and is going to be overwritten.
|
||||||
|
matrices[cmd.dest] = mat4.clone(matrices[cmd.copy]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (cmd.restoreID != null) curMat = mat4.clone(matrices[cmd.restoreID]);
|
if (cmd.restoreID != null) curMat = mat4.clone(matrices[cmd.restoreID]);
|
||||||
var o = objs[cmd.obj];
|
var o = objs[cmd.obj];
|
||||||
mat4.multiply(curMat, curMat, o.mat);
|
mat4.multiply(curMat, curMat, o.mat);
|
||||||
if (o.billboardMode == 1) mat4.multiply(curMat, curMat, nitroRender.billboardMat);
|
if (o.billboardMode == 1) mat4.multiply(curMat, curMat, nitroRender.billboardMat);
|
||||||
if (o.billboardMode == 2) mat4.multiply(curMat, curMat, nitroRender.yBillboardMat);
|
if (o.billboardMode == 2) mat4.multiply(curMat, curMat, nitroRender.yBillboardMat);
|
||||||
|
|
||||||
if (cmd.stackID != null) {
|
if (cmd.stackID != null) {
|
||||||
matrices[cmd.stackID] = mat4.clone(curMat);
|
matrices[cmd.stackID] = mat4.clone(curMat);
|
||||||
lastStackID = cmd.stackID;
|
lastStackID = cmd.stackID;
|
||||||
|
if (lastStackID > highestUsed) highestUsed = lastStackID;
|
||||||
} else {
|
} else {
|
||||||
matrices[lastStackID] = mat4.clone(curMat);
|
matrices[lastStackID] = mat4.clone(curMat);
|
||||||
}
|
}
|
||||||
|
@ -742,10 +864,14 @@ function nitroModel(bmd, btx, remap) {
|
||||||
|
|
||||||
model.lastStackID = lastStackID;
|
model.lastStackID = lastStackID;
|
||||||
|
|
||||||
|
var scale = [model.head.scale, model.head.scale, model.head.scale];
|
||||||
targ.set(matBufEmpty);
|
targ.set(matBufEmpty);
|
||||||
var off=0;
|
var off=0;
|
||||||
for (var i=0; i<31; i++) {
|
for (var i=0; i<=highestUsed; i++) {
|
||||||
if (matrices[i] != null) targ.set(matrices[i], off);
|
if (matrices[i] != null) {
|
||||||
|
mat4.scale(matrices[i], matrices[i], scale);
|
||||||
|
targ.set(matrices[i], off);
|
||||||
|
}
|
||||||
off += 16;
|
off += 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -113,7 +113,8 @@ window.nitroShaders = new (function() {
|
||||||
}\n\
|
}\n\
|
||||||
\n\
|
\n\
|
||||||
void main(void) {\n\
|
void main(void) {\n\
|
||||||
vec4 col = texture2D(uSampler, vTextureCoord)*color;\n\
|
vec4 colorPM = vec4(color.rgb * color.a, color.a);\n\
|
||||||
|
vec4 col = texture2D(uSampler, vTextureCoord)*colorPM;\n\
|
||||||
\n\
|
\n\
|
||||||
vec2 ldNorm = abs((lightDist.xy)-vec2(0.5, 0.5));\n\
|
vec2 ldNorm = abs((lightDist.xy)-vec2(0.5, 0.5));\n\
|
||||||
float dist = max(ldNorm.x, ldNorm.y);\n\
|
float dist = max(ldNorm.x, ldNorm.y);\n\
|
||||||
|
|
|
@ -32,22 +32,25 @@ window.Race3DUI = function(scene, type, animStart) {
|
||||||
|
|
||||||
var params = {
|
var params = {
|
||||||
"count": [ //offset 21 down
|
"count": [ //offset 21 down
|
||||||
-128/1024, 128/1024, -(192-11)/1024, 11/1024
|
-128/1024, 128/1024, -(96)/1024, 96/1024
|
||||||
],
|
],
|
||||||
"start": [ //offset 86 up
|
"start": [ //offset 86 up
|
||||||
-128/1024, 128/1024, -(192+66)/1024, -66/1024
|
-128/1024, 128/1024, -(96)/1024, 96/1024
|
||||||
],
|
],
|
||||||
"goal": [ //why are these all so different?
|
"goal": [ //why are these all so different?
|
||||||
-128/1024, 128/1024, -(512 + 64)/1024, -(512 - 128)/1024
|
-128/1024, 128/1024, -(96)/1024, 96/1024
|
||||||
|
//-128/1024, 128/1024, -(512 + 64)/1024, -(512 - 128)/1024
|
||||||
],
|
],
|
||||||
|
|
||||||
//animations seem completely broken for these two (quickly files off screen after start)
|
//animations seem completely broken for these two (quickly files off screen after start)
|
||||||
//right now the vertical range of the viewport is large to try figure out where the hell it's going?
|
//right now the vertical range of the viewport is large to try figure out where the hell it's going?
|
||||||
"win": [
|
"win": [
|
||||||
-128/1024, 128/1024, -(1024)/1024, 1024/1024
|
-128/1024, 128/1024, -(96)/1024, 96/1024
|
||||||
|
//-128/1024, 128/1024, -(1024)/1024, 1024/1024
|
||||||
],
|
],
|
||||||
"lose": [
|
"lose": [
|
||||||
-128/1024, 128/1024, -(1024)/1024, 1024/1024
|
-128/1024, 128/1024, -(96)/1024, 96/1024
|
||||||
|
//-128/1024, 128/1024, -(1024)/1024, 1024/1024
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue