Migration from libeye to Gutamatics.

Updated credits.
This commit is contained in:
Mari the Deer 2021-04-02 19:52:13 +02:00
commit 3b3918de8a
22 changed files with 1022 additions and 967 deletions

View file

@ -16,7 +16,7 @@ Most of the work here is original, but there are some notable exceptions:
* Dark Souls
* Dark Souls 2
* Dark Souls 3
- This mod uses libeye, by KeksDose. Big thanks.
- This mod uses Gutamatics, by Gutawer. Big thanks.
- Title theme, "Traumatic State", by Teque (which a lot of people just know as "the AS-Golgotha music").
- Intermission theme, "Dragony", also by Teque (very comfy music considering the rest of his repertoire).
- Startup/credits theme, "Hidden Tune #242", also by Teque too (super comfy music, ideal for this use).

View file

@ -53,43 +53,6 @@ SWWM_SKEYS = "Share Keys";
SWWM_SVARMORUSE = "Enforce Armor Auto-Use";
SWWM_SVHEALTHUSE = "Enforce Health Auto-Use";
SWWM_SVAMMOUSE = "Enforce Fabricator Auto-Use";
SWWM_MCREDS = "SWWM GZ Credits";
SWWM_CSTITLE = "UnSX Side Story: Demolitionist";
SWWM_CSTITLE2 = "Cute Robutt Whomst Love To Explode Demons";
SWWM_CLEAD = "Development Lead:";
SWWM_CDEV2 = "Self-proclaimed ZScript Magician";
SWWM_CASSETS = "Additional Assets:";
SWWM_ASSBARONS = "XDeath Sprites for Hell Nobles";
SWWM_ASSOTHERS = "XDeath Sprites for Demon, Cacodemon, Revenant, Archvile";
SWWM_ASSKEEN = "Less Mean-Spirited Keen Replacement";
SWWM_CMUSIC = "Music Tracks Used:";
SWWM_CFANART = "Intermission Fanart:";
SWWM_CPATRON = "Patreon Backers:";
SWWM_CTHANK = "Special Thanks:";
SWWM_CMAB2 = "For being an amazing friend who believes in me, and for inspiring me to keep working on what I love. I'm really really happy to have met you.";
SWWM_CDRAGON2 = "For being a good pet dragon who cares about me.";
SWWM_CLUCY2 = "For the Tewi font, which I've used for many many years. I hope you're doing well, wherever you are.";
SWWM_CKEKS2 = "For libeye, assistance with exception handling code, and also for being such a cool Touhou nerd.";
SWWM_CZN2 = "For slope alignment code, and to Nash also for being a cool smart cactus dude.";
SWWM_CVAL2 = "For the custom Nashgore footprints, and for being a good friend and cute bun.";
SWWM_CSLEDGE2 = "For being a great bud, not just for me but for all of us, and also for encouraging me to take part in the Doom modiverse.";
SWWM_CSPY2 = "For being so enthusiastic about everything in this mod, and for being the cute little brother I never had.";
SWWM_CINSP1 = "Combine Kegan, HyperUltra64, TerminusEst13, Yholl and friends";
SWWM_CINSP2 = "For inspiration and also for being really cool people. Bless all of you, keep being amazing.";
SWWM_CCOMMUNITY1 = "All my amazing friends from the Doom community";
SWWM_CCOMMUNITY2 = "For helping me keep this whole thing rolling, and for all the time we spend together. Here's to many more years of Dooming, stay awesome, everyone!";
SWWM_CDEVS1 = "Randi, Graf, Rachael and the rest of the GZDoom dev team";
SWWM_CDEVS2 = "For your work on the source port that brought back my faith in modding. I don't care what others think, you're great.";
SWWM_CYOU1 = "You";
SWWM_CYOU2 = "For playing this mod, thank you so much.";
SWWM_CLOCAL = "Localization:";
SWWM_LOCDE = "German";
SWWM_LOCES = "Spanish";
SWWM_LOCFR = "French";
SWWM_LOCIT = "Italian";
SWWM_LOCJP = "Japanese";
SWWM_LOCPL = "Polish";
SWWM_LOCRU = "Russian";
SWWM_FORCEDISABLE = "Force Disable";
SWWM_USERSET = "User Set";
SWWM_FORCEENABLE = "Force Enable";
@ -385,6 +348,45 @@ TOOLTIP_SWWM_MM_THINGCOLOR_CITEM = "Color for items that count towards map tally
TOOLTIP_SWWM_MM_THINGCOLOR_VIPITEM = "Color for very important items.";
TOOLTIP_SWWM_MM_THINGCOLOR_SHOOTABLE = "Color for things that can be damaged.";
TOOLTIP_SWWM_MM_THINGCOLOR_MISSILE = "Color for projectiles.";
// credits
SWWM_MCREDS = "SWWM GZ Credits";
SWWM_CSTITLE = "UnSX Side Story: Demolitionist";
SWWM_CSTITLE2 = "Cute Robutt Whomst Love To Explode Demons";
SWWM_CLEAD = "Development Lead:";
SWWM_CDEV2 = "Self-proclaimed ZScript Magician";
SWWM_CASSETS = "Additional Assets:";
SWWM_ASSBARONS = "XDeath Sprites for Hell Nobles";
SWWM_ASSOTHERS = "XDeath Sprites for Demon, Cacodemon, Revenant, Archvile";
SWWM_ASSKEEN = "Less Mean-Spirited Keen Replacement";
SWWM_CMUSIC = "Music Tracks Used:";
SWWM_CFANART = "Intermission Fanart:";
SWWM_CPATRON = "Patreon Backers:";
SWWM_CTHANK = "Special Thanks:";
SWWM_CMAB2 = "For being an amazing friend who believes in me, and for inspiring me to keep working on what I love. I'm really really happy to have met you.";
SWWM_CDRAGON2 = "For being a good pet dragon who cares about me.";
SWWM_CLUCY2 = "For the Tewi font, which I've used for many many years. I hope you're doing well, wherever you are.";
SWWM_CGUTA2 = "For the Gutamatics library, and for helping me with learning ZScript.";
SWWM_CKEKS2 = "For assistance with exception handling code, and also for being such a cool Touhou nerd.";
SWWM_CZN2 = "For slope alignment code, and to Nash also for being a cool smart cactus dude.";
SWWM_CVAL2 = "For the custom Nashgore footprints, and for being a good friend and cute bun.";
SWWM_CSLEDGE2 = "For being great buds, not just for me but for all of us, and also for encouraging me to take part in the Doom modiverse.";
SWWM_CSPY2 = "For being so enthusiastic about everything in this mod, and for being the cute little brother I never had.";
SWWM_CINSP1 = "Combine Kegan, HyperUltra64, TerminusEst13, Yholl and friends";
SWWM_CINSP2 = "For inspiration and also for being really cool people. Bless all of you, keep being amazing.";
SWWM_CCOMMUNITY1 = "All my amazing friends from the Doom community";
SWWM_CCOMMUNITY2 = "For helping me keep this whole thing rolling, and for all the time we spend together. Here's to many more years of Dooming, stay awesome, everyone!";
SWWM_CDEVS1 = "Randi, Graf, Rachael and the rest of the GZDoom dev team";
SWWM_CDEVS2 = "For your work on the source port that brought back my faith in modding. I don't care what others think, you're great.";
SWWM_CYOU1 = "You";
SWWM_CYOU2 = "For playing this mod, thank you so much.";
SWWM_CLOCAL = "Localization:";
SWWM_LOCDE = "German";
SWWM_LOCES = "Spanish";
SWWM_LOCFR = "French";
SWWM_LOCIT = "Italian";
SWWM_LOCJP = "Japanese";
SWWM_LOCPL = "Polish";
SWWM_LOCRU = "Russian";
// knowledge base
SWWM_COMINGSOON = "(coming soon)";
SWWM_MISSTAB = "Mission";

View file

@ -53,41 +53,6 @@ SWWM_SKEYS = "Compartir Llaves";
SWWM_SVARMORUSE = "Forzar Auto-Uso de Armadura";
SWWM_SVHEALTHUSE = "Forzar Auto-Uso de Salud";
SWWM_SVAMMOUSE = "Forzar Auto-Uso de Fabricadores";
SWWM_MCREDS = "Créditos de SWWM GZ";
SWWM_CLEAD = "Desarrollo Principal:";
SWWM_CDEV2 = "Autoproclamada Maga de ZScript";
SWWM_CASSETS = "Assets Adicionales:";
SWWM_ASSBARONS = "Sprites de XDeath para Nobles del Infierno";
SWWM_ASSOTHERS = "Sprites de XDeath para Demonio, Cacodemonio, Renacido, Archvile";
SWWM_ASSKEEN = "Reemplazo Menos Vil de Keen";
SWWM_CMUSIC = "Música Usada:";
SWWM_CFANART = "Fanart de Intermisión:";
SWWM_CPATRON = "Mecenas de Patreon:";
SWWM_CTHANK = "Agradecimientos Especiales:";
SWWM_CMAB2 = "Por ser una grandísima amiga que cree en mí, y por inspirarme a seguir trabajando en lo que me gusta. Me alegro muchísimo de haberte conocido.";
SWWM_CDRAGON2 = "Por ser un buen dragón mascota que se preocupa por mí.";
SWWM_CLUCY2 = "Por la fuente Tewi, que he seguido usando todos estos años. Espero que estés donde estés, te encuentres bien.";
SWWM_CKEKS2 = "Por libeye, asistencia con el código de manejo de excepciones, y también por ser un friki tan guay de Touhou.";
SWWM_CZN2 = "Por el código para alineación con superficies inclinadas, y a Nash además por ser un molón tío cactus listo.";
SWWM_CVAL2 = "Por las huellas personalizadas para Nashgore, y por ser una buena amiga y conejita mona.";
SWWM_CSLEDGE2 = "Por ser un gran colega, no solo conmigo si no con todos nosotros, y también por animarme a tomar parte en el modiverso de Doom.";
SWWM_CSPY2 = "Por el nivel de entusiasmo mostrado por todo en este mod, y por ser el adorable hermano menor que nunca tuve.";
SWWM_CINSP1 = "Combine Kegan, HyperUltra64, TerminusEst13, Yholl y amigos";
SWWM_CINSP2 = "Por servir de inspiración y por ser gente muy molona. Benditos seais todos, seguid siendo fenomenales.";
SWWM_CCOMMUNITY1 = "Todo mi súper colegueo de la comunidad de Doom";
SWWM_CCOMMUNITY2 = "Por ayudarme a seguir trabajando en esto, y por todo el tiempo que compartimos. Por muchos más años de Doom. ¡Os adoro!";
SWWM_CDEVS1 = "Randi, Graf, Rachael y el resto del equipo de desarrollo de GZDoom";
SWWM_CDEVS2 = "Por vuestro trabajo en el source port que me ha devuelto la fe en el modding. No me importa lo que piensen los demás, sois la caña.";
SWWM_CYOU1 = "Tú";
SWWM_CYOU2 = "Por jugar a este mod, muchísimas gracias.";
SWWM_CLOCAL = "Traducción:";
SWWM_LOCDE = "Alemán";
SWWM_LOCES = "Castellano";
SWWM_LOCFR = "Francés";
SWWM_LOCIT = "Italiano";
SWWM_LOCJP = "Japonés";
SWWM_LOCPL = "Polaco";
SWWM_LOCRU = "Ruso";
SWWM_FORCEDISABLE = "Forzar Desactivado";
SWWM_USERSET = "Elegido por Usuario";
SWWM_FORCEENABLE = "Forzar Activado";
@ -382,6 +347,43 @@ TOOLTIP_SWWM_MM_THINGCOLOR_CITEM = "Color para ítems que cuentan para el tanteo
TOOLTIP_SWWM_MM_THINGCOLOR_VIPITEM = "Color para ítems muy importantes.";
TOOLTIP_SWWM_MM_THINGCOLOR_SHOOTABLE = "Color para cosas que pueden ser dañadas.";
TOOLTIP_SWWM_MM_THINGCOLOR_MISSILE = "Color para proyectiles.";
// credits
SWWM_MCREDS = "Créditos de SWWM GZ";
SWWM_CLEAD = "Desarrollo Principal:";
SWWM_CDEV2 = "Autoproclamada Maga de ZScript";
SWWM_CASSETS = "Assets Adicionales:";
SWWM_ASSBARONS = "Sprites de XDeath para Nobles del Infierno";
SWWM_ASSOTHERS = "Sprites de XDeath para Demonio, Cacodemonio, Renacido, Archvile";
SWWM_ASSKEEN = "Reemplazo Menos Vil de Keen";
SWWM_CMUSIC = "Música Usada:";
SWWM_CFANART = "Fanart de Intermisión:";
SWWM_CPATRON = "Mecenas de Patreon:";
SWWM_CTHANK = "Agradecimientos Especiales:";
SWWM_CMAB2 = "Por ser una grandísima amiga que cree en mí, y por inspirarme a seguir trabajando en lo que me gusta. Me alegro muchísimo de haberte conocido.";
SWWM_CDRAGON2 = "Por ser un buen dragón mascota que se preocupa por mí.";
SWWM_CLUCY2 = "Por la fuente Tewi, que he seguido usando todos estos años. Espero que estés donde estés, te encuentres bien.";
SWWM_CGUTA2 = "Por la librería de Gutamatics, y por ayudarme a aprender ZScript.";
SWWM_CKEKS2 = "Por asistencia con el código de manejo de excepciones, y también por ser un friki tan guay de Touhou.";
SWWM_CZN2 = "Por el código para alineación con superficies inclinadas, y a Nash además por ser un molón tío cactus listo.";
SWWM_CVAL2 = "Por las huellas personalizadas para Nashgore, y por ser una buena amiga y conejita mona.";
SWWM_CSLEDGE2 = "Por ser grandes colegas, no solo conmigo si no con todos nosotros, y también por animarme a tomar parte en el modiverso de Doom.";
SWWM_CSPY2 = "Por el nivel de entusiasmo mostrado por todo en este mod, y por ser el adorable hermano menor que nunca tuve.";
SWWM_CINSP1 = "Combine Kegan, HyperUltra64, TerminusEst13, Yholl y amigos";
SWWM_CINSP2 = "Por servir de inspiración y por ser gente muy molona. Benditos seais todos, seguid siendo fenomenales.";
SWWM_CCOMMUNITY1 = "Todo mi súper colegueo de la comunidad de Doom";
SWWM_CCOMMUNITY2 = "Por ayudarme a seguir trabajando en esto, y por todo el tiempo que compartimos. Por muchos más años de Doom. ¡Os adoro!";
SWWM_CDEVS1 = "Randi, Graf, Rachael y el resto del equipo de desarrollo de GZDoom";
SWWM_CDEVS2 = "Por vuestro trabajo en el source port que me ha devuelto la fe en el modding. No me importa lo que piensen los demás, sois la caña.";
SWWM_CYOU1 = "Tú";
SWWM_CYOU2 = "Por jugar a este mod, muchísimas gracias.";
SWWM_CLOCAL = "Traducción:";
SWWM_LOCDE = "Alemán";
SWWM_LOCES = "Castellano";
SWWM_LOCFR = "Francés";
SWWM_LOCIT = "Italiano";
SWWM_LOCJP = "Japonés";
SWWM_LOCPL = "Polaco";
SWWM_LOCRU = "Ruso";
// knowledge base
SWWM_COMINGSOON = "(próximamente)";
SWWM_MISSTAB = "Misión";

