I did not think of a solution yet but I am leaning on doing exactly that. I've been trying really HARD to keep the new code-base understandable for all and not just me.. Then I've been working on optimising & micro-optimising (because this is low level graphics) stuff but trying to keep it readable as well..
The current plugin has way too much patch code and it's really hard for me to just stick in new stuff..
For example, some micro-ops would be:
(2195 mod 256) - 11 I've optimised to (2195 & 255) - 11 to avoid the cost of modulo.. Now it may not look like a huge change, but when ran over thousands of times per second and bottle necks the CPU, it counts a lot. I've benched it and tested it.
And since all textures are GL_TEXTURE_2D (power of two's.. about damn time Jagex updated their stuff), it makes this even faster than ever.
Then I got rid of GL_TEXTURE_ARB (non-power of two textures) because the game no longer uses it and there's no need for unnecessary branching and extra checks.. Sometimes it uses it for the tiniest irrelevant code.
I completely re-wrote fonts so that it doesn't have to store almost 10k fonts per frame per area.. Instead, it stores nothing and grabs them on the fly due to the above calculation.. This means that I don't have to delete anything because there's no memory being used..
For example.. Previously I did:
C++ Code:
void Font::LogEndList()
{
if (this->Found) //check if a character was possibly rendered.
{
this->LastFont = false; //is this the last character rendered in a font set (256 * however many lists the game loads.. roughly 4k).
if (!this->Fonts.size()) //if this is the last font, reset the next font to start at "32" aka space in ASCII.
this->Letter = 32;
else
this->Letter = (!this->Fonts.back().LastFont) ? this->Fonts.back().Letter + 1 : 32; //Else, the next char is the last char + 1 in the ASCII table.
if (++this->Count >= 255) //Every 256 fonts, we need to reset the font counter and start over.. There's roughly 4k lists to go through.
{
this->Count = 0;
this->LastFont = true;
this->Found = false;
}
this->Fonts.push_back(*this); //Store the current character in the Font vector structure.
}
}
and now I do:
C++ Code:
void Font::LogFont(unsigned int List)
{
font->Letter = (List & 0xFF) - 11;
}
And trust me when I say that's probably the biggest improvement so far (because I haven't worked on anything else yet other than raw API code).. I went from storing 10k "Font structures" in a vector or maps and doing a look up to get each letter rendered.. To doing no look ups and storing nothing OR storing on a frame-by-frame basis in the very worst case.
You can see that the new above generic algorithm works for all characters:
C++ Code:
1632 % 256 = 96 minus 11 = U
1662 % 256 = 126 minus 11 = s
1648 % 256 = 112 minus 11 = e
1661 % 256 = ? minus 11 = ?
1657 ...
1644 ...
Derived Algorithm: Character = (List % 256) - 11.
Faster Derived algorithm: Character = (List & 255) - 11.
You can test it out for the remaining letters and see that it spells out "Username" which was the text written on the login screen. I tested this on menus, login, etc..
Other than that, I've been working on the math and calculations:
C++ Code:
Mat4 Mat4::Transpose() const
{
Mat4 res;
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
res[j][i] = mat[i][j];
}
}
return res;
}
Mat4 Mat4::Identity()
{
Mat4 res;
res[0][0] = 1.0f;
res[1][1] = 1.0f;
res[2][2] = 1.0f;
res[3][3] = 1.0f;
return res;
}
Mat4 Mat4::Orthagonal(float w, float h, float zNear, float zFar)
{
Mat4 res;
res[0][0] = 2.0f / w;
res[1][1] = -2.0f / h;
res[2][2] = -2.0f / (zFar - zNear);
res[3][3] = 1.0f;
res[3][0] = -1.0f;
res[3][1] = 1.0f;
res[3][2] = (zFar + zNear) / (zFar - zNear);
return res;
}
Mat4 Mat4::Perspective(float w, float h, float zNear, float zFar)
{
Mat4 res;
res[0][0] = 2.0f * zNear / w;
res[1][1] = -2.0f * zNear / h;
res[2][2] = ((-zFar - zNear) * zFar) / (zFar - zNear);
res[2][3] = -2.0f * zNear / (zNear - zFar);
res[3][2] = -1.0f;
res[0][2] = 1.0f;
res[1][2] = -1.0f;
return res;
}
Mat4 Mat4::FieldOfView(float fov, float aspect, float zNear, float zFar)
{
Mat4 res;
res[0][0] = 1.0f / tanf(fov / 2.0f);
res[1][1] = aspect / tanf(fov / 2.0f);
res[2][2] = zFar / (zFar - zNear);
res[3][2] = (zFar * zNear) / (zNear - zFar);
res[2][3] = -1.0f;
res[3][3] = 0.0f;
return res;
}
Mat4 Mat4::LookAt(const Vec3& Eye, const Vec3& At, const Vec3& Up)
{
Vec3 XAxis, YAxis, ZAxis;
ZAxis = (Eye - At).Normalise();
XAxis = (Up.Cross(ZAxis)).Normalise();
YAxis = (ZAxis.Cross(XAxis)).Normalise();
Mat4 res;
res[0][0] = XAxis.x;
res[1][0] = XAxis.y;
res[2][0] = XAxis.z;
res[3][0] = -XAxis.Dot(Eye);
res[0][1] = YAxis.x;
res[1][1] = YAxis.y;
res[2][1] = YAxis.z;
res[3][1] = -YAxis.Dot(Eye);
res[0][2] = ZAxis.x;
res[1][2] = ZAxis.y;
res[2][2] = ZAxis.z;
res[3][2] = -ZAxis.Dot(Eye);
res[3][3] = 1.0f;
return res;
}
Mat4 Mat4::RotX(float rad)
{
float ct = cosf(rad);
float st = sinf(rad);
Mat4 res = Identity();
res[1][1] = ct;
res[1][2] = st;
res[2][1] = -st;
res[2][2] = ct;
return res;
}
Mat4 Mat4::RotY(float rad)
{
float ct = cosf(rad);
float st = sinf(rad);
Mat4 res = Identity();
res[0][0] = ct;
res[0][2] = st;
res[2][0] = -st;
res[2][2] = ct;
return res;
}
Mat4 Mat4::RotZ(float rad)
{
float ct = cosf(rad);
float st = sinf(rad);
Mat4 res = Identity();
res[0][0] = ct;
res[0][1] = st;
res[1][0] = -st;
res[1][1] = ct;
return res;
}
Mat4 Mat4::Rot(float pitch, float yaw, float roll)
{
return RotY(yaw) * RotX(pitch) * RotZ(roll);
}
Mat4 Mat4::Scale(float val)
{
return Scale(val, val, val);
}
Mat4 Mat4::Scale(float x, float y, float z)
{
Mat4 res;
res[0][0] = x;
res[1][1] = y;
res[2][2] = z;
res[3][3] = 1.0f;
return res;
}
Mat4 Mat4::Translate(float x, float y, float z)
{
Mat4 res;
res[3][0] = x;
res[3][1] = y;
res[3][2] = z;
res[0][0] = 1.0f;
res[1][1] = 1.0f;
res[2][2] = 1.0f;
res[3][3] = 1.0f;
return res;
}
But I re-wrote a bunch of code as well:
Serialisation is brand spanking new and way faster and more efficient than before:
C++ Code:
#ifndef STREAM_HXX_INCLUDED
#define STREAM_HXX_INCLUDED
#include <vector>
#include <list>
#include <string>
#include <sstream>
/********************************************//**
* @brief A class for serialising all data-types & containers into a buffer.
***********************************************/
class Stream
{
private:
std::stringstream Data;
/********************************************//**
* @brief Determines if two types are the exact same when decayed at compile time.
*
* @param T - Type to compare to.
* @param U - Type that is decayed first and compared to T.
* @return std::integral_constant<bool, true> if the types matched, std::integral_constant<bool, false> otherwise.
*
***********************************************/
template<typename T, typename U>
struct is_same_decay : public std::integral_constant<bool, std::is_same<T, typename std::decay<U>::type>::value> {};
/********************************************//**
* @brief Compares a type to a char*, or const char* when decayed at compile time.
*
* @param T - The type to decay and compare.
* @return std::integral_constant<bool, true> if the types matched, std::integral_constant<bool, false> otherwise.
*
***********************************************/
template<typename T>
struct is_cstring : public std::integral_constant<bool, is_same_decay<char*, T>::value || is_same_decay<const char*, T>::value> {};
/********************************************//**
* @brief Compares a type to a wchar_t*, or const wchar_t* when decayed at compile time.
*
* @param T - The type to decay and compare.
* @return std::integral_constant<bool, true> if the types matched, std::integral_constant<bool, false> otherwise.
*
***********************************************/
template<typename T>
struct is_wcstring : public std::integral_constant<bool, is_same_decay<wchar_t*, T>::value || is_same_decay<const wchar_t*, T>::value> {};
public:
/********************************************//**
* @brief Constructs a stream class.
*
* @param Buffer void* - Buffer to serialise/de-serialise data to/from.
* @param BufferSize std::size_t - Size of the specified buffer.
*
***********************************************/
Stream(void* Buffer, std::size_t BufferSize);
template<typename T>
void Read(T &Value);
template<typename T>
void Write(const T &Value);
template<typename T>
typename std::enable_if<!is_cstring<T>::value && !is_wcstring<T>::value, Stream&>::type operator << (const T &Value);
template<typename T>
typename std::enable_if<!is_cstring<T>::value && !is_wcstring<T>::value, Stream&>::type operator >> (T &Value);
template<typename T, typename Allocator>
Stream& operator << (const std::vector<T, Allocator> &Value);
template<typename T, typename Allocator>
Stream& operator << (const std::list<T, Allocator> &Value);
template<typename T, typename Allocator>
Stream& operator << (const std::basic_string<T, Allocator> &Value);
template<typename T, typename Allocator>
Stream& operator >> (std::vector<T, Allocator> &Value);
template<typename T, typename Allocator>
Stream& operator >> (std::list<T, Allocator> &Value);
template<typename T, typename Allocator>
Stream& operator >> (std::basic_string<T, Allocator> &Value);
};
#endif // STREAM_HXX_INCLUDED
Graphics is re-written too:
C++ Code:
#ifndef GRAPHICS_HXX_INCLUDED
#define GRAPHICS_HXX_INCLUDED
#include <GL/gl.h>
#include <GL/glext.h>
#include <utility>
#include <string>
#include <cstring>
#include <vector>
#include <fstream>
#if defined _WIN32 || defined _WIN64
#include <windows.h>
#endif
#include "Libraries/LodePNG.hxx"
/********************************************//**
* @brief Structured Union representing a tightly packed RGBA memory layout.
***********************************************/
typedef union RGBA
{
unsigned int Colour;
struct
{
unsigned char R, G, B, A;
};
} *PRGB;
/********************************************//**
* @brief A class representing a 32 bit Image such as Bitmaps and PNGs.
* 24-bit is NOT support on purpose. Graphics cards process 32-bit images faster.
***********************************************/
class Image
{
private:
#ifdef GRAPHICS_IMAGE_LOADING
std::vector<unsigned char> Pixels;
#else
unsigned char* Pixels;
#endif
unsigned int Width;
unsigned int Height;
public:
enum class Format {GL_BMP, GL_PNG};
#ifdef GRAPHICS_IMAGE_LOADING
Image(const char* Path);
#endif // ENABLE_IMAGE_LOADING
Image(unsigned int Target, unsigned int ID);
Image(void* Data, unsigned int Width, unsigned int Height);
~Image();
const void* GetPixels() const;
void Save(const char* Path, Format Fmt = Format::GL_PNG);
void Flip();
void FlipTo(void* Data);
void Transpose();
void TransposeTo(void* Data);
static void Flip(void* In, void* &Out, int width, int height, uint32_t Bpp = 32);
};
/********************************************//**
* @brief A class representing a texture in video memory. Upon destruction, the texture is deleted.
***********************************************/
class Texture
{
private:
unsigned int ID;
unsigned int Target;
unsigned int Width, Height;
unsigned int Format;
public:
/********************************************//**
* @brief Texture Constructor. Initialises a texture in video memory.
*
* @param Target unsigned int - Can be GL_TEXTURE_RECTANGLE OR GL_TEXTURE_2D
* @param Width unsigned int - Width of the texture to create.
* @param Height unsigned int - Height of the texture to create.
* @param unsigned char* Pixels = nullptr - Pixels are optional. If specified, must be in a 32-bit format (ARGB, BGRA, ABGR, etc).
* @param unsigned int Format = GL_BGRA - Format of the 32-bit layout. (ARGB, BGRA, ABGR, etc..) CANNOT be 24-bit formats.
*
***********************************************/
Texture(unsigned int Target, unsigned int Width, unsigned int Height, unsigned char* Pixels = nullptr, unsigned int Format = GL_BGRA);
/********************************************//**
* @brief Texture Destructor. Deletes a texture from video memory. Cleanup.
***********************************************/
~Texture();
/********************************************//**
* @brief Sets the pixels of the entire texture.
* Pixels must be Width * Height * 4 OR ((Width * 32 + 31) / 32) * 4 * Height.
*
* @param unsigned char* Pixels - Buffer containing the pixels to be set.
* @return
*
***********************************************/
void SetPixels(unsigned char* Pixels);
/********************************************//**
* @brief Draws this texture to the back-buffer with glColor4ub(255, 255, 255, 255).
*
* @param X1 float - The X-coordinate of the upper-left corner of the rectangle in which to render this texture.
* @param Y1 float - The Y-coordinate of the upper-left corner of the rectangle in which to render this texture.
* @param X2 float - The X-coordinate of the lower-right corner of the rectangle in which to render this texture.
* @param Y2 float - The Y-coordinate of the lower-right corner of the rectangle in which to render this texture.
* @return void
*
***********************************************/
void Draw(float X1, float Y1, float X2, float Y2);
/********************************************//**
* @brief Returns the ID of the texture generated by this class.
*
* @return unsigned int - ID of the texture in video memory.
*
***********************************************/
unsigned int GetID() const {return ID;}
/********************************************//**
* @brief Returns the PixelFormat of the texture generated by this class. Always returns a 32-bit format.
*
* @return unsigned int - Pixel Format: GL_RGBA, GL_BGRA, etc..
*
***********************************************/
unsigned int GetPixelFormat() const {return Format;}
/********************************************//**
* @brief Returns the Width of this texture as specified upon creation.
*
* @return int
*
***********************************************/
int GetWidth() const {return Width;}
/********************************************//**
* @brief Returns the Height of this texture as specified upon creation.
*
* @return int
*
***********************************************/
int GetHeight() const {return Height;}
/********************************************//**
* @brief Returns the Target of this texture as specified upon creation. GL_TEXTURE_RECTANGLE OR GL_TEXTURE_2D
*
* @return unsigned int
*
***********************************************/
unsigned int GetTarget() const {return Target;}
};
/********************************************//**
* @brief A class representing a Font in Graphics Memory (DeviceContext). Upon destruction, the font is deleted.
***********************************************/
class Font
{
private:
HDC DC;
unsigned int Base;
unsigned int Size;
const char* Name;
/********************************************//**
* @brief Determines if two types are the exact same when decayed at compile time.
*
* @param T - Type to compare to.
* @param U - Type that is decayed first and compared to T.
* @return std::integral_constant<bool, true> if the types matched, std::integral_constant<bool, false> otherwise.
*
***********************************************/
template<typename T, typename U>
struct is_same_decay : public std::integral_constant<bool, std::is_same<T, typename std::decay<U>::type>::value> {};
/********************************************//**
* @brief Compares a type to a char, char*, or const char* when decayed at compile time.
*
* @param T - The type to decay and compare.
* @return std::integral_constant<bool, true> if the types matched, std::integral_constant<bool, false> otherwise.
*
***********************************************/
template<typename T>
struct is_char_type : public std::integral_constant<bool, is_same_decay<char, T>::value || is_same_decay<char*, T>::value || is_same_decay<const char*, T>::value> {};
/********************************************//**
* @brief Overrides std::to_string via ADL but only if the parameter is a char, char* or const char* when decayed.
*
* @param value T to be convert to a string. Must be a char, char* or const char* for this function to be triggered.
* @return An STL string containing the contents of the parameter.
*
***********************************************/
template<typename T>
std::string to_string(T value)
{
static_assert(is_char_type<T>::value, "Argument: Value Must be of literal type.");
std::string Result;
Result += value;
return Result;
}
public:
/********************************************//**
* @brief Constructor for constructing a BitmapFont in video memory.
*
* @param Name const char* - Name of the font to be used. Arial, TimesNewRoman, etc..
* @param DC HDC - Device Context in which to select the new font into.
* @param Size unsigned int - Size of the font.
*
***********************************************/
Font(const char* Name, HDC DC, unsigned int Size);
/********************************************//**
* @brief Destructor deleted the font from video memory.
***********************************************/
~Font();
/********************************************//**
* @brief Variadic template version of PrintF for OpenGL. Draws a string in white.
*
* @param X float - X position on the screen where the top left of the string is to be drawn.
* @param Y float - Y position on the screen where the top left of the string is to be drawn.
* @param Text const char* - Text containing the string as well as the format if necessary.
* @param args Args&&... - Variadic parameter pack containing arguments deduced by the format string if any.
* @return void
*
* Draw(X, Y, "This is a non-formated string");
* Draw(X, Y, "This is a formated string with % number(s), 1);
*
***********************************************/
template<typename... Args>
void Draw(float X, float Y, const char* Text, Args&&... args);
/********************************************//**
* @brief Variadic template version of PrintF for OpenGL. Draws a string in any colour.
*
* @param X float - X position on the screen where the top left of the string is to be drawn.
* @param Y float - Y position on the screen where the top left of the string is to be drawn.
* @param R float - R component in the RGB model of the colour to be used. 0.0f <= R <= 1.0f.
* @param G float - G component in the RGB model of the colour to be used. 0.0f <= G <= 1.0f.
* @param B float - B component in the RGB model of the colour to be used. 0.0f <= B <= 1.0f.
* @param Text const char* - Text containing the string as well as the format if necessary.
* @param args Args&&... - Variadic parameter pack containing arguments deduced by the format string if any.
* @return void
*
* Draw(X, Y, 0.5f, 0.5f, 0.5f, "This is a non-formated string");
* Draw(X, Y, 1.0f, 0.0f, 0.0f, "This is a formated string with % number(s), 1);
***********************************************/
template<typename... Args>
void Draw(float X, float Y, float R, float G, float B, const char* Text, Args&&... args);
};
#endif // GRAPHICS_HXX_INCLUDED
The documentation takes the longest and wastes the most time.. So all in all, I have NOT been working on animated textures or patching the current plugin. I've instead been getting rid of the old code and slowly replacing it with faster and better new code with proper documentation.. That includes the hooking API so that things will be EASIER for me to hook. Otherwise, there's not really a way for me to just shove in a new texture API without headaches..
Also been trying to make it as cross-platform as I can.. The reason for the re-write is simply.. It's impossible to hook model vertices with the current code.. This is something I keep getting asked to implement.. With the new code-base, I can port most of my Direct-X code to OpenGL for model hooks (We'll see if it works).
Things like this:
https://github.com/Brandon-T/SRL-GLX...ore.simba#L981 NEEDS to be updated.. It was written when Lape was in its earliest release stage and it's extremely nasty. I don't think I even remember how it works -__-..