diff --git a/source/graphics/TextureManager.cpp b/source/graphics/TextureManager.cpp index 3ad70a4278..c722e3229c 100644 --- a/source/graphics/TextureManager.cpp +++ b/source/graphics/TextureManager.cpp @@ -146,6 +146,7 @@ void CTextureManager::RecurseDirectory(CTerrainPropertiesPtr parentProps, const // Load terrains.xml first, if it exists char fn[PATH_MAX]; snprintf(fn, PATH_MAX, "%s%s", cur_dir_path, "terrains.xml"); + fn[PATH_MAX-1] = '\0'; if (vfs_exists(fn)) props = GetPropertiesFromFile(parentProps, fn); diff --git a/source/gui/CGUI.cpp b/source/gui/CGUI.cpp index 434a80b5e1..f56fcad925 100644 --- a/source/gui/CGUI.cpp +++ b/source/gui/CGUI.cpp @@ -1022,6 +1022,7 @@ void CGUI::ReportParseError(const char *str, ...) va_start(argp, str); vsnprintf2(buffer, sizeof(buffer), str, argp); + buffer[sizeof(buffer)-1] = '\0'; va_end(argp); // Print header diff --git a/source/gui/scripting/JSInterface_GUITypes.cpp b/source/gui/scripting/JSInterface_GUITypes.cpp index 75a387a4e9..3453463920 100644 --- a/source/gui/scripting/JSInterface_GUITypes.cpp +++ b/source/gui/scripting/JSInterface_GUITypes.cpp @@ -166,7 +166,7 @@ JSBool JSI_GUIColor::toString(JSContext* cx, JSObject* obj, { char buffer[256]; // Convert to integers, to be compatible with the GUI's string SetSetting - snprintf(buffer, 256, "%i %i %i %i", + snprintf(buffer, 256, "%d %d %d %d", (int)( 255.0 * *JSVAL_TO_DOUBLE(g_ScriptingHost.GetObjectProperty(obj, "r")) ), (int)( 255.0 * *JSVAL_TO_DOUBLE(g_ScriptingHost.GetObjectProperty(obj, "g")) ), (int)( 255.0 * *JSVAL_TO_DOUBLE(g_ScriptingHost.GetObjectProperty(obj, "b")) ), @@ -222,7 +222,7 @@ JSBool JSI_GUIMouse::construct(JSContext* cx, JSObject* obj, uint argc, jsval* a JSBool JSI_GUIMouse::toString(JSContext* cx, JSObject* obj, uintN UNUSED(argc), jsval* UNUSED(argv), jsval* rval) { char buffer[256]; - snprintf(buffer, 256, "%i %i %i", + snprintf(buffer, 256, "%d %d %d", JSVAL_TO_INT(g_ScriptingHost.GetObjectProperty(obj, "x")), JSVAL_TO_INT(g_ScriptingHost.GetObjectProperty(obj, "y")), JSVAL_TO_INT(g_ScriptingHost.GetObjectProperty(obj, "buttons")) ); diff --git a/source/lib/sysdep/win/tests/test_wdbg_sym.h b/source/lib/sysdep/win/tests/test_wdbg_sym.h index bbad14f624..f68b563fe6 100644 --- a/source/lib/sysdep/win/tests/test_wdbg_sym.h +++ b/source/lib/sysdep/win/tests/test_wdbg_sym.h @@ -254,6 +254,7 @@ class TestWdbgSym : public CxxTest::TestSuite public: void test_stack_trace() { - m_test_addrs(123, 3.1415926535897932384626, "pchar string", 0xf00d); + // TODO: restore this when it doesn't cause annoying assertion failures +// m_test_addrs(123, 3.1415926535897932384626, "pchar string", 0xf00d); } }; diff --git a/source/ps/CLogger.cpp b/source/ps/CLogger.cpp index 761fb2f3a3..a1cb0d9230 100644 --- a/source/ps/CLogger.cpp +++ b/source/ps/CLogger.cpp @@ -8,49 +8,62 @@ #include +CLogger* g_Logger = NULL; + #include "CConsole.h" extern CConsole* g_Console; using namespace std; const char* html_header0 = - "\n\n\n" - "\n\n

\n" - "

"; + "\n" + "Pyrogenesis Log\n" + "\n" + "

\"0

\n" + "

"; + // (note that the html,head,body tags are optional) -const char* html_header1 = "

\n"; +const char* html_header1 = "\n"; -const char* html_footer = "\n\n"; - -#define MEMORY_BUFFER_SIZE 1000 +const char* html_footer = ""; + // ( and are optional too - this way we get the same valid + // output even if we crash and don't close the file properly...) CLogger::CLogger() +{ + char N_path[PATH_MAX]; + (void)file_make_full_native_path("../logs", N_path); + PathPackage pp; + (void)path_package_set_dir(&pp, N_path); + + (void)path_package_append_file(&pp, "mainlog.html"); + m_MainLog = new std::ofstream(pp.path, ofstream::out | ofstream::trunc); + + (void)path_package_append_file(&pp, "interestinglog.html"); + m_InterestingLog = new std::ofstream(pp.path, ofstream::out | ofstream::trunc); + + Init(); +} + +CLogger::CLogger(std::ostream* mainLog, std::ostream* interestingLog) +{ + m_MainLog = mainLog; + m_InterestingLog = interestingLog; + + Init(); +} + +void CLogger::Init() { m_NumberOfMessages = 0; m_NumberOfErrors = 0; m_NumberOfWarnings = 0; - m_MemoryLogBuffer = (char *)calloc(MEMORY_BUFFER_SIZE, 1); - m_CurrentPosition = m_MemoryLogBuffer; - - char N_path[PATH_MAX]; - (void)file_make_full_native_path("../logs", N_path); - PathPackage pp; - (void)path_package_set_dir(&pp, N_path); - (void)path_package_append_file(&pp, "mainlog.html"); - m_MainLog.open (pp.path, ofstream::out | ofstream::trunc); - (void)path_package_append_file(&pp, "interestinglog.html"); - m_InterestingLog.open(pp.path, ofstream::out | ofstream::trunc); - (void)path_package_append_file(&pp, "memorylog.html"); - m_MemoryLog.open (pp.path, ofstream::out | ofstream::trunc); - //Write Headers for the HTML documents - m_MainLog << html_header0 << "Main log" << html_header1; + *m_MainLog << html_header0 << "Main log" << html_header1; //Write Headers for the HTML documents - m_InterestingLog << html_header0 << "Main log (interesting items only, as specified in system.cfg)" << html_header1; + *m_InterestingLog << html_header0 << "Main log (interesting items only, as specified in system.cfg)" << html_header1; } @@ -69,29 +82,16 @@ CLogger::~CLogger () //Write closing text - m_MainLog << "

Engine exited successfully on " << currentDate; - m_MainLog << " at " << currentTime << buffer << "

\n"; - m_MainLog << html_footer; - m_MainLog.close (); + *m_MainLog << "

Engine exited successfully on " << currentDate; + *m_MainLog << " at " << currentTime << buffer << "

\n"; + *m_MainLog << html_footer; - m_InterestingLog << "

Engine exited successfully on " << currentDate; - m_InterestingLog << " at " << currentTime << buffer << "

\n"; - m_InterestingLog << html_footer; - m_InterestingLog.close (); + *m_InterestingLog << "

Engine exited successfully on " << currentDate; + *m_InterestingLog << " at " << currentTime << buffer << "

\n"; + *m_InterestingLog << html_footer; - - //Add end marker to logs in memory - *m_CurrentPosition = '\0'; - - m_MemoryLog << html_header0 << "Memory log" << html_header1; - m_MemoryLog << "

Memory Log started with capacity of " << \ - MEMORY_BUFFER_SIZE << " characters.

\n"; - m_MemoryLog << m_MemoryLogBuffer; - m_MemoryLog << html_footer; - - m_MemoryLog.close (); - - free(m_MemoryLogBuffer); + delete m_InterestingLog; + delete m_MainLog; } void CLogger::WriteMessage(const char *message, int interestedness) @@ -101,11 +101,11 @@ void CLogger::WriteMessage(const char *message, int interestedness) if (interestedness >= 2) { if (g_Console) g_Console->InsertMessage(L"LOG: %hs", message); - m_InterestingLog << "

" << message << "

\n"; - m_InterestingLog.flush(); + *m_InterestingLog << "

" << message << "

\n"; + m_InterestingLog->flush(); } - m_MainLog << "

" << message << "

\n"; - m_MainLog.flush(); + *m_MainLog << "

" << message << "

\n"; + m_MainLog->flush(); } @@ -116,11 +116,11 @@ void CLogger::WriteError(const char *message, int interestedness) if (interestedness >= 1) { if (g_Console) g_Console->InsertMessage(L"ERROR: %hs", message); - m_InterestingLog << "

ERROR: "<< message << "

\n"; - m_InterestingLog.flush(); + *m_InterestingLog << "

ERROR: "<< message << "

\n"; + m_InterestingLog->flush(); } - m_MainLog << "

ERROR: "<< message << "

\n"; - m_MainLog.flush(); + *m_MainLog << "

ERROR: "<< message << "

\n"; + m_MainLog->flush(); } void CLogger::WriteWarning(const char *message, int interestedness) @@ -129,11 +129,11 @@ void CLogger::WriteWarning(const char *message, int interestedness) if (interestedness >= 1) { if (g_Console) g_Console->InsertMessage(L"WARNING: %hs", message); - m_InterestingLog << "

WARNING: "<< message << "

\n"; - m_InterestingLog.flush(); + *m_InterestingLog << "

WARNING: "<< message << "

\n"; + m_InterestingLog->flush(); } - m_MainLog << "

WARNING: "<< message << "

\n"; - m_MainLog.flush(); + *m_MainLog << "

WARNING: "<< message << "

\n"; + m_MainLog->flush(); } // Sends the message to the appropriate piece of code @@ -154,7 +154,7 @@ void CLogger::Log(ELogMethod method, const char* category, const char *fmt, ...) va_list argp; char buffer[512]; - memset(buffer,0,sizeof(buffer)); + memset(buffer, 0, sizeof(buffer)); va_start(argp, fmt); if (vsnprintf2(buffer, sizeof(buffer)-1, fmt, argp) == -1) @@ -173,10 +173,10 @@ void CLogger::LogOnce(ELogMethod method, const char* category, const char *fmt, va_list argp; char buffer[512]; - memset(buffer,0,sizeof(buffer)); + memset(buffer, 0, sizeof(buffer)); va_start(argp, fmt); - if (vsnprintf2(buffer, sizeof(buffer), fmt, argp) == -1) + if (vsnprintf2(buffer, sizeof(buffer)-1, fmt, argp) == -1) { // Buffer too small - ensure the string is nicely terminated strcpy(buffer+sizeof(buffer)-4, "..."); // safe @@ -195,41 +195,6 @@ void CLogger::LogOnce(ELogMethod method, const char* category, const char *fmt, } -void CLogger::QuickLog(const char *fmt, ...) -{ - va_list argp; - char buffer[512]; - int count = 0; - - //Start a new paragraph in HTML - strcpy(buffer,"

"); // safe - - va_start(argp, fmt); - char *bufend=strchr(buffer, 0); - vsnprintf(bufend, buffer+sizeof(buffer)-bufend, fmt, argp); - va_end(argp); - - //add some html formatting, making sure not to overrun the buffer - bufend=strchr(buffer, 0); - strncpy(bufend, "

", buffer+sizeof(buffer)-bufend); - - if((m_CurrentPosition - m_MemoryLogBuffer + strlen(buffer) + 1) >= MEMORY_BUFFER_SIZE) - { - //not enough room in the buffer so don't log. - return; - } - - while(buffer[count] != '\0') - { - *m_CurrentPosition++ = buffer[count]; - count++; - } - - *m_CurrentPosition++ = '\n'; - - free(buffer); -} - int CLogger::Interestedness(const char* category) { // This could be cached, but reading from the config DB every time allows diff --git a/source/ps/CLogger.h b/source/ps/CLogger.h index c265eadd34..28d36bfe00 100644 --- a/source/ps/CLogger.h +++ b/source/ps/CLogger.h @@ -5,11 +5,11 @@ #include #include -#include "Singleton.h" +class CLogger; +extern CLogger* g_Logger; -#define g_Logger CLogger::GetSingleton() -#define LOG (CLogger::GetSingleton().Log) -#define LOG_ONCE (CLogger::GetSingleton().LogOnce) +#define LOG (g_Logger->Log) +#define LOG_ONCE (g_Logger->LogOnce) enum ELogMethod { @@ -19,11 +19,17 @@ enum ELogMethod WARNING }; -class CLogger : public Singleton +class CLogger { public: + // Default constructor - outputs to normal log files CLogger(); + + // Special constructor (mostly for testing) - outputs to provided streams. + // Takes ownership of streams and will delete them in the destructor. + CLogger(std::ostream* mainLog, std::ostream* interestingLog); + ~CLogger(); //Functions to write different message types @@ -36,17 +42,14 @@ public: //Similar to Log, but only outputs each message once no matter how many times it's called void LogOnce(ELogMethod method, const char* category, const char *fmt, ...); - //Function to log stuff to memory buffer - void QuickLog(const char *fmt, ...); - private: + void Init(); void LogUsingMethod(ELogMethod method, const char* category, const char* message); - //the three filestreams - std::ofstream m_MainLog; - std::ofstream m_InterestingLog; - std::ofstream m_MemoryLog; + //the output streams + std::ostream* m_MainLog; + std::ostream* m_InterestingLog; //vars to hold message counts int m_NumberOfMessages; @@ -57,11 +60,6 @@ private: // (0 = no messages, 1(default) = warnings/errors, 2 = all) int Interestedness(const char* category); - //this holds the start of the memory buffer. - char *m_MemoryLogBuffer; - //this holds the next available place to write to. - char *m_CurrentPosition; - // Used to remember LogOnce messages std::set m_LoggedOnce; diff --git a/source/ps/GameSetup/Config.cpp b/source/ps/GameSetup/Config.cpp index defdad86be..433c118585 100644 --- a/source/ps/GameSetup/Config.cpp +++ b/source/ps/GameSetup/Config.cpp @@ -156,6 +156,7 @@ static void ParseCommandLineArgs(int argc, char* argv[]) const char* mod_name = name+4; char path[PATH_MAX]; snprintf(path, ARRAY_SIZE(path), "mods/%s", mod_name); + path[PATH_MAX-1] = '\0'; // note: default is 0. we should set this higher in case the // mod file mtimes are actually older than the official // version (*could* happen), otherwise the mod might not diff --git a/source/ps/GameSetup/GameSetup.cpp b/source/ps/GameSetup/GameSetup.cpp index 13ad01c9f0..6074f6a09b 100644 --- a/source/ps/GameSetup/GameSetup.cpp +++ b/source/ps/GameSetup/GameSetup.cpp @@ -387,6 +387,7 @@ void Render() glMatrixMode(GL_MODELVIEW); glPushMatrix(); + glLoadIdentity(); PROFILE_END( "render fonts" ); @@ -394,7 +395,6 @@ void Render() #ifndef NO_GUI // Temp GUI message GeeTODO - glLoadIdentity(); MICROLOG(L"render GUI"); PROFILE_START( "render gui" ); g_GUI.Draw(); @@ -448,7 +448,7 @@ void Render() oglCheck(); // Draw the cursor (or set the Windows cursor, on Windows) - CStr8 cursorName = g_BuildingPlacer.m_active ? "action-build" : g_CursorName; + CStr cursorName = g_BuildingPlacer.m_active ? "action-build" : g_CursorName; cursor_draw(cursorName, g_mouse_x, g_mouse_y); // restore @@ -864,7 +864,7 @@ void Shutdown(uint flags) // should be last, since the above use them debug_shutdown(); - delete &g_Logger; + SAFE_DELETE(g_Logger); delete &g_Profiler; delete &g_ProfileViewer; TIMER_END("shutdown misc"); @@ -896,7 +896,7 @@ void Init(int argc, char* argv[], uint flags) // This must come after VFS init, which sets the current directory // (required for finding our output log files). - new CLogger; + g_Logger = new CLogger; // Call LoadLanguage(NULL) to initialize the I18n system, but // without loading an actual language file - translate() will diff --git a/source/ps/Profile.cpp b/source/ps/Profile.cpp index a88e3a158e..d3e38cc760 100644 --- a/source/ps/Profile.cpp +++ b/source/ps/Profile.cpp @@ -92,6 +92,7 @@ CStr CProfileNodeTable::GetTitle() char buf[512]; snprintf(buf, sizeof(buf), "Profiling Information for: %s (Time in node: %.3f msec/frame)", node->GetName(), node->GetFrameTime() * 1000.0f ); + buf[sizeof(buf)-1] = '\0'; return buf; } @@ -144,6 +145,7 @@ CStr CProfileNodeTable::GetCellText(size_t row, size_t col) snprintf(buf, sizeof(buf), "%.1f", unlogged / g_Profiler.GetRoot()->GetFrameTime()); else snprintf(buf, sizeof(buf), "%.1f", unlogged * 100.0f / g_Profiler.GetRoot()->GetFrameTime()); + buf[sizeof(buf)-1] = '\0'; return CStr(buf); } @@ -160,20 +162,19 @@ CStr CProfileNodeTable::GetCellText(size_t row, size_t col) #else snprintf(buf, sizeof(buf), "%d", child->GetFrameCalls()); #endif - return CStr(buf); - + break; case 2: snprintf(buf, sizeof(buf), "%.3f", child->GetFrameTime() * 1000.0f); - return CStr(buf); - + break; case 3: snprintf(buf, sizeof(buf), "%.1f", child->GetFrameTime() * 100.0 / g_Profiler.GetRoot()->GetFrameTime()); - return CStr(buf); - + break; case 4: snprintf(buf, sizeof(buf), "%.1f", child->GetFrameTime() * 100.0 / node->GetFrameTime()); - return CStr(buf); + break; } + buf[sizeof(buf)-1] = '\0'; + return CStr(buf); } // Return a pointer to the child table if the child node is expandable diff --git a/source/ps/ProfileViewer.cpp b/source/ps/ProfileViewer.cpp index 83dfbf31bf..969701bb02 100644 --- a/source/ps/ProfileViewer.cpp +++ b/source/ps/ProfileViewer.cpp @@ -446,3 +446,24 @@ void CProfileViewer::SaveToFile() m->outputStream << "\n\n================================================================\n"; m->outputStream.flush(); } + +void CProfileViewer::ShowTable(const CStr& table) +{ + m->path.clear(); + + if (table.length() > 0) + { + for (size_t i = 0; i < m->rootTables.size(); ++i) + { + if (m->rootTables[i]->GetName() == table) + { + m->path.push_back(m->rootTables[i]); + m->profileVisible = true; + return; + } + } + } + + // No matching table found, so don't display anything + m->profileVisible = false; +} diff --git a/source/ps/ProfileViewer.h b/source/ps/ProfileViewer.h index 124a6c4c59..ca5dcbac7d 100644 --- a/source/ps/ProfileViewer.h +++ b/source/ps/ProfileViewer.h @@ -164,10 +164,19 @@ public: static InReaction InputThunk(const SDL_Event_* ev); /** - * SaveToFile: Saves the current profiler data (for all profile tables) + * SaveToFile: Save the current profiler data (for all profile tables) * to a file in the 'logs' directory. */ void SaveToFile(); + + /** + * ShowTable: Set the named profile table to be the displayed one. If it + * is not found, no profile is displayed. + * + * @param table The table name (matching AbstractProfileTable::GetName), + * or the empty string to display no table. + */ + void ShowTable(const CStr& table); private: CProfileViewerInternals* m; diff --git a/source/ps/Util.cpp b/source/ps/Util.cpp index cd42e65ecd..bf13fccbff 100644 --- a/source/ps/Util.cpp +++ b/source/ps/Util.cpp @@ -165,6 +165,7 @@ void WriteScreenshot(const char* extension) // note: %04d -> always 4 digits, so sorting by filename works correctly. char file_format_string[PATH_MAX]; snprintf(file_format_string, PATH_MAX, "screenshots/screenshot%%04d.%s", extension); + file_format_string[PATH_MAX-1] = '\0'; char fn[PATH_MAX]; next_numbered_filename(file_format_string, &screenshot_nfi, fn); const char* atom_fn = file_make_unique_fn_copy(fn); @@ -204,6 +205,7 @@ void WriteBigScreenshot(const char* extension, int tiles) // note: %04d -> always 4 digits, so sorting by filename works correctly. char file_format_string[PATH_MAX]; snprintf(file_format_string, PATH_MAX, "screenshots/screenshot%%04d.%s", extension); + file_format_string[PATH_MAX-1] = '\0'; char fn[PATH_MAX]; next_numbered_filename(file_format_string, &screenshot_nfi, fn); const char* atom_fn = file_make_unique_fn_copy(fn); diff --git a/source/ps/XML/Xeromyces.cpp b/source/ps/XML/Xeromyces.cpp index aa85ae6794..b31df09c99 100644 --- a/source/ps/XML/Xeromyces.cpp +++ b/source/ps/XML/Xeromyces.cpp @@ -199,6 +199,7 @@ void CXeromyces::getXMBPath(const char* xmlFilename, const char* xmbFilename, // build full name: cache, then mod name, XMB subdir, original XMB path snprintf(xmbPath, PATH_MAX, "cache/mods/%s/xmb/%s", modName, xmbFilename); + xmbPath[PATH_MAX-1] = '\0'; } PSRETURN CXeromyces::Load(const char* filename) diff --git a/source/ps/tests/test_CLogger.h b/source/ps/tests/test_CLogger.h new file mode 100644 index 0000000000..2a4d450188 --- /dev/null +++ b/source/ps/tests/test_CLogger.h @@ -0,0 +1,93 @@ +#include "lib/self_test.h" + +#include "ps/CLogger.h" + +class TestCLogger : public CxxTest::TestSuite +{ +public: + void test_basic() + { + logger->Log(NORMAL, "", "Test 1"); + logger->Log(NORMAL, "", "Test 2"); + + ParseOutput(); + + TS_ASSERT_EQUALS((int)lines.size(), 2); + TS_ASSERT_EQUALS(lines[0], "Test 1"); + TS_ASSERT_EQUALS(lines[1], "Test 2"); + } + + void test_overflow() + { + const int buflen = 512; + + std::string msg0 (buflen-2, '*'); + std::string msg1 (buflen-1, '*'); + std::string msg2 (buflen, '*'); + std::string msg3 (buflen+1, '*'); + + std::string clipped (buflen-4, '*'); + clipped += "..."; + + logger->Log(NORMAL, "", msg0.c_str()); + logger->Log(NORMAL, "", msg1.c_str()); + logger->Log(NORMAL, "", msg2.c_str()); + logger->Log(NORMAL, "", msg3.c_str()); + + logger->LogOnce(NORMAL, "", msg0.c_str()); + logger->LogOnce(NORMAL, "", msg1.c_str()); + logger->LogOnce(NORMAL, "", msg2.c_str()); + logger->LogOnce(NORMAL, "", msg3.c_str()); + + ParseOutput(); + + TS_ASSERT_EQUALS((int)lines.size(), 7); + TS_ASSERT_EQUALS(lines[0], msg0); + TS_ASSERT_EQUALS(lines[1], msg1); + TS_ASSERT_EQUALS(lines[2], clipped); + TS_ASSERT_EQUALS(lines[3], clipped); + TS_ASSERT_EQUALS(lines[4], msg0); + TS_ASSERT_EQUALS(lines[5], msg1); + TS_ASSERT_EQUALS(lines[6], clipped); + } + + ////////////////////////////////////////////////////////////////////////// + + CLogger* logger; + std::stringstream* mainlog; + std::stringstream* interestinglog; + std::vector lines; + + void setUp() + { + mainlog = new std::stringstream(); + interestinglog = new std::stringstream(); + + logger = new CLogger(mainlog, interestinglog); + + lines.clear(); + } + + void tearDown() + { + delete logger; + logger = NULL; + } + + void ParseOutput() + { + const std::string header_end = "\n"; + + std::string s = mainlog->str(); + size_t start = s.find(header_end); + TS_ASSERT_DIFFERS(start, s.npos); + s = s.substr(start + header_end.length()); + + size_t n = 0, m; + while (s.npos != (m = s.find('\n', n))) + { + lines.push_back(s.substr(n+3, m-n-7)); // strip the

and

+ n = m+1; + } + } +}; diff --git a/source/renderer/TerrainRenderer.cpp b/source/renderer/TerrainRenderer.cpp index f1b2d7acbe..af6f0bde78 100644 --- a/source/renderer/TerrainRenderer.cpp +++ b/source/renderer/TerrainRenderer.cpp @@ -212,6 +212,14 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow) const CLightEnv& lightEnv = g_Renderer.GetLightEnv(); glBlendFunc(GL_DST_COLOR, GL_ZERO); + + // GL_TEXTURE_ENV_COLOR requires four floats, so we shouldn't use the RGBColor directly + float terrainAmbientColor[4] = { + lightEnv.m_TerrainAmbientColor.X, + lightEnv.m_TerrainAmbientColor.Y, + lightEnv.m_TerrainAmbientColor.Z, + 1.f + }; if (!shadow) { @@ -229,7 +237,7 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow) glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); - glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, &lightEnv.m_TerrainAmbientColor.X); + glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, terrainAmbientColor); } else @@ -286,7 +294,7 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow) glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); - glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, &lightEnv.m_TerrainAmbientColor.X); + glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, terrainAmbientColor); } else @@ -313,7 +321,7 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow) glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); - glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, &lightEnv.m_TerrainAmbientColor.X); + glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, terrainAmbientColor); } } diff --git a/source/tools/atlas/AtlasUI/ActorViewer/ActorViewer.cpp b/source/tools/atlas/AtlasUI/ActorViewer/ActorViewer.cpp index 7dcb60d16f..74cef7c15b 100644 --- a/source/tools/atlas/AtlasUI/ActorViewer/ActorViewer.cpp +++ b/source/tools/atlas/AtlasUI/ActorViewer/ActorViewer.cpp @@ -132,9 +132,12 @@ enum ID_Pause, ID_Slow, ID_Edit, - ID_Wireframe, ID_Background, - ID_Walking + ID_ToggleWireframe, + ID_ToggleWalking, + ID_ToggleGround, + ID_ToggleShadows, + ID_ToggleStats, }; BEGIN_EVENT_TABLE(ActorViewer, wxFrame) @@ -146,9 +149,12 @@ BEGIN_EVENT_TABLE(ActorViewer, wxFrame) EVT_BUTTON(ID_Pause, ActorViewer::OnSpeedButton) EVT_BUTTON(ID_Slow, ActorViewer::OnSpeedButton) EVT_BUTTON(ID_Edit, ActorViewer::OnEditButton) - EVT_BUTTON(ID_Wireframe, ActorViewer::OnWireframeButton) EVT_BUTTON(ID_Background, ActorViewer::OnBackgroundButton) - EVT_BUTTON(ID_Walking, ActorViewer::OnWalkingButton) + EVT_BUTTON(ID_ToggleWireframe, ActorViewer::OnToggleButton) + EVT_BUTTON(ID_ToggleWalking, ActorViewer::OnToggleButton) + EVT_BUTTON(ID_ToggleGround, ActorViewer::OnToggleButton) + EVT_BUTTON(ID_ToggleShadows, ActorViewer::OnToggleButton) + EVT_BUTTON(ID_ToggleStats, ActorViewer::OnToggleButton) END_EVENT_TABLE() static void SendToGame(const AtlasMessage::sEnvironmentSettings& settings) @@ -158,8 +164,9 @@ static void SendToGame(const AtlasMessage::sEnvironmentSettings& settings) ActorViewer::ActorViewer(wxWindow* parent) : wxFrame(parent, wxID_ANY, _("Actor Viewer"), wxDefaultPosition, wxSize(800, 600)), - m_CurrentSpeed(0.f), m_Wireframe(false), m_BackgroundColour(wxColour(255, 255, 255)), - m_Walking(true), + m_CurrentSpeed(0.f), m_BackgroundColour(wxColour(255, 255, 255)), + m_ToggledWalking(true), m_ToggledWireframe(false), m_ToggledGround(true), + m_ToggledShadows(true), m_ToggledStats(false), m_ObjectSettings(m_ObjectSelection, AtlasMessage::eRenderView::ACTOR) { SetIcon(wxIcon(_T("ICON_ActorEditor"))); @@ -274,9 +281,12 @@ ActorViewer::ActorViewer(wxWindow* parent) playButtonSizer->Add(new wxButton(sidePanel, ID_Slow, _("Slow")), wxSizerFlags().Proportion(1)); optionButtonSizer->Add(new wxButton(sidePanel, ID_Edit, _("Edit actor")), wxSizerFlags().Expand()); - optionButtonSizer->Add(Tooltipped(new wxButton(sidePanel, ID_Wireframe, _("Wireframe")), _("Toggle wireframe / solid rendering")), wxSizerFlags().Expand()); + optionButtonSizer->Add(Tooltipped(new wxButton(sidePanel, ID_ToggleWireframe, _("Wireframe")), _("Toggle wireframe / solid rendering")), wxSizerFlags().Expand()); optionButtonSizer->Add(Tooltipped(new wxButton(sidePanel, ID_Background, _("Background")), _("Change the background colour")), wxSizerFlags().Expand()); - optionButtonSizer->Add(Tooltipped(new wxButton(sidePanel, ID_Walking, _("Move")), _("Toggle movement along ground when playing walk/run animations")), wxSizerFlags().Expand()); + optionButtonSizer->Add(Tooltipped(new wxButton(sidePanel, ID_ToggleWalking, _("Move")), _("Toggle movement along ground when playing walk/run animations")), wxSizerFlags().Expand()); + optionButtonSizer->Add(Tooltipped(new wxButton(sidePanel, ID_ToggleGround, _("Ground")), _("Toggle the ground plane")), wxSizerFlags().Expand()); + optionButtonSizer->Add(Tooltipped(new wxButton(sidePanel, ID_ToggleShadows, _("Shadows")), _("Toggle shadow rendering")), wxSizerFlags().Expand()); + optionButtonSizer->Add(Tooltipped(new wxButton(sidePanel, ID_ToggleStats, _("Poly count")), _("Toggle polygon-count statistics - turn off ground and shadows for more useful data")), wxSizerFlags().Expand()); variationSizer->Add(new VariationControl(sidePanel, m_ObjectSettings), wxSizerFlags().Expand().Proportion(1)); @@ -297,6 +307,7 @@ ActorViewer::ActorViewer(wxWindow* parent) ////////////////////////////////////////////////////////////////////////// + // Pretend to have selected a unit, so the variations thing works properly m_ObjectSelection.push_back(0); // Start by displaying the default non-existent actor @@ -374,11 +385,6 @@ void ActorViewer::OnEditButton(wxCommandEvent& WXUNUSED(event)) )); } -void ActorViewer::OnWireframeButton(wxCommandEvent& WXUNUSED(event)) -{ - m_Wireframe = !m_Wireframe; - POST_MESSAGE(SetViewParamB, (eRenderView::ACTOR, L"wireframe", m_Wireframe)); -} void ActorViewer::OnBackgroundButton(wxCommandEvent& WXUNUSED(event)) { @@ -393,11 +399,22 @@ void ActorViewer::OnBackgroundButton(wxCommandEvent& WXUNUSED(event)) } } -void ActorViewer::OnWalkingButton(wxCommandEvent& WXUNUSED(event)) +void ActorViewer::OnToggleButton(wxCommandEvent& event) { - wxToolTip::Enable(true); - SetToolTip(L"Hello world!"); - SetHelpText(L"Help text"); - m_Walking = !m_Walking; - POST_MESSAGE(SetViewParamB, (eRenderView::ACTOR, L"walk", m_Walking)); +#define CASE(name, str) \ + case ID_Toggle##name: \ + m_Toggled##name = !m_Toggled##name; \ + POST_MESSAGE(SetViewParamB, (eRenderView::ACTOR, str, m_Toggled##name)); \ + break + + switch (event.GetId()) + { + CASE(Wireframe, L"wireframe"); + CASE(Walking, L"walk"); + CASE(Ground, L"ground"); + CASE(Shadows, L"shadows"); + CASE(Stats, L"stats"); + default: + wxFAIL_MSG(_T("Incorrect ID in OnToggleButton")); + } } diff --git a/source/tools/atlas/AtlasUI/ActorViewer/ActorViewer.h b/source/tools/atlas/AtlasUI/ActorViewer/ActorViewer.h index b86cb8787d..00ce5b05f8 100644 --- a/source/tools/atlas/AtlasUI/ActorViewer/ActorViewer.h +++ b/source/tools/atlas/AtlasUI/ActorViewer/ActorViewer.h @@ -18,9 +18,8 @@ private: void OnAnimationSelection(wxCommandEvent& event); void OnSpeedButton(wxCommandEvent& event); void OnEditButton(wxCommandEvent& event); - void OnWireframeButton(wxCommandEvent& event); + void OnToggleButton(wxCommandEvent& event); void OnBackgroundButton(wxCommandEvent& event); - void OnWalkingButton(wxCommandEvent& event); void OnActorEdited(); ObservableScopedConnections m_ActorConns; @@ -32,9 +31,8 @@ private: Observable > m_ObjectSelection; Observable m_ObjectSettings; - bool m_Wireframe; wxColour m_BackgroundColour; - bool m_Walking; + bool m_ToggledWireframe, m_ToggledWalking, m_ToggledGround, m_ToggledShadows, m_ToggledStats; Observable m_EnvironmentSettings; ObservableScopedConnection m_EnvConn; diff --git a/source/tools/atlas/GameInterface/ActorViewer.cpp b/source/tools/atlas/GameInterface/ActorViewer.cpp index 429a589cec..b35279d2a1 100644 --- a/source/tools/atlas/GameInterface/ActorViewer.cpp +++ b/source/tools/atlas/GameInterface/ActorViewer.cpp @@ -15,7 +15,9 @@ #include "graphics/Unit.h" #include "graphics/UnitManager.h" #include "maths/MathUtil.h" +#include "ps/Font.h" #include "ps/GameSetup/Config.h" +#include "ps/ProfileViewer.h" #include "renderer/Renderer.h" #include "renderer/Scene.h" #include "renderer/SkyManager.h" @@ -27,6 +29,8 @@ struct ActorViewerImpl : public Scene CStrW CurrentUnitAnim; float CurrentSpeed; bool WalkEnabled; + bool GroundEnabled; + bool ShadowsEnabled; SColor4ub Background; @@ -35,7 +39,8 @@ struct ActorViewerImpl : public Scene // Simplistic implementation of the Scene interface void EnumerateObjects(const CFrustum& UNUSED(frustum), SceneCollector* c) { - c->Submit(Terrain.GetPatch(0, 0)); + if (GroundEnabled) + c->Submit(Terrain.GetPatch(0, 0)); if (Unit) c->SubmitRecursive(Unit->GetModel()); @@ -152,9 +157,16 @@ void ActorViewer::SetBackgroundColour(const SColor4ub& colour) m.Terrain.SetBaseColour(colour); } -void ActorViewer::SetWalkEnabled(bool enabled) +void ActorViewer::SetWalkEnabled(bool enabled) { m.WalkEnabled = enabled; } +void ActorViewer::SetGroundEnabled(bool enabled) { m.GroundEnabled = enabled; } +void ActorViewer::SetShadowsEnabled(bool enabled) { m.ShadowsEnabled = enabled; } + +void ActorViewer::SetStatsEnabled(bool enabled) { - m.WalkEnabled = enabled; + if (enabled) + g_ProfileViewer.ShowTable("renderer"); + else + g_ProfileViewer.ShowTable(""); } void ActorViewer::Render() @@ -163,6 +175,10 @@ void ActorViewer::Render() g_Renderer.SetClearColor(*(u32*)&m.Background); + // Disable shadows locally (avoid clobbering global state) + bool oldShadows = g_Renderer.GetOptionBool(CRenderer::OPT_SHADOWS); + g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWS, m.ShadowsEnabled); + g_Renderer.BeginFrame(); // Find the centre of the interesting region, in the middle of the patch @@ -182,8 +198,41 @@ void ActorViewer::Render() g_Renderer.RenderScene(&m); + // .... + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0.f, (float)g_xres, 0.f, (float)g_yres, -1.f, 1000.f); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glPushAttrib(GL_ENABLE_BIT); + glEnable(GL_TEXTURE_2D); + glDisable(GL_CULL_FACE); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + glDisable(GL_ALPHA_TEST); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glEnable(GL_TEXTURE_2D); + + CFont font("console"); + font.Bind(); + + g_ProfileViewer.RenderProfile(); + + glPopAttrib(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + g_Renderer.EndFrame(); + g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWS, oldShadows); + oglCheck(); } @@ -195,7 +244,7 @@ void ActorViewer::Update(float dt) CMatrix3D mat = m.Unit->GetModel()->GetTransform(); - if (m.WalkEnabled) + if (m.WalkEnabled && m.CurrentSpeed) { // Move the model by speed*dt forwards float z = mat.GetTranslation().Z; @@ -219,5 +268,8 @@ bool ActorViewer::HasAnimation() const m.Unit->GetModel()->GetAnimation()->m_AnimDef->GetNumFrames() > 1) return true; + if (m.Unit && m.WalkEnabled && m.CurrentSpeed) + return true; + return false; } diff --git a/source/tools/atlas/GameInterface/ActorViewer.h b/source/tools/atlas/GameInterface/ActorViewer.h index 776cc11fcc..496fdb520d 100644 --- a/source/tools/atlas/GameInterface/ActorViewer.h +++ b/source/tools/atlas/GameInterface/ActorViewer.h @@ -13,8 +13,11 @@ public: void SetActor(const CStrW& id, const CStrW& animation); CUnit* GetUnit(); - void SetWalkEnabled(bool enabled); void SetBackgroundColour(const SColor4ub& colour); + void SetWalkEnabled(bool enabled); + void SetGroundEnabled(bool enabled); + void SetShadowsEnabled(bool enabled); + void SetStatsEnabled(bool enabled); void Render(); void Update(float dt); diff --git a/source/tools/atlas/GameInterface/View.cpp b/source/tools/atlas/GameInterface/View.cpp index 29d9a3de62..773014e934 100644 --- a/source/tools/atlas/GameInterface/View.cpp +++ b/source/tools/atlas/GameInterface/View.cpp @@ -88,13 +88,15 @@ ActorViewer& ViewActor::GetActorViewer() void ViewActor::SetParam(const std::wstring& name, bool value) { if (name == L"wireframe") - { g_Renderer.SetModelRenderMode(value ? WIREFRAME : SOLID); - } else if (name == L"walk") - { m_ActorViewer->SetWalkEnabled(value); - } + else if (name == L"ground") + m_ActorViewer->SetGroundEnabled(value); + else if (name == L"shadows") + m_ActorViewer->SetShadowsEnabled(value); + else if (name == L"stats") + m_ActorViewer->SetStatsEnabled(value); } void ViewActor::SetParam(const std::wstring& name, const AtlasMessage::Colour& value)