View file

@ -1,3 +1,3 @@
[default]
SWWM_MODVER="\chSWWM \czGZ\c- \cw0.9.11b-pre r425 \cu(Fri 2 Apr 19:51:08 CEST 2021)\c-";
SWWM_SHORTVER="\cw0.9.11b-pre r425 \cu(2021-04-02 19:51:08)\c-";
SWWM_MODVER="\chSWWM \czGZ\c- \cw0.9.11b-pre r426 \cu(Fri 2 Apr 19:52:13 CEST 2021)\c-";
SWWM_SHORTVER="\cw0.9.11b-pre r426 \cu(2021-04-02 19:52:13)\c-";

View file

@ -8,12 +8,8 @@ version "4.5"
*/
// utility code
#include "zscript/swwm_libeye/projector.txt"
#include "zscript/swwm_libeye/projector gl.txt"
#include "zscript/swwm_libeye/projector planar.txt"
#include "zscript/swwm_libeye/viewport.txt"
#include "zscript/swwm_Gutamatics/Include.zsc"
#include "zscript/utility/swwm_coordutil.zsc"
#include "zscript/utility/swwm_quaternion.zsc"
#include "zscript/utility/swwm_utility.zsc"
// base code
#include "zscript/swwm_common.zsc"

View file

@ -43,12 +43,8 @@ Class SWWMStatusBar : BaseStatusBar
// shared from renderunderlay, needed for proper interpolation of some things
Vector3 viewpos, viewrot;
// libeye stuff
swwmLe__ProjScreen proj;
swwmLe__GLScreen gl_proj;
swwmLe__SWScreen sw_proj;
swwmLe__Viewport viewport;
bool can_project;
// projection data cache
SWWMProjectionData projdata;
DynamicValueInterpolator HealthInter, ScoreInter, ScoreInter2, FuelInter, DashInter;
@ -222,21 +218,6 @@ Class SWWMStatusBar : BaseStatusBar
return true;
}
void PrepareProjection()
{
switch ( vid_rendermode )
{
case 0:
case 1:
proj = sw_proj;
break;
default:
proj = gl_proj;
break;
}
can_project = proj;
}
private bool CmpTarget( SWWMCombatTracker a, SWWMCombatTracker b )
{
if ( !a || !b ) return true;
@ -384,7 +365,7 @@ Class SWWMStatusBar : BaseStatusBar
override void Tick()
{
Super.Tick();
PrepareProjection();
SWWMUtility.PrepareProjData(projdata,ViewPos,ViewRot.x,ViewRot.y,ViewRot.z,players[consoleplayer].fov);
int maxtargetnum = max(0,swwm_maxtargets);
int maxscorenum = max(0,swwm_maxscorenums);
int maxdamnum = max(0,swwm_maxdamnums);
@ -447,9 +428,10 @@ Class SWWMStatusBar : BaseStatusBar
// ignore points clearly outside of player view
Vector3 tdir = level.Vec3Diff(viewpos,poi.pos);
if ( viewvec dot tdir < 0 ) continue;
proj.ProjectWorldPos(viewpos+tdir);
proj.ProjectToNormal();
if ( !proj.IsInFront() || !proj.IsInScreen() ) continue;
Vector3 ndc = SWWMUtility.ProjectPoint(projdata,viewpos+tdir);
if ( ndc.z >= 1. ) continue;
Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc);
if ( !SWWMUtility.TestScreenBounds(projdata,vpos) ) continue;
intpoints[i++] = poi;
}
// squeeze if some were discarded
@ -478,9 +460,10 @@ Class SWWMStatusBar : BaseStatusBar
// ignore trackers clearly outside of player view
Vector3 tdir = level.Vec3Diff(viewpos,trk.pos);
if ( viewvec dot tdir < 0 ) continue;
proj.ProjectWorldPos(viewpos+tdir);
proj.ProjectToNormal();
if ( !proj.IsInFront() || !proj.IsInScreen() ) continue;
Vector3 ndc = SWWMUtility.ProjectPoint(projdata,viewpos+tdir);
if ( ndc.z >= 1. ) continue;
Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc);
if ( !SWWMUtility.TestScreenBounds(projdata,vpos) ) continue;
trackers[i++] = trk;
}
// squeeze if some were discarded
@ -513,9 +496,10 @@ Class SWWMStatusBar : BaseStatusBar
// ignore numbers clearly outside of player view
Vector3 tdir = level.Vec3Diff(viewpos,scr.pos);
if ( viewvec dot tdir < 0 ) continue;
proj.ProjectWorldPos(viewpos+tdir);
proj.ProjectToNormal();
if ( !proj.IsInFront() || !proj.IsInScreen() ) continue;
Vector3 ndc = SWWMUtility.ProjectPoint(projdata,viewpos+tdir);
if ( ndc.z >= 1. ) continue;
Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc);
if ( !SWWMUtility.TestScreenBounds(projdata,vpos) ) continue;
scoreobjs[i++] = scr;
}
}
@ -526,9 +510,10 @@ Class SWWMStatusBar : BaseStatusBar
// ignore numbers clearly outside of player view
Vector3 tdir = level.Vec3Diff(viewpos,scr.pos);
if ( viewvec dot tdir < 0 ) continue;
proj.ProjectWorldPos(viewpos+tdir);
proj.ProjectToNormal();
if ( !proj.IsInFront() || !proj.IsInScreen() ) continue;
Vector3 ndc = SWWMUtility.ProjectPoint(projdata,viewpos+tdir);
if ( ndc.z >= 1. ) continue;
Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc);
if ( !SWWMUtility.TestScreenBounds(projdata,vpos) ) continue;
scoreobjs[i++] = scr;
}
}
@ -546,13 +531,14 @@ Class SWWMStatusBar : BaseStatusBar
i = 0;
for ( SWWMItemSense s=demo.itemsense; s; s=s.next )
{
if ( !thesight && (level.maptime > s.updated+35) ) continue;
// ignore points clearly outside of player view
Vector3 tdir = level.Vec3Diff(viewpos,s.pos);
if ( viewvec dot tdir < 0 ) continue;
proj.ProjectWorldPos(viewpos+tdir);
proj.ProjectToNormal();
if ( !proj.IsInFront() || !proj.IsInScreen() ) continue;
if ( !thesight && (level.maptime > s.updated+35) ) continue;
Vector3 ndc = SWWMUtility.ProjectPoint(projdata,viewpos+tdir);
if ( ndc.z >= 1. ) continue;
Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc);
if ( !SWWMUtility.TestScreenBounds(projdata,vpos) ) continue;
senseitems[i++] = s;
}
// squeeze if some were discarded
@ -643,9 +629,6 @@ Class SWWMStatusBar : BaseStatusBar
ScoreInter2 = DynamicValueInterpolator.Create(0,.1,1,999999999);
FuelInter = DynamicValueInterpolator.Create(120,.5,1,100);
DashInter = DynamicValueInterpolator.Create(120,.5,1,40);
gl_proj = new("swwmLe__GLScreen");
sw_proj = new("swwmLe__SWScreen");
PrepareProjection();
hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
PrevFrame = MSTime();
}
@ -661,12 +644,9 @@ Class SWWMStatusBar : BaseStatusBar
{
// don't draw when dead or with automap open
if ( (CPlayer.health <= 0) || automapactive ) return;
viewport.FromHud();
proj.CacheResolution();
proj.CacheFov(players[consoleplayer].fov);
proj.Reorient(ViewPos,ViewRot);
proj.BeginProjection();
Vector3 vdir = (cos(ViewRot.x)*cos(ViewRot.y),sin(ViewRot.x)*cos(ViewRot.y),-sin(ViewRot.y));
SWWMUtility.PrepareProjData(projdata,ViewPos,ViewRot.x,ViewRot.y,ViewRot.z,players[consoleplayer].fov);
int cliptop = projdata.viewy, clipbottom = projdata.viewy+projdata.viewh,
clipleft = projdata.viewx, clipright = projdata.viewx+projdata.vieww;
bool thesight = !!CPlayer.mo.FindInventory("Omnisight");
// points of interest
String tag;
@ -676,11 +656,10 @@ Class SWWMStatusBar : BaseStatusBar
{
let poi = intpoints[i];
if ( !poi ) continue;
Vector3 tdir = Level.Vec3Diff(ViewPos,poi.pos);
proj.ProjectWorldPos(ViewPos+tdir);
Vector2 npos = proj.ProjectToNormal();
if ( !proj.IsInFront() ) continue;
Vector2 vpos = viewport.SceneToWindow(npos);
Vector3 tdir = level.Vec3Diff(ViewPos,poi.pos);
Vector3 ndc = SWWMUtility.ProjectPoint(projdata,ViewPos+tdir);
if ( ndc.z >= 1. ) continue;
Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc);
if ( poi.type == INT_Key ) tag = String.Format("\cf%s\c-",poi.keytag);
else if ( poi.type == INT_Exit )
{
@ -693,9 +672,9 @@ Class SWWMStatusBar : BaseStatusBar
else tag = String.Format("\cy%s\c-",StringTable.Localize("$SWWM_NEXIT"));
}
Font fnt = LangFont(mMiniwiFont);
Screen.DrawText(fnt,Font.CR_WHITE,(vpos.x-hsi.x*fnt.StringWidth(tag)/2.)/hsi.x,(vpos.y-hsi.y*fnt.GetHeight()/2.)/hsi.y,tag,DTA_VirtualWidthF,ssi.x,DTA_VirtualHeightF,ssi.y,DTA_KeepRatio,true);
Screen.DrawText(fnt,Font.CR_WHITE,(vpos.x-hsi.x*fnt.StringWidth(tag)/2.)/hsi.x,(vpos.y-hsi.y*fnt.GetHeight()/2.)/hsi.y,tag,DTA_VirtualWidthF,ssi.x,DTA_VirtualHeightF,ssi.y,DTA_KeepRatio,true,DTA_ClipTop,cliptop,DTA_ClipBottom,clipbottom,DTA_ClipLeft,clipleft,DTA_ClipRight,clipright);
tag = String.Format("\cu(%s\cu)\c-",FormatDist(tdir.length()));
Screen.DrawText(fnt,Font.CR_WHITE,(vpos.x-hsi.x*fnt.StringWidth(tag)/2.)/hsi.x,(vpos.y+hsi.y*fnt.GetHeight()/2.)/hsi.y,tag,DTA_VirtualWidthF,ssi.x,DTA_VirtualHeightF,ssi.y,DTA_KeepRatio,true);
Screen.DrawText(fnt,Font.CR_WHITE,(vpos.x-hsi.x*fnt.StringWidth(tag)/2.)/hsi.x,(vpos.y+hsi.y*fnt.GetHeight()/2.)/hsi.y,tag,DTA_VirtualWidthF,ssi.x,DTA_VirtualHeightF,ssi.y,DTA_KeepRatio,true,DTA_ClipTop,cliptop,DTA_ClipBottom,clipbottom,DTA_ClipLeft,clipleft,DTA_ClipRight,clipright);
}
}
// sensed items
@ -706,19 +685,18 @@ Class SWWMStatusBar : BaseStatusBar
{
let s = senseitems[i];
if ( !s || !s.item ) continue;
Vector3 tdir = Level.Vec3Diff(ViewPos,s.pos);
proj.ProjectWorldPos(ViewPos+tdir);
Vector2 npos = proj.ProjectToNormal();
if ( !proj.IsInFront() ) continue;
Vector2 vpos = viewport.SceneToWindow(npos);
Vector3 tdir = level.Vec3Diff(ViewPos,s.pos);
Vector3 ndc = SWWMUtility.ProjectPoint(projdata,ViewPos+tdir);
if ( ndc.z >= 1. ) continue;
Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc);
Font fnt = LangFont(mMiniwiFont);
int mtime = thesight?70:35;
double alph = clamp(((s.updated+mtime)-level.maptime)/35.,0.,1.);
alph *= clamp(1.5-1.5*(tdir.length()/(thesight?1200.:800.)),0.,1.);
tag = s.tag;
Screen.DrawText(fnt,s.vipitem?Font.CR_PURPLE:s.scoreitem?Font.CR_GOLD:Font.CR_GREEN,(vpos.x-hsd.x*fnt.StringWidth(tag)/2.)/hsd.x,(vpos.y-hsd.y*fnt.GetHeight()/2.)/hsd.y,tag,DTA_VirtualWidthF,ssd.x,DTA_VirtualHeightF,ssd.y,DTA_KeepRatio,true,DTA_Alpha,alph);
Screen.DrawText(fnt,s.vipitem?Font.CR_PURPLE:s.scoreitem?Font.CR_GOLD:Font.CR_GREEN,(vpos.x-hsd.x*fnt.StringWidth(tag)/2.)/hsd.x,(vpos.y-hsd.y*fnt.GetHeight()/2.)/hsd.y,tag,DTA_VirtualWidthF,ssd.x,DTA_VirtualHeightF,ssd.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_ClipTop,cliptop,DTA_ClipBottom,clipbottom,DTA_ClipLeft,clipleft,DTA_ClipRight,clipright);
tag = String.Format("\cu(%s\cu)\c-",FormatDist(tdir.length()));
Screen.DrawText(fnt,Font.CR_WHITE,(vpos.x-hsd.x*fnt.StringWidth(tag)/2.)/hsd.x,(vpos.y+hsd.y*fnt.GetHeight()/2.)/hsd.y,tag,DTA_VirtualWidthF,ssd.x,DTA_VirtualHeightF,ssd.y,DTA_KeepRatio,true,DTA_Alpha,alph);
Screen.DrawText(fnt,Font.CR_WHITE,(vpos.x-hsd.x*fnt.StringWidth(tag)/2.)/hsd.x,(vpos.y+hsd.y*fnt.GetHeight()/2.)/hsd.y,tag,DTA_VirtualWidthF,ssd.x,DTA_VirtualHeightF,ssd.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_ClipTop,cliptop,DTA_ClipBottom,clipbottom,DTA_ClipLeft,clipleft,DTA_ClipRight,clipright);
}
}
// targetting array
@ -726,11 +704,9 @@ Class SWWMStatusBar : BaseStatusBar
{
let targ = trackers[i];
if ( !targ ) continue;
Vector3 tdir = Level.Vec3Diff(ViewPos,targ.prevpos*(1.-fractic)+targ.pos*fractic);
proj.ProjectWorldPos(ViewPos+tdir);
Vector2 npos = proj.ProjectToNormal();
if ( !proj.IsInFront() ) continue;
Vector2 vpos = viewport.SceneToWindow(npos);
Vector3 ndc = SWWMUtility.ProjectPoint(projdata,ViewPos+level.Vec3Diff(ViewPos,targ.prevpos*(1.-fractic)+targ.pos*fractic));
if ( ndc.z >= 1. ) continue;
Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc);
tag = targ.mytag;
if ( tag != "" )
{
@ -752,30 +728,30 @@ Class SWWMStatusBar : BaseStatusBar
barpos.y -= 16.;
Font fnt = LangFont(swwm_bigtags?mTewiFont:mMiniwiFont);
if ( swwm_targettags || targ.myplayer && (tag != "") )
Screen.DrawText(fnt,Font.CR_WHITE,(barpos.x+barsiz.x/2.-(fnt.StringWidth(tag)*hsb.x)/2.)/hsb.x,(barpos.y-fnt.GetHeight()*hsb.y)/hsb.y,tag,DTA_VirtualWidthF,ssb.x,DTA_VirtualHeightF,ssb.y,DTA_KeepRatio,true,DTA_Alpha,alph);
Screen.DrawTexture(EnemyBTex,false,barpos.x/hsb.x,barpos.y/hsb.y,DTA_VirtualWidthF,ssb.x,DTA_VirtualHeightF,ssb.y,DTA_KeepRatio,true,DTA_Alpha,alph);
Screen.DrawText(fnt,Font.CR_WHITE,(barpos.x+barsiz.x/2.-(fnt.StringWidth(tag)*hsb.x)/2.)/hsb.x,(barpos.y-fnt.GetHeight()*hsb.y)/hsb.y,tag,DTA_VirtualWidthF,ssb.x,DTA_VirtualHeightF,ssb.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_ClipTop,cliptop,DTA_ClipBottom,clipbottom,DTA_ClipLeft,clipleft,DTA_ClipRight,clipright);
Screen.DrawTexture(EnemyBTex,false,barpos.x/hsb.x,barpos.y/hsb.y,DTA_VirtualWidthF,ssb.x,DTA_VirtualHeightF,ssb.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_ClipTop,cliptop,DTA_ClipBottom,clipbottom,DTA_ClipLeft,clipleft,DTA_ClipRight,clipright);
double ht = clamp(targ.intp.GetValue(),0,targ.maxhealth*10);
double hw = (min(ht,targ.maxhealth)*50.)/targ.maxhealth;
if ( targ.mytarget && (targ.mytarget.bInvulnerable || (targ.myplayer && (targ.myplayer.cheats&(CF_GODMODE|CF_GODMODE2))) || targ.mytarget.FindInventory("InvinciballPower")) )
{
Screen.DrawTexture(EnemyHTex[4],false,(barpos.x+2*hsb.x)/hsb.x,(barpos.y+2*hsb.y)/hsb.y,DTA_VirtualWidthF,ssb.x,DTA_VirtualHeightF,ssb.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw);
Screen.DrawTexture(EnemyHTex[4],false,(barpos.x+2*hsb.x)/hsb.x,(barpos.y+2*hsb.y)/hsb.y,DTA_VirtualWidthF,ssb.x,DTA_VirtualHeightF,ssb.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw,DTA_ClipTop,cliptop,DTA_ClipBottom,clipbottom,DTA_ClipLeft,clipleft,DTA_ClipRight,clipright);
continue;
}
Screen.DrawTexture(EnemyHTex[0],false,(barpos.x+2*hsb.x)/hsb.x,(barpos.y+2*hsb.y)/hsb.y,DTA_VirtualWidthF,ssb.x,DTA_VirtualHeightF,ssb.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw);
Screen.DrawTexture(EnemyHTex[0],false,(barpos.x+2*hsb.x)/hsb.x,(barpos.y+2*hsb.y)/hsb.y,DTA_VirtualWidthF,ssb.x,DTA_VirtualHeightF,ssb.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw,DTA_ClipTop,cliptop,DTA_ClipBottom,clipbottom,DTA_ClipLeft,clipleft,DTA_ClipRight,clipright);
if ( ht > targ.maxhealth )
{
hw = (min(ht-targ.maxhealth,targ.maxhealth)*50.)/targ.maxhealth;
Screen.DrawTexture(EnemyHTex[1],false,(barpos.x+2*hsb.x)/hsb.x,(barpos.y+2*hsb.y)/hsb.y,DTA_VirtualWidthF,ssb.x,DTA_VirtualHeightF,ssb.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw);
Screen.DrawTexture(EnemyHTex[1],false,(barpos.x+2*hsb.x)/hsb.x,(barpos.y+2*hsb.y)/hsb.y,DTA_VirtualWidthF,ssb.x,DTA_VirtualHeightF,ssb.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw,DTA_ClipTop,cliptop,DTA_ClipBottom,clipbottom,DTA_ClipLeft,clipleft,DTA_ClipRight,clipright);
}
if ( ht > targ.maxhealth*2 )
{
hw = (min(ht-targ.maxhealth*2,targ.maxhealth*3)*50.)/(targ.maxhealth*3);
Screen.DrawTexture(EnemyHTex[2],false,(barpos.x+2*hsb.x)/hsb.x,(barpos.y+2*hsb.y)/hsb.y,DTA_VirtualWidthF,ssb.x,DTA_VirtualHeightF,ssb.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw);
Screen.DrawTexture(EnemyHTex[2],false,(barpos.x+2*hsb.x)/hsb.x,(barpos.y+2*hsb.y)/hsb.y,DTA_VirtualWidthF,ssb.x,DTA_VirtualHeightF,ssb.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw,DTA_ClipTop,cliptop,DTA_ClipBottom,clipbottom,DTA_ClipLeft,clipleft,DTA_ClipRight,clipright);
}
if ( ht > targ.maxhealth*5 )
{
hw = (min(ht-targ.maxhealth*5,targ.maxhealth*5)*50.)/(targ.maxhealth*5);
Screen.DrawTexture(EnemyHTex[3],false,(barpos.x+2*hsb.x)/hsb.x,(barpos.y+2*hsb.y)/hsb.y,DTA_VirtualWidthF,ssb.x,DTA_VirtualHeightF,ssb.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw);
Screen.DrawTexture(EnemyHTex[3],false,(barpos.x+2*hsb.x)/hsb.x,(barpos.y+2*hsb.y)/hsb.y,DTA_VirtualWidthF,ssb.x,DTA_VirtualHeightF,ssb.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_WindowRightF,hw,DTA_ClipTop,cliptop,DTA_ClipBottom,clipbottom,DTA_ClipLeft,clipleft,DTA_ClipRight,clipright);
}
}
// floating kill scores and others
@ -783,11 +759,9 @@ Class SWWMStatusBar : BaseStatusBar
{
let snum = scoreobjs[i];
if ( !snum ) continue;
Vector3 tdir = Level.Vec3Diff(ViewPos,snum.pos);
proj.ProjectWorldPos(ViewPos+tdir);
Vector2 npos = proj.ProjectToNormal();
if ( !proj.IsInFront() ) continue;
Vector2 vpos = viewport.SceneToWindow(npos);
Vector3 ndc = SWWMUtility.ProjectPoint(projdata,ViewPos+level.Vec3Diff(ViewPos,snum.pos));
if ( ndc.z >= 1. ) continue;
Vector2 vpos = SWWMUtility.NDCToViewport(projdata,ndc);
tag = String.Format("%+d",snum.score);
double alph = clamp((snum.lifespan+fractic)/35.,0.,1.);
Vector2 fo = (0,0);
@ -820,7 +794,7 @@ Class SWWMStatusBar : BaseStatusBar
Font fnt = mMiniwiFont.mFont;
Vector2 hs0 = isscore?hss:hsn;
Vector2 ss0 = isscore?sss:ssn;
Screen.DrawText(fnt,snum.tcolor,(vpos.x-hs0.x*(fo.x+fnt.StringWidth(tag)/2.))/hs0.x,(vpos.y-hs0.y*(fo.y+(fnt.GetHeight()/2.)))/hs0.y,tag,DTA_VirtualWidthF,ss0.x,DTA_VirtualHeightF,ss0.y,DTA_KeepRatio,true,DTA_Alpha,alph);
Screen.DrawText(fnt,snum.tcolor,(vpos.x-hs0.x*(fo.x+fnt.StringWidth(tag)/2.))/hs0.x,(vpos.y-hs0.y*(fo.y+(fnt.GetHeight()/2.)))/hs0.y,tag,DTA_VirtualWidthF,ss0.x,DTA_VirtualHeightF,ss0.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_ClipTop,cliptop,DTA_ClipBottom,clipbottom,DTA_ClipLeft,clipleft,DTA_ClipRight,clipright);
// extra strings (if available)
if ( !swwm_scorebonus ) continue;
fo.y += fnt.GetHeight();
@ -830,7 +804,7 @@ Class SWWMStatusBar : BaseStatusBar
tag = snum.xstr[i];
if ( snum.xscore[i] == int.max ) tag.AppendFormat(" MAX");
else if ( snum.xscore[i] > 0 ) tag.AppendFormat(" x%d",snum.xscore[i]);
Screen.DrawText(fnt,snum.xtcolor[i],(vpos.x-hss.x*(fo.x+fnt.StringWidth(tag)/2.))/hss.x,(vpos.y-hss.y*(fo.y+(fnt.GetHeight()/2.)))/hss.y,tag,DTA_VirtualWidthF,sss.x,DTA_VirtualHeightF,sss.y,DTA_KeepRatio,true,DTA_Alpha,alph);
Screen.DrawText(fnt,snum.xtcolor[i],(vpos.x-hss.x*(fo.x+fnt.StringWidth(tag)/2.))/hss.x,(vpos.y-hss.y*(fo.y+(fnt.GetHeight()/2.)))/hss.y,tag,DTA_VirtualWidthF,sss.x,DTA_VirtualHeightF,sss.y,DTA_KeepRatio,true,DTA_Alpha,alph,DTA_ClipTop,cliptop,DTA_ClipBottom,clipbottom,DTA_ClipLeft,clipleft,DTA_ClipRight,clipright);
fo.y += fnt.GetHeight();
}
}

