commit
42b11dc1a7
|
@ -42,7 +42,14 @@ window.cameraIngame = function(kart) {
|
|||
camNormal[2] += (kart.kartNormal[2]-camNormal[2])*0.075;
|
||||
vec3.normalize(camNormal, camNormal);
|
||||
|
||||
camAngle += dirDiff(kart.physicalDir+kart.driftOff/2, camAngle)*0.075;
|
||||
if (kart.physBasis != null) {
|
||||
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;
|
||||
} else {
|
||||
camAngle += dirDiff(kart.physicalDir+kart.driftOff/2, camAngle)*0.075;
|
||||
}
|
||||
camAngle = fixDir(camAngle);
|
||||
|
||||
boostOff += (((kart.boostNorm+kart.boostMT > 0)?5:0) - boostOff)*0.075
|
||||
|
@ -52,14 +59,23 @@ window.cameraIngame = function(kart) {
|
|||
var dist = 192;
|
||||
this.targetShadowPos = vec3.add([], kart.pos, [Math.sin(kart.angle)*dist, 0, -Math.cos(kart.angle)*dist])
|
||||
|
||||
thisObj.view = {p:p, mv:mat};
|
||||
thisObj.view = {p:p, mv:mat, pos: vec3.scale([], vec3.transformMat4([], [0,0,0], mat4.invert([], mat)), 1024)};
|
||||
|
||||
return thisObj.view;
|
||||
}
|
||||
|
||||
function buildBasis() {
|
||||
//order y, x, z
|
||||
var basis = gramShmidt(camNormal, [Math.cos(camAngle), 0, Math.sin(camAngle)], [Math.sin(camAngle), 0, -Math.cos(camAngle)]);
|
||||
var kart = thisObj.kart;
|
||||
var forward = [Math.sin(camAngle), 0, -Math.cos(camAngle)];
|
||||
var side = [Math.cos(camAngle), 0, Math.sin(camAngle)];
|
||||
/*
|
||||
if (kart.physBasis != null) {
|
||||
vec3.transformMat4(forward, forward, kart.physBasis.mat);
|
||||
vec3.transformMat4(side, side, kart.physBasis.mat);
|
||||
}
|
||||
*/
|
||||
var basis = gramShmidt(camNormal, side, forward);
|
||||
var temp = basis[0];
|
||||
basis[0] = basis[1];
|
||||
basis[1] = temp; //todo: cleanup
|
||||
|
|
|
@ -102,7 +102,7 @@ window.cameraIntro = function() {
|
|||
|
||||
thisObj.targetShadowPos = lookAtPos;
|
||||
|
||||
return {p:p, mv:mat}
|
||||
return {p:p, mv:mat, pos: vec3.scale([], vec3.transformMat4([], [0,0,0], mat4.invert([], mat)), 1024)}
|
||||
}
|
||||
|
||||
var initCam = function(scene, came) {
|
||||
|
|
|
@ -55,6 +55,7 @@ window.cameraSpectator = function(kart) {
|
|||
if (zoomLevel > curCam.zoomEnd) zoomLevel = curCam.zoomEnd;
|
||||
|
||||
thisObj.view = camFunc[curCam.camType](scene, curCam);
|
||||
thisObj.view.pos = vec3.scale([], vec3.transformMat4([], [0,0,0], mat4.invert([], thisObj.view.mv)), 1024)
|
||||
return thisObj.view;
|
||||
}
|
||||
|
||||
|
|
|
@ -141,8 +141,10 @@ window.MKDS_COLTYPE = new (function(){
|
|||
this.PHYS_MAP[this.LOOP] = 11;
|
||||
|
||||
//collision sound handlers
|
||||
//26 is blue water, 30 is white
|
||||
//28 and 15 might be sand/dirt
|
||||
|
||||
var waterRoad = {drift: MKDS_COLSOUNDS.DRIFT_WATER, brake: MKDS_COLSOUNDS.BRAKE_WATER, land: MKDS_COLSOUNDS.LAND_WATER, drive: MKDS_COLSOUNDS.DRIVE_WATER};
|
||||
var waterRoad = {drift: MKDS_COLSOUNDS.DRIFT_WATER, brake: MKDS_COLSOUNDS.BRAKE_WATER, land: MKDS_COLSOUNDS.LAND_WATER, drive: MKDS_COLSOUNDS.DRIVE_WATER, particle: 30};
|
||||
|
||||
this.SOUNDMAP = {
|
||||
0x00: //road
|
||||
|
@ -164,7 +166,7 @@ window.MKDS_COLTYPE = new (function(){
|
|||
{drift: MKDS_COLSOUNDS.DRIFT_ASPHALT, brake: MKDS_COLSOUNDS.BRAKE, land: MKDS_COLSOUNDS.LAND_ASPHALT},
|
||||
{drift: MKDS_COLSOUNDS.DRIFT_ASPHALT, brake: MKDS_COLSOUNDS.BRAKE, land: MKDS_COLSOUNDS.LAND_ASPHALT},
|
||||
{drift: MKDS_COLSOUNDS.DRIFT_ASPHALT, brake: MKDS_COLSOUNDS.BRAKE, land: MKDS_COLSOUNDS.LAND_ASPHALT},
|
||||
{drift: MKDS_COLSOUNDS.DRIFT_WATER, brake: MKDS_COLSOUNDS.BRAKE_WATER, land: MKDS_COLSOUNDS.LAND_WATERDEEP, drive: MKDS_COLSOUNDS.DRIVE_WATER},
|
||||
{drift: MKDS_COLSOUNDS.DRIFT_WATER, brake: MKDS_COLSOUNDS.BRAKE_WATER, land: MKDS_COLSOUNDS.LAND_WATERDEEP, drive: MKDS_COLSOUNDS.DRIVE_WATER, particle: 30},
|
||||
{drift: MKDS_COLSOUNDS.DRIFT_ASPHALT, brake: MKDS_COLSOUNDS.BRAKE, land: MKDS_COLSOUNDS.LAND_ASPHALT},
|
||||
{},
|
||||
{},
|
||||
|
@ -187,27 +189,27 @@ window.MKDS_COLTYPE = new (function(){
|
|||
|
||||
0x03: //road 4
|
||||
[
|
||||
{drift: MKDS_COLSOUNDS.DRIFT_SAND, brake: MKDS_COLSOUNDS.BRAKE_SAND , land: MKDS_COLSOUNDS.LAND_SAND, drive: MKDS_COLSOUNDS.DRIVE_SAND},
|
||||
{drift: MKDS_COLSOUNDS.DRIFT_DIRT, brake: MKDS_COLSOUNDS.BRAKE_DIRT, land: MKDS_COLSOUNDS.LAND_DIRT, drive: MKDS_COLSOUNDS.DRIVE_DIRT},
|
||||
{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_DIRT, brake: MKDS_COLSOUNDS.BRAKE_DIRT, land: MKDS_COLSOUNDS.LAND_DIRT, drive: MKDS_COLSOUNDS.DRIVE_DIRT, particle: 15},
|
||||
|
||||
{drift: MKDS_COLSOUNDS.DRIFT_ASPHALT, brake: MKDS_COLSOUNDS.BRAKE, land: MKDS_COLSOUNDS.LAND_GRASS, drive: MKDS_COLSOUNDS.DRIVE_GRASS},
|
||||
{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},
|
||||
{drift: MKDS_COLSOUNDS.DRIFT_SAND, brake: MKDS_COLSOUNDS.BRAKE_SAND, land: MKDS_COLSOUNDS.LAND_SAND, drive: MKDS_COLSOUNDS.DRIVE_SAND},
|
||||
{drift: MKDS_COLSOUNDS.DRIFT_ASPHALT, brake: MKDS_COLSOUNDS.BRAKE, land: MKDS_COLSOUNDS.LAND_SNOW}, //snow
|
||||
{drift: MKDS_COLSOUNDS.DRIFT_SAND, brake: MKDS_COLSOUNDS.BRAKE_SAND, land: MKDS_COLSOUNDS.LAND_SAND, drive: MKDS_COLSOUNDS.DRIVE_SAND, particle: 28},
|
||||
{drift: MKDS_COLSOUNDS.DRIFT_SAND, brake: MKDS_COLSOUNDS.BRAKE_SAND, land: MKDS_COLSOUNDS.LAND_SAND, drive: MKDS_COLSOUNDS.DRIVE_SAND, particle: 28},
|
||||
{drift: MKDS_COLSOUNDS.DRIFT_ASPHALT, brake: MKDS_COLSOUNDS.BRAKE, land: MKDS_COLSOUNDS.LAND_SNOW, particle:112}, //snow
|
||||
{},
|
||||
{}
|
||||
],
|
||||
|
||||
0x05: //road 5
|
||||
[
|
||||
{drift: MKDS_COLSOUNDS.DRIFT_SAND, brake: MKDS_COLSOUNDS.BRAKE_SAND , land: MKDS_COLSOUNDS.LAND_SAND, drive: MKDS_COLSOUNDS.DRIVE_SAND},
|
||||
{drift: MKDS_COLSOUNDS.DRIFT_DIRT, brake: MKDS_COLSOUNDS.BRAKE_DIRT, land: MKDS_COLSOUNDS.LAND_DIRT, drive: MKDS_COLSOUNDS.DRIVE_DIRT},
|
||||
{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_DIRT, brake: MKDS_COLSOUNDS.BRAKE_DIRT, land: MKDS_COLSOUNDS.LAND_DIRT, drive: MKDS_COLSOUNDS.DRIVE_DIRT, particle: 15},
|
||||
|
||||
{drift: MKDS_COLSOUNDS.DRIFT_ASPHALT, brake: MKDS_COLSOUNDS.BRAKE, land: MKDS_COLSOUNDS.LAND_GRASS, drive: MKDS_COLSOUNDS.DRIVE_GRASS},
|
||||
{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},
|
||||
{drift: MKDS_COLSOUNDS.DRIFT_ASPHALT, brake: MKDS_COLSOUNDS.BRAKE, land: MKDS_COLSOUNDS.LAND_GRASS, drive: MKDS_COLSOUNDS.DRIVE_GRASS},
|
||||
{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_GRASS, drive: MKDS_COLSOUNDS.DRIVE_GRASS, particle: 32},
|
||||
{},
|
||||
{},
|
||||
{}
|
||||
|
@ -216,7 +218,7 @@ window.MKDS_COLTYPE = new (function(){
|
|||
0x06: //slippery
|
||||
[
|
||||
{drift: MKDS_COLSOUNDS.DRIFT_ICE, brake: MKDS_COLSOUNDS.BRAKE_ICE, land:MKDS_COLSOUNDS.LAND_ICE},
|
||||
{drift: MKDS_COLSOUNDS.DRIFT_MARSH, brake: MKDS_COLSOUNDS.BRAKE_MARSH, land:MKDS_COLSOUNDS.LAND_MARSH, drive: MKDS_COLSOUNDS.DRIVE_MARSH},
|
||||
{drift: MKDS_COLSOUNDS.DRIFT_MARSH, brake: MKDS_COLSOUNDS.BRAKE_MARSH, land:MKDS_COLSOUNDS.LAND_MARSH, drive: MKDS_COLSOUNDS.DRIVE_MARSH, particle: 24},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
|
|
|
@ -19,6 +19,7 @@ window.controlRaceCPU = function(nkm) {
|
|||
}
|
||||
|
||||
this.fetchInput = fetchInput;
|
||||
this.setRouteID = setRouteID;
|
||||
|
||||
var battleMode = (nkm.sections["EPAT"] == null);
|
||||
|
||||
|
@ -53,11 +54,30 @@ window.controlRaceCPU = function(nkm) {
|
|||
|
||||
var dist = vec3.dot(destNorm, kart.pos) + destConst;
|
||||
if (dist < ePoi.pointSize) advancePoint();
|
||||
if (ePath.loop) debugger;
|
||||
|
||||
destPoint = vec3.add([], ePoi.pos, vec3.scale([], vec3.lerp([], posOffset, destOff, offTrans), ePoi.pointSize));
|
||||
var dirToPt = Math.atan2(destPoint[0]-kart.pos[0], kart.pos[2]-destPoint[2]);
|
||||
|
||||
var diff = dirDiff(dirToPt, kart.physicalDir);
|
||||
var physDir = kart.physicalDir;
|
||||
if (kart.physBasis) {
|
||||
if (kart.physBasis.loop) {
|
||||
return {
|
||||
accel: true, //x
|
||||
decel: false, //z
|
||||
drift: false, //s
|
||||
item: false, //a
|
||||
|
||||
//-1 to 1, intensity.
|
||||
turn: 0,
|
||||
airTurn: 0 //air excitebike turn, doesn't really have much function
|
||||
};
|
||||
}
|
||||
var forward = [Math.sin(physDir), 0, -Math.cos(physDir)];
|
||||
vec3.transformMat4(forward, forward, kart.physBasis.mat);
|
||||
var physDir = Math.atan2(forward[0], -forward[2]);
|
||||
}
|
||||
var diff = dirDiff(dirToPt, physDir);
|
||||
var turn = Math.min(Math.max(-1, (diff*3)), 1);
|
||||
|
||||
offTrans += 1/240;
|
||||
|
@ -94,6 +114,11 @@ window.controlRaceCPU = function(nkm) {
|
|||
|
||||
}
|
||||
|
||||
function setRouteID(routeID) {
|
||||
ePoiInd = routeID-1
|
||||
advancePoint();
|
||||
}
|
||||
|
||||
function advancePoint() {
|
||||
if (++ePoiInd < ePath.startInd+ePath.pathLen) {
|
||||
//next within this path
|
||||
|
@ -102,10 +127,14 @@ window.controlRaceCPU = function(nkm) {
|
|||
//advance to one of next possible paths
|
||||
|
||||
if (battleMode) {
|
||||
var pathInd = ((Math.random()>0.5 && ePath.source.length>0)?ePath.source:ePath.dest)[Math.floor(Math.random()*ePath.dest.length)];
|
||||
var loc = (Math.random()>0.5 && ePath.source.length>0)?ePath.source:ePath.dest;
|
||||
var pathInd = loc[Math.floor(Math.random()*loc.length)];
|
||||
ePoiInd = pathInd;
|
||||
ePoi = points[ePoiInd];
|
||||
recomputePath();
|
||||
var pt = points[ePoiInd];
|
||||
if (pt != null) {
|
||||
ePoi = pt;
|
||||
recomputePath();
|
||||
}
|
||||
} else {
|
||||
var pathInd = ePath.dest[Math.floor(Math.random()*ePath.dest.length)];
|
||||
ePath = paths[pathInd];
|
||||
|
|
|
@ -18,6 +18,7 @@ window.IngameRes = function(rom) {
|
|||
|
||||
this.Race = new narc(lz77.decompress(rom.getFile("/data/Scene/Race.carc"))); //contains lakitu, count, various graphics
|
||||
this.RaceLoc = new narc(lz77.decompress(rom.getFile("/data/Scene/Race_us.carc"))); //contains lakitu lap signs, START, YOU WIN etc. some of these will be replaced by hi res graphics by default.
|
||||
this.RaceEffect = new spa(r.MainEffect.getFile("RaceEffect.spa"));
|
||||
|
||||
this.MainFont = new nftr(r.Main2D.getFile("marioFont.NFTR"));
|
||||
//debugger;
|
||||
|
@ -30,20 +31,25 @@ window.IngameRes = function(rom) {
|
|||
"koura_w" /*blue shell item rep*/, "f_box", "killer" /*bullet bill*/
|
||||
]
|
||||
|
||||
//order
|
||||
//donkey, toad, bowser?, luigi, mario, peach, wario, yoshi, daisy, waluigi, dry bones (karon), robo, heyho
|
||||
var toSoundOff = [
|
||||
4, 0, 1, 2, 5, 6, 7, 3, 10, 8, 9, 11, 12
|
||||
];
|
||||
|
||||
var charNames = [
|
||||
"mario", "donkey", "kinopio", "koopa", "peach", "wario", "yoshi", "luigi", "karon", "daisy", "waluigi", "robo", "heyho"
|
||||
]
|
||||
];
|
||||
|
||||
var charAbbrv = [
|
||||
"MR", "DK", "KO", "KP", "PC", "WR", "YS", "LG", "KA", "DS", "WL", "RB", "HH"
|
||||
]
|
||||
];
|
||||
|
||||
var tireName = ["kart_tire_L", "kart_tire_M", "kart_tire_S"];
|
||||
|
||||
var characters = [];
|
||||
var karts = [];
|
||||
|
||||
var test = new spa(r.MainEffect.getFile("RaceEffect.spa"));
|
||||
loadItems();
|
||||
loadTires();
|
||||
|
||||
|
@ -75,6 +81,7 @@ window.IngameRes = function(rom) {
|
|||
loseA: new nsbca(r.KartModelSub.getFile(base+"_lose.nsbca")),
|
||||
spinA: new nsbca(r.KartModelSub.getFile(base+"_spin.nsbca")),
|
||||
winA: new nsbca(r.KartModelSub.getFile(base+"_win.nsbca")),
|
||||
sndOff: toSoundOff[ind]*14,
|
||||
}
|
||||
characters[ind] = obj;
|
||||
return characters[ind];
|
||||
|
|
|
@ -165,6 +165,11 @@ window.courseScene = function(mainNarc, texNarc, music, chars, options, gameRes)
|
|||
vec3.scale(targ, targ, 1/1024);
|
||||
mat4.mul(scn.shadMat, mat4.ortho(mat4.create(), targ[0]-shadres, targ[0]+shadres, targ[1]-shadres, targ[1]+shadres, -targ[2]-2.5, -targ[2]+2.5), scn.lightMat);
|
||||
|
||||
var places = [];
|
||||
for (var i=0; i<scn.karts.length; i++) { places.push(scn.karts[i]); }
|
||||
places.sort(function(a, b) {return b.getPosition() - a.getPosition()});
|
||||
for (var i=0; i<places.length; i++) { places[i].placement = i+1; };
|
||||
|
||||
for (var i=0; i<scn.karts.length; i++) {
|
||||
var ent = scn.karts[i];
|
||||
if (ent.active) ent.update(scn);
|
||||
|
@ -291,9 +296,9 @@ window.courseScene = function(mainNarc, texNarc, music, chars, options, gameRes)
|
|||
// the thresholds for different win sounds and music
|
||||
// thresh, goalsound, goalmusic, goalpostmusic
|
||||
var finishPercents = [
|
||||
[0, 66, 46, 58],
|
||||
[0.5, 66, 47, 56],
|
||||
[1.1, 67, 48, 57]
|
||||
[0, 66, 46, 58, 9],
|
||||
[0.5, 66, 47, 56, 10],
|
||||
[1.1, 67, 48, 57, 11]
|
||||
]
|
||||
|
||||
function lapAdvance(kart) {
|
||||
|
@ -309,20 +314,21 @@ window.courseScene = function(mainNarc, texNarc, music, chars, options, gameRes)
|
|||
else if (kart.lapNumber == 4) {
|
||||
var finishTuple = [];
|
||||
for (var i=0; i<finishPercents.length; i++) {
|
||||
if (finishPercents[i][0] > winPercent) continue;
|
||||
finishTuple = finishPercents[i];
|
||||
if (finishPercents[i][0] >= winPercent) break;
|
||||
}
|
||||
|
||||
kart.controller = new controlRaceCPU(scn.nkm, {});
|
||||
kart.controller.setKart(kart);
|
||||
|
||||
kart.anim.setAnim(winPercent>0.5?kart.charRes.LoseA:kart.charRes.winA);
|
||||
kart.anim.setAnim(winPercent>0.5?kart.charRes.loseA:kart.charRes.winA);
|
||||
kart.animMode = "raceEnd";
|
||||
|
||||
scn.camera = (new cameraSpectator(kart, scn));
|
||||
nitroAudio.playSound(finishTuple[1], {volume:2}, 0);
|
||||
nitroAudio.playSound(finishTuple[2], {volume:2}, null);
|
||||
nitroAudio.instaKill(scn.musicPlayer);
|
||||
kart.playCharacterSound(finishTuple[4], 2);
|
||||
musicRestartTimer = 0;
|
||||
musicRestart = 7.5*60;
|
||||
musicRestartType = 1;
|
||||
|
|
|
@ -179,7 +179,7 @@ window.ObjDecor = function(obji, scene) {
|
|||
forceBill = false;
|
||||
return {mdl:[{nsbmd:"choropu.nsbmd"}], other:[null, null, "choropu.nsbtp"]}; //has nsbtp
|
||||
case 0x019B: //cheep cheep (bouncing)
|
||||
return {mdl:[{nsbmd:"pukupuku.nsbmd"}], other:[null, null, "pukupuku.nsbtp"]}; //has nsbtp
|
||||
return {mdl:[{nsbmd:"pukupuku.nsbmd"}]}; //has nsbtp //, other:[null, null, "pukupuku.nsbtp"]
|
||||
case 0x019D: //snowman
|
||||
return {mdl:[{nsbmd:"sman_top.nsbmd"}, {nsbmd:"sman_bottom.nsbmd"}]};
|
||||
case 0x019E: //trunk with bats
|
||||
|
|
|
@ -45,6 +45,7 @@ window.ItemBox = function(obji, scene) {
|
|||
for (var j=0; j<10; j++) {
|
||||
scene.particles.push(new ItemShard(scene, ok, res.mdl[2]));
|
||||
}
|
||||
scene.particles.push(new NitroEmitter(scene, ok, 47));
|
||||
t.mode = 1;
|
||||
t.time = 0;
|
||||
break;
|
||||
|
|
|
@ -21,6 +21,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
|
||||
var params = scene.gameRes.kartPhys.karts[kartN];
|
||||
var offsets = scene.gameRes.kartOff.karts[kartN];
|
||||
this.wheelClass = (offsets.name[10] == "L")?2:((offsets.name[10] == "M")?1:0);
|
||||
|
||||
this.local = controller.local;
|
||||
this.active = true;
|
||||
|
@ -66,12 +67,20 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
this.drawWheels = drawWheels;
|
||||
this.drawChar = drawChar;
|
||||
|
||||
this.getPosition = getPosition;
|
||||
this.playCharacterSound = playCharacterSound;
|
||||
|
||||
this.trackAttach = null; //a normal for the kart to attach to (loop)
|
||||
this.boostMT = 0;
|
||||
this.boostNorm = 0;
|
||||
|
||||
this.kartColVel = vec3.create();
|
||||
this.kartColTimer = 0;
|
||||
this.kartWallTimer = 0;
|
||||
this.charSoundTimer = 0;
|
||||
|
||||
this.placement = 0;
|
||||
this.lastPlacement = 0;
|
||||
|
||||
var charRes = scene.gameRes.getChar(charN);
|
||||
var kartRes = scene.gameRes.getKart(kartN);
|
||||
|
@ -95,14 +104,24 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
this.lastInput = null;
|
||||
|
||||
//race statistics
|
||||
this.lapNumber = 0;
|
||||
this.lapNumber = 1;
|
||||
this.passedKTP2 = false;
|
||||
this.checkPointNumber = 0;
|
||||
this.OOB = 0;
|
||||
|
||||
this.wheelParticles = [
|
||||
new NitroEmitter(scene, k, -1, [1, 1.5, -1]),
|
||||
new NitroEmitter(scene, k, -1, [-1, 1.5, -1])
|
||||
];
|
||||
|
||||
scene.particles.push(this.wheelParticles[0]);
|
||||
scene.particles.push(this.wheelParticles[1]);
|
||||
|
||||
var nkm = scene.nkm;
|
||||
var startLine = nkm.sections["KTPS"].entries[0];
|
||||
var passLine = nkm.sections["KTP2"].entries[0];
|
||||
var checkpoints = nkm.sections["CPOI"].entries;
|
||||
var respawns = nkm.sections["CPOI"].entries;
|
||||
var respawns = nkm.sections["KTPJ"].entries;
|
||||
var futureChecks = [1];
|
||||
|
||||
var hitGroundAnim = [ //length 13, on y axis
|
||||
|
@ -193,6 +212,10 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
mat4.scale(wmat, wmat, [scale, scale, scale]);
|
||||
if (i<2) mat4.rotateY(wmat, wmat, ((k.driveAnimF-14)/14)*Math.PI/6);
|
||||
mat4.rotateX(wmat, wmat, wheelTurn);
|
||||
|
||||
if (i>1) {
|
||||
k.wheelParticles[i-2].offset = vec3.scale(k.wheelParticles[i-2].offset, vec3.add(k.wheelParticles[i-2].offset, offsets.wheels[i], [k.wheelClass*(i-2.5)*-2, (-params.colRadius)-k.wheelClass*2, 0]), 1/16);
|
||||
}
|
||||
}
|
||||
|
||||
var scale = 16;
|
||||
|
@ -262,7 +285,20 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
drawChar(view, pMatrix);
|
||||
}
|
||||
|
||||
|
||||
k.lWheelParticle = null;
|
||||
|
||||
function update(scene) {
|
||||
if (k.placement != k.lastPlacement) {
|
||||
if (k.placement < k.lastPlacement) {
|
||||
if (k.charSoundTimer == 0) {
|
||||
playCharacterSound(5);
|
||||
k.charSoundTimer = 60;
|
||||
}
|
||||
}
|
||||
k.lastPlacement = k.placement;
|
||||
}
|
||||
|
||||
var lastPos = vec3.clone(k.pos);
|
||||
updateMat = true;
|
||||
|
||||
|
@ -314,7 +350,8 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
sounds.boost = null;
|
||||
}
|
||||
|
||||
if (onGround && k.speed > 0.5) {
|
||||
var isMoving = onGround && k.speed > 0.5;
|
||||
if (isMoving) {
|
||||
if (lastCollided != sounds.lastTerrain || lastBE != sounds.lastBE || sounds.drive == null) {
|
||||
if (sounds.drive != null) nitroAudio.kill(sounds.drive);
|
||||
if (lastColSounds.drive != null) {
|
||||
|
@ -337,7 +374,10 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
} else {
|
||||
if (sounds.drift != null) { nitroAudio.kill(sounds.drift); sounds.drift = null; }
|
||||
if (sounds.drive != null) { nitroAudio.kill(sounds.drive); sounds.drive = null; }
|
||||
|
||||
}
|
||||
k.wheelParticles[0].pause = !isMoving;
|
||||
k.wheelParticles[1].pause = !isMoving;
|
||||
|
||||
//end sound update
|
||||
|
||||
|
@ -346,25 +386,55 @@ 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];
|
||||
|
||||
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 (c.id2 != 0) {
|
||||
var c2 = scene.nkm.sections["KTPC"].entries[c.id2];
|
||||
c = c2;
|
||||
|
||||
var forward = [0, 0, 1];
|
||||
var up = [0, 1, 0];
|
||||
var mat = mat4.create();
|
||||
mat4.rotateY(mat, mat, c.angle[1]*(Math.PI/180));
|
||||
mat4.rotateX(mat, mat, c.angle[0]*(-Math.PI/180));
|
||||
|
||||
k.vel = vec3.scale([], vec3.transformMat4(forward, forward, mat), MAXSPEED);
|
||||
k.speed = MAXSPEED;
|
||||
vec3.add(k.pos, k.pos, k.vel);
|
||||
k.physicalDir = (180-c.angle[1])*(Math.PI/180);
|
||||
k.angle = k.physicalDir;
|
||||
k.kartTargetNormal = vec3.transformMat4(up, up, mat);
|
||||
k.pos = vec3.clone(c2.pos);
|
||||
vec3.add(k.pos, k.pos, vec3.transformMat4([], [0,16,16], mat));
|
||||
|
||||
var planeConst = -vec3.dot(c.pos, forward);
|
||||
var cannonDist = vec3.dot(k.pos, forward) + planeConst;
|
||||
if (cannonDist > 0) k.cannon = null;
|
||||
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));
|
||||
|
||||
var forward = [0, 0, 1];
|
||||
var up = [0, 1, 0];
|
||||
|
||||
k.vel = vec3.scale([], vec3.transformMat4(forward, forward, mat), MAXSPEED);
|
||||
k.speed = Math.min(k.speed+1, MAXSPEED);
|
||||
vec3.add(k.pos, k.pos, k.vel);
|
||||
k.physicalDir = (180-c.angle[1])*(Math.PI/180);
|
||||
k.angle = k.physicalDir;
|
||||
k.kartTargetNormal = vec3.transformMat4(up, up, mat);
|
||||
|
||||
var planeConst = -vec3.dot(c.pos, forward);
|
||||
var cannonDist = vec3.dot(k.pos, forward) + planeConst;
|
||||
if (cannonDist > 0) k.cannon = null;
|
||||
}
|
||||
} else { //default kart mode
|
||||
|
||||
if (k.OOB > 0) {
|
||||
playCharacterSound(0);
|
||||
var current = checkpoints[k.checkPointNumber];
|
||||
var respawn = respawns[current.respawn];
|
||||
k.physicalDir = (180-respawn.angle[1])*(Math.PI/180);
|
||||
k.angle = k.physicalDir;
|
||||
k.speed = 0;
|
||||
k.vel = vec3.create();
|
||||
k.pos = vec3.clone(respawn.pos);
|
||||
vec3.add(k.pos, k.pos, [0,16,0]);
|
||||
if (k.controller.setRouteID != null) k.controller.setRouteID(respawn.id1);
|
||||
k.OOB = 0;
|
||||
}
|
||||
var groundEffect = 0;
|
||||
if (lastCollided != -1) {
|
||||
groundEffect = MKDS_COLTYPE.PHYS_MAP[lastCollided];
|
||||
|
@ -402,6 +472,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
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;
|
||||
|
@ -434,11 +505,15 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
|
||||
if (onGround) {
|
||||
if (!k.driftLanded) {
|
||||
if (k.driftMode == 0) k.drifting = false;
|
||||
if (k.driftMode == 0) {
|
||||
k.drifting = false;
|
||||
clearWheelParticles();
|
||||
}
|
||||
else {
|
||||
k.driftPSMode = 0;
|
||||
k.driftPSTick = 0;
|
||||
k.driftLanded = true;
|
||||
if (k.drifting) setWheelParticles(20, 1); //20 = smoke, 1 = drift priority
|
||||
}
|
||||
}
|
||||
if (k.drifting) {
|
||||
|
@ -467,7 +542,8 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
k.driftPSMode++;
|
||||
k.driftPSTick = 1;
|
||||
|
||||
//play blue spark sound
|
||||
//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;
|
||||
|
||||
|
@ -487,6 +563,8 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
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;
|
||||
|
@ -562,6 +640,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
|
||||
if (!onGround) {
|
||||
this.kartTargetNormal = [0, 1, 0];
|
||||
if (k.physBasis != null) vec3.transformMat4(this.kartTargetNormal,this.kartTargetNormal,k.physBasis.mat);
|
||||
vec3.add(k.vel, k.vel, k.gravity)
|
||||
if (k.ylock >= 0) {
|
||||
ylvel += k.gravity[1];
|
||||
|
@ -584,6 +663,8 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
}
|
||||
|
||||
if (k.kartColTimer > 0) k.kartColTimer--;
|
||||
if (k.kartWallTimer > 0) k.kartWallTimer--;
|
||||
if (k.charSoundTimer > 0) k.charSoundTimer--;
|
||||
|
||||
wheelTurn += k.speed/16;
|
||||
wheelTurn = fixDir(wheelTurn);
|
||||
|
@ -602,7 +683,15 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
|
||||
var steps = 0;
|
||||
var remainingT = 1;
|
||||
var velSeg = vec3.clone(k.vel);
|
||||
var baseVel = k.vel;
|
||||
if (k.physBasis != null) {
|
||||
if (k.physBasis.time-- < 0) exitBasis();
|
||||
else {
|
||||
baseVel = vec3.transformMat4([], baseVel, k.physBasis.mat);
|
||||
k.vel[1] = -1;
|
||||
}
|
||||
}
|
||||
var velSeg = vec3.clone(baseVel);
|
||||
var posSeg = vec3.clone(k.pos);
|
||||
var ignoreList = [];
|
||||
while (steps++ < 10 && remainingT > 0.01) {
|
||||
|
@ -611,7 +700,10 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
colResponse(posSeg, velSeg, result, ignoreList)
|
||||
remainingT -= result.t;
|
||||
if (remainingT > 0.01) {
|
||||
velSeg = vec3.scale(vec3.create(), k.vel, remainingT);
|
||||
if (k.physBasis != null) {
|
||||
baseVel = vec3.transformMat4([], k.vel, k.physBasis.mat);
|
||||
}
|
||||
velSeg = vec3.scale(vec3.create(), baseVel, remainingT);
|
||||
}
|
||||
} else {
|
||||
vec3.add(posSeg, posSeg, velSeg);
|
||||
|
@ -642,6 +734,43 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
positionChanged(lastPos, k.pos);
|
||||
}
|
||||
|
||||
function playCharacterSound(sound, volume) {
|
||||
//0 - hit
|
||||
//1 - hit spin
|
||||
//2 - hit ?? hit grnd
|
||||
//3 - hit banana? race start?
|
||||
//4 - hit spin?
|
||||
//5 - good pass?
|
||||
//6 - good OK!
|
||||
//7 - use item
|
||||
//8 - hit someone?
|
||||
//9 = win
|
||||
//10 = alright
|
||||
//11 = bad
|
||||
//12 = good record
|
||||
//13 = bad record
|
||||
if (volume == null) volume = 1;
|
||||
nitroAudio.playSound(sound + charRes.sndOff, {volume: 2*volume}, 2, k);
|
||||
}
|
||||
|
||||
function clearWheelParticles(prio) {
|
||||
for (var i=0; i<2; i++) {
|
||||
if (prio == null) {
|
||||
//clear all specials
|
||||
k.wheelParticles[i].clearEmitter(1); //drift mode
|
||||
//k.wheelParticles[i].clearEmitter(2); //drift flare (blue mt, red big flash)
|
||||
} else {
|
||||
k.wheelParticles[i].clearEmitter(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setWheelParticles(id, prio) {
|
||||
for (var i=0; i<2; i++) {
|
||||
k.wheelParticles[i].setEmitter(id, prio);
|
||||
}
|
||||
}
|
||||
|
||||
function genFutureChecks() {
|
||||
//all future points that
|
||||
var chosen = {}
|
||||
|
@ -661,6 +790,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
|
||||
function positionChanged(oldPos, pos) {
|
||||
//crossed into new checkpoint?
|
||||
if (checkpoints.length == 0) return;
|
||||
for (var i=0; i<futureChecks.length; i++) {
|
||||
var check = checkpoints[futureChecks[i]];
|
||||
var distOld = vec2.sub([], [check.x1, check.z1], [oldPos[0], oldPos[2]]);
|
||||
|
@ -685,11 +815,21 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
k.lapNumber++;
|
||||
k.checkPointNumber = 0;
|
||||
k.passedKTP2 = 0;
|
||||
futureChecks = [1];
|
||||
scene.lapAdvance(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getPosition() {
|
||||
if (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]);
|
||||
|
||||
return k.checkPointNumber + (1-(Math.abs(dot)/(0xFFFF))) + k.lapNumber*checkpoints.length;
|
||||
}
|
||||
|
||||
function forwardCrossedKTP(ktp, oldPos, pos) {
|
||||
var distOld = vec2.sub([], [ktp.pos[0], ktp.pos[2]], [oldPos[0], oldPos[2]]);
|
||||
var dist = vec2.sub([], [ktp.pos[0], ktp.pos[2]], [pos[0], pos[2]]);
|
||||
|
@ -768,7 +908,13 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
function buildBasis() {
|
||||
//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 basis = gramShmidt(k.kartNormal, [Math.cos(dir), 0, Math.sin(dir)], [Math.sin(dir), 0, -Math.cos(dir)]);
|
||||
var forward = [Math.sin(dir), 0, -Math.cos(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);
|
||||
}
|
||||
var basis = gramShmidt(k.kartNormal, side, forward);
|
||||
var temp = basis[0];
|
||||
basis[0] = basis[1];
|
||||
basis[1] = temp; //todo: cleanup
|
||||
|
@ -781,6 +927,56 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
|
||||
}
|
||||
|
||||
function enterBasis(normal) {
|
||||
//establish a new basis for the kart velocity based on this normal.
|
||||
//used by looping and sticky track surface types.
|
||||
|
||||
//first let's get the forward direction in our current basis
|
||||
|
||||
var dir = k.angle;
|
||||
var forward, side;
|
||||
if (k.physBasis != null) {
|
||||
forward = vec3.transformMat4([], [- Math.sin(dir), 0, Math.cos(dir)], k.physBasis.mat);
|
||||
side = vec3.transformMat4([], [Math.cos(dir), 0, Math.sin(dir)], k.physBasis.mat);
|
||||
} else {
|
||||
forward = [-Math.sin(dir), 0, Math.cos(dir)];
|
||||
side = [Math.cos(dir), 0, Math.sin(dir)];
|
||||
}
|
||||
|
||||
var basis = gramShmidt(normal, side, forward);
|
||||
var temp = basis[0];
|
||||
basis[0] = basis[1];
|
||||
basis[1] = temp; //todo: cleanup
|
||||
var m4 = [
|
||||
basis[0][0], basis[0][1], basis[0][2], 0,
|
||||
basis[1][0], basis[1][1], basis[1][2], 0,
|
||||
basis[2][0], basis[2][1], basis[2][2], 0,
|
||||
0, 0, 0, 1
|
||||
];
|
||||
|
||||
k.physicalDir = dirDiff(k.physicalDir, k.angle);
|
||||
k.angle = 0; //our front direction is now aligned with z.
|
||||
k.vel = [Math.sin(k.angle)*k.speed, k.vel[1], -Math.cos(k.angle)*k.speed];
|
||||
|
||||
k.physBasis = {
|
||||
mat: m4,
|
||||
inv: mat4.invert([], m4),
|
||||
time: 15,
|
||||
loop: false
|
||||
};
|
||||
}
|
||||
|
||||
function exitBasis() {
|
||||
//return to a normal y up, z forward basis.
|
||||
|
||||
var v = vec3.transformMat4([], k.vel, k.physBasis.mat);
|
||||
k.physicalDir = dirDiff(k.physicalDir, k.angle);
|
||||
k.angle = Math.atan2(v[0], -v[2]);
|
||||
k.physicalDir += k.angle;
|
||||
k.vel = v;
|
||||
k.physBasis = null;
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -805,6 +1001,11 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
return MKDS_COLTYPE.SOUNDMAP[collision][effect] || {};
|
||||
}
|
||||
|
||||
function colParticle(collision, effect) {
|
||||
if (MKDS_COLTYPE.SOUNDMAP[collision] == null) return null
|
||||
return MKDS_COLTYPE.SOUNDMAP[collision][effect].particle || null;
|
||||
}
|
||||
|
||||
function project(u, v) {
|
||||
return vec3.scale([], u, (vec3.dot(u, v)/vec3.dot(u, u)))
|
||||
}
|
||||
|
@ -815,26 +1016,42 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
var colType = (plane.CollisionType>>8)&31;
|
||||
var colBE = (plane.CollisionType>>5)&7;
|
||||
|
||||
var change = (colType != lastCollided);
|
||||
lastCollided = colType;
|
||||
lastBE = colBE;
|
||||
lastColSounds = colSound(lastCollided, colBE);
|
||||
|
||||
var n = vec3.normalize([], dat.normal);
|
||||
var an = n;
|
||||
if (k.physBasis != null) {
|
||||
an = vec3.transformMat4([], n, k.physBasis.inv);
|
||||
}
|
||||
var gravS = Math.sqrt(vec3.dot(k.gravity, k.gravity));
|
||||
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) {
|
||||
k.OOB = 1;
|
||||
}
|
||||
|
||||
if (MKDS_COLTYPE.GROUP_WALL.indexOf(colType) != -1) { //wall
|
||||
//sliding plane, except normal is transformed to be entirely on the xz plane (cannot ride on top of wall, treated as vertical)
|
||||
var xz = Math.sqrt(n[0]*n[0]+n[2]*n[2])
|
||||
var adjN = [n[0]/xz, 0, n[2]/xz]
|
||||
var xz = Math.sqrt(an[0]*an[0]+an[2]*an[2])
|
||||
var adjN = [an[0]/xz, 0, an[2]/xz]
|
||||
var proj = vec3.dot(k.vel, adjN);
|
||||
|
||||
if (proj < -1) {
|
||||
if (lastColSounds.hit != null) nitroAudio.playSound(lastColSounds.hit, {volume:1}, 0, k)
|
||||
if (k.kartWallTimer == 0) {
|
||||
if (lastColSounds.hit != null) nitroAudio.playSound(lastColSounds.hit, {volume:1}, 0, k)
|
||||
var colObj = {pos:pos, vel:[0,0,0], mat: mat4.fromTranslation([], pos)};
|
||||
scene.particles.push(new NitroEmitter(scene, colObj, 13));
|
||||
scene.particles.push(new NitroEmitter(scene, colObj, 14));
|
||||
}
|
||||
k.kartWallTimer = 15;
|
||||
}
|
||||
vec3.sub(k.vel, k.vel, vec3.scale(vec3.create(), adjN, proj));
|
||||
|
||||
|
||||
//convert back to angle + speed to keep change to kart vel
|
||||
|
||||
var v = k.vel;
|
||||
|
@ -846,13 +1063,31 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
|
|||
k.boostNorm = BOOSTTIME;
|
||||
}
|
||||
|
||||
var stick = (colType == MKDS_COLTYPE.STICKY || colType == MKDS_COLTYPE.LOOP);
|
||||
|
||||
if (k.vel[1] > 0) k.vel[1] = 0;
|
||||
var proj = vec3.dot(k.vel, n);
|
||||
if (proj < -4 && k.vel[1] < -2) { proj -= 1.5; }
|
||||
vec3.sub(k.vel, k.vel, vec3.scale(vec3.create(), n, proj));
|
||||
var proj = vec3.dot(k.vel, an);
|
||||
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) {
|
||||
enterBasis(dat.pNormal);
|
||||
k.physBasis.loop = colType == MKDS_COLTYPE.LOOP;
|
||||
} else {
|
||||
if (k.physBasis != null)
|
||||
exitBasis();
|
||||
}
|
||||
|
||||
k.kartTargetNormal = dat.pNormal;
|
||||
if (!onGround) {
|
||||
console.log("ground: "+colType+", "+colBE);
|
||||
|
||||
if (change) {
|
||||
var particle = colParticle(lastCollided, colBE);
|
||||
if (particle == null)
|
||||
clearWheelParticles(0);
|
||||
else
|
||||
setWheelParticles(particle, 0);
|
||||
}
|
||||
if (!onGround && !stick) {
|
||||
groundAnim = 0;
|
||||
if (lastColSounds.land != null) nitroAudio.playSound(lastColSounds.land, {volume:1}, 0, k)
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
window.spa = function(input) {
|
||||
var t = this;
|
||||
this.load = load;
|
||||
this.getTexture = getTexture;
|
||||
|
||||
var colourBuffer;
|
||||
|
||||
|
@ -36,42 +37,108 @@ window.spa = function(input) {
|
|||
|
||||
offset += 24;
|
||||
if (version == "12_1") {
|
||||
this.particles = [];
|
||||
for (let i=0; i<particleCount; i++) {
|
||||
this.particles[i] = readParticle(view, offset);
|
||||
offset = this.particles[i].nextOff;
|
||||
t.particles = [];
|
||||
for (var i=0; i<particleCount; i++) {
|
||||
t.particles[i] = readParticle(view, offset);
|
||||
t.particles[i].parent = t;
|
||||
offset = t.particles[i].nextOff;
|
||||
}
|
||||
}
|
||||
|
||||
offset = firstTexOffset;
|
||||
this.particleTextures = [];
|
||||
for (let i=0; i<particleTexCount; i++) {
|
||||
this.particleTextures[i] = readParticleTexture(view, offset);
|
||||
offset = this.particleTextures[i].nextOff;
|
||||
t.particleTextures = [];
|
||||
for (var i=0; i<particleTexCount; i++) {
|
||||
t.particleTextures[i] = readParticleTexture(view, offset);
|
||||
offset = t.particleTextures[i].nextOff;
|
||||
}
|
||||
|
||||
//window.debugParticle = true;
|
||||
if (window.debugParticle) {
|
||||
for (let i=0; i<particleCount; i++) {
|
||||
for (var i=0; i<particleCount; i++) {
|
||||
var text = document.createElement("textarea");
|
||||
var p = this.particles[i];
|
||||
var p = t.particles[i];
|
||||
p.parent = null;
|
||||
text.value = JSON.stringify(p, true, 4);
|
||||
p.parent = t;
|
||||
text.style.width = 500;
|
||||
text.style.height = 200;
|
||||
|
||||
|
||||
var obj = this.particleTextures[p.textureId];
|
||||
if (p.texAnim) obj = this.particleTextures[p.texAnim.textures[0]];
|
||||
var obj = t.particleTextures[p.textureId];
|
||||
if (p.texAnim) obj = t.particleTextures[p.texAnim.textures[0]];
|
||||
if (obj == null) {
|
||||
continue;
|
||||
}
|
||||
var test = readTexWithPal(obj.info, obj);
|
||||
document.body.appendChild(document.createElement("br"));
|
||||
document.body.appendChild(document.createTextNode(i+":"));
|
||||
document.body.appendChild(test);
|
||||
document.body.appendChild(text);
|
||||
document.body.appendChild(document.createElement("br"));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getTexture(id, gl) {
|
||||
var t = this;
|
||||
var obj = t.particleTextures[id];
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
if (obj.glTex == null) {
|
||||
var canvas = readTexWithPal(obj.info, obj);
|
||||
var m = obj.info;
|
||||
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();
|
||||
}
|
||||
var t = loadTex(fC, gl, !m.repeatX, !m.repeatY);
|
||||
t.realWidth = canvas.width;
|
||||
t.realHeight = canvas.height;
|
||||
obj.glTex = t;
|
||||
} else {
|
||||
var t = loadTex(canvas, gl, !m.repeatX, !m.repeatY);
|
||||
t.realWidth = canvas.width;
|
||||
t.realHeight = canvas.height;
|
||||
obj.glTex = t;
|
||||
}
|
||||
}
|
||||
return obj.glTex;
|
||||
}
|
||||
|
||||
function loadTex(img, gl, clampx, clampy) { //general purpose function for loading an image into a texture.
|
||||
var texture = gl.createTexture();
|
||||
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_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);
|
||||
|
||||
texture.width = img.width;
|
||||
texture.height = img.height;
|
||||
|
||||
gl.bindTexture(gl.TEXTURE_2D, null);
|
||||
return texture;
|
||||
}
|
||||
|
||||
function readParticle(view, off) {
|
||||
var obj = {};
|
||||
var ParticleFlags =
|
||||
|
@ -106,6 +173,7 @@ window.spa = function(input) {
|
|||
Bit29: 0x20000000
|
||||
}
|
||||
|
||||
obj.ParticleFlags = ParticleFlags;
|
||||
obj.flag = view.getUint32(off, true);
|
||||
obj.position = [view.getInt32(off+0x4, true)/4096, view.getInt32(off+0x8, true)/4096, view.getInt32(off+0xC, true)/4096];
|
||||
//this is just hilarious at this point
|
||||
|
@ -118,8 +186,8 @@ window.spa = function(input) {
|
|||
//not sure what this vector is for. grass it's (0, 1, 0), smoke it's (-0.706787109375, 0, -0.707275390625) billboard alignment vector? (it's a bit crazy for powerslide)
|
||||
obj.vector = [view.getInt16(off+0x1C, true)/4096, view.getInt16(off+0x1E, true)/4096, view.getInt16(off+0x20, true)/4096];
|
||||
obj.color = view.getUint16(off+0x22, true); //15 bit, usually 32767 for white.
|
||||
obj.randomxz = view.getUint32(off+0x24, true); //random xz velocity intensity
|
||||
obj.velocity = view.getUint32(off+0x28, true); //initial velocity related
|
||||
obj.randomxz = view.getUint32(off+0x24, true)/4096; //random xz velocity intensity
|
||||
obj.velocity = view.getUint32(off+0x28, true)/4096; //initial velocity related (along predefined vector)
|
||||
obj.size = view.getUint32(off+0x2C, true)/4096; //size
|
||||
obj.aspect = view.getUint16(off+0x30, true) / 4096; //aspect
|
||||
|
||||
|
@ -130,8 +198,8 @@ window.spa = function(input) {
|
|||
obj.rotVelFrom = view.getInt16(off+0x34, true);
|
||||
obj.rotVelTo = view.getInt16(off+0x36, true);
|
||||
|
||||
obj.unknown13 = view.getUint16(off+0x38, true); //??? (0)
|
||||
obj.unknown14 = view.getUint16(off+0x3A, true); //??? (4B)
|
||||
obj.scX = view.getInt16(off+0x38, true)/0x8000; //??? (0) //scale center offset?
|
||||
obj.scY = view.getInt16(off+0x3A, true)/0x8000; //??? (4B) //scale center offset?
|
||||
obj.emitterLifetime = view.getUint16(off+0x3C, true); //stop emitting particles after this many frames
|
||||
obj.duration = view.getUint16(off+0x3E, true);
|
||||
|
||||
|
@ -146,8 +214,8 @@ window.spa = function(input) {
|
|||
obj.textureId = view.getUint8(off+0x47, true);
|
||||
obj.unknown21 = view.getUint32(off+0x48, true); //negative number makes grass disappear (1 for grass, smoke)
|
||||
obj.unknown22 = view.getUint32(off+0x4C, true); //some numbers make grass disappear (0x458d00 for grass, 0x74725f60 for smoke)
|
||||
obj.xScaleDelta = view.getUint16(off+0x50, true); //x scale delta for some reason. usually 0
|
||||
obj.yScaleDelta = view.getUint16(off+0x52, true); //y scale delta for some reason. usually 0
|
||||
obj.xScaleDelta = view.getInt16(off+0x50, true)/4096; //x scale delta for some reason. usually 0
|
||||
obj.yScaleDelta = view.getInt16(off+0x52, true)/4096; //y scale delta for some reason. usually 0
|
||||
obj.unknown25 = view.getUint32(off+0x54, true); //FFFFFFFF makes run at half framerate. idk? usually 0
|
||||
off += 0x58;
|
||||
|
||||
|
@ -165,7 +233,8 @@ window.spa = function(input) {
|
|||
unkBase: view.getUint16(off, true)/4096,
|
||||
scaleFrom: view.getUint16(off+2, true)/4096,
|
||||
scaleTo: view.getUint16(off+4, true)/4096,
|
||||
param: view.getUint16(off+6, true),
|
||||
fromZeroTime: view.getUint8(off+6, true)/0xFF, //time to dedicate to an animation from zero size
|
||||
holdTime: view.getUint8(off+7, true)/0xFF, //time to dedicate to holding state at the end.
|
||||
flagParam: view.getUint16(off+8, true),
|
||||
unk4b: view.getUint16(off+10, true),
|
||||
};
|
||||
|
@ -173,7 +242,7 @@ window.spa = function(input) {
|
|||
}
|
||||
if ((obj.flag & ParticleFlags.ColorAnimation) != 0)
|
||||
{
|
||||
obj.ColorAnimation = {
|
||||
obj.colorAnim = {
|
||||
colorFrom: view.getUint16(off, true), //color from
|
||||
colorTo: view.getUint16(off+2, true), //color to (seems to be same as base color)
|
||||
framePct: view.getUint16(off+4, true), //frame pct to become color to (FFFF means always from, 8000 is about the middle)
|
||||
|
@ -203,7 +272,7 @@ window.spa = function(input) {
|
|||
if ((obj.flag & ParticleFlags.TextureAnimation) != 0)
|
||||
{
|
||||
var textures = [];
|
||||
for (let i=0; i<8; i++) textures[i] = view.getUint8(off+i);
|
||||
for (var i=0; i<8; i++) textures[i] = view.getUint8(off+i);
|
||||
obj.texAnim = {
|
||||
textures: textures,
|
||||
frames: view.getUint8(off+8),
|
||||
|
@ -215,7 +284,7 @@ window.spa = function(input) {
|
|||
if ((obj.flag & ParticleFlags.Bit16) != 0)
|
||||
{
|
||||
obj.Bit16 = [];
|
||||
for (let i=0; i<20; i++) obj.Bit16[i] = view.getUint8(off+i);
|
||||
for (var i=0; i<20; i++) obj.Bit16[i] = view.getUint8(off+i);
|
||||
off += 20;
|
||||
}
|
||||
if ((obj.flag & ParticleFlags.Gravity) != 0)
|
||||
|
@ -239,31 +308,31 @@ window.spa = function(input) {
|
|||
{
|
||||
//seems to be 4 int 16s typically in some kind of pattern.
|
||||
obj.Bit25 = [];
|
||||
for (let i=0; i<8; i++) obj.Bit25[i] = view.getUint8(off+i);
|
||||
for (var i=0; i<8; i++) obj.Bit25[i] = view.getUint8(off+i);
|
||||
off += 8;
|
||||
}
|
||||
if ((obj.flag & ParticleFlags.Bit26) != 0)
|
||||
{
|
||||
obj.Bit26 = [];
|
||||
for (let i=0; i<16; i++) obj.Bit26[i] = view.getUint8(off+i);
|
||||
for (var i=0; i<16; i++) obj.Bit26[i] = view.getUint8(off+i);
|
||||
off += 16;
|
||||
}
|
||||
if ((obj.flag & ParticleFlags.Bit27) != 0)
|
||||
{
|
||||
obj.Bit27 = [];
|
||||
for (let i=0; i<4; i++) obj.Bit27[i] = view.getUint8(off+i);
|
||||
for (var i=0; i<4; i++) obj.Bit27[i] = view.getUint8(off+i);
|
||||
off += 4;
|
||||
}
|
||||
if ((obj.flag & ParticleFlags.Bit28) != 0)
|
||||
{
|
||||
obj.Bit28 = [];
|
||||
for (let i=0; i<8; i++) obj.Bit28[i] = view.getUint8(off+i);
|
||||
for (var i=0; i<8; i++) obj.Bit28[i] = view.getUint8(off+i);
|
||||
off += 8;
|
||||
}
|
||||
if ((obj.flag & ParticleFlags.Bit29) != 0)
|
||||
{
|
||||
obj.Bit29 = [];
|
||||
for (let i=0; i<16; i++) obj.Bit29[i] = view.getUint8(off+i);
|
||||
for (var i=0; i<16; i++) obj.Bit29[i] = view.getUint8(off+i);
|
||||
off += 16;
|
||||
}
|
||||
|
||||
|
@ -278,7 +347,7 @@ window.spa = function(input) {
|
|||
|
||||
var flags = view.getUint16(off+4, true);
|
||||
obj.info = {
|
||||
pal0trans: (flags>>3)&1, //weirdly different format
|
||||
pal0trans: true,//z(flags>>3)&1, //weirdly different format
|
||||
format: ((flags)&7),
|
||||
height: 8 << ((flags>>8)&0xF),
|
||||
width: 8 << ((flags>>4)&0xF),
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
|
@ -5,35 +5,35 @@
|
|||
//
|
||||
|
||||
window.ItemShard = function(scene, targ, model) {
|
||||
var t = this;
|
||||
t.update = update;
|
||||
t.draw = draw;
|
||||
var t = this;
|
||||
t.update = update;
|
||||
t.draw = draw;
|
||||
|
||||
t.time = 0;
|
||||
t.pos = vec3.clone(targ.pos);
|
||||
t.vel = vec3.add([], targ.vel, [(Math.random()-0.5)*5, Math.random()*7, (Math.random()-0.5)*5]);
|
||||
t.dirVel = [(Math.random()-0.5), (Math.random()-0.5), (Math.random()-0.5)];
|
||||
t.dir = [Math.random()*2*Math.PI, Math.random()*2*Math.PI, Math.random()*2*Math.PI];
|
||||
t.scale = Math.random()+0.5;
|
||||
t.scale = [t.scale, t.scale, t.scale];
|
||||
t.time = 0;
|
||||
t.pos = vec3.clone(targ.pos);
|
||||
t.vel = vec3.add([], targ.vel, [(Math.random()-0.5)*5, Math.random()*7, (Math.random()-0.5)*5]);
|
||||
t.dirVel = [(Math.random()-0.5), (Math.random()-0.5), (Math.random()-0.5)];
|
||||
t.dir = [Math.random()*2*Math.PI, Math.random()*2*Math.PI, Math.random()*2*Math.PI];
|
||||
t.scale = Math.random()+0.5;
|
||||
t.scale = [t.scale, t.scale, t.scale];
|
||||
|
||||
function update(scene) {
|
||||
vec3.add(t.pos, t.pos, t.vel);
|
||||
vec3.add(t.vel, t.vel, [0, -0.17, 0]);
|
||||
vec3.add(t.dir, t.dir, t.dirVel);
|
||||
function update(scene) {
|
||||
vec3.add(t.pos, t.pos, t.vel);
|
||||
vec3.add(t.vel, t.vel, [0, -0.17, 0]);
|
||||
vec3.add(t.dir, t.dir, t.dirVel);
|
||||
|
||||
if (t.time++ > 30) scene.removeParticle(t);
|
||||
}
|
||||
if (t.time++ > 30) scene.removeParticle(t);
|
||||
}
|
||||
|
||||
function draw(view, pMatrix, gl) {
|
||||
var mat = mat4.translate(mat4.create(), view, t.pos);
|
||||
function draw(view, pMatrix, gl) {
|
||||
var mat = mat4.translate(mat4.create(), view, t.pos);
|
||||
|
||||
mat4.rotateZ(mat, mat, t.dir[2]);
|
||||
mat4.rotateY(mat, mat, t.dir[1]);
|
||||
mat4.rotateX(mat, mat, t.dir[0]);
|
||||
mat4.rotateZ(mat, mat, t.dir[2]);
|
||||
mat4.rotateY(mat, mat, t.dir[1]);
|
||||
mat4.rotateX(mat, mat, t.dir[0]);
|
||||
|
||||
mat4.scale(mat, mat, vec3.scale([], t.scale, 16));
|
||||
model.draw(mat, pMatrix);
|
||||
}
|
||||
mat4.scale(mat, mat, vec3.scale([], t.scale, 16));
|
||||
model.draw(mat, pMatrix);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
//
|
||||
// nitroEmitter.js
|
||||
//--------------------
|
||||
// Implemtents the generic nitro particle emitter.
|
||||
// by riperiperi
|
||||
//
|
||||
|
||||
window.NitroEmitter = function(scene, targ, emitterID, vector, offset) {
|
||||
var t = this;
|
||||
t.update = update;
|
||||
t.draw = draw;
|
||||
|
||||
var pRes = scene.gameRes.RaceEffect;
|
||||
var emitter = (emitterID == -1)?null:pRes.particles[emitterID];
|
||||
t.attached = targ; //an entity with pos and vel.
|
||||
|
||||
if (vector == null) vector = [0, 1, 0];
|
||||
if (offset == null) offset = [0,0,0];
|
||||
t.offset = offset;
|
||||
t.vector = vector;
|
||||
t.pctParticle = 0;
|
||||
t.time = 0;
|
||||
t.dead = false;
|
||||
t.curPrio = 0;
|
||||
t.doNotDelete = (emitter == null);
|
||||
t.pause = false;
|
||||
|
||||
t.setEmitter = setEmitter;
|
||||
t.clearEmitter = clearEmitter;
|
||||
|
||||
var particleList = [emitterID, -1, -1, -1];
|
||||
|
||||
function setEmitter(emitterID, prio) {
|
||||
particleList[prio] = emitterID;
|
||||
if (t.curPrio <= prio) {
|
||||
//activate this emitter immediately.
|
||||
t.curPrio = prio;
|
||||
t.dead = false;
|
||||
emitter = pRes.particles[emitterID];
|
||||
t.time = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function clearEmitter(prio) {
|
||||
//if (t.curPrio > prio) return; //this emitter cannot be unset
|
||||
if (prio == t.curPrio) {
|
||||
findNextEmitter();
|
||||
} else {
|
||||
particleList[prio] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
function findNextEmitter() {
|
||||
particleList[t.curPrio] = -1;
|
||||
var em = t.curPrio-1;
|
||||
while (em >= 0) {
|
||||
if (particleList[em] != -1) {
|
||||
t.dead = false;
|
||||
emitter = pRes.particles[particleList[em]];
|
||||
t.time = 0;
|
||||
t.curPrio = em;
|
||||
return;
|
||||
}
|
||||
em--;
|
||||
}
|
||||
if (em == -1) {
|
||||
t.curPrio = 0;
|
||||
emitter = null;
|
||||
dead = true;
|
||||
}
|
||||
t.curPrio = em;
|
||||
}
|
||||
|
||||
function update(scene) {
|
||||
if (emitter == null || t.dead || t.pause) return;
|
||||
if ((t.time % (emitter.frequency)) == 0 && t.time >= emitter.delay) {
|
||||
//should we create new particles? fractional logic for doing this
|
||||
t.pctParticle += emitter.particleChance;
|
||||
while (t.pctParticle >= 1) {
|
||||
|
||||
var attach = (emitter.flag & 0x8000) > 0;
|
||||
|
||||
t.pctParticle -= 1;
|
||||
//create a new particle
|
||||
//TODO: make these transform with the target's world matrix
|
||||
var pos = vec3.create();
|
||||
//add offset
|
||||
vec3.add(pos, pos, t.offset);
|
||||
//add emitter properties
|
||||
vec3.add(pos, pos, emitter.position);
|
||||
var spread = [Math.random()*2-1, Math.random()*2-1, Math.random()*2-1];
|
||||
var spreadMode = (emitter.flag & 0xF);
|
||||
if (spreadMode == 2) {
|
||||
spread[1] = 0; //spread is only in xz direction
|
||||
}
|
||||
vec3.normalize(spread, spread);
|
||||
if (spreadMode == 0) {
|
||||
spread = [0,0,0];
|
||||
}
|
||||
vec3.scale(spread, spread, Math.random()*emitter.areaSpread*2);
|
||||
vec3.add(pos, pos, spread);
|
||||
|
||||
vec3.scale(pos, pos, 16);
|
||||
if (!attach) {
|
||||
if (targ.mat != null) {
|
||||
vec3.transformMat4(pos, pos, targ.mat);
|
||||
} else {
|
||||
vec3.add(pos, pos, targ.pos);
|
||||
}
|
||||
}
|
||||
|
||||
//inherit velocity
|
||||
var vel = (attach)?vec3.create():vec3.clone(targ.vel);
|
||||
vec3.scale(vel, vel, 1/32);
|
||||
|
||||
var vector = vec3.clone(t.vector);
|
||||
if (!attach) {
|
||||
if (targ.mat != null) {
|
||||
//tranform our vector by the target matrix
|
||||
var mat = targ.mat;
|
||||
var org = [];
|
||||
mat4.getTranslation(org, mat);
|
||||
mat[12] = 0;
|
||||
mat[13] = 0;
|
||||
mat[14] = 0;
|
||||
|
||||
vec3.transformMat4(vector, vector, mat);
|
||||
|
||||
mat[12] = org[0];
|
||||
mat[13] = org[1];
|
||||
mat[14] = org[2];
|
||||
}
|
||||
}
|
||||
vec3.normalize(vector, vector);
|
||||
vec3.add(vel, vel, vec3.scale([], vector, emitter.velocity));
|
||||
|
||||
var xz = [Math.random()*2-1, 0, Math.random()*2-1];
|
||||
vec3.normalize(xz, xz);
|
||||
vec3.scale(xz, xz, Math.random()*emitter.randomxz);
|
||||
vec3.add(vel, vel, xz);
|
||||
|
||||
var rotVel = ((emitter.rotVelFrom + ((Math.random()*emitter.rotVelTo-emitter.rotVelFrom) | 0))/65535) * Math.PI*2;
|
||||
var dir = ((emitter.flag & 0x2000) > 0)?(Math.random()*Math.PI*2):0;
|
||||
var duration = emitter.duration + emitter.duration * emitter.varDuration/0xFF * (Math.random() * 2 - 1);
|
||||
var scaleMod = (emitter.varScale/0xFF * (Math.random() * 2 - 1)) + 1;
|
||||
|
||||
var scale = [scaleMod * emitter.size, scaleMod * emitter.size * emitter.aspect];
|
||||
|
||||
var particle = new NitroParticle(scene, emitter, pos, vel, dir, rotVel, duration, scale, (attach?t.attached:null));
|
||||
particle.ovel = (attach)?vec3.create():vec3.scale([], targ.vel, 1/32);
|
||||
|
||||
scene.particles.push(particle);
|
||||
}
|
||||
|
||||
|
||||
var pos = vec3.clone(targ.pos);
|
||||
|
||||
}
|
||||
t.time++;
|
||||
|
||||
if (t.time == emitter.emitterLifetime) {
|
||||
t.dead = true;
|
||||
if (!t.doNotDelete) scene.removeParticle(t);
|
||||
else {
|
||||
findNextEmitter();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function draw(view, pMatrix, gl) {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,251 @@
|
|||
//
|
||||
// nitroParticle.js
|
||||
//--------------------
|
||||
// Implements a generic nitro particle. Currently positioned and updated in software for debug
|
||||
// simplicity - but should be moved to vertex shader in future.
|
||||
// by riperiperi
|
||||
//
|
||||
|
||||
window.NitroParticle = function(scene, emitter, pos, vel, dir, dirVel, duration, scale, attached) {
|
||||
var t = this;
|
||||
t.update = update;
|
||||
t.draw = draw;
|
||||
|
||||
t.time = 0;
|
||||
t.duration = duration | 0;
|
||||
t.pos = vec3.add(pos, pos, [0, 0, 0]);
|
||||
t.vel = vel;
|
||||
t.dirVel = dirVel; //float
|
||||
t.dir = dir; //float
|
||||
t.attached = attached;
|
||||
t.scale = scale; //vec2
|
||||
t.emitter = emitter;
|
||||
|
||||
t.aScale = 1;
|
||||
//decode 16 bit color into float
|
||||
t.baseColor = convertCol(emitter.color);
|
||||
t.baseColor[3] = emitter.opacity / 0x1F;
|
||||
t.aColor = vec4.clone(t.baseColor);
|
||||
|
||||
t.frame = emitter.textureId;
|
||||
if (t.emitter.texAnim)
|
||||
t.frame = t.emitter.texAnim.textures[0];
|
||||
|
||||
function convertCol(col) {
|
||||
return [
|
||||
((col&31)/31),
|
||||
(((col>>5)&31)/31),
|
||||
(((col>>10)&31)/31),
|
||||
1 //Math.round((col>>15)*255);
|
||||
];
|
||||
}
|
||||
|
||||
function update(scene) {
|
||||
var particlePct = t.time / t.duration;
|
||||
|
||||
t.pos[0] += t.vel[0] * 16;
|
||||
t.pos[1] += t.vel[1] * (t.emitter.yOffIntensity/128) * 16;
|
||||
t.pos[2] += t.vel[2] * 16;
|
||||
if (t.emitter.gravity) {
|
||||
vec3.add(t.vel, t.vel, t.emitter.gravity);
|
||||
}
|
||||
if (t.emitter.colorAnim) {
|
||||
var ca = t.emitter.colorAnim;
|
||||
var from = convertCol(ca.colorFrom);
|
||||
var to = convertCol(ca.colorTo);
|
||||
var pctFloat = (ca.framePct/0xFFFF);
|
||||
vec4.lerp(t.aColor, from, to, Math.max(0, (particlePct - pctFloat)/(1 - pctFloat)));
|
||||
t.aColor[3] = t.emitter.opacity/0x1F;
|
||||
} else {
|
||||
t.aColor = vec4.clone(t.baseColor);
|
||||
}
|
||||
if (t.emitter.opacityAnim) {
|
||||
var oa = t.emitter.opacityAnim;
|
||||
var pctFade = oa.startFade / 0xFFFF;
|
||||
var opaMul = 1-Math.max(0, (particlePct - pctFade)/(1 - pctFade));
|
||||
t.aColor[3] = opaMul;// * oa.intensity/0x0FFF;
|
||||
//vec4.scale(t.aColor, t.aColor, opaMul);
|
||||
}
|
||||
if (t.emitter.texAnim) {
|
||||
var ta = t.emitter.texAnim;
|
||||
var frame = 0;
|
||||
if ((ta.unknown1 & 128) > 0) {
|
||||
//select frame based on particle duration
|
||||
var frame = ta.textures[Math.min((particlePct * ta.frames) | 0, ta.frames-1)];
|
||||
} else {
|
||||
//repeating anim with framerate
|
||||
//not sure what framerate is, but its likely in the unknowns.
|
||||
var frame = ta.textures[(t.time % ta.frames)];
|
||||
}
|
||||
t.frame = frame;
|
||||
}
|
||||
|
||||
t.dir += t.dirVel;
|
||||
|
||||
if (t.time++ >= t.duration) scene.removeParticle(t);
|
||||
}
|
||||
|
||||
function draw(view, pMatrix, gl) {
|
||||
var particlePct = t.time / t.duration;
|
||||
|
||||
var pos = t.pos;
|
||||
var vel = t.vel;
|
||||
|
||||
if (t.attached != null) {
|
||||
pos = vec3.transformMat4([], pos, t.attached.mat);
|
||||
|
||||
|
||||
//tranform our vector by the target matrix
|
||||
var mat = t.attached.mat;
|
||||
var org = [];
|
||||
|
||||
mat4.getTranslation(org, mat);
|
||||
mat[12] = 0;
|
||||
mat[13] = 0;
|
||||
mat[14] = 0;
|
||||
|
||||
vel = vec3.transformMat4([], vel, mat);
|
||||
|
||||
mat[12] = org[0];
|
||||
mat[13] = org[1];
|
||||
mat[14] = org[2];
|
||||
|
||||
}
|
||||
|
||||
var mat = mat4.translate(mat4.create(), view, pos);
|
||||
|
||||
var bbMode = t.emitter.flag & 0xF0;
|
||||
|
||||
if (bbMode == 0x10) { //spark, billboards towards camera
|
||||
var camPos = scene.camera.view.pos;
|
||||
|
||||
camPos = vec3.sub([], camPos, pos);
|
||||
vec3.normalize(camPos, camPos);
|
||||
|
||||
var n = vec3.sub([], vel, t.ovel);
|
||||
vec3.normalize(n,n);
|
||||
mat4.multiply(mat, mat, mat4.invert([], mat4.lookAt([], [0,0,0], camPos, n)));
|
||||
|
||||
} else if (bbMode == 0x20) { //no billboard
|
||||
mat4.rotateY(mat, mat, t.dir);
|
||||
} else if (bbMode == 0x30) { //spark, no billboard
|
||||
var camPos = scene.camera.view.pos;
|
||||
|
||||
camPos = vec3.sub([], camPos, pos);
|
||||
vec3.normalize(camPos, camPos);
|
||||
|
||||
var n = vec3.sub([], vel, t.ovel);
|
||||
vec3.normalize(n,n);
|
||||
mat4.multiply(mat, mat, mat4.invert([], mat4.lookAt([], [0,0,0], camPos, n)));
|
||||
mat4.rotateY(mat, mat, t.dir);
|
||||
} else { //billboard
|
||||
mat4.multiply(mat, mat, nitroRender.billboardMat);
|
||||
mat4.rotateZ(mat, mat, t.dir);
|
||||
}
|
||||
var finalScale = 1;
|
||||
if (t.emitter.scaleAnim) {
|
||||
var sa = t.emitter.scaleAnim;
|
||||
if (particlePct < sa.fromZeroTime) {
|
||||
var fzPct = particlePct / sa.fromZeroTime;
|
||||
finalScale = sa.scaleFrom * fzPct;
|
||||
} else {
|
||||
var rescaledPct = Math.min(1, (particlePct - sa.fromZeroTime) / (1-(sa.fromZeroTime + sa.holdTime*(1-sa.fromZeroTime))));
|
||||
finalScale = sa.scaleFrom * (1-rescaledPct) + sa.scaleTo * rescaledPct;
|
||||
}
|
||||
}
|
||||
mat4.scale(mat, mat, vec3.scale([], [t.scale[0], t.scale[1], 1], 12*finalScale));
|
||||
mat4.translate(mat, mat, [t.emitter.xScaleDelta, t.emitter.yScaleDelta, 0]);
|
||||
|
||||
drawGeneric(mat, pMatrix, gl);
|
||||
}
|
||||
|
||||
var MAT3I = mat3.create();
|
||||
var MAT4I = mat4.create();
|
||||
function drawGeneric(mv, project, gl) {
|
||||
var shader = nitroRender.nitroShader;
|
||||
if (!nitroRender.flagShadow) {
|
||||
gl.uniform1f(shader.shadOffUniform, 0.001);
|
||||
gl.uniform1f(shader.lightIntensityUniform, 0);
|
||||
}
|
||||
if (window.VTX_PARTICLE == null) genGlobalVtx(gl);
|
||||
var obj = window.VTX_PARTICLE;
|
||||
|
||||
nitroRender.setColMult(t.aColor);
|
||||
|
||||
gl.uniformMatrix4fv(shader.mvMatrixUniform, false, mv);
|
||||
gl.uniformMatrix4fv(shader.pMatrixUniform, false, project);
|
||||
//matrix stack unused, just put an identity in slot 0
|
||||
gl.uniformMatrix4fv(shader.matStackUniform, false, MAT4I);
|
||||
|
||||
var frame = t.emitter.parent.getTexture(t.frame, gl);
|
||||
gl.bindTexture(gl.TEXTURE_2D, frame);
|
||||
//texture matrix not used
|
||||
gl.uniformMatrix3fv(shader.texMatrixUniform, false, MAT3I);
|
||||
if (obj != nitroRender.last.obj) {
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, obj.vPos);
|
||||
gl.vertexAttribPointer(shader.vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, obj.vTx);
|
||||
gl.vertexAttribPointer(shader.textureCoordAttribute, 2, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, obj.vCol);
|
||||
gl.vertexAttribPointer(shader.colorAttribute, 4, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, obj.vMat);
|
||||
gl.vertexAttribPointer(shader.matAttribute, 1, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, obj.vNorm);
|
||||
gl.vertexAttribPointer(shader.normAttribute, 3, gl.FLOAT, false, 0, 0);
|
||||
nitroRender.last.obj = obj;
|
||||
}
|
||||
|
||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
nitroRender.setColMult([1, 1, 1, 1]);
|
||||
if (!nitroRender.flagShadow) {
|
||||
nitroRender.resetShadOff();
|
||||
gl.uniform1f(shader.lightIntensityUniform, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
function genGlobalVtx(gl) {
|
||||
var vecPos = [-1,-1,0, 1,-1,0, -1,1,0, 1,1,0];
|
||||
var vecTx = [1,1, 0,1, 1,0, 0,0];
|
||||
var vecCol = [1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1];
|
||||
var vecMat = [0,0,0,0];
|
||||
var vecNorm = [0,1,0, 0,1,0, 0,1,0, 0,1,0];
|
||||
|
||||
var pos = gl.createBuffer();
|
||||
var col = gl.createBuffer();
|
||||
var tx = gl.createBuffer();
|
||||
var mat = gl.createBuffer();
|
||||
var norm = gl.createBuffer();
|
||||
|
||||
var posArray = new Float32Array(vecPos);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, pos);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, posArray, gl.STATIC_DRAW);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, tx);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vecTx), gl.STATIC_DRAW);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, col);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vecCol), gl.STATIC_DRAW);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, mat);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vecMat), gl.STATIC_DRAW);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, norm);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vecNorm), gl.STATIC_DRAW);
|
||||
|
||||
window.VTX_PARTICLE = {
|
||||
posArray: posArray,
|
||||
vPos: pos,
|
||||
vTx: tx,
|
||||
vCol: col,
|
||||
vMat: mat,
|
||||
vNorm: norm,
|
||||
verts: vecPos.length/3,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -246,10 +246,9 @@ window.nitroRender = new function() {
|
|||
|
||||
gl.uniformMatrix4fv(shader.shadowMatUniform, false, sMat);
|
||||
gl.uniformMatrix4fv(shader.farShadowMatUniform, false, fsMat);
|
||||
gl.uniform1f(shader.lightIntensityUniform, 0.3);
|
||||
|
||||
gl.uniform1f(shader.shadOffUniform, 0.00005+((mobile)?0.0005:0));
|
||||
gl.uniform1f(shader.farShadOffUniform, 0.0005);
|
||||
|
||||
this.resetShadOff();
|
||||
gl.activeTexture(gl.TEXTURE1);
|
||||
gl.bindTexture(gl.TEXTURE_2D, sTex);
|
||||
gl.uniform1i(shader.lightSamplerUniform, 1);
|
||||
|
@ -262,6 +261,12 @@ window.nitroRender = new function() {
|
|||
this.prepareShader();
|
||||
}
|
||||
|
||||
this.resetShadOff = function() {
|
||||
var shader = shaders[1];
|
||||
gl.uniform1f(shader.shadOffUniform, 0.00005+((mobile)?0.0005:0));
|
||||
gl.uniform1f(shader.farShadOffUniform, 0.0005);
|
||||
}
|
||||
|
||||
this.unsetShadowMode = function() {
|
||||
this.nitroShader = shaders[0];
|
||||
gl.useProgram(this.nitroShader);
|
||||
|
@ -384,7 +389,7 @@ function nitroModel(bmd, btx, remap) {
|
|||
}
|
||||
|
||||
if (remap != null) {
|
||||
setTextureRemap(remap)
|
||||
setTextureRemap(remap);
|
||||
}
|
||||
|
||||
if (btx != null) {
|
||||
|
|
|
@ -16,9 +16,39 @@ window.nitroShaders = new (function() {
|
|||
\n\
|
||||
uniform sampler2D uSampler;\n\
|
||||
\n\
|
||||
float indexValue() {\n\
|
||||
int x = int(mod(gl_FragCoord.x, 4.0));\n\
|
||||
int y = int(mod(gl_FragCoord.y, 4.0));\n\
|
||||
int i = (x + y * 4);\n\
|
||||
if (i == 0) return 0.0;\n\
|
||||
else if (i == 1) return 8.0;\n\
|
||||
else if (i == 2) return 2.0;\n\
|
||||
else if (i == 3) return 10.0;\n\
|
||||
else if (i == 4) return 12.0;\n\
|
||||
else if (i == 5) return 4.0;\n\
|
||||
else if (i == 6) return 14.0;\n\
|
||||
else if (i == 7) return 6.0;\n\
|
||||
else if (i == 8) return 3.0;\n\
|
||||
else if (i == 9) return 11.0;\n\
|
||||
else if (i == 10) return 1.0;\n\
|
||||
else if (i == 11) return 9.0;\n\
|
||||
else if (i == 12) return 15.0;\n\
|
||||
else if (i == 13) return 7.0;\n\
|
||||
else if (i == 14) return 13.0;\n\
|
||||
else if (i == 15) return 5.0;\n\
|
||||
}\n\
|
||||
\n\
|
||||
float dither(float color) {\n\
|
||||
float closestColor = (color < 0.5) ? 0.0 : 1.0;\n\
|
||||
float secondClosestColor = 1.0 - closestColor;\n\
|
||||
float d = indexValue();\n\
|
||||
float distance = abs(closestColor - color);\n\
|
||||
return (distance < d) ? closestColor : secondClosestColor;\n\
|
||||
}\n\
|
||||
\n\
|
||||
void main(void) {\n\
|
||||
gl_FragColor = texture2D(uSampler, vTextureCoord)*color;\n\
|
||||
if (gl_FragColor.a == 0.0) discard;\n\
|
||||
if (gl_FragColor.a < 1.0 && (gl_FragColor.a == 0.0 || dither(gl_FragColor.a) == 0.0)) discard;\n\
|
||||
}"
|
||||
|
||||
this.defaultVert = "attribute vec3 aVertexPosition;\n\
|
||||
|
@ -62,20 +92,20 @@ window.nitroShaders = new (function() {
|
|||
\n\
|
||||
uniform sampler2D uSampler;\n\
|
||||
\n\
|
||||
float shadowCompare(sampler2D map, vec2 pos, float compare) {\n\
|
||||
float shadowCompare(sampler2D map, vec2 pos, float compare, float so) {\n\
|
||||
float depth = texture2D(map, pos).r;\n\
|
||||
return step(compare, depth);\n\
|
||||
return smoothstep(compare-so, compare, depth);\n\
|
||||
}\n\
|
||||
\n\
|
||||
float shadowLerp(sampler2D depths, vec2 size, vec2 uv, float compare){\n\
|
||||
float shadowLerp(sampler2D depths, vec2 size, vec2 uv, float compare, float so){\n\
|
||||
vec2 texelSize = vec2(1.0)/size;\n\
|
||||
vec2 f = fract(uv*size+0.5);\n\
|
||||
vec2 centroidUV = floor(uv*size+0.5)/size;\n\
|
||||
\n\
|
||||
float lb = shadowCompare(depths, centroidUV+texelSize*vec2(0.0, 0.0), compare);\n\
|
||||
float lt = shadowCompare(depths, centroidUV+texelSize*vec2(0.0, 1.0), compare);\n\
|
||||
float rb = shadowCompare(depths, centroidUV+texelSize*vec2(1.0, 0.0), compare);\n\
|
||||
float rt = shadowCompare(depths, centroidUV+texelSize*vec2(1.0, 1.0), compare);\n\
|
||||
float lb = shadowCompare(depths, centroidUV+texelSize*vec2(0.0, 0.0), compare, so);\n\
|
||||
float lt = shadowCompare(depths, centroidUV+texelSize*vec2(0.0, 1.0), compare, so);\n\
|
||||
float rb = shadowCompare(depths, centroidUV+texelSize*vec2(1.0, 0.0), compare, so);\n\
|
||||
float rt = shadowCompare(depths, centroidUV+texelSize*vec2(1.0, 1.0), compare, so);\n\
|
||||
float a = mix(lb, lt, f.y);\n\
|
||||
float b = mix(rb, rt, f.y);\n\
|
||||
float c = mix(a, b, f.x);\n\
|
||||
|
@ -89,14 +119,14 @@ window.nitroShaders = new (function() {
|
|||
float dist = max(ldNorm.x, ldNorm.y);\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));\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\
|
||||
} else if (dist > 0.4) {\n\
|
||||
float lerp1 = shadowLerp(farLightDSampler, vec2(4096.0, 4096.0), fLightDist.xy, fLightDist.z-farShadOff);\n\
|
||||
float lerp2 = shadowLerp(lightDSampler, vec2(2048.0, 2048.0), lightDist.xy, lightDist.z-shadOff);\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\
|
||||
\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\
|
||||
} 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));\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\
|
||||
}\n\
|
||||
\n\
|
||||
if (gl_FragColor.a == 0.0) discard;\n\
|
||||
|
@ -118,6 +148,7 @@ window.nitroShaders = new (function() {
|
|||
\n\
|
||||
uniform mat4 shadowMat;\n\
|
||||
uniform mat4 farShadowMat;\n\
|
||||
uniform float lightIntensity; \n\
|
||||
\n\
|
||||
varying vec2 vTextureCoord;\n\
|
||||
varying vec4 color;\n\
|
||||
|
@ -133,7 +164,7 @@ window.nitroShaders = new (function() {
|
|||
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\
|
||||
float diffuse = 0.7-dot(adjNorm, vec3(0.0, -1.0, 0.0))*0.3;\n\
|
||||
float diffuse = (1.0-lightIntensity)-dot(adjNorm, vec3(0.0, -1.0, 0.0))*lightIntensity;\n\
|
||||
\n\
|
||||
color = aColor*colMult;\n\
|
||||
color = vec4(color.x*diffuse, color.y*diffuse, color.z*diffuse, color.w);\n\
|
||||
|
@ -270,6 +301,7 @@ window.nitroShaders = new (function() {
|
|||
var shadUnif = [
|
||||
["shadowMatUniform", "shadowMat"],
|
||||
["farShadowMatUniform", "farShadowMat"],
|
||||
["lightIntensityUniform", "lightIntensity"],
|
||||
|
||||
["shadOffUniform", "shadOff"],
|
||||
["farShadOffUniform", "farShadOff"],
|
||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue