Merge remote-tracking branch 'gzdoom/master' into asmjit

This commit is contained in:
Magnus Norddahl 2018-09-02 03:46:06 +02:00
commit 55955b9c22
36 changed files with 1227 additions and 482 deletions

View file

@ -1154,6 +1154,7 @@ set (PCH_SOURCES
r_data/models/models_md2.cpp
r_data/models/models_voxel.cpp
r_data/models/models_ue1.cpp
r_data/models/models_obj.cpp
scripting/symbols.cpp
scripting/types.cpp
scripting/thingdef.cpp

View file

@ -38,6 +38,7 @@
#include "doomtype.h"
#include "configfile.h"
#include "files.h"
#include "m_random.h"
#define READBUFFERSIZE 256
@ -601,17 +602,16 @@ FConfigFile::FConfigEntry *FConfigFile::NewConfigEntry (
void FConfigFile::LoadConfigFile ()
{
FILE *file = fopen (PathName, "r");
FileReader file;
bool succ;
FileExisted = false;
if (file == NULL)
if (!file.OpenFile (PathName))
{
return;
}
succ = ReadConfig (file);
fclose (file);
succ = ReadConfig (&file);
FileExisted = succ;
}
@ -787,7 +787,7 @@ FConfigFile::FConfigEntry *FConfigFile::ReadMultiLineValue(void *file, FConfigSe
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;
}
FILE *file = fopen (PathName, "w");
FileWriter *file = FileWriter::Open (PathName);
FConfigSection *section;
FConfigEntry *entry;
@ -820,27 +820,27 @@ bool FConfigFile::WriteConfigFile () const
entry = section->RootEntry;
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)
{
if (strpbrk(entry->Value, "\r\n") == NULL)
{ // Single-line value
fprintf (file, "%s=%s\n", entry->Key, entry->Value);
file->Printf ("%s=%s\n", entry->Key, entry->Value);
}
else
{ // Multi-line 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);
}
entry = entry->Next;
}
section = section->Next;
fputs ("\n", file);
file->Write ("\n", 1);
}
fclose (file);
delete file;
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
{
}

View file

@ -35,6 +35,7 @@
#define __CONFIGFILE_H__
#include <stdio.h>
#include "files.h"
#include "zstring.h"
class FConfigFile
@ -73,7 +74,7 @@ public:
bool WriteConfigFile () const;
protected:
virtual void WriteCommentHeader (FILE *file) const;
virtual void WriteCommentHeader (FileWriter *file) const;
virtual char *ReadLine (char *string, int n, void *file) const;
bool ReadConfig (void *file);

View file

@ -468,7 +468,7 @@ public:
TObjPtr<AActor*> MUSINFOactor = nullptr; // For MUSINFO purposes
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 crouchdir = 0;

View file

@ -167,22 +167,19 @@ class TObjPtr
DObject *o;
};
public:
TObjPtr() throw()
{
}
TObjPtr() = default;
TObjPtr(const TObjPtr<T> &q) = default;
TObjPtr(T q) throw()
: pp(q)
{
}
TObjPtr(const TObjPtr<T> &q) throw()
: pp(q.pp)
T operator=(T q)
{
pp = q;
return *this;
}
T operator=(T q) throw()
{
return pp = q;
// The caller must now perform a write barrier.
}
operator T() throw()
{
return GC::ReadBarrier(pp);

View file

@ -103,7 +103,7 @@ enum
struct PalEntry
{
PalEntry () {}
PalEntry() = default;
PalEntry (uint32_t argb) { d = argb; }
operator uint32_t () const { return d; }
void SetRGB(PalEntry other)
@ -146,6 +146,7 @@ struct PalEntry
{
return (d & 0xffffff) == 0xffffff;
}
PalEntry &operator= (const PalEntry &other) = default;
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; }
#ifdef __BIG_ENDIAN__
@ -205,7 +206,7 @@ class FTextureID
friend void R_InitSpriteDefs();
public:
FTextureID() throw() {}
FTextureID() = default;
bool isNull() const { return texnum == 0; }
bool isValid() const { return texnum > 0; }
bool Exists() const { return texnum >= 0; }

View file

@ -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)

View file

@ -58,7 +58,7 @@ public:
void ReadNetVars ();
protected:
void WriteCommentHeader (FILE *file) const;
void WriteCommentHeader (FileWriter *file) const;
void CreateStandardAutoExec (const char *section, bool start);
private:

View file

@ -399,7 +399,6 @@ void GLHorizonPortal::DrawContents(HWDrawInfo *hwdi)
Clocker c(PortalAll);
FMaterial * gltexture;
PalEntry color;
player_t * player=&players[consoleplayer];
GLSectorPlane * sp = &origin->plane;
auto &vp = di->Viewpoint;

View file

