mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-12-02 08:53:42 +00:00
Merge remote-tracking branch 'gzdoom/master' into asmjit
This commit is contained in:
commit
55955b9c22
36 changed files with 1227 additions and 482 deletions
|
@ -1154,6 +1154,7 @@ set (PCH_SOURCES
|
||||||
r_data/models/models_md2.cpp
|
r_data/models/models_md2.cpp
|
||||||
r_data/models/models_voxel.cpp
|
r_data/models/models_voxel.cpp
|
||||||
r_data/models/models_ue1.cpp
|
r_data/models/models_ue1.cpp
|
||||||
|
r_data/models/models_obj.cpp
|
||||||
scripting/symbols.cpp
|
scripting/symbols.cpp
|
||||||
scripting/types.cpp
|
scripting/types.cpp
|
||||||
scripting/thingdef.cpp
|
scripting/thingdef.cpp
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
|
|
||||||
#include "doomtype.h"
|
#include "doomtype.h"
|
||||||
#include "configfile.h"
|
#include "configfile.h"
|
||||||
|
#include "files.h"
|
||||||
#include "m_random.h"
|
#include "m_random.h"
|
||||||
|
|
||||||
#define READBUFFERSIZE 256
|
#define READBUFFERSIZE 256
|
||||||
|
@ -601,17 +602,16 @@ FConfigFile::FConfigEntry *FConfigFile::NewConfigEntry (
|
||||||
|
|
||||||
void FConfigFile::LoadConfigFile ()
|
void FConfigFile::LoadConfigFile ()
|
||||||
{
|
{
|
||||||
FILE *file = fopen (PathName, "r");
|
FileReader file;
|
||||||
bool succ;
|
bool succ;
|
||||||
|
|
||||||
FileExisted = false;
|
FileExisted = false;
|
||||||
if (file == NULL)
|
if (!file.OpenFile (PathName))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
succ = ReadConfig (file);
|
succ = ReadConfig (&file);
|
||||||
fclose (file);
|
|
||||||
FileExisted = succ;
|
FileExisted = succ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -787,7 +787,7 @@ FConfigFile::FConfigEntry *FConfigFile::ReadMultiLineValue(void *file, FConfigSe
|
||||||
|
|
||||||
char *FConfigFile::ReadLine (char *string, int n, void *file) const
|
char *FConfigFile::ReadLine (char *string, int n, void *file) const
|
||||||
{
|
{
|
||||||
return fgets (string, n, (FILE *)file);
|
return ((FileReader *)file)->Gets (string, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
//====================================================================
|
//====================================================================
|
||||||
|
@ -805,7 +805,7 @@ bool FConfigFile::WriteConfigFile () const
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *file = fopen (PathName, "w");
|
FileWriter *file = FileWriter::Open (PathName);
|
||||||
FConfigSection *section;
|
FConfigSection *section;
|
||||||
FConfigEntry *entry;
|
FConfigEntry *entry;
|
||||||
|
|
||||||
|
@ -820,27 +820,27 @@ bool FConfigFile::WriteConfigFile () const
|
||||||
entry = section->RootEntry;
|
entry = section->RootEntry;
|
||||||
if (section->Note.IsNotEmpty())
|
if (section->Note.IsNotEmpty())
|
||||||
{
|
{
|
||||||
fputs (section->Note.GetChars(), file);
|
file->Write (section->Note.GetChars(), section->Note.Len());
|
||||||
}
|
}
|
||||||
fprintf (file, "[%s]\n", section->SectionName.GetChars());
|
file->Printf ("[%s]\n", section->SectionName.GetChars());
|
||||||
while (entry != NULL)
|
while (entry != NULL)
|
||||||
{
|
{
|
||||||
if (strpbrk(entry->Value, "\r\n") == NULL)
|
if (strpbrk(entry->Value, "\r\n") == NULL)
|
||||||
{ // Single-line value
|
{ // Single-line value
|
||||||
fprintf (file, "%s=%s\n", entry->Key, entry->Value);
|
file->Printf ("%s=%s\n", entry->Key, entry->Value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{ // Multi-line value
|
{ // Multi-line value
|
||||||
const char *endtag = GenerateEndTag(entry->Value);
|
const char *endtag = GenerateEndTag(entry->Value);
|
||||||
fprintf (file, "%s=<<<%s\n%s\n>>>%s\n", entry->Key,
|
file->Printf ("%s=<<<%s\n%s\n>>>%s\n", entry->Key,
|
||||||
endtag, entry->Value, endtag);
|
endtag, entry->Value, endtag);
|
||||||
}
|
}
|
||||||
entry = entry->Next;
|
entry = entry->Next;
|
||||||
}
|
}
|
||||||
section = section->Next;
|
section = section->Next;
|
||||||
fputs ("\n", file);
|
file->Write ("\n", 1);
|
||||||
}
|
}
|
||||||
fclose (file);
|
delete file;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -890,7 +890,7 @@ const char *FConfigFile::GenerateEndTag(const char *value)
|
||||||
//
|
//
|
||||||
//====================================================================
|
//====================================================================
|
||||||
|
|
||||||
void FConfigFile::WriteCommentHeader (FILE *file) const
|
void FConfigFile::WriteCommentHeader (FileWriter *file) const
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#define __CONFIGFILE_H__
|
#define __CONFIGFILE_H__
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include "files.h"
|
||||||
#include "zstring.h"
|
#include "zstring.h"
|
||||||
|
|
||||||
class FConfigFile
|
class FConfigFile
|
||||||
|
@ -73,7 +74,7 @@ public:
|
||||||
bool WriteConfigFile () const;
|
bool WriteConfigFile () const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void WriteCommentHeader (FILE *file) const;
|
virtual void WriteCommentHeader (FileWriter *file) const;
|
||||||
|
|
||||||
virtual char *ReadLine (char *string, int n, void *file) const;
|
virtual char *ReadLine (char *string, int n, void *file) const;
|
||||||
bool ReadConfig (void *file);
|
bool ReadConfig (void *file);
|
||||||
|
|
|
@ -468,7 +468,7 @@ public:
|
||||||
TObjPtr<AActor*> MUSINFOactor = nullptr; // For MUSINFO purposes
|
TObjPtr<AActor*> MUSINFOactor = nullptr; // For MUSINFO purposes
|
||||||
int8_t MUSINFOtics = 0;
|
int8_t MUSINFOtics = 0;
|
||||||
|
|
||||||
bool settings_controller = false; // Player can control game settings.
|
bool settings_controller = true; // Player can control game settings.
|
||||||
int8_t crouching = 0;
|
int8_t crouching = 0;
|
||||||
int8_t crouchdir = 0;
|
int8_t crouchdir = 0;
|
||||||
|
|
||||||
|
|
17
src/dobjgc.h
17
src/dobjgc.h
|
@ -167,22 +167,19 @@ class TObjPtr
|
||||||
DObject *o;
|
DObject *o;
|
||||||
};
|
};
|
||||||
public:
|
public:
|
||||||
TObjPtr() throw()
|
TObjPtr() = default;
|
||||||
{
|
TObjPtr(const TObjPtr<T> &q) = default;
|
||||||
}
|
|
||||||
TObjPtr(T q) throw()
|
TObjPtr(T q) throw()
|
||||||
: pp(q)
|
: pp(q)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
TObjPtr(const TObjPtr<T> &q) throw()
|
T operator=(T q)
|
||||||
: pp(q.pp)
|
|
||||||
{
|
{
|
||||||
|
pp = q;
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
T operator=(T q) throw()
|
|
||||||
{
|
|
||||||
return pp = q;
|
|
||||||
// The caller must now perform a write barrier.
|
|
||||||
}
|
|
||||||
operator T() throw()
|
operator T() throw()
|
||||||
{
|
{
|
||||||
return GC::ReadBarrier(pp);
|
return GC::ReadBarrier(pp);
|
||||||
|
|
|
@ -103,7 +103,7 @@ enum
|
||||||
|
|
||||||
struct PalEntry
|
struct PalEntry
|
||||||
{
|
{
|
||||||
PalEntry () {}
|
PalEntry() = default;
|
||||||
PalEntry (uint32_t argb) { d = argb; }
|
PalEntry (uint32_t argb) { d = argb; }
|
||||||
operator uint32_t () const { return d; }
|
operator uint32_t () const { return d; }
|
||||||
void SetRGB(PalEntry other)
|
void SetRGB(PalEntry other)
|
||||||
|
@ -146,6 +146,7 @@ struct PalEntry
|
||||||
{
|
{
|
||||||
return (d & 0xffffff) == 0xffffff;
|
return (d & 0xffffff) == 0xffffff;
|
||||||
}
|
}
|
||||||
|
PalEntry &operator= (const PalEntry &other) = default;
|
||||||
PalEntry &operator= (uint32_t other) { d = other; return *this; }
|
PalEntry &operator= (uint32_t other) { d = other; return *this; }
|
||||||
PalEntry InverseColor() const { PalEntry nc; nc.a = a; nc.r = 255 - r; nc.g = 255 - g; nc.b = 255 - b; return nc; }
|
PalEntry InverseColor() const { PalEntry nc; nc.a = a; nc.r = 255 - r; nc.g = 255 - g; nc.b = 255 - b; return nc; }
|
||||||
#ifdef __BIG_ENDIAN__
|
#ifdef __BIG_ENDIAN__
|
||||||
|
@ -205,7 +206,7 @@ class FTextureID
|
||||||
friend void R_InitSpriteDefs();
|
friend void R_InitSpriteDefs();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FTextureID() throw() {}
|
FTextureID() = default;
|
||||||
bool isNull() const { return texnum == 0; }
|
bool isNull() const { return texnum == 0; }
|
||||||
bool isValid() const { return texnum > 0; }
|
bool isValid() const { return texnum > 0; }
|
||||||
bool Exists() const { return texnum >= 0; }
|
bool Exists() const { return texnum >= 0; }
|
||||||
|
|
|
@ -167,9 +167,9 @@ FGameConfigFile::~FGameConfigFile ()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void FGameConfigFile::WriteCommentHeader (FILE *file) const
|
void FGameConfigFile::WriteCommentHeader (FileWriter *file) const
|
||||||
{
|
{
|
||||||
fprintf (file, "# This file was generated by " GAMENAME " %s on %s\n", GetVersionString(), myasctime());
|
file->Printf ("# This file was generated by " GAMENAME " %s on %s\n", GetVersionString(), myasctime());
|
||||||
}
|
}
|
||||||
|
|
||||||
void FGameConfigFile::DoAutoloadSetup (FIWadManager *iwad_man)
|
void FGameConfigFile::DoAutoloadSetup (FIWadManager *iwad_man)
|
||||||
|
|
|
@ -58,7 +58,7 @@ public:
|
||||||
void ReadNetVars ();
|
void ReadNetVars ();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void WriteCommentHeader (FILE *file) const;
|
void WriteCommentHeader (FileWriter *file) const;
|
||||||
void CreateStandardAutoExec (const char *section, bool start);
|
void CreateStandardAutoExec (const char *section, bool start);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -399,7 +399,6 @@ void GLHorizonPortal::DrawContents(HWDrawInfo *hwdi)
|
||||||
Clocker c(PortalAll);
|
Clocker c(PortalAll);
|
||||||
|
|
||||||
FMaterial * gltexture;
|
FMaterial * gltexture;
|
||||||
PalEntry color;
|
|
||||||
player_t * player=&players[consoleplayer];
|
player_t * player=&players[consoleplayer];
|
||||||
GLSectorPlane * sp = &origin->plane;
|
GLSectorPlane * sp = &origin->plane;
|
||||||
auto &vp = di->Viewpoint;
|
auto &vp = di->Viewpoint;
|
||||||
|
|
|
@ -228,7 +228,6 @@ int LevelAABBTree::GenerateTreeNode(int *lines, int num_lines, const FVector2 *c
|
||||||
// Try sort at longest axis, then if that fails then the other one.
|
// Try sort at longest axis, then if that fails then the other one.
|
||||||
// We place the sorted lines into work_buffer and then move the result back to the lines list when done.
|
// We place the sorted lines into work_buffer and then move the result back to the lines list when done.
|
||||||
int left_count, right_count;
|
int left_count, right_count;
|
||||||
FVector2 axis;
|
|
||||||
for (int attempt = 0; attempt < 2; attempt++)
|
for (int attempt = 0; attempt < 2; attempt++)
|
||||||
{
|
{
|
||||||
// Find the sort plane for axis
|
// Find the sort plane for axis
|
||||||
|
|
|
@ -319,7 +319,6 @@ void FMaterial::SetSpriteRect()
|
||||||
|
|
||||||
bool FMaterial::TrimBorders(uint16_t *rect)
|
bool FMaterial::TrimBorders(uint16_t *rect)
|
||||||
{
|
{
|
||||||
PalEntry col;
|
|
||||||
int w;
|
int w;
|
||||||
int h;
|
int h;
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ class FString;
|
||||||
class FName
|
class FName
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FName() = default;// : Index(0) {}
|
FName() = default;
|
||||||
FName (const char *text) { Index = NameData.FindName (text, false); }
|
FName (const char *text) { Index = NameData.FindName (text, false); }
|
||||||
FName (const char *text, bool noCreate) { Index = NameData.FindName (text, noCreate); }
|
FName (const char *text, bool noCreate) { Index = NameData.FindName (text, noCreate); }
|
||||||
FName (const char *text, size_t textlen, bool noCreate) { Index = NameData.FindName (text, textlen, noCreate); }
|
FName (const char *text, size_t textlen, bool noCreate) { Index = NameData.FindName (text, textlen, noCreate); }
|
||||||
|
@ -63,7 +63,7 @@ public:
|
||||||
|
|
||||||
FName &operator = (const char *text) { Index = NameData.FindName (text, false); return *this; }
|
FName &operator = (const char *text) { Index = NameData.FindName (text, false); return *this; }
|
||||||
FName &operator = (const FString &text);
|
FName &operator = (const FString &text);
|
||||||
FName &operator = (const FName &other) { Index = other.Index; return *this; }
|
FName &operator = (const FName &other) = default;
|
||||||
FName &operator = (ENamedName index) { Index = index; return *this; }
|
FName &operator = (ENamedName index) { Index = index; return *this; }
|
||||||
|
|
||||||
int SetName (const char *text, bool noCreate=false) { return Index = NameData.FindName (text, noCreate); }
|
int SetName (const char *text, bool noCreate=false) { return Index = NameData.FindName (text, noCreate); }
|
||||||
|
|
|
@ -2345,7 +2345,6 @@ double P_XYMovement (AActor *mo, DVector2 scroll)
|
||||||
{
|
{
|
||||||
static int pushtime = 0;
|
static int pushtime = 0;
|
||||||
bool bForceSlide = !scroll.isZero();
|
bool bForceSlide = !scroll.isZero();
|
||||||
DAngle Angle;
|
|
||||||
DVector2 ptry;
|
DVector2 ptry;
|
||||||
player_t *player;
|
player_t *player;
|
||||||
DVector2 move;
|
DVector2 move;
|
||||||
|
|
|
@ -306,7 +306,7 @@ static void P_SetSlopesFromVertexHeights(FMapThing *firstmt, FMapThing *lastmt,
|
||||||
{
|
{
|
||||||
if (sec.Lines.Size() != 3) continue; // only works with triangular sectors
|
if (sec.Lines.Size() != 3) continue; // only works with triangular sectors
|
||||||
|
|
||||||
DVector3 vt1, vt2, vt3, cross;
|
DVector3 vt1, vt2, vt3;
|
||||||
DVector3 vec1, vec2;
|
DVector3 vec1, vec2;
|
||||||
int vi1, vi2, vi3;
|
int vi1, vi2, vi3;
|
||||||
|
|
||||||
|
|
|
@ -153,13 +153,13 @@ static struct SteamAppInfo
|
||||||
const int AppID;
|
const int AppID;
|
||||||
} AppInfo[] =
|
} AppInfo[] =
|
||||||
{
|
{
|
||||||
/*{"doom 2/base", 2300},
|
{"Doom 2/base", 2300},
|
||||||
{"final doom/base", 2290},
|
{"Final Doom/base", 2290},
|
||||||
{"heretic shadow of the serpent riders/base", 2390},
|
{"Heretic Shadow of the Serpent Riders/base", 2390},
|
||||||
{"hexen/base", 2360},
|
{"Hexen/base", 2360},
|
||||||
{"hexen deathkings of the dark citadel/base", 2370},
|
{"Hexen Deathkings of the Dark Citadel/base", 2370},
|
||||||
{"ultimate doom/base", 2280},
|
{"Ultimate Doom/base", 2280},
|
||||||
{"DOOM 3 BFG Edition/base/wads", 208200},*/
|
{"DOOM 3 BFG Edition/base/wads", 208200},
|
||||||
{"Strife", 317040}
|
{"Strife", 317040}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -190,7 +190,13 @@ TArray<FString> I_GetSteamPath()
|
||||||
if(home != NULL && *home != '\0')
|
if(home != NULL && *home != '\0')
|
||||||
{
|
{
|
||||||
FString regPath;
|
FString regPath;
|
||||||
regPath.Format("%s/.local/share/Steam/config/config.vdf", home);
|
regPath.Format("%s/.steam/config/config.vdf", home);
|
||||||
|
// [BL] The config seems to have moved from the more modern .local to
|
||||||
|
// .steam at some point. Not sure if it's just my setup so I guess we
|
||||||
|
// can fall back on it?
|
||||||
|
if(!FileExists(regPath))
|
||||||
|
regPath.Format("%s/.local/share/Steam/config/config.vdf", home);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
SteamInstallFolders = ParseSteamRegistry(regPath);
|
SteamInstallFolders = ParseSteamRegistry(regPath);
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
#include "r_utility.h"
|
#include "r_utility.h"
|
||||||
#include "r_data/models/models.h"
|
#include "r_data/models/models.h"
|
||||||
#include "r_data/models/models_ue1.h"
|
#include "r_data/models/models_ue1.h"
|
||||||
|
#include "r_data/models/models_obj.h"
|
||||||
#include "i_time.h"
|
#include "i_time.h"
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
|
@ -419,7 +420,7 @@ static unsigned FindModel(const char * path, const char * modelfile)
|
||||||
FMemLump lumpd = Wads.ReadLump(lump);
|
FMemLump lumpd = Wads.ReadLump(lump);
|
||||||
char * buffer = (char*)lumpd.GetMem();
|
char * buffer = (char*)lumpd.GetMem();
|
||||||
|
|
||||||
if ( (size_t)fullname.IndexOf("_d.3d") == fullname.Len()-5 )
|
if ( (size_t)fullname.LastIndexOf("_d.3d") == fullname.Len()-5 )
|
||||||
{
|
{
|
||||||
FString anivfile = fullname.GetChars();
|
FString anivfile = fullname.GetChars();
|
||||||
anivfile.Substitute("_d.3d","_a.3d");
|
anivfile.Substitute("_d.3d","_a.3d");
|
||||||
|
@ -428,7 +429,7 @@ static unsigned FindModel(const char * path, const char * modelfile)
|
||||||
model = new FUE1Model;
|
model = new FUE1Model;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ( (size_t)fullname.IndexOf("_a.3d") == fullname.Len()-5 )
|
else if ( (size_t)fullname.LastIndexOf("_a.3d") == fullname.Len()-5 )
|
||||||
{
|
{
|
||||||
FString datafile = fullname.GetChars();
|
FString datafile = fullname.GetChars();
|
||||||
datafile.Substitute("_a.3d","_d.3d");
|
datafile.Substitute("_a.3d","_d.3d");
|
||||||
|
@ -437,6 +438,10 @@ static unsigned FindModel(const char * path, const char * modelfile)
|
||||||
model = new FUE1Model;
|
model = new FUE1Model;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if ( (size_t)fullname.LastIndexOf(".obj") == fullname.Len() - 4 )
|
||||||
|
{
|
||||||
|
model = new FOBJModel;
|
||||||
|
}
|
||||||
else if (!memcmp(buffer, "DMDM", 4))
|
else if (!memcmp(buffer, "DMDM", 4))
|
||||||
{
|
{
|
||||||
model = new FDMDModel;
|
model = new FDMDModel;
|
||||||
|
|
588
src/r_data/models/models_obj.cpp
Normal file
588
src/r_data/models/models_obj.cpp
Normal file
|
@ -0,0 +1,588 @@
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Copyright(C) 2018 Kevin Caccamo
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||||
|
//
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "w_wad.h"
|
||||||
|
#include "r_data/models/models_obj.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load an OBJ model
|
||||||
|
*
|
||||||
|
* @param fn The path to the model file
|
||||||
|
* @param lumpnum The lump index in the wad collection
|
||||||
|
* @param buffer The contents of the model file
|
||||||
|
* @param length The size of the model file
|
||||||
|
* @return Whether or not the model was parsed successfully
|
||||||
|
*/
|
||||||
|
bool FOBJModel::Load(const char* fn, int lumpnum, const char* buffer, int length)
|
||||||
|
{
|
||||||
|
FString objName = Wads.GetLumpFullPath(lumpnum);
|
||||||
|
FString objBuf(buffer, length);
|
||||||
|
|
||||||
|
// Do some replacements before we parse the OBJ string
|
||||||
|
{
|
||||||
|
// Ensure usemtl statements remain intact
|
||||||
|
TArray<FString> mtlUsages;
|
||||||
|
TArray<long> mtlUsageIdxs;
|
||||||
|
long bpos = 0, nlpos = 0, slashpos = 0;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
bpos = objBuf.IndexOf("\nusemtl", bpos);
|
||||||
|
if (bpos == -1) break;
|
||||||
|
slashpos = objBuf.IndexOf('/', bpos);
|
||||||
|
nlpos = objBuf.IndexOf('\n', ++bpos);
|
||||||
|
if (slashpos > nlpos || slashpos == -1)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (nlpos == -1)
|
||||||
|
{
|
||||||
|
nlpos = objBuf.Len();
|
||||||
|
}
|
||||||
|
FString lineStr(objBuf.GetChars() + bpos, nlpos - bpos);
|
||||||
|
mtlUsages.Push(lineStr);
|
||||||
|
mtlUsageIdxs.Push(bpos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace forward slashes with percent signs so they aren't parsed as line comments
|
||||||
|
objBuf.ReplaceChars('/', *newSideSep);
|
||||||
|
char* wObjBuf = objBuf.LockBuffer();
|
||||||
|
|
||||||
|
// Substitute broken usemtl statements with old ones
|
||||||
|
for (size_t i = 0; i < mtlUsages.Size(); i++)
|
||||||
|
{
|
||||||
|
bpos = mtlUsageIdxs[i];
|
||||||
|
nlpos = objBuf.IndexOf('\n', bpos);
|
||||||
|
if (nlpos == -1)
|
||||||
|
{
|
||||||
|
nlpos = objBuf.Len();
|
||||||
|
}
|
||||||
|
memcpy(wObjBuf + bpos, mtlUsages[i].GetChars(), nlpos - bpos);
|
||||||
|
}
|
||||||
|
|
||||||
|
bpos = 0;
|
||||||
|
// Find each OBJ line comment, and convert each to a C-style line comment
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
bpos = objBuf.IndexOf('#', bpos);
|
||||||
|
if (bpos == -1) break;
|
||||||
|
if (objBuf[(unsigned int)bpos + 1] == '\n')
|
||||||
|
{
|
||||||
|
wObjBuf[bpos] = ' ';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wObjBuf[bpos] = '/';
|
||||||
|
wObjBuf[bpos+1] = '/';
|
||||||
|
}
|
||||||
|
bpos += 1;
|
||||||
|
}
|
||||||
|
wObjBuf = nullptr;
|
||||||
|
objBuf.UnlockBuffer();
|
||||||
|
}
|
||||||
|
sc.OpenString(objName, objBuf);
|
||||||
|
|
||||||
|
FTextureID curMtl = FNullTextureID();
|
||||||
|
OBJSurface *curSurface = nullptr;
|
||||||
|
int aggSurfFaceCount = 0;
|
||||||
|
int curSurfFaceCount = 0;
|
||||||
|
|
||||||
|
while(sc.GetString())
|
||||||
|
{
|
||||||
|
if (sc.Compare("v")) // Vertex
|
||||||
|
{
|
||||||
|
ParseVector<FVector3, 3>(this->verts);
|
||||||
|
}
|
||||||
|
else if (sc.Compare("vn")) // Vertex normal
|
||||||
|
{
|
||||||
|
ParseVector<FVector3, 3>(this->norms);
|
||||||
|
}
|
||||||
|
else if (sc.Compare("vt")) // UV Coordinates
|
||||||
|
{
|
||||||
|
ParseVector<FVector2, 2>(this->uvs);
|
||||||
|
}
|
||||||
|
else if (sc.Compare("usemtl"))
|
||||||
|
{
|
||||||
|
// Get material name and try to load it
|
||||||
|
sc.MustGetString();
|
||||||
|
|
||||||
|
curMtl = LoadSkin("", sc.String);
|
||||||
|
if (!curMtl.isValid())
|
||||||
|
{
|
||||||
|
// Relative to model file path?
|
||||||
|
curMtl = LoadSkin(fn, sc.String);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!curMtl.isValid())
|
||||||
|
{
|
||||||
|
sc.ScriptMessage("Material %s (#%u) not found.", sc.String, surfaces.Size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build surface...
|
||||||
|
if (curSurface == nullptr)
|
||||||
|
{
|
||||||
|
// First surface
|
||||||
|
curSurface = new OBJSurface(curMtl);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (curSurfFaceCount > 0)
|
||||||
|
{
|
||||||
|
// Add previous surface
|
||||||
|
curSurface->numFaces = curSurfFaceCount;
|
||||||
|
curSurface->faceStart = aggSurfFaceCount;
|
||||||
|
surfaces.Push(*curSurface);
|
||||||
|
delete curSurface;
|
||||||
|
// Go to next surface
|
||||||
|
curSurface = new OBJSurface(curMtl);
|
||||||
|
aggSurfFaceCount += curSurfFaceCount;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
curSurface->skin = curMtl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
curSurfFaceCount = 0;
|
||||||
|
}
|
||||||
|
else if (sc.Compare("f"))
|
||||||
|
{
|
||||||
|
FString sides[4];
|
||||||
|
OBJFace face;
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
// A face must have at least 3 sides
|
||||||
|
sc.MustGetString();
|
||||||
|
sides[i] = sc.String;
|
||||||
|
if (!ParseFaceSide(sides[i], face, i)) return false;
|
||||||
|
}
|
||||||
|
face.sideCount = 3;
|
||||||
|
if (sc.GetString())
|
||||||
|
{
|
||||||
|
if (!sc.Compare("f") && FString(sc.String).IndexOfAny("-0123456789") == 0)
|
||||||
|
{
|
||||||
|
sides[3] = sc.String;
|
||||||
|
face.sideCount += 1;
|
||||||
|
if (!ParseFaceSide(sides[3], face, 3)) return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sc.UnGet(); // No 4th side, move back
|
||||||
|
}
|
||||||
|
}
|
||||||
|
faces.Push(face);
|
||||||
|
curSurfFaceCount += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sc.Close();
|
||||||
|
|
||||||
|
if (curSurface == nullptr)
|
||||||
|
{ // No valid materials detected
|
||||||
|
FTextureID dummyMtl = LoadSkin("", "-NOFLAT-"); // Built-in to GZDoom
|
||||||
|
curSurface = new OBJSurface(dummyMtl);
|
||||||
|
}
|
||||||
|
curSurface->numFaces = curSurfFaceCount;
|
||||||
|
curSurface->faceStart = aggSurfFaceCount;
|
||||||
|
surfaces.Push(*curSurface);
|
||||||
|
delete curSurface;
|
||||||
|
|
||||||
|
if (uvs.Size() == 0)
|
||||||
|
{ // Needed so that OBJs without UVs can work
|
||||||
|
uvs.Push(FVector2(0.0, 0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse an x-Dimensional vector
|
||||||
|
*
|
||||||
|
* @tparam T A subclass of TVector2 to be used
|
||||||
|
* @tparam L The length of the vector to parse
|
||||||
|
* @param[out] array The array to append the parsed vector to
|
||||||
|
*/
|
||||||
|
template<typename T, size_t L> void FOBJModel::ParseVector(TArray<T> &array)
|
||||||
|
{
|
||||||
|
float *coord = new float[L];
|
||||||
|
for (size_t axis = 0; axis < L; axis++)
|
||||||
|
{
|
||||||
|
sc.MustGetFloat();
|
||||||
|
coord[axis] = (float)sc.Float;
|
||||||
|
}
|
||||||
|
T vec(coord);
|
||||||
|
array.Push(vec);
|
||||||
|
delete[] coord;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a side of a face
|
||||||
|
*
|
||||||
|
* @param[in] sideStr The side definition string
|
||||||
|
* @param[out] face The face to assign the parsed side data to
|
||||||
|
* @param sidx The 0-based index of the side
|
||||||
|
* @return Whether or not the face side was parsed successfully
|
||||||
|
*/
|
||||||
|
bool FOBJModel::ParseFaceSide(const FString &sideStr, OBJFace &face, int sidx)
|
||||||
|
{
|
||||||
|
OBJFaceSide side;
|
||||||
|
int origIdx;
|
||||||
|
if (sideStr.IndexOf(newSideSep) >= 0)
|
||||||
|
{
|
||||||
|
TArray<FString> sides = sideStr.Split(newSideSep, FString::TOK_KEEPEMPTY);
|
||||||
|
|
||||||
|
if (sides[0].Len() > 0)
|
||||||
|
{
|
||||||
|
origIdx = atoi(sides[0].GetChars());
|
||||||
|
side.vertref = ResolveIndex(origIdx, FaceElement::VertexIndex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sc.ScriptError("Vertex reference is not optional!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sides[1].Len() > 0)
|
||||||
|
{
|
||||||
|
origIdx = atoi(sides[1].GetChars());
|
||||||
|
side.uvref = ResolveIndex(origIdx, FaceElement::UVIndex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
side.uvref = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sides.Size() > 2)
|
||||||
|
{
|
||||||
|
if (sides[2].Len() > 0)
|
||||||
|
{
|
||||||
|
origIdx = atoi(sides[2].GetChars());
|
||||||
|
side.normref = ResolveIndex(origIdx, FaceElement::VNormalIndex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
side.normref = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
side.normref = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
origIdx = atoi(sideStr.GetChars());
|
||||||
|
side.vertref = ResolveIndex(origIdx, FaceElement::VertexIndex);
|
||||||
|
side.normref = -1;
|
||||||
|
side.uvref = -1;
|
||||||
|
}
|
||||||
|
face.sides[sidx] = side;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve an OBJ index to an absolute index
|
||||||
|
*
|
||||||
|
* OBJ indices are 1-based, and can also be negative
|
||||||
|
*
|
||||||
|
* @param origIndex The original OBJ index to resolve
|
||||||
|
* @param el What type of element the index references
|
||||||
|
* @return The absolute index of the element
|
||||||
|
*/
|
||||||
|
int FOBJModel::ResolveIndex(int origIndex, FaceElement el)
|
||||||
|
{
|
||||||
|
if (origIndex > 0)
|
||||||
|
{
|
||||||
|
return origIndex - 1; // OBJ indices start at 1
|
||||||
|
}
|
||||||
|
else if (origIndex < 0)
|
||||||
|
{
|
||||||
|
if (el == FaceElement::VertexIndex)
|
||||||
|
{
|
||||||
|
return verts.Size() + origIndex; // origIndex is negative
|
||||||
|
}
|
||||||
|
else if (el == FaceElement::UVIndex)
|
||||||
|
{
|
||||||
|
return uvs.Size() + origIndex;
|
||||||
|
}
|
||||||
|
else if (el == FaceElement::VNormalIndex)
|
||||||
|
{
|
||||||
|
return norms.Size() + origIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct the vertex buffer for this model
|
||||||
|
*
|
||||||
|
* @param renderer A pointer to the model renderer. Used to allocate the vertex buffer.
|
||||||
|
*/
|
||||||
|
void FOBJModel::BuildVertexBuffer(FModelRenderer *renderer)
|
||||||
|
{
|
||||||
|
if (GetVertexBuffer(renderer))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int vbufsize = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < surfaces.Size(); i++)
|
||||||
|
{
|
||||||
|
ConstructSurfaceTris(surfaces[i]);
|
||||||
|
surfaces[i].vbStart = vbufsize;
|
||||||
|
vbufsize += surfaces[i].numTris * 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto vbuf = renderer->CreateVertexBuffer(false,true);
|
||||||
|
SetVertexBuffer(renderer, vbuf);
|
||||||
|
|
||||||
|
FModelVertex *vertptr = vbuf->LockVertexBuffer(vbufsize);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < surfaces.Size(); i++)
|
||||||
|
{
|
||||||
|
for (size_t j = 0; j < surfaces[i].numTris; j++)
|
||||||
|
{
|
||||||
|
for (size_t side = 0; side < 3; side++)
|
||||||
|
{
|
||||||
|
FModelVertex *mdv = vertptr +
|
||||||
|
side + j * 3 + // Current surface and previous triangles
|
||||||
|
surfaces[i].vbStart; // Previous surfaces
|
||||||
|
|
||||||
|
OBJFaceSide &curSide = surfaces[i].tris[j].sides[side];
|
||||||
|
|
||||||
|
int vidx = curSide.vertref;
|
||||||
|
int uvidx = (curSide.uvref >= 0 && (unsigned int)curSide.uvref < uvs.Size()) ? curSide.uvref : 0;
|
||||||
|
int nidx = curSide.normref;
|
||||||
|
|
||||||
|
FVector3 curVvec = RealignVector(verts[vidx]);
|
||||||
|
FVector2 curUvec = FixUV(uvs[uvidx]);
|
||||||
|
FVector3 *nvec = nullptr;
|
||||||
|
|
||||||
|
mdv->Set(curVvec.X, curVvec.Y, curVvec.Z, curUvec.X, curUvec.Y);
|
||||||
|
|
||||||
|
if (nidx >= 0 && (unsigned int)nidx < norms.Size())
|
||||||
|
{
|
||||||
|
nvec = new FVector3(RealignVector(norms[nidx]));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal
|
||||||
|
// Find other sides of triangle
|
||||||
|
int nextSidx = side + 2;
|
||||||
|
if (nextSidx >= 3) nextSidx -= 3;
|
||||||
|
|
||||||
|
int lastSidx = side + 1;
|
||||||
|
if (lastSidx >= 3) lastSidx -= 3;
|
||||||
|
|
||||||
|
OBJFaceSide &nextSide = surfaces[i].tris[j].sides[nextSidx];
|
||||||
|
OBJFaceSide &lastSide = surfaces[i].tris[j].sides[lastSidx];
|
||||||
|
|
||||||
|
// Cross-multiply the U-vector and V-vector
|
||||||
|
FVector3 uvec = RealignVector(verts[nextSide.vertref]) - curVvec;
|
||||||
|
FVector3 vvec = RealignVector(verts[lastSide.vertref]) - curVvec;
|
||||||
|
|
||||||
|
nvec = new FVector3(uvec ^ vvec);
|
||||||
|
}
|
||||||
|
mdv->SetNormal(nvec->X, nvec->Y, nvec->Z);
|
||||||
|
delete nvec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete[] surfaces[i].tris;
|
||||||
|
}
|
||||||
|
vbuf->UnlockVertexBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fill in the triangle data for a surface
|
||||||
|
*
|
||||||
|
* @param[in,out] surf The surface to fill in the triangle data for
|
||||||
|
*/
|
||||||
|
void FOBJModel::ConstructSurfaceTris(OBJSurface &surf)
|
||||||
|
{
|
||||||
|
unsigned int triCount = 0;
|
||||||
|
|
||||||
|
size_t start = surf.faceStart;
|
||||||
|
size_t end = start + surf.numFaces;
|
||||||
|
for (size_t i = start; i < end; i++)
|
||||||
|
{
|
||||||
|
triCount += faces[i].sideCount - 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
surf.numTris = triCount;
|
||||||
|
surf.tris = new OBJFace[triCount];
|
||||||
|
|
||||||
|
for (size_t i = start, triIdx = 0; i < end; i++, triIdx++)
|
||||||
|
{
|
||||||
|
surf.tris[triIdx].sideCount = 3;
|
||||||
|
if (faces[i].sideCount == 3)
|
||||||
|
{
|
||||||
|
memcpy(surf.tris[triIdx].sides, faces[i].sides, sizeof(OBJFaceSide) * 3);
|
||||||
|
}
|
||||||
|
else if (faces[i].sideCount == 4) // Triangulate face
|
||||||
|
{
|
||||||
|
OBJFace *triangulated = new OBJFace[2];
|
||||||
|
TriangulateQuad(faces[i], triangulated);
|
||||||
|
memcpy(surf.tris[triIdx].sides, triangulated[0].sides, sizeof(OBJFaceSide) * 3);
|
||||||
|
memcpy(surf.tris[triIdx+1].sides, triangulated[1].sides, sizeof(OBJFaceSide) * 3);
|
||||||
|
delete[] triangulated;
|
||||||
|
triIdx += 1; // Filling out two faces
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triangulate a 4-sided face
|
||||||
|
*
|
||||||
|
* @param[in] quad The 4-sided face to triangulate
|
||||||
|
* @param[out] tris The resultant triangle data
|
||||||
|
*/
|
||||||
|
void FOBJModel::TriangulateQuad(const OBJFace &quad, OBJFace *tris)
|
||||||
|
{
|
||||||
|
tris[0].sideCount = 3;
|
||||||
|
tris[1].sideCount = 3;
|
||||||
|
|
||||||
|
int tsidx[2][3] = {{0, 1, 3}, {1, 2, 3}};
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < 2; j++)
|
||||||
|
{
|
||||||
|
tris[j].sides[i].vertref = quad.sides[tsidx[j][i]].vertref;
|
||||||
|
tris[j].sides[i].uvref = quad.sides[tsidx[j][i]].uvref;
|
||||||
|
tris[j].sides[i].normref = quad.sides[tsidx[j][i]].normref;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Re-align a vector to match MD3 alignment
|
||||||
|
*
|
||||||
|
* @param vecToRealign The vector to re-align
|
||||||
|
* @return The re-aligned vector
|
||||||
|
*/
|
||||||
|
inline FVector3 FOBJModel::RealignVector(FVector3 vecToRealign)
|
||||||
|
{
|
||||||
|
vecToRealign.Z *= -1;
|
||||||
|
return vecToRealign;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fix UV coordinates of a UV vector
|
||||||
|
*
|
||||||
|
* @param vecToRealign The vector to fix
|
||||||
|
* @return The fixed UV coordinate vector
|
||||||
|
*/
|
||||||
|
inline FVector2 FOBJModel::FixUV(FVector2 vecToRealign)
|
||||||
|
{
|
||||||
|
vecToRealign.Y *= -1;
|
||||||
|
return vecToRealign;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the index of the frame with the given name
|
||||||
|
*
|
||||||
|
* OBJ models are not animated, so this always returns 0
|
||||||
|
*
|
||||||
|
* @param name The name of the frame
|
||||||
|
* @return The index of the frame
|
||||||
|
*/
|
||||||
|
int FOBJModel::FindFrame(const char* name)
|
||||||
|
{
|
||||||
|
return 0; // OBJs are not animated.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the model
|
||||||
|
*
|
||||||
|
* @param renderer The model renderer
|
||||||
|
* @param skin The loaded skin for the surface
|
||||||
|
* @param frameno Unused
|
||||||
|
* @param frameno2 Unused
|
||||||
|
* @param inter Unused
|
||||||
|
* @param translation The translation for the skin
|
||||||
|
*/
|
||||||
|
void FOBJModel::RenderFrame(FModelRenderer *renderer, FTexture * skin, int frameno, int frameno2, double inter, int translation)
|
||||||
|
{
|
||||||
|
for (unsigned int i = 0; i < surfaces.Size(); i++)
|
||||||
|
{
|
||||||
|
OBJSurface *surf = &surfaces[i];
|
||||||
|
|
||||||
|
FTexture *userSkin = skin;
|
||||||
|
if (!userSkin)
|
||||||
|
{
|
||||||
|
if (i < MD3_MAX_SURFACES && curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].isValid())
|
||||||
|
{
|
||||||
|
userSkin = TexMan(curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i]);
|
||||||
|
}
|
||||||
|
else if (surf->skin.isValid())
|
||||||
|
{
|
||||||
|
userSkin = TexMan(surf->skin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Still no skin after checking for one?
|
||||||
|
if (!userSkin)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer->SetMaterial(userSkin, false, translation);
|
||||||
|
GetVertexBuffer(renderer)->SetupFrame(renderer, surf->vbStart, surf->vbStart, surf->numTris * 3);
|
||||||
|
renderer->DrawArrays(0, surf->numTris * 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pre-cache skins for the model
|
||||||
|
*
|
||||||
|
* @param hitlist The list of textures
|
||||||
|
*/
|
||||||
|
void FOBJModel::AddSkins(uint8_t* hitlist)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < surfaces.Size(); i++)
|
||||||
|
{
|
||||||
|
if (i < MD3_MAX_SURFACES && curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].isValid())
|
||||||
|
{
|
||||||
|
// Precache skins manually reassigned by the user.
|
||||||
|
// On OBJs with lots of skins, such as Doom map OBJs exported from GZDB,
|
||||||
|
// there may be too many skins for the user to manually change, unless
|
||||||
|
// the limit is bumped or surfaceskinIDs is changed to a TArray<FTextureID>.
|
||||||
|
hitlist[curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].GetIndex()] |= FTextureManager::HIT_Flat;
|
||||||
|
return; // No need to precache skin that was replaced
|
||||||
|
}
|
||||||
|
|
||||||
|
OBJSurface * surf = &surfaces[i];
|
||||||
|
if (surf->skin.isValid())
|
||||||
|
{
|
||||||
|
hitlist[surf->skin.GetIndex()] |= FTextureManager::HIT_Flat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the data that was loaded
|
||||||
|
*/
|
||||||
|
FOBJModel::~FOBJModel()
|
||||||
|
{
|
||||||
|
verts.Clear();
|
||||||
|
norms.Clear();
|
||||||
|
uvs.Clear();
|
||||||
|
faces.Clear();
|
||||||
|
surfaces.Clear();
|
||||||
|
}
|
87
src/r_data/models/models_obj.h
Normal file
87
src/r_data/models/models_obj.h
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Copyright(C) 2018 Kevin Caccamo
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||||
|
//
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef __GL_MODELS_OBJ_H__
|
||||||
|
#define __GL_MODELS_OBJ_H__
|
||||||
|
|
||||||
|
#include "models.h"
|
||||||
|
#include "sc_man.h"
|
||||||
|
|
||||||
|
class FOBJModel : public FModel
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
const char *newSideSep = "$"; // OBJ side separator is /, which is parsed as a line comment by FScanner if two of them are next to each other.
|
||||||
|
|
||||||
|
enum class FaceElement
|
||||||
|
{
|
||||||
|
VertexIndex,
|
||||||
|
UVIndex,
|
||||||
|
VNormalIndex
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OBJFaceSide
|
||||||
|
{
|
||||||
|
int vertref;
|
||||||
|
int normref;
|
||||||
|
int uvref;
|
||||||
|
};
|
||||||
|
struct OBJFace
|
||||||
|
{
|
||||||
|
unsigned int sideCount;
|
||||||
|
OBJFaceSide sides[4];
|
||||||
|
};
|
||||||
|
struct OBJSurface // 1 surface per 'usemtl'
|
||||||
|
{
|
||||||
|
unsigned int numTris; // Number of triangulated faces
|
||||||
|
unsigned int numFaces; // Number of faces
|
||||||
|
unsigned int vbStart; // First index in vertex buffer
|
||||||
|
unsigned int faceStart; // Index of first face in faces array
|
||||||
|
OBJFace* tris; // Triangles
|
||||||
|
FTextureID skin;
|
||||||
|
OBJSurface(FTextureID skin): numTris(0), numFaces(0), vbStart(0), faceStart(0), tris(nullptr), skin(skin) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
TArray<FVector3> verts;
|
||||||
|
TArray<FVector3> norms;
|
||||||
|
TArray<FVector2> uvs;
|
||||||
|
TArray<OBJFace> faces;
|
||||||
|
TArray<OBJSurface> surfaces;
|
||||||
|
FScanner sc;
|
||||||
|
|
||||||
|
template<typename T, size_t L> void ParseVector(TArray<T> &array);
|
||||||
|
bool ParseFaceSide(const FString &side, OBJFace &face, int sidx);
|
||||||
|
void ConstructSurfaceTris(OBJSurface &surf);
|
||||||
|
int ResolveIndex(int origIndex, FaceElement el);
|
||||||
|
void TriangulateQuad(const OBJFace &quad, OBJFace *tris);
|
||||||
|
FVector3 RealignVector(FVector3 vecToRealign);
|
||||||
|
FVector2 FixUV(FVector2 vecToRealign);
|
||||||
|
public:
|
||||||
|
FOBJModel() {}
|
||||||
|
~FOBJModel();
|
||||||
|
bool Load(const char* fn, int lumpnum, const char* buffer, int length) override;
|
||||||
|
int FindFrame(const char* name) override;
|
||||||
|
void RenderFrame(FModelRenderer* renderer, FTexture* skin, int frame, int frame2, double inter, int translation=0) override;
|
||||||
|
void BuildVertexBuffer(FModelRenderer* renderer) override;
|
||||||
|
void AddSkins(uint8_t* hitlist) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -776,8 +776,8 @@ static void CalcPosVel(int type, const AActor *actor, const sector_t *sector,
|
||||||
{
|
{
|
||||||
DVector2 disp = level.Displacements.getOffset(pgroup, poly->CenterSubsector->sector->PortalGroup);
|
DVector2 disp = level.Displacements.getOffset(pgroup, poly->CenterSubsector->sector->PortalGroup);
|
||||||
CalcPolyobjSoundOrg(listenpos + disp, poly, *pos);
|
CalcPolyobjSoundOrg(listenpos + disp, poly, *pos);
|
||||||
pos->X += (float)disp.X;
|
pos->X -= (float)disp.X;
|
||||||
pos->Z += (float)disp.Y;
|
pos->Z -= (float)disp.Y;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,15 +114,8 @@ public:
|
||||||
{
|
{
|
||||||
ID = S_FindSound(name.GetChars());
|
ID = S_FindSound(name.GetChars());
|
||||||
}
|
}
|
||||||
FSoundID(const FSoundID &other)
|
FSoundID(const FSoundID &other) = default;
|
||||||
{
|
FSoundID &operator=(const FSoundID &other) = default;
|
||||||
ID = other.ID;
|
|
||||||
}
|
|
||||||
FSoundID &operator=(const FSoundID &other)
|
|
||||||
{
|
|
||||||
ID = other.ID;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
FSoundID &operator=(const char *name)
|
FSoundID &operator=(const char *name)
|
||||||
{
|
{
|
||||||
ID = S_FindSound(name);
|
ID = S_FindSound(name);
|
||||||
|
@ -168,19 +161,7 @@ class FSoundIDNoInit : public FSoundID
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FSoundIDNoInit() : FSoundID(NoInit) {}
|
FSoundIDNoInit() : FSoundID(NoInit) {}
|
||||||
|
using FSoundID::operator=;
|
||||||
FSoundID &operator=(const FSoundID &other)
|
|
||||||
{
|
|
||||||
return FSoundID::operator=(other);
|
|
||||||
}
|
|
||||||
FSoundID &operator=(const char *name)
|
|
||||||
{
|
|
||||||
return FSoundID::operator=(name);
|
|
||||||
}
|
|
||||||
FSoundID &operator=(const FString &name)
|
|
||||||
{
|
|
||||||
return FSoundID::operator=(name);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern FRolloffInfo S_Rolloff;
|
extern FRolloffInfo S_Rolloff;
|
||||||
|
|
|
@ -327,7 +327,7 @@ static FFlagDef ActorFlagDefs[]=
|
||||||
DEFINE_FLAG(RF, INVISIBLE, AActor, renderflags),
|
DEFINE_FLAG(RF, INVISIBLE, AActor, renderflags),
|
||||||
DEFINE_FLAG(RF, FORCEYBILLBOARD, AActor, renderflags),
|
DEFINE_FLAG(RF, FORCEYBILLBOARD, AActor, renderflags),
|
||||||
DEFINE_FLAG(RF, FORCEXYBILLBOARD, AActor, renderflags),
|
DEFINE_FLAG(RF, FORCEXYBILLBOARD, AActor, renderflags),
|
||||||
DEFINE_FLAG(RF, ROLLSPRITE, AActor, renderflags), // [marrub] roll the sprite billboard
|
DEFINE_FLAG(RF, ROLLSPRITE, AActor, renderflags), // [marrub] roll the sprite billboard
|
||||||
// [fgsfds] Flat sprites
|
// [fgsfds] Flat sprites
|
||||||
DEFINE_FLAG(RF, FLATSPRITE, AActor, renderflags),
|
DEFINE_FLAG(RF, FLATSPRITE, AActor, renderflags),
|
||||||
DEFINE_FLAG(RF, WALLSPRITE, AActor, renderflags),
|
DEFINE_FLAG(RF, WALLSPRITE, AActor, renderflags),
|
||||||
|
@ -593,7 +593,7 @@ FFlagDef *FindFlag (const PClass *type, const char *part1, const char *part2, bo
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// Gets the name of an actor flag
|
// Gets the name of an actor flag
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
|
@ -1266,6 +1266,14 @@ DEFINE_ACTION_FUNCTION(FStringStruct, IndexOf)
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_ACTION_FUNCTION(FStringStruct, LastIndexOf)
|
DEFINE_ACTION_FUNCTION(FStringStruct, LastIndexOf)
|
||||||
|
{
|
||||||
|
PARAM_SELF_STRUCT_PROLOGUE(FString);
|
||||||
|
PARAM_STRING(substr);
|
||||||
|
PARAM_INT_DEF(endIndex);
|
||||||
|
ACTION_RETURN_INT(self->LastIndexOfBroken(substr, endIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_ACTION_FUNCTION(FStringStruct, RightIndexOf)
|
||||||
{
|
{
|
||||||
PARAM_SELF_STRUCT_PROLOGUE(FString);
|
PARAM_SELF_STRUCT_PROLOGUE(FString);
|
||||||
PARAM_STRING(substr);
|
PARAM_STRING(substr);
|
||||||
|
|
12
src/stats.h
12
src/stats.h
|
@ -57,12 +57,6 @@ public:
|
||||||
class cycle_t
|
class cycle_t
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
cycle_t &operator= (const cycle_t &o)
|
|
||||||
{
|
|
||||||
Sec = o.Sec;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Reset()
|
void Reset()
|
||||||
{
|
{
|
||||||
Sec = 0;
|
Sec = 0;
|
||||||
|
@ -153,12 +147,6 @@ inline uint64_t rdtsc()
|
||||||
class cycle_t
|
class cycle_t
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
cycle_t &operator= (const cycle_t &o)
|
|
||||||
{
|
|
||||||
Counter = o.Counter;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Reset()
|
void Reset()
|
||||||
{
|
{
|
||||||
Counter = 0;
|
Counter = 0;
|
||||||
|
|
|
@ -961,8 +961,6 @@ PalEntry FTexture::averageColor(const uint32_t *data, int size, int maxout)
|
||||||
|
|
||||||
PalEntry FTexture::GetSkyCapColor(bool bottom)
|
PalEntry FTexture::GetSkyCapColor(bool bottom)
|
||||||
{
|
{
|
||||||
PalEntry col;
|
|
||||||
|
|
||||||
if (!bSWSkyColorDone)
|
if (!bSWSkyColorDone)
|
||||||
{
|
{
|
||||||
bSWSkyColorDone = true;
|
bSWSkyColorDone = true;
|
||||||
|
|
|
@ -594,6 +594,46 @@ void F2DDrawer::AddLine(int x1, int y1, int x2, int y2, int palcolor, uint32_t c
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
|
void F2DDrawer::AddThickLine(int x1, int y1, int x2, int y2, double thickness, uint32_t color)
|
||||||
|
{
|
||||||
|
PalEntry p = (PalEntry)color;
|
||||||
|
|
||||||
|
DVector2 point0(x1, y1);
|
||||||
|
DVector2 point1(x2, y2);
|
||||||
|
|
||||||
|
DVector2 delta = point1 - point0;
|
||||||
|
DVector2 perp(-delta.Y, delta.X);
|
||||||
|
perp.MakeUnit();
|
||||||
|
perp *= thickness / 2;
|
||||||
|
|
||||||
|
DVector2 corner0 = point0 + perp;
|
||||||
|
DVector2 corner1 = point0 - perp;
|
||||||
|
DVector2 corner2 = point1 + perp;
|
||||||
|
DVector2 corner3 = point1 - perp;
|
||||||
|
|
||||||
|
RenderCommand dg;
|
||||||
|
|
||||||
|
dg.mType = DrawTypeTriangles;
|
||||||
|
dg.mVertCount = 4;
|
||||||
|
dg.mVertIndex = (int)mVertices.Reserve(4);
|
||||||
|
dg.mRenderStyle = LegacyRenderStyles[STYLE_Translucent];
|
||||||
|
auto ptr = &mVertices[dg.mVertIndex];
|
||||||
|
ptr->Set(corner0.X, corner0.Y, 0, 0, 0, p); ptr++;
|
||||||
|
ptr->Set(corner1.X, corner1.Y, 0, 0, 0, p); ptr++;
|
||||||
|
ptr->Set(corner2.X, corner2.Y, 0, 0, 0, p); ptr++;
|
||||||
|
ptr->Set(corner3.X, corner3.Y, 0, 0, 0, p); ptr++;
|
||||||
|
dg.mIndexIndex = mIndices.Size();
|
||||||
|
dg.mIndexCount += 6;
|
||||||
|
AddIndices(dg.mVertIndex, 6, 0, 1, 2, 1, 3, 2);
|
||||||
|
AddCommand(&dg);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
void F2DDrawer::AddPixel(int x1, int y1, int palcolor, uint32_t color)
|
void F2DDrawer::AddPixel(int x1, int y1, int palcolor, uint32_t color)
|
||||||
{
|
{
|
||||||
PalEntry p = color ? (PalEntry)color : GPalette.BaseColors[palcolor];
|
PalEntry p = color ? (PalEntry)color : GPalette.BaseColors[palcolor];
|
||||||
|
|
|
@ -149,6 +149,7 @@ public:
|
||||||
|
|
||||||
|
|
||||||
void AddLine(int x1, int y1, int x2, int y2, int palcolor, uint32_t color);
|
void AddLine(int x1, int y1, int x2, int y2, int palcolor, uint32_t color);
|
||||||
|
void AddThickLine(int x1, int y1, int x2, int y2, double thickness, uint32_t color);
|
||||||
void AddPixel(int x1, int y1, int palcolor, uint32_t color);
|
void AddPixel(int x1, int y1, int palcolor, uint32_t color);
|
||||||
|
|
||||||
void Clear();
|
void Clear();
|
||||||
|
|
|
@ -1088,6 +1088,24 @@ DEFINE_ACTION_FUNCTION(_Screen, DrawLine)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DFrameBuffer::DrawThickLine(int x0, int y0, int x1, int y1, double thickness, uint32_t realcolor) {
|
||||||
|
m2DDrawer.AddThickLine(x0, y0, x1, y1, thickness, realcolor);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_ACTION_FUNCTION(_Screen, DrawThickLine)
|
||||||
|
{
|
||||||
|
PARAM_PROLOGUE;
|
||||||
|
PARAM_INT(x0);
|
||||||
|
PARAM_INT(y0);
|
||||||
|
PARAM_INT(x1);
|
||||||
|
PARAM_INT(y1);
|
||||||
|
PARAM_FLOAT(thickness);
|
||||||
|
PARAM_INT(color);
|
||||||
|
if (!screen->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function");
|
||||||
|
screen->DrawThickLine(x0, y0, x1, y1, thickness, color);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// Draw a single pixel
|
// Draw a single pixel
|
||||||
|
|
|
@ -1995,7 +1995,14 @@ FSpecialFont::FSpecialFont (const char *name, int first, int count, FTexture **l
|
||||||
|
|
||||||
FixXMoves();
|
FixXMoves();
|
||||||
|
|
||||||
if (!noTranslate) LoadTranslations();
|
if (noTranslate)
|
||||||
|
{
|
||||||
|
ActiveColors = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LoadTranslations();
|
||||||
|
}
|
||||||
|
|
||||||
delete[] charlumps;
|
delete[] charlumps;
|
||||||
}
|
}
|
||||||
|
|
|
@ -498,6 +498,9 @@ public:
|
||||||
// Draws a line
|
// Draws a line
|
||||||
void DrawLine(int x0, int y0, int x1, int y1, int palColor, uint32_t realcolor);
|
void DrawLine(int x0, int y0, int x1, int y1, int palColor, uint32_t realcolor);
|
||||||
|
|
||||||
|
// Draws a line with thickness
|
||||||
|
void DrawThickLine(int x0, int y0, int x1, int y1, double thickness, uint32_t realcolor);
|
||||||
|
|
||||||
// Draws a single pixel
|
// Draws a single pixel
|
||||||
void DrawPixel(int x, int y, int palcolor, uint32_t rgbcolor);
|
void DrawPixel(int x, int y, int palcolor, uint32_t rgbcolor);
|
||||||
|
|
||||||
|
|
110
src/vectors.h
110
src/vectors.h
|
@ -66,19 +66,14 @@ struct TVector2
|
||||||
{
|
{
|
||||||
vec_t X, Y;
|
vec_t X, Y;
|
||||||
|
|
||||||
TVector2 ()
|
TVector2() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
TVector2 (vec_t a, vec_t b)
|
TVector2 (vec_t a, vec_t b)
|
||||||
: X(a), Y(b)
|
: X(a), Y(b)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
TVector2 (const TVector2 &other)
|
TVector2(const TVector2 &other) = default;
|
||||||
: X(other.X), Y(other.Y)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
TVector2 (const TVector3<vec_t> &other) // Copy the X and Y from the 3D vector and discard the Z
|
TVector2 (const TVector3<vec_t> &other) // Copy the X and Y from the 3D vector and discard the Z
|
||||||
: X(other.X), Y(other.Y)
|
: X(other.X), Y(other.Y)
|
||||||
|
@ -95,18 +90,7 @@ struct TVector2
|
||||||
return X == 0 && Y == 0;
|
return X == 0 && Y == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
TVector2 &operator= (const TVector2 &other)
|
TVector2 &operator= (const TVector2 &other) = default;
|
||||||
{
|
|
||||||
// This might seem backwards, but this helps produce smaller code when a newly
|
|
||||||
// created vector is assigned, because the components can just be popped off
|
|
||||||
// the FPU stack in order without the need for fxch. For platforms with a
|
|
||||||
// more sensible registered-based FPU, of course, the order doesn't matter.
|
|
||||||
// (And, yes, I know fxch can improve performance in the right circumstances,
|
|
||||||
// but this isn't one of those times. Here, it's little more than a no-op that
|
|
||||||
// makes the exe 2 bytes larger whenever you assign one vector to another.)
|
|
||||||
Y = other.Y, X = other.X;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Access X and Y as an array
|
// Access X and Y as an array
|
||||||
vec_t &operator[] (int index)
|
vec_t &operator[] (int index)
|
||||||
|
@ -317,9 +301,7 @@ struct TVector3
|
||||||
|
|
||||||
vec_t X, Y, Z;
|
vec_t X, Y, Z;
|
||||||
|
|
||||||
TVector3 ()
|
TVector3() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
TVector3 (vec_t a, vec_t b, vec_t c)
|
TVector3 (vec_t a, vec_t b, vec_t c)
|
||||||
: X(a), Y(b), Z(c)
|
: X(a), Y(b), Z(c)
|
||||||
|
@ -331,10 +313,7 @@ struct TVector3
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
TVector3 (const TVector3 &other)
|
TVector3(const TVector3 &other) = default;
|
||||||
: X(other.X), Y(other.Y), Z(other.Z)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
TVector3 (const Vector2 &xy, vec_t z)
|
TVector3 (const Vector2 &xy, vec_t z)
|
||||||
: X(xy.X), Y(xy.Y), Z(z)
|
: X(xy.X), Y(xy.Y), Z(z)
|
||||||
|
@ -353,11 +332,7 @@ struct TVector3
|
||||||
return X == 0 && Y == 0 && Z == 0;
|
return X == 0 && Y == 0 && Z == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
TVector3 &operator= (const TVector3 &other)
|
TVector3 &operator= (const TVector3 &other) = default;
|
||||||
{
|
|
||||||
Z = other.Z, Y = other.Y, X = other.X;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Access X and Y and Z as an array
|
// Access X and Y and Z as an array
|
||||||
vec_t &operator[] (int index)
|
vec_t &operator[] (int index)
|
||||||
|
@ -546,13 +521,12 @@ struct TVector3
|
||||||
{
|
{
|
||||||
right = { 0.f, 0.f, 1.f };
|
right = { 0.f, 0.f, 1.f };
|
||||||
}
|
}
|
||||||
|
else if (major == 1 || (major == 2 && n[2] > 0.f))
|
||||||
if (major == 1 || (major == 2 && n[2] > 0.f))
|
|
||||||
{
|
{
|
||||||
right = { 1.f, 0.f, 0.f };
|
right = { 1.f, 0.f, 0.f };
|
||||||
}
|
}
|
||||||
|
// Unconditional to ease static analysis
|
||||||
if (major == 2 && n[2] < 0.0f)
|
else // major == 2 && n[2] <= 0.0f
|
||||||
{
|
{
|
||||||
right = { -1.f, 0.f, 0.f };
|
right = { -1.f, 0.f, 0.f };
|
||||||
}
|
}
|
||||||
|
@ -662,9 +636,7 @@ struct TVector4
|
||||||
|
|
||||||
vec_t X, Y, Z, W;
|
vec_t X, Y, Z, W;
|
||||||
|
|
||||||
TVector4()
|
TVector4() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
TVector4(vec_t a, vec_t b, vec_t c, vec_t d)
|
TVector4(vec_t a, vec_t b, vec_t c, vec_t d)
|
||||||
: X(a), Y(b), Z(c), W(d)
|
: X(a), Y(b), Z(c), W(d)
|
||||||
|
@ -676,10 +648,7 @@ struct TVector4
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
TVector4(const TVector4 &other)
|
TVector4(const TVector4 &other) = default;
|
||||||
: X(other.X), Y(other.Y), Z(other.Z), W(other.W)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
TVector4(const Vector3 &xyz, vec_t w)
|
TVector4(const Vector3 &xyz, vec_t w)
|
||||||
: X(xyz.X), Y(xyz.Y), Z(xyz.Z), W(w)
|
: X(xyz.X), Y(xyz.Y), Z(xyz.Z), W(w)
|
||||||
|
@ -696,11 +665,7 @@ struct TVector4
|
||||||
return X == 0 && Y == 0 && Z == 0 && W == 0;
|
return X == 0 && Y == 0 && Z == 0 && W == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
TVector4 &operator= (const TVector4 &other)
|
TVector4 &operator= (const TVector4 &other) = default;
|
||||||
{
|
|
||||||
W = other.W, Z = other.Z, Y = other.Y, X = other.X;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Access X and Y and Z as an array
|
// Access X and Y and Z as an array
|
||||||
vec_t &operator[] (int index)
|
vec_t &operator[] (int index)
|
||||||
|
@ -939,16 +904,8 @@ struct TMatrix3x3
|
||||||
|
|
||||||
vec_t Cells[3][3];
|
vec_t Cells[3][3];
|
||||||
|
|
||||||
TMatrix3x3()
|
TMatrix3x3() = default;
|
||||||
{
|
TMatrix3x3(const TMatrix3x3 &other) = default;
|
||||||
}
|
|
||||||
|
|
||||||
TMatrix3x3(const TMatrix3x3 &other)
|
|
||||||
{
|
|
||||||
(*this)[0] = other[0];
|
|
||||||
(*this)[1] = other[1];
|
|
||||||
(*this)[2] = other[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
TMatrix3x3(const Vector3 &row1, const Vector3 &row2, const Vector3 &row3)
|
TMatrix3x3(const Vector3 &row1, const Vector3 &row2, const Vector3 &row3)
|
||||||
{
|
{
|
||||||
|
@ -1151,32 +1108,15 @@ struct TAngle
|
||||||
TAngle &operator= (long other) = delete;
|
TAngle &operator= (long other) = delete;
|
||||||
TAngle &operator= (unsigned long other) = delete;
|
TAngle &operator= (unsigned long other) = delete;
|
||||||
|
|
||||||
TAngle ()
|
TAngle() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
TAngle (vec_t amt)
|
TAngle (vec_t amt)
|
||||||
: Degrees(amt)
|
: Degrees(amt)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
TAngle(const TAngle &other) = default;
|
||||||
TAngle (int amt)
|
TAngle &operator= (const TAngle &other) = default;
|
||||||
: Degrees(vec_t(amt))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
TAngle (const TAngle &other)
|
|
||||||
: Degrees(other.Degrees)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
TAngle &operator= (const TAngle &other)
|
|
||||||
{
|
|
||||||
Degrees = other.Degrees;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
TAngle &operator= (double other)
|
TAngle &operator= (double other)
|
||||||
{
|
{
|
||||||
|
@ -1491,25 +1431,15 @@ struct TRotator
|
||||||
Angle Roll; // rotation about the forward axis.
|
Angle Roll; // rotation about the forward axis.
|
||||||
Angle CamRoll; // Roll specific to actor cameras. Used by quakes.
|
Angle CamRoll; // Roll specific to actor cameras. Used by quakes.
|
||||||
|
|
||||||
TRotator ()
|
TRotator() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
TRotator (const Angle &p, const Angle &y, const Angle &r)
|
TRotator (const Angle &p, const Angle &y, const Angle &r)
|
||||||
: Pitch(p), Yaw(y), Roll(r)
|
: Pitch(p), Yaw(y), Roll(r)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
TRotator (const TRotator &other)
|
TRotator(const TRotator &other) = default;
|
||||||
: Pitch(other.Pitch), Yaw(other.Yaw), Roll(other.Roll)
|
TRotator &operator= (const TRotator &other) = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
TRotator &operator= (const TRotator &other)
|
|
||||||
{
|
|
||||||
Roll = other.Roll, Yaw = other.Yaw, Pitch = other.Pitch;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Access angles as an array
|
// Access angles as an array
|
||||||
Angle &operator[] (int index)
|
Angle &operator[] (int index)
|
||||||
|
|
|
@ -55,9 +55,9 @@ const char *GetVersionString();
|
||||||
#define RC_FILEVERSION 3,5,9999,0
|
#define RC_FILEVERSION 3,5,9999,0
|
||||||
#define RC_PRODUCTVERSION 3,5,9999,0
|
#define RC_PRODUCTVERSION 3,5,9999,0
|
||||||
#define RC_PRODUCTVERSION2 VERSIONSTR
|
#define RC_PRODUCTVERSION2 VERSIONSTR
|
||||||
// These are for content versioning. The current state is '3.5'.
|
// These are for content versioning.
|
||||||
#define VER_MAJOR 3
|
#define VER_MAJOR 3
|
||||||
#define VER_MINOR 5
|
#define VER_MINOR 6
|
||||||
#define VER_REVISION 0
|
#define VER_REVISION 0
|
||||||
|
|
||||||
// Version identifier for network games.
|
// Version identifier for network games.
|
||||||
|
|
|
@ -507,31 +507,11 @@ long FString::IndexOfAny (const char *charset, long startIndex) const
|
||||||
return long(brk - Chars);
|
return long(brk - Chars);
|
||||||
}
|
}
|
||||||
|
|
||||||
long FString::LastIndexOf (const FString &substr) const
|
|
||||||
{
|
|
||||||
return LastIndexOf (substr.Chars, long(Len()), substr.Len());
|
|
||||||
}
|
|
||||||
|
|
||||||
long FString::LastIndexOf (const char *substr) const
|
|
||||||
{
|
|
||||||
return LastIndexOf (substr, long(Len()), strlen(substr));
|
|
||||||
}
|
|
||||||
|
|
||||||
long FString::LastIndexOf (char subchar) const
|
long FString::LastIndexOf (char subchar) const
|
||||||
{
|
{
|
||||||
return LastIndexOf (subchar, long(Len()));
|
return LastIndexOf (subchar, long(Len()));
|
||||||
}
|
}
|
||||||
|
|
||||||
long FString::LastIndexOf (const FString &substr, long endIndex) const
|
|
||||||
{
|
|
||||||
return LastIndexOf (substr.Chars, endIndex, substr.Len());
|
|
||||||
}
|
|
||||||
|
|
||||||
long FString::LastIndexOf (const char *substr, long endIndex) const
|
|
||||||
{
|
|
||||||
return LastIndexOf (substr, endIndex, strlen(substr));
|
|
||||||
}
|
|
||||||
|
|
||||||
long FString::LastIndexOf (char subchar, long endIndex) const
|
long FString::LastIndexOf (char subchar, long endIndex) const
|
||||||
{
|
{
|
||||||
if ((size_t)endIndex > Len())
|
if ((size_t)endIndex > Len())
|
||||||
|
@ -548,8 +528,10 @@ long FString::LastIndexOf (char subchar, long endIndex) const
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
long FString::LastIndexOf (const char *substr, long endIndex, size_t substrlen) const
|
long FString::LastIndexOfBroken (const FString &_substr, long endIndex) const
|
||||||
{
|
{
|
||||||
|
const char *substr = _substr.GetChars();
|
||||||
|
size_t substrlen = _substr.Len();
|
||||||
if ((size_t)endIndex > Len())
|
if ((size_t)endIndex > Len())
|
||||||
{
|
{
|
||||||
endIndex = long(Len());
|
endIndex = long(Len());
|
||||||
|
@ -596,6 +578,43 @@ long FString::LastIndexOfAny (const char *charset, long endIndex) const
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long FString::LastIndexOf (const FString &substr) const
|
||||||
|
{
|
||||||
|
return LastIndexOf(substr.Chars, long(Len() - substr.Len()), substr.Len());
|
||||||
|
}
|
||||||
|
|
||||||
|
long FString::LastIndexOf (const FString &substr, long endIndex) const
|
||||||
|
{
|
||||||
|
return LastIndexOf(substr.Chars, endIndex, substr.Len());
|
||||||
|
}
|
||||||
|
|
||||||
|
long FString::LastIndexOf (const char *substr) const
|
||||||
|
{
|
||||||
|
return LastIndexOf(substr, long(Len() - strlen(substr)), strlen(substr));
|
||||||
|
}
|
||||||
|
|
||||||
|
long FString::LastIndexOf (const char *substr, long endIndex) const
|
||||||
|
{
|
||||||
|
return LastIndexOf(substr, endIndex, strlen(substr));
|
||||||
|
}
|
||||||
|
|
||||||
|
long FString::LastIndexOf (const char *substr, long endIndex, size_t substrlen) const
|
||||||
|
{
|
||||||
|
if ((size_t)endIndex + substrlen > Len())
|
||||||
|
{
|
||||||
|
endIndex = long(Len() - substrlen);
|
||||||
|
}
|
||||||
|
while (endIndex >= 0)
|
||||||
|
{
|
||||||
|
if (strncmp (substr, Chars + endIndex, substrlen) == 0)
|
||||||
|
{
|
||||||
|
return endIndex;
|
||||||
|
}
|
||||||
|
endIndex--;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
void FString::ToUpper ()
|
void FString::ToUpper ()
|
||||||
{
|
{
|
||||||
LockBuffer();
|
LockBuffer();
|
||||||
|
@ -1003,7 +1022,7 @@ void FString::Substitute (const char *oldstr, const char *newstr, size_t oldstrl
|
||||||
|
|
||||||
bool FString::IsInt () const
|
bool FString::IsInt () const
|
||||||
{
|
{
|
||||||
// String must match: [whitespace] [{+ | –}] [0 [{ x | X }]] [digits] [whitespace]
|
// String must match: [whitespace] [{+ | <EFBFBD>}] [0 [{ x | X }]] [digits] [whitespace]
|
||||||
|
|
||||||
/* This state machine is based on a simplification of re2c's output for this input:
|
/* This state machine is based on a simplification of re2c's output for this input:
|
||||||
digits = [0-9];
|
digits = [0-9];
|
||||||
|
|
|
@ -201,19 +201,22 @@ public:
|
||||||
long IndexOfAny (const FString &charset, long startIndex=0) const;
|
long IndexOfAny (const FString &charset, long startIndex=0) const;
|
||||||
long IndexOfAny (const char *charset, long startIndex=0) const;
|
long IndexOfAny (const char *charset, long startIndex=0) const;
|
||||||
|
|
||||||
long LastIndexOf (const FString &substr) const;
|
// This is only kept for backwards compatibility with old ZScript versions that used this function and depend on its bug.
|
||||||
long LastIndexOf (const char *substr) const;
|
|
||||||
long LastIndexOf (char subchar) const;
|
long LastIndexOf (char subchar) const;
|
||||||
long LastIndexOf (const FString &substr, long endIndex) const;
|
long LastIndexOfBroken (const FString &substr, long endIndex) const;
|
||||||
long LastIndexOf (const char *substr, long endIndex) const;
|
|
||||||
long LastIndexOf (char subchar, long endIndex) const;
|
long LastIndexOf (char subchar, long endIndex) const;
|
||||||
long LastIndexOf (const char *substr, long endIndex, size_t substrlen) const;
|
|
||||||
|
|
||||||
long LastIndexOfAny (const FString &charset) const;
|
long LastIndexOfAny (const FString &charset) const;
|
||||||
long LastIndexOfAny (const char *charset) const;
|
long LastIndexOfAny (const char *charset) const;
|
||||||
long LastIndexOfAny (const FString &charset, long endIndex) const;
|
long LastIndexOfAny (const FString &charset, long endIndex) const;
|
||||||
long LastIndexOfAny (const char *charset, long endIndex) const;
|
long LastIndexOfAny (const char *charset, long endIndex) const;
|
||||||
|
|
||||||
|
long LastIndexOf (const FString &substr) const;
|
||||||
|
long LastIndexOf (const FString &substr, long endIndex) const;
|
||||||
|
long LastIndexOf (const char *substr) const;
|
||||||
|
long LastIndexOf (const char *substr, long endIndex) const;
|
||||||
|
long LastIndexOf (const char *substr, long endIndex, size_t substrlen) const;
|
||||||
|
|
||||||
void ToUpper ();
|
void ToUpper ();
|
||||||
void ToLower ();
|
void ToLower ();
|
||||||
void SwapCase ();
|
void SwapCase ();
|
||||||
|
@ -463,4 +466,3 @@ template<> struct THashTraits<FString>
|
||||||
// Compares two keys, returning zero if they are the same.
|
// Compares two keys, returning zero if they are the same.
|
||||||
int Compare(const FString &left, const FString &right) { return left.Compare(right); }
|
int Compare(const FString &left, const FString &right) { return left.Compare(right); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2820,6 +2820,8 @@ GLPREFMNU_DITHER = "Dither output";
|
||||||
GLPREFMNU_PALTONEMAPORDER = "Tonemap Palette Order";
|
GLPREFMNU_PALTONEMAPORDER = "Tonemap Palette Order";
|
||||||
GLPREFMNU_PALTONEMAPPOWER = "Tonemap Palette Exponent";
|
GLPREFMNU_PALTONEMAPPOWER = "Tonemap Palette Exponent";
|
||||||
GLPREFMNU_SWLMBANDED = "Banded SW Lightmode";
|
GLPREFMNU_SWLMBANDED = "Banded SW Lightmode";
|
||||||
|
GLPREFMNU_VRIPD = "Distance Between Your Eyes";
|
||||||
|
GLPREFMNU_VRSCREENDIST = "Distance From Your Screen";
|
||||||
|
|
||||||
// Option Values
|
// Option Values
|
||||||
OPTVAL_SMART = "Smart";
|
OPTVAL_SMART = "Smart";
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2290,6 +2290,8 @@ OptionMenu "OpenGLOptions" protected
|
||||||
{
|
{
|
||||||
Option "$GLPREFMNU_VRQUADSTEREO", vr_enable_quadbuffered, "OnOff"
|
Option "$GLPREFMNU_VRQUADSTEREO", vr_enable_quadbuffered, "OnOff"
|
||||||
}
|
}
|
||||||
|
Slider "$GLPREFMNU_VRIPD", vr_ipd, 0.041, 0.073, 0.001, 3
|
||||||
|
Slider "$GLPREFMNU_VRSCREENDIST", vr_screendist, 0.2, 10.0, 0.1, 1
|
||||||
StaticText " "
|
StaticText " "
|
||||||
Option "$GLPREFMNU_MULTISAMPLE", gl_multisample, "Multisample"
|
Option "$GLPREFMNU_MULTISAMPLE", gl_multisample, "Multisample"
|
||||||
Option "$GLPREFMNU_TONEMAP", gl_tonemap, "TonemapModes"
|
Option "$GLPREFMNU_TONEMAP", gl_tonemap, "TonemapModes"
|
||||||
|
|
|
@ -194,6 +194,7 @@ struct Screen native
|
||||||
native static vararg void DrawChar(Font font, int normalcolor, double x, double y, int character, ...);
|
native static vararg void DrawChar(Font font, int normalcolor, double x, double y, int character, ...);
|
||||||
native static vararg void DrawText(Font font, int normalcolor, double x, double y, String text, ...);
|
native static vararg void DrawText(Font font, int normalcolor, double x, double y, String text, ...);
|
||||||
native static void DrawLine(int x0, int y0, int x1, int y1, Color color);
|
native static void DrawLine(int x0, int y0, int x1, int y1, Color color);
|
||||||
|
native static void DrawThickLine(int x0, int y0, int x1, int y1, double thickness, Color color);
|
||||||
native static void DrawFrame(int x, int y, int w, int h);
|
native static void DrawFrame(int x, int y, int w, int h);
|
||||||
native static Vector2, Vector2 VirtualToRealCoords(Vector2 pos, Vector2 size, Vector2 vsize, bool vbottom=false, bool handleaspect=true);
|
native static Vector2, Vector2 VirtualToRealCoords(Vector2 pos, Vector2 size, Vector2 vsize, bool vbottom=false, bool handleaspect=true);
|
||||||
native static double GetAspectRatio();
|
native static double GetAspectRatio();
|
||||||
|
@ -815,7 +816,8 @@ struct StringStruct native
|
||||||
native int CharCodeAt(int pos) const;
|
native int CharCodeAt(int pos) const;
|
||||||
native String Filter();
|
native String Filter();
|
||||||
native int IndexOf(String substr, int startIndex = 0) const;
|
native int IndexOf(String substr, int startIndex = 0) const;
|
||||||
native int LastIndexOf(String substr, int endIndex = 2147483647) const;
|
deprecated("3.5.1") native int LastIndexOf(String substr, int endIndex = 2147483647) const;
|
||||||
|
native int RightIndexOf(String substr, int endIndex = 2147483647) const;
|
||||||
native void ToUpper();
|
native void ToUpper();
|
||||||
native void ToLower();
|
native void ToLower();
|
||||||
native int ToInt(int base = 0) const;
|
native int ToInt(int base = 0) const;
|
||||||
|
|
Loading…
Reference in a new issue