View file

@ -199,10 +199,11 @@ Class SWWMCreditsMenu : GenericMenu
cthanks.Push(new("SWWMCreditsEntry").Init(sfnt,"Marrub","$SWWM_CMAB2",Font.FindFontColor('Heliotrope'),Font.FindFontColor('BlushPink')));
cthanks.Push(new("SWWMCreditsEntry").Init(sfnt,"KynikossDragonn","$SWWM_CDRAGON2"));
cthanks.Push(new("SWWMCreditsEntry").Init(sfnt,"Lucy","$SWWM_CLUCY2"));
cthanks.Push(new("SWWMCreditsEntry").Init(sfnt,"Gutawer","$SWWM_CGUTA2"));
cthanks.Push(new("SWWMCreditsEntry").Init(sfnt,"KeksDose","$SWWM_CKEKS2"));
cthanks.Push(new("SWWMCreditsEntry").Init(sfnt,"ZZYZX & Nash","$SWWM_CZN2"));
cthanks.Push(new("SWWMCreditsEntry").Init(sfnt,"Val Pal","$SWWM_CVAL2"));
cthanks.Push(new("SWWMCreditsEntry").Init(sfnt,"CptSledge","$SWWM_CSLEDGE2"));
cthanks.Push(new("SWWMCreditsEntry").Init(sfnt,"\ctCptSledge\c- & \cdBunray\c-","$SWWM_CSLEDGE2"));
cthanks.Push(new("SWWMCreditsEntry").Init(sfnt,"a1337spy","$SWWM_CSPY2"));
cthanks.Push(new("SWWMCreditsEntry").Init(sfnt,"$SWWM_CINSP1","$SWWM_CINSP2"));
cthanks.Push(new("SWWMCreditsEntry").Init(sfnt,"$SWWM_CCOMMUNITY1","$SWWM_CCOMMUNITY2"));

