Compare commits
16 Commits
Author | SHA1 | Date |
---|---|---|
riperiperi | c45f9e2ba7 | |
riperiperi | 7d09b4790b | |
riperiperi | f972b8e616 | |
riperiperi | f979ef5379 | |
riperiperi | 2ae06f6345 | |
riperiperi | 1d9588e9a2 | |
riperiperi | b85b071ad8 | |
riperiperi | 3637ea13a4 | |
riperiperi | 446d07f1b4 | |
riperiperi | a773b60b95 | |
riperiperi | bbb61394f4 | |
riperiperi | f583ad11b1 | |
riperiperi | ff21142744 | |
riperiperi | e010627b5d | |
riperiperi | e1dc8603d1 | |
riperiperi | 4fdee73a5b |
|
@ -1,4 +1,5 @@
|
|||
*.nds
|
||||
*.exe
|
||||
*.sdat
|
||||
mongoose.conf
|
||||
Code/Engine/col.lua
|
|
@ -13,13 +13,13 @@
|
|||
fileQuota = 1;
|
||||
filesLoaded = 0;
|
||||
window.onload = function(argument) {
|
||||
loadFile("sound_data.sdat");
|
||||
loadFile("SD_BBP2p.sdat");
|
||||
}
|
||||
|
||||
var i=0;
|
||||
var last = null;
|
||||
function init() {
|
||||
nitroAudio.init(new sdat(files["sound_data.sdat"])); //89
|
||||
nitroAudio.init(new sdat(files["SD_BBP2p.sdat"])); //89
|
||||
//you need to extract this one yourself!
|
||||
|
||||
play.addEventListener('click', function() {
|
||||
|
@ -29,7 +29,7 @@
|
|||
})
|
||||
/*
|
||||
var ctx = new AudioContext();
|
||||
test = new sdat(files["sound_data.sdat"]);
|
||||
test = new sdat(files["SD_BBP2p.sdat"]);
|
||||
testAud = new SSEQPlayer(test.sections["$INFO"][0][5], test, ctx);
|
||||
|
||||
var elem = document.getElementById('play'),
|
||||
|
|
|
@ -18,6 +18,7 @@ window.nitroAudio = new (function() {
|
|||
t.kill = kill;
|
||||
t.init = init;
|
||||
t.instaKill = instaKill;
|
||||
t.updateListener = updateListener;
|
||||
|
||||
t.sdat = null;
|
||||
|
||||
|
@ -33,6 +34,25 @@ window.nitroAudio = new (function() {
|
|||
t.sdat = sdat;
|
||||
}
|
||||
|
||||
function updateListener(pos, view) {
|
||||
var listener = ctx.listener;
|
||||
if (listener.positionX == null) {
|
||||
//use old setters. safari ios
|
||||
listener.setPosition(pos[0], pos[1], pos[2]);
|
||||
listener.setOrientation(view[8], -view[9], -view[10], view[4], view[5], view[6]);
|
||||
} else {
|
||||
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() {
|
||||
for (var i=0; i<t.sounds.length; i++) {
|
||||
var snd = t.sounds[i];
|
||||
|
@ -76,6 +96,7 @@ window.nitroAudio = new (function() {
|
|||
output.connect(sound.gainN);
|
||||
sound.panner = output;
|
||||
|
||||
if (sound.obj.soundProps == null) sound.obj.soundProps = obj;
|
||||
updatePanner(sound.panner, sound.obj.soundProps);
|
||||
} else {
|
||||
output = ctx.createGain();
|
||||
|
@ -105,9 +126,10 @@ window.nitroAudio = new (function() {
|
|||
if (panner == null || soundProps == null) return;
|
||||
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.refDistance != null) panner.refDistance = soundProps.refDistance;
|
||||
|
||||
panner.refDistance = soundProps.refDistance || 192;
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -20,35 +20,48 @@ window.cameraIngame = function(kart) {
|
|||
var lookAtOffset = [0, 16, 0]
|
||||
|
||||
var camNormal = [0, 1, 0];
|
||||
var forwardNormal = null;
|
||||
var camAngle = 0;
|
||||
var boostOff = 0;
|
||||
|
||||
function tweenVec3(from, to) {
|
||||
from[0] += (to[0]-from[0])*0.075;
|
||||
from[1] += (to[1]-from[1])*0.075;
|
||||
from[2] += (to[2]-from[2])*0.075;
|
||||
}
|
||||
|
||||
function getView(scene) {
|
||||
var loop = kart.physBasis != null && kart.physBasis.loop;
|
||||
var basis = buildBasis();
|
||||
tweenVec3(camOffset, loop ? [0, 12, -57] : [0, 32, -48]);
|
||||
var camPos = vec3.transformMat4([], camOffset, basis);
|
||||
var lookAtPos = vec3.transformMat4([], lookAtOffset, basis);
|
||||
|
||||
vec3.scale(camPos, camPos, 1/1024);
|
||||
vec3.scale(lookAtPos, lookAtPos, 1/1024);
|
||||
|
||||
var mat = mat4.lookAt(mat4.create(), camPos, lookAtPos, [0, 1, 0]);
|
||||
var mat = mat4.lookAt(mat4.create(), camPos, lookAtPos, (kart.physBasis) ? camNormal : [0, 1, 0]);
|
||||
var kpos = vec3.clone(kart.pos);
|
||||
if (kart.drifting && !kart.driftLanded && kart.ylock>0) kpos[1] -= kart.ylock;
|
||||
mat4.translate(mat, mat, vec3.scale([], kpos, -1/1024));
|
||||
|
||||
//interpolate visual normal roughly to target
|
||||
camNormal[0] += (kart.kartNormal[0]-camNormal[0])*0.075;
|
||||
camNormal[1] += (kart.kartNormal[1]-camNormal[1])*0.075;
|
||||
camNormal[2] += (kart.kartNormal[2]-camNormal[2])*0.075;
|
||||
tweenVec3(camNormal, kart.kartNormal);
|
||||
vec3.normalize(camNormal, camNormal);
|
||||
|
||||
if (kart.physBasis != null) {
|
||||
if (loop) {
|
||||
var kartA = kart.physicalDir+kart.driftOff/2;
|
||||
var forward = [Math.sin(kartA), 0, -Math.cos(kartA)];
|
||||
vec3.transformMat4(forward, forward, kart.physBasis.mat);
|
||||
camAngle += dirDiff(Math.atan2(forward[0], -forward[2]), camAngle)*0.075;
|
||||
if (forwardNormal == null) {
|
||||
forwardNormal = [Math.sin(camAngle), 0, -Math.cos(camAngle)];
|
||||
} else {
|
||||
tweenVec3(forwardNormal, forward);
|
||||
}
|
||||
} else {
|
||||
camAngle += dirDiff(kart.physicalDir+kart.driftOff/2, camAngle)*0.075;
|
||||
forwardNormal = null;
|
||||
}
|
||||
camAngle = fixDir(camAngle);
|
||||
|
||||
|
@ -67,8 +80,8 @@ window.cameraIngame = function(kart) {
|
|||
function buildBasis() {
|
||||
//order y, x, z
|
||||
var kart = thisObj.kart;
|
||||
var forward = [Math.sin(camAngle), 0, -Math.cos(camAngle)];
|
||||
var side = [Math.cos(camAngle), 0, Math.sin(camAngle)];
|
||||
var forward = (forwardNormal != null) ? forwardNormal : [Math.sin(camAngle), 0, -Math.cos(camAngle)];
|
||||
var side = vec3.cross([], forward, camNormal);
|
||||
/*
|
||||
if (kart.physBasis != null) {
|
||||
vec3.transformMat4(forward, forward, kart.physBasis.mat);
|
||||
|
|
|
@ -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.SMALLJUMP = 0x0E; //choco island 2's disaster ramps
|
||||
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.BOOST2 = 0x12;
|
||||
this.LOOP = 0x13; //like sticky but with boost applied. see rainbow road ds
|
||||
this.SOUNDROAD = 0x14;
|
||||
this.RR_SPECIAL_WALL = 0x15;
|
||||
|
||||
this.KNOCKBACK_DAMAGE = 0x1F;
|
||||
|
||||
this.GROUP_ROAD = [
|
||||
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,
|
||||
|
@ -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.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.WALL, this.WALL2, this.RR_SPECIAL_WALL
|
||||
this.WALL, this.WALL2, this.WALLOOB, this.RR_SPECIAL_WALL, this.KNOCKBACK_DAMAGE
|
||||
]
|
||||
|
||||
this.GROUP_BOOST = [
|
||||
this.BOOST, this.BOOST2, this.LOOP
|
||||
]
|
||||
|
||||
this.GROUP_OOB = [
|
||||
this.OOB, this.FALL
|
||||
]
|
||||
|
||||
this.PHYS_MAP = new Array(31);
|
||||
this.PHYS_MAP[this.ROAD] = 0;
|
||||
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_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_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},
|
||||
],
|
||||
|
||||
0x10: //wall 3
|
||||
0x10: //wall oob
|
||||
[
|
||||
{hit: MKDS_COLSOUNDS.HIT_CONCRETE},
|
||||
{},
|
||||
|
|
|
@ -7,6 +7,11 @@
|
|||
// includes: main.js
|
||||
//
|
||||
|
||||
function getPlayerControls() {
|
||||
if (mobile) return controlMobile;
|
||||
else return controlDefault;
|
||||
}
|
||||
|
||||
window.controlDefault = function() {
|
||||
|
||||
var thisObj = this;
|
||||
|
@ -28,7 +33,96 @@ window.controlDefault = function() {
|
|||
|
||||
//-1 to 1, intensity.
|
||||
turn: (keysArray[37]?-1:0)+(keysArray[39]?1:0),
|
||||
airTurn: (keysArray[40]?-1:0)+(keysArray[38]?1:0) //air excitebike turn, doesn't really have much function
|
||||
airTurn: (keysArray[40]?-1:0)+(keysArray[38]?1:0) //air excitebike turn, item fire direction
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
window.controlMobile = function() {
|
||||
var thisObj = this;
|
||||
this.local = true;
|
||||
var kart;
|
||||
var item = false;
|
||||
|
||||
this.setKart = function(k) {
|
||||
kart = k;
|
||||
thisObj.kart = k;
|
||||
}
|
||||
this.fetchInput = fetchInput;
|
||||
|
||||
function searchForTouch(rect) { //{touch: Touch, enterLeave: number} 1 is enter, leave is 2,
|
||||
for (var i=0; i<touches.length; i++) {
|
||||
var touch = touches[i];
|
||||
var inNow = (touch.x > rect[0] && touch.y > rect[1] && touch.x < rect[2] && touch.y < rect[3]);
|
||||
var inBefore = (touch.lastx > rect[0] && touch.lasty > rect[1] && touch.lastx < rect[2] && touch.lasty < rect[3]);
|
||||
|
||||
var active = inNow && !touch.released;
|
||||
|
||||
if (inNow == inBefore && inNow) {
|
||||
return {touch: touch, enterLeave: 0, active: active};
|
||||
} else if (inNow) {
|
||||
return {touch: touch, enterLeave: 1, active: active};
|
||||
} else if (inBefore) {
|
||||
return {touch: touch, enterLeave: 2, active: active};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function step(start, end, value) {
|
||||
return Math.max(0, Math.min(1, (value-start)/(end-start)));
|
||||
}
|
||||
|
||||
function fetchInput() {
|
||||
var targW = 1136;
|
||||
var targH = 640;
|
||||
//window.touches array is filled by the game container
|
||||
//touches [{x:number (0-1), y:number (0-1), pressed:boolean, released:boolean, lastx:number (0-1), lasty:number (0-1)}]
|
||||
|
||||
//accel unless reverse button is pressed
|
||||
var reverse = searchForTouch([955/targW, 320/targH, (955+125)/targW, (320+125)/targH]);
|
||||
reverse = (reverse != null) && reverse.active;
|
||||
|
||||
var driftTouch = searchForTouch([780/targW, 468/targH, (780+300)/targW, (468+125)/targH]); //drift button on the right
|
||||
var itemTouch = searchForTouch([50/targW, 468/targH, (50+300)/targW, (468+125)/targH]); //touch the button exactly
|
||||
var dPadTouch = searchForTouch([0/targW, (468-50)/targH, (0+400)/targW, (468+225)/targH]); //allow for some space
|
||||
|
||||
var turn = 0;
|
||||
if (dPadTouch != null && dPadTouch.active) {
|
||||
turn = step(0/targW, 400/targW, dPadTouch.touch.x);
|
||||
//digitize
|
||||
turn = Math.floor(turn*3) - 1;
|
||||
}
|
||||
|
||||
var itemDir = 0;
|
||||
if (!item) {
|
||||
//if we touch the dpad (more exact than direction), start pressing item
|
||||
if (itemTouch != null && itemTouch.active && itemTouch.touch.pressed) {
|
||||
item = true;
|
||||
}
|
||||
} else {
|
||||
//if we release dpad, fire the item
|
||||
if (dPadTouch == null || !dPadTouch.active) {
|
||||
if (dPadTouch != null) {
|
||||
//set direction based on flick direction or position
|
||||
var vel = dPadTouch.touch.lasty - dPadTouch.touch.y;
|
||||
if (vel > 2/targH) itemDir = -1; //flicked down
|
||||
if (vel < -2/targH) itemDir = 1; //flicked up
|
||||
}
|
||||
item = false;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
accel: !reverse, //x
|
||||
decel: reverse, //z
|
||||
drift: (driftTouch != null && driftTouch.active), //s
|
||||
item: item, //a
|
||||
|
||||
//-1 to 1, intensity.
|
||||
turn: turn,
|
||||
airTurn: itemDir //air excitebike turn, item fire direction
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,8 @@ window.controlRaceCPU = function(nkm) {
|
|||
var destConst;
|
||||
var destPoint;
|
||||
|
||||
var item = false;
|
||||
|
||||
function fetchInput() {
|
||||
//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.
|
||||
|
@ -83,6 +85,7 @@ window.controlRaceCPU = function(nkm) {
|
|||
offTrans += 1/240;
|
||||
|
||||
if (offTrans >= 1) chooseNewOff();
|
||||
item = !item;
|
||||
|
||||
return {
|
||||
accel: accel, //x
|
||||
|
|
|
@ -5,6 +5,137 @@
|
|||
// 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) {
|
||||
var r = this;
|
||||
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.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;
|
||||
|
||||
this.getChar = getChar;
|
||||
|
@ -61,6 +242,7 @@ window.IngameRes = function(rom) {
|
|||
}
|
||||
t.blueShell = new nitroModel(new nsbmd(r.MainRace.getFile("/Item/koura_w.nsbmd")));
|
||||
t.splat = new nitroModel(new nsbmd(r.MainRace.getFile("/Item/geso_sumi.nsbmd")));
|
||||
t.fakeBox = new nitroModel(new nsbmd(r.MainRace.getFile("/MapObj/box.nsbmd")));
|
||||
r.items = t;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@ window.ItemController = function(scene) {
|
|||
t.changeItem = changeItem;
|
||||
t.update = update;
|
||||
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
|
||||
|
||||
|
@ -47,10 +49,25 @@ window.ItemController = function(scene) {
|
|||
}
|
||||
|
||||
function draw(mvMatrix, pMatrix, gl) {
|
||||
nitroRender.setShadBias(0.001);
|
||||
for (var i=0; i<t.items.length; i++) {
|
||||
var e = t.items[i];
|
||||
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) {
|
||||
|
|
|
@ -14,12 +14,28 @@ window.lsc = new (function() {
|
|||
this.sweepEllipse = sweepEllipse;
|
||||
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 t=1;
|
||||
var tris = getTriList(pos, dir, kclO);
|
||||
var colPlane = null;
|
||||
var colPoint = null; //can be calculated from t, but we calculate it anyway so why not include
|
||||
t=1;
|
||||
var tris = getTriList(pos, dir, scn.kcl);
|
||||
colPlane = null;
|
||||
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++) {
|
||||
//first, check if we intersect the plane within reasonable t.
|
||||
//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;
|
||||
|
||||
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 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
|
||||
|
@ -44,24 +60,72 @@ window.lsc = new (function() {
|
|||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if (colPlane != null) {
|
||||
return {
|
||||
t: t,
|
||||
plane: colPlane,
|
||||
colPoint: colPoint,
|
||||
object: colO,
|
||||
normal: colPlane.Normal
|
||||
}
|
||||
} 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) {
|
||||
var obj = {};
|
||||
obj.Vertex1 = vec3.transformMat4([], tri.Vertex1, mat);
|
||||
obj.Vertex2 = vec3.transformMat4([], tri.Vertex2, mat);
|
||||
obj.Vertex3 = vec3.transformMat4([], tri.Vertex3, mat);
|
||||
obj.Vertices = [];
|
||||
obj.Vertices[0] = vec3.transformMat4([], tri.Vertices[0], 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);
|
||||
obj.CollisionType = tri.CollisionType;
|
||||
return obj;
|
||||
|
@ -69,17 +133,16 @@ window.lsc = new (function() {
|
|||
|
||||
function scaleTri(tri, eDim) {
|
||||
var obj = {};
|
||||
obj.Vertex1 = vec3.divide([], tri.Vertex1, eDim);
|
||||
obj.Vertex2 = vec3.divide([], tri.Vertex2, eDim);
|
||||
obj.Vertex3 = vec3.divide([], tri.Vertex3, eDim);
|
||||
obj.Vertices = [];
|
||||
obj.Vertices[0] = vec3.divide([], tri.Vertices[0], eDim);
|
||||
obj.Vertices[1] = vec3.divide([], tri.Vertices[1], eDim);
|
||||
obj.Vertices[2] = vec3.divide([], tri.Vertices[2], eDim);
|
||||
|
||||
obj.Normal = tri.Normal
|
||||
obj.CollisionType = tri.CollisionType;
|
||||
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.
|
||||
t=1;
|
||||
|
||||
|
@ -140,7 +203,7 @@ window.lsc = new (function() {
|
|||
if (ignoreList.indexOf(oTri) != -1) continue;
|
||||
|
||||
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 modDir = vec3.dot(tri.Normal, dir);
|
||||
|
||||
|
@ -200,14 +263,14 @@ window.lsc = new (function() {
|
|||
}
|
||||
|
||||
//no inside intersection check vertices:
|
||||
for (var j=1; j<=3; j++) {
|
||||
var vert = vec3.sub([], pos, tri["Vertex"+j]);
|
||||
for (var j=0; j<=2; 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);
|
||||
if (root != null) {
|
||||
t = root;
|
||||
colPlane = oTri;
|
||||
colO = targ;
|
||||
colPoint = vec3.clone(tri["Vertex"+j]); //result!
|
||||
colPoint = vec3.clone(tri.Vertices[j]); //result!
|
||||
planeNormal = tri.Normal;
|
||||
edge = false;
|
||||
}
|
||||
|
@ -215,9 +278,9 @@ window.lsc = new (function() {
|
|||
|
||||
//... and lines
|
||||
|
||||
for (var j=1; j<=3; j++) {
|
||||
var vert = tri["Vertex"+j];
|
||||
var nextV = tri["Vertex"+((j%3)+1)];
|
||||
for (var j=0; j<=2; j++) {
|
||||
var vert = tri.Vertices[j];
|
||||
var nextV = tri.Vertices[(j+1)%3];
|
||||
|
||||
var distVert = vec3.sub([], vert, pos);
|
||||
var distLine = vec3.sub([], nextV, vert);
|
||||
|
@ -277,9 +340,9 @@ window.lsc = new (function() {
|
|||
|
||||
function pointInTriangle(tri, point, error) { //barycentric check
|
||||
//compute direction vectors to the other verts and the point
|
||||
var v0 = vec3.sub([], tri.Vertex3, tri.Vertex1);
|
||||
var v1 = vec3.sub([], tri.Vertex2, tri.Vertex1);
|
||||
var v2 = vec3.sub([], point, tri.Vertex1);
|
||||
var v0 = vec3.sub([], tri.Vertices[2], tri.Vertices[0]);
|
||||
var v1 = vec3.sub([], tri.Vertices[1], tri.Vertices[0]);
|
||||
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
|
||||
//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
|
||||
|
|
|
@ -7,56 +7,60 @@
|
|||
|
||||
window.MKDSCONST = new (function() {
|
||||
|
||||
this.DAMAGE_SPIN = 0;
|
||||
this.DAMAGE_FLIP = 1;
|
||||
this.DAMAGE_EXPLODE = 2;
|
||||
|
||||
this.COURSEDIR = "/data/Course/";
|
||||
|
||||
this.COURSES = [ //in order of course id, nitro through retro
|
||||
"cross_course",
|
||||
"bank_course",
|
||||
"beach_course",
|
||||
"mansion_course",
|
||||
{name:"cross_course", music: 74},
|
||||
{name:"bank_course", music: 16},
|
||||
{name:"beach_course", music: 15},
|
||||
{name:"mansion_course", music: 21, lightHeight: 20/180, lightAngle: 160/180},
|
||||
|
||||
"desert_course",
|
||||
"town_course",
|
||||
"pinball_course",
|
||||
"ridge_course",
|
||||
{name:"desert_course", music: 38, lightHeight: 40/180},
|
||||
{name:"town_course", music: 17},
|
||||
{name:"pinball_course", music: 19},
|
||||
{name:"ridge_course", music: 36},
|
||||
|
||||
"snow_course",
|
||||
"clock_course",
|
||||
"mario_course",
|
||||
"airship_course",
|
||||
{name:"snow_course", music: 37},
|
||||
{name:"clock_course", music: 39},
|
||||
{name:"mario_course", music: 74},
|
||||
{name:"airship_course", music: 18, lightHeight: 40/180, lightAngle: 140/180},
|
||||
|
||||
"stadium_course",
|
||||
"garden_course",
|
||||
"koopa_course",
|
||||
"rainbow_course",
|
||||
{name:"stadium_course", music: 19},
|
||||
{name:"garden_course", music: 20},
|
||||
{name:"koopa_course", music: 40},
|
||||
{name:"rainbow_course", music: 41},
|
||||
|
||||
|
||||
"old_mario_sfc",
|
||||
"old_momo_64",
|
||||
"old_peach_agb",
|
||||
"old_luigi_gc",
|
||||
{name:"old_mario_sfc", music: 22},
|
||||
{name:"old_momo_64", music: 30},
|
||||
{name:"old_peach_agb", music: 26},
|
||||
{name:"old_luigi_gc", music: 33},
|
||||
|
||||
"old_donut_sfc",
|
||||
"old_frappe_64",
|
||||
"old_koopa_agb",
|
||||
"old_baby_gc",
|
||||
{name:"old_donut_sfc", music: 24},
|
||||
{name:"old_frappe_64", music: 31},
|
||||
{name:"old_koopa_agb", music: 27},
|
||||
{name:"old_baby_gc", music: 34},
|
||||
|
||||
"old_noko_sfc",
|
||||
"old_choco_64",
|
||||
"old_luigi_agb",
|
||||
"old_kinoko_gc",
|
||||
{name:"old_noko_sfc", music: 23},
|
||||
{name:"old_choco_64", music: 29},
|
||||
{name:"old_luigi_agb", music: 26},
|
||||
{name:"old_kinoko_gc", music: 35},
|
||||
|
||||
"old_choco_sfc",
|
||||
"old_hyudoro_64",
|
||||
"old_sky_agb",
|
||||
"old_yoshi_gc",
|
||||
{name:"old_choco_sfc", music: 25},
|
||||
{name:"old_hyudoro_64", music: 32},
|
||||
{name:"old_sky_agb", music: 28, skyboxShadows: true},
|
||||
{name:"old_yoshi_gc", music: 33, lightHeight: 30/180, lightAngle: 111/180},
|
||||
|
||||
"mini_stage1",
|
||||
"mini_stage2",
|
||||
"mini_stage3",
|
||||
"mini_stage4",
|
||||
"mini_block_64",
|
||||
"mini_dokan_gc"
|
||||
{name:"mini_stage1", music: 43, battle: true},
|
||||
{name:"mini_stage2", music: 43, battle: true, lightHeight: 20/180, lightAngle: 160/180},
|
||||
{name:"mini_stage3", music: 43, battle: true},
|
||||
{name:"mini_stage4", music: 43, battle: true},
|
||||
{name:"mini_block_64", music: 43, battle: true},
|
||||
{name:"mini_dokan_gc", music: 43, battle: true}
|
||||
|
||||
]
|
||||
|
||||
|
|
|
@ -88,11 +88,11 @@ window.clientScene = function(wsUrl, wsInstance, res) {
|
|||
var mainNarc, texNarc
|
||||
if (obj.c.substr(0, 5) == "mkds/") {
|
||||
var cnum = Number(obj.c.substr(5));
|
||||
var music = MKDSCONST.COURSE_MUSIC[cnum];
|
||||
var cDir = MKDSCONST.COURSEDIR+MKDSCONST.COURSES[cnum];
|
||||
var course = MKDSCONST.COURSE[cnum];
|
||||
var cDir = MKDSCONST.COURSEDIR+course.name;
|
||||
var mainNarc = new narc(lz77.decompress(gameROM.getFile(cDir+".carc")));
|
||||
var texNarc = new narc(lz77.decompress(gameROM.getFile(cDir+"Tex.carc")));
|
||||
setUpCourse(mainNarc, texNarc, music, obj)
|
||||
setUpCourse(mainNarc, texNarc, course, obj)
|
||||
}
|
||||
else throw "custom tracks are not implemented yet!"
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ window.clientScene = function(wsUrl, wsInstance, res) {
|
|||
wsH["$+"] = function(obj) { //add kart. only used in debug circumstances. (people can't join normal gamemodes midgame)
|
||||
console.log("kart added");
|
||||
if (t.mode != 1) return;
|
||||
var kart = new Kart([0, -2000, 0], 0, 0, obj.k.kart, obj.k.char, new ((obj.p)?((window.prompt("press y for cpu controlled") == "y")?controlRaceCPU:controlDefault):controlNetwork)(t.activeScene.nkm, {}), t.activeScene);
|
||||
var kart = new Kart([0, -2000, 0], 0, 0, obj.k.kart, obj.k.char, new ((obj.p)?((window.prompt("press y for cpu controlled") == "y")?controlRaceCPU:getPlayerControls()):controlNetwork)(t.activeScene.nkm, {}), t.activeScene);
|
||||
t.activeScene.karts.push(kart);
|
||||
}
|
||||
|
||||
|
@ -124,13 +124,13 @@ window.clientScene = function(wsUrl, wsInstance, res) {
|
|||
}
|
||||
}
|
||||
|
||||
function setUpCourse(mainNarc, texNarc, music, obj) {
|
||||
function setUpCourse(mainNarc, texNarc, course, obj) {
|
||||
var chars = [];
|
||||
for (var i=0; i<obj.k.length; i++) {
|
||||
var k = obj.k[i];
|
||||
var pKart = (i == obj.p);
|
||||
//TODO: custom character support
|
||||
chars.push({charN:k.char, kartN:k.kart, controller:((pKart)?((window.prompt("press y for cpu controlled") == "y")?controlRaceCPU:controlDefault):controlNetwork), raceCam:pKart, extraParams:[{k:"name", v:k.name}, {k:"active", v:k.active}]});
|
||||
chars.push({charN:k.char, kartN:k.kart, controller:((pKart)?((window.prompt("press y for cpu controlled") == "y")?controlRaceCPU:getPlayerControls()):controlNetwork), raceCam:pKart, extraParams:[{k:"name", v:k.name}, {k:"active", v:k.active}]});
|
||||
|
||||
if (pKart) {
|
||||
for (var i=0; i<7; i++) {
|
||||
|
@ -142,7 +142,7 @@ window.clientScene = function(wsUrl, wsInstance, res) {
|
|||
}
|
||||
}
|
||||
|
||||
t.activeScene = new courseScene(mainNarc, texNarc, music, chars, {}, res);
|
||||
t.activeScene = new courseScene(mainNarc, texNarc, course, chars, {}, res);
|
||||
|
||||
for (var i=0; i<obj.k.length; i++) {
|
||||
t.activeScene.karts[i].active = obj.k[i].active;
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
// render/*
|
||||
//
|
||||
|
||||
window.courseScene = function(mainNarc, texNarc, music, chars, options, gameRes) {
|
||||
|
||||
window.courseScene = function(mainNarc, texNarc, courseObj, chars, options, gameRes) {
|
||||
var music = courseObj.music;
|
||||
var startSetups = [
|
||||
{maxplayers:12, toAline:4, xspacing:32, yspacing:32, liney:160},
|
||||
{maxplayers:24, toAline:4, xspacing:32, yspacing:32, liney:80},
|
||||
|
@ -56,12 +56,17 @@ window.courseScene = function(mainNarc, texNarc, music, chars, options, gameRes)
|
|||
|
||||
//load main course
|
||||
var courseTx = new nsbtx(texNarc.getFile("/course_model.nsbtx"), false, true);
|
||||
|
||||
var taFile = mainNarc.getFile("/course_model.nsbta");
|
||||
if (taFile != null) var courseTa = new nsbta(taFile); //can be null
|
||||
var tpFile = mainNarc.getFile("/course_model.nsbtp");
|
||||
if (tpFile != null) var courseTp = new nsbtp(tpFile); //can be null
|
||||
|
||||
var courseMdl = new nsbmd(mainNarc.getFile("/course_model.nsbmd"));
|
||||
|
||||
var course = new nitroModel(courseMdl, courseTx)
|
||||
if (taFile != null) course.loadTexAnim(courseTa);
|
||||
if (tpFile != null) course.loadTexPAnim(courseTp);
|
||||
|
||||
//load sky
|
||||
var skyTx = new nsbtx(texNarc.getFile("/course_model_V.nsbtx"), false, true);
|
||||
|
@ -107,12 +112,18 @@ window.courseScene = function(mainNarc, texNarc, music, chars, options, gameRes)
|
|||
if (!shadow) {
|
||||
var skyMat = mat4.scale(mat4.create(), mvMatrix, [1/64, 1/64, 1/64]);
|
||||
sky.setFrame(frame);
|
||||
if (!courseObj.skyboxShadows) nitroRender.setLightIntensities(0, 0);
|
||||
sky.draw(skyMat, pMatrix);
|
||||
if (!courseObj.skyboxShadows) nitroRender.setLightIntensities();
|
||||
}
|
||||
|
||||
var lvlMat = mat4.scale(mat4.create(), mvMatrix, [1/64, 1/64, 1/64]);//[2, 2, 2]);
|
||||
course.setFrame(frame);
|
||||
nitroRender.forceFlatNormals = true;
|
||||
nitroRender.setLightIntensities(0);
|
||||
course.draw(lvlMat, pMatrix);
|
||||
nitroRender.setLightIntensities();
|
||||
nitroRender.forceFlatNormals = false;
|
||||
|
||||
var transE = [];
|
||||
|
||||
|
@ -134,13 +145,14 @@ window.courseScene = function(mainNarc, texNarc, music, chars, options, gameRes)
|
|||
else e.draw(mvMatrix, pMatrix, gl);
|
||||
}
|
||||
|
||||
nitroRender.setLightIntensities(0);
|
||||
for (var i=0; i<scn.particles.length; i++) {
|
||||
var e = scn.particles[i];
|
||||
e.draw(mvMatrix, pMatrix, gl);
|
||||
}
|
||||
|
||||
scn.items.draw(mvMatrix, pMatrix, gl);
|
||||
|
||||
nitroRender.setLightIntensities();
|
||||
}
|
||||
|
||||
function sndUpdate(view) {
|
||||
|
@ -202,6 +214,8 @@ window.courseScene = function(mainNarc, texNarc, music, chars, options, gameRes)
|
|||
}
|
||||
entsToRemove = [];
|
||||
var mat = scn.camera.getView(scn, nitroRender.getViewWidth(), nitroRender.getViewHeight());
|
||||
|
||||
nitroAudio.updateListener(mat.pos, mat.mv);
|
||||
frame++;
|
||||
}
|
||||
|
||||
|
@ -229,12 +243,28 @@ window.courseScene = function(mainNarc, texNarc, music, chars, options, gameRes)
|
|||
scn.paths = paths;
|
||||
}
|
||||
|
||||
function getLightCenter() {
|
||||
var average = vec3.create();
|
||||
var objs = scn.nkm.sections["OBJI"].entries;
|
||||
for (var i=0; i<objs.length; i++) {
|
||||
vec3.add(average, average, objs[i].pos);
|
||||
}
|
||||
vec3.scale(average, average, (1/objs.length) /-1024);
|
||||
return average;
|
||||
}
|
||||
|
||||
function startCourse() {
|
||||
scn.lightMat = mat4.create();
|
||||
mat4.rotateX(scn.lightMat, scn.lightMat, Math.PI*(61/180));
|
||||
mat4.rotateY(scn.lightMat, scn.lightMat, Math.PI*(21/180));
|
||||
|
||||
mat4.mul(scn.farShadMat, mat4.ortho(mat4.create(), -5, 5, -5, 5, -5, 5), scn.lightMat);
|
||||
mat4.rotateX(scn.lightMat, scn.lightMat, Math.PI*(courseObj.lightHeight || (61/180)));
|
||||
mat4.rotateY(scn.lightMat, scn.lightMat, Math.PI*(courseObj.lightAngle || (21/180)));
|
||||
scn.lightDir = [0, 0, 1];
|
||||
vec3.transformMat3(scn.lightDir, scn.lightDir, mat3.invert([], mat3.fromMat4([], scn.lightMat)));
|
||||
|
||||
scn.farShadMat = mat4.create();
|
||||
mat4.translate(scn.farShadMat, scn.lightMat, getLightCenter());
|
||||
|
||||
mat4.mul(scn.farShadMat, mat4.ortho(mat4.create(), -5, 5, -5, 5, -5, 5), scn.farShadMat);
|
||||
|
||||
compilePaths();
|
||||
|
||||
|
@ -388,7 +418,7 @@ window.courseScene = function(mainNarc, texNarc, music, chars, options, gameRes)
|
|||
switch (mode.id) {
|
||||
case 0:
|
||||
//race init. fade scene in and play init music.
|
||||
nitroAudio.playSound(11, {volume:2}, null); //7:race (gp), 11:race2 (vs), 12:battle
|
||||
nitroAudio.playSound((courseObj.battle)?12:11, {volume:2}, null); //7:race (gp), 11:race2 (vs), 12:battle
|
||||
break;
|
||||
case 1:
|
||||
//spawn lakitu and countdown animation. allow pre-acceleration.
|
||||
|
|
|
@ -84,7 +84,7 @@ window.sceneDrawer = new function() {
|
|||
gl.colorMask(false, false, false, false);
|
||||
scn.draw(gl, shadMat, true);
|
||||
|
||||
nitroRender.setShadowMode(shadowTarg.depth, scn.farShad.depth, shadMat, scn.farShadMat);
|
||||
nitroRender.setShadowMode(shadowTarg.depth, scn.farShad.depth, shadMat, scn.farShadMat, scn.lightDir);
|
||||
nitroRender.flagShadow = false;
|
||||
|
||||
nitroRender.updateBillboards(view.mv);
|
||||
|
|
|
@ -52,18 +52,18 @@ window.singleScene = function(course, wsInstance, res) {
|
|||
var mainNarc, texNarc
|
||||
if (course.substr(0, 5) == "mkds/") {
|
||||
var cnum = Number(course.substr(5));
|
||||
var music = MKDSCONST.COURSE_MUSIC[cnum];
|
||||
var cDir = MKDSCONST.COURSEDIR+MKDSCONST.COURSES[cnum];
|
||||
var course = MKDSCONST.COURSES[cnum];
|
||||
var cDir = MKDSCONST.COURSEDIR+course.name;
|
||||
var mainNarc = new narc(lz77.decompress(gameROM.getFile(cDir+".carc")));
|
||||
var texNarc = new narc(lz77.decompress(gameROM.getFile(cDir+"Tex.carc")));
|
||||
setUpCourse(mainNarc, texNarc, music)
|
||||
setUpCourse(mainNarc, texNarc, course)
|
||||
} else throw "custom tracks are not implemented yet!"
|
||||
}
|
||||
|
||||
|
||||
function setUpCourse(mainNarc, texNarc, music) {
|
||||
function setUpCourse(mainNarc, texNarc, course) {
|
||||
var chars = [];
|
||||
chars.push({charN:mchar, kartN:mkart, controller:((window.prompt("press y for cpu controlled") == "y")?controlRaceCPU:controlDefault), raceCam:true, extraParams:[{k:"name", v:"single"}, {k:"active", v:true}]});
|
||||
chars.push({charN:mchar, kartN:mkart, controller:((window.prompt("press y for cpu controlled") == "y")?controlRaceCPU:getPlayerControls()), raceCam:true, extraParams:[{k:"name", v:"single"}, {k:"active", v:true}]});
|
||||
|
||||
for (var i=0; i<7; i++) {
|
||||
var tchar = Math.floor(Math.random()*12);
|
||||
|
@ -72,7 +72,7 @@ window.singleScene = function(course, wsInstance, res) {
|
|||
chars.push({charN:tchar, kartN:tkart, controller:controlRaceCPU, raceCam:false, extraParams:[{k:"name", v:"no"}, {k:"active", v:true}]});
|
||||
}
|
||||
|
||||
t.activeScene = new courseScene(mainNarc, texNarc, music, chars, {}, res);
|
||||
t.activeScene = new courseScene(mainNarc, texNarc, course, chars, {}, res);
|
||||
|
||||
t.myKart = t.activeScene.karts[0];
|
||||
t.mode = {
|
||||
|
|
|
@ -10,7 +10,7 @@ window.fileStore = new (function(){
|
|||
|| window.mozIndexedDB
|
||||
|| window.shimIndexedDB;
|
||||
|
||||
var request = indexedDB.open("MKJS_DB", 1);
|
||||
var request = indexedDB.open("MKJS-DB", 1);
|
||||
request.onerror = window.onerror;
|
||||
|
||||
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) {
|
||||
if (typeof url == "string") {
|
||||
var xml = new XMLHttpRequest();
|
||||
|
@ -77,6 +89,7 @@ window.fileStore = new (function(){
|
|||
callback(dat);
|
||||
};
|
||||
request.onsuccess = function(event) {
|
||||
validateFiles();
|
||||
callback(dat);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// bowserPlatforms.js
|
||||
//--------------------
|
||||
// Provides platforms for Bowser's Castle
|
||||
// Provides moving platforms for Bowser's Castle and Delfino
|
||||
// by RHY3756547
|
||||
//
|
||||
// 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) {
|
||||
var obji = obji;
|
||||
var res = [];
|
||||
|
@ -111,12 +238,14 @@ window.ObjRoutePlatform = function(obji, scene) {
|
|||
t.elapsedTime = 0;
|
||||
|
||||
t.mode = 0;
|
||||
t.colFrame = 0;
|
||||
|
||||
var movVel;
|
||||
|
||||
//t.speed = (obji.setting1&0xFFFF)/8192;
|
||||
|
||||
function update(scene) {
|
||||
t.colFrame++;
|
||||
if (t.mode == 0) {
|
||||
t.elapsedTime += t.routeSpeed;
|
||||
movVel = vec3.sub([], t.nextNode.pos, t.prevPos);
|
||||
|
@ -159,15 +288,11 @@ window.ObjRoutePlatform = function(obji, scene) {
|
|||
function generateCol() {
|
||||
genCol = {dat: [
|
||||
{
|
||||
Vertex1: [25, 0, 11],
|
||||
Vertex2: [25, 0, -11],
|
||||
Vertex3: [-25, 0, -11],
|
||||
Vertices: [[25, 0, 11], [25, 0, -11], [-25, 0, -11]],
|
||||
Normal: [0, 1, 0]
|
||||
},
|
||||
{
|
||||
Vertex1: [-25, 0, -11],
|
||||
Vertex2: [-25, 0, 11],
|
||||
Vertex3: [25, 0, 11],
|
||||
Vertices: [[-25, 0, -11], [-25, 0, 11], [25, 0, 11]],
|
||||
Normal: [0, 1, 0]
|
||||
},
|
||||
], 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));
|
||||
|
||||
obj.mat = mat;
|
||||
obj.frame = t.colFrame;
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,22 +31,24 @@ window.ObjDecor = function(obji, scene) {
|
|||
var animMat = null;
|
||||
|
||||
function draw(view, pMatrix) {
|
||||
if (forceBill) nitroRender.setShadBias(0.001);
|
||||
mat4.translate(mat, view, 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));
|
||||
|
||||
if (anim != null) {
|
||||
animMat = anim.setFrame(0, 0, animFrame++);
|
||||
}
|
||||
|
||||
mat4.scale(mat, mat, vec3.scale([], t.scale, 16));
|
||||
res.mdl[0].draw(mat, pMatrix, animMat);
|
||||
if (forceBill) nitroRender.resetShadOff();
|
||||
}
|
||||
|
||||
function update() {
|
||||
|
||||
res.mdl[0].setFrame(animFrame);
|
||||
if (anim != null) {
|
||||
animMat = anim.setFrame(0, 0, animFrame);
|
||||
}
|
||||
animFrame++;
|
||||
}
|
||||
|
||||
function requireRes() { //scene asks what resources to load
|
||||
|
@ -75,7 +77,7 @@ window.ObjDecor = function(obji, scene) {
|
|||
case 0x0138:
|
||||
return {mdl:[{nsbmd:"GardenTree1.nsbmd"}]};
|
||||
case 0x0139:
|
||||
return {mdl:[{nsbmd:"kamome.nsbmd"}], other:[null, null, "kamone.nsbtp"]}; //animates using nsbtp, and uses route to move
|
||||
return {mdl:[{nsbmd:"kamome.nsbmd"}], other:[null, null, "kamome.nsbtp"]}; //animates using nsbtp, and uses route to move
|
||||
|
||||
case 0x013A:
|
||||
return {mdl:[{nsbmd:"CrossTree1.nsbmd"}]};
|
||||
|
@ -265,8 +267,12 @@ window.ObjDecor = function(obji, scene) {
|
|||
if (r.other.length > 0 && r.other[0] != null) {
|
||||
res.mdl[0].loadTexAnim(r.other[0]);
|
||||
}
|
||||
if (r.other.length > 1 && r.other[1] != null)
|
||||
if (r.other.length > 1 && r.other[1] != null) {
|
||||
anim = new nitroAnimator(r.mdl[0].bmd, r.other[1]);
|
||||
}
|
||||
if (r.other.length > 2 && r.other[2] != null) {
|
||||
res.mdl[0].loadTexPAnim(r.other[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,412 @@
|
|||
//
|
||||
// shell.js
|
||||
//--------------------
|
||||
// Entity type for any item. Specific item types in `/item` folder
|
||||
// Has a default collision handler, but can pass control to the specific item code.
|
||||
// by RHY3756547
|
||||
//
|
||||
// includes: gl-matrix.js (glMatrix 2.0)
|
||||
// /formats/kcl.js
|
||||
//
|
||||
var itemTypes = {
|
||||
//physics, holdable
|
||||
'$koura_g': GreenShellC,
|
||||
'$koura_r': RedShellC,
|
||||
'$banana': BananaC,
|
||||
'$bomb': BombC,
|
||||
'$f_box': FakeBoxC,
|
||||
|
||||
//groups
|
||||
'$koura_group': ShellGroupC,
|
||||
'$banana_group': BananaGroupC,
|
||||
|
||||
//one use items
|
||||
'$kinoko': MushroomC,
|
||||
'$kinoko_group': MushroomGroupC,
|
||||
'$kinoko_p': QueenMushroomC,
|
||||
'$star': StarC,
|
||||
'$thunder': ThunderC,
|
||||
'$gesso': BlooperC,
|
||||
'$teresa': BooC,
|
||||
'$killer': KillerC,
|
||||
'$koura_w': BlueShellC
|
||||
}
|
||||
|
||||
window.Item = function(scene, owner, type, id) {
|
||||
var t = this;
|
||||
var minimumMove = 0.01;
|
||||
|
||||
this.id = id;
|
||||
|
||||
this.pos = vec3.transformMat4([], [0, (-owner.params.colRadius)+1, 16], owner.mat);
|
||||
this.vel = vec3.create();
|
||||
this.gravity = [0, -0.17, 0]; //100% confirmed by me messing around with the gravity value in mkds
|
||||
this.minBounceVel = 0.5;
|
||||
this.airResist = 0.99;
|
||||
this.enablePhysics = true;
|
||||
this.floorBounce = 0.5;
|
||||
this.held = true;
|
||||
this.type = type;
|
||||
this.owner = owner;
|
||||
this.holdTime = 20;
|
||||
this.dead = false;
|
||||
|
||||
this.angle = owner.angle;
|
||||
this.speed = 10;
|
||||
this.yvel = 0;
|
||||
this.xyScale = [1, 1];
|
||||
|
||||
this.colRadius = 4;
|
||||
this.holdDist = 2;
|
||||
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;
|
||||
|
||||
this.groundTime = 0;
|
||||
|
||||
|
||||
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
|
||||
|
||||
//a controller makes this item what it is...
|
||||
// canBeHeld: boolean
|
||||
// canBeDropped: boolean | 'func'
|
||||
// isDestructive: boolean
|
||||
// update?: (scene: CourseScene) => void
|
||||
// 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
|
||||
// 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)
|
||||
// collideKart?: (item: Kart)
|
||||
var subtypeInd = type.indexOf('-');
|
||||
if (subtypeInd == -1) subtypeInd = type.length;
|
||||
this.controller = new itemTypes["$"+type.substr(0, subtypeInd)](this, scene, type.substr(subtypeInd + 1));
|
||||
|
||||
//functions
|
||||
this.update = update;
|
||||
this.draw = draw;
|
||||
|
||||
this.updateHold = updateHold;
|
||||
this.release = release;
|
||||
this.canBeHeld = canBeHeld;
|
||||
this.canBeDropped = canBeDropped;
|
||||
this.isDestructive = isDestructive;
|
||||
this.isSolid = isSolid;
|
||||
this.finalize = finalize;
|
||||
this.collide = collide;
|
||||
|
||||
function updateHold(kart) {
|
||||
//move the object behind the kart (physical direction without drift off)
|
||||
//assuming this will only be called for something that can be held
|
||||
var dir = kart.driftOff / 4;
|
||||
|
||||
//offset the kart's drift offset (on direction)
|
||||
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
|
||||
vec3.transformMat4(pos, pos, kart.mat);
|
||||
|
||||
vec3.sub(t.vel, pos, t.pos); //set the object's velocity to try move it to the hold location. (gravity is disabled)
|
||||
t.enablePhysics = true;
|
||||
}
|
||||
|
||||
function release(forward) {
|
||||
//release the item, either forward or back
|
||||
t.holdTime = 0;
|
||||
if (t.canBeHeld()) {
|
||||
t.updateHold(owner);
|
||||
updateCollision(scene);
|
||||
}
|
||||
t.enablePhysics = true;
|
||||
if (t.controller.release) t.controller.release(forward);
|
||||
else {
|
||||
//default drop and throw. just here for template purposes
|
||||
if (forward > 0) {
|
||||
nitroAudio.playSound(218, {volume: 2}, 0, owner);
|
||||
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 {
|
||||
t.vel = vec3.create();
|
||||
t.safeKart = null;
|
||||
}
|
||||
}
|
||||
t.held = false;
|
||||
}
|
||||
|
||||
function canBeHeld() {
|
||||
return t.controller.canBeHeld || false;
|
||||
}
|
||||
|
||||
function canBeDropped() {
|
||||
if (t.controller.canBeDropped == null) return true;
|
||||
return t.controller.canBeDropped;
|
||||
}
|
||||
|
||||
function isDestructive() {
|
||||
return t.controller.isDestructive || false;
|
||||
}
|
||||
|
||||
function isSolid() {
|
||||
if (t.controller.isSolid == null) return true;
|
||||
return t.controller.isSolid;
|
||||
}
|
||||
|
||||
function finalize() {
|
||||
//kill instantly
|
||||
if (t.controller.onDie) t.controller.onDie(true);
|
||||
t.deadTimer = deadTimerLength;
|
||||
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) {
|
||||
if (t.controller.collide) {
|
||||
t.controller.collide(item);
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.type) {
|
||||
//has a type, definitely an item
|
||||
if (item.isDestructive() || t.isDestructive()) {
|
||||
//mutual destruction. other side will deal with how they handle the collision
|
||||
t.deadTimer++;
|
||||
item.deadTimer++;
|
||||
} else if (item.isSolid() && t.isSolid()) {
|
||||
//bounce off other items that are not destructive
|
||||
//set our velocity to move away (not too intensely)
|
||||
//(only apply if our id is before, to avoid double adding the velocity)
|
||||
if (t.id < item.id) {
|
||||
var diff = vec3.sub(working, t.pos, item.pos);
|
||||
vec3.scale(diff, diff, 0.33);
|
||||
intensityMax(t.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;
|
||||
item.enablePhysics = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//is a kart. usually this is where objects differ
|
||||
if (t.controller.collideKart) {
|
||||
t.controller.collideKart(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function 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 == 1 && t.controller.onDie) t.controller.onDie(false);
|
||||
t.deadTimer++;
|
||||
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;
|
||||
}
|
||||
|
||||
if (t.held) {
|
||||
t.updateHold(owner);
|
||||
}
|
||||
|
||||
var hitSafe = false;
|
||||
//search for player collisions, collisions with other items
|
||||
for (var i=0; i<scene.karts.length; i++) {
|
||||
var ok = scene.karts[i];
|
||||
|
||||
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.
|
||||
//do we need to do something?
|
||||
if (ok === t.safeKart) {
|
||||
hitSafe = true;
|
||||
continue;
|
||||
}
|
||||
t.collide(ok);
|
||||
}
|
||||
}
|
||||
|
||||
if (t.safeKart && !hitSafe && !t.held) {
|
||||
t.safeTime--;
|
||||
if (t.safeTime <= 0) {
|
||||
t.safeKart = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (t.holdTime == 0) { //avoid mutual item destruction on the first frame
|
||||
for (var i=0; i<scene.items.items.length; i++) {
|
||||
var ot = scene.items.items[i];
|
||||
if (ot == t || (t.held && ot.held)) continue;
|
||||
var dist = vec3.dist(t.pos, ot.pos);
|
||||
if (dist < t.colRadius + ot.colRadius && ot.holdTime <= 7 && ot.deadTimer == 0) {
|
||||
//two items are colliding.
|
||||
t.collide(ot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
if (t.holdTime > 7) return;
|
||||
if (t.deadTimer > 0) nitroRender.setColMult([1, 1, 1, 1-(t.deadTimer/deadTimerLength)]); //fade out
|
||||
if (t.controller.draw) {
|
||||
t.controller.draw(mvMatrix, pMatrix);
|
||||
} else {
|
||||
var mat = mat4.translate(mat4.create(), mvMatrix, vec3.add(vec3.create(), t.pos, [0, t.colRadius * t.xyScale[1], 0]));
|
||||
|
||||
spritify(mat);
|
||||
var scale = 6*t.colRadius * (1 - t.holdTime/7);
|
||||
mat4.scale(mat, mat, [scale, scale, scale]);
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
var spritify = function(mat, scale) {
|
||||
var scale = (scale == null)?Math.sqrt(mat[0]*mat[0]+mat[1]*mat[1]+mat[2]*mat[2]):scale;
|
||||
|
||||
mat[0]=scale; mat[1]=0; mat[2]=0;
|
||||
mat[4]=0; mat[5]=scale; mat[6]=0;
|
||||
mat[8]=0; mat[9]=0; mat[10]=scale;
|
||||
}
|
||||
|
||||
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 adjustPos = true;
|
||||
|
||||
if (MKDS_COLTYPE.GROUP_WALL.indexOf(colType) != -1) { //wall
|
||||
//normally, item collision with a wall cause a perfect reflection of the velocity.
|
||||
var proj = vec3.dot(t.vel, n) * 2;
|
||||
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) {
|
||||
//sliding plane
|
||||
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));
|
||||
|
||||
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 {
|
||||
adjustPos = false;
|
||||
ignoreList.push(plane);
|
||||
}
|
||||
|
||||
if (adjustPos) { //move back from plane slightly
|
||||
vec3.add(pos, pos, vec3.scale(vec3.create(), n, minimumMove));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -48,6 +48,7 @@ window.ItemBox = function(obji, scene) {
|
|||
scene.particles.push(new NitroEmitter(scene, ok, 47));
|
||||
t.mode = 1;
|
||||
t.time = 0;
|
||||
ok.items.getItem(null); //todo: specific item from some
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -97,12 +98,15 @@ window.ItemBox = function(obji, scene) {
|
|||
}
|
||||
|
||||
function sndUpdate(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);
|
||||
else t.soundProps.vel = [0, 0, 0];
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
window.BananaC = function(item, scene, type) {
|
||||
var t = this;
|
||||
this.canBeHeld = true;
|
||||
this.canBeDropped = true;
|
||||
this.isDestructive = false;
|
||||
item.floorBounce = 0;
|
||||
|
||||
this.collideKart = collideKart;
|
||||
this.onRest = onRest;
|
||||
this.update = update;
|
||||
|
||||
function collideKart(kart) {
|
||||
item.deadTimer = 1;
|
||||
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) {
|
||||
this.canBeHeld = false;
|
||||
this.canBeDropped = 'func';
|
||||
this.rotationPeriod = 45;
|
||||
|
||||
item.colRadius = -Infinity;
|
||||
item.enablePhysics = false;
|
||||
|
||||
this.draw = draw;
|
||||
|
||||
function draw(mvMatrix, pMatrix) {
|
||||
//the group itself is invisible - the bananas draw individually
|
||||
}
|
||||
}
|
||||
|
||||
window.FakeBoxC = function(item, scene, type) {
|
||||
var t = this;
|
||||
this.canBeHeld = true;
|
||||
this.canBeDropped = true;
|
||||
this.isDestructive = false;
|
||||
this.isSolid = false;
|
||||
item.floorBounce = 0;
|
||||
item.airResist = 0.98;
|
||||
|
||||
this.collideKart = collideKart;
|
||||
this.onRest = onRest;
|
||||
this.update = update;
|
||||
this.draw = draw;
|
||||
|
||||
this.xyScale = [1,1];
|
||||
this.dir = 0;
|
||||
|
||||
function collideKart(kart) {
|
||||
item.deadTimer = 1;
|
||||
nitroAudio.playSound(250, {volume: 2}, 0, item);
|
||||
kart.damage(MKDSCONST.DAMAGE_FLIP);
|
||||
}
|
||||
|
||||
function onRest(normal) {
|
||||
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 = function(item, scene, type) {
|
||||
var t = this;
|
||||
this.canBeHeld = true;
|
||||
this.canBeDropped = true;
|
||||
this.isDestructive = true;
|
||||
|
||||
this.explodeTime = 0;
|
||||
|
||||
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.deadTimer > 0 && t.explodeTime == 0) {
|
||||
//begin explosion
|
||||
t.explodeTime = 1;
|
||||
}
|
||||
if (!item.held && item.colRadius < 6) {
|
||||
item.colRadius += 0.2;
|
||||
if (item.colRadius > 6) item.colRadius = 6;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
//boost, 3x boost, queen boost, star, ghost
|
||||
|
||||
window.MushroomC = null;
|
||||
window.MushroomGroupC = null;
|
||||
window.QueenMushroomC = null;
|
||||
window.StarC = null;
|
||||
window.ThunderC = null;
|
||||
window.BlooperC = null;
|
||||
window.BooC = null;
|
||||
window.KillerC = null;
|
|
@ -0,0 +1,205 @@
|
|||
window.GreenShellC = function(item, scene) {
|
||||
var t = this;
|
||||
this.canBeHeld = true;
|
||||
this.canBeDropped = 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));
|
||||
item.stuckTo = dat.object;
|
||||
} 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) {
|
||||
this.canBeHeld = true;
|
||||
this.canBeDropped = true;
|
||||
this.isDestructive = true;
|
||||
}
|
||||
|
||||
window.ShellGroupC = function(item, scene, type) {
|
||||
var t = this;
|
||||
this.canBeHeld = "func";
|
||||
this.canBeDropped = "func";
|
||||
this.rotationPeriod = 45;
|
||||
item.colRadius = -Infinity;
|
||||
|
||||
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) {
|
||||
//the group itself is invisible - the shells draw individually
|
||||
}
|
||||
}
|
||||
|
||||
window.BlueShellC = null;
|
|
@ -17,7 +17,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
var kartSoundBase = 170;
|
||||
|
||||
var COLBOUNCE_TIME = 20;
|
||||
var COLBOUNCE_STRENGTH = 1;
|
||||
var COLBOUNCE_STRENGTH = 4;
|
||||
|
||||
var params = scene.gameRes.kartPhys.karts[kartN];
|
||||
var offsets = scene.gameRes.kartOff.karts[kartN];
|
||||
|
@ -27,6 +27,9 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
this.active = true;
|
||||
this.preboost = true;
|
||||
|
||||
//supplimentary controllers
|
||||
this.items = new KartItems(this, scene);
|
||||
|
||||
this.soundProps = {};
|
||||
this.pos = pos;
|
||||
this.angle = angle;
|
||||
|
@ -70,6 +73,11 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
this.getPosition = getPosition;
|
||||
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.boostMT = 0;
|
||||
this.boostNorm = 0;
|
||||
|
@ -311,11 +319,12 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
kartAnim = (kartAnim+1)%8;
|
||||
var input = k.controller.fetchInput();
|
||||
k.lastInput = input;
|
||||
k.items.update(input);
|
||||
|
||||
if (input.turn > 0.3) {
|
||||
if (k.driveAnimF < 28) k.driveAnimF++;
|
||||
} else if (input.turn < -0.3) {
|
||||
if (k.driveAnimF > 0) k.driveAnimF--;
|
||||
} else if (input.turn < -0.3) {
|
||||
if (k.driveAnimF < 28) k.driveAnimF++;
|
||||
} else {
|
||||
if (k.driveAnimF > 14) k.driveAnimF--;
|
||||
else if (k.driveAnimF < 14) k.driveAnimF++;
|
||||
|
@ -386,7 +395,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
} else if (k.cannon != null) { //when cannon is active, we fly forward at max move speed until we get to the cannon point.
|
||||
var c = scene.nkm.sections["KTPC"].entries[k.cannon];
|
||||
|
||||
if (c.id2 != 0) {
|
||||
if (c.id1 != -1 && c.id2 != -1) {
|
||||
var c2 = scene.nkm.sections["KTPC"].entries[c.id2];
|
||||
c = c2;
|
||||
|
||||
|
@ -395,16 +404,25 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
mat4.rotateX(mat, mat, c.angle[0]*(-Math.PI/180));
|
||||
|
||||
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.angle = k.physicalDir;
|
||||
k.cannon = null;
|
||||
} else {
|
||||
|
||||
var mat = mat4.create();
|
||||
mat4.rotateY(mat, mat, c.angle[1]*(Math.PI/180));
|
||||
mat4.rotateX(mat, mat, c.angle[0]*(-Math.PI/180));
|
||||
if (true) {
|
||||
//vertical angle from position? airship fortress is impossible otherwise
|
||||
//var c2 = scene.nkm.sections["KTPC"].entries[c.id2];
|
||||
var diff = vec3.sub([], c.pos, k.pos);
|
||||
var dAdj = Math.sqrt(diff[0]*diff[0] + diff[2]*diff[2]);
|
||||
var dHyp = Math.sqrt(diff[0]*diff[0] + diff[1]*diff[1] + diff[2]*diff[2]);
|
||||
mat4.rotateX(mat, mat, ((diff[1] > 0) ? -1 : 1) * Math.acos(dAdj/dHyp));
|
||||
} else {
|
||||
mat4.rotateX(mat, mat, c.angle[0]*(-Math.PI/180));
|
||||
}
|
||||
|
||||
var forward = [0, 0, 1];
|
||||
var up = [0, 1, 0];
|
||||
|
@ -415,17 +433,25 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
k.physicalDir = (180-c.angle[1])*(Math.PI/180);
|
||||
k.angle = k.physicalDir;
|
||||
k.kartTargetNormal = vec3.transformMat4(up, up, mat);
|
||||
k.airTime = 0;
|
||||
|
||||
var planeConst = -vec3.dot(c.pos, forward);
|
||||
var cannonDist = vec3.dot(k.pos, forward) + planeConst;
|
||||
if (cannonDist > 0) k.cannon = null;
|
||||
if (cannonDist > 0) {
|
||||
k.cannon = null; //leaving cannon state
|
||||
k.speed = params.topSpeed;
|
||||
k.vel = vec3.scale([], vec3.transformMat4(forward, forward, mat), k.speed);
|
||||
}
|
||||
}
|
||||
} else { //default kart mode
|
||||
|
||||
if (k.OOB > 0) {
|
||||
playCharacterSound(0);
|
||||
var current = checkpoints[k.checkPointNumber];
|
||||
var respawn = respawns[current.respawn];
|
||||
var respawn;
|
||||
if (current == null)
|
||||
respawn = respawns[(Math.random() * respawns.length) | 0]; //todo: deterministic
|
||||
else
|
||||
respawn = respawns[current.respawn];
|
||||
k.physicalDir = (180-respawn.angle[1])*(Math.PI/180);
|
||||
k.angle = k.physicalDir;
|
||||
k.speed = 0;
|
||||
|
@ -445,179 +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 boosting = (k.boostNorm + k.boostMT)>0;
|
||||
|
||||
if (boosting) {
|
||||
var top2
|
||||
if (k.boostNorm>0){
|
||||
top2 = params.topSpeed*1.3;
|
||||
k.boostNorm--;
|
||||
} 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 (k.specialControlHandler != null) k.specialControlHandler();
|
||||
else {
|
||||
if (boosting) {
|
||||
var top2
|
||||
if (k.boostNorm>0){
|
||||
top2 = params.topSpeed*1.3;
|
||||
k.boostNorm--;
|
||||
} else {
|
||||
top2 = params.topSpeed*((effect.topSpeedMul >= 1)?1.3:effect.topSpeedMul);
|
||||
}
|
||||
|
||||
//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.boostMT>0) {
|
||||
k.boostMT--;
|
||||
}
|
||||
|
||||
if (k.speed <= top2) {
|
||||
k.speed += 1;
|
||||
if (k.speed > top2) k.speed = top2;
|
||||
} else {
|
||||
k.speed *= 0.95;
|
||||
}
|
||||
}
|
||||
|
||||
if (onGround) {
|
||||
if (!k.driftLanded) {
|
||||
if (k.driftMode == 0) {
|
||||
k.drifting = false;
|
||||
clearWheelParticles();
|
||||
//kart controls
|
||||
if (k.drifting) {
|
||||
if ((onGround) && !(input.accel && input.drift && (k.speed > 2 || !k.driftLanded))) {
|
||||
//end drift, execute miniturbo
|
||||
endDrift();
|
||||
if (k.driftPSMode == 3) {
|
||||
k.boostMT = params.miniTurbo;
|
||||
}
|
||||
else {
|
||||
k.driftPSMode = 0;
|
||||
k.driftPSTick = 0;
|
||||
k.driftLanded = true;
|
||||
if (k.drifting) setWheelParticles(20, 1); //20 = smoke, 1 = drift priority
|
||||
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.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 (k.speed <= top) {
|
||||
k.speed += (k.speed/top > params.driftAccelSwitch)?params.driftAccel2:params.driftAccel1;
|
||||
if (k.speed > top) k.speed = top;
|
||||
if (input.accel) {
|
||||
if (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 {
|
||||
k.speed *= 0.95;
|
||||
k.speed *= params.decel;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
sounds.powerslide = nitroAudio.playSound(209, {}, 0, k);
|
||||
sounds.powerslide.gainN.gain.value = 2;
|
||||
} else k.driftPSTick = 0;
|
||||
break;
|
||||
case 3: //turbo charged
|
||||
break;
|
||||
}
|
||||
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 (!k.drifting) {
|
||||
if (onGround) {
|
||||
var effect = params.colParam[groundEffect];
|
||||
if (!boosting) {
|
||||
if (input.accel) {
|
||||
if (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 {
|
||||
k.speed *= params.decel;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -647,19 +670,30 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
k.ylock += ylvel;
|
||||
}
|
||||
|
||||
/*
|
||||
if (k.kartColTimer == COLBOUNCE_TIME) {
|
||||
vec3.add(k.vel, k.vel, k.kartColVel);
|
||||
}
|
||||
*/
|
||||
} else {
|
||||
k.angle += dirDiff(k.physicalDir, k.angle)*effect.handling/2;
|
||||
k.angle = fixDir(k.physicalDir);
|
||||
k.angle += dirDiff(k.physicalDir, k.angle)*effect.handling;
|
||||
k.angle += dirDiff(k.physicalDir, k.angle)*effect.handling; //applying this twice appears to be identical to the original
|
||||
k.angle = fixDir(k.angle);
|
||||
|
||||
//reduce our forward speed by how much of our velocity is not going forwards
|
||||
var factor = Math.sin(k.physicalDir)*Math.sin(k.angle) + Math.cos(k.physicalDir)*Math.cos(k.angle);
|
||||
k.speed *= 1 - ((1-factor) * (1 - k.params.decel));
|
||||
//var reducedSpeed = k.vel[0]*Math.sin(k.angle) + k.vel[2]*(-Math.cos(k.angle));
|
||||
//reducedSpeed = ((reducedSpeed < 0) ? -1 : 1) * Math.sqrt(Math.abs(reducedSpeed));
|
||||
k.vel[1] += k.gravity[1];
|
||||
k.vel = [Math.sin(k.angle)*k.speed, k.vel[1], -Math.cos(k.angle)*k.speed]
|
||||
//k.speed = reducedSpeed;
|
||||
|
||||
/*
|
||||
if (k.kartColTimer > 0) {
|
||||
vec3.add(k.vel, k.vel, vec3.scale([], k.kartColVel, k.kartColTimer/10))
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
if (k.kartColTimer > 0) k.kartColTimer--;
|
||||
|
@ -679,8 +713,6 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
|
||||
//move kart.
|
||||
|
||||
|
||||
|
||||
var steps = 0;
|
||||
var remainingT = 1;
|
||||
var baseVel = k.vel;
|
||||
|
@ -692,6 +724,9 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
}
|
||||
}
|
||||
var velSeg = vec3.clone(baseVel);
|
||||
if (k.kartColTimer > 0) {
|
||||
vec3.add(velSeg, velSeg, vec3.scale([], k.kartColVel, k.kartColTimer/COLBOUNCE_TIME));
|
||||
}
|
||||
var posSeg = vec3.clone(k.pos);
|
||||
var ignoreList = [];
|
||||
while (steps++ < 10 && remainingT > 0.01) {
|
||||
|
@ -725,6 +760,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
var mat = mat4.create();
|
||||
mat4.translate(mat, mat, k.pos);
|
||||
k.mat = mat4.mul(mat, mat, k.basis);
|
||||
if (k.damageMat != null) mat4.mul(mat, mat, k.damageMat);
|
||||
|
||||
if (input.item) {
|
||||
scene.items.addItem(0, scene.karts.indexOf(k), {})
|
||||
|
@ -734,6 +770,101 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
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) {
|
||||
if (k.cannon != null) return;
|
||||
k.cannon = id;
|
||||
var c = scene.nkm.sections["KTPC"].entries[k.cannon];
|
||||
if (c.id1 != -1 && c.id2 != -1) {
|
||||
nitroAudio.playSound(345, {volume: 2.5}, 0, k);
|
||||
} else {
|
||||
nitroAudio.playSound(347, {volume: 2.5}, 0, k);
|
||||
if (k.local) {
|
||||
if (c.id2 == 0) {
|
||||
nitroAudio.playSound(380, {volume: 2}, 0, null); //airship fortress
|
||||
} else {
|
||||
nitroAudio.playSound(456, {volume: 2}, 0, null); //waluigi
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function playCharacterSound(sound, volume) {
|
||||
//0 - hit
|
||||
//1 - hit spin
|
||||
|
@ -822,7 +953,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
}
|
||||
|
||||
function getPosition() {
|
||||
if (futureChecks.length == 0) return 0;
|
||||
if (checkpoints.length == 0 || futureChecks.length == 0) return 0;
|
||||
var check = checkpoints[futureChecks[0]];
|
||||
var dist = vec2.sub([], [check.x1, check.z1], [k.pos[0], k.pos[2]]);
|
||||
var dot = vec2.dot(dist, [check.sinus, check.cosinus]);
|
||||
|
@ -852,7 +983,6 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
if (k != ok) {
|
||||
var dist = vec3.dist(k.pos, ok.pos);
|
||||
if (dist < 16) {
|
||||
|
||||
kartBounce(ok);
|
||||
ok.kartBounce(k);
|
||||
}
|
||||
|
@ -862,13 +992,18 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
}
|
||||
|
||||
function kartBounce(ok) {
|
||||
//play this kart's horn
|
||||
if (k.kartColTimer == 0) { //not if we're still being bounced
|
||||
nitroAudio.playSound(208, { volume: 2 }, 0, k);
|
||||
nitroAudio.playSound(193 + charRes.sndOff/14, { volume: 1.5 }, 0, k);
|
||||
}
|
||||
|
||||
k.kartColTimer = COLBOUNCE_TIME;
|
||||
var weightMul = COLBOUNCE_STRENGTH*(1+(ok.weight-k.weight))*((ok.boostNorm>0 || ok.boostMT>0)?2:1)*((k.boostNorm>0 || k.boostMT>0)?0.5:1);
|
||||
var weightMul = COLBOUNCE_STRENGTH*(1.5+(ok.weight-k.weight))*((ok.boostNorm>0 || ok.boostMT>0)?2:1)*((k.boostNorm>0 || k.boostMT>0)?0.5:1);
|
||||
|
||||
//as well as side bounce also add velocity difference if other vel > mine.
|
||||
|
||||
vec3.sub(k.kartColVel, k.pos, ok.pos);
|
||||
k.kartColVel[1] = 0;
|
||||
vec3.normalize(k.kartColVel, k.kartColVel);
|
||||
vec3.scale(k.kartColVel, k.kartColVel, weightMul);
|
||||
|
||||
|
@ -891,8 +1026,9 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
}
|
||||
|
||||
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 transpose = (mode == 0)?0:(22*turn*k.speed/params.topSpeed);
|
||||
var transpose = (mode == 0)?0:(22*turn*Math.min(1.3, k.speed/params.topSpeed));
|
||||
|
||||
sounds.transpose += (transpose-sounds.transpose)/15;
|
||||
if (mode != soundMode) {
|
||||
|
@ -909,7 +1045,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
//order y, x, z
|
||||
var dir = k.physicalDir+k.driftOff+(Math.sin((COLBOUNCE_TIME-k.kartColTimer)/3)*(Math.PI/6)*(k.kartColTimer/COLBOUNCE_TIME));
|
||||
var forward = [Math.sin(dir), 0, -Math.cos(dir)];
|
||||
var side = [Math.cos(dir), 0, Math.sin(dir)];
|
||||
var side = [-Math.cos(dir), 0, -Math.sin(dir)];
|
||||
if (k.physBasis != null) {
|
||||
vec3.transformMat4(forward, forward, k.physBasis.mat);
|
||||
vec3.transformMat4(side, side, k.physBasis.mat);
|
||||
|
@ -961,6 +1097,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
k.physBasis = {
|
||||
mat: m4,
|
||||
inv: mat4.invert([], m4),
|
||||
normal: normal,
|
||||
time: 15,
|
||||
loop: false
|
||||
};
|
||||
|
@ -978,12 +1115,15 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
}
|
||||
|
||||
function sndUpdate(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);
|
||||
else k.soundProps.vel = [0, 0, 0];
|
||||
*/
|
||||
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;
|
||||
|
||||
var calcVol = (k.soundProps.refDistance / (k.soundProps.refDistance + k.soundProps.rolloffFactor * (Math.sqrt(vec3.dot(k.soundProps.pos, k.soundProps.pos)) - k.soundProps.refDistance)));
|
||||
|
@ -1030,7 +1170,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 adjustPos = true;
|
||||
|
||||
if (colType == MKDS_COLTYPE.OOB || colType == MKDS_COLTYPE.FALL) {
|
||||
if (MKDS_COLTYPE.GROUP_OOB.indexOf(colType) != -1) {
|
||||
k.OOB = 1;
|
||||
}
|
||||
|
||||
|
@ -1051,12 +1191,18 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
}
|
||||
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
|
||||
|
||||
var v = k.vel;
|
||||
k.speed = Math.sqrt(v[0]*v[0]+v[2]*v[2]);
|
||||
k.angle = Math.atan2(v[0], -v[2]);
|
||||
stuckTo = dat.object;
|
||||
} else if (MKDS_COLTYPE.GROUP_ROAD.indexOf(colType) != -1) {
|
||||
//sliding plane
|
||||
if (MKDS_COLTYPE.GROUP_BOOST.indexOf(colType) != -1) {
|
||||
|
@ -1067,7 +1213,8 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
|
||||
if (k.vel[1] > 0) k.vel[1] = 0;
|
||||
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));
|
||||
|
||||
if (stick) {
|
||||
|
@ -1095,7 +1242,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
stuckTo = dat.object;
|
||||
} else if (colType == MKDS_COLTYPE.CANNON) {
|
||||
//cannon!!
|
||||
k.cannon = colBE;
|
||||
triggerCannon(colBE);
|
||||
} else {
|
||||
adjustPos = false;
|
||||
ignoreList.push(plane);
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
//item state for a kart. not an entity, just supplemental to one.
|
||||
|
||||
window.KartItems = function(kart, scene) {
|
||||
var t = this;
|
||||
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.specificItem = null;
|
||||
t.empty = true;
|
||||
t.cycleTime = 0;
|
||||
t.totalTime = 230;
|
||||
|
||||
var maxItemTime = 230;
|
||||
var minItemTime = 80;
|
||||
var carouselSfx = null;
|
||||
var lastItemState = false;
|
||||
var holdAppearDelay = 15;
|
||||
|
||||
var hurtExplodeDelay = 105; //turn right slightly, huge double backflip, small bounces.
|
||||
var hurtFlipDelay = 80; //turn right slightly, bounce twice, forward flip
|
||||
var hurtSpinDelay = 40; //counter clockwise spin
|
||||
|
||||
t.getItem = getItem;
|
||||
t.update = update;
|
||||
|
||||
var specialItems = ["star"];
|
||||
|
||||
function sfx(id) {
|
||||
if (kart.local) {
|
||||
return nitroAudio.playSound(id, {volume: 2}, 0, null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getItem(specific) {
|
||||
if (!t.empty) return false;
|
||||
else {
|
||||
//begin carousel
|
||||
t.cycleTime = 0;
|
||||
t.totalTime = (specific) ? 60 : maxItemTime;
|
||||
if (specific) t.specificItem = specific;
|
||||
t.empty = false;
|
||||
|
||||
carouselSfx = sfx(62);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
var pressed = (input.item && !lastItemState);
|
||||
var released = (lastItemState && !input.item);
|
||||
if (!t.empty) {
|
||||
if (t.currentItem == null) {
|
||||
//carousel
|
||||
t.cycleTime++;
|
||||
if (t.cycleTime >= t.totalTime) {
|
||||
if (carouselSfx != null) nitroAudio.kill(carouselSfx);
|
||||
|
||||
//decide on an item
|
||||
var item = "banana"; //koura_g, banana, f_box, koura_group, koura_group-bomb-7
|
||||
sfx((specialItems.indexOf(item) == -1) ? 63 : 64);
|
||||
t.currentItem = item;
|
||||
} else {
|
||||
//if item button is pressed, we speed up the carousel
|
||||
if (pressed && t.heldItem == null) {
|
||||
t.totalTime = Math.max(minItemTime, t.totalTime - 20);
|
||||
}
|
||||
}
|
||||
} else if (t.heldItem == null) {
|
||||
if (pressed) {
|
||||
//fire?
|
||||
t.heldItem = createItem();
|
||||
//t.currentItem = null;
|
||||
//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;
|
||||
}
|
||||
}
|
|
@ -75,7 +75,7 @@ window.objDatabase = new (function(){
|
|||
t[0x0195] = ObjBus;
|
||||
|
||||
|
||||
t[0x00CC] = ObjDecor; //DEBUG: pianta bridge
|
||||
t[0x00CC] = ObjBridge; //DEBUG: pianta bridge
|
||||
t[0x000D] = ObjDecor; //DEBUG: puddle
|
||||
|
||||
t[0x0158] = ObjDecor; //DEBUG: airship (routed)
|
||||
|
|
|
@ -41,11 +41,15 @@ window.ObjGear = function(obji, scene) {
|
|||
t.angle = 0;
|
||||
t.dir = (t.wB1 == 0)
|
||||
|
||||
t.colFrame = 0;
|
||||
|
||||
var colRes;
|
||||
|
||||
var dirVel = 0;
|
||||
|
||||
var prevMat;
|
||||
var curMat;
|
||||
setMat();
|
||||
var colMat = mat4.create();
|
||||
prevMat = curMat;
|
||||
|
||||
function setMat() {
|
||||
|
@ -59,6 +63,8 @@ window.ObjGear = function(obji, scene) {
|
|||
mat4.rotateX(mat, mat, obji.angle[0]*(Math.PI/180));
|
||||
|
||||
mat4.rotateY(mat, mat, t.angle);
|
||||
mat4.scale(colMat, mat, [colRes.scale, colRes.scale, colRes.scale]);
|
||||
t.colFrame++;
|
||||
curMat = mat;
|
||||
}
|
||||
|
||||
|
@ -119,43 +125,24 @@ window.ObjGear = function(obji, scene) {
|
|||
}
|
||||
}
|
||||
|
||||
function cloneKCL(kcl) {
|
||||
return JSON.parse(JSON.stringify(kcl));
|
||||
}
|
||||
|
||||
function provideRes(r) {
|
||||
res = r; //...and gives them to us. :)
|
||||
colRes = cloneKCL(res.mdl[0].getCollisionModel(0, 0));
|
||||
}
|
||||
|
||||
function getCollision() {
|
||||
var obj = {};
|
||||
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;
|
||||
return { tris: colRes.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)
|
||||
|
||||
/*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 ignoreList = [];
|
||||
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) {
|
||||
colResponse(posSeg, velSeg, result, ignoreList)
|
||||
remainingT -= result.t;
|
||||
|
@ -87,7 +87,9 @@ window.GreenShell = function(scene, owner, time, itemID, cliID, params) {
|
|||
|
||||
var v = t.vel;
|
||||
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) {
|
||||
//sliding plane
|
||||
var proj = vec3.dot(t.vel, n);
|
||||
|
|
|
@ -44,16 +44,20 @@ window.ObjSoundMaker = function(obji, scene) {
|
|||
}
|
||||
|
||||
function sndUpdate(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 = vec3.transformMat4([], t.pos, view);
|
||||
//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);
|
||||
//else t.soundProps.vel = [0, 0, 0];
|
||||
//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;
|
||||
|
||||
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 (sound != null) {
|
||||
|
|
|
@ -17,6 +17,7 @@ window.ObjTruck = function(obji, scene) {
|
|||
t.pos = vec3.clone(obji.pos);
|
||||
//t.angle = vec3.clone(obji.angle);
|
||||
t.scale = vec3.clone(obji.scale);
|
||||
t.vel = vec3.create();
|
||||
|
||||
t.requireRes = requireRes;
|
||||
t.provideRes = provideRes;
|
||||
|
@ -24,45 +25,138 @@ window.ObjTruck = function(obji, scene) {
|
|||
t.draw = draw;
|
||||
|
||||
t.route = scene.paths[obji.routeID];
|
||||
t.routeSpeed = (obji.setting1>>16)/100;
|
||||
t.routePos = (obji.setting1&0xFFFF)%t.route.length;
|
||||
t.routeSpeed = 0.01;
|
||||
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.prevPos = t.pos;
|
||||
t.elapsedTime = 0;
|
||||
|
||||
var facingNormal = [0, 1, 0];
|
||||
var curNormal = [0, 1, 0];
|
||||
var curFloorNormal = [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) {
|
||||
//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.pos = vec3.lerp([], t.prevPos, t.nextNode.pos, t.elapsedTime/t.nextNode.duration);
|
||||
if (t.elapsedTime >= t.nextNode.duration) {
|
||||
t.elapsedTime = 0;
|
||||
t.prevPos = t.nextNode.pos;
|
||||
var i = t.elapsedTime;
|
||||
|
||||
var newPos = cubic3D(t.nodes, i); //vec3.lerp([], t.prevPos, t.nextNode.pos, i);
|
||||
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.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)
|
||||
vec3.normalize(facingNormal, facingNormal);
|
||||
|
||||
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;
|
||||
curNormal = vec3.sub([], t.prevPos, t.pos)
|
||||
t.prevPos = vec3.clone(t.pos);
|
||||
vec3.normalize(curNormal, curNormal);
|
||||
if (isNaN(curNormal[0])) curNormal = [0, 0, 1];
|
||||
|
||||
var spos = vec3.clone(t.pos);
|
||||
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) {
|
||||
floorNormal = result.normal;
|
||||
} else {
|
||||
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) {
|
||||
|
@ -70,23 +164,44 @@ window.ObjTruck = function(obji, scene) {
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
function requireRes() { //scene asks what resources to load
|
||||
switch (obji.ID) {
|
||||
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:
|
||||
return {mdl:[{nsbmd:"truck_a.nsbmd"}]}; //one model, truck
|
||||
return {mdl:[{nsbmd:"truck_a.nsbmd"}], other:["truck_a.nsbtp"]}; //one model, truck
|
||||
case 0x0195:
|
||||
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) {
|
||||
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,11 @@ window.ObjWater = function(obji, scene) {
|
|||
t.update = update;
|
||||
t.draw = draw;
|
||||
var frame = 0;
|
||||
var wheight = 6.144;
|
||||
var wosc = 12.288;
|
||||
var wstay = 5*60;
|
||||
var wchange = 4*60;
|
||||
var useAlpha = true; //probably a crutch - this should be defined in the water material (though it might be in nsbma)
|
||||
|
||||
function draw(view, pMatrix) {
|
||||
if (nitroRender.flagShadow) return;
|
||||
|
@ -34,34 +39,35 @@ window.ObjWater = function(obji, scene) {
|
|||
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)
|
||||
|
||||
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])
|
||||
nitroRender.setAlpha(0x0A/31);
|
||||
mat4.translate(waterM, view, [Math.sin(frame/180)*96, height, Math.cos(frame/146)*96])
|
||||
if (useAlpha) nitroRender.setColMult([1, 1, 1, 0x0A/31]);
|
||||
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.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
|
||||
|
||||
if (!obji.ID == 9) {
|
||||
if (obji.ID != 9) {
|
||||
mat4.translate(waterM, view, [0, height, 0])
|
||||
nitroRender.setAlpha(0x10/31);
|
||||
if (useAlpha) 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
|
||||
}
|
||||
|
||||
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])
|
||||
if (useAlpha) 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() {
|
||||
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
|
||||
|
@ -69,12 +75,17 @@ window.ObjWater = function(obji, scene) {
|
|||
case 0x0001:
|
||||
return {mdl:[{nsbmd:"beach_waterC.nsbmd"}, {nsbmd:"beach_waterA.nsbmd"}]};
|
||||
case 0x0003:
|
||||
useAlpha = false;
|
||||
return {mdl:[{nsbmd:"town_waterC.nsbmd"}, {nsbmd:"town_waterA.nsbmd"}]};
|
||||
case 0x0006:
|
||||
useAlpha = false;
|
||||
return {mdl:[{nsbmd:"yoshi_waterC.nsbmd"}]};
|
||||
case 0x0009:
|
||||
useAlpha = false;
|
||||
return {mdl:[{nsbmd:"hyudoro_waterC.nsbmd"}, {nsbmd:"hyudoro_waterA.nsbmd"}]};
|
||||
case 0x000C:
|
||||
wheight = 38;
|
||||
wosc = 16;
|
||||
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();
|
||||
pos[0] = view.getInt32(off, true)/4096;
|
||||
pos[1] = view.getInt32(off+4, true)/4096;
|
||||
console.log("charPos: "+pos[1]);
|
||||
pos[2] = view.getInt32(off+8, true)/4096;
|
||||
off += 12;
|
||||
chars.push(pos);
|
||||
|
|
|
@ -84,7 +84,7 @@ window.kcl = function(input, mkwii) {
|
|||
while (offset < octreeOffset) {
|
||||
planes.push(new Plane(view, offset));
|
||||
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] > maxx) maxx=vert[0];
|
||||
if (vert[2] < minz) minz=vert[2];
|
||||
|
@ -177,9 +177,9 @@ window.kcl = function(input, mkwii) {
|
|||
var plane = planes[i];
|
||||
ctx.beginPath();
|
||||
|
||||
ctx.moveTo(plane.Vertex1[0], plane.Vertex1[2]);
|
||||
ctx.lineTo(plane.Vertex2[0], plane.Vertex2[2]);
|
||||
ctx.lineTo(plane.Vertex3[0], plane.Vertex3[2]);
|
||||
ctx.moveTo(plane.Vertices[0][0], plane.Vertices[0][2]);
|
||||
ctx.lineTo(plane.Vertices[1][0], plane.Vertices[1][2]);
|
||||
ctx.lineTo(plane.Vertices[2][0], plane.Vertices[2][2]);
|
||||
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
|
@ -253,7 +253,8 @@ window.kcl = function(input, mkwii) {
|
|||
|
||||
function Plane(view, offset) {
|
||||
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.NormalA = readNormal(view.getUint16(offset+0x8, 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 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.Vertex3 = vec3.scaleAndAdd(vec3.create(), this.Vertex1, crossA, (this.Len/vec3.dot(crossA, this.NormalC)));
|
||||
this.Vertices[1] = vec3.scaleAndAdd(vec3.create(), this.Vertices[0], crossB, (this.Len/vec3.dot(crossB, this.NormalC)));
|
||||
this.Vertices[2] = vec3.scaleAndAdd(vec3.create(), this.Vertices[0], crossA, (this.Len/vec3.dot(crossA, this.NormalC)));
|
||||
}
|
||||
|
||||
function readVert(num, view) {
|
||||
|
|
|
@ -5,10 +5,34 @@
|
|||
// 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) {
|
||||
|
||||
this.load = load;
|
||||
this.getFile = getFile;
|
||||
this.list = list;
|
||||
|
||||
var arc = this;
|
||||
var handlers = [];
|
||||
|
@ -18,8 +42,6 @@ window.narc = function(input) {
|
|||
mouseY = evt.pageY;
|
||||
}
|
||||
|
||||
this.scopeEval = function(code) {return eval(code)} //for debug purposes
|
||||
|
||||
function load(buffer) {
|
||||
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!
|
||||
}
|
||||
|
||||
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) {
|
||||
var table = arc.sections["BTAF"].files;
|
||||
var file = table[id];
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
window.ndsFS = function(input) {
|
||||
this.load = load;
|
||||
this.getFile = getFile;
|
||||
this.list = list;
|
||||
|
||||
var arc = this;
|
||||
var handlers = [];
|
||||
|
@ -65,6 +66,22 @@ window.ndsFS = function(input) {
|
|||
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) {
|
||||
var off = arc.fileOff+id*8;
|
||||
/*var table = arc.sections["BTAF"].files;
|
||||
|
|
|
@ -11,13 +11,17 @@
|
|||
window.nftr = function(input) {
|
||||
|
||||
var mainOff;
|
||||
var mainObj = this;
|
||||
var t = this;
|
||||
this.info = {};
|
||||
|
||||
if (input != null) {
|
||||
load(input);
|
||||
}
|
||||
this.load = load;
|
||||
|
||||
this.drawToCanvas = drawToCanvas;
|
||||
this.measureText = measureText;
|
||||
this.measureMapped = measureMapped;
|
||||
this.mapText = mapText;
|
||||
|
||||
function load(input) {
|
||||
var view = new DataView(input);
|
||||
|
@ -29,9 +33,249 @@ window.nftr = function(input) {
|
|||
header = nitro.readHeader(view);
|
||||
//debugger;
|
||||
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
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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++) {
|
||||
name += readChar(view, offset++)
|
||||
}
|
||||
objectData[i].name = name;
|
||||
names.push(name);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ window.nsbca = function(input) {
|
|||
|
||||
var mainOff;
|
||||
var animData;
|
||||
var speeds = [1.0, 0.5, 1/3];
|
||||
var speeds = [1.0, 0.5, 1/4];
|
||||
var mainObj = this;
|
||||
|
||||
if (input != null) {
|
||||
|
@ -72,10 +72,10 @@ window.nsbca = function(input) {
|
|||
|
||||
function readTrans(view, off, obj) {
|
||||
var flag = view.getUint16(off, true); //--zyx-Sr-RZYX-T-
|
||||
|
||||
off += 4;
|
||||
|
||||
var transform = {};
|
||||
transform.flag = flag;
|
||||
|
||||
if (!((flag>>1) & 1)) { //T: translation
|
||||
var translate = [[], [], []]; //store translations in x,y,z arrays
|
||||
|
@ -96,7 +96,7 @@ window.nsbca = function(input) {
|
|||
inf.off = view.getUint32(off+4, true);
|
||||
|
||||
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 off2 = obj.baseOff+inf.off;
|
||||
|
@ -132,7 +132,7 @@ window.nsbca = function(input) {
|
|||
inf.off = view.getUint32(off+4, true);
|
||||
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.
|
||||
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;
|
||||
try {
|
||||
|
@ -173,7 +173,7 @@ window.nsbca = function(input) {
|
|||
inf.off = view.getUint32(off+4, true);
|
||||
|
||||
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 off2 = obj.baseOff+inf.off;
|
||||
|
@ -211,21 +211,26 @@ window.nsbca = function(input) {
|
|||
};
|
||||
} else {
|
||||
var off2 = obj.baseOff+obj.off2+ind*10; //jump to rotation data
|
||||
var d1 = view.getInt16(off2, true);
|
||||
var d2 = view.getInt16(off2+2, true);
|
||||
var d3 = view.getInt16(off2+4, true);
|
||||
var d4 = view.getInt16(off2+6, true);
|
||||
var d5 = view.getInt16(off2+8, true);
|
||||
var d1 = view.getUint16(off2, true);
|
||||
var d2 = view.getUint16(off2+2, true);
|
||||
var d3 = view.getUint16(off2+4, true);
|
||||
var d4 = view.getUint16(off2+6, true);
|
||||
var d5 = view.getUint16(off2+8, true);
|
||||
|
||||
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 v2 = [d4>>3, d5>>3, i6]
|
||||
var v1 = [d1>>3, d2>>3, d3>>3];
|
||||
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(v2, v2, 1/4096);
|
||||
var v3 = vec3.cross([], v1, v2)
|
||||
var v3 = vec3.cross([], v1, v2);
|
||||
|
||||
var mat = [
|
||||
v1[0], v1[1], v1[2],
|
||||
|
|
|
@ -70,6 +70,7 @@ window.nsbmd = function(input) {
|
|||
head.maxStack = view.getUint8(offset+0x1A);
|
||||
|
||||
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.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 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);
|
||||
|
||||
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) {
|
||||
var last;
|
||||
var commands = [];
|
||||
var debug = true;
|
||||
if (debug) console.log("== Begin Parse Bones ==");
|
||||
|
||||
var freeStack = maxStack;
|
||||
var forceID=null;
|
||||
var lastMat = null;
|
||||
var bound = [];
|
||||
|
||||
var matMap = [];
|
||||
|
||||
while (offset<texPalOff) { //bones
|
||||
last = view.getUint8(offset++);
|
||||
console.log("bone cmd: 0x"+last.toString(16));
|
||||
switch (last) {
|
||||
case 0x06: //bind object transforms to parent. bone exists but is not placed in the stack
|
||||
var obj = view.getUint8(offset++);
|
||||
|
@ -117,21 +128,50 @@ window.nsbmd = function(input) {
|
|||
|
||||
var object = objects.objectData[obj];
|
||||
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++});
|
||||
break;
|
||||
case 0x26:
|
||||
case 0x46: //placed in the stack at stack id
|
||||
case 0x66:
|
||||
var obj = view.getUint8(offset++);
|
||||
var parent = view.getUint8(offset++);
|
||||
var zero = 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];
|
||||
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;
|
||||
/*
|
||||
case 0x66: //has ability to restore to another stack id. no idea how this works
|
||||
var obj = view.getUint8(offset++);
|
||||
var parent = view.getUint8(offset++);
|
||||
|
@ -142,8 +182,11 @@ window.nsbmd = function(input) {
|
|||
var object = objects.objectData[obj];
|
||||
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});
|
||||
break;
|
||||
*/
|
||||
case 0x04:
|
||||
case 0x24:
|
||||
case 0x44: //bind material to polygon: matID, 5, polyID
|
||||
|
@ -151,54 +194,69 @@ window.nsbmd = function(input) {
|
|||
lastMat = mat;
|
||||
var five = 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;
|
||||
|
||||
if (debug) console.log("[0x"+last.toString(16)+"] Bind material " + mat + " to poly " + poly + " (with stack id " + polys.objectData[poly].stackID + ")");
|
||||
break;
|
||||
case 1:
|
||||
//end of all
|
||||
if (debug) console.log("[0x"+last.toString(16)+"] END OF BONES");
|
||||
break;
|
||||
case 2: //node visibility (maybe to implement this set matrix to 0)
|
||||
|
||||
var node = view.getUint8(offset++);
|
||||
var vis = view.getUint8(offset++);
|
||||
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;
|
||||
break;
|
||||
case 3: //stack id for poly (wit)
|
||||
forceID = view.getUint8(offset++);
|
||||
console.log("stackid is "+forceID);
|
||||
if (debug) console.log("[0x"+last.toString(16)+"] Force stack id to " + forceID);
|
||||
case 0:
|
||||
break;
|
||||
case 5:
|
||||
//i don't... what??
|
||||
//holy shp!
|
||||
case 5: //"draw a mesh" supposedly. might require getting a snapshot of the matrices at this point
|
||||
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;
|
||||
|
||||
if (debug) console.log("[0x"+last.toString(16)+"] Draw " + poly + "(stack id " + polys.objectData[poly].stackID + ")");
|
||||
break;
|
||||
case 7:
|
||||
//sets object to be billboard
|
||||
var obj = view.getUint8(offset++);
|
||||
objects.objectData[obj].billboardMode = 1;
|
||||
if (debug) console.log("[0x"+last.toString(16)+"] Object " + obj + " set to full billboard mode.");
|
||||
mainObj.hasBillboards = true;
|
||||
break;
|
||||
case 8:
|
||||
//sets object to be billboard around only y axis. (xz remain unchanged)
|
||||
var obj = view.getUint8(offset++);
|
||||
objects.objectData[obj].billboardMode = 2;
|
||||
if (debug) console.log("[0x"+last.toString(16)+"] Object " + obj + " set to Y billboard mode.");
|
||||
mainObj.hasBillboards = true;
|
||||
break;
|
||||
case 9: //skinning equ. not used?
|
||||
if (debug) console.log("[0x"+last.toString(16)+"] Skinning Equation (UNIMPLEMENTED)");
|
||||
debugger;
|
||||
break;
|
||||
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:
|
||||
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:
|
||||
console.log("bone transform unknown: "+last);
|
||||
console.log("bone transform unknown: 0x"+last.toString(16));
|
||||
break;
|
||||
}
|
||||
}
|
||||
//if (window.throwWhatever) debugger;
|
||||
|
||||
if (debug) console.log("== End Parse Bones ==");
|
||||
return commands;
|
||||
}
|
||||
|
||||
|
@ -213,7 +271,6 @@ window.nsbmd = function(input) {
|
|||
}*/
|
||||
|
||||
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.
|
||||
|
||||
|
|
|
@ -120,6 +120,7 @@ window.nsbta = function(input) {
|
|||
if (i == 2) obj[prop[i]].push(data2);
|
||||
} else { //data is found at offset
|
||||
frames = frames>>obj.frameStep[prop[i]];
|
||||
if (obj.frameStep[prop[i]] > 0) frames++; //one extra frame, for interpolation
|
||||
//frames -= 1;
|
||||
var off = base + value-8;
|
||||
for (var j=0; j<frames*((i==2)?2:1); j++) {
|
||||
|
|
|
@ -36,6 +36,13 @@ window.nsbtp = function(input) {
|
|||
}
|
||||
this.load = load;
|
||||
|
||||
var texTotal;
|
||||
var palTotal;
|
||||
var texNamesOff;
|
||||
var palNamesOff;
|
||||
|
||||
var texNames;
|
||||
var palNames;
|
||||
|
||||
function load(input) {
|
||||
var view = new DataView(input);
|
||||
|
@ -56,7 +63,6 @@ window.nsbtp = function(input) {
|
|||
if (stamp != "PAT0") throw "NSBTP invalid. Expected PAT0, found "+stamp;
|
||||
|
||||
animData = nitro.read3dInfo(view, mainOff+8, animInfoHandler);
|
||||
debugger;
|
||||
mainObj.animData = animData;
|
||||
}
|
||||
|
||||
|
@ -92,15 +98,37 @@ window.nsbtp = function(input) {
|
|||
//8 bytes here? looks like texinfo
|
||||
|
||||
var duration = view.getUint16(offset, true);
|
||||
var tframes = view.getUint8(offset+2);
|
||||
var pframes = view.getUint8(offset+3);
|
||||
var unknown = view.getUint16(offset+4, true);
|
||||
var unknown2 = view.getUint16(offset+6, true);
|
||||
texTotal = view.getUint8(offset+2);
|
||||
palTotal = view.getUint8(offset+3);
|
||||
texNamesOff = view.getUint16(offset+4, 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
|
||||
var data = nitro.read3dInfo(view, offset+8, matInfoHandler);
|
||||
|
||||
return {data: data, nextoff: data.nextoff, tframes:tframes, pframes:pframes, 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) {
|
||||
|
@ -122,9 +150,9 @@ window.nsbtp = function(input) {
|
|||
// 16char texname
|
||||
//then (frame of these)
|
||||
// 16char palname
|
||||
// texture animations are bound to the material via the name of this block.
|
||||
|
||||
var frames = view.getUint16(offset, true);
|
||||
obj.matinfo = view.getUint16(offset+2, true);
|
||||
var frames = view.getUint32(offset, true);
|
||||
obj.flags = view.getUint16(offset+4, true);
|
||||
var offset2 = view.getUint16(offset+6, true);
|
||||
offset += 8;
|
||||
|
@ -133,22 +161,16 @@ window.nsbtp = function(input) {
|
|||
offset = matOff + offset2;
|
||||
//info and timing for each frame
|
||||
for (var i=0; i<frames; i++) {
|
||||
obj.frames[i] = {
|
||||
var entry = {
|
||||
time: view.getUint16(offset, true),
|
||||
tex: view.getUint8(offset+2),
|
||||
mat: view.getUint8(offset+3),
|
||||
tex: view.getUint8(offset+2), //index into names?
|
||||
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;
|
||||
}
|
||||
|
||||
//read 16char tex names
|
||||
for (var i=0; i<frames; i++) {
|
||||
|
||||
}
|
||||
//read 16char pal names
|
||||
for (var i=0; i<frames; i++) {
|
||||
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
|
|
@ -74,10 +74,21 @@ window.nsbtx = function(input, tex0) {
|
|||
paletteInfo = nitro.read3dInfo(view, mainOff + palInfoOff, palInfoHandler);
|
||||
textureInfo = nitro.read3dInfo(view, mainOff + texInfoOff, texInfoHandler);
|
||||
|
||||
buildNameToIndex(paletteInfo);
|
||||
buildNameToIndex(textureInfo);
|
||||
|
||||
thisObj.paletteInfo = paletteInfo;
|
||||
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) {
|
||||
var tex = textureInfo.objectData[textureId];
|
||||
var pal = paletteInfo.objectData[palId];
|
||||
|
@ -106,6 +117,7 @@ window.nsbtx = function(input, tex0) {
|
|||
var dat = texView.getUint8(off++)
|
||||
col = readPalColour(palView, palOff, dat&31, trans);
|
||||
col[3] = (dat>>5)*(255/7);
|
||||
premultiply(col);
|
||||
|
||||
} else if (format == 2) { //2 bit pal
|
||||
if (i%4 == 0) databuf = texView.getUint8(off++);
|
||||
|
@ -126,6 +138,7 @@ window.nsbtx = function(input, tex0) {
|
|||
var dat = texView.getUint8(off++)
|
||||
col = readPalColour(palView, palOff, dat&7, trans);
|
||||
col[3] = (dat>>3)*(255/31);
|
||||
premultiply(col);
|
||||
|
||||
} else if (format == 7) { //raw color data
|
||||
col = texView.getUint16(off, true);
|
||||
|
@ -134,6 +147,7 @@ window.nsbtx = function(input, tex0) {
|
|||
colourBuffer[2] = Math.round((((col>>10)&31)/31)*255)
|
||||
colourBuffer[3] = Math.round((col>>15)*255);
|
||||
col = colourBuffer;
|
||||
premultiply(col);
|
||||
off += 2;
|
||||
|
||||
} else {
|
||||
|
@ -146,6 +160,12 @@ window.nsbtx = function(input, tex0) {
|
|||
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.
|
||||
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)
|
||||
|
|
|
@ -406,6 +406,7 @@ window.spa = function(input) {
|
|||
var dat = texView.getUint8(off++)
|
||||
col = readPalColour(palView, palOff, dat&31, trans);
|
||||
col[3] = (dat>>5)*(255/7);
|
||||
premultiply(col);
|
||||
|
||||
} else if (format == 2) { //2 bit pal
|
||||
if (i%4 == 0) databuf = texView.getUint8(off++);
|
||||
|
@ -426,6 +427,7 @@ window.spa = function(input) {
|
|||
var dat = texView.getUint8(off++)
|
||||
col = readPalColour(palView, palOff, dat&7, trans);
|
||||
col[3] = (dat>>3)*(255/31);
|
||||
premultiply(col);
|
||||
|
||||
} else if (format == 7) { //raw color data
|
||||
col = texView.getUint16(off, true);
|
||||
|
@ -434,6 +436,7 @@ window.spa = function(input) {
|
|||
colourBuffer[2] = Math.round((((col>>10)&31)/31)*255)
|
||||
colourBuffer[3] = Math.round((col>>15)*255);
|
||||
col = colourBuffer;
|
||||
premultiply(col);
|
||||
off += 2;
|
||||
|
||||
} else {
|
||||
|
@ -446,6 +449,12 @@ window.spa = function(input) {
|
|||
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.
|
||||
throw "compressed tex not supported for particles! (unknowns for tex data offsets and lengths?)";
|
||||
var off = 0;//tex.texOffset;
|
||||
|
|
|
@ -199,6 +199,7 @@ window.NitroParticle = function(scene, emitter, pos, vel, dir, dirVel, duration,
|
|||
nitroRender.last.obj = obj;
|
||||
}
|
||||
|
||||
gl.disable(gl.CULL_FACE);
|
||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
nitroRender.setColMult([1, 1, 1, 1]);
|
||||
|
|
|
@ -42,10 +42,25 @@ window.nitroAnimator = function(bmd, bca) {
|
|||
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) {
|
||||
|
||||
var b = bca.animData.objectData[anim];
|
||||
|
||||
var totalLength = getLength(anim);
|
||||
frame %= getLength(anim);
|
||||
|
||||
var fLow = Math.floor(frame);
|
||||
var fHigh = Math.ceil(frame);
|
||||
var iterp = frame%1;
|
||||
|
@ -64,27 +79,21 @@ window.nitroAnimator = function(bmd, bca) {
|
|||
if (a.translate != null) {
|
||||
translate = [];
|
||||
if (a.tlExtra[0] != null) {
|
||||
var f = frame * a.tlExtra[0].speed;
|
||||
var fLow = Math.floor(f)%a.translate[0].length;
|
||||
var fHigh = Math.ceil(f)%a.translate[0].length;
|
||||
var p = f%1;
|
||||
translate[0] = a.translate[0][fHigh]*(p) + a.translate[0][fLow]*(1-p);
|
||||
var f = getFrames(a.tlExtra[0], a.translate[0].length, frame, totalLength);
|
||||
var p = f[2];
|
||||
translate[0] = a.translate[0][f[0]]*(1-p) + a.translate[0][f[1]]*(p);
|
||||
} else translate[0] = a.translate[0][0];
|
||||
|
||||
if (a.tlExtra[1] != null) {
|
||||
var f = frame * a.tlExtra[1].speed;
|
||||
var fLow = Math.floor(f)%a.translate[1].length;
|
||||
var fHigh = Math.ceil(f)%a.translate[1].length;
|
||||
var p = f%1;
|
||||
translate[1] = a.translate[1][fHigh]*(p) + a.translate[1][fLow]*(1-p);
|
||||
var f = getFrames(a.tlExtra[1], a.translate[1].length, frame, totalLength);
|
||||
var p = f[2];
|
||||
translate[1] = a.translate[1][f[0]]*(1-p) + a.translate[1][f[1]]*(p);
|
||||
} else translate[1] = a.translate[1][0];
|
||||
|
||||
if (a.tlExtra[2] != null) {
|
||||
var f = frame * a.tlExtra[2].speed;
|
||||
var fLow = Math.floor(f)%a.translate[2].length;
|
||||
var fHigh = Math.ceil(f)%a.translate[2].length;
|
||||
var p = f%1;
|
||||
translate[2] = a.translate[2][fHigh]*(p) + a.translate[2][fLow]*(1-p);
|
||||
var f = getFrames(a.tlExtra[2], a.translate[2].length, frame, totalLength);
|
||||
var p = f[2];
|
||||
translate[2] = a.translate[2][f[0]]*(1-p) + a.translate[2][f[1]]*(p);
|
||||
} else translate[2] = a.translate[2][0];
|
||||
} else {
|
||||
translate = fa.translate;
|
||||
|
@ -93,13 +102,11 @@ window.nitroAnimator = function(bmd, bca) {
|
|||
var rotate;
|
||||
if (a.rotate != null) {
|
||||
if (a.rotExtra != null) {
|
||||
var f = frame * a.rotExtra.speed;
|
||||
var fLow = Math.floor(f)%a.rotate.length;
|
||||
var fHigh = Math.ceil(f)%a.rotate.length;
|
||||
var p = f%1;
|
||||
var f = getFrames(a.rotExtra, a.rotate.length, frame, totalLength);
|
||||
var p = f[2];
|
||||
|
||||
var r1 = parseRotation(a.rotate[fLow]);
|
||||
var r2 = parseRotation(a.rotate[fHigh]);
|
||||
var r1 = parseRotation(a.rotate[f[0]]);
|
||||
var r2 = parseRotation(a.rotate[f[1]]);
|
||||
rotate = lerpMat3(r1, r2, p);
|
||||
} else {
|
||||
rotate = parseRotation(a.rotate[0]);
|
||||
|
@ -112,27 +119,21 @@ window.nitroAnimator = function(bmd, bca) {
|
|||
if (a.scale != null) {
|
||||
scale = [];
|
||||
if (a.scExtra[0] != null) {
|
||||
var f = frame * a.scExtra[0].speed;
|
||||
var fLow = Math.floor(f)%a.scale[0].length;
|
||||
var fHigh = Math.ceil(f)%a.scale[0].length;
|
||||
var p = f%1;
|
||||
scale[0] = a.scale[0][fHigh].s1*(p) + a.scale[0][fLow].s1*(1-p);
|
||||
var f = getFrames(a.scExtra[0], a.scale[0].length, frame, totalLength);
|
||||
var p = f[2];
|
||||
scale[0] = a.scale[0][f[0]].s1*(1-p) + a.scale[0][f[1]].s1*(p);
|
||||
} else scale[0] = a.scale[0][0].s1;
|
||||
|
||||
if (a.scExtra[1] != null) {
|
||||
var f = frame * a.scExtra[1].speed;
|
||||
var fLow = Math.floor(f)%a.scale[1].length;
|
||||
var fHigh = Math.ceil(f)%a.scale[1].length;
|
||||
var p = f%1;
|
||||
scale[1] = a.scale[1][fHigh].s1*(p) + a.scale[1][fLow].s1*(1-p);
|
||||
var f = getFrames(a.scExtra[1], a.scale[1].length, frame, totalLength);
|
||||
var p = f[2];
|
||||
scale[1] = a.scale[1][f[0]].s1*(1-p) + a.scale[1][f[1]].s1*(p);
|
||||
} else scale[1] = a.scale[1][0].s1;
|
||||
|
||||
if (a.scExtra[2] != null) {
|
||||
var f = frame * a.scExtra[2].speed;
|
||||
var fLow = Math.floor(f)%a.scale[2].length;
|
||||
var fHigh = Math.ceil(f)%a.scale[2].length;
|
||||
var p = f%1;
|
||||
scale[2] = a.scale[2][fHigh].s1*(p) + a.scale[2][fLow].s1*(1-p);
|
||||
var f = getFrames(a.scExtra[2], a.scale[2].length, frame, totalLength);
|
||||
var p = f[2];
|
||||
scale[2] = a.scale[2][f[0]].s1*(1-p) + a.scale[2][f[1]].s1*(p);
|
||||
} else scale[2] = a.scale[2][0].s1;
|
||||
} else {
|
||||
scale = fa.scale;
|
||||
|
@ -154,9 +155,15 @@ window.nitroAnimator = function(bmd, bca) {
|
|||
var cmds = model.commands;
|
||||
var curMat = mat4.create();
|
||||
var lastStackID = 0;
|
||||
var highestUsed = -1;
|
||||
|
||||
for (var i=0; i<cmds.length; 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]);
|
||||
var o = objs[cmd.obj];
|
||||
mat4.multiply(curMat, curMat, objMats[cmd.obj]);
|
||||
|
@ -165,6 +172,7 @@ window.nitroAnimator = function(bmd, bca) {
|
|||
if (cmd.stackID != null) {
|
||||
matrices[cmd.stackID] = mat4.clone(curMat);
|
||||
lastStackID = cmd.stackID;
|
||||
if (lastStackID > highestUsed) highestUsed = lastStackID;
|
||||
} else {
|
||||
matrices[lastStackID] = mat4.clone(curMat);
|
||||
}
|
||||
|
@ -172,10 +180,14 @@ window.nitroAnimator = function(bmd, bca) {
|
|||
|
||||
model.lastStackID = lastStackID;
|
||||
|
||||
var scale = [model.head.scale, model.head.scale, model.head.scale];
|
||||
targ.set(matBufEmpty);
|
||||
var off=0;
|
||||
for (var i=0; i<31; i++) {
|
||||
if (matrices[i] != null) targ.set(matrices[i], off);
|
||||
for (var i=0; i<=highestUsed; i++) {
|
||||
if (matrices[i] != null) {
|
||||
mat4.scale(matrices[i], matrices[i], scale);
|
||||
targ.set(matrices[i], off);
|
||||
}
|
||||
off += 16;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,8 +14,9 @@
|
|||
window.nitroRender = new function() {
|
||||
var gl, frag, vert, nitroShader;
|
||||
var cVec, color, texCoord, norm;
|
||||
var vecMode, vecPos, vecNorm, vecTx, vecCol, vecNum, vecMat, curMat;
|
||||
var vecMode, vecPos, vecNorm, vecTx, vecCol, vecNum, vecMat, curMat, stripAlt;
|
||||
var texWidth, texHeight, alphaMul = 1;
|
||||
var t = this;
|
||||
|
||||
this.cullModes = [];
|
||||
|
||||
|
@ -35,6 +36,7 @@ window.nitroRender = new function() {
|
|||
this.getViewHeight = getViewHeight;
|
||||
|
||||
this.flagShadow = false;
|
||||
this.forceFlatNormals = false; //generate flat normals for this mesh. Used for course model for better shadows.
|
||||
|
||||
var parameters = {
|
||||
0: 0,
|
||||
|
@ -127,6 +129,7 @@ window.nitroRender = new function() {
|
|||
vecMat = [];
|
||||
}
|
||||
vecNum = 0;
|
||||
stripAlt = 0;
|
||||
}
|
||||
|
||||
instructions[0x41] = function(view, off) { //end vtx
|
||||
|
@ -154,6 +157,24 @@ window.nitroRender = new function() {
|
|||
var norm = gl.createBuffer();
|
||||
|
||||
var posArray = new Float32Array(vecPos);
|
||||
if (t.forceFlatNormals && modes[vecMode] == gl.TRIANGLES) {
|
||||
//calculate new flat normals for each triangle
|
||||
for (var i=0; i<vecPos.length; i+=9) {
|
||||
var v1 = [vecPos[i], vecPos[i+1], vecPos[i+2]];
|
||||
var v2 = [vecPos[i+3], vecPos[i+4], vecPos[i+5]];
|
||||
var v3 = [vecPos[i+6], vecPos[i+7], vecPos[i+8]];
|
||||
|
||||
vec3.sub(v2, v2, v1);
|
||||
vec3.sub(v3, v3, v1);
|
||||
var newNorm = vec3.cross([], v2, v3);
|
||||
vec3.normalize(newNorm, newNorm);
|
||||
for (var j=0; j<3; j++) {
|
||||
for (var k=0; k<3; k++) {
|
||||
vecNorm[i+(j*3)+k] = newNorm[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, pos);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, posArray, gl.STATIC_DRAW);
|
||||
|
@ -192,11 +213,14 @@ window.nitroRender = new function() {
|
|||
}
|
||||
|
||||
if (optimiseTriangles && (vecMode > 1) && (vecNum > 2)) { //convert tri strips to individual triangles so we get one buffer per polygon
|
||||
vecPos = vecPos.concat(vecPos.slice(vecPos.length-6));
|
||||
vecNorm = vecNorm.concat(vecNorm.slice(vecNorm.length-6));
|
||||
vecTx = vecTx.concat(vecTx.slice(vecTx.length-4));
|
||||
vecCol = vecCol.concat(vecCol.slice(vecCol.length-8));
|
||||
vecMat = vecMat.concat(vecMat.slice(vecMat.length-2));
|
||||
var b = vecMat.length - (((stripAlt % 2) == 0)?1:3);
|
||||
var s2 = vecMat.length - (((stripAlt % 2) == 0)?2:1);
|
||||
vecPos = vecPos.concat(vecPos.slice(b*3, b*3+3)).concat(vecPos.slice(s2*3, s2*3+3));
|
||||
vecNorm = vecNorm.concat(vecNorm.slice(b*3, b*3+3)).concat(vecNorm.slice(s2*3, s2*3+3));
|
||||
vecTx = vecTx.concat(vecTx.slice(b*2, b*2+2)).concat(vecTx.slice(s2*2, s2*2+2));
|
||||
vecCol = vecCol.concat(vecCol.slice(b*4, b*4+4)).concat(vecCol.slice(s2*4, s2*4+4));
|
||||
vecMat = vecMat.concat(vecMat.slice(b, b+1)).concat(vecMat.slice(s2, s2+1));
|
||||
stripAlt++;
|
||||
}
|
||||
|
||||
vecNum++;
|
||||
|
@ -206,7 +230,6 @@ window.nitroRender = new function() {
|
|||
vecCol = vecCol.concat(color);
|
||||
vecNorm = vecNorm.concat(norm);
|
||||
vecMat.push(curMat);
|
||||
|
||||
}
|
||||
|
||||
function tenBitSign(val) {
|
||||
|
@ -223,6 +246,8 @@ window.nitroRender = new function() {
|
|||
this.init = function(ctx) {
|
||||
gl = ctx;
|
||||
this.gl = gl;
|
||||
this.billboardMat = mat4.create();
|
||||
this.yBillboardMat = mat4.create();
|
||||
|
||||
shaders = nitroShaders.compileShaders(gl);
|
||||
|
||||
|
@ -233,22 +258,25 @@ window.nitroRender = new function() {
|
|||
this.prepareShader = function() {
|
||||
//prepares the shader so no redundant calls have to be made. Should be called upon every program change.
|
||||
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 = {};
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.uniform1i(this.nitroShader.samplerUniform, 0);
|
||||
}
|
||||
|
||||
this.setShadowMode = function(sTex, fsTex, sMat, fsMat) {
|
||||
this.setShadowMode = function(sTex, fsTex, sMat, fsMat, dir) {
|
||||
this.nitroShader = shaders[1];
|
||||
var shader = shaders[1];
|
||||
gl.useProgram(shader);
|
||||
|
||||
vec3.normalize(dir, dir);
|
||||
gl.uniform3fv(shader.lightDirUniform, dir);
|
||||
gl.uniformMatrix4fv(shader.shadowMatUniform, false, sMat);
|
||||
gl.uniformMatrix4fv(shader.farShadowMatUniform, false, fsMat);
|
||||
gl.uniform1f(shader.lightIntensityUniform, 0.3);
|
||||
|
||||
this.resetShadOff();
|
||||
this.setNormalFlip(1);
|
||||
gl.activeTexture(gl.TEXTURE1);
|
||||
gl.bindTexture(gl.TEXTURE_2D, sTex);
|
||||
gl.uniform1i(shader.lightSamplerUniform, 1);
|
||||
|
@ -261,10 +289,33 @@ window.nitroRender = new function() {
|
|||
this.prepareShader();
|
||||
}
|
||||
|
||||
this.setLightIntensities = function(intensity, shadIntensity) {
|
||||
if (intensity == null) intensity = 0.3;
|
||||
if (shadIntensity == null) shadIntensity = 1;
|
||||
var shader = this.nitroShader;
|
||||
gl.useProgram(this.nitroShader);
|
||||
gl.uniform1f(shader.lightIntensityUniform, intensity);
|
||||
gl.uniform1f(shader.shadLightenUniform, 1-shadIntensity);
|
||||
}
|
||||
|
||||
this.setShadBias = function(bias) {
|
||||
var shader = this.nitroShader;
|
||||
gl.useProgram(this.nitroShader);
|
||||
gl.uniform1f(shader.shadOffUniform, bias);
|
||||
gl.uniform1f(shader.farShadOffUniform, bias);
|
||||
}
|
||||
|
||||
this.setNormalFlip = function(flip) {
|
||||
var shader = this.nitroShader;
|
||||
gl.useProgram(this.nitroShader);
|
||||
gl.uniform1f(shader.normalFlipUniform, flip);
|
||||
}
|
||||
|
||||
this.resetShadOff = function() {
|
||||
var shader = shaders[1];
|
||||
gl.uniform1f(shader.shadOffUniform, 0.00005+((mobile)?0.0005:0));
|
||||
gl.uniform1f(shader.farShadOffUniform, 0.0005);
|
||||
var shader = this.nitroShader;
|
||||
gl.useProgram(this.nitroShader);
|
||||
gl.uniform1f(shader.shadOffUniform, 0.0005+((mobile)?0.0005:0));
|
||||
gl.uniform1f(shader.farShadOffUniform, 0.0020);
|
||||
}
|
||||
|
||||
this.unsetShadowMode = function() {
|
||||
|
@ -275,6 +326,26 @@ window.nitroRender = new function() {
|
|||
this.prepareShader();
|
||||
}
|
||||
|
||||
var paused = false;
|
||||
|
||||
this.pauseShadowMode = function() {
|
||||
this.nitroShader = shaders[0];
|
||||
if (this.nitroShader == shaders[1]) paused = true;
|
||||
gl.useProgram(this.nitroShader);
|
||||
|
||||
this.setColMult([1, 1, 1, 1]);
|
||||
this.prepareShader();
|
||||
}
|
||||
|
||||
this.unpauseShadowMode = function() {
|
||||
if (!paused) return;
|
||||
this.nitroShader = shaders[1];
|
||||
gl.useProgram(this.nitroShader);
|
||||
|
||||
this.setColMult([1, 1, 1, 1]);
|
||||
this.prepareShader();
|
||||
}
|
||||
|
||||
this.setColMult = function(color) {
|
||||
gl.useProgram(this.nitroShader);
|
||||
gl.uniform4fv(this.nitroShader.colMultUniform, color);
|
||||
|
@ -324,6 +395,7 @@ window.nitroRender = new function() {
|
|||
|
||||
vecMode = 0;
|
||||
vecNum = 0;
|
||||
stripAlt = 0;
|
||||
vecPos = [];
|
||||
vecNorm = [];
|
||||
vecTx = [];
|
||||
|
@ -352,7 +424,7 @@ window.nitroRender = new function() {
|
|||
|
||||
};
|
||||
|
||||
function nitroModel(bmd, btx, remap) {
|
||||
function nitroModel(bmd, btx) {
|
||||
var bmd = bmd;
|
||||
this.bmd = bmd;
|
||||
var thisObj = this;
|
||||
|
@ -360,6 +432,7 @@ function nitroModel(bmd, btx, remap) {
|
|||
var texCanvas;
|
||||
var tex;
|
||||
var texAnim;
|
||||
var texPAnim;
|
||||
var texFrame;
|
||||
var modelBuffers;
|
||||
var collisionModel = [];
|
||||
|
@ -378,7 +451,9 @@ function nitroModel(bmd, btx, remap) {
|
|||
this.draw = draw;
|
||||
this.drawPoly = externDrawPoly;
|
||||
this.drawModel = externDrawModel;
|
||||
this.getBoundingCollisionModel = getBoundingCollisionModel;
|
||||
this.getCollisionModel = getCollisionModel;
|
||||
this.baseMat = mat4.create();
|
||||
|
||||
modelBuffers = []
|
||||
this.modelBuffers = modelBuffers;
|
||||
|
@ -388,10 +463,6 @@ function nitroModel(bmd, btx, remap) {
|
|||
matBuf.push({built: false, dat: new Float32Array(31*16)});
|
||||
}
|
||||
|
||||
if (remap != null) {
|
||||
setTextureRemap(remap);
|
||||
}
|
||||
|
||||
if (btx != null) {
|
||||
loadTexture(btx);
|
||||
} else if (bmd.tex != null) {
|
||||
|
@ -402,7 +473,7 @@ function nitroModel(bmd, btx, remap) {
|
|||
}
|
||||
|
||||
|
||||
function loadWhiteTex(btx) { //examines the materials in the loaded model and generates textures for each.
|
||||
function loadWhiteTex(btx) { //examines the materials in the loaded model and generates textures for each.
|
||||
var gl = nitroRender.gl; //get gl object from nitro render singleton
|
||||
loadedTex = btx;
|
||||
texCanvas = [];
|
||||
|
@ -440,83 +511,91 @@ function loadWhiteTex(btx) { //examines the materials in the loaded model and ge
|
|||
var model = models[j];
|
||||
var mat = model.materials.objectData
|
||||
for (var i=0; i<mat.length; i++) {
|
||||
var m = mat[i];
|
||||
var texI = mat[i].tex;
|
||||
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];
|
||||
|
||||
if (cached == null) {
|
||||
var canvas = btx.readTexWithPal(truetex, truepal);
|
||||
if (m.flipX || m.flipY) {
|
||||
var fC = document.createElement("canvas");
|
||||
var ctx = fC.getContext("2d");
|
||||
fC.width = (m.flipX)?canvas.width*2:canvas.width;
|
||||
fC.height = (m.flipY)?canvas.height*2:canvas.height;
|
||||
|
||||
ctx.drawImage(canvas, 0, 0);
|
||||
ctx.save();
|
||||
if (m.flipX) {
|
||||
ctx.translate(2*canvas.width, 0);
|
||||
ctx.scale(-1, 1);
|
||||
ctx.drawImage(canvas, 0, 0);
|
||||
ctx.restore();
|
||||
ctx.save();
|
||||
}
|
||||
if (m.flipY) {
|
||||
ctx.translate(0, 2*canvas.height);
|
||||
ctx.scale(1, -1);
|
||||
ctx.drawImage(fC, 0, 0);
|
||||
ctx.restore();
|
||||
}
|
||||
texCanvas.push(fC);
|
||||
var t = loadTex(fC, gl, !m.repeatX, !m.repeatY);
|
||||
t.realWidth = canvas.width;
|
||||
t.realHeight = canvas.height;
|
||||
tex.push(t);
|
||||
btx.cache[cacheID] = t;
|
||||
} else {
|
||||
texCanvas.push(canvas);
|
||||
var t = loadTex(canvas, gl, !m.repeatX, !m.repeatY);
|
||||
t.realWidth = canvas.width;
|
||||
t.realHeight = canvas.height;
|
||||
tex.push(t);
|
||||
btx.cache[cacheID] = t;
|
||||
}
|
||||
} else {
|
||||
tex.push(cached);
|
||||
}
|
||||
mat[i].texInd = tex.length;
|
||||
loadMatTex(mat[i], btx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setTextureRemap(remap) {
|
||||
texMap = remap;
|
||||
if (loadedTex != null) loadTexture(loadedTex)
|
||||
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) {
|
||||
var cacheID = truetex+":"+truepal;
|
||||
var cached = btx.cache[cacheID];
|
||||
|
||||
if (cached == null) {
|
||||
var canvas = btx.readTexWithPal(truetex, truepal);
|
||||
if (m.flipX || m.flipY) {
|
||||
var fC = document.createElement("canvas");
|
||||
var ctx = fC.getContext("2d");
|
||||
fC.width = (m.flipX)?canvas.width*2:canvas.width;
|
||||
fC.height = (m.flipY)?canvas.height*2:canvas.height;
|
||||
|
||||
ctx.drawImage(canvas, 0, 0);
|
||||
ctx.save();
|
||||
if (m.flipX) {
|
||||
ctx.translate(2*canvas.width, 0);
|
||||
ctx.scale(-1, 1);
|
||||
ctx.drawImage(canvas, 0, 0);
|
||||
ctx.restore();
|
||||
ctx.save();
|
||||
}
|
||||
if (m.flipY) {
|
||||
ctx.translate(0, 2*canvas.height);
|
||||
ctx.scale(1, -1);
|
||||
ctx.drawImage(fC, 0, 0);
|
||||
ctx.restore();
|
||||
}
|
||||
texCanvas.push(fC);
|
||||
var t = loadTex(fC, gl, !m.repeatX, !m.repeatY);
|
||||
t.realWidth = canvas.width;
|
||||
t.realHeight = canvas.height;
|
||||
btx.cache[cacheID] = t;
|
||||
return t;
|
||||
} else {
|
||||
texCanvas.push(canvas);
|
||||
var t = loadTex(canvas, gl, !m.repeatX, !m.repeatY);
|
||||
t.realWidth = canvas.width;
|
||||
t.realHeight = canvas.height;
|
||||
btx.cache[cacheID] = t;
|
||||
return t;
|
||||
}
|
||||
} else {
|
||||
return cached;
|
||||
}
|
||||
}
|
||||
|
||||
this.loadTexAnim = function(bta) {
|
||||
|
@ -524,10 +603,19 @@ function loadWhiteTex(btx) { //examines the materials in the loaded model and ge
|
|||
texFrame = 0;
|
||||
}
|
||||
|
||||
this.loadTexPAnim = function(btp) {
|
||||
texPAnim = btp;
|
||||
}
|
||||
|
||||
this.setFrame = function(frame) {
|
||||
texFrame = frame;
|
||||
}
|
||||
|
||||
this.setBaseMat = function(mat) {
|
||||
thisObj.baseMat = mat;
|
||||
thisObj.billboardID = -1;
|
||||
}
|
||||
|
||||
function externDrawModel(mv, project, mdl) {
|
||||
var models = bmd.modelData.objectData;
|
||||
drawModel(models[mdl], mv, project, mdl);
|
||||
|
@ -552,7 +640,7 @@ function loadWhiteTex(btx) { //examines the materials in the loaded model and ge
|
|||
|
||||
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.pMatrixUniform, false, project);
|
||||
|
@ -571,7 +659,85 @@ function loadWhiteTex(btx) { //examines the materials in the loaded model and ge
|
|||
}
|
||||
}
|
||||
|
||||
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][polyind] != null) return collisionModel[modelind][polyind];
|
||||
var model = bmd.modelData.objectData[modelind];
|
||||
|
@ -585,14 +751,16 @@ function loadWhiteTex(btx) { //examines the materials in the loaded model and ge
|
|||
var off = 0;
|
||||
for (var i=0; i<tC; i++) {
|
||||
var t = {}
|
||||
t.Vertex1 = [tris[off++], tris[off++], tris[off++]];
|
||||
t.Vertex2 = [tris[off++], tris[off++], tris[off++]];
|
||||
t.Vertex3 = [tris[off++], tris[off++], tris[off++]];
|
||||
t.Vertices = [];
|
||||
t.Vertices[0] = [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
|
||||
var v = vec3.sub([], t.Vertex2, t.Vertex1);
|
||||
var w = vec3.sub([], t.Vertex3, t.Vertex1);
|
||||
t.Normal = vec3.cross([], v, w)
|
||||
var v = vec3.sub([], t.Vertices[1], t.Vertices[0]);
|
||||
var w = vec3.sub([], t.Vertices[2], t.Vertices[0]);
|
||||
t.Normal = vec3.cross([], v, w);
|
||||
t.CollisionType = colType;
|
||||
vec3.normalize(t.Normal, t.Normal);
|
||||
out.push(t);
|
||||
}
|
||||
|
@ -615,7 +783,7 @@ function loadWhiteTex(btx) { //examines the materials in the loaded model and ge
|
|||
}
|
||||
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.pMatrixUniform, false, project);
|
||||
|
@ -635,26 +803,47 @@ function loadWhiteTex(btx) { //examines the materials in the loaded model and ge
|
|||
var gl = nitroRender.gl;
|
||||
|
||||
//texture 0 SHOULD be bound, assuming the nitrorender program has been prepared
|
||||
if (nitroRender.last.tex != tex[poly.mat]) {
|
||||
gl.bindTexture(gl.TEXTURE_2D, tex[poly.mat]); //load up material texture
|
||||
nitroRender.last.tex = tex[poly.mat];
|
||||
}
|
||||
var pmat = poly.mat;
|
||||
var matname = model.materials.names[pmat]; //attach tex anim to mat with same name
|
||||
if (texPAnim != null) {
|
||||
var info = texPAnim.animData.objectData[modelind];
|
||||
var anims = texPAnim.animData.objectData[modelind].data;
|
||||
var animNum = anims.names.indexOf(matname);
|
||||
if (animNum != -1) {
|
||||
var offFrame = texFrame % info.duration;
|
||||
//we got a match! it's wonderful :')
|
||||
var anim = anims.objectData[animNum];
|
||||
//look thru frames for the approprate point in the animation
|
||||
for (var i=anim.frames.length-1; i>=0; i--) {
|
||||
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]);
|
||||
*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var material = model.materials.objectData[poly.mat];
|
||||
nitroRender.setAlpha(material.alpha)
|
||||
if (nitroRender.last.tex != tex[pmat]) {
|
||||
gl.bindTexture(gl.TEXTURE_2D, tex[pmat]); //load up material texture
|
||||
nitroRender.last.tex = tex[pmat];
|
||||
}
|
||||
|
||||
var material = model.materials.objectData[pmat];
|
||||
nitroRender.setAlpha(material.alpha);
|
||||
|
||||
if (texAnim != null) {
|
||||
//generate and send texture matrix from data
|
||||
var matname = model.materials.names[poly.mat]; //attach tex anim to mat with same name
|
||||
var matname = model.materials.names[pmat]; //attach tex anim to mat with same name
|
||||
var anims = texAnim.animData.objectData[modelind].data;
|
||||
var animNum = anims.names.indexOf(matname);
|
||||
|
||||
if (animNum != -1) {
|
||||
//we got a match! it's wonderful :')
|
||||
var anim = anims.objectData[animNum];
|
||||
var mat = mat3.create(); //material texture mat is ignored
|
||||
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
|
||||
var mat = matAtFrame(texFrame, anim);
|
||||
gl.uniformMatrix3fv(shader.texMatrixUniform, false, mat);
|
||||
} else {
|
||||
gl.uniformMatrix3fv(shader.texMatrixUniform, false, material.texMat);
|
||||
|
@ -662,28 +851,74 @@ function loadWhiteTex(btx) { //examines the materials in the loaded model and ge
|
|||
|
||||
} else gl.uniformMatrix3fv(shader.texMatrixUniform, false, material.texMat);
|
||||
|
||||
if (modelBuffers[modelind][polyind] == null) modelBuffers[modelind][polyind] = nitroRender.renderDispList(poly.disp, tex[poly.mat], (poly.stackID == null)?model.lastStackID:poly.stackID);
|
||||
drawModelBuffer(modelBuffers[modelind][polyind], gl, shader);
|
||||
if (modelBuffers[modelind][polyind] == null) modelBuffers[modelind][polyind] = nitroRender.renderDispList(poly.disp, tex[poly.mat], (poly.stackID == null)?model.lastStackID:poly.stackID);
|
||||
|
||||
if (material.cullMode < 3) {
|
||||
gl.enable(gl.CULL_FACE);
|
||||
gl.cullFace(nitroRender.cullModes[material.cullMode]);
|
||||
} else {
|
||||
if (nitroRender.forceFlatNormals) {
|
||||
//dual side lighting model, course render mode essentially
|
||||
gl.enable(gl.CULL_FACE);
|
||||
gl.cullFace(gl.BACK);
|
||||
drawModelBuffer(modelBuffers[modelind][polyind], gl, shader);
|
||||
nitroRender.setNormalFlip(-1);
|
||||
gl.cullFace(gl.FRONT);
|
||||
drawModelBuffer(modelBuffers[modelind][polyind], gl, shader);
|
||||
nitroRender.setNormalFlip(1);
|
||||
return;
|
||||
}
|
||||
gl.disable(gl.CULL_FACE);
|
||||
}
|
||||
drawModelBuffer(modelBuffers[modelind][polyind], gl, shader);
|
||||
}
|
||||
|
||||
function generateMatrixStack(model, targ) { //this generates a matrix stack with the default bones. use nitroAnimator to pass custom matrix stacks using nsbca animations.
|
||||
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.
|
||||
var matrices = [];
|
||||
|
||||
var objs = model.objects.objectData;
|
||||
var cmds = model.commands;
|
||||
var curMat = mat4.create();
|
||||
var curMat = mat4.clone(thisObj.baseMat);
|
||||
var lastStackID = 0;
|
||||
var highestUsed = -1;
|
||||
|
||||
for (var i=0; i<cmds.length; 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]);
|
||||
var o = objs[cmd.obj];
|
||||
mat4.multiply(curMat, curMat, o.mat);
|
||||
if (o.billboardMode == 1) mat4.multiply(curMat, curMat, nitroRender.billboardMat);
|
||||
if (o.billboardMode == 2) mat4.multiply(curMat, curMat, nitroRender.yBillboardMat);
|
||||
|
||||
if (cmd.stackID != null) {
|
||||
matrices[cmd.stackID] = mat4.clone(curMat);
|
||||
lastStackID = cmd.stackID;
|
||||
if (lastStackID > highestUsed) highestUsed = lastStackID;
|
||||
} else {
|
||||
matrices[lastStackID] = mat4.clone(curMat);
|
||||
}
|
||||
|
@ -691,10 +926,14 @@ function generateMatrixStack(model, targ) { //this generates a matrix stack with
|
|||
|
||||
model.lastStackID = lastStackID;
|
||||
|
||||
var scale = [model.head.scale, model.head.scale, model.head.scale];
|
||||
targ.set(matBufEmpty);
|
||||
var off=0;
|
||||
for (var i=0; i<31; i++) {
|
||||
if (matrices[i] != null) targ.set(matrices[i], off);
|
||||
for (var i=0; i<=highestUsed; i++) {
|
||||
if (matrices[i] != null) {
|
||||
mat4.scale(matrices[i], matrices[i], scale);
|
||||
targ.set(matrices[i], off);
|
||||
}
|
||||
off += 16;
|
||||
}
|
||||
|
||||
|
@ -733,10 +972,11 @@ function loadTex(img, gl, clampx, clampy) { //general purpose function for loadi
|
|||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
|
||||
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||||
if (clampx) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
if (clampy) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
gl.generateMipmap(gl.TEXTURE_2D);
|
||||
|
||||
texture.width = img.width;
|
||||
texture.height = img.height;
|
||||
|
|
|
@ -84,13 +84,16 @@ window.nitroShaders = new (function() {
|
|||
varying vec4 color;\n\
|
||||
varying vec4 lightDist;\n\
|
||||
varying vec4 fLightDist;\n\
|
||||
varying vec3 normal;\n\
|
||||
\n\
|
||||
uniform float shadOff; \n\
|
||||
uniform float farShadOff; \n\
|
||||
uniform sampler2D lightDSampler;\n\
|
||||
uniform sampler2D farLightDSampler;\n\
|
||||
uniform float shadLighten; \n\
|
||||
\n\
|
||||
uniform sampler2D uSampler;\n\
|
||||
uniform vec3 lightDir;\n\
|
||||
\n\
|
||||
float shadowCompare(sampler2D map, vec2 pos, float compare, float so) {\n\
|
||||
float depth = texture2D(map, pos).r;\n\
|
||||
|
@ -113,21 +116,25 @@ window.nitroShaders = new (function() {
|
|||
}\n\
|
||||
\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\
|
||||
vec2 ldNorm = abs((lightDist.xy)-vec2(0.5, 0.5));\n\
|
||||
float dist = max(ldNorm.x, ldNorm.y);\n\
|
||||
float shadIntensity;\n\
|
||||
\n\
|
||||
if (dist > 0.5) {\n\
|
||||
gl_FragColor = col*mix(vec4(0.5, 0.5, 0.7, 1.0), vec4(1.0, 1.0, 1.0, 1.0), shadowLerp(farLightDSampler, vec2(4096.0, 4096.0), fLightDist.xy, fLightDist.z-farShadOff, farShadOff*2.0));\n\
|
||||
shadIntensity = shadowLerp(farLightDSampler, vec2(4096.0, 4096.0), fLightDist.xy, fLightDist.z-farShadOff, farShadOff*0.5);\n\
|
||||
} else if (dist > 0.4) {\n\
|
||||
float lerp1 = shadowLerp(farLightDSampler, vec2(4096.0, 4096.0), fLightDist.xy, fLightDist.z-farShadOff, farShadOff*2.0);\n\
|
||||
float lerp2 = shadowLerp(lightDSampler, vec2(2048.0, 2048.0), lightDist.xy, lightDist.z-shadOff, shadOff*4.0);\n\
|
||||
float lerp1 = shadowLerp(farLightDSampler, vec2(4096.0, 4096.0), fLightDist.xy, fLightDist.z-farShadOff, farShadOff*0.5);\n\
|
||||
float lerp2 = shadowLerp(lightDSampler, vec2(2048.0, 2048.0), lightDist.xy, lightDist.z-shadOff, shadOff*0.5);\n\
|
||||
\n\
|
||||
gl_FragColor = col*mix(vec4(0.5, 0.5, 0.7, 1.0), vec4(1.0, 1.0, 1.0, 1.0), mix(lerp2, lerp1, (dist-0.4)*10.0));\n\
|
||||
shadIntensity = mix(lerp2, lerp1, (dist-0.4)*10.0);\n\
|
||||
} else {\n\
|
||||
gl_FragColor = col*mix(vec4(0.5, 0.5, 0.7, 1.0), vec4(1.0, 1.0, 1.0, 1.0), shadowLerp(lightDSampler, vec2(2048.0, 2048.0), lightDist.xy, lightDist.z-shadOff, shadOff*4.0));\n\
|
||||
shadIntensity = shadowLerp(lightDSampler, vec2(2048.0, 2048.0), lightDist.xy, lightDist.z-shadOff, shadOff*0.5);\n\
|
||||
}\n\
|
||||
shadIntensity = min(shadIntensity, max(0.0, dot(normalize(normal), lightDir) * 5.0));\n\
|
||||
gl_FragColor = col*mix(vec4(0.5, 0.5, 0.7, 1.0), vec4(1.0, 1.0, 1.0, 1.0), min(1.0, shadIntensity + shadLighten));\n\
|
||||
\n\
|
||||
if (gl_FragColor.a == 0.0) discard;\n\
|
||||
}\n\
|
||||
|
@ -143,6 +150,7 @@ window.nitroShaders = new (function() {
|
|||
uniform mat4 uPMatrix;\n\
|
||||
uniform mat3 texMatrix;\n\
|
||||
uniform mat4 matStack[16];\n\
|
||||
uniform float normalFlip;\n\
|
||||
\n\
|
||||
uniform vec4 colMult;\n\
|
||||
\n\
|
||||
|
@ -154,6 +162,7 @@ window.nitroShaders = new (function() {
|
|||
varying vec4 color;\n\
|
||||
varying vec4 lightDist;\n\
|
||||
varying vec4 fLightDist;\n\
|
||||
varying vec3 normal;\n\
|
||||
\n\
|
||||
\n\
|
||||
void main(void) {\n\
|
||||
|
@ -163,7 +172,8 @@ window.nitroShaders = new (function() {
|
|||
\n\
|
||||
lightDist = (shadowMat*pos + vec4(1, 1, 1, 0)) / 2.0;\n\
|
||||
fLightDist = (farShadowMat*pos + vec4(1, 1, 1, 0)) / 2.0;\n\
|
||||
vec3 adjNorm = normalize(vec3(uMVMatrix * matStack[int(matrixID)] * vec4(aNormal, 0.0)));\n\
|
||||
vec3 adjNorm = normalize(vec3(uMVMatrix * matStack[int(matrixID)] * vec4(aNormal, 0.0))) * normalFlip;\n\
|
||||
normal = adjNorm; \n\
|
||||
float diffuse = (1.0-lightIntensity)-dot(adjNorm, vec3(0.0, -1.0, 0.0))*lightIntensity;\n\
|
||||
\n\
|
||||
color = aColor*colMult;\n\
|
||||
|
@ -302,12 +312,15 @@ window.nitroShaders = new (function() {
|
|||
["shadowMatUniform", "shadowMat"],
|
||||
["farShadowMatUniform", "farShadowMat"],
|
||||
["lightIntensityUniform", "lightIntensity"],
|
||||
["shadLightenUniform", "shadLighten"],
|
||||
["lightDirUniform", "lightDir"],
|
||||
["normalFlipUniform", "normalFlip"],
|
||||
|
||||
["shadOffUniform", "shadOff"],
|
||||
["farShadOffUniform", "farShadOff"],
|
||||
|
||||
["lightSamplerUniform", "lightDSampler"],
|
||||
["farLightSamplerUniform", "farLightDSampler"]
|
||||
["farLightSamplerUniform", "farLightDSampler"],
|
||||
]
|
||||
|
||||
config[0] = baseConf;
|
||||
|
|
|
@ -32,15 +32,30 @@ window.Race3DUI = function(scene, type, animStart) {
|
|||
|
||||
var params = {
|
||||
"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
|
||||
-128/1024, 128/1024, -(192+66)/1024, -66/1024
|
||||
]
|
||||
-128/1024, 128/1024, -(96)/1024, 96/1024
|
||||
],
|
||||
"goal": [ //why are these all so different?
|
||||
-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)
|
||||
//right now the vertical range of the viewport is large to try figure out where the hell it's going?
|
||||
"win": [
|
||||
-128/1024, 128/1024, -(96)/1024, 96/1024
|
||||
//-128/1024, 128/1024, -(1024)/1024, 1024/1024
|
||||
],
|
||||
"lose": [
|
||||
-128/1024, 128/1024, -(96)/1024, 96/1024
|
||||
//-128/1024, 128/1024, -(1024)/1024, 1024/1024
|
||||
],
|
||||
}
|
||||
|
||||
var param = params[type];
|
||||
if (param == null) param = params["count"]
|
||||
if (param == null) param = params["count"];
|
||||
|
||||
mat4.ortho(proj, param[0], param[1], param[2], param[3], -0.001, 10);
|
||||
buildOrtho(nitroRender.getViewWidth(), nitroRender.getViewHeight());
|
||||
|
@ -54,10 +69,13 @@ window.Race3DUI = function(scene, type, animStart) {
|
|||
bmd = new nsbmd(bmd);
|
||||
|
||||
var bca = new nsbca(scene.gameRes.Race.getFile(type+".nsbca"));
|
||||
var btp = scene.gameRes.Race.getFile(type+".nsbtp");
|
||||
if (btp != null) btp = new nsbtp(btp);
|
||||
anim = new nitroAnimator(bmd, bca);
|
||||
length = anim.getLength(0);
|
||||
if (type == "count") length *= 3;
|
||||
model = new nitroModel(bmd);
|
||||
model.loadTexPAnim(btp)
|
||||
}
|
||||
|
||||
function buildOrtho(width, height) {
|
||||
|
@ -72,11 +90,14 @@ window.Race3DUI = function(scene, type, animStart) {
|
|||
var width = nitroRender.getViewWidth();
|
||||
if (width != lastWidth) buildOrtho(width, nitroRender.getViewHeight());
|
||||
mat4.translate(mat, view, t.pos);
|
||||
nitroRender.pauseShadowMode();
|
||||
model.draw(mat, proj, animMat);
|
||||
nitroRender.unpauseShadowMode();
|
||||
}
|
||||
|
||||
function update() {
|
||||
if (anim != null) {
|
||||
model.setFrame(animFrame);
|
||||
animMat = anim.setFrame(0, 0, Math.max(0, animFrame++));
|
||||
}
|
||||
if (animFrame > length) {
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"name": "mkjs",
|
||||
"display": "fullscreen",
|
||||
"orientation": "landscape",
|
||||
"background_color": "black",
|
||||
|
||||
"icons": [
|
||||
{
|
||||
"src": "/resource/icon-512-512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
},
|
||||
{
|
||||
"src": "/resource/icon-192-192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "/resource/icon-180-180.png",
|
||||
"type": "image/png",
|
||||
"sizes": "180x180"
|
||||
},
|
||||
{
|
||||
"src": "/resource/icon-120-120.png",
|
||||
"type": "image/png",
|
||||
"sizes": "120x120"
|
||||
}
|
||||
]
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
Binary file not shown.
After Width: | Height: | Size: 94 KiB |
Loading…
Reference in New Issue