@ -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.
// 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;
FVector2 axis;
for (int attempt = 0; attempt < 2; attempt++)
{
// Find the sort plane for axis

View file

@ -319,7 +319,6 @@ void FMaterial::SetSpriteRect()
bool FMaterial::TrimBorders(uint16_t *rect)
{
PalEntry col;
int w;
int h;

View file

@ -46,7 +46,7 @@ class FString;
class FName
{
public:
FName() = default;// : Index(0) {}
FName() = default;
FName (const char *text) { Index = NameData.FindName (text, false); }
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); }
@ -63,7 +63,7 @@ public:
FName &operator = (const char *text) { Index = NameData.FindName (text, false); return *this; }
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; }
int SetName (const char *text, bool noCreate=false) { return Index = NameData.FindName (text, noCreate); }

View file

@ -2345,7 +2345,6 @@ double P_XYMovement (AActor *mo, DVector2 scroll)
{
static int pushtime = 0;
bool bForceSlide = !scroll.isZero();
DAngle Angle;
DVector2 ptry;
player_t *player;
DVector2 move;

View file

@ -306,7 +306,7 @@ static void P_SetSlopesFromVertexHeights(FMapThing *firstmt, FMapThing *lastmt,
{
if (sec.Lines.Size() != 3) continue; // only works with triangular sectors
DVector3 vt1, vt2, vt3, cross;
DVector3 vt1, vt2, vt3;
DVector3 vec1, vec2;
int vi1, vi2, vi3;

View file

@ -153,13 +153,13 @@ static struct SteamAppInfo
const int AppID;
} AppInfo[] =
{
/*{"doom 2/base", 2300},
{"final doom/base", 2290},
{"heretic shadow of the serpent riders/base", 2390},
{"hexen/base", 2360},
{"hexen deathkings of the dark citadel/base", 2370},
{"ultimate doom/base", 2280},
{"DOOM 3 BFG Edition/base/wads", 208200},*/
{"Doom 2/base", 2300},
{"Final Doom/base", 2290},
{"Heretic Shadow of the Serpent Riders/base", 2390},
{"Hexen/base", 2360},
{"Hexen Deathkings of the Dark Citadel/base", 2370},
{"Ultimate Doom/base", 2280},
{"DOOM 3 BFG Edition/base/wads", 208200},
{"Strife", 317040}
};
@ -190,7 +190,13 @@ TArray<FString> I_GetSteamPath()
if(home != NULL && *home != '\0')
{
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
{
SteamInstallFolders = ParseSteamRegistry(regPath);

View file

@ -40,6 +40,7 @@
#include "r_utility.h"
#include "r_data/models/models.h"
#include "r_data/models/models_ue1.h"
#include "r_data/models/models_obj.h"
#include "i_time.h"
#ifdef _MSC_VER
@ -419,7 +420,7 @@ static unsigned FindModel(const char * path, const char * modelfile)
FMemLump lumpd = Wads.ReadLump(lump);
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();
anivfile.Substitute("_d.3d","_a.3d");
@ -428,7 +429,7 @@ static unsigned FindModel(const char * path, const char * modelfile)
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();
datafile.Substitute("_a.3d","_d.3d");
@ -437,6 +438,10 @@ static unsigned FindModel(const char * path, const char * modelfile)
model = new FUE1Model;
}
}
else if ( (size_t)fullname.LastIndexOf(".obj") == fullname.Len() - 4 )
{
model = new FOBJModel;
}
else if (!memcmp(buffer, "DMDM", 4))
{
model = new FDMDModel;

View 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();
}

View 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

View file

@ -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);
CalcPolyobjSoundOrg(listenpos + disp, poly, *pos);
pos->X += (float)disp.X;
pos->Z += (float)disp.Y;
pos->X -= (float)disp.X;
pos->Z -= (float)disp.Y;
}
break;
}

View file

@ -114,15 +114,8 @@ public:
{
ID = S_FindSound(name.GetChars());
}
FSoundID(const FSoundID &other)
{
ID = other.ID;
}
FSoundID &operator=(const FSoundID &other)
{
ID = other.ID;
return *this;
}
FSoundID(const FSoundID &other) = default;
FSoundID &operator=(const FSoundID &other) = default;
FSoundID &operator=(const char *name)
{
ID = S_FindSound(name);
@ -168,19 +161,7 @@ class FSoundIDNoInit : public FSoundID
{
public:
FSoundIDNoInit() : FSoundID(NoInit) {}
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);
}
using FSoundID::operator=;
};
extern FRolloffInfo S_Rolloff;

View file

@ -1266,6 +1266,14 @@ DEFINE_ACTION_FUNCTION(FStringStruct, IndexOf)
}
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_STRING(substr);

View file