View file

@ -0,0 +1,177 @@
class swwm_GlobalMaths {
/// Returns the sign of s.
static int sign(double s) {
if (s > 0) return 1;
if (s < 0) return -1;
return 0;
}
/// Copies the sign from signSource to source.
static int copySignInt(int source, int signSource) {
return abs(source) * sign(signSource);
}
/// Copies the sign from signSource to source.
static double copySignDouble(double source, double signSource) {
return abs(source) * sign(signSource);
}
/// Remaps a value in a range to another range.
static double remapRange(double value, double range1L, double range1H, double range2L, double range2H) {
return range2L + (value - range1L) * (range2H - range1H) / (range1H - range1L);
}
/// Remaps a value in a range to another range.
static int remapRangeInt(int value, int range1L, int range1H, int range2L, int range2H) {
return int(range2L + (value - range1L) * (range2H - range1H) / double(range1H - range1L));
}
/// Returns if two values are close enough to be considered equal.
static bool closeEnough(double a, double b, double epsilon = double.epsilon) {
if (a == b) return true;
return abs(a - b) <= epsilon;
}
// Creates a smoothed transition between edge0 and edge1.
static double smoothStep(double x, double edge0 = 0, double edge1 = 1) {
x = clamp((x - edge0) / (edge1 - edge0), 0, 1);
return x * x * (3 - 2 * x);
}
// Creates a smoother transition between edge0 and edge1.
static double smootherStep(double x, double edge0 = 0, double edge1 = 1) {
x = clamp((x - edge0) / (edge1 - edge0), 0, 1);
return x * x * x * (x * (x * 6 - 15) + 10);
}
/// Converts from horizontal FOV to vertical FOV, according to how GZDoom handles it.
static double fovHToY(double fovH) {
// this is how gzdoom does it internally, so i'm using it here
double aspect = Screen.getAspectRatio();
double fovratio = (aspect >= 1.3) ? 1.333333 : aspect;
return 2 * atan(tan(clamp(fovH, 5, 170) / 2.0) / fovratio);
}
/// Linearly interpolates between two doubles, clamping the parameters.
static double lerpDouble(double from, double to, double time) {
time = clamp(time, 0, 1);
return lerpUnclampedDouble(from, to, time);
}
/// Linearly interpolates between two doubles.
static double lerpUnclampedDouble(double from, double to, double time) {
double ret;
double reverseTime = 1 - time;
ret = reverseTime * from + time * to;
return ret;
}
// Converts from Normalised Device Coordinates to Viewport coordinates.
static Vector2 ndcToViewport(Vector3 ndcCoords, bool useScreenblocks = true, PlayerInfo player = NULL) {
// player is needed for screenblocks
if (useScreenblocks) {
if (player == NULL) {
player = players[consoleplayer];
}
int viewwindowx, viewwindowy, viewwidth, viewheight;
[viewwindowx, viewwindowy, viewwidth, viewheight] = Screen.getViewWindow();
int screenHeight = Screen.getHeight();
int height = screenHeight;
int screenblocks = CVar.getCVar("screenblocks", player).getInt();
if (screenblocks < 10) {
height = (screenblocks * screenHeight / 10) & ~7;
}
int bottom = screenHeight - (height + viewwindowy - ((height - viewheight) / 2));
return (viewwindowx, screenHeight - bottom - height) + (((ndcCoords.x + 1) * viewwidth) / 2, ((-ndcCoords.y + 1) * height) / 2);
}
else {
return (((ndcCoords.x + 1) * Screen.getWidth()) / 2, ((-ndcCoords.y + 1) * Screen.getHeight()) / 2);
}
}
enum OutCode {
OUT_Inside = 0,
OUT_Left = 1 << 0,
OUT_Right = 1 << 1,
OUT_Bottom = 1 << 2,
OUT_Top = 1 << 3
}
/// Computes an outcode for a point in a rectangle.
static OutCode computeOutcode(Vector2 point, Vector2 min, Vector2 max) {
OutCode code = OUT_Inside;
if (point.x < min.x) {
code |= OUT_Left;
}
else if (point.x > max.x) {
code |= OUT_Right;
}
if (point.y < min.y) {
code |= OUT_Top;
}
else if (point.y > max.y) {
code |= OUT_Bottom;
}
return code;
}
/// Clips a line to a rectangle.
static bool, Vector2, Vector2 cohenSutherlandClip(Vector2 point0, Vector2 point1, Vector2 min, Vector2 max) {
OutCode outcode0 = computeOutCode(point0, min, max);
OutCode outcode1 = computeOutCode(point1, min, max);
while (true) {
// trivial accept - points are both on screen
if ((outcode0 | outcode1) == 0) {
return true, point0, point1;
}
// trivial reject - points are in the same region offscreen
else if ((outcode0 & outcode1) != 0) {
return false, point0, point1;
}
else {
Vector2 new;
OutCode outcodeOut = (outcode0 != 0) ? outcode0 : outcode1;
if ((outcodeOut & OUT_Bottom) != 0) {
new.x = point0.x + (point1.x - point0.x) * (max.y - point0.y) / (point1.y - point0.y);
new.y = max.y;
}
else if ((outcodeOut & OUT_Top) != 0) {
new.x = point0.x + (point1.x - point0.x) * (min.y - point0.y) / (point1.y - point0.y);
new.y = min.y;
}
else if ((outcodeOut & OUT_Right) != 0) {
new.y = point0.y + (point1.y - point0.y) * (max.x - point0.x) / (point1.x - point0.x);
new.x = max.x;
}
else if ((outcodeOut & OUT_Left) != 0) {
new.y = point0.y + (point1.y - point0.y) * (min.x - point0.x) / (point1.x - point0.x);
new.x = min.x;
}
if (outcodeOut == outCode0) {
point0.x = new.x;
point0.y = new.y;
outCode0 = computeOutCode(point0, min, max);
}
else {
point1.x = new.x;
point1.y = new.y;
outCode1 = computeOutCode(point1, min, max);
}
}
}
return false, (0, 0), (0, 0);
}
// Normalizes an angle to (-180, 180]. Like Actor.normalize180, but callable in data scope.
static double normalize180(double ang) {
ang = ang % 360;
ang = (ang + 360) % 360;
if (ang > 180) ang -= 360;
return ang;
}
}

View file

@ -0,0 +1,4 @@
#include "zscript/swwm_Gutamatics/GlobalMaths.zsc"
#include "zscript/swwm_Gutamatics/Matrix.zsc"
#include "zscript/swwm_Gutamatics/Quaternion.zsc"
#include "zscript/swwm_Gutamatics/VectorUtil.zsc"

View file

