From dd05797b956587f5d42dae785e21d5863842dac6 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 6 Mar 2025 13:49:52 +0300 Subject: [PATCH] Add renderer stuff --- engine/engine.dsp | 24 +++ engine/render/gl_shared.cpp | 4 + engine/render/gl_shared.h | 5 + engine/render/render_shared.h | 18 +++ engine/render/renderdevice.cpp | 8 +- engine/render/rendertarget.cpp | 2 - engine/render/texture2d.cpp | 255 ++++++++++++++++++++++++++++++ engine/render/texture2d.h | 49 ++++++ engine/render/texturesmanager.cpp | 192 ++++++++++++++++++++++ engine/render/texturesmanager.h | 38 +++++ engine/utils/rtti.h | 141 +++++++++++++++++ 11 files changed, 730 insertions(+), 6 deletions(-) create mode 100644 engine/render/texture2d.cpp create mode 100644 engine/render/texture2d.h create mode 100644 engine/render/texturesmanager.cpp create mode 100644 engine/render/texturesmanager.h create mode 100644 engine/utils/rtti.h diff --git a/engine/engine.dsp b/engine/engine.dsp index 5630562..f760127 100644 --- a/engine/engine.dsp +++ b/engine/engine.dsp @@ -137,10 +137,22 @@ SOURCE=.\render\renderdevice.cpp # End Source File # Begin Source File +SOURCE=.\render\rendertarget.cpp +# End Source File +# Begin Source File + SOURCE=.\filesystem\stream.cpp # End Source File # Begin Source File +SOURCE=.\render\texture2d.cpp +# End Source File +# Begin Source File + +SOURCE=.\render\texturesmanager.cpp +# End Source File +# Begin Source File + SOURCE=.\render\vertexbuffer.cpp # End Source File # End Group @@ -193,10 +205,22 @@ SOURCE=.\render\renderdevice.h # End Source File # Begin Source File +SOURCE=.\render\rendertarget.h +# End Source File +# Begin Source File + SOURCE=.\filesystem\stream.h # End Source File # Begin Source File +SOURCE=.\render\texture2d.h +# End Source File +# Begin Source File + +SOURCE=.\render\texturesmanager.h +# End Source File +# Begin Source File + SOURCE=.\render\vertexbuffer.h # End Source File # End Group diff --git a/engine/render/gl_shared.cpp b/engine/render/gl_shared.cpp index 8cf07a4..bb64643 100644 --- a/engine/render/gl_shared.cpp +++ b/engine/render/gl_shared.cpp @@ -44,6 +44,10 @@ void GL_CheckErrorFunction(const char* expression, const char* filename, int lin Msg( "OpenGL Error: %s (%s) at %s:%i", expression, GL_ErrorString( err ), filename, line );// Msg("OpenGL Error: %s [%s]\n", GL_ErrorString(err), GL_TargetToString(tex->target)); } +void GL_SetTexture( int slot, uint texture ) +{ +} + uint GetGLBlendFactor(BlendFactor factor) { switch (factor) diff --git a/engine/render/gl_shared.h b/engine/render/gl_shared.h index 49b5ffd..79bf6eb 100644 --- a/engine/render/gl_shared.h +++ b/engine/render/gl_shared.h @@ -13,6 +13,8 @@ void GL_CheckError(); void GL_CheckErrorEx(const char* filename, int line); void GL_CheckErrorFunction(const char* expression, const char* filename, int line); +void GL_SetTexture( int slot, uint texture ); + #define GL_CHECK_ERROR() \ GL_CheckErrorEx(__FILE__, __LINE__) @@ -20,6 +22,9 @@ void GL_CheckErrorFunction(const char* expression, const char* filename, int lin expr; \ GL_CheckErrorFunction(#expr, __FILE__, __LINE__) +// OpenGL 1.3 Functional + + // OpenGL 1.5 Functional extern PFNGLGENQUERIESPROC glGenQueries; extern PFNGLDELETEQUERIESPROC glDeleteQueries; diff --git a/engine/render/render_shared.h b/engine/render/render_shared.h index 328b394..8014980 100644 --- a/engine/render/render_shared.h +++ b/engine/render/render_shared.h @@ -57,6 +57,24 @@ enum PrimitiveType PT_TRIANGLES }; +enum TextureWrap +{ + TW_REPEAT, + TW_MIRROREDREPEAT, + TW_CLAMPTOEDGE, + TW_CLAMPTOBORDER +}; + +enum TextureFilter +{ + TF_NEAREST, + TF_LINEAR, + TF_NEARESTMIPMAPNEAREST, + TF_LINEARMIPMAPNEAREST, + TF_NEARESTMIPMAPLINEAR, + TF_LINEARMIPMAPLINEAR +}; + // Base structure for render view (view and projection matrices, viewport settings) struct View { diff --git a/engine/render/renderdevice.cpp b/engine/render/renderdevice.cpp index a43185a..c6868a4 100644 --- a/engine/render/renderdevice.cpp +++ b/engine/render/renderdevice.cpp @@ -136,11 +136,11 @@ void RenderDevice::SetReadRenderTarget(RenderTarget* renderTarget) if (renderTarget) { if (m_activeReadRT != renderTarget) { m_activeReadRT = renderTarget; - //glBindFramebuffer(GL_READ_FRAMEBUFFER, renderTarget->m_framebuffer); + glBindFramebuffer(GL_READ_FRAMEBUFFER, renderTarget->m_framebuffer); } } else { // set default rt - // glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); } } @@ -149,11 +149,11 @@ void RenderDevice::SetWriteRenderTarget(RenderTarget* renderTarget) if (renderTarget) { if (m_activeWriteRT != renderTarget) { m_activeWriteRT = renderTarget; - //glBindFramebuffer(GL_DRAW_FRAMEBUFFER, renderTarget->m_framebuffer); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, renderTarget->m_framebuffer); } } else { // set default rt - //glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); } } diff --git a/engine/render/rendertarget.cpp b/engine/render/rendertarget.cpp index 9f77b85..16c587f 100644 --- a/engine/render/rendertarget.cpp +++ b/engine/render/rendertarget.cpp @@ -4,8 +4,6 @@ #include "render/texturesmanager.h" #include "render/texture2d.h" -#include "glad/glad.h" - void RenderTarget::setDefaultFramebuffer() { glBindFramebuffer(GL_FRAMEBUFFER, 0); diff --git a/engine/render/texture2d.cpp b/engine/render/texture2d.cpp new file mode 100644 index 0000000..8b57f2f --- /dev/null +++ b/engine/render/texture2d.cpp @@ -0,0 +1,255 @@ +#include +#include "render/texture2d.h" +#include "render/texturesmanager.h" +#include "render/gl_shared.h" + +GLint GetGlWrap(TextureWrap wrap); +GLint GetGlTexFilter(TextureFilter filter); + +Texture2D* Texture2D::Create() +{ + return new Texture2D; +} + +Texture2D::Texture2D() +{ + m_pf = PF_UNKNOWN; + m_width = 0; + m_height = 0; + m_channels = 0; + m_handle = -1; +} + +Texture2D::~Texture2D() +{ + m_pf = PF_UNKNOWN; + m_width = 0; + m_height = 0; + m_channels = 0; + m_handle = -1; +} + +void Texture2D::CreateBlackTexture(int width, int height, int channels) +{ + size_t textureSize = width * height * channels; + byte* data = new byte[textureSize]; + assert(data); + + for (int i = 0; i < (int)textureSize; i++) { + data[i] = 0; + } + + CreateFromExistedData(data, width, height, channels); + + delete[] data; +} + +void Texture2D::CreateWhiteTexture(int width, int height, int channels) +{ + size_t textureSize = width * height * channels; + byte* data = new byte[textureSize]; + assert(data); + + for (int i = 0; i < (int)textureSize; i++) { + data[i] = 255; + } + + CreateFromExistedData(data, width, height, channels); + + delete[] data; +} + +void Texture2D::CreateGrayTexture(int width, int height, int channels) +{ + size_t textureSize = width * height * channels; + byte* data = new byte[textureSize]; + assert(data); + + for (int i = 0; i < (int)textureSize; i++) { + data[i] = 255 / 2; + } + + CreateFromExistedData(data, width, height, channels); + + delete[] data; +} + +void Texture2D::CreateTexture_Generator(int width, int height, int channels, int color) +{ + size_t textureSize = width * height * channels; + byte* data = new byte[textureSize]; + assert(data); + + m_textureFileName = "$generator_texture$"; + + for (int i = 0; i < (int)textureSize; i++) { + data[i] = color; + } + + CreateFromExistedData(data, width, height, channels); + + delete[] data; +} + +void Texture2D::CreateFromExistedData(void* data, int width, int height, int channels) +{ + //assert(data); + + m_width = width; + m_height = height; + m_channels = channels; + + glGenTextures(1, &m_handle); + glBindTexture(GL_TEXTURE_2D, m_handle); + glTexImage2D(GL_TEXTURE_2D, 0, (channels == 3) ? GL_RGB : GL_RGBA, width, height, 0, (channels == 3) ? GL_RGB : GL_RGBA, GL_UNSIGNED_BYTE, data); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_2D, 0); +} + +void Texture2D::CreateRaw(void* data, int width, int height, PixelFormat pf) +{ + m_width = width; + m_height = height; + m_channels = (pf == PF_R8G8B8) ? 3 : 4; + m_pf = pf; + + glGenTextures(1, &m_handle); + glBindTexture(GL_TEXTURE_2D, m_handle); + glTexImage2D(GL_TEXTURE_2D, 0, getGLInternalPF(pf), width, height, 0, getGLInternalPF(pf), GL_UNSIGNED_BYTE, data); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_2D, 0); +} + +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF + +void Texture2D::GenerateMipmaps() +{ + glBindTexture(GL_TEXTURE_2D, m_handle); + glGenerateMipmap(GL_TEXTURE_2D); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + //if (g_texAnisoFilter.getValueB()) { + // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, g_texAnisoLevel.getValueI()); + //} + + glBindTexture(GL_TEXTURE_2D, 0); +} + +void Texture2D::Bind() +{ + glBindTexture(GL_TEXTURE_2D, m_handle); +} + +void Texture2D::SetWrapS(TextureWrap wrap) +{ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, getGlWrap(wrap)); +} + +void Texture2D::SetWrapT(TextureWrap wrap) +{ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, getGlWrap(wrap)); +} + +void Texture2D::SetMin(TextureFilter filter) +{ + GLint param = 0; + param = getGlTexFilter(filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, param); +} + +void Texture2D::SetMag(TextureFilter filter) +{ + GLint param = 0; + param = getGlTexFilter(filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, param); +} + +GLint GetGlWrap(TextureWrap wrap) +{ + GLint param = 0; + + if (wrap == TextureWrap::Repeat) + param = GL_REPEAT; + else if (wrap == TextureWrap::MirroredRepeat) + param = GL_MIRRORED_REPEAT; + else if (wrap == TextureWrap::ClampToEdge) + param = GL_CLAMP_TO_EDGE; + else if (wrap == TextureWrap::ClampToBorder) + param = GL_CLAMP_TO_BORDER; + + return param; +} + +GLint GetGlTexFilter(TextureFilter filter) +{ + GLint param = 0; + + if (filter == TextureFilter::Linear) + param = GL_LINEAR; + else if (filter == TextureFilter::Nearest) + param = GL_NEAREST; + else if (filter == TextureFilter::LinearMipmapLinear) + param = GL_LINEAR_MIPMAP_LINEAR; + else if (filter == TextureFilter::LinearMipmapNearest) + param = GL_LINEAR_MIPMAP_NEAREST; + else if (filter == TextureFilter::NearestMipmapLinear) + param = GL_NEAREST_MIPMAP_LINEAR; + else if (filter == TextureFilter::NearestMipmapNearest) + param = GL_NEAREST_MIPMAP_NEAREST; + + return param; +} + +uint getGLPF(PixelFormat pf) +{ + return 0; + //return uint32_t(); +} + +// Kirill: Remove to render_main.cpp or something else +uint getGLInternalPF(PixelFormat pf) +{ + switch (pf) + { + case PF_UNKNOWN: + return 0; + + case PF_R8G8B8: + case PF_R8G8B8F: + return GL_RGB; + + case PF_R8G8B8A8: + case PF_R8G8B8A8F: + return GL_RGBA; + } + + return 0; +} + +uint getGLTypePF(PixelFormat pf) +{ + switch (pf) + { + case PF_UNKNOWN: + return 0; + + case PF_R8G8B8: + case PF_R8G8B8A8: + return GL_UNSIGNED_BYTE; + + case PF_R8G8B8F: + case PF_R8G8B8A8F: + return GL_FLOAT; + } + + return 0; +} diff --git a/engine/render/texture2d.h b/engine/render/texture2d.h new file mode 100644 index 0000000..c5822d5 --- /dev/null +++ b/engine/render/texture2d.h @@ -0,0 +1,49 @@ +#ifndef TEXTURE2D_H +#define TEXTURE2D_H + +#include + +#include "render/render_shared.h" + +class TexturesManager; + +class Texture2D +{ + friend class TexturesManager; +public: + static Texture2D* Create(); + +public: + Texture2D(); + ~Texture2D(); + + void CreateBlackTexture(int width, int height, int channels); + void CreateWhiteTexture(int width, int height, int channels); + void CreateGrayTexture(int width, int height, int channels); + void CreateTexture_Generator(int width, int height, int channels, int color); + void CreateFromExistedData(void* data, int width, int height, int channels); + void CreateFromFile(const char* filename); + + void CreateRaw(void* data, int width, int height, PixelFormat pf); + + void GenerateMipmaps(); + + void Bind(); + + void SetWrapS(TextureWrap wrap); + void SetWrapT(TextureWrap wrap); + void SetMin(TextureFilter filter); + void SetMag(TextureFilter filter); + + uint GetHandle() { return m_handle; } + +private: + std::string m_textureFileName; + PixelFormat m_pf; + int m_width; + int m_height; + int m_channels; + uint m_handle; +}; + +#endif // !TEXTURE2D_H diff --git a/engine/render/texturesmanager.cpp b/engine/render/texturesmanager.cpp new file mode 100644 index 0000000..b6d874f --- /dev/null +++ b/engine/render/texturesmanager.cpp @@ -0,0 +1,192 @@ +#include "utils/logger.h" +#include "filesystem/filemanager.h" +#include "render/texture2d.h" +#include "render/texturesmanager.h" +#include "render/gl_shared.h" + +// STB Image loading code +void Texture2D::CreateFromFile(const char* filename) +{ +/* + int width, height, channels; + m_textureFileName = filename; + + File* file = g_fileManager->OpenFile(filename, FileAccess::Read); + + file->seek(SeekDir::End, 0); + size_t imageSize = file->tell(); + file->seek(SeekDir::Begin, 0); + + uint8_t* fileData = (uint8_t*)malloc(imageSize); + file->read(fileData, imageSize); + + g_fileManager->CloseFile(file); + + uint8_t* imageData = stbi_load_from_memory(fileData, int(imageSize), &width, &height, &channels, 0); + + if (imageData == NULL) { + free(fileData); + Msg("Texture loading error: %s (%s)", filename, stbi_failure_reason()); + assert(imageData); + } + + CreateFromExistedData(imageData, width, height, channels); + m_textureFileName = filename; + + free(fileData); + + stbi_image_free(imageData); +*/ +} + +static const char* g_texFileExtensions[] = { ".png", ".jpeg", ".jpg", ".tga", ".bmp" }; +const int kTexFileExtensionsSize = sizeof(g_texFileExtensions) / sizeof(g_texFileExtensions[0]); + +TexturesManager* g_texturesManager = NULL; + +TexturesManager::TexturesManager() +{ + m_notex = NULL; +} + +TexturesManager::~TexturesManager() +{ + +} + +void TexturesManager::Init() +{ + //stbi_set_flip_vertically_on_load(true); + + m_notex = LoadTexture2D("content/textures/system/notex.png", true); + if (!m_notex) { + Logger::Error("TexturesManager::Init: Failed to initialize system texture! 'system/notex.png' is not exist."); + } +} + +void TexturesManager::Shutdown() +{ + if (!m_textures.empty()) { + Msg("--- unfreed textures ---"); + + for (std::vector::iterator it = m_textures.begin(); it != m_textures.end(); ++it) { + Msg("%s", (*it)->m_textureFileName.c_str()); + delete* it; + *it = NULL; + } + + m_textures.clear(); + } +} + +void TexturesManager::SetTexture(int slot, Texture2D* texture) +{ + glActiveTexture(GL_TEXTURE0 + slot); + glBindTexture(GL_TEXTURE_2D, texture ? texture->GetHandle() : 0); +} + +Texture2D* TexturesManager::CreateManual2D(const char* name, int width, int height, PixelFormat format, bool useAsRenderTarget) +{ + for (std::vector::iterator it = m_textures.begin(); it != m_textures.end(); ++it) { + if ((*it)->m_textureFileName == name) { + Logger::Error("TexturesManager::CreateManual2D: texture %s is already created!", name); + } + } + + // allocate + Texture2D* texture = Texture2D::Create(); + texture->CreateRaw(nullptr, width, height, format); + texture->m_textureFileName = name; + + if (useAsRenderTarget) + Msg("Created rt texture [%s]", name); + + return texture; +} + +bool IsSupportedExtension(const char* filename) +{ + std::string ext = fs::getFileExtension(filename); + + for (int i = 0; i < kTexFileExtensionsSize; i++) { + if (ext == g_texFileExtensions[i]) { + return true; + } + } + + return false; +} + +Texture2D* TexturesManager::LoadTexture2D(const char* texturename, bool useMipmaps /*= false*/) +{ + int texturesNbr = m_textures.size(); + for (int i = 0; i < texturesNbr; i++) { + if (m_textures[i]->m_textureFileName == texturename) + return m_textures[i]; + } + + if (strcmp(texturename, "$white$") == 0) { + Texture2D* tex = Texture2D::Create(); + tex->m_textureFileName = "$white$"; + tex->CreateWhiteTexture(16, 16, 3); + m_textures.push_back(tex); + return tex; + } + if (strcmp(texturename, "$black$") == 0) { + Texture2D* tex = Texture2D::Create(); + tex->m_textureFileName = "$black$"; + tex->CreateBlackTexture(16, 16, 3); + m_textures.push_back(tex); + return tex; + } + if (strcmp(texturename, "$gray$") == 0) { + Texture2D* tex = Texture2D::Create(); + tex->m_textureFileName = "$gray$"; + tex->CreateGrayTexture(16, 16, 3); + m_textures.push_back(tex); + return tex; + } + if (strcmp(texturename, "$gray_console$") == 0) { + Texture2D* tex = Texture2D::Create(); + tex->CreateTexture_Generator(16, 16, 3, 32); + m_textures.push_back(tex); + return tex; + } + + if (strlen(texturename) <= 0) { + return m_notex; + } + + std::string texnamebuf; + + // find texture from disk + for (int i = 0; i < kTexFileExtensionsSize; i++) + { + std::string textureFilename = fs::getFileNameWithoutExtension(texturename); + textureFilename += g_texFileExtensions[i]; + + if (g_fileManager->FileExist(textureFilename.c_str())) + { + texnamebuf = textureFilename; + break; + } + } + + if (!texnamebuf.empty()) { + Texture2D* texture = Texture2D::Create(); + texture->CreateFromFile(texnamebuf.c_str()); + + if (useMipmaps) + texture->GenerateMipmaps(); + + Msg("loaded %s", fs::getFilenameWithoutPathAndExtension(texturename).c_str()); + m_textures.push_back(texture); + return texture; + } + else if (texnamebuf.empty() && m_notex) { + Msg("not found %s", fs::getFilenameWithoutPathAndExtension(texturename).c_str()); + return m_notex; + } + + return NULL; +} diff --git a/engine/render/texturesmanager.h b/engine/render/texturesmanager.h new file mode 100644 index 0000000..80a2f8b --- /dev/null +++ b/engine/render/texturesmanager.h @@ -0,0 +1,38 @@ +#ifndef TEXTURESMANAGER_H +#define TEXTURESMANAGER_H + +#include + +#include "render/render_shared.h" + +class Texture2D; + +class TexturesManager +{ +public: + TexturesManager(); + ~TexturesManager(); + + void Init(); + void Shutdown(); + + void SetTexture(int slot, Texture2D* texture); + + Texture2D* CreateManual2D( + const char* name, + int width, + int height, + PixelFormat format, + bool useAsRenderTarget = false); + + Texture2D* LoadTexture2D(const char* texturename, bool useMipmaps = false); + +private: + std::vector m_textures; + + Texture2D* m_notex; +}; + +extern TexturesManager* g_texturesManager; + +#endif // !TEXTURESMANAGER_H diff --git a/engine/utils/rtti.h b/engine/utils/rtti.h new file mode 100644 index 0000000..4075fc8 --- /dev/null +++ b/engine/utils/rtti.h @@ -0,0 +1,141 @@ +/* + +The other day we tracked a bad bug where a base class was been cast to +the wrong thing. +The reason why it happed was because we didn't have any RTTI (Run Time +Type Information) in place. +We also didn't want to use the one that it comes with the compiler for +fear that it will add "evil" code. +So after hitting that bug I decided to do my on RTTI. I wanted to be +simple and very fast. +This is what we use now. It looks kind of neat so I thought to share it. +Enjoy. + +Tomas +tarce@zen-x.net + +*/ + +#include +#include +#include +//============================================================================== +// RTTI +//============================================================================== +// Using the RTTI +//------------------------------------------------------------------------------ +// struct base +// { +// CREATE_RTTI_BASE( base ); +// }; +// +// struct pepe : public base +// { +// CREATE_RTTI( pepe, base, base ) +// void Normal( void ){} +// }; +// +// struct super_pepe : public pepe +// { +// CREATE_RTTI( super_pepe, pepe, base ) +// void Super( void ){} +// }; +// +// void main( void ) +// { +// super_pepe SP; // The original class +// base& B = SP; // Some generic base reference +// +// // We want to safely convert to pepe +// pepe& P = pepe::GetSaveType( B ); +// +// // cast the base class right +// if( B.IsKindOf( super_pepe::GetRTTI() ) ) +// { +// super_pepe& SP = super_pepe::GetSaveType( B ); +// SP.Super(); +// } +// else if( B.IsKindOf( pepe::GetRTTI() ) ) +// { +// pepe& P = pepe::GetSaveType( B ); +// P.Normal(); +// } +// } +//============================================================================== +#define CREATE_RTTI( TYPE, BASE_CLASS ) +#define CREATE_RTTI_BASE( TYPE ) + +struct RTTI +{ + bool IsKindOf( const RTTI& RTTI ) const; + RTTI ( const char* pTypeName ); + RTTI ( const char* pTypeName, const RTTI& RTTI ); + + const char* pType; // This is not really need it, just useful in debugger. + const RTTI& Next; +}; + +//============================================================================== +// IMPLEMENTATION +//============================================================================== +#pragma warning( push ) +#pragma warning( disable : 4355 ) // warning 'this' used in base member initializer list +#undef CREATE_RTTI +#undef CREATE_RTTI_BASE + +inline RTTI::RTTI( const char* pTypeName ) : +pType(pTypeName), Next( *this ){} + +inline RTTI::RTTI( const char* pTypeName, const RTTI& RTTI ) : +pType(pTypeName), Next( RTTI ){} + +inline bool RTTI::IsKindOf( const RTTI& RTTI ) const +{ + const RTTI* p = this; + do + { + if( p == &RTTI ) return true; + if( p == &p->Next ) break; + p = &p->Next; + + } while(1); + return false; +} + +#define PRIVATE_RTTI_FUNCTION_SET( TYPE, BASE_CLASS ) \ + virtual inline const RTTI& GetObjectRTTI( void ) const \ + { \ + return TYPE::GetRTTI(); \ + } \ + inline const bool IsKindOf( const RTTI& RTTI ) const \ + { \ + return GetObjectRTTI().IsKindOf( RTTI ); \ + } \ + static inline TYPE& GetSaveType( BASE_CLASS& Object ) \ + { \ + assert( Object.IsKindOf(TYPE::GetRTTI()) ); \ + return *((TYPE*)&Object); \ + } \ + static inline const TYPE& GetSaveType( const BASE_CLASS& Object ) \ + { \ + assert( Object.IsKindOf(TYPE::GetRTTI()) ); \ + return *((const TYPE*)&Object); \ + } + +#define CREATE_RTTI( TYPE, TYPE_PARENT, BASE_CLASS ) \ + static inline const RTTI& GetRTTI( void ) \ + { \ + static RTTI s_RTTI( #TYPE, TYPE_PARENT::GetRTTI() ); \ + return s_RTTI; \ + } \ + PRIVATE_RTTI_FUNCTION_SET(TYPE,BASE_CLASS) + +#define CREATE_RTTI_BASE( TYPE ) \ + static inline const RTTI& GetRTTI( void ) \ + { \ + static RTTI s_RTTI( #TYPE ); \ + return s_RTTI; \ + } \ + PRIVATE_RTTI_FUNCTION_SET(TYPE,TYPE) + +#pragma warning( pop )