Compare commits

...
41 Commits
Author SHA1 Message Date
Automatic Release Builder 9be955262e Terrasync: re-add persistent update cache
Re-add (with some tweaks) the persistent tile-cache code, so that
TerraSync checks the server at most once per 24 hour period, for a
given repository path.

(Disbale the cache by setting /sim/terrasync/enable-persistent-cache=0)
2020-10-01 10:00:22 +01:00
Automatic Release Builder bfa411e9b7 Packages: fix circular refernece bug
Packages had a strong back-pointer to their catalog, creating a circular
reference loop. Break this so catalogs & packages are freed on exit.
2020-09-30 15:16:41 +01:00
Automatic Release Builder dab015742a Catalogs: fix enabling/disabling
Relates to bug Michael found in the launcher:
https://sourceforge.net/p/flightgear/codetickets/2380/
2020-09-30 14:38:35 +01:00
Automatic Release Builder 0ab81d36b9 TerraSync: tweak warnings around checksum failures
Trying to reduce log spam when there is a server-side failure.
2020-09-29 17:43:18 +01:00
Automatic Release Builder 4d905135e8 Set optimisation flags for RelwithDebInfo
Ensure we use-O3 for AppImage (RelWithDebInfo) builds
2020-09-27 11:15:28 +01:00
Automatic Release Builder afad224ca0 Fix level of terrasync start/stop messages 2020-09-25 13:54:36 +01:00
Automatic Release Builder 99c159d46e Harden Repo::computeHashForPath
Check for some error cases in computeHashForPath, since this showed up
in some crash reports.
2020-09-23 22:08:55 +01:00
Automatic Release Builder 733efd08dd Exceptions: optional callback for exception throws.
This is to allow recording an error+stacktrace whenever an exception
is thrown, since that’s the point when the stack is interesting.
2020-09-22 15:26:33 +01:00
James Turner 3753c62783 Add MANDATORY_INFO log priority.
New log-level to avoid using ‘ALERT’ for expected mandatory messages,
since I want to do error collection on real alerts in a future commit.

As part of this, split logstream.hxx into separate header files, and
add a new virtual hook to LogCallback which takes the complete log-entry
2020-09-18 14:49:33 +01:00
James Turner 3e804605b7 ASan: make copied files in log entries safe at init.
Ensure that if we copy file-names of log entries while startup
logging is active, we free them, but only once startup logging is
disabled, or on shutdown. This is needed to avoid crashes when
we use file-name copying for the Qt message handler.
2020-09-18 14:49:33 +01:00
Stuart Buchanan 35b1d321fe Check correctly for failed BUILDING_LIST
Previous additional checks for a value BUILDING_LIST entry
used an enum incorrectly, resulting in all BUILDING_LIST entries
failing.  Now fixed to check bad() / fail() which are bool.
2020-09-09 21:59:24 +01:00
James Turner 6ab7f68f4b Bindings: don’t cache the command pointer
Caching it complicates add/remove command logic, so making a simple
fix for now, which can be back-ported to 2020.2; ideally we would
cache the pointer but have an invalidation scheme, but that’s
considerably more work and risk.

Relates to Sentry crash:
https://sentry.io/organizations/flightgear/issues/1858764364
2020-09-06 15:23:52 +01:00
James Turner b2e149a737 Refactor SGBuildingBin
Trying to trace a crash that was reported, but along the way fix a leak
of BuildingBins. Also avoid allocating some std::strings on the heap.
2020-09-06 15:23:48 +01:00
Erik Hofman e28c4fa5ca Mipmapping requires a power-of-two destination buffer even if the source isn't. Neglecting this causes a massive bufferoverflow of the destination buffer. So for now we do not mipmap non-power-of-two textures and throw a warning. This affects at least the PC-9M and F-16, presumable many others. The effect will be a black texture when zoomed out and the proper texture when zooming in close enough. 2020-09-06 15:23:37 +01:00
Automatic Release Builder 61dc19f635 Fix for SGTexturedTriangleBin leaks
Adapted from next commit
0d5552851b
2020-09-06 15:23:21 +01:00
Automatic Release Builder 60634bc445 Fix include that dropped when squashing. 2020-08-26 14:21:45 +01:00
Hans Kunkell b3e93eaf6e new method: makeStringSafeForPropertyName, replaces invalid chars 2020-08-26 10:37:54 +01:00
James Turner 36dca92c2b HTTPRepo: Fix ownership of HTTPDirectory 2020-08-26 10:37:54 +01:00
James Turner 5a1ed52d7c Particles: rewrite global manager for thread-safety
Avoid re-registering the global node callback each time a new particle
animation is loaded, and use mutexes to protected all shared data.
2020-08-26 10:37:54 +01:00
James Turner fd191b51ce Avoid a race in MatModels loading
Add a mutex to ensure an SGMatModels only loads its models once. Caught
be ASan, hurrah.
2020-08-26 10:37:54 +01:00
James Turner 6167159795 API to reset the shared tree geometry 2020-08-26 10:37:54 +01:00
James Turner 672afdbc34 Fix for sprintf of nil numeric in Nasal
Spotted by valgrind, w00t. If you used a numerical formatter, such
as f,d or g, with. a nil value, we would read uninitialised data, and
generally get confused.
2020-08-26 10:37:54 +01:00
James Turner 27e61b3dec Ensure SGTerraSync::unbind drops all properties
Avoid some noise on reset, by dropping /all/ our SGPropertyNodes
2020-08-26 10:37:54 +01:00
legoboyvdlp R f1d00c9b40 Drizzle is considered to be light rain. Backportable to 2020.2. Fixes ticket 1792 2020-08-26 10:37:54 +01:00
James Turner 1b00ece8c4 SGBuildingBin: avoid read of un-inited memory
Where the building line is not as long as expected, ensure we read
valid memory.
2020-08-26 10:37:54 +01:00
James Turner d0f24229b2 HTTP: Use curl_multi_wait everywhere
Also check the result of the curl methods, in case they fail
2020-08-26 10:37:54 +01:00
Richard Harrison ab8795f6dc Directional lighting fix
getLights modified to use points rather than triangles based on the configuration.

This fixes point lights (with directional disabled) on AMD
2020-08-26 10:37:42 +01:00
James Turner 2fe60c9635 Terraysnc: fix incorrect handling of ‘entirely ocean’ tiles
We had some confusion between two error codes, meaning we would log an
error (and treat as a failure) a pure ocean 1x1 area, even though this
was entirely correct and expected behaviour.

Terrasync.cxx expected REPO_ERROR_NOT_FOUND, we need to convert the
file-level error to that, and avoid logging a warning.
2020-08-25 21:10:54 +01:00
James Turner 27a3ee3bce TerraSync: restart after max-errors is exceeded.
When we trip the max-error count for a session, back off for a period
of time and then retry (selecting a new TerraSync server).
2020-08-25 21:02:39 +01:00
James Turner 0721db3acd TerraSync: handle reinit better
Fix various cases where re-init could get things blocked. Remove the
duplicate storage of the active paths; now we always check the primary
data, and hence it can’t be out of sync.

Also remove the obsolete persistent cache code.

Fixes some of the issues discussed in:
https://sourceforge.net/p/flightgear/codetickets/2308/

Further improvements still to come, especially to retry on a better
schedule for intermittent connections.
2020-08-25 21:02:16 +01:00
Richard Harrison 24b58cbe21 XMLSound bugfixes
- using expressions for pitch and volume didn't work
- multiple volume elements not computed properly

Discussion here: https://web.archive.org/web/20200730232940/http://fguk.me/forum/development-hangar/8360-supersonic-audio?start=40#46287

          <volume>
                <property>/a/v1</property>
                <factor>0.1</factor>
            </volume>
            <volume>
                <property>/a/v2</property>
                <offset>1</offset>
                <factor>-3</factor>
            </volume>
            <volume>
                <property>/a/v3</property>
                <offset>1</offset>
                <factor>-1</factor>
            </volume>

evaluates as follows:

update(): Have 3 volume entries
 --> 0:prop /a[0]/v1[0] => 0.110000 factor = 0.100000 v=0.011000 offset 0.000000, now 0.000000, v=0.011000 vol=0.011000 ==> 0.011000
 --> 1:prop /a[0]/v2[0] => 0.120000 factor = 3.000000 v=0.360000 -ve offset 1.000000 1.360000 = 0.014960  ==> 0.014960
 --> 2:prop /a[0]/v3[0] => 0.130000 factor = 1.000000 v=0.130000 -ve offset 1.000000 1.130000 = 0.016905  ==> 0.016905
2020-08-04 11:58:27 +01:00
Richard Harrison 5b3274e688 Exclude images with less than 16 bits per pixel from the DDS Texture Cache 2020-08-04 11:58:13 +01:00
James Turner fcd75cfae5 Logging: add log overload which copies filename
This is needed to fix a Valgrind error in the test-cases, due to logging
Nasal filenames which are GCed.
2020-08-04 11:58:05 +01:00
James Turner 6387a1d6d0 Fix compilation for some MSVC versions 2020-07-14 13:59:14 +01:00
Julian Smith fc4ce2528b Renamed version -> simgear-version to avoid breaking clang++ on OpenBSD.
It seems that clang++ headers #include <version>, which found simgear/version
because we need to put singear/ in include path for some code to compile.
2020-07-14 13:59:09 +01:00
James Turner 18d2bfcd8b HTTP: allow CAINFO to be set
Env var is SIMGEAR_TLS_CERT_PATH
2020-07-14 13:58:43 +01:00
James Turner 6738a3aa2b Helper to map string to log priority 2020-06-26 10:31:16 +01:00
Julian Smith a8c1bef0bf Avoid various gcc warnings. 2020-06-26 10:30:58 +01:00
James Turner 4faf0ea468 API to reset the resource manager, and remove
providers.

Needed to fix a bug with parsing -set.xml files in the launcher;
correctly loading XML needs the resource paths to be defined.
2020-06-26 10:30:47 +01:00
Lars Toenning 80cc09fe90 Fix compiler warning of different signedness 2020-06-26 10:30:37 +01:00
James Turner ca8dbb985e Fix zero-interval repeat on pick/knob animation
See:
https://sourceforge.net/p/flightgear/codetickets/2241/