@ -0,0 +1,7 @@
Copyright 2020 Jessica Russell
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -0,0 +1,334 @@
enum swwm_VectorType {
swwm_Vector_Position,
swwm_Vector_Direction
}
class swwm_Matrix {
private Array<double> values;
private int columns;
private int rows;
/// Initialises a new Matrix.
swwm_Matrix init(int columns, int rows) {
if (columns <= 0 || rows <= 0) {
throwAbortException("Error: <%p>.init(%d, %d) - Matrix needs to be at least 1 * 1", self, columns, rows);
}
self.rows = rows;
self.columns = columns;
values.resize(columns * rows);
for (int i = 0; i < values.size(); i++) {
values[i] = 0;
}
return self;
}
/// Initialises a new Matrix in a static context.
static swwm_Matrix create(int columns, int rows) {
return new("swwm_Matrix").init(columns, rows);
}
/// Returns an identity matrix.
static swwm_Matrix identity(int dimension) {
swwm_Matrix ret = swwm_Matrix.create(dimension, dimension);
for (int i = 0; i < dimension; i++) {
ret.set(i, i, 1);
}
return ret;
}
/// Returns a rotation matrix from euler angles.
static swwm_Matrix fromEulerAngles(double yaw, double pitch, double roll) {
swwm_Matrix rYaw = swwm_Matrix.identity(4);
double sYaw = sin(yaw);
double cYaw = cos(yaw);
rYaw.set(0, 0, cYaw);
rYaw.set(1, 0, -sYaw);
rYaw.set(0, 1, sYaw);
rYaw.set(1, 1, cYaw);
swwm_Matrix rPitch = swwm_Matrix.identity(4);
double sPitch = sin(pitch);
double cPitch = cos(pitch);
rPitch.set(0, 0, cPitch);
rPitch.set(0, 2, -sPitch);
rPitch.set(2, 0, sPitch);
rPitch.set(2, 2, cPitch);
swwm_Matrix rRoll = swwm_Matrix.identity(4);
double sRoll = sin(roll);
double cRoll = cos(roll);
rRoll.set(1, 1, cRoll);
rRoll.set(2, 1, -sRoll);
rRoll.set(1, 2, sRoll);
rRoll.set(2, 2, cRoll);
// concatenate ypr to get the final matrix
swwm_Matrix ret = rYaw.multiplyMatrix(rPitch);
ret = ret.multiplyMatrix(rRoll);
return ret;
}
/// Returns a rotation matrix from an axis and an angle.
static swwm_Matrix fromAxisAngle(Vector3 axis, double angle) {
swwm_Matrix ret = swwm_Matrix.identity(4);
double c = cos(angle);
double s = sin(angle);
double x = axis.x;
double y = axis.y;
double z = axis.z;
ret.set(0, 0, (x * x * (1.0 - c) + c));
ret.set(1, 0, (x * y * (1.0 - c) - z * s));
ret.set(2, 0, (x * z * (1.0 - c) + y * s));
ret.set(0, 1, (y * x * (1.0 - c) + z * s));
ret.set(1, 1, (y * y * (1.0 - c) + c));
ret.set(2, 1, (y * z * (1.0 - c) - x * s));
ret.set(0, 2, (x * z * (1.0 - c) - y * s));
ret.set(1, 2, (y * z * (1.0 - c) + x * s));
ret.set(2, 2, (z * z * (1.0 - c) + c));
return ret;
}
/// Converts back from the rotation matrix to euler angles.
double, double, double rotationToEulerAngles() {
if (swwm_GlobalMaths.closeEnough(get(0, 2), -1)) {
double x = 90;
double y = 0;
double z = atan2(get(1, 0), get(2, 0));
return z, x, y;
}
else if (swwm_GlobalMaths.closeEnough(get(0, 2), 1)) {
double x = -90;
double y = 0;
double z = atan2(-get(1, 0), -get(2, 0));
return z, x, y;
}
else {
float x1 = -asin(get(0, 2));
float x2 = 180 - x1;
float y1 = atan2(get(1, 2) / cos(x1), get(2, 2) / cos(x1));
float y2 = atan2(get(1, 2) / cos(x2), get(2, 2) / cos(x2));
float z1 = atan2(get(0, 1) / cos(x1), get(0, 0) / cos(x1));
float z2 = atan2(get(0, 1) / cos(x2), get(0, 0) / cos(x2));
if ((abs(x1) + abs(y1) + abs(z1)) <= (abs(x2) + abs(y2) + abs(z2))) {
return z1, x1, y1;
}
else {
return z2, x2, y2;
}
}
}
static swwm_Matrix createTRSEuler(Vector3 translate, double yaw, double pitch, double roll, Vector3 scale) {
swwm_Matrix translateMat = swwm_Matrix.identity(4);
translateMat.set(3, 0, translate.x);
translateMat.set(3, 1, translate.y);
translateMat.set(3, 2, translate.z);
swwm_Matrix rotateMat = swwm_Matrix.fromEulerAngles(yaw, pitch, roll);
swwm_Matrix scaleMat = swwm_Matrix.identity(4);
scaleMat.set(0, 0, scale.x);
scaleMat.set(1, 1, scale.y);
scaleMat.set(2, 2, scale.z);
swwm_Matrix ret = translateMat.multiplyMatrix(rotateMat);
ret = ret.multiplyMatrix(scaleMat);
return ret;
}
static swwm_Matrix createTRSAxisAngle(Vector3 translate, Vector3 axis, double angle, Vector3 scale) {
swwm_Matrix translateMat = swwm_Matrix.identity(4);
translateMat.set(3, 0, translate.x);
translateMat.set(3, 1, translate.y);
translateMat.set(3, 2, translate.z);
swwm_Matrix rotateMat = swwm_Matrix.fromAxisAngle(axis, angle);
swwm_Matrix scaleMat = swwm_Matrix.identity(4);
scaleMat.set(0, 0, scale.x);
scaleMat.set(1, 1, scale.y);
scaleMat.set(2, 2, scale.z);
swwm_Matrix ret = translateMat.multiplyMatrix(rotateMat);
ret = ret.multiplyMatrix(scaleMat);
return ret;
}
/// Returns a view matrix.
static swwm_Matrix view(Vector3 camPos, double yaw, double pitch, double roll) {
// all of this is basically lifted and converted from PolyRenderer::SetupPerspectiveMatrix(),
// so credit goes to Graf Zahl/dpJudas/whoever else
// pitch needs to be adjusted by the pixel ratio
float pixelRatio = level.pixelstretch;
double angx = cos(pitch);
double angy = sin(pitch) * pixelRatio;
double alen = sqrt(angx * angx + angy * angy);
double adjustedPitch = asin(angy / alen);
double adjustedYaw = yaw - 90;
// rotations
swwm_Matrix rotR = swwm_Matrix.fromAxisAngle((0, 0, 1), roll);
swwm_Matrix rotP = swwm_Matrix.fromAxisAngle((1, 0, 0), adjustedPitch);
swwm_Matrix rotY = swwm_Matrix.fromAxisAngle((0, -1, 0), adjustedYaw);
// pixel ratio scaling
swwm_Matrix scale = swwm_Matrix.identity(4);
scale.set(1, 1, pixelRatio);
// swapping y and z
swwm_Matrix swapYZ = swwm_Matrix.create(4, 4);
swapYZ.set(0, 0, 1);
swapYZ.set(2, 1, 1);
swapYZ.set(1, 2, -1);
swapYZ.set(3, 3, 1);
// translation
swwm_Matrix translate = swwm_Matrix.identity(4);
translate.set(3, 0, -camPos.x);
translate.set(3, 1, -camPos.y);
translate.set(3, 2, -camPos.z);
// concatenate them all to get a final matrix
swwm_Matrix ret = rotR.multiplyMatrix(rotP);
ret = ret.multiplyMatrix(rotY);
ret = ret.multiplyMatrix(scale);
ret = ret.multiplyMatrix(swapYZ);
ret = ret.multiplyMatrix(translate);
return ret;
}
/// Returns a perspective matrix (same format as gluPerspective).
static swwm_Matrix perspective(double fovy, double aspect, double zNear, double zFar) {
swwm_Matrix ret = swwm_Matrix.create(4, 4);
double f = 1 / tan(fovy / 2.0);
// x coord
ret.set(0, 0, f / aspect);
// y coord
ret.set(1, 1, f);
// z buffer coord
ret.set(2, 2, (zFar + zNear) / (zNear - zFar));
ret.set(3, 2, (2 * zFar * zNear) / (zNear - zFar));
// w (homogeneous coordinates)
ret.set(2, 3, -1);
return ret;
}
/// Returns a world->clip coords matrix from the passed args.
static swwm_Matrix worldToClip(Vector3 viewPos, double yaw, double pitch, double roll, double FOV) {
double aspect = Screen.getAspectRatio();
double fovy = swwm_GlobalMaths.fovHToY(FOV);
swwm_Matrix view = swwm_Matrix.view(viewPos, yaw, pitch, roll);
// 5 & 65535 are what are used internally, so they're used here for consistency
swwm_Matrix perp = swwm_Matrix.perspective(fovy, aspect, 5, 65535);
swwm_Matrix worldToClip = perp.multiplyMatrix(view);
return worldToClip;
}
/// Gets the value at col, row.
double get(int col, int row) const {
return values[columns * row + col];
}
/// Sets the value at col, row.
void set(int col, int row, double val) {
values[columns * row + col] = val;
}
/// Adds two matrices and returns the result.
swwm_Matrix addMatrix(swwm_Matrix other) const {
if (rows != other.rows || columns != other.columns) {
throwAbortException("Error: <%p>.addMatrix(<%p>) - Matrices need to be equal size", self, other);
}
swwm_Matrix ret = swwm_Matrix.create(columns, rows);
for (int row = 0; row < rows; row++) {
for (int col = 0; col < columns; col++) {
ret.set(col, row, get(col, row) + other.get(col, row));
}
}
return ret;
}
/// Multiplies the matrix by a scalar and returns the result.
swwm_Matrix multiplyScalar(double scalar) const {
swwm_Matrix ret = swwm_Matrix.create(rows, columns);
for (int row = 0; row < rows; row++) {
for (int col = 0; col < columns; col++) {
ret.set(col, row, get(col, row) * scalar);
}
}
return ret;
}
/// Multiplies two matrices and returns the result.
swwm_Matrix multiplyMatrix(swwm_Matrix other) const {
if (columns != other.rows) {
throwAbortException("Error: <%p>.multiplyMatrix(<%p>) - Matrix A columns needs to equal Matrix B rows", self, other);
}
swwm_Matrix ret = swwm_Matrix.create(other.columns, rows);
for (int row = 0; row < ret.rows; row++) {
for (int col = 0; col < ret.columns; col++) {
double val = 0;
for (int i = 0; i < columns; i++) {
val += get(i, row) * other.get(col, i);
}
ret.set(col, row, val);
}
}
return ret;
}
/// Multiplies this Matrix by a 2D vector.
swwm_Matrix multiplyVector2(Vector2 vec, swwm_VectorType type = swwm_Vector_Position) const {
swwm_Matrix vec2Matrix = swwm_Matrix.create(1, 3);
vec2Matrix.set(0, 0, vec.x);
vec2Matrix.set(0, 1, vec.y);
if (type == swwm_Vector_Position) vec2Matrix.set(0, 2, 1);
else if (type == swwm_Vector_Direction) vec2Matrix.set(0, 2, 0);
else throwAbortException("Error: Invalid vector type for multiplyVector2 (%d)", type);
return multiplyMatrix(vec2Matrix);
}
/// Multiplies this Matrix by a 3D vector.
swwm_Matrix multiplyVector3(Vector3 vec, swwm_VectorType type = swwm_Vector_Position) const {
swwm_Matrix vec3Matrix = swwm_Matrix.create(1, 4);
vec3Matrix.set(0, 0, vec.x);
vec3Matrix.set(0, 1, vec.y);
vec3Matrix.set(0, 2, vec.z);
if (type == swwm_Vector_Position) vec3Matrix.set(0, 3, 1);
else if (type == swwm_Vector_Direction) vec3Matrix.set(0, 3, 0);
else throwAbortException("Error: Invalid vector type for multiplyVector3 (%d)", type);
return multiplyMatrix(vec3Matrix);
}
/// Returns the Matrix in Vector2 form, optionally dividing by z.
Vector2 asVector2(bool divideZ = true) const {
if (columns != 1 || rows != 3) {
throwAbortException("Error: <%p>.asVector2() - Matrix needs to be 1 * 3", self);
}
if (divideZ) return (get(0, 0), get(0, 1)) / get(0, 2);
else return (get(0, 0), get(0, 1));
}
/// Returns the Matrix in Vector3 form, optionally dividing by w.
Vector3 asVector3(bool divideW = true) const {
if (columns != 1 || rows != 4) {
throwAbortException("Error: <%p>.asVector3() - Matrix needs to be 1 * 4", self);
}
if (divideW) return (get(0, 0), get(0, 1), get(0, 2)) / get(0, 3);
else return (get(0, 0), get(0, 1), get(0, 2));
}
/// Returns the number of columns.
int getColumns() const {
return columns;
}
/// Returns the number of rows.
int getRows() const {
return rows;
}
}

View file