@ -57,12 +57,6 @@ public:
class cycle_t
{
public:
cycle_t &operator= (const cycle_t &o)
{
Sec = o.Sec;
return *this;
}
void Reset()
{
Sec = 0;
@ -153,12 +147,6 @@ inline uint64_t rdtsc()
class cycle_t
{
public:
cycle_t &operator= (const cycle_t &o)
{
Counter = o.Counter;
return *this;
}
void Reset()
{
Counter = 0;

View file

@ -961,8 +961,6 @@ PalEntry FTexture::averageColor(const uint32_t *data, int size, int maxout)
PalEntry FTexture::GetSkyCapColor(bool bottom)
{
PalEntry col;
if (!bSWSkyColorDone)
{
bSWSkyColorDone = true;

View file

@ -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)
{
PalEntry p = color ? (PalEntry)color : GPalette.BaseColors[palcolor];

View file

@ -149,6 +149,7 @@ public:
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 Clear();

View file

@ -1088,6 +1088,24 @@ DEFINE_ACTION_FUNCTION(_Screen, DrawLine)
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

View file

@ -1995,7 +1995,14 @@ FSpecialFont::FSpecialFont (const char *name, int first, int count, FTexture **l
FixXMoves();
if (!noTranslate) LoadTranslations();
if (noTranslate)
{
ActiveColors = 0;
}
else
{
LoadTranslations();
}
delete[] charlumps;
}

View file

@ -498,6 +498,9 @@ public:
// Draws a line
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
void DrawPixel(int x, int y, int palcolor, uint32_t rgbcolor);

View file

@ -66,19 +66,14 @@ struct TVector2
{
vec_t X, Y;
TVector2 ()
{
}
TVector2() = default;
TVector2 (vec_t a, vec_t b)
: X(a), Y(b)
{
}
TVector2 (const TVector2 &other)
: X(other.X), Y(other.Y)
{
}
TVector2(const TVector2 &other) = default;
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)
@ -95,18 +90,7 @@ struct TVector2
return X == 0 && Y == 0;
}
TVector2 &operator= (const TVector2 &other)
{
// 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;
}
TVector2 &operator= (const TVector2 &other) = default;
// Access X and Y as an array
vec_t &operator[] (int index)
@ -317,9 +301,7 @@ struct TVector3
vec_t X, Y, Z;
TVector3 ()
{
}
TVector3() = default;
TVector3 (vec_t a, vec_t b, vec_t c)
: X(a), Y(b), Z(c)
@ -331,10 +313,7 @@ struct TVector3
{
}
TVector3 (const TVector3 &other)
: X(other.X), Y(other.Y), Z(other.Z)
{
}
TVector3(const TVector3 &other) = default;
TVector3 (const Vector2 &xy, vec_t z)
: X(xy.X), Y(xy.Y), Z(z)
@ -353,11 +332,7 @@ struct TVector3
return X == 0 && Y == 0 && Z == 0;
}
TVector3 &operator= (const TVector3 &other)
{
Z = other.Z, Y = other.Y, X = other.X;
return *this;
}
TVector3 &operator= (const TVector3 &other) = default;
// Access X and Y and Z as an array
vec_t &operator[] (int index)
@ -546,13 +521,12 @@ struct TVector3
{
right = { 0.f, 0.f, 1.f };
}
if (major == 1 || (major == 2 && n[2] > 0.f))
else if (major == 1 || (major == 2 && n[2] > 0.f))
{
right = { 1.f, 0.f, 0.f };
}
if (major == 2 && n[2] < 0.0f)
// Unconditional to ease static analysis
else // major == 2 && n[2] <= 0.0f
{
right = { -1.f, 0.f, 0.f };
}
@ -662,9 +636,7 @@ struct TVector4
vec_t X, Y, Z, W;
TVector4()
{
}
TVector4() = default;
TVector4(vec_t a, vec_t b, vec_t c, vec_t d)
: X(a), Y(b), Z(c), W(d)
@ -676,10 +648,7 @@ struct TVector4
{
}
TVector4(const TVector4 &other)
: X(other.X), Y(other.Y), Z(other.Z), W(other.W)
{
}
TVector4(const TVector4 &other) = default;
TVector4(const Vector3 &xyz, vec_t 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;
}
TVector4 &operator= (const TVector4 &other)
{
W = other.W, Z = other.Z, Y = other.Y, X = other.X;
return *this;
}
TVector4 &operator= (const TVector4 &other) = default;
// Access X and Y and Z as an array
vec_t &operator[] (int index)
@ -939,16 +904,8 @@ struct TMatrix3x3
vec_t Cells[3][3];
TMatrix3x3()
{
}
TMatrix3x3(const TMatrix3x3 &other)
{
(*this)[0] = other[0];
(*this)[1] = other[1];
(*this)[2] = other[2];
}
TMatrix3x3() = default;
TMatrix3x3(const TMatrix3x3 &other) = default;
TMatrix3x3(const Vector3 &row1, const Vector3 &row2, const Vector3 &row3)
{
@ -1151,32 +1108,15 @@ struct TAngle
TAngle &operator= (long other) = delete;
TAngle &operator= (unsigned long other) = delete;
TAngle ()
{
}
TAngle() = default;
TAngle (vec_t amt)
: Degrees(amt)
{
}
/*
TAngle (int amt)
: Degrees(vec_t(amt))
{
}
*/
TAngle (const TAngle &other)
: Degrees(other.Degrees)
{
}
TAngle &operator= (const TAngle &other)
{
Degrees = other.Degrees;
return *this;
}
TAngle(const TAngle &other) = default;
TAngle &operator= (const TAngle &other) = default;
TAngle &operator= (double other)
{
@ -1491,25 +1431,15 @@ struct TRotator
Angle Roll; // rotation about the forward axis.
Angle CamRoll; // Roll specific to actor cameras. Used by quakes.
TRotator ()
{
}
TRotator() = default;
TRotator (const Angle &p, const Angle &y, const Angle &r)
: Pitch(p), Yaw(y), Roll(r)
{
}
TRotator (const TRotator &other)
: Pitch(other.Pitch), Yaw(other.Yaw), Roll(other.Roll)
{
}
TRotator &operator= (const TRotator &other)
{
Roll = other.Roll, Yaw = other.Yaw, Pitch = other.Pitch;
return *this;
}
TRotator(const TRotator &other) = default;
TRotator &operator= (const TRotator &other) = default;
// Access angles as an array
Angle &operator[] (int index)

View file

@ -55,9 +55,9 @@ const char *GetVersionString();
#define RC_FILEVERSION 3,5,9999,0
#define RC_PRODUCTVERSION 3,5,9999,0
#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_MINOR 5
#define VER_MINOR 6
#define VER_REVISION 0
// Version identifier for network games.

View file

@ -507,31 +507,11 @@ long FString::IndexOfAny (const char *charset, long startIndex) const
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
{
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
{
if ((size_t)endIndex > Len())
@ -548,8 +528,10 @@ long FString::LastIndexOf (char subchar, long endIndex) const
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())
{
endIndex = long(Len());
@ -596,6 +578,43 @@ long FString::LastIndexOfAny (const char *charset, long endIndex) const
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 ()
{
LockBuffer();
@ -1003,7 +1022,7 @@ void FString::Substitute (const char *oldstr, const char *newstr, size_t oldstrl
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:
digits = [0-9];

View file

@ -201,19 +201,22 @@ public:
long IndexOfAny (const FString &charset, long startIndex=0) const;
long IndexOfAny (const char *charset, long startIndex=0) const;
long LastIndexOf (const FString &substr) const;
long LastIndexOf (const char *substr) const;
// This is only kept for backwards compatibility with old ZScript versions that used this function and depend on its bug.
long LastIndexOf (char subchar) const;
long LastIndexOf (const FString &substr, long endIndex) const;
long LastIndexOf (const char *substr, long endIndex) const;
long LastIndexOfBroken (const FString &substr, 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 char *charset) const;
long LastIndexOfAny (const FString &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 ToLower ();
void SwapCase ();
@ -463,4 +466,3 @@ template<> struct THashTraits<FString>
// Compares two keys, returning zero if they are the same.
int Compare(const FString &left, const FString &right) { return left.Compare(right); }
};

View file

@ -2820,6 +2820,8 @@ GLPREFMNU_DITHER = "Dither output";
GLPREFMNU_PALTONEMAPORDER = "Tonemap Palette Order";
GLPREFMNU_PALTONEMAPPOWER = "Tonemap Palette Exponent";
GLPREFMNU_SWLMBANDED = "Banded SW Lightmode";
GLPREFMNU_VRIPD = "Distance Between Your Eyes";
GLPREFMNU_VRSCREENDIST = "Distance From Your Screen";
// Option Values
OPTVAL_SMART = "Smart";

File diff suppressed because it is too large Load diff

View file

@ -2290,6 +2290,8 @@ OptionMenu "OpenGLOptions" protected
{
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 " "
Option "$GLPREFMNU_MULTISAMPLE", gl_multisample, "Multisample"
Option "$GLPREFMNU_TONEMAP", gl_tonemap, "TonemapModes"

View file

@ -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 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 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 Vector2, Vector2 VirtualToRealCoords(Vector2 pos, Vector2 size, Vector2 vsize, bool vbottom=false, bool handleaspect=true);
native static double GetAspectRatio();
@ -815,7 +816,8 @@ struct StringStruct native
native int CharCodeAt(int pos) const;
native String Filter();
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 ToLower();
native int ToInt(int base = 0) const;