Note did not adjust bug in knob-animation ignoring repeatable flag
(always marked repeatable) since this might break some aircraft.
2020-06-15 16:13:39 +01:00
59 changed files with 1570 additions and 1036 deletions
+5 -1
View File
@@ -49,7 +49,7 @@ set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED YES) set(CMAKE_CXX_STANDARD_REQUIRED YES)
# read 'version' file into a variable (stripping any newlines or spaces) # read 'version' file into a variable (stripping any newlines or spaces)
file(READ version versionFile) file(READ simgear-version versionFile)
string(STRIP ${versionFile} SIMGEAR_VERSION) string(STRIP ${versionFile} SIMGEAR_VERSION)
project(SimGear VERSION ${SIMGEAR_VERSION} LANGUAGES C CXX) project(SimGear VERSION ${SIMGEAR_VERSION} LANGUAGES C CXX)
@@ -433,6 +433,10 @@ if(CMAKE_COMPILER_IS_GNUCXX)
check_cxx_source_compiles( check_cxx_source_compiles(
"int main() { unsigned mValue; return __sync_add_and_fetch(&mValue, 1); }" "int main() { unsigned mValue; return __sync_add_and_fetch(&mValue, 1); }"
GCC_ATOMIC_BUILTINS_FOUND) GCC_ATOMIC_BUILTINS_FOUND)
# override CMake default RelWithDebInfo flags.
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g -DNDEBUG")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O3 -g -DNDEBUG")
endif(CMAKE_COMPILER_IS_GNUCXX) endif(CMAKE_COMPILER_IS_GNUCXX)
if (CLANG) if (CLANG)
View File
+1 -1
View File
@@ -850,7 +850,7 @@ namespace canvas
void fillRow(GLubyte* row, GLuint pixel, GLuint width, GLuint pixelBytes) void fillRow(GLubyte* row, GLuint pixel, GLuint width, GLuint pixelBytes)
{ {
GLubyte* dst = row; GLubyte* dst = row;
for (int x = 0; x < width; ++x) { for (GLuint x = 0; x < width; ++x) {
memcpy(dst, &pixel, pixelBytes); memcpy(dst, &pixel, pixelBytes);
dst += pixelBytes; dst += pixelBytes;
} }
+1 -1
View File
@@ -73,7 +73,7 @@ namespace canvas
int stretch, int stretch,
uint8_t alignment ) uint8_t alignment )
{ {
ItemData item_data = {0}; ItemData item_data = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
item_data.layout_item = item; item_data.layout_item = item;
item_data.stretch = std::max(0, stretch); item_data.stretch = std::max(0, stretch);
+1 -1
View File
@@ -26,7 +26,7 @@
#include <vector> #include <vector>
#include <memory> // for std::unique_ptr #include <memory> // for std::unique_ptr
#include <simgear/debug/logstream.hxx> #include <simgear/debug/LogCallback.hxx>
namespace simgear namespace simgear
{ {
+5 -2
View File
@@ -1,7 +1,10 @@
include (SimGearComponent) include (SimGearComponent)
set(HEADERS debug_types.h logstream.hxx BufferedLogCallback.hxx OsgIoCapture.hxx) set(HEADERS debug_types.h
set(SOURCES logstream.cxx BufferedLogCallback.cxx) logstream.hxx BufferedLogCallback.hxx OsgIoCapture.hxx
LogCallback.hxx LogEntry.hxx)
set(SOURCES logstream.cxx BufferedLogCallback.cxx
LogCallback.cxx LogEntry.cxx)
simgear_component(debug debug "${SOURCES}" "${HEADERS}") simgear_component(debug debug "${SOURCES}" "${HEADERS}")
+116
View File
@@ -0,0 +1,116 @@
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <simgear_config.h>
#include "LogCallback.hxx"
using namespace simgear;
LogCallback::LogCallback(sgDebugClass c, sgDebugPriority p) : m_class(c),
m_priority(p)
{
}
void LogCallback::operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& aMessage)
{
// override me
}
bool LogCallback::doProcessEntry(const LogEntry& e)
{
return false;
}
void LogCallback::processEntry(const LogEntry& e)
{
if (doProcessEntry(e))
return; // derived class used the new API
// call the old API
(*this)(e.debugClass, e.debugPriority, e.file, e.line, e.message);
}
bool LogCallback::shouldLog(sgDebugClass c, sgDebugPriority p) const
{
if ((c & m_class) != 0 && p >= m_priority)
return true;
if (c == SG_OSG) // always have OSG logging as it OSG logging is configured separately.
return true;
return false;
}
void LogCallback::setLogLevels(sgDebugClass c, sgDebugPriority p)
{
m_priority = p;
m_class = c;
}
const char* LogCallback::debugPriorityToString(sgDebugPriority p)
{
switch (p) {
case SG_DEV_ALERT:
case SG_ALERT:
return "ALRT";
case SG_BULK: return "BULK";
case SG_DEBUG: return "DBUG";
case SG_MANDATORY_INFO:
case SG_INFO:
return "INFO";
case SG_POPUP: return "POPU";
case SG_DEV_WARN:
case SG_WARN:
return "WARN";
default: return "UNKN";
}
}
const char* LogCallback::debugClassToString(sgDebugClass c)
{
switch (c) {
case SG_NONE: return "none";
case SG_TERRAIN: return "terrain";
case SG_ASTRO: return "astro";
case SG_FLIGHT: return "flight";
case SG_INPUT: return "input";
case SG_GL: return "opengl";
case SG_VIEW: return "view";
case SG_COCKPIT: return "cockpit";
case SG_GENERAL: return "general";
case SG_MATH: return "math";
case SG_EVENT: return "event";
case SG_AIRCRAFT: return "aircraft";
case SG_AUTOPILOT: return "autopilot";
case SG_IO: return "io";
case SG_CLIPPER: return "clipper";
case SG_NETWORK: return "network";
case SG_ATC: return "atc";
case SG_NASAL: return "nasal";
case SG_INSTR: return "instruments";
case SG_SYSTEMS: return "systems";
case SG_AI: return "ai";
case SG_ENVIRONMENT: return "environment";
case SG_SOUND: return "sound";
case SG_NAVAID: return "navaid";
case SG_GUI: return "gui";
case SG_TERRASYNC: return "terrasync";
case SG_PARTICLES: return "particles";
case SG_HEADLESS: return "headless";
case SG_OSG: return "OSG";
default: return "unknown";
}
}
+56
View File
@@ -0,0 +1,56 @@
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#pragma once
#include <string>
#include "LogEntry.hxx"
#include "debug_types.h"
namespace simgear {
class LogCallback
{
public:
virtual ~LogCallback() = default;
// newer API: return true if you handled the message, otherwise
// the old API will be called
virtual bool doProcessEntry(const LogEntry& e);
// old API, kept for compatability
virtual void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& aMessage);
void setLogLevels(sgDebugClass c, sgDebugPriority p);
void processEntry(const LogEntry& e);
protected:
LogCallback(sgDebugClass c, sgDebugPriority p);
bool shouldLog(sgDebugClass c, sgDebugPriority p) const;
static const char* debugClassToString(sgDebugClass c);
static const char* debugPriorityToString(sgDebugPriority p);
private:
sgDebugClass m_class;
sgDebugPriority m_priority;
};
} // namespace simgear
+45
View File
@@ -0,0 +1,45 @@
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <simgear_config.h>
#include "LogEntry.hxx"
#include <cstring> // for strdup
namespace simgear {
LogEntry::~LogEntry()
{
if (freeFilename) {
free(const_cast<char*>(file));
}
}
LogEntry::LogEntry(const LogEntry& c) : debugClass(c.debugClass),
debugPriority(c.debugPriority),
originalPriority(c.originalPriority),
file(c.file),
line(c.line),
message(c.message),
freeFilename(c.freeFilename)
{
if (c.freeFilename) {
file = strdup(c.file);
}
}
} // namespace simgear
+54
View File
@@ -0,0 +1,54 @@
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#pragma once
#include <string>
#include "debug_types.h"
namespace simgear {
/**
* storage of a single log entry. This is used to pass log entries from
* the various threads to the logging thread, and also to store the startup
* entries
*/
class LogEntry
{
public:
LogEntry(sgDebugClass c, sgDebugPriority p,
sgDebugPriority op,
const char* f, int l, const std::string& msg) : debugClass(c), debugPriority(p), originalPriority(op),
file(f), line(l),
message(msg)
{
}
LogEntry(const LogEntry& c);
LogEntry& operator=(const LogEntry& c) = delete;
~LogEntry();
const sgDebugClass debugClass;
const sgDebugPriority debugPriority;
const sgDebugPriority originalPriority;
const char* file;
const int line;
const std::string message;
bool freeFilename = false; ///< if true, we own, and therefore need to free, the memory pointed to by 'file'
};
} // namespace simgear
+1 -2
View File
@@ -2,8 +2,7 @@
#include <osg/Notify> #include <osg/Notify>
using namespace osg; #include <simgear/debug/logstream.hxx>
/** /**
* merge OSG output into our logging system, so it gets recorded to file, * merge OSG output into our logging system, so it gets recorded to file,
+12 -9
View File
@@ -1,3 +1,5 @@
#pragma once
/** \file debug_types.h /** \file debug_types.h
* Define the various logging classes and priorities * Define the various logging classes and priorities
*/ */
@@ -52,16 +54,17 @@ typedef enum {
* appended, or the priority Nasal reports to compiled code will change. * appended, or the priority Nasal reports to compiled code will change.
*/ */
typedef enum { typedef enum {
SG_BULK = 1, // For frequent messages SG_BULK = 1, // For frequent messages
SG_DEBUG, // Less frequent debug type messages SG_DEBUG, // Less frequent debug type messages
SG_INFO, // Informatory messages SG_INFO, // Informatory messages
SG_WARN, // Possible impending problem SG_WARN, // Possible impending problem
SG_ALERT, // Very possible impending problem SG_ALERT, // Very possible impending problem
SG_POPUP, // Severe enough to alert using a pop-up window SG_POPUP, // Severe enough to alert using a pop-up window
// SG_EXIT, // Problem (no core) // SG_EXIT, // Problem (no core)
// SG_ABORT // Abandon ship (core) // SG_ABORT // Abandon ship (core)
SG_DEV_WARN, // Warning for developers, translated to other priority SG_DEV_WARN, // Warning for developers, translated to other priority
SG_DEV_ALERT // Alert for developers, translated SG_DEV_ALERT, // Alert for developers, translated
} sgDebugPriority;
SG_MANDATORY_INFO // information, but should always be shown
} sgDebugPriority;
+67 -131
View File
@@ -35,6 +35,7 @@
#include <simgear/threads/SGThread.hxx> #include <simgear/threads/SGThread.hxx>
#include <simgear/threads/SGQueue.hxx> #include <simgear/threads/SGQueue.hxx>
#include "LogCallback.hxx"
#include <simgear/io/iostreams/sgstream.hxx> #include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/misc/sg_path.hxx> #include <simgear/misc/sg_path.hxx>
#include <simgear/misc/strutils.hxx> #include <simgear/misc/strutils.hxx>
@@ -47,86 +48,9 @@
#include <io.h> #include <io.h>
#endif #endif
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
namespace simgear
{
LogCallback::LogCallback(sgDebugClass c, sgDebugPriority p) :
m_class(c),
m_priority(p)
{
}
bool LogCallback::shouldLog(sgDebugClass c, sgDebugPriority p) const
{
if ((c & m_class) != 0 && p >= m_priority)
return true;
if (c == SG_OSG) // always have OSG logging as it OSG logging is configured separately.
return true;
return false;
}
void LogCallback::setLogLevels( sgDebugClass c, sgDebugPriority p )
{
m_priority = p;
m_class = c;
}
const char* LogCallback::debugPriorityToString(sgDebugPriority p)
{
switch (p) {
case SG_ALERT: return "ALRT";
case SG_BULK: return "BULK";
case SG_DEBUG: return "DBUG";
case SG_INFO: return "INFO";
case SG_POPUP: return "POPU";
case SG_WARN: return "WARN";
default: return "UNKN";
}
}
const char* LogCallback::debugClassToString(sgDebugClass c)
{
switch (c) {
case SG_NONE: return "none";
case SG_TERRAIN: return "terrain";
case SG_ASTRO: return "astro";
case SG_FLIGHT: return "flight";
case SG_INPUT: return "input";
case SG_GL: return "opengl";
case SG_VIEW: return "view";
case SG_COCKPIT: return "cockpit";
case SG_GENERAL: return "general";
case SG_MATH: return "math";
case SG_EVENT: return "event";
case SG_AIRCRAFT: return "aircraft";
case SG_AUTOPILOT: return "autopilot";
case SG_IO: return "io";
case SG_CLIPPER: return "clipper";
case SG_NETWORK: return "network";
case SG_ATC: return "atc";
case SG_NASAL: return "nasal";
case SG_INSTR: return "instruments";
case SG_SYSTEMS: return "systems";
case SG_AI: return "ai";
case SG_ENVIRONMENT:return "environment";
case SG_SOUND: return "sound";
case SG_NAVAID: return "navaid";
case SG_GUI: return "gui";
case SG_TERRASYNC: return "terrasync";
case SG_PARTICLES: return "particles";
case SG_HEADLESS: return "headless";
case SG_OSG: return "OSG";
default: return "unknown";
}
}
} // of namespace simgear
//////////////////////////////////////////////////////////////////////////////
class FileLogCallback : public simgear::LogCallback class FileLogCallback : public simgear::LogCallback
{ {
@@ -139,8 +63,8 @@ public:
logTimer.stamp(); logTimer.stamp();
} }
virtual void operator()(sgDebugClass c, sgDebugPriority p, void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& message) const char* file, int line, const std::string& message) override
{ {
if (!shouldLog(c, p)) return; if (!shouldLog(c, p)) return;
@@ -196,8 +120,8 @@ public:
} }
#endif #endif
virtual void operator()(sgDebugClass c, sgDebugPriority p, void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& aMessage) const char* file, int line, const std::string& aMessage) override
{ {
if (!shouldLog(c, p)) return; if (!shouldLog(c, p)) return;
//fprintf(stderr, "%s\n", aMessage.c_str()); //fprintf(stderr, "%s\n", aMessage.c_str());
@@ -226,8 +150,8 @@ public:
{ {
} }
virtual void operator()(sgDebugClass c, sgDebugPriority p, void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& aMessage) const char* file, int line, const std::string& aMessage) override
{ {
if (!shouldLog(c, p)) return; if (!shouldLog(c, p)) return;
@@ -242,28 +166,6 @@ public:
class logstream::LogStreamPrivate : public SGThread class logstream::LogStreamPrivate : public SGThread
{ {
private: private:
/**
* storage of a single log entry. This is used to pass log entries from
* the various threads to the logging thread, and also to store the startup
* entries
*/
class LogEntry
{
public:
LogEntry(sgDebugClass c, sgDebugPriority p,
const char* f, int l, const std::string& msg) :
debugClass(c), debugPriority(p), file(f), line(l),
message(msg)
{
}
const sgDebugClass debugClass;
const sgDebugPriority debugPriority;
const char* file;
const int line;
const std::string message;
};
/** /**
* RAII object to pause the logging thread if it's running, and restart it. * RAII object to pause the logging thread if it's running, and restart it.
* used to safely make configuration changes. * used to safely make configuration changes.
@@ -401,13 +303,20 @@ public:
~LogStreamPrivate() ~LogStreamPrivate()
{ {
removeCallbacks(); removeCallbacks();
// house-keeping, avoid leak warnings if we exit before disabling
// startup logging
{
std::lock_guard<std::mutex> g(m_lock);
clearStartupEntriesLocked();
}
} }
std::mutex m_lock; std::mutex m_lock;
SGBlockingQueue<LogEntry> m_entries; SGBlockingQueue<simgear::LogEntry> m_entries;
// log entries posted during startup // log entries posted during startup
std::vector<LogEntry> m_startupEntries; std::vector<simgear::LogEntry> m_startupEntries;
bool m_startupLogging = false; bool m_startupLogging = false;
typedef std::vector<simgear::LogCallback*> CallbackVec; typedef std::vector<simgear::LogCallback*> CallbackVec;
@@ -430,6 +339,8 @@ public:
// test suite mode. // test suite mode.
bool m_testMode = false; bool m_testMode = false;
std::vector<std::string> _popupMessages;
void startLog() void startLog()
{ {
std::lock_guard<std::mutex> g(m_lock); std::lock_guard<std::mutex> g(m_lock);
@@ -447,14 +358,19 @@ public:
{ {
std::lock_guard<std::mutex> g(m_lock); std::lock_guard<std::mutex> g(m_lock);
m_startupLogging = on; m_startupLogging = on;
m_startupEntries.clear(); clearStartupEntriesLocked();
} }
} }
virtual void run() void clearStartupEntriesLocked()
{
m_startupEntries.clear();
}
void run() override
{ {
while (1) { while (1) {
LogEntry entry(m_entries.pop()); simgear::LogEntry entry(m_entries.pop());
// special marker entry detected, terminate the thread since we are // special marker entry detected, terminate the thread since we are
// making a configuration change or quitting the app // making a configuration change or quitting the app
if ((entry.debugClass == SG_NONE) && entry.file && !strcmp(entry.file, "done")) { if ((entry.debugClass == SG_NONE) && entry.file && !strcmp(entry.file, "done")) {
@@ -470,8 +386,7 @@ public:
} }
// submit to each installed callback in turn // submit to each installed callback in turn
for (simgear::LogCallback* cb : m_callbacks) { for (simgear::LogCallback* cb : m_callbacks) {
(*cb)(entry.debugClass, entry.debugPriority, cb->processEntry(entry);
entry.file, entry.line, entry.message);
} }
} // of main thread loop } // of main thread loop
} }
@@ -486,7 +401,7 @@ public:
// log a special marker value, which will cause the thread to wakeup, // log a special marker value, which will cause the thread to wakeup,
// and then exit // and then exit
log(SG_NONE, SG_ALERT, "done", -1, ""); log(SG_NONE, SG_ALERT, "done", -1, "", false);
} }
join(); join();
@@ -501,9 +416,8 @@ public:
// we clear startup entries not using this, so always safe to run // we clear startup entries not using this, so always safe to run
// this code, container will simply be empty // this code, container will simply be empty
for (auto entry : m_startupEntries) { for (const auto& entry : m_startupEntries) {
(*cb)(entry.debugClass, entry.debugPriority, cb->processEntry(entry);
entry.file, entry.line, entry.message);
} }
} }
@@ -531,9 +445,9 @@ public:
PauseThread pause(this); PauseThread pause(this);
m_logPriority = p; m_logPriority = p;
m_logClass = c; m_logClass = c;
for (auto cb : m_consoleCallbacks) { for (auto cb : m_consoleCallbacks) {
cb->setLogLevels(c, p); cb->setLogLevels(c, p);
} }
} }
bool would_log( sgDebugClass c, sgDebugPriority p ) const bool would_log( sgDebugClass c, sgDebugPriority p ) const
@@ -551,14 +465,17 @@ public:
} }
void log( sgDebugClass c, sgDebugPriority p, void log( sgDebugClass c, sgDebugPriority p,
const char* fileName, int line, const std::string& msg) const char* fileName, int line, const std::string& msg,
bool freeFilename)
{ {
p = translatePriority(p); auto tp = translatePriority(p);
if (!m_fileLine) { if (!m_fileLine) {
/* This prevents output of file:line in StderrLogCallback. */ /* This prevents output of file:line in StderrLogCallback. */
line = -line; line = -line;
} }
LogEntry entry(c, p, fileName, line, msg);
simgear::LogEntry entry(c, tp, p, fileName, line, msg);
entry.freeFilename = freeFilename;
m_entries.push(entry); m_entries.push(entry);
} }
@@ -589,8 +506,8 @@ logstream::logstream()
logstream::~logstream() logstream::~logstream()
{ {
popup_msgs.clear();
d->stop(); d->stop();
d.reset();
} }
void void
@@ -625,7 +542,14 @@ void
logstream::log( sgDebugClass c, sgDebugPriority p, logstream::log( sgDebugClass c, sgDebugPriority p,
const char* fileName, int line, const std::string& msg) const char* fileName, int line, const std::string& msg)
{ {
d->log(c, p, fileName, line, msg); d->log(c, p, fileName, line, msg, false);
}
void
logstream::logCopyingFilename( sgDebugClass c, sgDebugPriority p,
const char* fileName, int line, const std::string& msg)
{
d->log(c, p, strdup(fileName), line, msg, true);
} }
@@ -686,17 +610,18 @@ void logstream::hexdump(sgDebugClass c, sgDebugPriority p, const char* fileName,
void void
logstream::popup( const std::string& msg) logstream::popup( const std::string& msg)
{ {
popup_msgs.push_back(msg); std::lock_guard<std::mutex> g(d->m_lock);
d->_popupMessages.push_back(msg);
} }
std::string std::string
logstream::get_popup() logstream::get_popup()
{ {
std::string rv = ""; std::string rv;
if (!popup_msgs.empty()) std::lock_guard<std::mutex> g(d->m_lock);
{ if (!d->_popupMessages.empty()) {
rv = popup_msgs.front(); rv = d->_popupMessages.front();
popup_msgs.erase(popup_msgs.begin()); d->_popupMessages.erase(d->_popupMessages.begin());
} }
return rv; return rv;
} }
@@ -704,7 +629,8 @@ logstream::get_popup()
bool bool
logstream::has_popup() logstream::has_popup()
{ {
return (popup_msgs.size() > 0) ? true : false; std::lock_guard<std::mutex> g(d->m_lock);
return !d->_popupMessages.empty();
} }
bool bool
@@ -737,6 +663,16 @@ logstream::set_log_classes( sgDebugClass c)
d->setLogLevels(c, d->m_logPriority); d->setLogLevels(c, d->m_logPriority);
} }
sgDebugPriority logstream::priorityFromString(const std::string& s)
{
if (s == "bulk") return SG_BULK;
if (s == "debug") return SG_DEBUG;
if (s == "info") return SG_INFO;
if (s == "warn") return SG_WARN;
if (s == "alert") return SG_ALERT;
throw std::invalid_argument("Couldn't parse log prioirty:" + s);
}
logstream& logstream&
sglog() sglog()
+14 -22
View File
@@ -38,26 +38,7 @@ class SGPath;
namespace simgear namespace simgear
{ {
class LogCallback class LogCallback;
{
public:
virtual ~LogCallback() {}
virtual void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& aMessage) = 0;
void setLogLevels(sgDebugClass c, sgDebugPriority p);
protected:
LogCallback(sgDebugClass c, sgDebugPriority p);
bool shouldLog(sgDebugClass c, sgDebugPriority p) const;
static const char* debugClassToString(sgDebugClass c);
static const char* debugPriorityToString(sgDebugPriority p);
private:
sgDebugClass m_class;
sgDebugPriority m_priority;
};
/** /**
* Helper force a console on platforms where it might optional, when * Helper force a console on platforms where it might optional, when
* we need to show a console. This basically means Windows at the * we need to show a console. This basically means Windows at the
@@ -105,6 +86,11 @@ public:
sgDebugPriority get_log_priority() const; sgDebugPriority get_log_priority() const;
/**
@brief convert a string value to a log prioirty.
throws std::invalid_argument if the string is not valid
*/
static sgDebugPriority priorityFromString(const std::string& s);
/** /**
* set developer mode on/off. In developer mode, SG_DEV_WARN messags * set developer mode on/off. In developer mode, SG_DEV_WARN messags
* are treated as warnings. In normal (non-developer) mode they are * are treated as warnings. In normal (non-developer) mode they are
@@ -124,6 +110,14 @@ public:
void log( sgDebugClass c, sgDebugPriority p, void log( sgDebugClass c, sgDebugPriority p,
const char* fileName, int line, const std::string& msg); const char* fileName, int line, const std::string& msg);
// overload of above, which can transfer ownership of the file-name.
// this is unecesary overhead when logging from C++, since __FILE__ points
// to constant data, but it's needed when the filename is Nasal data (for
// example) since during shutdown the filename is freed by Nasal GC
// asynchronously with the logging thread.
void logCopyingFilename( sgDebugClass c, sgDebugPriority p,
const char* fileName, int line, const std::string& msg);
/** /**
* output formatted hex dump of memory block * output formatted hex dump of memory block
*/ */
@@ -185,8 +179,6 @@ private:
// constructor // constructor
logstream(); logstream();
std::vector<std::string> popup_msgs;
class LogStreamPrivate; class LogStreamPrivate;
std::unique_ptr<LogStreamPrivate> d; std::unique_ptr<LogStreamPrivate> d;
+2
View File
@@ -952,6 +952,8 @@ bool SGMetar::scanWeather()
weather += string(a->text) + " "; weather += string(a->text) + " ";
if (!strcmp(a->id, "RA")) if (!strcmp(a->id, "RA"))
_rain = w.intensity; _rain = w.intensity;
else if (!strcmp(a->id, "DZ"))
_rain = LIGHT;
else if (!strcmp(a->id, "HA")) else if (!strcmp(a->id, "HA"))
_hail = w.intensity; _hail = w.intensity;
else if (!strcmp(a->id, "SN")) else if (!strcmp(a->id, "SN"))
+31 -24
View File
@@ -105,6 +105,8 @@ public:
unsigned int bytesTransferred; unsigned int bytesTransferred;
unsigned int lastTransferRate; unsigned int lastTransferRate;
uint64_t totalBytesDownloaded; uint64_t totalBytesDownloaded;
SGPath tlsCertificatePath;
}; };
Client::Client() : Client::Client() :
@@ -120,6 +122,8 @@ Client::Client() :
d->maxPipelineDepth = 5; d->maxPipelineDepth = 5;
setUserAgent("SimGear-" SG_STRINGIZE(SIMGEAR_VERSION)); setUserAgent("SimGear-" SG_STRINGIZE(SIMGEAR_VERSION));
d->tlsCertificatePath = SGPath::fromEnv("SIMGEAR_TLS_CERT_PATH");
static bool didInitCurlGlobal = false; static bool didInitCurlGlobal = false;
static std::mutex initMutex; static std::mutex initMutex;
@@ -161,6 +165,14 @@ void Client::setMaxPipelineDepth(unsigned int depth)
#endif #endif
} }
void Client::reset()
{
curl_multi_cleanup(d->curlMulti);
d.reset(new ClientPrivate);
d->tlsCertificatePath = SGPath::fromEnv("SIMGEAR_TLS_CERT_PATH");
d->createCurlMulti();
}
void Client::update(int waitTimeout) void Client::update(int waitTimeout)
{ {
if (d->requests.empty()) { if (d->requests.empty()) {
@@ -171,32 +183,22 @@ void Client::update(int waitTimeout)
} }
int remainingActive, messagesInQueue; int remainingActive, messagesInQueue;
#if defined(SG_MAC) int numFds;
// Mac 10.8 libCurl lacks this, let's keep compat for now CURLMcode mc = curl_multi_wait(d->curlMulti, NULL, 0, waitTimeout, &numFds);
fd_set curlReadFDs, curlWriteFDs, curlErrorFDs; if (mc != CURLM_OK) {
int maxFD; SG_LOG(SG_IO, SG_WARN, "curl_multi_wait failed:" << curl_multi_strerror(mc));
curl_multi_fdset(d->curlMulti, return;
&curlReadFDs,
&curlWriteFDs,
&curlErrorFDs,
&maxFD);
struct timeval timeout;
long t;
curl_multi_timeout(d->curlMulti, &t);
if ((t < 0) || (t > waitTimeout)) {
t = waitTimeout;
} }
timeout.tv_sec = t / 1000; mc = curl_multi_perform(d->curlMulti, &remainingActive);
timeout.tv_usec = (t % 1000) * 1000; if (mc == CURLM_CALL_MULTI_PERFORM) {
::select(maxFD, &curlReadFDs, &curlWriteFDs, &curlErrorFDs, &timeout); // we could loop here, but don't want to get blocked
#else // also this shouldn't ocurr in any modern libCurl
int numFds; curl_multi_perform(d->curlMulti, &remainingActive);
curl_multi_wait(d->curlMulti, NULL, 0, waitTimeout, &numFds); } else if (mc != CURLM_OK) {
#endif SG_LOG(SG_IO, SG_WARN, "curl_multi_perform failed:" << curl_multi_strerror(mc));
curl_multi_perform(d->curlMulti, &remainingActive); return;
}
CURLMsg* msg; CURLMsg* msg;
while ((msg = curl_multi_info_read(d->curlMulti, &messagesInQueue))) { while ((msg = curl_multi_info_read(d->curlMulti, &messagesInQueue))) {
@@ -285,6 +287,11 @@ void Client::makeRequest(const Request_ptr& r)
curl_easy_setopt(curlRequest, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(curlRequest, CURLOPT_FOLLOWLOCATION, 1);
if (!d->tlsCertificatePath.isNull()) {
const auto utf8 = d->tlsCertificatePath.utf8Str();
curl_easy_setopt(curlRequest, CURLOPT_CAINFO, utf8.c_str());
}
if (!d->proxy.empty()) { if (!d->proxy.empty()) {
curl_easy_setopt(curlRequest, CURLOPT_PROXY, d->proxy.c_str()); curl_easy_setopt(curlRequest, CURLOPT_PROXY, d->proxy.c_str());
curl_easy_setopt(curlRequest, CURLOPT_PROXYPORT, d->proxyPort); curl_easy_setopt(curlRequest, CURLOPT_PROXYPORT, d->proxyPort);
+2
View File
@@ -47,6 +47,8 @@ public:
void update(int waitTimeout = 0); void update(int waitTimeout = 0);
void reset();
void makeRequest(const Request_ptr& r); void makeRequest(const Request_ptr& r);
void cancelRequest(const Request_ptr& r, std::string reason = std::string()); void cancelRequest(const Request_ptr& r, std::string reason = std::string());
+34 -26
View File
@@ -49,6 +49,7 @@ namespace simgear
{ {
class HTTPDirectory; class HTTPDirectory;
using HTTPDirectory_ptr = std::unique_ptr<HTTPDirectory>;
class HTTPRepoGetRequest : public HTTP::Request class HTTPRepoGetRequest : public HTTP::Request
{ {
@@ -137,7 +138,7 @@ public:
SGPath basePath; SGPath basePath;
bool isUpdating; bool isUpdating;
HTTPRepository::ResultCode status; HTTPRepository::ResultCode status;
HTTPDirectory* rootDir; HTTPDirectory_ptr rootDir;
size_t totalDownloaded; size_t totalDownloaded;
HTTP::Request_ptr updateFile(HTTPDirectory* dir, const std::string& name, HTTP::Request_ptr updateFile(HTTPDirectory* dir, const std::string& name,
@@ -165,7 +166,7 @@ public:
HTTPDirectory* getOrCreateDirectory(const std::string& path); HTTPDirectory* getOrCreateDirectory(const std::string& path);
bool deleteDirectory(const std::string& relPath, const SGPath& absPath); bool deleteDirectory(const std::string& relPath, const SGPath& absPath);
typedef std::vector<HTTPDirectory*> DirectoryVector; typedef std::vector<HTTPDirectory_ptr> DirectoryVector;
DirectoryVector directories; DirectoryVector directories;
SGPath installedCopyPath; SGPath installedCopyPath;
@@ -416,7 +417,7 @@ public:
SG_LOG(SG_TERRASYNC, SG_WARN, "updated file but not found in dir:" << _relativePath << " " << file); SG_LOG(SG_TERRASYNC, SG_WARN, "updated file but not found in dir:" << _relativePath << " " << file);
} else { } else {
if (it->hash != hash) { if (it->hash != hash) {
SG_LOG(SG_TERRASYNC, SG_INFO, "Checksum error for " << absolutePath() << "/" << file << " " << it->hash << " " << hash); SG_LOG(SG_TERRASYNC, SG_WARN, "Checksum error for " << absolutePath() << "/" << file << " " << it->hash << " " << hash);
// we don't erase the file on a hash mismatch, because if we're syncing during the // we don't erase the file on a hash mismatch, because if we're syncing during the
// middle of a server-side update, the downloaded file may actually become valid. // middle of a server-side update, the downloaded file may actually become valid.
_repository->failedToUpdateChild(_relativePath, HTTPRepository::REPO_ERROR_CHECKSUM); _repository->failedToUpdateChild(_relativePath, HTTPRepository::REPO_ERROR_CHECKSUM);
@@ -592,7 +593,7 @@ private:
void removeChild(SGPath path) void removeChild(SGPath path)
{ {
bool ok; bool ok;
SG_LOG(SG_TERRASYNC, SG_WARN, "Removing:" << path); SG_LOG(SG_TERRASYNC, SG_INFO, "Removing:" << path);
std::string fpath = _relativePath + "/" + path.file(); std::string fpath = _relativePath + "/" + path.file();
if (path.isDir()) { if (path.isDir()) {
@@ -626,7 +627,7 @@ HTTPRepository::HTTPRepository(const SGPath& base, HTTP::Client *cl) :
{ {
_d->http = cl; _d->http = cl;
_d->basePath = base; _d->basePath = base;
_d->rootDir = new HTTPDirectory(_d.get(), ""); _d->rootDir.reset(new HTTPDirectory(_d.get(), ""));
_d->parseHashCache(); _d->parseHashCache();
} }
@@ -663,7 +664,7 @@ void HTTPRepository::update()
_d->status = REPO_NO_ERROR; _d->status = REPO_NO_ERROR;
_d->isUpdating = true; _d->isUpdating = true;
_d->failures.clear(); _d->failures.clear();
_d->updateDir(_d->rootDir, std::string(), 0); _d->updateDir(_d->rootDir.get(), {}, 0);
} }
bool HTTPRepository::isDoingSync() const bool HTTPRepository::isDoingSync() const
@@ -931,10 +932,7 @@ HTTPRepository::failure() const
http->cancelRequest(*rq, "Repository object deleted"); http->cancelRequest(*rq, "Repository object deleted");
} }
DirectoryVector::iterator it; directories.clear(); // wil delete them all
for (it=directories.begin(); it != directories.end(); ++it) {
delete *it;
}
} }
HTTP::Request_ptr HTTPRepoPrivate::updateFile(HTTPDirectory* dir, const std::string& name, size_t sz) HTTP::Request_ptr HTTPRepoPrivate::updateFile(HTTPDirectory* dir, const std::string& name, size_t sz)
@@ -986,16 +984,24 @@ HTTPRepository::failure() const
std::string HTTPRepoPrivate::computeHashForPath(const SGPath& p) std::string HTTPRepoPrivate::computeHashForPath(const SGPath& p)
{ {
if (!p.exists()) if (!p.exists())
return std::string(); return {};
sha1nfo info; sha1nfo info;
sha1_init(&info); sha1_init(&info);
char* buf = static_cast<char*>(malloc(1024 * 1024));
const int bufSize = 1024 * 1024;
char* buf = static_cast<char*>(malloc(bufSize));
if (!buf) {
sg_io_exception("Couldn't allocate SHA1 computation buffer");
}
size_t readLen; size_t readLen;
SGBinaryFile f(p); SGBinaryFile f(p);
if (!f.open(SG_IO_IN)) { if (!f.open(SG_IO_IN)) {
free(buf);
throw sg_io_exception("Couldn't open file for compute hash", p); throw sg_io_exception("Couldn't open file for compute hash", p);
} }
while ((readLen = f.read(buf, 1024 * 1024)) > 0) { while ((readLen = f.read(buf, bufSize)) > 0) {
sha1_write(&info, buf, readLen); sha1_write(&info, buf, readLen);
} }
@@ -1097,7 +1103,7 @@ HTTPRepository::failure() const
{ {
public: public:
DirectoryWithPath(const std::string& p) : path(p) {} DirectoryWithPath(const std::string& p) : path(p) {}
bool operator()(const HTTPDirectory* entry) const bool operator()(const HTTPDirectory_ptr& entry) const
{ return entry->relativePath() == path; } { return entry->relativePath() == path; }
private: private:
std::string path; std::string path;
@@ -1106,14 +1112,13 @@ HTTPRepository::failure() const
HTTPDirectory* HTTPRepoPrivate::getOrCreateDirectory(const std::string& path) HTTPDirectory* HTTPRepoPrivate::getOrCreateDirectory(const std::string& path)
{ {
DirectoryWithPath p(path); DirectoryWithPath p(path);
DirectoryVector::iterator it = std::find_if(directories.begin(), directories.end(), p); auto it = std::find_if(directories.begin(), directories.end(), p);
if (it != directories.end()) { if (it != directories.end()) {
return *it; return it->get();
} }
HTTPDirectory* d = new HTTPDirectory(this, path); directories.emplace_back(new HTTPDirectory(this, path));
directories.push_back(d); return directories.back().get();
return d;
} }
bool HTTPRepoPrivate::deleteDirectory(const std::string& relPath, const SGPath& absPath) bool HTTPRepoPrivate::deleteDirectory(const std::string& relPath, const SGPath& absPath)
@@ -1121,10 +1126,9 @@ HTTPRepository::failure() const
DirectoryWithPath p(relPath); DirectoryWithPath p(relPath);
auto it = std::find_if(directories.begin(), directories.end(), p); auto it = std::find_if(directories.begin(), directories.end(), p);
if (it != directories.end()) { if (it != directories.end()) {
HTTPDirectory* d = *it; HTTPDirectory* d = it->get();
assert(d->absolutePath() == absPath); assert(d->absolutePath() == absPath);
directories.erase(it); directories.erase(it);
delete d;
} else { } else {
// we encounter this code path when deleting an orphaned directory // we encounter this code path when deleting an orphaned directory
} }
@@ -1173,8 +1177,12 @@ HTTPRepository::failure() const
void HTTPRepoPrivate::failedToGetRootIndex(HTTPRepository::ResultCode st) void HTTPRepoPrivate::failedToGetRootIndex(HTTPRepository::ResultCode st)
{ {
SG_LOG(SG_TERRASYNC, SG_WARN, "Failed to get root of repo:" << baseUrl << " " << st); if (st == HTTPRepository::REPO_ERROR_FILE_NOT_FOUND) {
status = st; status = HTTPRepository::REPO_ERROR_NOT_FOUND;
} else {
SG_LOG(SG_TERRASYNC, SG_WARN, "Failed to get root of repo:" << baseUrl << " " << st);
status = st;
}
} }
void HTTPRepoPrivate::failedToUpdateChild(const SGPath& relativePath, void HTTPRepoPrivate::failedToUpdateChild(const SGPath& relativePath,
@@ -1201,15 +1209,15 @@ HTTPRepository::failure() const
} else if (fileStatus == HTTPRepository::REPO_ERROR_CANCELLED) { } else if (fileStatus == HTTPRepository::REPO_ERROR_CANCELLED) {
// if we were cancelled, don't report or log // if we were cancelled, don't report or log
return; return;
} else {
SG_LOG(SG_TERRASYNC, SG_WARN, "failed to update entry:" << relativePath << " status/code: "
<< innerResultCodeAsString(fileStatus) << "/" << fileStatus);
} }
Failure f; Failure f;
f.path = relativePath; f.path = relativePath;
f.error = fileStatus; f.error = fileStatus;
failures.push_back(f); failures.push_back(f);
SG_LOG(SG_TERRASYNC, SG_WARN, "failed to update entry:" << relativePath << " status/code: "
<< innerResultCodeAsString(fileStatus) << "/" << fileStatus);
} }
} // of namespace simgear } // of namespace simgear
+24 -10
View File
@@ -193,13 +193,16 @@ void Request::onDone()
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void Request::onFail() void Request::onFail()
{ {
SG_LOG // log if we FAIELD< but not if we CANCELLED
( if (_ready_state == FAILED) {
SG_IO, SG_LOG
SG_INFO, (
"request failed:" << url() << " : " SG_IO,
<< responseCode() << "/" << responseReason() SG_INFO,
); "request failed:" << url() << " : "
<< responseCode() << "/" << responseReason()
);
}
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -344,12 +347,17 @@ void Request::setSuccess(int code)
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void Request::setFailure(int code, const std::string& reason) void Request::setFailure(int code, const std::string& reason)
{ {
// we use -1 for cancellation, don't be noisy in that case
if (code >= 0) {
SG_LOG(SG_IO, SG_WARN, "HTTP request: set failure:" << code << " reason " << reason); SG_LOG(SG_IO, SG_WARN, "HTTP request: set failure:" << code << " reason " << reason);
}
_responseStatus = code; _responseStatus = code;
_responseReason = reason; _responseReason = reason;
if( !isComplete() ) if( !isComplete() ) {
setReadyState(FAILED); setReadyState(code < 0 ? CANCELLED : FAILED);
}
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -373,6 +381,12 @@ void Request::setReadyState(ReadyState state)
_cb_fail(this); _cb_fail(this);
} }
else if (state == CANCELLED )
{
onFail(); // do this for compatability
onAlways();
_cb_fail(this);
}
else else
return; return;
@@ -402,7 +416,7 @@ bool Request::serverSupportsPipelining() const
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
bool Request::isComplete() const bool Request::isComplete() const
{ {
return _ready_state == DONE || _ready_state == FAILED; return _ready_state == DONE || _ready_state == FAILED || _ready_state == CANCELLED;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
+2 -1
View File
@@ -54,7 +54,8 @@ public:
HEADERS_RECEIVED, HEADERS_RECEIVED,
LOADING, LOADING,
DONE, DONE,
FAILED FAILED,
CANCELLED
}; };
virtual ~Request(); virtual ~Request();
+11
View File
@@ -56,6 +56,15 @@ ResourceManager::~ResourceManager()
std::for_each(_providers.begin(), _providers.end(), std::for_each(_providers.begin(), _providers.end(),
[](ResourceProvider* p) { delete p; }); [](ResourceProvider* p) { delete p; });
} }
void ResourceManager::reset()
{
if (static_manager) {
delete static_manager;
static_manager = nullptr;
}
}
/** /**
* trivial provider using a fixed base path * trivial provider using a fixed base path
*/ */
@@ -107,6 +116,8 @@ void ResourceManager::removeProvider(ResourceProvider* aProvider)
SG_LOG(SG_GENERAL, SG_DEV_ALERT, "unknown provider doing remove"); SG_LOG(SG_GENERAL, SG_DEV_ALERT, "unknown provider doing remove");
return; return;
} }
_providers.erase(it);
} }
SGPath ResourceManager::findPath(const std::string& aResource, SGPath aContext) SGPath ResourceManager::findPath(const std::string& aResource, SGPath aContext)
+2
View File
@@ -47,6 +47,8 @@ public:
static ResourceManager* instance(); static ResourceManager* instance();
static void reset();
/** /**
* add a simple fixed resource location, to resolve against * add a simple fixed resource location, to resolve against
*/ */
+32
View File
@@ -267,6 +267,38 @@ namespace simgear {
return do_strip( s, BOTHSTRIP ); return do_strip( s, BOTHSTRIP );
} }
string makeStringSafeForPropertyName(const std::string& str)
{
// This function replaces all characters in 'str' that are not
// alphanumeric or '-'.
// TODO: make the function multibyte safe.
string res = str;
int index = 0;
for (char& c : res) {
if (!std::isalpha(c) && !std::isdigit(c) && c != '-') {
switch (c) {
case ' ':
case '\t':
case '\n':
case '\r':
case '_':
case '.':
case '/':
case '\\':
res[index] = '-';
break;
default:
res[index] = '_';
SG_LOG(SG_GENERAL, SG_WARN, "makeStringSafeForPropertyName: Modified '" << str << "' to '" << res << "'");
}
}
index++;
}
return res;
}
void void
stripTrailingNewlines_inplace(string& s) stripTrailingNewlines_inplace(string& s)
{ {
+3 -1
View File
@@ -75,7 +75,9 @@ namespace simgear {
std::string rstrip( const std::string& s ); std::string rstrip( const std::string& s );
std::string strip( const std::string& s ); std::string strip( const std::string& s );
/** std::string makeStringSafeForPropertyName(const std::string& str);
/**
* Return a new string with any trailing \\r and \\n characters removed. * Return a new string with any trailing \\r and \\n characters removed.
* Typically useful to clean a CR-terminated line obtained from * Typically useful to clean a CR-terminated line obtained from
* std::getline() which, upon reading CRLF (\\r\\n), discards the Line * std::getline() which, upon reading CRLF (\\r\\n), discards the Line
+6
View File
@@ -737,6 +737,11 @@ void testDecodeHex()
SG_VERIFY(decoded == data1); SG_VERIFY(decoded == data1);
} }
void test_makeStringSafeForPropertyName()
{
SG_CHECK_EQUAL(strutils::makeStringSafeForPropertyName(" ABC/01234\t:\\\"_*$"), "-ABC-01234-_-_-__");
}
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
test_strip(); test_strip();
@@ -761,6 +766,7 @@ int main(int argc, char* argv[])
test_formatGeod(); test_formatGeod();
test_iequals(); test_iequals();
testDecodeHex(); testDecodeHex();
test_makeStringSafeForPropertyName();
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
+1 -1
View File
@@ -36,7 +36,7 @@ void naRuntimeError(naContext c, const char* fmt, ...)
void naRethrowError(naContext subc) void naRethrowError(naContext subc)
{ {
strncpy(subc->callParent->error, subc->error, sizeof(subc->error)); strncpy(subc->callParent->error, subc->error, sizeof(subc->callParent->error));
subc->callParent->dieArg = subc->dieArg; subc->callParent->dieArg = subc->dieArg;
longjmp(subc->callParent->jumpHandle, 1); longjmp(subc->callParent->jumpHandle, 1);
} }
+1
View File
@@ -21,6 +21,7 @@
#include "NasalString.hxx" #include "NasalString.hxx"
#include <cassert> #include <cassert>
#include <stdexcept> // for std::runtime_error
namespace nasal namespace nasal
{ {
+1 -1
View File
@@ -7,7 +7,7 @@
#include "iolib.h" #include "iolib.h"
static void ghostDestroy(void* g); static void ghostDestroy(void* g);
naGhostType naIOGhostType = { ghostDestroy, "iofile" }; naGhostType naIOGhostType = { ghostDestroy, "iofile", NULL, NULL };
static struct naIOGhost* ioghost(naRef r) static struct naIOGhost* ioghost(naRef r)
{ {
+1 -1
View File
@@ -472,7 +472,7 @@ static naRef f_sprintf(naContext c, naRef me, int argc, naRef* args)
} else { } else {
arg = naNumValue(arg); arg = naNumValue(arg);
if(naIsNil(arg)) if(naIsNil(arg))
fout = dosprintf(fstr, "nil"); fout = dosprintf("nil");
else if(t=='d' || t=='i' || t=='c') else if(t=='d' || t=='i' || t=='c')
fout = dosprintf(fstr, (int)naNumValue(arg).num); fout = dosprintf(fstr, (int)naNumValue(arg).num);
else if(t=='o' || t=='u' || t=='x' || t=='X') else if(t=='o' || t=='u' || t=='x' || t=='X')
+2 -2
View File
@@ -9,10 +9,10 @@
#include "code.h" #include "code.h"
static void lockDestroy(void* lock) { naFreeLock(lock); } static void lockDestroy(void* lock) { naFreeLock(lock); }
static naGhostType LockType = { lockDestroy }; static naGhostType LockType = { lockDestroy, NULL, NULL, NULL };
static void semDestroy(void* sem) { naFreeSem(sem); } static void semDestroy(void* sem) { naFreeSem(sem); }
static naGhostType SemType = { semDestroy }; static naGhostType SemType = { semDestroy, NULL, NULL, NULL };
typedef struct { typedef struct {
naContext ctx; naContext ctx;
+10 -6
View File
@@ -595,13 +595,17 @@ void Catalog::setUserEnabled(bool b)
m_userEnabled = b; m_userEnabled = b;
SGPath disableMarkerFile = installRoot() / "_disabled_"; SGPath disableMarkerFile = installRoot() / "_disabled_";
if (m_userEnabled) {
sg_ofstream of(disableMarkerFile); if (m_userEnabled == false) {
of << "1\n"; // touch the file sg_ofstream of(disableMarkerFile, std::ios::trunc | std::ios::out);
of << "1" << std::flush; // touch the file
of.close();
} else { } else {
bool ok = disableMarkerFile.remove(); if (disableMarkerFile.exists()) {
if (!ok) { const bool ok = disableMarkerFile.remove();
SG_LOG(SG_GENERAL, SG_ALERT, "Failed to remove catalog-disable marker file:" << disableMarkerFile); if (!ok) {
SG_LOG(SG_IO, SG_WARN, "Failed to remove catalog-disable marker file:" << disableMarkerFile);
}
} }
} }
+6 -1
View File
@@ -34,7 +34,7 @@ namespace simgear {
namespace pkg { namespace pkg {
Package::Package(const SGPropertyNode* aProps, CatalogRef aCatalog) : Package::Package(const SGPropertyNode* aProps, CatalogRef aCatalog) :
m_catalog(aCatalog) m_catalog(aCatalog.get())
{ {
initWithProps(aProps); initWithProps(aProps);
} }
@@ -235,6 +235,11 @@ std::string Package::id() const
return m_id; return m_id;
} }
CatalogRef Package::catalog() const
{
return {m_catalog};
}
std::string Package::qualifiedId() const std::string Package::qualifiedId() const
{ {
return m_catalog->id() + "." + id(); return m_catalog->id() + "." + id();
+2 -3
View File
@@ -130,8 +130,7 @@ public:
size_t fileSizeBytes() const; size_t fileSizeBytes() const;
CatalogRef catalog() const CatalogRef catalog() const;
{ return m_catalog; }
bool matches(const SGPropertyNode* aFilter) const; bool matches(const SGPropertyNode* aFilter) const;
@@ -236,7 +235,7 @@ private:
SGPropertyNode_ptr m_props; SGPropertyNode_ptr m_props;
std::string m_id; std::string m_id;
string_set m_tags; string_set m_tags;
CatalogRef m_catalog; Catalog* m_catalog = nullptr; // non-owning ref, explicitly
string_list m_variants; string_list m_variants;
mutable function_list<InstallCallback> _install_cb; mutable function_list<InstallCallback> _install_cb;
+8 -5
View File
@@ -690,14 +690,17 @@ void Root::catalogRefreshStatus(CatalogRef aCat, Delegate::StatusCode aReason)
d->refreshing.erase(aCat); d->refreshing.erase(aCat);
} }
if ((aReason == Delegate::STATUS_REFRESHED) && (catIt == d->catalogs.end())) { if (aCat->isUserEnabled() &&
(aReason == Delegate::STATUS_REFRESHED) &&
(catIt == d->catalogs.end()))
{
assert(!aCat->id().empty()); assert(!aCat->id().empty());
d->catalogs.insert(catIt, CatalogDict::value_type(aCat->id(), aCat)); d->catalogs.insert(catIt, CatalogDict::value_type(aCat->id(), aCat));
// catalog might have been previously disabled, let's remove in that case // catalog might have been previously disabled, let's remove in that case
auto j = std::find(d->disabledCatalogs.begin(), auto j = std::find(d->disabledCatalogs.begin(),
d->disabledCatalogs.end(), d->disabledCatalogs.end(),
aCat); aCat);
if (j != d->disabledCatalogs.end()) { if (j != d->disabledCatalogs.end()) {
SG_LOG(SG_GENERAL, SG_INFO, "re-enabling disabled catalog:" << aCat->id()); SG_LOG(SG_GENERAL, SG_INFO, "re-enabling disabled catalog:" << aCat->id());
d->disabledCatalogs.erase(j); d->disabledCatalogs.erase(j);
@@ -705,7 +708,7 @@ void Root::catalogRefreshStatus(CatalogRef aCat, Delegate::StatusCode aReason)
} }
if (!aCat->isEnabled()) { if (!aCat->isEnabled()) {
// catalog has errors, disable it // catalog has errors or was disabled by user, disable it
auto j = std::find(d->disabledCatalogs.begin(), auto j = std::find(d->disabledCatalogs.begin(),
d->disabledCatalogs.end(), d->disabledCatalogs.end(),
aCat); aCat);
+6 -5
View File
@@ -2197,11 +2197,12 @@ public:
_property->addChangeListener(this,initial); _property->addChangeListener(this,initial);
} }
SGPropertyChangeCallback(const SGPropertyChangeCallback<T>& other) : SGPropertyChangeCallback(const SGPropertyChangeCallback<T>& other)
_obj(other._obj), _callback(other._callback), _property(other._property) : SGPropertyChangeListener(other),
{ _obj(other._obj), _callback(other._callback), _property(other._property)
_property->addChangeListener(this,false); {
} _property->addChangeListener(this,false);
}
virtual ~SGPropertyChangeCallback() virtual ~SGPropertyChangeCallback()
{ {
+32 -30
View File
@@ -28,7 +28,7 @@
#include <simgear/compiler.h> #include <simgear/compiler.h>
#include <map> #include <map>
#include <mutex>
#include <osg/AlphaFunc> #include <osg/AlphaFunc>
#include <osg/Group> #include <osg/Group>
@@ -103,37 +103,39 @@ SGMatModel::get_model_count( SGPropertyNode *prop_root )
inline void inline void
SGMatModel::load_models( SGPropertyNode *prop_root ) SGMatModel::load_models( SGPropertyNode *prop_root )
{ {
// Load model only on demand std::lock_guard<std::mutex> g(_loadMutex);
if (!_models_loaded) {
for (unsigned int i = 0; i < _paths.size(); i++) {
osg::Node *entity = SGModelLib::loadModel(_paths[i], prop_root);
if (entity != 0) {
// FIXME: this stuff can be handled
// in the XML wrapper as well (at least,
// the billboarding should be handled
// there).
if (_heading_type == HEADING_BILLBOARD) { // Load model only on demand
// if the model is a billboard, it is likely : if (!_models_loaded) {
// 1. a branch with only leaves, for (unsigned int i = 0; i < _paths.size(); i++) {
// 2. a tree or a non rectangular shape faked by transparency osg::Node* entity = SGModelLib::loadModel(_paths[i], prop_root);
// We add alpha clamp then if (entity != 0) {
osg::StateSet* stateSet = entity->getOrCreateStateSet(); // FIXME: this stuff can be handled
osg::AlphaFunc* alphaFunc = // in the XML wrapper as well (at least,
new osg::AlphaFunc(osg::AlphaFunc::GREATER, 0.01f); // the billboarding should be handled
stateSet->setAttributeAndModes(alphaFunc, // there).
osg::StateAttribute::OVERRIDE);
stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); if (_heading_type == HEADING_BILLBOARD) {
// if the model is a billboard, it is likely :
// 1. a branch with only leaves,
// 2. a tree or a non rectangular shape faked by transparency
// We add alpha clamp then
osg::StateSet* stateSet = entity->getOrCreateStateSet();
osg::AlphaFunc* alphaFunc =
new osg::AlphaFunc(osg::AlphaFunc::GREATER, 0.01f);
stateSet->setAttributeAndModes(alphaFunc,
osg::StateAttribute::OVERRIDE);
stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
}
_models.push_back(entity);
} else {
SG_LOG(SG_INPUT, SG_ALERT, "Failed to load object " << _paths[i]);
// Ensure the vector contains something, otherwise get_random_model below fails
_models.push_back(new osg::Node());
}
} }
_models.push_back(entity);
} else {
SG_LOG(SG_INPUT, SG_ALERT, "Failed to load object " << _paths[i]);
// Ensure the vector contains something, otherwise get_random_model below fails
_models.push_back(new osg::Node());
}
}
} }
_models_loaded = true; _models_loaded = true;
} }
+2
View File
@@ -149,6 +149,8 @@ private:
double _spacing_m; double _spacing_m;
double _range_m; double _range_m;
HeadingType _heading_type; HeadingType _heading_type;
std::mutex _loadMutex;
}; };
+15 -11
View File
@@ -82,13 +82,13 @@ void _writeColor(GLenum pixelFormat, T* data, float scale, osg::Vec4 value)
switch(pixelFormat) switch(pixelFormat)
{ {
case(GL_DEPTH_COMPONENT): //intentionally fall through and execute the code for GL_LUMINANCE case(GL_DEPTH_COMPONENT): //intentionally fall through and execute the code for GL_LUMINANCE
case(GL_LUMINANCE): { *data++ = value.r()*scale; break; } case(GL_LUMINANCE): { *data = value.r()*scale; break; }
case(GL_ALPHA): { *data++ = value.a()*scale; break; } case(GL_ALPHA): { *data = value.a()*scale; break; }
case(GL_LUMINANCE_ALPHA): { *data++ = value.r()*scale; *data++ = value.a()*scale; break; } case(GL_LUMINANCE_ALPHA): { *data++ = value.r()*scale; *data = value.a()*scale; break; }
case(GL_RGB): { *data++ = value.r()*scale; *data++ = value.g()*scale; *data++ = value.b()*scale; break; } case(GL_RGB): { *data++ = value.r()*scale; *data++ = value.g()*scale; *data = value.b()*scale; break; }
case(GL_RGBA): { *data++ = value.r()*scale; *data++ = value.g()*scale; *data++ = value.b()*scale; *data++ = value.a()*scale; break; } case(GL_RGBA): { *data++ = value.r()*scale; *data++ = value.g()*scale; *data++ = value.b()*scale; *data = value.a()*scale; break; }
case(GL_BGR): { *data++ = value.b()*scale; *data++ = value.g()*scale; *data++ = value.r()*scale; break; } case(GL_BGR): { *data++ = value.b()*scale; *data++ = value.g()*scale; *data = value.r()*scale; break; }
case(GL_BGRA): { *data++ = value.b()*scale; *data++ = value.g()*scale; *data++ = value.r()*scale; *data++ = value.a()*scale; break; } case(GL_BGRA): { *data++ = value.b()*scale; *data++ = value.g()*scale; *data++ = value.r()*scale; *data = value.a()*scale; break; }
} }
} }
@@ -265,7 +265,13 @@ osg::Image* computeMipmap( osg::Image* image, MipMapTuple attrs )
{ {
bool computeMipmap = false; bool computeMipmap = false;
unsigned int nbComponents = osg::Image::computeNumComponents( image->getPixelFormat() ); unsigned int nbComponents = osg::Image::computeNumComponents( image->getPixelFormat() );
if ( std::get<0>(attrs) != AUTOMATIC && int s = image->s();
int t = image->t();
if ( (s & (s - 1)) || (t & (t - 1)) ) // power of two test
{
SG_LOG(SG_IO, SG_DEV_ALERT, "mipmapping: texture size not a power-of-two: " + image->getFileName());
}
else if ( std::get<0>(attrs) != AUTOMATIC &&
( std::get<1>(attrs) != AUTOMATIC || nbComponents < 2 ) && ( std::get<1>(attrs) != AUTOMATIC || nbComponents < 2 ) &&
( std::get<2>(attrs) != AUTOMATIC || nbComponents < 3 ) && ( std::get<2>(attrs) != AUTOMATIC || nbComponents < 3 ) &&
( std::get<3>(attrs) != AUTOMATIC || nbComponents < 4 ) ) ( std::get<3>(attrs) != AUTOMATIC || nbComponents < 4 ) )
@@ -283,9 +289,7 @@ osg::Image* computeMipmap( osg::Image* image, MipMapTuple attrs )
if ( computeMipmap ) if ( computeMipmap )
{ {
osg::ref_ptr<osg::Image> mipmaps = new osg::Image(); osg::ref_ptr<osg::Image> mipmaps = new osg::Image();
int s = image->s(), int r = image->r();
t = image->t(),
r = image->r();
int nb = osg::Image::computeNumberOfMipmapLevels(s, t, r); int nb = osg::Image::computeNumberOfMipmapLevels(s, t, r);
osg::Image::MipmapDataType mipmapOffsets; osg::Image::MipmapDataType mipmapOffsets;
unsigned int offset = 0; unsigned int offset = 0;
+7
View File
@@ -359,6 +359,8 @@ ModelRegistry::readImage(const string& fileName,
if (res.validImage()) { if (res.validImage()) {
osg::ref_ptr<osg::Image> srcImage = res.getImage(); osg::ref_ptr<osg::Image> srcImage = res.getImage();
int width = srcImage->s(); int width = srcImage->s();
//int packing = srcImage->getPacking();
//printf("packing %d format %x pixel size %d InternalTextureFormat %x\n", packing, srcImage->getPixelFormat(), srcImage->getPixelSizeInBits(), srcImage->getInternalTextureFormat() );
bool transparent = srcImage->isImageTranslucent(); bool transparent = srcImage->isImageTranslucent();
bool isNormalMap = false; bool isNormalMap = false;
bool isEffect = false; bool isEffect = false;
@@ -367,6 +369,11 @@ ModelRegistry::readImage(const string& fileName,
*/ */
bool can_compress = (transparent && compress_transparent) || (!transparent && compress_solid); bool can_compress = (transparent && compress_transparent) || (!transparent && compress_solid);
if (srcImage->getPixelSizeInBits() <= 16) {
SG_LOG(SG_IO, SG_INFO, "Ignoring " + absFileName + " for inclusion into the texture cache because pixel density too low at " << srcImage->getPixelSizeInBits() << " bits per pixek");
can_compress = false;
}
int height = srcImage->t(); int height = srcImage->t();
// use the new file origin to determine any special processing // use the new file origin to determine any special processing
+22 -8
View File
@@ -136,11 +136,19 @@ osg::Vec2d eventToWindowCoords(const osgGA::GUIEventAdapter& ea)
virtual void update(double dt, int keyModState) virtual void update(double dt, int keyModState)
{ {
if (!_condition || _condition->test()) { SG_UNUSED(keyModState);
SG_UNUSED(keyModState); if (_condition && !_condition->test()) {
if (!_repeatable) return;
return; }
if (!_repeatable)
return;
const bool zeroInterval = (_repeatInterval <= 0.0);
if (zeroInterval) {
// fire once per frame
fireBindingList(_bindingsDown);
} else {
_repeatTime += dt; _repeatTime += dt;
while (_repeatInterval < _repeatTime) { while (_repeatInterval < _repeatTime) {
_repeatTime -= _repeatInterval; _repeatTime -= _repeatInterval;
@@ -679,11 +687,17 @@ public:
return; return;
} }
_repeatTime += dt; const bool zeroInterval = (_repeatInterval <= 0.0);
while (_repeatInterval < _repeatTime) { if (zeroInterval) {
_repeatTime -= _repeatInterval; // fire once per frame
fire(keyModState & osgGA::GUIEventAdapter::MODKEY_SHIFT, _direction); fire(keyModState & osgGA::GUIEventAdapter::MODKEY_SHIFT, _direction);
} // of repeat iteration } else {
_repeatTime += dt;
while (_repeatInterval < _repeatTime) {
_repeatTime -= _repeatInterval;
fire(keyModState & osgGA::GUIEventAdapter::MODKEY_SHIFT, _direction);
} // of repeat iteration
}
} }
bool hover( const osg::Vec2d& windowPos, const Info& ) override bool hover( const osg::Vec2d& windowPos, const Info& ) override
+5 -4
View File
@@ -490,7 +490,8 @@ sgLoad3DModel_internal(const SGPath& path,
} }
} }
if (GlobalParticleCallback::getEnabled()){//dbOptions->getPluginStringData("SimGear::PARTICLESYSTEM") != "OFF") { auto particlesManager = ParticlesGlobalManager::instance();
if (particlesManager->isEnabled()) { //dbOptions->getPluginStringData("SimGear::PARTICLESYSTEM") != "OFF") {
std::vector<SGPropertyNode_ptr> particle_nodes; std::vector<SGPropertyNode_ptr> particle_nodes;
particle_nodes = props->getChildren("particlesystem"); particle_nodes = props->getChildren("particlesystem");
for (unsigned i = 0; i < particle_nodes.size(); ++i) { for (unsigned i = 0; i < particle_nodes.size(); ++i) {
@@ -503,9 +504,9 @@ sgLoad3DModel_internal(const SGPath& path,
options2->setDatabasePath(texturepath.utf8Str()); options2->setDatabasePath(texturepath.utf8Str());
} }
group->addChild(Particles::appendParticles(particle_nodes[i], group->addChild(particlesManager->appendParticles(particle_nodes[i],
prop_root, prop_root,
options2.get())); options2.get()));
} }
} }
+319 -124
View File
@@ -17,9 +17,12 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
// //
#ifdef HAVE_CONFIG_H #include <simgear_config.h>
# include <simgear_config.h>
#endif #include "particles.hxx"
#include <mutex>
#include <simgear/misc/sg_path.hxx> #include <simgear/misc/sg_path.hxx>
#include <simgear/props/props.hxx> #include <simgear/props/props.hxx>
@@ -42,53 +45,122 @@
#include <osg/MatrixTransform> #include <osg/MatrixTransform>
#include <osg/Node> #include <osg/Node>
#include "particles.hxx"
#include <simgear/scene/model/animation.hxx>
namespace simgear namespace simgear
{ {
void GlobalParticleCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
{
enabled = !enabledNode || enabledNode->getBoolValue();
if (!enabled)
return;
SGQuatd q
= SGQuatd::fromLonLatDeg(modelRoot->getFloatValue("/position/longitude-deg",0),
modelRoot->getFloatValue("/position/latitude-deg",0));
osg::Matrix om(toOsg(q));
osg::Vec3 v(0,0,9.81);
gravity = om.preMult(v);
// NOTE: THIS WIND COMPUTATION DOESN'T SEEM TO AFFECT PARTICLES
const osg::Vec3& zUpWind = Particles::getWindVector();
osg::Vec3 w(zUpWind.y(), zUpWind.x(), -zUpWind.z());
wind = om.preMult(w);
// SG_LOG(SG_PARTICLES, SG_ALERT, class ParticlesGlobalManager::ParticlesGlobalManagerPrivate : public osg::NodeCallback
// "wind vector:" << w[0] << "," <<w[1] << "," << w[2]); {
public:
ParticlesGlobalManagerPrivate() : _updater(new osgParticle::ParticleSystemUpdater),
_commonGeode(new osg::Geode)
{
}
void operator()(osg::Node* node, osg::NodeVisitor* nv) override
{
std::lock_guard<std::mutex> g(_lock);
_enabled = !_enabledNode || _enabledNode->getBoolValue();
if (!_enabled)
return;
const auto q = SGQuatd::fromLonLatDeg(_longitudeNode->getFloatValue(), _latitudeNode->getFloatValue());
osg::Matrix om(toOsg(q));
osg::Vec3 v(0, 0, 9.81);
_gravity = om.preMult(v);
// NOTE: THIS WIND COMPUTATION DOESN'T SEEM TO AFFECT PARTICLES
// const osg::Vec3& zUpWind = _wind;
// osg::Vec3 w(zUpWind.y(), zUpWind.x(), -zUpWind.z());
// _localWind = om.preMult(w);
}
// only call this with the lock held!
osg::Group* internalGetCommonRoot()
{
if (!_commonRoot.valid()) {
SG_LOG(SG_PARTICLES, SG_DEBUG, "Particle common root called.");
_commonRoot = new osg::Group;
_commonRoot->setName("common particle system root");
_commonGeode->setName("common particle system geode");
_commonRoot->addChild(_commonGeode);
_commonRoot->addChild(_updater);
_commonRoot->setNodeMask(~simgear::MODELLIGHT_BIT);
}
return _commonRoot.get();
}
std::mutex _lock;
bool _frozen = false;
osg::ref_ptr<osgParticle::ParticleSystemUpdater> _updater;
osg::ref_ptr<osg::Group> _commonRoot;
osg::ref_ptr<osg::Geode> _commonGeode;
osg::Vec3 _wind;
bool _globalCallbackRegistered = false;
bool _enabled = true;
osg::Vec3 _gravity;
// osg::Vec3 _localWind;
SGConstPropertyNode_ptr _enabledNode;
SGConstPropertyNode_ptr _longitudeNode, _latitudeNode;
};
static std::mutex static_managerLock;
static std::unique_ptr<ParticlesGlobalManager> static_instance;
ParticlesGlobalManager* ParticlesGlobalManager::instance()
{
std::lock_guard<std::mutex> g(static_managerLock);
if (!static_instance) {
static_instance.reset(new ParticlesGlobalManager);
}
return static_instance.get();
} }
void ParticlesGlobalManager::clear()
//static members
osg::Vec3 GlobalParticleCallback::gravity;
osg::Vec3 GlobalParticleCallback::wind;
bool GlobalParticleCallback::enabled = true;
SGConstPropertyNode_ptr GlobalParticleCallback::enabledNode = 0;
osg::ref_ptr<osg::Group> Particles::commonRoot;
osg::ref_ptr<osgParticle::ParticleSystemUpdater> Particles::psu = new osgParticle::ParticleSystemUpdater;
osg::ref_ptr<osg::Geode> Particles::commonGeode = new osg::Geode;
osg::Vec3 Particles::_wind;
bool Particles::_frozen = false;
Particles::Particles() :
useGravity(false),
useWind(false)
{ {
std::lock_guard<std::mutex> g(static_managerLock);
static_instance.reset();
}
ParticlesGlobalManager::ParticlesGlobalManager() : d(new ParticlesGlobalManagerPrivate)
{
}
ParticlesGlobalManager::~ParticlesGlobalManager()
{
if (d->_globalCallbackRegistered) {
// is this actually necessary? possibly not
d->_updater->setUpdateCallback(nullptr);
}
}
bool ParticlesGlobalManager::isEnabled() const
{
std::lock_guard<std::mutex> g(d->_lock);
return d->_enabled;
}
bool ParticlesGlobalManager::isFrozen() const
{
std::lock_guard<std::mutex> g(d->_lock);
return d->_frozen;
}
osg::Vec3 ParticlesGlobalManager::getWindVector() const
{
std::lock_guard<std::mutex> g(d->_lock);
return d->_wind;
} }
template <typename Object> template <typename Object>
class PointerGuard{ class PointerGuard{
public: public:
PointerGuard() : _ptr(0) {}
Object* get() { return _ptr; } Object* get() { return _ptr; }
Object* operator () () Object* operator () ()
{ {
@@ -97,24 +169,9 @@ public:
return _ptr; return _ptr;
} }
private: private:
Object* _ptr; Object* _ptr = nullptr;
}; };
osg::Group* Particles::getCommonRoot()
{
if(!commonRoot.valid())
{
SG_LOG(SG_PARTICLES, SG_DEBUG, "Particle common root called.");
commonRoot = new osg::Group;
commonRoot.get()->setName("common particle system root");
commonGeode.get()->setName("common particle system geode");
commonRoot.get()->addChild(commonGeode.get());
commonRoot.get()->addChild(psu.get());
commonRoot->setNodeMask( ~simgear::MODELLIGHT_BIT );
}
return commonRoot.get();
}
void transformParticles(osgParticle::ParticleSystem* particleSys, void transformParticles(osgParticle::ParticleSystem* particleSys,
const osg::Matrix& mat) const osg::Matrix& mat)
{ {
@@ -129,10 +186,182 @@ void transformParticles(osgParticle::ParticleSystem* particleSys,
} }
} }
osg::Group * Particles::appendParticles(const SGPropertyNode* configNode, void Particles::operator()(osg::Node* node, osg::NodeVisitor* nv)
SGPropertyNode* modelRoot, {
const osgDB::Options* auto globalManager = ParticlesGlobalManager::instance();
options)
//SG_LOG(SG_PARTICLES, SG_ALERT, "callback!\n");
particleSys->setFrozen(globalManager->isFrozen());
using namespace osg;
if (shooterValue)
shooter->setInitialSpeedRange(shooterValue->getValue(),
(shooterValue->getValue() + shooterExtraRange));
if (counterValue)
counter->setRateRange(counterValue->getValue(),
counterValue->getValue() + counterExtraRange);
else if (counterCond)
counter->setRateRange(counterStaticValue,
counterStaticValue + counterStaticExtraRange);
if (!globalManager->isEnabled() || (counterCond && !counterCond->test()))
counter->setRateRange(0, 0);
bool colorchange = false;
for (int i = 0; i < 8; ++i) {
if (colorComponents[i]) {
staticColorComponents[i] = colorComponents[i]->getValue();
colorchange = true;
}
}
if (colorchange)
particleSys->getDefaultParticleTemplate().setColorRange(osgParticle::rangev4(Vec4(staticColorComponents[0], staticColorComponents[1], staticColorComponents[2], staticColorComponents[3]), Vec4(staticColorComponents[4], staticColorComponents[5], staticColorComponents[6], staticColorComponents[7])));
if (startSizeValue)
startSize = startSizeValue->getValue();
if (endSizeValue)
endSize = endSizeValue->getValue();
if (startSizeValue || endSizeValue)
particleSys->getDefaultParticleTemplate().setSizeRange(osgParticle::rangef(startSize, endSize));
if (lifeValue)
particleSys->getDefaultParticleTemplate().setLifeTime(lifeValue->getValue());
if (particleFrame.valid()) {
MatrixList mlist = node->getWorldMatrices();
if (!mlist.empty()) {
const Matrix& particleMat = particleFrame->getMatrix();
Vec3d emitOrigin(mlist[0](3, 0), mlist[0](3, 1), mlist[0](3, 2));
Vec3d displace = emitOrigin - Vec3d(particleMat(3, 0), particleMat(3, 1),
particleMat(3, 2));
if (displace * displace > 10000.0 * 10000.0) {
// Make new frame for particle system, coincident with
// the emitter frame, but oriented with local Z.
SGGeod geod = SGGeod::fromCart(toSG(emitOrigin));
Matrix newParticleMat = makeZUpFrame(geod);
Matrix changeParticleFrame = particleMat * Matrix::inverse(newParticleMat);
particleFrame->setMatrix(newParticleMat);
transformParticles(particleSys.get(), changeParticleFrame);
}
}
}
if (program.valid() && useWind)
program->setWind(globalManager->getWindVector());
}
void Particles::setupShooterSpeedData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot)
{
shooterValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if (!shooterValue) {
SG_LOG(SG_GENERAL, SG_DEV_WARN, "Particles: shooter property error!\n");
}
shooterExtraRange = configNode->getFloatValue("extrarange", 0);
}
void Particles::setupCounterData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot)
{
counterValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if (!counterValue) {
SG_LOG(SG_GENERAL, SG_DEV_WARN, "counter property error!\n");
}
counterExtraRange = configNode->getFloatValue("extrarange", 0);
}
void Particles::Particles::setupCounterCondition(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot)
{
counterCond = sgReadCondition(modelRoot, configNode);
}
void Particles::setupCounterCondition(float aCounterStaticValue,
float aCounterStaticExtraRange)
{
counterStaticValue = aCounterStaticValue;
counterStaticExtraRange = aCounterStaticExtraRange;
}
void Particles::setupStartSizeData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot)
{
startSizeValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if (!startSizeValue) {
SG_LOG(SG_GENERAL, SG_DEV_WARN, "Particles: startSizeValue error!\n");
}
}
void Particles::setupEndSizeData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot)
{
endSizeValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if (!endSizeValue) {
SG_LOG(SG_GENERAL, SG_DEV_WARN, "Particles: startSizeValue error!\n");
}
}
void Particles::setupLifeData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot)
{
lifeValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if (!lifeValue) {
SG_LOG(SG_GENERAL, SG_DEV_WARN, "Particles: lifeValue error!\n");
}
}
void Particles::setupColorComponent(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot, int color,
int component)
{
SGSharedPtr<SGExpressiond> colorValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(),
SGLimitsd::max());
if (!colorValue) {
SG_LOG(SG_GENERAL, SG_DEV_WARN, "Particles: color property error!\n");
}
colorComponents[(color * 4) + component] = colorValue;
//number of color components = 4
}
void Particles::setupStaticColorComponent(float r1, float g1, float b1, float a1,
float r2, float g2, float b2, float a2)
{
staticColorComponents[0] = r1;
staticColorComponents[1] = g1;
staticColorComponents[2] = b1;
staticColorComponents[3] = a1;
staticColorComponents[4] = r2;
staticColorComponents[5] = g2;
staticColorComponents[6] = b2;
staticColorComponents[7] = a2;
}
void ParticlesGlobalManager::setWindVector(const osg::Vec3& wind)
{
std::lock_guard<std::mutex> g(d->_lock);
d->_wind = wind;
}
void ParticlesGlobalManager::setWindFrom(const double from_deg, const double speed_kt)
{
double map_rad = -from_deg * SG_DEGREES_TO_RADIANS;
double speed_mps = speed_kt * SG_KT_TO_MPS;
std::lock_guard<std::mutex> g(d->_lock);
d->_wind[0] = cos(map_rad) * speed_mps;
d->_wind[1] = sin(map_rad) * speed_mps;
d->_wind[2] = 0.0;
}
osg::Group* ParticlesGlobalManager::getCommonRoot()
{
std::lock_guard<std::mutex> g(d->_lock);
return d->internalGetCommonRoot();
}
osg::ref_ptr<osg::Group> ParticlesGlobalManager::appendParticles(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, const osgDB::Options* options)
{ {
SG_LOG(SG_PARTICLES, SG_DEBUG, SG_LOG(SG_PARTICLES, SG_DEBUG,
"Setting up a particle system." << std::boolalpha "Setting up a particle system." << std::boolalpha
@@ -152,7 +381,7 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
<< "\n Wind: " << configNode->getChild("program")->getBoolValue("wind", true) << "\n Wind: " << configNode->getChild("program")->getBoolValue("wind", true)
<< std::noboolalpha); << std::noboolalpha);
osgParticle::ParticleSystem *particleSys; osg::ref_ptr<osgParticle::ParticleSystem> particleSys;
//create a generic particle system //create a generic particle system
std::string type = configNode->getStringValue("type", "normal"); std::string type = configNode->getStringValue("type", "normal");
@@ -160,11 +389,10 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
particleSys = new osgParticle::ParticleSystem; particleSys = new osgParticle::ParticleSystem;
else else
particleSys = new osgParticle::ConnectedParticleSystem; particleSys = new osgParticle::ConnectedParticleSystem;
//may not be used depending on the configuration //may not be used depending on the configuration
PointerGuard<Particles> callback; PointerGuard<Particles> callback;
getPSU()->addParticleSystem(particleSys);
getPSU()->setUpdateCallback(new GlobalParticleCallback(modelRoot));
//contains counter, placer and shooter by default //contains counter, placer and shooter by default
osgParticle::ModularEmitter* emitter = new osgParticle::ModularEmitter; osgParticle::ModularEmitter* emitter = new osgParticle::ModularEmitter;
@@ -172,7 +400,7 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
// Set up the alignment node ("stolen" from animation.cxx) // Set up the alignment node ("stolen" from animation.cxx)
// XXX Order of rotations is probably not correct. // XXX Order of rotations is probably not correct.
osg::MatrixTransform *align = new osg::MatrixTransform; osg::ref_ptr<osg::MatrixTransform> align = new osg::MatrixTransform;
osg::Matrix res_matrix; osg::Matrix res_matrix;
res_matrix.makeRotate( res_matrix.makeRotate(
configNode->getFloatValue("offsets/pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS, configNode->getFloatValue("offsets/pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS,
@@ -187,12 +415,8 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
configNode->getFloatValue("offsets/y-m", 0.0), configNode->getFloatValue("offsets/y-m", 0.0),
configNode->getFloatValue("offsets/z-m", 0.0)); configNode->getFloatValue("offsets/z-m", 0.0));
align->setMatrix(res_matrix * tmat); align->setMatrix(res_matrix * tmat);
align->setName("particle align"); align->setName("particle align");
//if (dynamic_cast<CustomModularEmitter*>(emitter)==0) SG_LOG(SG_PARTICLES, SG_ALERT, "observer error\n");
//align->addObserver(dynamic_cast<CustomModularEmitter*>(emitter));
align->addChild(emitter); align->addChild(emitter);
//this name can be used in the XML animation as if it was a submodel //this name can be used in the XML animation as if it was a submodel
@@ -209,7 +433,6 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
osg::Geode* g = new osg::Geode; osg::Geode* g = new osg::Geode;
g->addDrawable(particleSys); g->addDrawable(particleSys);
callback()->particleFrame->addChild(g); callback()->particleFrame->addChild(g);
getCommonRoot()->addChild(callback()->particleFrame.get());
} }
std::string textureFile; std::string textureFile;
if (configNode->hasValue("texture")) { if (configNode->hasValue("texture")) {
@@ -505,66 +728,38 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
emitter->setUpdateCallback(callback.get()); emitter->setUpdateCallback(callback.get());
} }
// touch shared data now (and not before)
{
std::lock_guard<std::mutex> g(d->_lock);
d->_updater->addParticleSystem(particleSys);
if (attach != "local") {
d->internalGetCommonRoot()->addChild(callback()->particleFrame);
}
if (!d->_globalCallbackRegistered) {
SG_LOG(SG_PARTICLES, SG_INFO, "Registering global particles callback");
d->_globalCallbackRegistered = true;
d->_longitudeNode = modelRoot->getNode("/position/longitude-deg", true);
d->_latitudeNode = modelRoot->getNode("/position/latitude-deg", true);
d->_updater->setUpdateCallback(d.get());
}
}
return align; return align;
} }
void Particles::operator()(osg::Node* node, osg::NodeVisitor* nv) void ParticlesGlobalManager::setSwitchNode(const SGPropertyNode* n)
{ {
//SG_LOG(SG_PARTICLES, SG_ALERT, "callback!\n"); std::lock_guard<std::mutex> g(d->_lock);
this->particleSys->setFrozen(_frozen); d->_enabledNode = n;
using namespace osg;
if (shooterValue)
shooter->setInitialSpeedRange(shooterValue->getValue(),
(shooterValue->getValue()
+ shooterExtraRange));
if (counterValue)
counter->setRateRange(counterValue->getValue(),
counterValue->getValue() + counterExtraRange);
else if (counterCond)
counter->setRateRange(counterStaticValue,
counterStaticValue + counterStaticExtraRange);
if (!GlobalParticleCallback::getEnabled() || (counterCond && !counterCond->test()))
counter->setRateRange(0, 0);
bool colorchange=false;
for (int i = 0; i < 8; ++i) {
if (colorComponents[i]) {
staticColorComponents[i] = colorComponents[i]->getValue();
colorchange=true;
}
}
if (colorchange)
particleSys->getDefaultParticleTemplate().setColorRange(osgParticle::rangev4( Vec4(staticColorComponents[0], staticColorComponents[1], staticColorComponents[2], staticColorComponents[3]), Vec4(staticColorComponents[4], staticColorComponents[5], staticColorComponents[6], staticColorComponents[7])));
if (startSizeValue)
startSize = startSizeValue->getValue();
if (endSizeValue)
endSize = endSizeValue->getValue();
if (startSizeValue || endSizeValue)
particleSys->getDefaultParticleTemplate().setSizeRange(osgParticle::rangef(startSize, endSize));
if (lifeValue)
particleSys->getDefaultParticleTemplate().setLifeTime(lifeValue->getValue());
if (particleFrame.valid()) {
MatrixList mlist = node->getWorldMatrices();
if (!mlist.empty()) {
const Matrix& particleMat = particleFrame->getMatrix();
Vec3d emitOrigin(mlist[0](3, 0), mlist[0](3, 1), mlist[0](3, 2));
Vec3d displace
= emitOrigin - Vec3d(particleMat(3, 0), particleMat(3, 1),
particleMat(3, 2));
if (displace * displace > 10000.0 * 10000.0) {
// Make new frame for particle system, coincident with
// the emitter frame, but oriented with local Z.
SGGeod geod = SGGeod::fromCart(toSG(emitOrigin));
Matrix newParticleMat = makeZUpFrame(geod);
Matrix changeParticleFrame
= particleMat * Matrix::inverse(newParticleMat);
particleFrame->setMatrix(newParticleMat);
transformParticles(particleSys.get(), changeParticleFrame);
}
}
}
if (program.valid() && useWind)
program->setWind(_wind);
} }
void ParticlesGlobalManager::setFrozen(bool b)
{
std::lock_guard<std::mutex> g(d->_lock);
d->_frozen = b;
}
} // namespace simgear } // namespace simgear
+57 -184
View File
@@ -1,5 +1,5 @@
// particles.hxx - classes to manage particles // particles.hxx - classes to manage particles
// Copyright (C) 2008 Tiago Gusmão // Copyright (C) 2008 Tiago Gusm�o
// //
// This program is free software; you can redistribute it and/or // This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as // modify it under the terms of the GNU General Public License as
@@ -53,133 +53,37 @@ class ParticleSystemUpdater;
#include <simgear/math/SGMatrix.hxx> #include <simgear/math/SGMatrix.hxx>
// Has anyone done anything *really* stupid, like making min and max macros?
#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif
#include "animation.hxx"
namespace simgear namespace simgear
{ {
class GlobalParticleCallback : public osg::NodeCallback class ParticlesGlobalManager;
{
public:
GlobalParticleCallback(const SGPropertyNode* modelRoot)
{
this->modelRoot=modelRoot;
}
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
static const osg::Vec3 &getGravityVector()
{
return gravity;
}
static const osg::Vec3 &getWindVector()
{
return wind;
}
static void setSwitch(const SGPropertyNode* n)
{
enabledNode = n;
}
static bool getEnabled()
{
return enabled;
}
private:
static osg::Vec3 gravity;
static osg::Vec3 wind;
SGConstPropertyNode_ptr modelRoot;
static SGConstPropertyNode_ptr enabledNode;
static bool enabled;
};
class Particles : public osg::NodeCallback class Particles : public osg::NodeCallback
{ {
public: public:
Particles(); Particles() = default;
static osg::Group * appendParticles(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, const osgDB::Options* options); void operator()(osg::Node* node, osg::NodeVisitor* nv) override;
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
void setupShooterSpeedData(const SGPropertyNode* configNode, void setupShooterSpeedData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot) SGPropertyNode* modelRoot);
{
shooterValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if(!shooterValue){
SG_LOG(SG_GENERAL, SG_ALERT, "shooter property error!\n");
}
shooterExtraRange = configNode->getFloatValue("extrarange",0);
}
void setupCounterData(const SGPropertyNode* configNode, void setupCounterData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot) SGPropertyNode* modelRoot);
{
counterValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if(!counterValue){
SG_LOG(SG_GENERAL, SG_ALERT, "counter property error!\n");
}
counterExtraRange = configNode->getFloatValue("extrarange",0);
}
void setupCounterCondition(const SGPropertyNode* configNode, void setupCounterCondition(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot) SGPropertyNode* modelRoot);
{
counterCond = sgReadCondition(modelRoot, configNode);
}
void setupCounterCondition(float counterStaticValue, void setupCounterCondition(float counterStaticValue,
float counterStaticExtraRange) float counterStaticExtraRange);
{
this->counterStaticValue = counterStaticValue;
this->counterStaticExtraRange = counterStaticExtraRange;
}
void setupStartSizeData(const SGPropertyNode* configNode, void setupStartSizeData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot) SGPropertyNode* modelRoot);
{
startSizeValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if(!startSizeValue)
{
SG_LOG(SG_GENERAL, SG_ALERT, "startSizeValue error!\n");
}
}
void setupEndSizeData(const SGPropertyNode* configNode, void setupEndSizeData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot) SGPropertyNode* modelRoot);
{
endSizeValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if(!endSizeValue){
SG_LOG(SG_GENERAL, SG_ALERT, "startSizeValue error!\n");
}
}
void setupLifeData(const SGPropertyNode* configNode, void setupLifeData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot) SGPropertyNode* modelRoot);
{
lifeValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if(!lifeValue){
SG_LOG(SG_GENERAL, SG_ALERT, "lifeValue error!\n");
}
}
void setupStaticSizeData(float startSize, float endSize) void setupStaticSizeData(float startSize, float endSize)
{ {
@@ -212,60 +116,14 @@ public:
//component: r=0, g=1, ... ; color: 0=start color, 1=end color //component: r=0, g=1, ... ; color: 0=start color, 1=end color
void setupColorComponent(const SGPropertyNode* configNode, void setupColorComponent(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot, int color, SGPropertyNode* modelRoot, int color,
int component) int component);
{
SGExpressiond *colorValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(),
SGLimitsd::max());
if(!colorValue){
SG_LOG(SG_GENERAL, SG_ALERT, "color property error!\n");
}
colorComponents[(color*4)+component] = colorValue;
//number of color components = 4
}
void setupStaticColorComponent(float r1,float g1, float b1, float a1, void setupStaticColorComponent(float r1, float g1, float b1, float a1,
float r2, float g2, float b2, float a2) float r2, float g2, float b2, float a2);
{
staticColorComponents[0] = r1;
staticColorComponents[1] = g1;
staticColorComponents[2] = b1;
staticColorComponents[3] = a1;
staticColorComponents[4] = r2;
staticColorComponents[5] = g2;
staticColorComponents[6] = b2;
staticColorComponents[7] = a2;
}
static osg::Group * getCommonRoot();
static osg::Geode * getCommonGeode()
{
return commonGeode.get();
}
static osgParticle::ParticleSystemUpdater * getPSU()
{
return psu.get();
}
static void setFrozen(bool e) { _frozen = e; }
/**
* Set and get the wind vector for particles in the
* atmosphere. This vector is in the Z-up Y-north frame, and the
* magnitude is the velocity in meters per second.
*/
static void setWindVector(const osg::Vec3& wind) { _wind = wind; }
static void setWindFrom(const double from_deg, const double speed_kt) {
double map_rad = -from_deg * SG_DEGREES_TO_RADIANS;
double speed_mps = speed_kt * SG_KT_TO_MPS;
_wind[0] = cos(map_rad) * speed_mps;
_wind[1] = sin(map_rad) * speed_mps;
_wind[2] = 0.0;
}
static const osg::Vec3& getWindVector() { return _wind; }
protected: protected:
friend class ParticlesGlobalManager;
float shooterExtraRange; float shooterExtraRange;
float counterExtraRange; float counterExtraRange;
SGSharedPtr<SGExpressiond> shooterValue; SGSharedPtr<SGExpressiond> shooterValue;
@@ -286,38 +144,53 @@ protected:
osg::ref_ptr<osgParticle::FluidProgram> program; osg::ref_ptr<osgParticle::FluidProgram> program;
osg::ref_ptr<osg::MatrixTransform> particleFrame; osg::ref_ptr<osg::MatrixTransform> particleFrame;
bool useGravity; bool useGravity = false;
bool useWind; bool useWind = false;
static bool _frozen;
static osg::ref_ptr<osgParticle::ParticleSystemUpdater> psu;
static osg::ref_ptr<osg::Group> commonRoot;
static osg::ref_ptr<osg::Geode> commonGeode;
static osg::Vec3 _wind;
}; };
}
#endif
class ParticlesGlobalManager
/*
class CustomModularEmitter : public osgParticle::ModularEmitter, public osg::Observer
{ {
public: public:
~ParticlesGlobalManager();
virtual void objectDeleted (void *) static ParticlesGlobalManager* instance();
{ static void clear();
SG_LOG(SG_GENERAL, SG_ALERT, "object deleted!\n");
SGParticles::getCommonRoot()->removeChild(this->getParticleSystem()->getParent(0)); bool isEnabled() const;
SGParticles::getPSU()->removeParticleSystem(this->getParticleSystem());
}
osg::ref_ptr<osg::Group> appendParticles(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, const osgDB::Options* options);
// ~CustomModularEmitter() osg::Group* getCommonRoot();
// {
// SG_LOG(SG_GENERAL, SG_ALERT, "object deleted!\n"); osg::Geode* getCommonGeode();
//
// SGParticles::getCommonRoot()->removeChild(this->getParticleSystem()->getParent(0)); osgParticle::ParticleSystemUpdater* getPSU();
// SGParticles::getPSU()->removeParticleSystem(this->getParticleSystem());
// } void setFrozen(bool e);
bool isFrozen() const;
void setSwitchNode(const SGPropertyNode* n);
/**
* Set and get the wind vector for particles in the
* atmosphere. This vector is in the Z-up Y-north frame, and the
* magnitude is the velocity in meters per second.
*/
void setWindVector(const osg::Vec3& wind);
void setWindFrom(const double from_deg, const double speed_kt);
osg::Vec3 getWindVector() const;
private:
ParticlesGlobalManager();
class ParticlesGlobalManagerPrivate;
// because Private inherits NodeCallback, we need to own it
// via an osg::ref_ptr
osg::ref_ptr<ParticlesGlobalManagerPrivate> d;
}; };
*/
} // namespace simgear
#endif
+21 -9
View File
@@ -221,29 +221,41 @@ struct ReaderWriterSTG::_ModelBin {
if (signBuilder.getSignsGroup()) if (signBuilder.getSignsGroup())
group->addChild(signBuilder.getSignsGroup()); group->addChild(signBuilder.getSignsGroup());
if (_buildingList.size() > 0) { if (!_buildingList.empty()) {
SGMaterialLibPtr matlib = _options->getMaterialLib(); SGMaterialLibPtr matlib = _options->getMaterialLib();
bool useVBOs = (_options->getPluginStringData("SimGear::USE_VBOS") == "ON"); bool useVBOs = (_options->getPluginStringData("SimGear::USE_VBOS") == "ON");
if (!matlib) { if (!matlib) {
SG_LOG( SG_TERRAIN, SG_ALERT, "Unable to get materials definition for buildings"); SG_LOG( SG_TERRAIN, SG_ALERT, "Unable to get materials definition for buildings");
} else { } else {
for (std::list<_BuildingList>::iterator i = _buildingList.begin(); i != _buildingList.end(); ++i) { for (const auto& b : _buildingList) {
// Build buildings for each list of buildings // Build buildings for each list of buildings
SGGeod geodPos = SGGeod::fromDegM(i->_lon, i->_lat, 0.0); SGGeod geodPos = SGGeod::fromDegM(b._lon, b._lat, 0.0);
SGMaterial* mat = matlib->find(i->_material_name, geodPos); SGSharedPtr<SGMaterial> mat = matlib->find(b._material_name, geodPos);
SGPath path = SGPath(i->_filename);
// trying to avoid crash on null material, see:
// https://sentry.io/organizations/flightgear/issues/1867075869
if (!mat) {
SG_LOG(SG_TERRAIN, SG_ALERT, "Building specifies unknown material: " << b._material_name);
continue;
}
const auto path = SGPath(b._filename);
SGBuildingBin* buildingBin = new SGBuildingBin(path, mat, useVBOs); SGBuildingBin* buildingBin = new SGBuildingBin(path, mat, useVBOs);
SGBuildingBinList buildingBinList; SGBuildingBinList bbList;
buildingBinList.push_back(buildingBin); bbList.push_back(buildingBin);
osg::MatrixTransform* matrixTransform; osg::MatrixTransform* matrixTransform;
matrixTransform = new osg::MatrixTransform(makeZUpFrame(SGGeod::fromDegM(i->_lon, i->_lat, i->_elev))); matrixTransform = new osg::MatrixTransform(makeZUpFrame(SGGeod::fromDegM(b._lon, b._lat, b._elev)));
matrixTransform->setName("rotateBuildings"); matrixTransform->setName("rotateBuildings");
matrixTransform->setDataVariance(osg::Object::STATIC); matrixTransform->setDataVariance(osg::Object::STATIC);
matrixTransform->addChild(createRandomBuildings(buildingBinList, osg::Matrix::identity(), _options)); matrixTransform->addChild(createRandomBuildings(bbList, osg::Matrix::identity(), _options));
group->addChild(matrixTransform); group->addChild(matrixTransform);
std::for_each(bbList.begin(), bbList.end(), [](SGBuildingBin* bb) {
delete bb;
});
} }
} }
} }
+59 -48
View File
@@ -462,9 +462,17 @@ typedef QuadTreeBuilder<LOD*, SGBuildingBin::BuildingInstance, MakeBuildingLeaf,
// F is the number of floors (integer) // F is the number of floors (integer)
// WT is the texture index to use (integer) for walls. Buildings with the same WT value will have the same wall texture assigned. There are 6 small, 6 medium and 4 large textures. // WT is the texture index to use (integer) for walls. Buildings with the same WT value will have the same wall texture assigned. There are 6 small, 6 medium and 4 large textures.
// RT is the texture index to use (integer) for roofs. Buildings with the same RT value will have the same roof texture assigned. There are 6 small, 6 medium and 4 large textures. // RT is the texture index to use (integer) for roofs. Buildings with the same RT value will have the same roof texture assigned. There are 6 small, 6 medium and 4 large textures.
float x, y, z, r, w, d, h, p; float x = 0.0f, y = 0.0f, z = 0.0f, r = 0.0f, w = 0.0f, d = 0.0f, h = 0.0f, p = 0.0f;
int b, s, o, f, wt, rt; int b = 0, s = 0, o = 0, f = 0, wt = 0, rt = 0;
in >> x >> y >> z >> r >> b >> w >> d >> h >> p >> s >> o >> f >> wt >> rt; in >> x >> y >> z >> r >> b;
if (in.bad() || in.fail()) {
SG_LOG(SG_TERRAIN, SG_WARN, "Error parsing build entry in: " << absoluteFileName << " line: \"" << line << "\"");
continue;
}
// these might fail, so check them after we look at failbit
in >> w >> d >> h >> p >> s >> o >> f >> wt >> rt;
//SG_LOG(SG_TERRAIN, SG_ALERT, "Building entry " << x << " " << y << " " << z << " " << b ); //SG_LOG(SG_TERRAIN, SG_ALERT, "Building entry " << x << " " << y << " " << z << " " << b );
SGVec3f loc = SGVec3f(x,y,z); SGVec3f loc = SGVec3f(x,y,z);
@@ -489,14 +497,20 @@ typedef QuadTreeBuilder<LOD*, SGBuildingBin::BuildingInstance, MakeBuildingLeaf,
}; };
// Set up the building set based on the material definitions // Set up the building set based on the material definitions
SGBuildingBin::SGBuildingBin(const SGMaterial *mat, bool useVBOs) { SGBuildingBin::SGBuildingBin(const SGMaterial* mat, bool useVBOs) : _material(const_cast<SGMaterial*>(mat))
material_name = new std::string(mat->get_names()[0]); {
SG_LOG(SG_TERRAIN, SG_DEBUG, "Building material " << material_name); const auto& materialNames = mat->get_names();
material = mat; if (materialNames.empty()) {
texture = new std::string(mat->get_building_texture()); SG_LOG(SG_TERRAIN, SG_DEV_ALERT, "SGBuildingBin: material has zero names defined");
lightMap = new std::string(mat->get_building_lightmap()); } else {
buildingRange = mat->get_building_range(); _materialName = materialNames.front();
SG_LOG(SG_TERRAIN, SG_DEBUG, "Building texture " << texture); SG_LOG(SG_TERRAIN, SG_DEBUG, "Building material " << _materialName);
}
_textureName = mat->get_building_texture();
_lightMapName = mat->get_building_lightmap();
buildingRange = mat->get_building_range();
SG_LOG(SG_TERRAIN, SG_DEBUG, "Building texture " << _textureName);
} }
SGBuildingBin::~SGBuildingBin() { SGBuildingBin::~SGBuildingBin() {
@@ -644,15 +658,15 @@ typedef QuadTreeBuilder<LOD*, SGBuildingBin::BuildingInstance, MakeBuildingLeaf,
if (buildingtype == SGBuildingBin::SMALL) { if (buildingtype == SGBuildingBin::SMALL) {
// Small building // Small building
// Maximum number of floors is 3, and maximum width/depth is 192m. // Maximum number of floors is 3, and maximum width/depth is 192m.
width = material->get_building_small_min_width() + mt_rand(&seed) * mt_rand(&seed) * (material->get_building_small_max_width() - material->get_building_small_min_width()); width = _material->get_building_small_min_width() + mt_rand(&seed) * mt_rand(&seed) * (_material->get_building_small_max_width() - _material->get_building_small_min_width());
depth = material->get_building_small_min_depth() + mt_rand(&seed) * mt_rand(&seed) * (material->get_building_small_max_depth() - material->get_building_small_min_depth()); depth = _material->get_building_small_min_depth() + mt_rand(&seed) * mt_rand(&seed) * (_material->get_building_small_max_depth() - _material->get_building_small_min_depth());
floors = SGMisc<double>::round(material->get_building_small_min_floors() + mt_rand(&seed) * (material->get_building_small_max_floors() - material->get_building_small_min_floors())); floors = SGMisc<double>::round(_material->get_building_small_min_floors() + mt_rand(&seed) * (_material->get_building_small_max_floors() - _material->get_building_small_min_floors()));
height = floors * (2.8 + mt_rand(&seed)); height = floors * (2.8 + mt_rand(&seed));
// Small buildings are never deeper than they are wide. // Small buildings are never deeper than they are wide.
if (depth > width) { depth = width; } if (depth > width) { depth = width; }
pitch_height = (mt_rand(&seed) < material->get_building_small_pitch()) ? 3.0 : 0.0; pitch_height = (mt_rand(&seed) < _material->get_building_small_pitch()) ? 3.0 : 0.0;
if (pitch_height == 0.0) { if (pitch_height == 0.0) {
roof_shape = 0; roof_shape = 0;
@@ -663,18 +677,18 @@ typedef QuadTreeBuilder<LOD*, SGBuildingBin::BuildingInstance, MakeBuildingLeaf,
} }
} else if (buildingtype == SGBuildingBin::MEDIUM) { } else if (buildingtype == SGBuildingBin::MEDIUM) {
// MEDIUM BUILDING // MEDIUM BUILDING
width = material->get_building_medium_min_width() + mt_rand(&seed) * mt_rand(&seed) * (material->get_building_medium_max_width() - material->get_building_medium_min_width()); width = _material->get_building_medium_min_width() + mt_rand(&seed) * mt_rand(&seed) * (_material->get_building_medium_max_width() - _material->get_building_medium_min_width());
depth = material->get_building_medium_min_depth() + mt_rand(&seed) * mt_rand(&seed) * (material->get_building_medium_max_depth() - material->get_building_medium_min_depth()); depth = _material->get_building_medium_min_depth() + mt_rand(&seed) * mt_rand(&seed) * (_material->get_building_medium_max_depth() - _material->get_building_medium_min_depth());
floors = SGMisc<double>::round(material->get_building_medium_min_floors() + mt_rand(&seed) * (material->get_building_medium_max_floors() - material->get_building_medium_min_floors())); floors = SGMisc<double>::round(_material->get_building_medium_min_floors() + mt_rand(&seed) * (_material->get_building_medium_max_floors() - _material->get_building_medium_min_floors()));
height = floors * (2.8 + mt_rand(&seed)); height = floors * (2.8 + mt_rand(&seed));
while ((height > width) && (floors > material->get_building_medium_min_floors())) { while ((height > width) && (floors > _material->get_building_medium_min_floors())) {
// Ensure that medium buildings aren't taller than they are wide // Ensure that medium buildings aren't taller than they are wide
floors--; floors--;
height = floors * (2.8 + mt_rand(&seed)); height = floors * (2.8 + mt_rand(&seed));
} }
pitch_height = (mt_rand(&seed) < material->get_building_medium_pitch()) ? 3.0 : 0.0; pitch_height = (mt_rand(&seed) < _material->get_building_medium_pitch()) ? 3.0 : 0.0;
if (pitch_height == 0.0) { if (pitch_height == 0.0) {
roof_shape = 0; roof_shape = 0;
@@ -685,11 +699,11 @@ typedef QuadTreeBuilder<LOD*, SGBuildingBin::BuildingInstance, MakeBuildingLeaf,
} }
} else { } else {
// LARGE BUILDING // LARGE BUILDING
width = material->get_building_large_min_width() + mt_rand(&seed) * (material->get_building_large_max_width() - material->get_building_large_min_width()); width = _material->get_building_large_min_width() + mt_rand(&seed) * (_material->get_building_large_max_width() - _material->get_building_large_min_width());
depth = material->get_building_large_min_depth() + mt_rand(&seed) * (material->get_building_large_max_depth() - material->get_building_large_min_depth()); depth = _material->get_building_large_min_depth() + mt_rand(&seed) * (_material->get_building_large_max_depth() - _material->get_building_large_min_depth());
floors = SGMisc<double>::round(material->get_building_large_min_floors() + mt_rand(&seed) * (material->get_building_large_max_floors() - material->get_building_large_min_floors())); floors = SGMisc<double>::round(_material->get_building_large_min_floors() + mt_rand(&seed) * (_material->get_building_large_max_floors() - _material->get_building_large_min_floors()));
height = floors * (2.8 + mt_rand(&seed)); height = floors * (2.8 + mt_rand(&seed));
pitch_height = (mt_rand(&seed) < material->get_building_large_pitch()) ? 3.0 : 0.0; pitch_height = (mt_rand(&seed) < _material->get_building_large_pitch()) ? 3.0 : 0.0;
if (pitch_height == 0.0) { if (pitch_height == 0.0) {
roof_shape = 0; roof_shape = 0;
@@ -718,36 +732,35 @@ typedef QuadTreeBuilder<LOD*, SGBuildingBin::BuildingInstance, MakeBuildingLeaf,
} }
SGBuildingBin::BuildingType SGBuildingBin::getBuildingType(float roll) { SGBuildingBin::BuildingType SGBuildingBin::getBuildingType(float roll) {
if (roll < _material->get_building_small_fraction()) {
return SGBuildingBin::SMALL;
}
if (roll < material->get_building_small_fraction()) { if (roll < (_material->get_building_small_fraction() + _material->get_building_medium_fraction())) {
return SGBuildingBin::SMALL; return SGBuildingBin::MEDIUM;
} }
if (roll < (material->get_building_small_fraction() + material->get_building_medium_fraction())) {
return SGBuildingBin::MEDIUM;
}
return SGBuildingBin::LARGE; return SGBuildingBin::LARGE;
} }
float SGBuildingBin::getBuildingMaxRadius(BuildingType type) { float SGBuildingBin::getBuildingMaxRadius(BuildingType type) {
if (type == SGBuildingBin::SMALL) return material->get_building_small_max_width(); if (type == SGBuildingBin::SMALL) return _material->get_building_small_max_width();
if (type == SGBuildingBin::MEDIUM) return material->get_building_medium_max_width(); if (type == SGBuildingBin::MEDIUM) return _material->get_building_medium_max_width();
if (type == SGBuildingBin::LARGE) return material->get_building_large_max_width(); if (type == SGBuildingBin::LARGE) return _material->get_building_large_max_width();
return 0; return 0;
} }
float SGBuildingBin::getBuildingMaxDepth(BuildingType type) { float SGBuildingBin::getBuildingMaxDepth(BuildingType type) {
if (type == SGBuildingBin::SMALL) return material->get_building_small_max_depth(); if (type == SGBuildingBin::SMALL) return _material->get_building_small_max_depth();
if (type == SGBuildingBin::MEDIUM) return material->get_building_medium_max_depth(); if (type == SGBuildingBin::MEDIUM) return _material->get_building_medium_max_depth();
if (type == SGBuildingBin::LARGE) return material->get_building_large_max_depth(); if (type == SGBuildingBin::LARGE) return _material->get_building_large_max_depth();
return 0; return 0;
} }
ref_ptr<Group> SGBuildingBin::createBuildingsGroup(Matrix transInv, const SGReaderWriterOptions* options) ref_ptr<Group> SGBuildingBin::createBuildingsGroup(Matrix transInv, const SGReaderWriterOptions* options)
{ {
ref_ptr<Effect> effect; ref_ptr<Effect> effect;
EffectMap::iterator iter = buildingEffectMap.find(*texture); auto iter = buildingEffectMap.find(_textureName);
if ((iter == buildingEffectMap.end())|| if ((iter == buildingEffectMap.end())||
(!iter->second.lock(effect))) (!iter->second.lock(effect)))
@@ -756,16 +769,14 @@ typedef QuadTreeBuilder<LOD*, SGBuildingBin::BuildingInstance, MakeBuildingLeaf,
makeChild(effectProp, "inherits-from")->setStringValue("Effects/building"); makeChild(effectProp, "inherits-from")->setStringValue("Effects/building");
SGPropertyNode* params = makeChild(effectProp, "parameters"); SGPropertyNode* params = makeChild(effectProp, "parameters");
// Main texture - n=0 // Main texture - n=0
params->getChild("texture", 0, true)->getChild("image", 0, true) params->getChild("texture", 0, true)->getChild("image", 0, true)->setStringValue(_textureName);
->setStringValue(*texture);
// Light map - n=3 // Light map - n=3
params->getChild("texture", 3, true)->getChild("image", 0, true) params->getChild("texture", 3, true)->getChild("image", 0, true)->setStringValue(_lightMapName);
->setStringValue(*lightMap);
effect = makeEffect(effectProp, true, options); effect = makeEffect(effectProp, true, options);
if (iter == buildingEffectMap.end()) if (iter == buildingEffectMap.end())
buildingEffectMap.insert(EffectMap::value_type(*texture, effect)); buildingEffectMap.insert(EffectMap::value_type(_textureName, effect));
else else
iter->second = effect; // update existing, but empty observer iter->second = effect; // update existing, but empty observer
} }
+10 -11
View File
@@ -145,20 +145,19 @@ public:
}; };
private: private:
const SGSharedPtr<SGMaterial> _material;
const SGMaterial *material; std::string _materialName;
std::string _textureName;
std::string _lightMapName;
std::string* material_name; // Visibility range for buildings
std::string* texture; float buildingRange;
std::string* lightMap;
// Visibility range for buildings
float buildingRange;
// Information for an instance of a building - position and orientation // Information for an instance of a building - position and orientation
typedef std::vector<BuildingInstance> BuildingInstanceList; typedef std::vector<BuildingInstance> BuildingInstanceList;
BuildingInstanceList buildingLocations; BuildingInstanceList buildingLocations;
public: public:
@@ -187,7 +186,7 @@ public:
bool checkMinDist (SGVec3f p, float radius); bool checkMinDist (SGVec3f p, float radius);
std::string* getMaterialName() { return material_name; } const std::string& getMaterialName() const { return _materialName; }
BuildingType getBuildingType(float roll); BuildingType getBuildingType(float roll);
float getBuildingMaxRadius(BuildingType); float getBuildingMaxRadius(BuildingType);
+5 -5
View File
@@ -428,12 +428,12 @@ public:
return 0; return 0;
// FIXME: do not include all values here ... // FIXME: do not include all values here ...
osg::Vec3Array* vertices = new osg::Vec3Array; osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
osg::Vec3Array* normals = new osg::Vec3Array; osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array;
osg::Vec2Array* priTexCoords = new osg::Vec2Array; osg::ref_ptr<osg::Vec2Array> priTexCoords = new osg::Vec2Array;
osg::Vec2Array* secTexCoords = new osg::Vec2Array; osg::ref_ptr<osg::Vec2Array> secTexCoords = new osg::Vec2Array;
osg::Vec4Array* colors = new osg::Vec4Array; osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array;
colors->push_back(osg::Vec4(1, 1, 1, 1)); colors->push_back(osg::Vec4(1, 1, 1, 1));
osg::Geometry* geometry = new osg::Geometry; osg::Geometry* geometry = new osg::Geometry;
+17 -5
View File
@@ -151,14 +151,26 @@ Geometry* makeSharedTreeGeometry(int numQuads)
return result; return result;
} }
ref_ptr<Geometry> sharedTreeGeometry; static std::mutex static_sharedGeometryMutex;
static ref_ptr<Geometry> sharedTreeGeometry;
void clearSharedTreeGeometry()
{
std::lock_guard<std::mutex> g(static_sharedGeometryMutex);
sharedTreeGeometry = {};
}
Geometry* createTreeGeometry(float width, float height, int varieties) Geometry* createTreeGeometry(float width, float height, int varieties)
{ {
if (!sharedTreeGeometry) Geometry* quadGeom = nullptr;
sharedTreeGeometry = makeSharedTreeGeometry(1600); {
Geometry* quadGeom = simgear::clone(sharedTreeGeometry.get(), std::lock_guard<std::mutex> g(static_sharedGeometryMutex);
CopyOp::SHALLOW_COPY); if (!sharedTreeGeometry)
sharedTreeGeometry = makeSharedTreeGeometry(1600);
quadGeom = simgear::clone(sharedTreeGeometry.get(),
CopyOp::SHALLOW_COPY);
}
Vec3Array* params = new Vec3Array; Vec3Array* params = new Vec3Array;
params->push_back(Vec3(width, height, (float)varieties)); params->push_back(Vec3(width, height, (float)varieties));
quadGeom->setNormalArray(params); quadGeom->setNormalArray(params);
+19 -10
View File
@@ -35,11 +35,14 @@ namespace simgear
{ {
class TreeBin { class TreeBin {
public: public:
struct Tree { ~TreeBin() = default;
SGVec3f position;
SGVec3f tnormal; struct Tree {
Tree(const SGVec3f& p, const SGVec3f& t) : position(p),tnormal(t) SGVec3f position;
{ } SGVec3f tnormal;
Tree(const SGVec3f& p, const SGVec3f& t) : position(p), tnormal(t)
{
}
}; };
typedef std::vector<Tree> TreeList; typedef std::vector<Tree> TreeList;
@@ -55,19 +58,25 @@ public:
{ _trees.push_back(t); } { _trees.push_back(t); }
void insert(const SGVec3f& p, const SGVec3f& tnorm) void insert(const SGVec3f& p, const SGVec3f& tnorm)
{insert(Tree(p,tnorm));} {
_trees.emplace_back(p, tnorm);
}
unsigned getNumTrees() const unsigned getNumTrees() const
{ return _trees.size(); } { return _trees.size(); }
const Tree& getTree(unsigned i) const const Tree& getTree(unsigned i) const
{ return _trees[i]; } {
assert(i < _trees.size());
return _trees.at(i);
}
TreeList _trees; TreeList _trees;
~TreeBin() {
_trees.clear();
}
}; };
void clearSharedTreeGeometry();
typedef std::list<TreeBin*> SGTreeBinList; typedef std::list<TreeBin*> SGTreeBinList;
osg::Group* createForest(SGTreeBinList& forestList, const osg::Matrix& transform, osg::Group* createForest(SGTreeBinList& forestList, const osg::Matrix& transform,
+11 -2
View File
@@ -280,9 +280,18 @@ SGLightFactory::getLights(const SGDirectionalLightBin& lights)
//stateSet->setRenderBinDetails(POINT_LIGHTS_BIN, "DepthSortedBin"); //stateSet->setRenderBinDetails(POINT_LIGHTS_BIN, "DepthSortedBin");
//stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF); //stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
static SGSceneFeatures* sceneFeatures = SGSceneFeatures::instance();
bool useTriangles = sceneFeatures->getEnableTriangleDirectionalLights();
osg::DrawArrays* drawArrays; osg::DrawArrays* drawArrays;
drawArrays = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, if (useTriangles)
0, vertices->size()); drawArrays = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES,
0, vertices->size());
else
drawArrays = new osg::DrawArrays(osg::PrimitiveSet::POINTS,
0, vertices->size());
geometry->addPrimitiveSet(drawArrays); geometry->addPrimitiveSet(drawArrays);
return geometry; return geometry;
} }
+224 -145
View File
@@ -46,6 +46,7 @@
#include <fstream> #include <fstream>
#include <string> #include <string>
#include <map> #include <map>
#include <algorithm>
#include <simgear/version.h> #include <simgear/version.h>
@@ -155,20 +156,16 @@ public:
class SyncSlot class SyncSlot
{ {
public: public:
SyncSlot() : SyncSlot() = default;
isNewDirectory(false),
busy(false),
pendingKBytes(0)
{}
SyncItem currentItem; SyncItem currentItem;
bool isNewDirectory; bool isNewDirectory = false;
std::queue<SyncItem> queue; std::deque<SyncItem> queue;
std::unique_ptr<HTTPRepository> repository; std::unique_ptr<HTTPRepository> repository;
SGTimeStamp stamp; SGTimeStamp stamp;
bool busy; ///< is the slot working or idle bool busy = false; ///< is the slot working or idle
unsigned int pendingKBytes; unsigned int pendingKBytes = 0;
unsigned int nextWarnTimeout; unsigned int nextWarnTimeout = 0;
}; };
static const int SYNC_SLOT_TILES = 0; ///< Terrain and Objects sync static const int SYNC_SLOT_TILES = 0; ///< Terrain and Objects sync
@@ -205,7 +202,6 @@ struct TerrasyncThreadState
_updated_tile_count(0), _updated_tile_count(0),
_success_count(0), _success_count(0),
_consecutive_errors(0), _consecutive_errors(0),
_allowed_errors(6),
_cache_hits(0), _cache_hits(0),
_transfer_rate(0), _transfer_rate(0),
_total_kb_downloaded(0), _total_kb_downloaded(0),
@@ -219,7 +215,6 @@ struct TerrasyncThreadState
int _updated_tile_count; int _updated_tile_count;
int _success_count; int _success_count;
int _consecutive_errors; int _consecutive_errors;
int _allowed_errors;
int _cache_hits; int _cache_hits;
int _transfer_rate; int _transfer_rate;
// kbytes, not bytes, because bytes might overflow 2^31 // kbytes, not bytes, because bytes might overflow 2^31
@@ -306,13 +301,6 @@ public:
void setInstalledDir(const SGPath& p) { _installRoot = p; } void setInstalledDir(const SGPath& p) { _installRoot = p; }
void setAllowedErrorCount(int errors)
{
std::lock_guard<std::mutex> g(_stateLock);
_state._allowed_errors = errors;
}
void setCachePath(const SGPath& p) {_persistentCachePath = p;}
void setCacheHits(unsigned int hits) void setCacheHits(unsigned int hits)
{ {
std::lock_guard<std::mutex> g(_stateLock); std::lock_guard<std::mutex> g(_stateLock);
@@ -328,7 +316,12 @@ public:
} }
return st; return st;
} }
private:
bool isDirActive(const std::string& path) const;
void setCachePath(const SGPath &p) { _persistentCachePath = p; }
private:
void incrementCacheHits() void incrementCacheHits()
{ {
std::lock_guard<std::mutex> g(_stateLock); std::lock_guard<std::mutex> g(_stateLock);
@@ -341,15 +334,18 @@ private:
void runInternal(); void runInternal();
void updateSyncSlot(SyncSlot& slot); void updateSyncSlot(SyncSlot& slot);
void drainWaitingTiles();
// commond helpers between both internal and external models // commond helpers between both internal and external models
SyncItem::Status isPathCached(const SyncItem& next) const; SyncItem::Status isPathCached(const SyncItem& next) const;
void initCompletedTilesPersistentCache();
void writeCompletedTilesPersistentCache() const;
void updated(SyncItem item, bool isNewDirectory); void updated(SyncItem item, bool isNewDirectory);
void fail(SyncItem failedItem); void fail(SyncItem failedItem);
void notFound(SyncItem notFoundItem); void notFound(SyncItem notFoundItem);
void initCompletedTilesPersistentCache();
void writeCompletedTilesPersistentCache() const;
HTTP::Client _http; HTTP::Client _http;
SyncSlot _syncSlots[NUM_SYNC_SLOTS]; SyncSlot _syncSlots[NUM_SYNC_SLOTS];
@@ -370,7 +366,7 @@ private:
string _dnsdn; string _dnsdn;
TerrasyncThreadState _state; TerrasyncThreadState _state;
std::mutex _stateLock; mutable std::mutex _stateLock;
}; };
SGTerraSync::WorkerThread::WorkerThread() : SGTerraSync::WorkerThread::WorkerThread() :
@@ -398,6 +394,18 @@ void SGTerraSync::WorkerThread::stop()
SyncItem w(string(), SyncItem::Stop); SyncItem w(string(), SyncItem::Stop);
request(w); request(w);
join(); join();
// clear the sync slots, in case we restart
for (unsigned int slot = 0; slot < NUM_SYNC_SLOTS; ++slot) {
_syncSlots[slot] = {};
}
// clear these so if re-init-ing, we check again
_completedTiles.clear();
_notFoundItems.clear();
_http.reset();
_http.setUserAgent("terrascenery-" SG_STRINGIZE(SIMGEAR_VERSION));
} }
bool SGTerraSync::WorkerThread::start() bool SGTerraSync::WorkerThread::start()
@@ -417,12 +425,18 @@ bool SGTerraSync::WorkerThread::start()
SGPath path(_local_dir); SGPath path(_local_dir);
if (!path.exists()) if (!path.exists())
{ {
SG_LOG(SG_TERRASYNC,SG_ALERT, const SGPath parentDir = path.dirPath();
"Cannot start scenery download. Directory '" << _local_dir << if (parentDir.exists()) {
"' does not exist. Set correct directory path or create directory folder."); // attempt to create terraSync dir ourselves
_state._fail_count++; bool ok = path.create_dir(0755);
_state._stalled = true; if (!ok) {
return false; SG_LOG(SG_TERRASYNC, SG_ALERT,
"Cannot start scenery download. Directory '" << _local_dir << "' does not exist. Set correct directory path or create directory folder.");
_state._fail_count++;
_state._stalled = true;
return false;
}
}
} }
path.append("version"); path.append("version");
@@ -439,10 +453,8 @@ bool SGTerraSync::WorkerThread::start()
_stop = false; _stop = false;
_state = TerrasyncThreadState(); // clean state _state = TerrasyncThreadState(); // clean state
// not really an alert - but we want to (always) see this message, so user is SG_LOG(SG_TERRASYNC, SG_MANDATORY_INFO,
// aware we're downloading scenery (and using bandwidth). "Starting automatic scenery download/synchronization to '" << _local_dir << "'.");
SG_LOG(SG_TERRASYNC,SG_ALERT,
"Starting automatic scenery download/synchronization to '"<< _local_dir << "'.");
SGThread::start(); SGThread::start();
return true; return true;
@@ -524,7 +536,6 @@ void SGTerraSync::WorkerThread::run()
} }
initCompletedTilesPersistentCache(); initCompletedTilesPersistentCache();
runInternal(); runInternal();
{ {
@@ -541,7 +552,7 @@ void SGTerraSync::WorkerThread::updateSyncSlot(SyncSlot &slot)
if (slot.stamp.elapsedMSec() > (int)slot.nextWarnTimeout) { if (slot.stamp.elapsedMSec() > (int)slot.nextWarnTimeout) {
SG_LOG(SG_TERRASYNC, SG_INFO, "sync taking a long time:" << slot.currentItem._dir << " taken " << slot.stamp.elapsedMSec()); SG_LOG(SG_TERRASYNC, SG_INFO, "sync taking a long time:" << slot.currentItem._dir << " taken " << slot.stamp.elapsedMSec());
SG_LOG(SG_TERRASYNC, SG_INFO, "HTTP request count:" << _http.hasActiveRequests()); SG_LOG(SG_TERRASYNC, SG_INFO, "HTTP request count:" << _http.hasActiveRequests());
slot.nextWarnTimeout += 10000; slot.nextWarnTimeout += 30 * 1000;
} }
#endif #endif
// convert bytes to kbytes here // convert bytes to kbytes here
@@ -551,6 +562,7 @@ void SGTerraSync::WorkerThread::updateSyncSlot(SyncSlot &slot)
// check result // check result
HTTPRepository::ResultCode res = slot.repository->failure(); HTTPRepository::ResultCode res = slot.repository->failure();
if (res == HTTPRepository::REPO_ERROR_NOT_FOUND) { if (res == HTTPRepository::REPO_ERROR_NOT_FOUND) {
notFound(slot.currentItem); notFound(slot.currentItem);
} else if (res != HTTPRepository::REPO_NO_ERROR) { } else if (res != HTTPRepository::REPO_NO_ERROR) {
@@ -565,12 +577,13 @@ void SGTerraSync::WorkerThread::updateSyncSlot(SyncSlot &slot)
slot.busy = false; slot.busy = false;
slot.repository.reset(); slot.repository.reset();
slot.pendingKBytes = 0; slot.pendingKBytes = 0;
slot.currentItem = {};
} }
// init and start sync of the next repository // init and start sync of the next repository
if (!slot.queue.empty()) { if (!slot.queue.empty()) {
slot.currentItem = slot.queue.front(); slot.currentItem = slot.queue.front();
slot.queue.pop(); slot.queue.pop_front();
SGPath path(_local_dir); SGPath path(_local_dir);
path.append(slot.currentItem._dir); path.append(slot.currentItem._dir);
@@ -605,7 +618,7 @@ void SGTerraSync::WorkerThread::updateSyncSlot(SyncSlot &slot)
return; return;
} }
slot.nextWarnTimeout = 20000; slot.nextWarnTimeout = 30 * 1000;
slot.stamp.stamp(); slot.stamp.stamp();
slot.busy = true; slot.busy = true;
slot.pendingKBytes = slot.repository->bytesToDownload(); slot.pendingKBytes = slot.repository->bytesToDownload();
@@ -616,21 +629,23 @@ void SGTerraSync::WorkerThread::updateSyncSlot(SyncSlot &slot)
void SGTerraSync::WorkerThread::runInternal() void SGTerraSync::WorkerThread::runInternal()
{ {
unsigned dnsRetryCount = 0;
while (!_stop) { while (!_stop) {
// try to find a terrasync server // try to find a terrasync server
if( !hasServer() ) { if( !hasServer() ) {
if( ++dnsRetryCount > 5 ) { const auto haveServer = findServer();
SG_LOG(SG_TERRASYNC, SG_WARN, "Can't find a terrasync server. TS disabled."); if (haveServer) {
break; hasServer(true);
}
if( hasServer( findServer() ) ) { std::lock_guard<std::mutex> g(_stateLock);
SG_LOG(SG_TERRASYNC, SG_INFO, "terrasync scenery provider of the day is '" << _httpServer << "'"); _state._consecutive_errors = 0;
}
continue; SG_LOG(SG_TERRASYNC, SG_INFO, "terrasync scenery provider of the day is '" << _httpServer << "'");
} else {
std::lock_guard<std::mutex> g(_stateLock);
_state._consecutive_errors++;
}
continue;
} }
dnsRetryCount = 0;
try { try {
_http.update(10); _http.update(10);
@@ -648,21 +663,7 @@ void SGTerraSync::WorkerThread::runInternal()
if (_stop) if (_stop)
break; break;
// drain the waiting tiles queue into the sync slot queues. drainWaitingTiles();
while (!waitingTiles.empty()) {
SyncItem next = waitingTiles.pop_front();
SyncItem::Status cacheStatus = isPathCached(next);
if (cacheStatus != SyncItem::Invalid) {
incrementCacheHits();
SG_LOG(SG_TERRASYNC, SG_DEBUG, "\nTerraSync Cache hit for: '" << next._dir << "'");
next._status = cacheStatus;
_freshTiles.push_back(next);
continue;
}
unsigned int slot = syncSlotForType(next._type);
_syncSlots[slot].queue.push(next);
}
bool anySlotBusy = false; bool anySlotBusy = false;
unsigned int newPendingCount = 0; unsigned int newPendingCount = 0;
@@ -691,7 +692,7 @@ void SGTerraSync::WorkerThread::runInternal()
SyncItem::Status SGTerraSync::WorkerThread::isPathCached(const SyncItem& next) const SyncItem::Status SGTerraSync::WorkerThread::isPathCached(const SyncItem& next) const
{ {
TileAgeCache::const_iterator ii = _completedTiles.find( next._dir ); auto ii = _completedTiles.find(next._dir);
if (ii == _completedTiles.end()) { if (ii == _completedTiles.end()) {
ii = _notFoundItems.find( next._dir ); ii = _notFoundItems.find( next._dir );
// Invalid means 'not cached', otherwise we want to return to // Invalid means 'not cached', otherwise we want to return to
@@ -760,68 +761,117 @@ void SGTerraSync::WorkerThread::updated(SyncItem item, bool isNewDirectory)
writeCompletedTilesPersistentCache(); writeCompletedTilesPersistentCache();
} }
void SGTerraSync::WorkerThread::initCompletedTilesPersistentCache() void SGTerraSync::WorkerThread::drainWaitingTiles()
{ {
if (!_persistentCachePath.exists()) { // drain the waiting tiles queue into the sync slot queues.
return; while (!waitingTiles.empty()) {
} SyncItem next = waitingTiles.pop_front();
SyncItem::Status cacheStatus = isPathCached(next);
SGPropertyNode_ptr cacheRoot(new SGPropertyNode); if (cacheStatus != SyncItem::Invalid) {
time_t now = time(0); incrementCacheHits();
SG_LOG(SG_TERRASYNC, SG_BULK, "\nTerraSync Cache hit for: '" << next._dir << "'");
try { next._status = cacheStatus;
readProperties(_persistentCachePath, cacheRoot); _freshTiles.push_back(next);
} catch (sg_exception& e) {
SG_LOG(SG_TERRASYNC, SG_INFO, "corrupted persistent cache, discarding " << e.getFormattedMessage());
return;
}
for (int i=0; i<cacheRoot->nChildren(); ++i) {
SGPropertyNode* entry = cacheRoot->getChild(i);
bool isNotFound = (strcmp(entry->getName(), "not-found") == 0);
string tileName = entry->getStringValue("path");
time_t stamp = entry->getIntValue("stamp");
if (stamp < now) {
continue; continue;
} }
if (isNotFound) { const auto slot = syncSlotForType(next._type);
_completedTiles[tileName] = stamp; _syncSlots[slot].queue.push_back(next);
} else {
_notFoundItems[tileName] = stamp;
}
} }
} }
void SGTerraSync::WorkerThread::writeCompletedTilesPersistentCache() const bool SGTerraSync::WorkerThread::isDirActive(const std::string& path) const
{ {
// cache is disabled // check waiting tiles first. we have to copy it to check safely,
if (_persistentCachePath.isNull()) { // but since it's normally empty, this is not a big deal.
return; const auto copyOfWaiting = waitingTiles.copy();
auto it = std::find_if(copyOfWaiting.begin(), copyOfWaiting.end(), [&path](const SyncItem& i) {
return i._dir == path;
});
if (it != copyOfWaiting.end()) {
return true;
} }
sg_ofstream f(_persistentCachePath, std::ios::trunc); // check each sync slot in turn
if (!f.is_open()) { std::lock_guard<std::mutex> g(_stateLock);
return; for (unsigned int slot = 0; slot < NUM_SYNC_SLOTS; ++slot) {
const auto& syncSlot = _syncSlots[slot];
if (syncSlot.currentItem._dir == path)
return true;
auto it = std::find_if(syncSlot.queue.begin(), syncSlot.queue.end(), [&path](const SyncItem& i) {
return i._dir == path;
});
if (it != syncSlot.queue.end()) {
return true;
}
} // of sync slots iteration
return false;
}
void SGTerraSync::WorkerThread::initCompletedTilesPersistentCache() {
if (!_persistentCachePath.exists()) {
return;
}
SGPropertyNode_ptr cacheRoot(new SGPropertyNode);
time_t now = time(0);
try {
readProperties(_persistentCachePath, cacheRoot);
} catch (sg_exception &e) {
SG_LOG(SG_TERRASYNC, SG_INFO, "corrupted persistent cache, discarding");
return;
}
for (int i = 0; i < cacheRoot->nChildren(); ++i) {
SGPropertyNode *entry = cacheRoot->getChild(i);
bool isNotFound = (strcmp(entry->getName(), "not-found") == 0);
string tileName = entry->getStringValue("path");
time_t stamp = entry->getIntValue("stamp");
if (stamp < now) {
continue;
} }
SGPropertyNode_ptr cacheRoot(new SGPropertyNode); if (isNotFound) {
TileAgeCache::const_iterator it = _completedTiles.begin(); _notFoundItems[tileName] = stamp;
for (; it != _completedTiles.end(); ++it) { } else {
SGPropertyNode* entry = cacheRoot->addChild("entry"); _completedTiles[tileName] = stamp;
entry->setStringValue("path", it->first);
entry->setIntValue("stamp", it->second);
} }
}
}
it = _notFoundItems.begin(); void SGTerraSync::WorkerThread::writeCompletedTilesPersistentCache() const {
for (; it != _notFoundItems.end(); ++it) { // cache is disabled
SGPropertyNode* entry = cacheRoot->addChild("not-found"); if (_persistentCachePath.isNull()) {
entry->setStringValue("path", it->first); return;
entry->setIntValue("stamp", it->second); }
}
writeProperties(f, cacheRoot, true /* write_all */); sg_ofstream f(_persistentCachePath, std::ios::trunc);
f.close(); if (!f.is_open()) {
return;
}
SGPropertyNode_ptr cacheRoot(new SGPropertyNode);
TileAgeCache::const_iterator it = _completedTiles.begin();
for (; it != _completedTiles.end(); ++it) {
SGPropertyNode *entry = cacheRoot->addChild("entry");
entry->setStringValue("path", it->first);
entry->setIntValue("stamp", it->second);
}
it = _notFoundItems.begin();
for (; it != _notFoundItems.end(); ++it) {
SGPropertyNode *entry = cacheRoot->addChild("not-found");
entry->setStringValue("path", it->first);
entry->setIntValue("stamp", it->second);
}
writeProperties(f, cacheRoot, true /* write_all */);
f.close();
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@@ -850,6 +900,12 @@ SGTerraSync::~SGTerraSync()
void SGTerraSync::setRoot(SGPropertyNode_ptr root) void SGTerraSync::setRoot(SGPropertyNode_ptr root)
{ {
if (!root) {
_terraRoot.clear();
_renderingRoot.clear();
return;
}
_terraRoot = root->getNode("/sim/terrasync",true); _terraRoot = root->getNode("/sim/terrasync",true);
_renderingRoot = root->getNode("/sim/rendering", true); _renderingRoot = root->getNode("/sim/rendering", true);
} }
@@ -897,11 +953,17 @@ void SGTerraSync::reinit()
#else #else
_workerThread->setDNSDN( _terraRoot->getStringValue("dnsdn","terrasync.flightgear.org") ); _workerThread->setDNSDN( _terraRoot->getStringValue("dnsdn","terrasync.flightgear.org") );
#endif #endif
_workerThread->setLocalDir(_terraRoot->getStringValue("scenery-dir",""));
SGPath sceneryRoot{_terraRoot->getStringValue("scenery-dir", "")};
_workerThread->setLocalDir(sceneryRoot.utf8Str());
SGPath installPath(_terraRoot->getStringValue("installation-dir")); SGPath installPath(_terraRoot->getStringValue("installation-dir"));
_workerThread->setInstalledDir(installPath); _workerThread->setInstalledDir(installPath);
_workerThread->setAllowedErrorCount(_terraRoot->getIntValue("max-errors",5));
if (_terraRoot->getBoolValue("enable-persistent-cache", true)) {
_workerThread->setCachePath(sceneryRoot / "RecheckCache");
}
_workerThread->setCacheHits(_terraRoot->getIntValue("cache-hit", 0)); _workerThread->setCacheHits(_terraRoot->getIntValue("cache-hit", 0));
if (_workerThread->start()) if (_workerThread->start())
@@ -944,12 +1006,7 @@ void SGTerraSync::bind()
_downloadedKBtesNode = _terraRoot->getNode("downloaded-kbytes", true); _downloadedKBtesNode = _terraRoot->getNode("downloaded-kbytes", true);
_enabledNode = _terraRoot->getNode("enabled", true); _enabledNode = _terraRoot->getNode("enabled", true);
_availableNode = _terraRoot->getNode("available", true); _availableNode = _terraRoot->getNode("available", true);
//_busyNode->setAttribute(SGPropertyNode::WRITE, false); _maxErrorsNode = _terraRoot->getNode("max-errors", true);
//_activeNode->setAttribute(SGPropertyNode::WRITE, false);
//_updateCountNode->setAttribute(SGPropertyNode::WRITE, false);
//_errorCountNode->setAttribute(SGPropertyNode::WRITE, false);
//_tileCountNode->setAttribute(SGPropertyNode::WRITE, false);
} }
void SGTerraSync::unbind() void SGTerraSync::unbind()
@@ -961,7 +1018,20 @@ void SGTerraSync::unbind()
_terraRoot.clear(); _terraRoot.clear();
_stalledNode.clear(); _stalledNode.clear();
_activeNode.clear();
_cacheHits.clear(); _cacheHits.clear();
_renderingRoot.clear();
_busyNode.clear();
_updateCountNode.clear();
_errorCountNode.clear();
_tileCountNode.clear();
_cacheHitsNode.clear();
_transferRateBytesSecNode.clear();
_pendingKbytesNode.clear();
_downloadedKBtesNode.clear();
_enabledNode.clear();
_availableNode.clear();
_maxErrorsNode.clear();
} }
void SGTerraSync::update(double) void SGTerraSync::update(double)
@@ -969,16 +1039,21 @@ void SGTerraSync::update(double)
auto enabled = _enabledNode->getBoolValue(); auto enabled = _enabledNode->getBoolValue();
auto worker_running = _workerThread->isRunning(); auto worker_running = _workerThread->isRunning();
// hold enabled false until retry time passes
if (enabled && (_retryTime > SGTimeStamp::now())) {
enabled = false;
}
// see if the enabled status has changed; and if so take the appropriate action. // see if the enabled status has changed; and if so take the appropriate action.
if (enabled && !worker_running) if (enabled && !worker_running)
{ {
reinit(); reinit();
SG_LOG(SG_TERRASYNC, SG_ALERT, "Terrasync started"); SG_LOG(SG_TERRASYNC, SG_MANDATORY_INFO, "Terrasync started");
} }
else if (!enabled && worker_running) else if (!enabled && worker_running)
{ {
reinit(); reinit();
SG_LOG(SG_TERRASYNC, SG_ALERT, "Terrasync stopped"); SG_LOG(SG_TERRASYNC, SG_MANDATORY_INFO, "Terrasync stopped");
} }
TerrasyncThreadState copiedState(_workerThread->threadsafeCopyState()); TerrasyncThreadState copiedState(_workerThread->threadsafeCopyState());
@@ -994,14 +1069,21 @@ void SGTerraSync::update(double)
_stalledNode->setBoolValue(_workerThread->isStalled()); _stalledNode->setBoolValue(_workerThread->isStalled());
_activeNode->setBoolValue(worker_running); _activeNode->setBoolValue(worker_running);
int allowedErrors = _maxErrorsNode->getIntValue();
if (worker_running && (copiedState._consecutive_errors >= allowedErrors)) {
_workerThread->stop();
_retryBackOffSeconds = std::min(_retryBackOffSeconds + 60, 60u * 15);
const int seconds = static_cast<int>(sg_random() * _retryBackOffSeconds);
_retryTime = SGTimeStamp::now() + SGTimeStamp::fromSec(seconds);
SG_LOG(SG_TERRASYNC, SG_ALERT, "Terrasync paused due to " << copiedState._consecutive_errors << " consecutive errors during sync; will retry in " << seconds << " seconds.");
}
while (_workerThread->hasNewTiles()) while (_workerThread->hasNewTiles())
{ {
SyncItem next = _workerThread->getNewTile(); // ensure they are popped
_workerThread->getNewTile();
if ((next._type == SyncItem::Tile) || (next._type == SyncItem::AIData)) { }
_activeTileDirs.erase(next._dir);
}
} // of freshly synced items
} }
bool SGTerraSync::isIdle() {return _workerThread->isIdle();} bool SGTerraSync::isIdle() {return _workerThread->isIdle();}
@@ -1046,17 +1128,16 @@ string_list SGTerraSync::getSceneryPathSuffixes() const
void SGTerraSync::syncAreaByPath(const std::string& aPath) void SGTerraSync::syncAreaByPath(const std::string& aPath)
{ {
string_list scenerySuffixes = getSceneryPathSuffixes(); if (!_workerThread->isRunning()) {
string_list::const_iterator it = scenerySuffixes.begin(); return;
}
for (; it != scenerySuffixes.end(); ++it) for (const auto& suffix : getSceneryPathSuffixes()) {
{ const auto dir = suffix + "/" + aPath;
std::string dir = *it + "/" + aPath; if (_workerThread->isDirActive(dir)) {
if (_activeTileDirs.find(dir) != _activeTileDirs.end()) {
continue; continue;
} }
_activeTileDirs.insert(dir);
SyncItem w(dir, SyncItem::Tile); SyncItem w(dir, SyncItem::Tile);
_workerThread->request( w ); _workerThread->request( w );
} }
@@ -1075,13 +1156,9 @@ bool SGTerraSync::isTileDirPending(const std::string& sceneryDir) const
return false; return false;
} }
string_list scenerySuffixes = getSceneryPathSuffixes(); for (const auto& suffix : getSceneryPathSuffixes()) {
string_list::const_iterator it = scenerySuffixes.begin(); const auto s = suffix + "/" + sceneryDir;
if (_workerThread->isDirActive(s)) {
for (; it != scenerySuffixes.end(); ++it)
{
string s = *it + "/" + sceneryDir;
if (_activeTileDirs.find(s) != _activeTileDirs.end()) {
return true; return true;
} }
} }
@@ -1091,14 +1168,16 @@ bool SGTerraSync::isTileDirPending(const std::string& sceneryDir) const
void SGTerraSync::scheduleDataDir(const std::string& dataDir) void SGTerraSync::scheduleDataDir(const std::string& dataDir)
{ {
if (_activeTileDirs.find(dataDir) != _activeTileDirs.end()) { if (!_workerThread->isRunning()) {
return;
}
if (_workerThread->isDirActive(dataDir)) {
return; return;
} }
_activeTileDirs.insert(dataDir);
SyncItem w(dataDir, SyncItem::AIData); SyncItem w(dataDir, SyncItem::AIData);
_workerThread->request( w ); _workerThread->request( w );
} }
bool SGTerraSync::isDataDirPending(const std::string& dataDir) const bool SGTerraSync::isDataDirPending(const std::string& dataDir) const
@@ -1107,7 +1186,7 @@ bool SGTerraSync::isDataDirPending(const std::string& dataDir) const
return false; return false;
} }
return (_activeTileDirs.find(dataDir) != _activeTileDirs.end()); return _workerThread->isDirActive(dataDir);
} }
void SGTerraSync::reposition() void SGTerraSync::reposition()
+5 -2
View File
@@ -107,6 +107,7 @@ private:
SGPropertyNode_ptr _transferRateBytesSecNode; SGPropertyNode_ptr _transferRateBytesSecNode;
SGPropertyNode_ptr _pendingKbytesNode; SGPropertyNode_ptr _pendingKbytesNode;
SGPropertyNode_ptr _downloadedKBtesNode; SGPropertyNode_ptr _downloadedKBtesNode;
SGPropertyNode_ptr _maxErrorsNode;
// we manually bind+init TerraSync during early startup // we manually bind+init TerraSync during early startup
// to get better overlap of slow operations (Shared Models sync // to get better overlap of slow operations (Shared Models sync
@@ -117,8 +118,10 @@ private:
simgear::TiedPropertyList _tiedProperties; simgear::TiedPropertyList _tiedProperties;
BufferedLogCallback* _log; BufferedLogCallback* _log;
typedef std::set<std::string> string_set; /// if we disabled TerraSync due to errors, this is the time at which we will restart it
string_set _activeTileDirs; /// automatically.
SGTimeStamp _retryTime;
unsigned int _retryBackOffSeconds = 0;
}; };
} }
+1
View File
@@ -594,6 +594,7 @@ unsigned int SGSoundMgr::request_buffer(SGSoundSample *sample)
case SG_SAMPLE_STEREO8: case SG_SAMPLE_STEREO8:
SG_LOG(SG_SOUND, SG_POPUP, "Stereo sound detected:\n" << sample->get_sample_name() << "\nUse two separate mono files instead if required."); SG_LOG(SG_SOUND, SG_POPUP, "Stereo sound detected:\n" << sample->get_sample_name() << "\nUse two separate mono files instead if required.");
format = AL_FORMAT_STEREO8; format = AL_FORMAT_STEREO8;
break;
default: default:
SG_LOG(SG_SOUND, SG_ALERT, "unsupported audio format"); SG_LOG(SG_SOUND, SG_ALERT, "unsupported audio format");
return buffer; return buffer;
+17 -11
View File
@@ -414,9 +414,11 @@ SGXmlSound::update (double dt)
double v = 1.0; double v = 1.0;
if (_volume[i].expr) { if (_volume[i].expr) {
v = _volume[i].expr->getValue(nullptr); double expression_value = _volume[i].expr->getValue(nullptr);
expr = true; if (expression_value >= 0)
continue; volume *= expression_value;
expr = true;
continue;
} }
if (_volume[i].prop) { if (_volume[i].prop) {
@@ -437,16 +439,20 @@ SGXmlSound::update (double dt)
if (_volume[i].max && (v > _volume[i].max)) if (_volume[i].max && (v > _volume[i].max))
v = _volume[i].max; v = _volume[i].max;
else if (v < _volume[i].min) else if (v < _volume[i].min)
v = _volume[i].min; v = _volume[i].min;
if (_volume[i].subtract) // Hack!
volume = _volume[i].offset - v;
if (_volume[i].subtract){ // Hack!
v = v + _volume[i].offset;
if (v >= 0)
volume = volume * v;
}
else { else {
volume_offset += _volume[i].offset; if (v >= 0) {
volume *= v; volume_offset += _volume[i].offset;
volume = volume * v;
}
} }
} }
@@ -462,9 +468,9 @@ SGXmlSound::update (double dt)
double p = 1.0; double p = 1.0;
if (_pitch[i].expr) { if (_pitch[i].expr) {
p = _pitch[i].expr->getValue(nullptr); pitch *= _pitch[i].expr->getValue(nullptr);
expr = true; expr = true;
continue; continue;
} }
if (_pitch[i].prop) { if (_pitch[i].prop) {
+23 -33
View File
@@ -18,24 +18,16 @@
#include <simgear/structure/exception.hxx> #include <simgear/structure/exception.hxx>
SGBinding::SGBinding() SGBinding::SGBinding()
: _command(0), : _arg(new SGPropertyNode)
_arg(new SGPropertyNode),
_setting(0)
{ {
} }
SGBinding::SGBinding(const std::string& commandName) SGBinding::SGBinding(const std::string& commandName)
: _command(0),
_arg(0),
_setting(0)
{ {
_command_name = commandName; _command_name = commandName;
} }
SGBinding::SGBinding(const SGPropertyNode* node, SGPropertyNode* root) SGBinding::SGBinding(const SGPropertyNode* node, SGPropertyNode* root)
: _command(0),
_arg(0),
_setting(0)
{ {
read(node, root); read(node, root);
} }
@@ -43,8 +35,8 @@ SGBinding::SGBinding(const SGPropertyNode* node, SGPropertyNode* root)
void void
SGBinding::clear() SGBinding::clear()
{ {
_command = NULL;
_arg.clear(); _arg.clear();
_root.clear();
_setting.clear(); _setting.clear();
} }
@@ -57,14 +49,12 @@ SGBinding::read(const SGPropertyNode* node, SGPropertyNode* root)
_command_name = node->getStringValue("command", ""); _command_name = node->getStringValue("command", "");
if (_command_name.empty()) { if (_command_name.empty()) {
SG_LOG(SG_INPUT, SG_WARN, "No command supplied for binding."); SG_LOG(SG_INPUT, SG_DEV_ALERT, "No command supplied for binding.");
_command = 0;
} }
_arg = const_cast<SGPropertyNode*>(node); _arg = const_cast<SGPropertyNode*>(node);
_root = const_cast<SGPropertyNode*>(root); _root = const_cast<SGPropertyNode*>(root);
_setting = 0; _setting.clear();
} }
void void
@@ -78,30 +68,29 @@ SGBinding::fire() const
void void
SGBinding::innerFire () const SGBinding::innerFire () const
{ {
if (_command == 0) auto cmd = SGCommandMgr::instance()->getCommand(_command_name);
_command = SGCommandMgr::instance()->getCommand(_command_name); if (!cmd) {
if (_command == 0) { SG_LOG(SG_INPUT, SG_WARN, "No command found for binding:" << _command_name);
SG_LOG(SG_INPUT, SG_WARN, "No command attached to binding:" << _command_name); return;
} else { }
try {
if (!(*_command)(_arg, _root)) { try {
SG_LOG(SG_INPUT, SG_ALERT, "Failed to execute command " if (!(*cmd)(_arg, _root)) {
<< _command_name); SG_LOG(SG_INPUT, SG_ALERT, "Failed to execute command " << _command_name);
} }
} catch (sg_exception& e) { } catch (sg_exception& e) {
SG_LOG(SG_GENERAL, SG_ALERT, "command '" << _command_name << "' failed with exception\n" SG_LOG(SG_GENERAL, SG_ALERT, "command '" << _command_name << "' failed with exception\n"
<< "\tmessage:" << e.getMessage() << " (from " << e.getOrigin() << ")"); << "\tmessage:" << e.getMessage() << " (from " << e.getOrigin() << ")");
} }
}
} }
void void
SGBinding::fire (SGPropertyNode* params) const SGBinding::fire (SGPropertyNode* params) const
{ {
if (test()) { if (test()) {
if (params != NULL) { if (params != nullptr) {
copyProperties(params, _arg); copyProperties(params, _arg);
} }
innerFire(); innerFire();
} }
@@ -122,8 +111,9 @@ SGBinding::fire (double setting) const
if (test()) { if (test()) {
// A value is automatically added to // A value is automatically added to
// the args // the args
if (_setting == 0) // save the setting node for efficiency if (!_setting) { // save the setting node for efficiency
_setting = _arg->getChild("setting", 0, true); _setting = _arg->getChild("setting", 0, true);
}
_setting->setDoubleValue(setting); _setting->setDoubleValue(setting);
innerFire(); innerFire();
} }
-11
View File
@@ -77,16 +77,6 @@ public:
*/ */
const std::string &getCommandName () const { return _command_name; } const std::string &getCommandName () const { return _command_name; }
/**
* Get the command itself.
*
* @return The command associated with this binding, or 0 if none
* is present.
*/
SGCommandMgr::Command* getCommand () const { return _command; }
/** /**
* Get the argument that will be passed to the command. * Get the argument that will be passed to the command.
* *
@@ -140,7 +130,6 @@ private:
SGBinding (const SGBinding &binding); SGBinding (const SGBinding &binding);
std::string _command_name; std::string _command_name;
mutable SGCommandMgr::Command* _command;
mutable SGPropertyNode_ptr _arg; mutable SGPropertyNode_ptr _arg;
mutable SGPropertyNode_ptr _setting; mutable SGPropertyNode_ptr _setting;
mutable SGPropertyNode_ptr _root; mutable SGPropertyNode_ptr _root;
+31 -60
View File
@@ -13,6 +13,8 @@
#include <simgear/misc/sg_path.hxx> #include <simgear/misc/sg_path.hxx>
static ThrowCallback static_callback;
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
// Implementation of sg_location class. // Implementation of sg_location class.
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
@@ -49,19 +51,7 @@ sg_location::sg_location (const char* path, int line, int column)
setPath(path); setPath(path);
} }
sg_location::~sg_location () void sg_location::setPath(const char *path) {
{
}
const char*
sg_location::getPath () const
{
return _path;
}
void
sg_location::setPath (const char* path)
{
if (path) { if (path) {
strncpy(_path, path, max_path); strncpy(_path, path, max_path);
_path[max_path -1] = '\0'; _path[max_path -1] = '\0';
@@ -70,17 +60,9 @@ sg_location::setPath (const char* path)
} }
} }
int const char *sg_location::getPath() const { return _path; }
sg_location::getLine () const
{
return _line;
}
void int sg_location::getLine() const { return _line; }
sg_location::setLine (int line)
{
_line = line;
}
int int
sg_location::getColumn () const sg_location::getColumn () const
@@ -88,11 +70,6 @@ sg_location::getColumn () const
return _column; return _column;
} }
void
sg_location::setColumn (int column)
{
_column = column;
}
int int
sg_location::getByte () const sg_location::getByte () const
@@ -100,12 +77,6 @@ sg_location::getByte () const
return _byte; return _byte;
} }
void
sg_location::setByte (int byte)
{
_byte = byte;
}
std::string std::string
sg_location::asString () const sg_location::asString () const
{ {
@@ -126,8 +97,8 @@ sg_location::asString () const
return out.str(); return out.str();
} }
bool sg_location::isValid() const { return strlen(_path) > 0; }
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
// Implementation of sg_throwable class. // Implementation of sg_throwable class.
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
@@ -138,10 +109,14 @@ sg_throwable::sg_throwable ()
_origin[0] = '\0'; _origin[0] = '\0';
} }
sg_throwable::sg_throwable (const char* message, const char* origin) sg_throwable::sg_throwable(const char *message, const char *origin,
{ const sg_location &loc) {
setMessage(message); setMessage(message);
setOrigin(origin); setOrigin(origin);
if (static_callback) {
static_callback(_message, _origin, loc);
}
} }
sg_throwable::~sg_throwable () sg_throwable::~sg_throwable ()
@@ -228,16 +203,13 @@ sg_exception::sg_exception ()
{ {
} }
sg_exception::sg_exception (const char* message, const char* origin) sg_exception::sg_exception(const char *message, const char *origin,
: sg_throwable(message, origin) const sg_location &loc)
{ : sg_throwable(message, origin, loc) {}
}
sg_exception::sg_exception( const std::string& message, sg_exception::sg_exception(const std::string &message,
const std::string& origin ) const std::string &origin, const sg_location &loc)
: sg_throwable(message.c_str(), origin.c_str()) : sg_throwable(message.c_str(), origin.c_str(), loc) {}
{
}
sg_exception::~sg_exception () sg_exception::~sg_exception ()
{ {
@@ -257,13 +229,10 @@ sg_io_exception::sg_io_exception (const char* message, const char* origin)
{ {
} }
sg_io_exception::sg_io_exception (const char* message, sg_io_exception::sg_io_exception(const char *message,
const sg_location &location, const sg_location &location,
const char* origin) const char *origin)
: sg_exception(message, origin), : sg_exception(message, origin, location), _location(location) {}
_location(location)
{
}
sg_io_exception::sg_io_exception( const std::string& message, sg_io_exception::sg_io_exception( const std::string& message,
const std::string& origin ) const std::string& origin )
@@ -271,13 +240,10 @@ sg_io_exception::sg_io_exception( const std::string& message,
{ {
} }
sg_io_exception::sg_io_exception( const std::string& message, sg_io_exception::sg_io_exception(const std::string &message,
const sg_location &location, const sg_location &location,
const std::string& origin ) const std::string &origin)
: sg_exception(message, origin), : sg_exception(message, origin, location), _location(location) {}
_location(location)
{
}
sg_io_exception::~sg_io_exception () sg_io_exception::~sg_io_exception ()
{ {
@@ -382,4 +348,9 @@ sg_range_exception::sg_range_exception(const std::string& message,
sg_range_exception::~sg_range_exception () sg_range_exception::~sg_range_exception ()
{ {
} }
////////////////////////////////////////////////////////////////////////
void setThrowCallback(ThrowCallback cb) { static_callback = cb; }
// end of exception.cxx // end of exception.cxx
+33 -14
View File
@@ -11,6 +11,7 @@
#define __SIMGEAR_MISC_EXCEPTION_HXX 1 #define __SIMGEAR_MISC_EXCEPTION_HXX 1
#include <exception> #include <exception>
#include <functional>
#include <simgear/compiler.h> #include <simgear/compiler.h>
#include <string> #include <string>
@@ -31,17 +32,20 @@ public:
sg_location(const std::string& path, int line = -1, int column = -1); sg_location(const std::string& path, int line = -1, int column = -1);
sg_location(const SGPath& path, int line = -1, int column = -1); sg_location(const SGPath& path, int line = -1, int column = -1);
explicit sg_location(const char* path, int line = -1, int column = -1); explicit sg_location(const char* path, int line = -1, int column = -1);
virtual ~sg_location();
virtual const char* getPath() const; ~sg_location() = default;
virtual void setPath (const char* path);
virtual int getLine () const; const char *getPath() const;
virtual void setLine (int line); int getLine() const;
virtual int getColumn () const; int getColumn() const;
virtual void setColumn (int column); int getByte() const;
virtual int getByte () const;
virtual void setByte (int byte); std::string asString() const;
virtual std::string asString () const; bool isValid() const;
private: private:
void setPath(const char *p);
char _path[max_path]; char _path[max_path];
int _line; int _line;
int _column; int _column;
@@ -57,7 +61,9 @@ class sg_throwable : public std::exception
public: public:
enum {MAX_TEXT_LEN = 1024}; enum {MAX_TEXT_LEN = 1024};
sg_throwable (); sg_throwable ();
sg_throwable (const char* message, const char* origin = 0); sg_throwable(const char *message, const char *origin = 0,
const sg_location &loc = {});
virtual ~sg_throwable (); virtual ~sg_throwable ();
virtual const char* getMessage () const; virtual const char* getMessage () const;
virtual const std::string getFormattedMessage () const; virtual const std::string getFormattedMessage () const;
@@ -86,7 +92,7 @@ class sg_error : public sg_throwable
public: public:
sg_error (); sg_error ();
sg_error (const char* message, const char* origin = 0); sg_error (const char* message, const char* origin = 0);
sg_error (const std::string& message, const std::string& origin = ""); sg_error(const std::string &message, const std::string &origin = {});
virtual ~sg_error (); virtual ~sg_error ();
}; };
@@ -109,8 +115,10 @@ class sg_exception : public sg_throwable
{ {
public: public:
sg_exception (); sg_exception ();
sg_exception (const char* message, const char* origin = 0); sg_exception(const char *message, const char *origin = 0,
sg_exception (const std::string& message, const std::string& = ""); const sg_location &loc = {});
sg_exception(const std::string &message, const std::string & = {},
const sg_location &loc = {});
virtual ~sg_exception (); virtual ~sg_exception ();
}; };
@@ -193,6 +201,17 @@ public:
virtual ~sg_range_exception (); virtual ~sg_range_exception ();
}; };
using ThrowCallback = std::function<void(
const char *message, const char *origin, const sg_location &loc)>;
/**
* @brief Specify a callback to be invoked when an exception is created.
*
* This is used to capture a stack-trace for our crash/error reporting system,
* if a callback is defined
*/
void setThrowCallback(ThrowCallback cb);
#endif #endif
// end of exception.hxx // end of exception.hxx
+62 -44
View File
@@ -272,30 +272,35 @@ template<class T>
class SGBlockingDeque class SGBlockingDeque
{ {
public: public:
using value_type = T;
using container_type = std::deque<T>;
/** /**
* Create a new SGBlockingDequeue. * Create a new SGBlockingDequeue.
*/ */
SGBlockingDeque() {} SGBlockingDeque() = default;
/** /**
* Destroy this dequeue. * Destroy this dequeue.
*/ */
virtual ~SGBlockingDeque() {} ~SGBlockingDeque() = default;
/** /**
* *
*/ */
virtual void clear() { void clear()
std::lock_guard<std::mutex> g(mutex); {
this->queue.clear(); std::lock_guard<std::mutex> g(mutex);
this->queue.clear();
} }
/** /**
* *
*/ */
virtual bool empty() { bool empty() const
std::lock_guard<std::mutex> g(mutex); {
return this->queue.empty(); std::lock_guard<std::mutex> g(mutex);
return this->queue.empty();
} }
/** /**
@@ -303,10 +308,11 @@ public:
* *
* @param item The object to add. * @param item The object to add.
*/ */
virtual void push_front( const T& item ) { void push_front(const T& item)
std::lock_guard<std::mutex> g(mutex); {
this->queue.push_front( item ); std::lock_guard<std::mutex> g(mutex);
not_empty.signal(); this->queue.push_front(item);
not_empty.signal();
} }
/** /**
@@ -314,10 +320,11 @@ public:
* *
* @param item The object to add. * @param item The object to add.
*/ */
virtual void push_back( const T& item ) { void push_back(const T& item)
std::lock_guard<std::mutex> g(mutex); {
this->queue.push_back( item ); std::lock_guard<std::mutex> g(mutex);
not_empty.signal(); this->queue.push_back(item);
not_empty.signal();
} }
/** /**
@@ -326,14 +333,15 @@ public:
* *
* @return The next available object. * @return The next available object.
*/ */
virtual T front() { T front() const
std::lock_guard<std::mutex> g(mutex); {
std::lock_guard<std::mutex> g(mutex);
assert(this->queue.empty() != true); assert(this->queue.empty() != true);
//if (queue.empty()) throw ?? //if (queue.empty()) throw ??
T item = this->queue.front(); T item = this->queue.front();
return item; return item;
} }
/** /**
@@ -342,18 +350,19 @@ public:
* *
* @return The next available object. * @return The next available object.
*/ */
virtual T pop_front() { T pop_front()
std::lock_guard<std::mutex> g(mutex); {
std::lock_guard<std::mutex> g(mutex);
while (this->queue.empty()) while (this->queue.empty())
not_empty.wait(mutex); not_empty.wait(mutex);
assert(this->queue.empty() != true); assert(this->queue.empty() != true);
//if (queue.empty()) throw ?? //if (queue.empty()) throw ??
T item = this->queue.front(); T item = this->queue.front();
this->queue.pop_front(); this->queue.pop_front();
return item; return item;
} }
/** /**
@@ -362,18 +371,19 @@ public:
* *
* @return The next available object. * @return The next available object.
*/ */
virtual T pop_back() { T pop_back()
std::lock_guard<std::mutex> g(mutex); {
std::lock_guard<std::mutex> g(mutex);
while (this->queue.empty()) while (this->queue.empty())
not_empty.wait(mutex); not_empty.wait(mutex);
assert(this->queue.empty() != true); assert(this->queue.empty() != true);
//if (queue.empty()) throw ?? //if (queue.empty()) throw ??
T item = this->queue.back(); T item = this->queue.back();
this->queue.pop_back(); this->queue.pop_back();
return item; return item;
} }
/** /**
@@ -381,8 +391,9 @@ public:
* *
* @return Size of queue. * @return Size of queue.
*/ */
virtual size_t size() { size_t size() const
std::lock_guard<std::mutex> g(mutex); {
std::lock_guard<std::mutex> g(mutex);
return this->queue.size(); return this->queue.size();
} }
@@ -391,12 +402,19 @@ public:
while (this->queue.empty()) while (this->queue.empty())
not_empty.wait(mutex); not_empty.wait(mutex);
} }
container_type copy() const
{
std::lock_guard<std::mutex> g(mutex);
return queue;
}
private: private:
/** /**
* Mutex to serialise access. * Mutex to serialise access.
*/ */
std::mutex mutex; mutable std::mutex mutex;
/** /**
* Condition to signal when queue not empty. * Condition to signal when queue not empty.
@@ -409,7 +427,7 @@ private:
SGBlockingDeque& operator=( const SGBlockingDeque& ); SGBlockingDeque& operator=( const SGBlockingDeque& );
protected: protected:
std::deque<T> queue; container_type queue;
}; };
#endif // SGQUEUE_HXX_INCLUDED #endif // SGQUEUE_HXX_INCLUDED