@ -0,0 +1,275 @@
class swwm_Quaternion {
double w, x, y, z;
/// Initialises the Quaternion.
swwm_Quaternion init(double w, double x, double y, double z) {
self.w = w;
self.x = x;
self.y = y;
self.z = z;
return self;
}
/// Initialises the Quaternion in a static context.
static swwm_Quaternion create(double w, double x, double y, double z) {
return new("swwm_Quaternion").init(w, x, y, z);
}
/// Sets up the quaternion using axis and angle.
void setAxisAngle(Vector3 axis, double angle) {
double lengthSquared = axis dot axis;
// avoid a division by 0 and just return the identity
if (swwm_GlobalMaths.closeEnough(lengthSquared, 0)) {
init(1, 0, 0, 0);
return;
}
angle *= 0.5;
double sinTheta = sin(angle);
double cosTheta = cos(angle);
double factor = sinTheta / sqrt(lengthSquared);
w = cosTheta;
x = factor * axis.x;
y = factor * axis.y;
z = factor * axis.z;
}
/// Initialises the Quaternion using axis and angle.
swwm_Quaternion initFromAxisAngle(Vector3 axis, double angle) {
setAxisAngle(axis, angle);
return self;
}
/// Initialises the Quaternion using axis and angle in a static context.
static swwm_Quaternion createFromAxisAngle(Vector3 axis, double angle) {
return new("swwm_Quaternion").initFromAxisAngle(axis, angle);
}
/// Sets up the quaternion using euler angles.
void setAngles(double yaw, double pitch, double roll) {
swwm_Quaternion zRotation = new("swwm_Quaternion").initFromAxisAngle((0, 0, 1), yaw);
swwm_Quaternion yRotation = new("swwm_Quaternion").initFromAxisAngle((0, 1, 0), pitch);
swwm_Quaternion xRotation = new("swwm_Quaternion").initFromAxisAngle((1, 0, 0), roll);
swwm_Quaternion finalRotation = zRotation.multiplyQuat(yRotation);
finalRotation = finalRotation.multiplyQuat(xRotation);
copy(finalRotation);
}
/// Initialises the quaternion using euler angles.
swwm_Quaternion initFromAngles(double yaw, double pitch, double roll) {
setAngles(yaw, pitch, roll);
return self;
}
/// Initialises the quaternion using euler angles in a static context.
static swwm_Quaternion createFromAngles(double yaw, double pitch, double roll) {
return new("swwm_Quaternion").initFromAngles(yaw, pitch, roll);
}
/// Returns the euler angles from the Quaternion.
double, double, double toAngles() {
double singularityTest = z * x - w * y;
double yawY = 2 * (w * z + x * y);
double yawX = (1 - 2 * (y * y + z * z));
double singularityThreshold = 0.4999995;
double yaw = 0;
double pitch = 0;
double roll = 0;
if (singularityTest < -singularityThreshold) {
pitch = 90;
yaw = atan2(yawY, yawX);
roll = swwm_GlobalMaths.normalize180(yaw + (2 * atan2(x, w)));
}
else if (singularityTest > singularityThreshold) {
pitch = -90;
yaw = atan2(yawY, yawX);
roll = swwm_GlobalMaths.normalize180(yaw + (2 * atan2(x, w)));
}
else {
pitch = -asin(2 * singularityTest);
yaw = atan2(yawY, yawX);
roll = atan2(2 * (w * x + y * z), (1 - 2 * (x * x + y * y)));
}
return yaw, pitch, roll;
}
/// Returns the conjugate of the Quaternion.
swwm_Quaternion conjugate() const {
return new("swwm_Quaternion").init(w, -x, -y, -z);
}
/// Returns the normalised form of the Quaternion.
swwm_Quaternion unit() const {
double lengthSquared = w * w + x * x + y * y + z * z;
if (swwm_GlobalMaths.closeEnough(lengthSquared, 0)) {
return zero();
}
double factor = 1 / sqrt(lengthSquared);
return new("swwm_Quaternion").init(w * factor, x * factor, y * factor, z * factor);
}
/// Returns the inverse of the Quaternion (equal to conjugate if normalised).
swwm_Quaternion inverse() {
double norm = w * w + x * x + y * y + z * z;
// if this is a zero quaternion, just return self
if (swwm_GlobalMaths.closeEnough(norm, 0)) {
return self;
}
double inverseNorm = 1/norm;
return new("swwm_Quaternion").init(w * inverseNorm, x * -inverseNorm, y * -inverseNorm, z * -inverseNorm);
}
/// Adds two Quaternions, returning the result.
swwm_Quaternion add(swwm_Quaternion other) const {
return new("swwm_Quaternion").init(w + other.w, x + other.x, y + other.y, z + other.z);
}
/// Subtracts two Quaternions, returning the result.
swwm_Quaternion subtract(swwm_Quaternion other) const {
return new("swwm_Quaternion").init(w - other.w, x - other.x, y - other.y, z - other.z);
}
/// Multiplies the Quaternion by a scalar, returning the result.
swwm_Quaternion multiplyScalar(double scalar) const {
return new("swwm_Quaternion").init(w * scalar, x * scalar, y * scalar, z * scalar);
}
/// Multiplies two Quaternions, returning the result.
swwm_Quaternion multiplyQuat(swwm_Quaternion other) const {
return new("swwm_Quaternion").init(w * other.w - x * other.x - y * other.y - z * other.z,
w * other.x + x * other.w + y * other.z - z * other.y,
w * other.y + y * other.w + z * other.x - x * other.z,
w * other.z + z * other.w + x * other.y - y * other.x );
}
/// Negates the Quaternion.
swwm_Quaternion negate() const {
return new("swwm_Quaternion").init(-w, -x, -y, -z);
}
/// Sets the values to 0 if they're close enough to 0.
void clean() {
if (swwm_GlobalMaths.closeEnough(w, 0)) w = 0;
if (swwm_GlobalMaths.closeEnough(x, 0)) x = 0;
if (swwm_GlobalMaths.closeEnough(y, 0)) y = 0;
if (swwm_GlobalMaths.closeEnough(z, 0)) z = 0;
}
/// Returns the length of the Quaternion squared.
double lengthSquared() const {
return (w * w + x * x + y * y + z * z);
}
/// Returns the length of the Quaternion.
double length() const {
return sqrt(w * w + x * x + y * y + z * z);
}
/// Returns whether the two Quaternions are equal.
bool equals(swwm_Quaternion other) const {
return swwm_GlobalMaths.closeEnough(w, other.w) && swwm_GlobalMaths.closeEnough(x, other.x) &&
swwm_GlobalMaths.closeEnough(y, other.y) && swwm_GlobalMaths.closeEnough(z, other.z) ;
}
/// Returns if the Quaternion is a 0 Quaternion.
bool isZero() const {
return swwm_GlobalMaths.closeEnough(w * w + x * x + y * y + z * z, 0);
}
/// Returns if the Quaternion is a unit Quaternion.
bool isUnit() const {
return swwm_GlobalMaths.closeEnough(w * w + x * x + y * y + z * z, 1);
}
/// Returns if the Quaternion is an identity Quaternion.
bool isIdentity() const {
return swwm_GlobalMaths.closeEnough(w, 1) && swwm_GlobalMaths.closeEnough(x, 0) &&
swwm_GlobalMaths.closeEnough(y, 0) && swwm_GlobalMaths.closeEnough(z, 0) ;
}
/// Returns the dot product of two Quaternions.
double dotProduct(swwm_Quaternion other) const {
return (w * other.w + x * other.x + y * other.y + z * other.z);
}
/// Copies another Quaternion into this one.
void copy(swwm_Quaternion other) {
w = other.w;
x = other.x;
y = other.y;
z = other.z;
}
/// Rotates a Vector3 using this Quaternion.
Vector3 rotateVector3(Vector3 vec) const {
swwm_Quaternion q = unit();
Vector3 u = (q.x, q.y, q.z);
double s = q.w;
return 2 * (u dot vec) * u + (s * s - (u dot u)) * vec + 2 * s * u cross vec;
}
/// Linearly interpolates between two Quaternions, clamping the parameters.
static swwm_Quaternion lerp(swwm_Quaternion from, swwm_Quaternion to, double time) {
time = clamp(time, 0, 1);
return lerpUnclamped(from, to, time);
}
/// Linearly interpolates between two Quaternions.
static swwm_Quaternion lerpUnclamped(swwm_Quaternion from, swwm_Quaternion to, double time) {
swwm_Quaternion ret = new("swwm_Quaternion");
double reverseTime = 1 - time;
ret.x = reverseTime * from.x + time * to.x;
ret.y = reverseTime * from.y + time * to.y;
ret.z = reverseTime * from.z + time * to.z;
ret.w = reverseTime * from.w + time * to.w;
ret = ret.unit();
return ret;
}
/// Spherically interpolates between two Quaternions, clamping the parameters.
static swwm_Quaternion slerp(swwm_Quaternion from, swwm_Quaternion to, double time) {
time = clamp(time, 0, 1);
return slerpUnclamped(from, to, time);
}
/// Spherically interpolates between two Quaternions.
static swwm_Quaternion slerpUnclamped(swwm_Quaternion from, swwm_Quaternion to, double time) {
swwm_Quaternion q3;
double fromToDot = from.dotProduct(to);
if (fromToDot < 0) {
fromToDot = -fromToDot;
q3 = to.negate();
}
else {
q3 = to;
}
if (fromToDot < 0.95) {
double angle = acos(fromToDot);
return ((from.multiplyScalar(sin(angle * (1 - time)))).add(q3.multiplyScalar(sin(angle * time)))).multiplyScalar(1 / sin(angle));
}
else {
return lerp(from, q3, time);
}
}
/// Returns the 0 Quaternion.
static swwm_Quaternion zero() {
return new("swwm_Quaternion").init(0, 0, 0, 0);
}
/// Returns the identity Quaternion.
static swwm_Quaternion identity() {
return new("swwm_Quaternion").init(1, 0, 0, 0);
}
}

View file

@ -0,0 +1,29 @@
class swwm_VectorUtil {
/// Linearly interpolates between two Vector3s, clamping the parameters.
static Vector3 lerpVec3(Vector3 from, Vector3 to, double time) {
time = clamp(time, 0, 1);
return lerpUnclampedVec3(from, to, time);
}
/// Linearly interpolates between two Vector3s.
static Vector3 lerpUnclampedVec3(Vector3 from, Vector3 to, double time) {
Vector3 ret;
double reverseTime = 1 - time;
ret = reverseTime * from + time * to;
return ret;
}
/// Linearly interpolates between two Vector2s, clamping the parameters.
static Vector2 lerpVec2(Vector2 from, Vector2 to, double time) {
time = clamp(time, 0, 1);
return lerpUnclampedVec2(from, to, time);
}
/// Linearly interpolates between two Vector2s.
static Vector2 lerpUnclampedVec2(Vector2 from, Vector2 to, double time) {
Vector2 ret;
double reverseTime = 1 - time;
ret = reverseTime * from + time * to;
return ret;
}
}

View file

@ -1,58 +0,0 @@
libeye (for projection and deprojection)
written by KeksDose / MemeDose (May 2019)
(updated July 2019)
All rights etc. etc. who cares, you may reuse this as you wish and edit it, and
leave this note intact.
//
//
/////((
@@////////// ((
(( %////((///// (( (( ((
(( @@//(((/////// ((( ////////////// ((
( /////////////////////////(((((((////////// ((
@@///@@@@@@//////////(((((((((//@@@////////// ((
(( ///// /////((((((((((((((//@@% %%@/////////// ((
//////////((((((((((((((//////((((( %%@@////////// ((
((%%//////////@@%% @@//(((((((/// ( (((( %%///////// (
(( ////////@@ ((( @@//(((((((///// (( ( @@/////////
((/////////@ (( (( @@/(((((((///// (( (( @@/((((///((
(( ///((//@@ (( (( %@@@@@@////////// (((( /((((/// ((
((/////((//%%( ////%%%@@////////// @//((((///
////(((// ///////// //@@/////////////////(((((//
( ////(((////( //////////////////////@@@@////////////(((((////((
////(((((((// //////////////////////////// %%@@///(((((((((// (
//(((((((((/////////////////// //////////// %%@//(((((((//// ((
//(((((((((((////////////((/// /////////////// @@//(((((((///
//(((((((((((////////(( /////////((//////////// ( //(((((((///////
//((((((( ((///////( ##### ////( ((/////// ( @@//(((((///////
//((((((( (((//////((( ### (((( ##### ((/////// (( //(((///////@@
//((((((((((((@@/////////(((((((((((((((((((((///// ///////////% ((
//((((((((((((%% ///////((((((((((((//////// //////////////@@%% ((
@@//(((((((((/%%(( /////////////////////// ///////////////// (((
( //(((((((((/ (( ///////////////////////
%%//(((((((///(( (( //////////////@///////%%
(( @@/((((((///(( (( //////////////@@%% /////// ((
(( /(((((((// ( //////////////@%% //////%((
(( @@//(((////(( (( /////(((((((///@@ ((( /////// (
( @@(((((// ( ////(((((((///@@% (((( /////////%%((
((%%//(((/////////(((((((//@%% //////////%% ((
((////(((((///////(((((///////////////////////% ((
( ////(((((/////(((((((((//////////((((///@@%% ((
////(((//////@//////(((((((((((((////@@@%% (
(( /////////@@%% %%@@@@@@@@@@@@@@@@@@@%% ((
(( @@@@@%% ((
(( (((
%%//%
( //((/ ((
( ///////((
( ///////((
///// ((
//
//
(( ///// ((

View file

@ -1,150 +0,0 @@
/* kd:
In open-gl, your screen rotates nicely and you can do mostly what you know
to be sane. It's all about making a rotation of your view and using what
you know about right triangles.
*/
class swwmLe__GlScreen : swwmLe__ProjScreen {
protected vector3 forw_unit;
protected vector3 right_unit;
protected vector3 down_unit;
override void Reorient (vector3 world_view_pos, vector3 world_ang) {
// kd: Pitch is a weird gzd joke. It's probably to compensate looking
// speed and all. After that, you see what makes this fast.
world_ang.y = VectorAngle(
cos(world_ang.y),
sin(world_ang.y) * pixel_stretch);
super.Reorient(world_view_pos, world_ang);
let cosang = cos(world_ang.x);
let cosvang = cos(world_ang.y);
let cosrang = cos(world_ang.z);
let sinang = sin(world_ang.x);
let sinvang = sin(world_ang.y);
let sinrang = sin(world_ang.z);
let right_no_roll = (
sinang,
- cosang,
0);
let down_no_roll = (
- sinvang * cosang,
- sinvang * sinang,
- cosvang);
forw_unit = (
cosvang * cosang,
cosvang * sinang,
- sinvang);
down_unit = cosrang * down_no_roll - sinrang * right_no_roll;
right_unit = cosrang * right_no_roll + sinrang * down_no_roll;
}
// kd: Projection handling. These get called to make stuff a little faster,
// since you may wanna project many many times.
protected vector3 forw_in;
protected vector3 right_in;
protected vector3 down_in;
override void BeginProjection () {
forw_in = forw_unit;
right_in = right_unit / tan_fov_2.x;
down_in = down_unit / tan_fov_2.y;
forw_in.z *= pixel_stretch;
right_in.z *= pixel_stretch;
down_in.z *= pixel_stretch;
}
override void ProjectWorldPos (vector3 world_pos) {
diff = levellocals.vec3diff(view_pos, world_pos);
proj_pos = (diff dot right_in, diff dot down_in);
depth = diff dot forw_in;
}
override void ProjectActorPos (Actor mo, vector3 offset, double t) {
let inter_pos = mo.prev + t * (mo.pos - mo.prev);
diff = levellocals.vec3diff(view_pos, inter_pos + offset);
proj_pos = (diff dot right_in, diff dot down_in);
depth = diff dot forw_in;
}
override void ProjectActorPosPortal (Actor mo, vector3 offset, double t) {
let inter_pos = mo.prev + t * levellocals.vec3diff(mo.prev, mo.pos);
diff = levellocals.vec3diff(view_pos, inter_pos + offset);
proj_pos = (diff dot right_in, diff dot down_in);
depth = diff dot forw_in;
}
override vector2 ProjectToNormal () const {
return proj_pos / depth;
}
override vector2 ProjectToScreen () const {
let normal_pos = proj_pos / depth + (1, 1);
return 0.5 * (
normal_pos.x * resolution.x,
normal_pos.y * resolution.y);
}
override vector2 ProjectToCustom (
vector2 origin,
vector2 resolution) const {
let normal_pos = proj_pos / depth + (1, 1);
return origin + 0.5 * (
normal_pos.x * resolution.x,
normal_pos.y * resolution.y);
}
// kd: Same deal but backwards-ish.
protected vector3 forw_out;
protected vector3 right_out;
protected vector3 down_out;
override void BeginDeprojection () {
// kd: Same deal as above, but reversed. This time, we're compensating
// for what we rightfully assume is a projected position.
forw_out = forw_unit;
right_out = right_unit * tan_fov_2.x;
down_out = down_unit * tan_fov_2.y;
forw_out.z /= pixel_stretch;
right_out.z /= pixel_stretch;
down_out.z /= pixel_stretch;
}
override vector3 DeprojectNormalToDiff (
vector2 normal_pos,
double depth) const {
return depth * (
forw_out +
normal_pos.x * right_out +
normal_pos.y * down_out);
}
override vector3 DeprojectScreenToDiff (
vector2 screen_pos,
double depth) const {
// kd: Same thing...
let normal_pos = 2 * (
screen_pos.x / resolution.x,
screen_pos.y / resolution.y) - (1, 1);
return depth * (
forw_out +
normal_pos.x * right_out +
normal_pos.y * down_out);
}
}

View file

@ -1,125 +0,0 @@
/* kd:
This does projection stuff in Carmack / software renderer. It's conceptually
simpler, but nonetheless a little tricky to understand if you're
used to open-gl.
*/
class swwmLe__SwScreen : swwmLe__ProjScreen {
// kd: Less info necessary than for open-gl, but it's there.
protected vector2 right_planar_unit;
protected vector3 forw_planar_unit;
override void Reorient (vector3 world_view_pos, vector3 world_ang) {
super.Reorient(world_view_pos, world_ang);
right_planar_unit = (
sin(view_ang.x),
- cos(view_ang.x));
forw_planar_unit = (
- right_planar_unit.y,
right_planar_unit.x,
tan(view_ang.y));
}
// kd: Projection:
protected vector3 forw_planar_in;
protected vector2 right_planar_in;
override void BeginProjection () {
// kd: This doesn't cause any imprecisions. It also prevents two
// multiplications with every projection.
right_planar_in = right_planar_unit / tan_fov_2.x;
forw_planar_in = forw_planar_unit;
}
override void ProjectWorldPos (vector3 world_pos) {
// kd: Your view is flat. If you pitch up or down, imagine that all the
// actors move up and down in reality. That's effectively how it works.
// You can see this in the addition to diff.z.
diff = levellocals.vec3diff(view_pos, world_pos);
depth = forw_planar_in.xy dot diff.xy;
diff.z += forw_planar_in.z * depth;
proj_pos = (
right_planar_in dot diff.xy,
- pixel_stretch * diff.z / tan_fov_2.y);
}
override void ProjectActorPos (Actor mo, vector3 offset, double t) {
let inter_pos = mo.prev + t * (mo.pos - mo.prev);
ProjectWorldPos(inter_pos + offset);
}
override void ProjectActorPosPortal (Actor mo, vector3 offset, double t) {
let inter_pos = mo.prev + t * levellocals.vec3diff(mo.prev, mo.pos);
ProjectWorldPos(inter_pos + offset);
}
override vector2 ProjectToNormal () const {
return proj_pos / depth;
}
override vector2 ProjectToScreen () const {
let normal_pos = proj_pos / depth + (1, 1);
return 0.5 * (
normal_pos.x * resolution.x,
normal_pos.y * resolution.y);
}
override vector2 ProjectToCustom (
vector2 origin,
vector2 resolution) const {
let normal_pos = proj_pos / depth + (1, 1);
return origin + 0.5 * (
normal_pos.x * resolution.x,
normal_pos.y * resolution.y);
}
// kd: Just as simple. You again assume you are trying to reverse a
// projected position from the screen back into the world.
protected vector3 forw_planar_out;
protected vector3 right_planar_out;
protected vector3 down_planar_out;
override void BeginDeprojection () {
forw_planar_out.xy = forw_planar_unit.xy;
forw_planar_out.z = 0;
right_planar_out.xy = tan_fov_2.x * right_planar_unit;
right_planar_out.z = 0;
down_planar_out = (
0,
0,
tan_fov_2.y / pixel_stretch);
}
override vector3 DeprojectNormalToDiff (
vector2 normal_pos,
double depth) const {
return depth * (
forw_planar_out +
normal_pos.x * right_planar_out +
- (0, 0, forw_planar_unit.z) - normal_pos.y * down_planar_out);
}
override vector3 DeprojectScreenToDiff (
vector2 screen_pos,
double depth) const {
// kd: Same thing...
let normal_pos = 2 * (
screen_pos.x / resolution.x,
screen_pos.y / resolution.y) - (1, 1);
return depth * (
forw_planar_out +
normal_pos.x * right_planar_out +
- (0, 0, forw_planar_unit.z) - normal_pos.y * down_planar_out);
}
}

View file

@ -1,193 +0,0 @@
/* kd:
Here's how to do projections and deprojections. You'd use the subclasses
to do anything worthwhile. You may project world to screen and backwards.
*/
class swwmLe__ProjScreen {
// kd: Screen info
protected vector2 resolution;
protected vector2 origin;
protected vector2 tan_fov_2;
protected double pixel_stretch;
protected double aspect_ratio;
// kd: Setup calls which you'll need to call at least once.
void CacheResolution () {
CacheCustomResolution((Screen.GetWidth(), Screen.GetHeight()) );
}
void CacheCustomResolution (vector2 new_resolution) {
// kd: This is for convenience and converting normal <-> screen pos.
resolution = new_resolution;
// kd: This isn't really necessary but I kinda like it.
pixel_stretch = level.pixelstretch;
// kd: Get the aspect ratio. 5:4 is handled just like 4:3... I GUESS
// this'll do.
aspect_ratio = max(4.0 / 3, Screen.GetAspectRatio());
}
double AspectRatio () const {
return aspect_ratio;
}
// kd: Once you know you got screen info, you can call this whenever your
// fov changes. Like CacheFov(player.fov) will do.
void CacheFov (double hor_fov = 90) {
// kd: This holds: aspect ratio = tan(horizontal fov) / tan(ver fov).
// gzd always uses hor fov, but the fov only holds in 4:3 (in a 4:3 box
// in your screen centre), so we just extend it.
tan_fov_2.x = tan(hor_fov / 2) * aspect_ratio / (4.0 / 3);
tan_fov_2.y = tan_fov_2.x / aspect_ratio;
}
// kd: Also need some view info. Angle is yaw, pitch, roll in world format
// so positive pitch is up. Call one of the following functions.
protected vector3 view_ang;
protected vector3 view_pos;
ui void OrientForRenderOverlay (RenderEvent event) {
Reorient(
event.viewpos, (
event.viewangle,
event.viewpitch,
event.viewroll));
}
ui void OrientForRenderUnderlay (RenderEvent event) {
Reorient(
event.viewpos, (
event.viewangle,
event.viewpitch,
event.viewroll));
}
void OrientForPlayer (PlayerInfo player) {
Reorient(
player.mo.vec3offset(0, 0, player.viewheight), (
player.mo.angle,
player.mo.pitch,
player.mo.roll));
}
virtual void Reorient (vector3 world_view_pos, vector3 world_ang) {
view_ang = world_ang;
view_pos = world_view_pos;
}
// kd: Now we can do projections and such (position in the level, go to
// your screen).
protected double depth;
protected vector2 proj_pos;
protected vector3 diff;
virtual void BeginProjection () {}
virtual void ProjectWorldPos (vector3 world_pos) {}
virtual void ProjectActorPos (
Actor mo,
vector3 offset = (0,0,0),
double t = 1) {}
// kd: Portal aware version.
virtual void ProjectActorPosPortal (
Actor mo,
vector3 offset = (0,0,0),
double t = 1) {}
virtual vector2 ProjectToNormal () const { return (0, 0); }
virtual vector2 ProjectToScreen () const { return (0, 0); }
virtual vector2 ProjectToCustom (
vector2 origin,
vector2 resolution) const {
return (0, 0);
}
bool IsInFront () const {
return 0 < depth;
}
bool IsInScreen () const {
if( proj_pos.x < -depth || depth < proj_pos.x ||
proj_pos.y < -depth || depth < proj_pos.y) {
return false;
}
return true;
}
// kd: Deprojection (point on screen, go into the world):
virtual void BeginDeprojection () {}
virtual vector3 DeprojectNormalToDiff (
vector2 normal_pos,
double depth = 1) const {
return (0, 0, 0);
}
virtual vector3 DeprojectScreenToDiff (
vector2 screen_pos,
double depth = 1) const {
return (0, 0, 0);
}
virtual vector3 DeprojectCustomToDiff (
vector2 origin,
vector2 resolution,
vector2 screen_pos,
double depth = 1) const {
return (0, 0, 0);
}
// kd: A normal position is in the -1 <= x, y <= 1 range on your screen.
// This will be your screen no matter the resolution:
/*
(-1, -1) -- --- --- (0, -1) --- --- --- --- (1, -1)
| |
| |
| |
(-1, 0) (0, 0) (1, 0)
| |
| |
| |
(-1, 1) --- --- --- (0, 1) --- --- --- --- (1, 1)
*/
// So this scales such a position back into your drawing resolution.
vector2 NormalToScreen (vector2 normal_pos) const {
normal_pos = 0.5 * (normal_pos + (1, 1));
return (
normal_pos.x * resolution.x,
normal_pos.y * resolution.y);
}
// kd: And this brings a screen position to normal. Make sure the resolution
// is the same for your cursor.
vector2 ScreenToNormal (vector2 screen_pos) const {
screen_pos = (
screen_pos.x / resolution.x,
screen_pos.y / resolution.y);
return 2 * screen_pos - (1, 1);
}
// kd: Other interesting stuff.
vector3 Difference () const {
return diff;
}
double Distance () const {
return diff.length();
}
}

View file

@ -1,141 +0,0 @@
/* kd:
This helps repositioning the view port for stuff like screen blocks. It's a
little more than that, cuz it can also determine stuff like, "is this scene
position in the viewport?" Cuz the scene doesn't necessarily match the
viewport.
Well yea... see the examples. Imagine how annoying it is to even get this
idea to begin with.
*/
struct swwmLe__Viewport {
private vector2 scene_origin;
private vector2 scene_size;
private vector2 viewport_origin;
private vector2 viewport_bound;
private vector2 viewport_size;
private double scene_aspect;
private double viewport_aspect;
private double scale_f;
private vector2 scene_to_viewport;
ui void FromHud () const {
scene_aspect = Screen.GetAspectRatio();
vector2 hud_origin;
vector2 hud_size;
[hud_origin.x, hud_origin.y, hud_size.x, hud_size.y] =
Screen.GetViewWindow();
let window_resolution = (
Screen.GetWidth(),
Screen.GetHeight());
let window_to_normal = (
1.0 / window_resolution.x,
1.0 / window_resolution.y);
viewport_origin = (
window_to_normal.x * hud_origin.x,
window_to_normal.y * hud_origin.y);
viewport_size = (
window_to_normal.x * hud_size.x,
window_to_normal.y * hud_size.y);
viewport_aspect = hud_size.x / hud_size.y;
viewport_bound = viewport_origin + viewport_size;
// kd: The scene is what is actually rendered. It's not always the same
// as the viewport. When the statusbar comes into play, the scene is
// obscured by the viewport being too small.
// Example: Compare screenblocks 11 against screenblocks 10 in unmodded
// Doom. You will notice that the scaling of the 3d world is the same,
// but it's moved up by half the height of the statusbar.
// That makes this viewport stuff kinda really annoying to deal with.
// Also statusbar.getsomethingfromstatusbar, really really nice naming.
let statusbar_height =
(window_resolution.y - Statusbar.GetTopOfStatusbar()) / window_resolution.y;
scale_f = hud_size.x / window_resolution.x;
scene_aspect = Screen.GetAspectRatio();
let offset = 10 < screenblocks ? 0 : 0.5 * statusbar_height;
scene_size = (
scale_f,
scale_f);
scene_origin = viewport_origin - (0, 0.5 * (scene_size.y - viewport_size.y));
scene_to_viewport = (
viewport_size.x / scene_size.x,
viewport_size.y / scene_size.y);
}
// kd: Is the scene pos (normal, just like projected normal) inside the
// view port? If yes, it's visible in the 3d world, even through resizing.
bool IsInside (vector2 scene_pos) const {
let normal_pos = scene_origin + (
scene_size.x * 0.5 * (1 + scene_pos.x),
scene_size.y * 0.5 * (1 + scene_pos.y));
if( normal_pos.x < viewport_origin.x || viewport_bound.x < normal_pos.x ||
normal_pos.y < viewport_origin.y || viewport_bound.y < normal_pos.y) {
return false;
}
return true;
}
// kd: Use these for drawing (and make sure the aspect ratios match).
vector2 SceneToCustom (vector2 scene_pos, vector2 resolution) const {
let normal_pos = 0.5 * (
(scene_pos.x + 1) * scene_size.x,
(scene_pos.y + 1) * scene_size.y);
return (
(scene_origin.x + normal_pos.x) * resolution.x,
(scene_origin.y + normal_pos.y) * resolution.y);
}
vector2 SceneToWindow (vector2 scene_pos) const {
return SceneToCustom(
scene_pos,
(Screen.GetWidth(), Screen.GetHeight()) );
}
vector2 ViewportToCustom (vector2 viewport_pos, vector2 resolution) const {
let normal_pos = 0.5 * (
(viewport_pos.x + 1) * viewport_size.x,
(viewport_pos.y + 1) * viewport_size.y);
return (
(viewport_origin.x + normal_pos.x) * resolution.x,
(viewport_origin.y + normal_pos.y) * resolution.y);
}
vector2 ViewportToWindow (vector2 viewport_pos) const {
return ViewportToCustom(
viewport_pos,
(Screen.GetWidth(), Screen.GetHeight()) );
}
double Scale () const {
return scale_f;
}
}

View file

@ -1,115 +0,0 @@
/*
Quaternion math helper class.
Provides only the bare minimum needed to implement 6DOF functionality. For a
more extensive implementation, please refer to Gutamatics.
Copyright (c)2018-2021 Marisa Kirisame, UnSX Team
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
*/
Class swwm_Quat
{
protected double W, X, Y, Z;
swwm_Quat init( double w, double x, double y, double z )
{
self.W = w;
self.X = x;
self.Y = y;
self.Z = z;
return self;
}
void copy( swwm_Quat q )
{
W = q.W;
X = q.X;
Y = q.Y;
Z = q.Z;
}
static swwm_Quat create( double w, double x, double y, double z )
{
return new("swwm_Quat").init(w,x,y,z);
}
static swwm_Quat create_axis( Vector3 axis, double theta )
{
double scale = axis dot axis;
if ( scale < double.epsilon ) return swwm_Quat.create(1,0,0,0);
theta *= 0.5;
double f = sin(theta)/sqrt(scale);
return swwm_Quat.create(cos(theta),axis.x*f,axis.y*f,axis.z*f);
}
static swwm_Quat create_euler( double pitch, double yaw, double roll )
{
swwm_Quat zrot = swwm_Quat.create_axis((0,0,1),yaw);
swwm_Quat yrot = swwm_Quat.create_axis((0,1,0),pitch);
swwm_Quat xrot = swwm_Quat.create_axis((1,0,0),roll);
swwm_Quat sum = zrot.qmul(yrot);
sum = sum.qmul(xrot);
return sum;
}
// copied here since Actor.Normalize180 is not (yet) clearscope
static double Normalize180( double ang )
{
ang = ang%360;
ang = (ang+360)%360;
if ( ang > 180 ) ang -= 360;
return ang;
}
double, double, double to_euler()
{
double stest = z*x-w*y;
double yawY = 2*(w*z+x*y);
double yawX = 1-2*(y*y+z*z);
double st = 0.4999995;
double pitch = 0;
double yaw = 0;
double roll = 0;
if ( stest <= -st )
{
pitch = 90;
yaw = atan2(yawY,yawX);
roll = Normalize180(yaw+(2*atan2(x,w)));
}
else if ( stest > st )
{
pitch = -90;
yaw = atan2(yawY,yawX);
roll = Normalize180(yaw+(2*atan2(x,w)));
}
else
{
pitch = -asin(2*stest);
yaw = atan2(yawY,yawX);
roll = atan2(2*(w*x+y*z),(1-2*(x*x+y*y)));
}
return pitch, yaw, roll;
}
swwm_Quat qmul( swwm_Quat q ) const
{
return swwm_Quat.create(w*q.w-x*q.x-y*q.y-z*q.z,w*q.x+x*q.w+y*q.z-z*q.y,w*q.y+y*q.w+z*q.x-x*q.z,w*q.z+z*q.w+x*q.y-y*q.x);
}
}

View file

@ -22,6 +22,12 @@ Class SWWMAchievement
bool hasformat;
}
Struct SWWMProjectionData
{
swwm_Matrix wtc;
int viewx, viewy, vieww, viewh;
}
Class SWWMUtility
{
// loaded from a file, neater than having a bunch of static arrays
@ -137,6 +143,48 @@ Class SWWMUtility
cv.SetFloat(cval+inc);
}
// gutamatics caching
static void PrepareProjData( out SWWMProjectionData d, Vector3 viewpos, double angle, double pitch, double roll, double fov )
{
double aspect = Screen.GetAspectRatio();
// vertical fov
double fovratio = (aspect>=1.3)?1.333333:aspect;
double fovy = 2.*atan(tan(clamp(fov,5,170)/2.)/fovratio);
// world→clip matrix
swwm_Matrix view = swwm_Matrix.view(viewpos,angle,pitch,roll);
swwm_Matrix perp = swwm_Matrix.perspective(fovy,aspect,5,65535);
d.wtc = perp.multiplyMatrix(view);
// screen coord data
int sblocks = CVar.FindCVar('screenblocks').GetInt();
int viewx, viewy, vieww, viewh;
[viewx, viewy, vieww, viewh] = Screen.GetViewWindow();
int sh = Screen.GetHeight();
int h = sh;
if ( sblocks < 10 ) h = (sblocks*sh/10)&~7;
int bottom = sh-(h+viewy-((h-viewh)/2));
d.viewx = viewx;
d.viewy = sh-bottom-h;
d.vieww = vieww;
d.viewh = h;
}
static Vector3 ProjectPoint( SWWMProjectionData d, Vector3 worldpos )
{
return d.wtc.multiplyVector3(worldpos).asVector3();
}
static Vector2 NDCToViewport( SWWMProjectionData d, Vector3 ndc )
{
return (d.viewx,d.viewy)+(((ndc.x+1)*d.vieww)/2,((-ndc.y+1)*d.viewh)/2);
}
// checks if a point is inside the viewport
static bool TestScreenBounds( SWWMProjectionData d, Vector2 vpos )
{
return ((vpos.x == clamp(vpos.x,d.viewx,d.viewx+d.vieww))
&& (vpos.y == clamp(vpos.y,d.viewy,d.viewy+d.viewh)));
}
// thanks zscript
static clearscope double fract( double a )
{

View file

@ -61,18 +61,12 @@ extend Class SWWMWeapon
if ( crosshairforce ) return;
let sb = SWWMStatusBar(StatusBar);
if ( !sb ) return;
sb.viewport.FromHud();
sb.proj.CacheResolution();
sb.proj.CacheFov(players[consoleplayer].fov);
sb.proj.OrientForRenderUnderlay(e);
sb.proj.BeginProjection();
SWWMUtility.PrepareProjData(sb.projdata,e.ViewPos,e.ViewAngle,e.ViewPitch,e.ViewRoll,players[consoleplayer].fov);
Vector3 tdir = level.Vec3Diff(e.ViewPos,cpos);
// project
sb.proj.ProjectWorldPos(e.ViewPos+tdir);
Vector2 vpos;
if ( !sb.proj.IsInFront() ) return;
Vector2 npos = sb.proj.ProjectToNormal();
vpos = sb.viewport.SceneToWindow(npos);
Vector3 ndc = SWWMUtility.ProjectPoint(sb.projdata,e.ViewPos+tdir);
if ( ndc.z >= 1. ) return;
Vector2 vpos = SWWMUtility.NDCToViewport(sb.projdata,ndc);
double frametime = (MSTime()-prevms)/1000.;
double theta = clamp(15.*frametime,0.,1.); // naive, but whatever
if ( !prevms || (lagvpos == (0,0)) ) lagvpos = vpos;

View file

@ -144,11 +144,7 @@ Class Wallbuster : SWWMWeapon
if ( crosshairforce ) return;
let sb = SWWMStatusBar(StatusBar);
if ( !sb ) return;
sb.viewport.FromHud();
sb.proj.CacheResolution();
sb.proj.CacheFov(players[consoleplayer].fov);
sb.proj.OrientForRenderUnderlay(e);
sb.proj.BeginProjection();
SWWMUtility.PrepareProjData(sb.projdata,e.ViewPos,e.ViewAngle,e.ViewPitch,e.ViewRoll,players[consoleplayer].fov);
int cnum = abs(CVar.FindCVar('crosshair').GetInt());
if ( !cnum ) return;
String tn = String.Format("XHAIR%s%d",(Screen.GetWidth()<640)?"S":"B",cnum);
@ -162,23 +158,21 @@ Class Wallbuster : SWWMWeapon
if ( crosshairgrow ) sz *= sb.CrosshairSize;
Vector3 tdir = level.Vec3Diff(e.ViewPos,cpos);
// project
sb.proj.ProjectWorldPos(e.ViewPos+tdir);
Vector2 vpos;
if ( !sb.proj.IsInFront() ) return;
Vector2 npos = sb.proj.ProjectToNormal();
vpos = sb.viewport.SceneToWindow(npos);
Vector3 ndc = SWWMUtility.ProjectPoint(sb.projdata,e.ViewPos+tdir);
if ( ndc.z >= 1. ) return;
Vector2 vpos = SWWMUtility.NDCToViewport(sb.projdata,ndc);
double frametime = (MSTime()-prevms)/1000.;
double theta = clamp(15.*frametime,0.,1.); // naive, but whatever
if ( !prevms || (lagvpos == (0,0)) ) lagvpos = vpos;
else lagvpos = lagvpos*(1.-theta)+vpos*theta;
for ( int i=0; i<25; i++ )
{
tdir = level.Vec3Diff(e.ViewPos,cpos25[i]);
// project
sb.proj.ProjectWorldPos(e.ViewPos+tdir);
if ( !sb.proj.IsInFront() ) continue;
npos = sb.proj.ProjectToNormal();
vpos = sb.viewport.SceneToWindow(npos);
ndc = SWWMUtility.ProjectPoint(sb.projdata,e.ViewPos+tdir);
if ( ndc.z >= 1. ) return;
vpos = SWWMUtility.NDCToViewport(sb.projdata,ndc);
if ( !prevms || (lagvpos25[i] == (0,0)) ) lagvpos25[i] = vpos;
else lagvpos25[i] = lagvpos25[i]*(1.-theta)+vpos*theta;
Screen.DrawTexture(ctex,false,int(lagvpos25[i].x),int(lagvpos25[i].y),DTA_DestWidthF,ts.x*sz,DTA_DestHeightF,ts.y*sz,DTA_AlphaChannel,true,DTA_FillColor,ccol25[i]);