Compare 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
James Turner 751cdc32a8 new version: 2020.2.1 2020-06-15 12:33:50 +01:00
Julian Smith 1d978429f5 simgear/props/props.cxx: Avoid possible SEGV e.g. if listener removes itself.
simgear/props/props_test.cxx: test for self-unregister.

SGPropertyNode::removeChangeListener() used to modify the _listeners vector or
even delete it and set _listeners to NULL, if a listener was removed.

This could cause a SEGV if removeChangeListener() was called from
a listener, because listeners are called from methods such as
SGPropertyNode::fireValueChanged() which iterate through _listeners and don't
expect it to be changed to NULL.

The fix here is to keep a track of whether we are iterating through the
_listeners vector; if we are, SGPropertyNode::removeChangeListener() sets the
entry to NULL, instead of removing it.

We have a new internal function forEachListener() which takes
care of the iterating details - incrementing/decrementing the new
_listeners->_num_iterators count and skipping NULL entries. Before returning,
if _num_iterators is now zero it removes any NULL entries in the vector of
listeners.

The nListeners() member is no longer inline, because it needs to ignore NULL
entries in the vector. And _listeners now points to an internally-defined
struct which contains a new _num_iterators as well as the vector of listeners.

Added self-unregister test to simgear/props/props_test.cxx.
2020-06-13 18:11:49 +01:00
James Turner 2b2e3ae5c4 Fix CMake value 2020-06-05 13:53:54 +01:00
James Turner 0e3ac7e078 Set CMake OpenGL VND policy 2020-06-05 13:32:43 +01:00
James Turner 01ab3b2385 New SGGeodesy::advance, taking degrees 2020-05-30 16:27:42 +01:00
James Turner 11cf87951c SIMD flags: ensure we respect other compiler flags
Ensure we only ever extend the currently set compile flags when
ENABLE_SIMD is requested
2020-05-28 10:46:18 +01:00
James Turner 7c004a4c90 Fix compilation with Boost >= 1.73 2020-05-28 10:45:47 +01:00
James Turner a48693d273 Kill explicit setting of compiler flags in Debug
Use the CMake defaults for this.
2020-05-24 14:32:13 +01:00
James Turner 12d57a6373 Adjust RelWithDebInfo optimisation for Clang
This should give a bit of speed boost to macOS official builds.
2020-05-13 14:23:00 +01:00
James Turner c7c8fc7777 Better RelWithDebInfo flags for CL.exe 2020-05-08 14:31:44 +01:00
James Turner 6e054e57ef Windows: ensure RelWithDebInfo is fast
Default CMake RelWithDebInfo pessimizes inlining
2020-05-07 14:13:54 +01:00
James Turner 1d89a76d13 Old-style texture compression: better check
Make even more sure new-style texture-compression is active, before
deciding which old-style mode to use.
2020-05-07 11:47:25 +01:00
James Turner 65925cccdf Enable old-style texture compression
Thanks to Dany for tracking this down!
2020-05-05 20:40:29 +01:00
James Turner 898559ab31 Add copyPropertiesIf helper 2020-05-03 12:21:36 +01:00
James Turner ad3621e23b Add Canvas::Image.dirtyPixels() 2020-04-28 09:36:40 +01:00
James Turner 57b0f70a55 Nasal: improve message for non-object member access
Based on some discussion on this ticket:

https://sourceforge.net/p/flightgear/codetickets/2186/

Make this message slightly clearer.
2020-04-28 09:36:40 +01:00
Erik Hofman 8d6e543cc2 Only use readwav for OpenAL 2020-04-28 09:41:11 +02:00
James Turner bd93fb279b new version: 2020.2.0 2020-04-27 09:29:55 +01:00
66 changed files with 1780 additions and 1089 deletions
+30 -29
View File
@@ -10,6 +10,12 @@ if(COMMAND cmake_policy)
if(POLICY CMP0067)
cmake_policy(SET CMP0067 NEW)
endif()
# OpenGL VND policy : use the old definition for now, until we can audit this
if(POLICY CMP0072)
cmake_policy(SET CMP0072 OLD)
endif()
if(POLICY CMP0093)
cmake_policy(SET CMP0093 NEW)
endif()
@@ -43,7 +49,7 @@ set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED YES)
# 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)
project(SimGear VERSION ${SIMGEAR_VERSION} LANGUAGES C CXX)
@@ -417,16 +423,8 @@ if(CMAKE_COMPILER_IS_GNUCXX)
message(WARNING "GCC 4.4 will be required soon, please upgrade")
endif()
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CMAKE_C_FLAGS
"${CMAKE_C_FLAGS} -O0 -fno-omit-frame-pointer -fno-inline")
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -O0 -fno-omit-frame-pointer -fno-inline")
elseif (ENABLE_SIMD)
if (X86 OR X86_64)
set(CMAKE_C_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse -ftree-vectorize -ftree-slp-vectorize")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse -ftree-vectorize -ftree-slp-vectorize")
endif()
if (X86 OR X86_64)
set(SIMD_COMPILER_FLAGS "-msse2 -mfpmath=sse -ftree-vectorize -ftree-slp-vectorize")
endif()
# certain GCC versions don't provide the atomic builds, and hence
@@ -435,6 +433,10 @@ if(CMAKE_COMPILER_IS_GNUCXX)
check_cxx_source_compiles(
"int main() { unsigned mValue; return __sync_add_and_fetch(&mValue, 1); }"
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)
if (CLANG)
@@ -449,16 +451,7 @@ if (CLANG)
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g -DNDEBUG")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O3 -g -DNDEBUG")
if (ENABLE_SIMD)
if (X86 OR X86_64)
set(CMAKE_C_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse -ftree-vectorize -ftree-slp-vectorize")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse -ftree-vectorize -ftree-slp-vectorize")
# propogate to the RelWithDebInfo flags
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELEASE} -g -DNDEBUG")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELEASE} -g -DNDEBUG")
endif()
endif()
set(SIMD_COMPILER_FLAGS "-msse2 -mfpmath=sse -ftree-vectorize -ftree-slp-vectorize")
endif()
if (ENABLE_OPENMP)
@@ -490,14 +483,8 @@ if(WIN32)
if(MSVC)
set(MSVC_FLAGS "-DWIN32 -DNOMINMAX -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS -D__CRT_NONSTDC_NO_WARNINGS /MP")
if(ENABLE_SIMD)
if (X86)
SET(CMAKE_C_FLAGS_RELEASE "/O2 /arch:SSE /arch:SSE2")
SET(CMAKE_CXX_FLAGS_RELEASE "/O2 /arch:SSE /arch:SSE2")
else()
SET(CMAKE_C_FLAGS_RELEASE "/O2")
SET(CMAKE_CXX_FLAGS_RELEASE "/O2")
endif()
if (X86)
set(SIMD_COMPILER_FLAGS "/arch:SSE /arch:SSE2")
endif()
if (NOT OSG_FSTREAM_EXPORT_FIXED)
@@ -521,6 +508,20 @@ if(WIN32)
set( RT_LIBRARY "winmm" )
endif(WIN32)
# append the SIMD flags if requested
if (ENABLE_SIMD)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SIMD_COMPILER_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SIMD_COMPILER_FLAGS}")
# set for multi-configuration generators
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${SIMD_COMPILER_FLAGS}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${SIMD_COMPILER_FLAGS}")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} ${SIMD_COMPILER_FLAGS}")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${SIMD_COMPILER_FLAGS}")
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS_C} ${MSVC_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS_CXX} ${MSVC_FLAGS} ${BOOST_CXX_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MSVC_LD_FLAGS}")
+1
View File
@@ -0,0 +1 @@
2020.2.1
+1 -1
View File
@@ -850,7 +850,7 @@ namespace canvas
void fillRow(GLubyte* row, GLuint pixel, GLuint width, GLuint pixelBytes)
{
GLubyte* dst = row;
for (int x = 0; x < width; ++x) {
for (GLuint x = 0; x < width; ++x) {
memcpy(dst, &pixel, pixelBytes);
dst += pixelBytes;
}
+1 -1
View File
@@ -73,7 +73,7 @@ namespace canvas
int stretch,
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.stretch = std::max(0, stretch);
+1 -1
View File
@@ -26,7 +26,7 @@
#include <vector>
#include <memory> // for std::unique_ptr
#include <simgear/debug/logstream.hxx>
#include <simgear/debug/LogCallback.hxx>
namespace simgear
{
+5 -2
View File
@@ -1,7 +1,10 @@
include (SimGearComponent)
set(HEADERS debug_types.h logstream.hxx BufferedLogCallback.hxx OsgIoCapture.hxx)
set(SOURCES logstream.cxx BufferedLogCallback.cxx)
set(HEADERS debug_types.h
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}")
+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>
using namespace osg;
#include <simgear/debug/logstream.hxx>
/**
* 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
* Define the various logging classes and priorities
*/
@@ -52,16 +54,17 @@ typedef enum {
* appended, or the priority Nasal reports to compiled code will change.
*/
typedef enum {
SG_BULK = 1, // For frequent messages
SG_DEBUG, // Less frequent debug type messages
SG_INFO, // Informatory messages
SG_WARN, // Possible impending problem
SG_ALERT, // Very possible impending problem
SG_POPUP, // Severe enough to alert using a pop-up window
SG_BULK = 1, // For frequent messages
SG_DEBUG, // Less frequent debug type messages
SG_INFO, // Informatory messages
SG_WARN, // Possible impending problem
SG_ALERT, // Very possible impending problem
SG_POPUP, // Severe enough to alert using a pop-up window
// SG_EXIT, // Problem (no core)
// SG_ABORT // Abandon ship (core)
SG_DEV_WARN, // Warning for developers, translated to other priority
SG_DEV_ALERT // Alert for developers, translated
} sgDebugPriority;
SG_DEV_WARN, // Warning for developers, translated to other priority
SG_DEV_ALERT, // Alert for developers, translated
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/SGQueue.hxx>
#include "LogCallback.hxx"
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/misc/strutils.hxx>
@@ -47,86 +48,9 @@
#include <io.h>
#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
{
@@ -139,8 +63,8 @@ public:
logTimer.stamp();
}
virtual void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& message)
void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& message) override
{
if (!shouldLog(c, p)) return;
@@ -196,8 +120,8 @@ public:
}
#endif
virtual void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& aMessage)
void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& aMessage) override
{
if (!shouldLog(c, p)) return;
//fprintf(stderr, "%s\n", aMessage.c_str());
@@ -226,8 +150,8 @@ public:
{
}
virtual void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& aMessage)
void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& aMessage) override
{
if (!shouldLog(c, p)) return;
@@ -242,28 +166,6 @@ public:
class logstream::LogStreamPrivate : public SGThread
{
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.
* used to safely make configuration changes.
@@ -401,13 +303,20 @@ public:
~LogStreamPrivate()
{
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;
SGBlockingQueue<LogEntry> m_entries;
SGBlockingQueue<simgear::LogEntry> m_entries;
// log entries posted during startup
std::vector<LogEntry> m_startupEntries;
std::vector<simgear::LogEntry> m_startupEntries;
bool m_startupLogging = false;
typedef std::vector<simgear::LogCallback*> CallbackVec;
@@ -430,6 +339,8 @@ public:
// test suite mode.
bool m_testMode = false;
std::vector<std::string> _popupMessages;
void startLog()
{
std::lock_guard<std::mutex> g(m_lock);
@@ -447,14 +358,19 @@ public:
{
std::lock_guard<std::mutex> g(m_lock);
m_startupLogging = on;
m_startupEntries.clear();
clearStartupEntriesLocked();
}
}
virtual void run()
void clearStartupEntriesLocked()
{
m_startupEntries.clear();
}
void run() override
{
while (1) {
LogEntry entry(m_entries.pop());
simgear::LogEntry entry(m_entries.pop());
// special marker entry detected, terminate the thread since we are
// making a configuration change or quitting the app
if ((entry.debugClass == SG_NONE) && entry.file && !strcmp(entry.file, "done")) {
@@ -470,8 +386,7 @@ public:
}
// submit to each installed callback in turn
for (simgear::LogCallback* cb : m_callbacks) {
(*cb)(entry.debugClass, entry.debugPriority,
entry.file, entry.line, entry.message);
cb->processEntry(entry);
}
} // of main thread loop
}
@@ -486,7 +401,7 @@ public:
// log a special marker value, which will cause the thread to wakeup,
// and then exit
log(SG_NONE, SG_ALERT, "done", -1, "");
log(SG_NONE, SG_ALERT, "done", -1, "", false);
}
join();
@@ -501,9 +416,8 @@ public:
// we clear startup entries not using this, so always safe to run
// this code, container will simply be empty
for (auto entry : m_startupEntries) {
(*cb)(entry.debugClass, entry.debugPriority,
entry.file, entry.line, entry.message);
for (const auto& entry : m_startupEntries) {
cb->processEntry(entry);
}
}
@@ -531,9 +445,9 @@ public:
PauseThread pause(this);
m_logPriority = p;
m_logClass = c;
for (auto cb : m_consoleCallbacks) {
cb->setLogLevels(c, p);
}
for (auto cb : m_consoleCallbacks) {
cb->setLogLevels(c, p);
}
}
bool would_log( sgDebugClass c, sgDebugPriority p ) const
@@ -551,14 +465,17 @@ public:
}
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) {
/* This prevents output of file:line in StderrLogCallback. */
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);
}
@@ -589,8 +506,8 @@ logstream::logstream()
logstream::~logstream()
{
popup_msgs.clear();
d->stop();
d.reset();
}
void
@@ -625,7 +542,14 @@ void
logstream::log( sgDebugClass c, sgDebugPriority p,
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
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
logstream::get_popup()
{
std::string rv = "";
if (!popup_msgs.empty())
{
rv = popup_msgs.front();
popup_msgs.erase(popup_msgs.begin());
std::string rv;
std::lock_guard<std::mutex> g(d->m_lock);
if (!d->_popupMessages.empty()) {
rv = d->_popupMessages.front();
d->_popupMessages.erase(d->_popupMessages.begin());
}
return rv;
}
@@ -704,7 +629,8 @@ logstream::get_popup()
bool
logstream::has_popup()
{
return (popup_msgs.size() > 0) ? true : false;
std::lock_guard<std::mutex> g(d->m_lock);
return !d->_popupMessages.empty();
}
bool
@@ -737,6 +663,16 @@ logstream::set_log_classes( sgDebugClass c)
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&
sglog()
+14 -22
View File
@@ -37,27 +37,8 @@ class SGPath;
namespace simgear
{
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;
};
class LogCallback;
/**
* Helper force a console on platforms where it might optional, when
* we need to show a console. This basically means Windows at the
@@ -105,6 +86,11 @@ public:
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
* are treated as warnings. In normal (non-developer) mode they are
@@ -124,6 +110,14 @@ public:
void log( sgDebugClass c, sgDebugPriority p,
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
*/
@@ -185,8 +179,6 @@ private:
// constructor
logstream();
std::vector<std::string> popup_msgs;
class LogStreamPrivate;
std::unique_ptr<LogStreamPrivate> d;
+2
View File
@@ -952,6 +952,8 @@ bool SGMetar::scanWeather()
weather += string(a->text) + " ";
if (!strcmp(a->id, "RA"))
_rain = w.intensity;
else if (!strcmp(a->id, "DZ"))
_rain = LIGHT;
else if (!strcmp(a->id, "HA"))
_hail = w.intensity;
else if (!strcmp(a->id, "SN"))
+31 -24
View File
@@ -105,6 +105,8 @@ public:
unsigned int bytesTransferred;
unsigned int lastTransferRate;
uint64_t totalBytesDownloaded;
SGPath tlsCertificatePath;
};
Client::Client() :
@@ -120,6 +122,8 @@ Client::Client() :
d->maxPipelineDepth = 5;
setUserAgent("SimGear-" SG_STRINGIZE(SIMGEAR_VERSION));
d->tlsCertificatePath = SGPath::fromEnv("SIMGEAR_TLS_CERT_PATH");
static bool didInitCurlGlobal = false;
static std::mutex initMutex;
@@ -161,6 +165,14 @@ void Client::setMaxPipelineDepth(unsigned int depth)
#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)
{
if (d->requests.empty()) {
@@ -171,32 +183,22 @@ void Client::update(int waitTimeout)
}
int remainingActive, messagesInQueue;
#if defined(SG_MAC)
// Mac 10.8 libCurl lacks this, let's keep compat for now
fd_set curlReadFDs, curlWriteFDs, curlErrorFDs;
int maxFD;
curl_multi_fdset(d->curlMulti,
&curlReadFDs,
&curlWriteFDs,
&curlErrorFDs,
&maxFD);
struct timeval timeout;
long t;
curl_multi_timeout(d->curlMulti, &t);
if ((t < 0) || (t > waitTimeout)) {
t = waitTimeout;
int numFds;
CURLMcode mc = curl_multi_wait(d->curlMulti, NULL, 0, waitTimeout, &numFds);
if (mc != CURLM_OK) {
SG_LOG(SG_IO, SG_WARN, "curl_multi_wait failed:" << curl_multi_strerror(mc));
return;
}
timeout.tv_sec = t / 1000;
timeout.tv_usec = (t % 1000) * 1000;
::select(maxFD, &curlReadFDs, &curlWriteFDs, &curlErrorFDs, &timeout);
#else
int numFds;
curl_multi_wait(d->curlMulti, NULL, 0, waitTimeout, &numFds);
#endif
curl_multi_perform(d->curlMulti, &remainingActive);
mc = curl_multi_perform(d->curlMulti, &remainingActive);
if (mc == CURLM_CALL_MULTI_PERFORM) {
// we could loop here, but don't want to get blocked
// also this shouldn't ocurr in any modern libCurl
curl_multi_perform(d->curlMulti, &remainingActive);
} else if (mc != CURLM_OK) {
SG_LOG(SG_IO, SG_WARN, "curl_multi_perform failed:" << curl_multi_strerror(mc));
return;
}
CURLMsg* msg;
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);
if (!d->tlsCertificatePath.isNull()) {
const auto utf8 = d->tlsCertificatePath.utf8Str();
curl_easy_setopt(curlRequest, CURLOPT_CAINFO, utf8.c_str());
}
if (!d->proxy.empty()) {
curl_easy_setopt(curlRequest, CURLOPT_PROXY, d->proxy.c_str());
curl_easy_setopt(curlRequest, CURLOPT_PROXYPORT, d->proxyPort);
+2
View File
@@ -47,6 +47,8 @@ public:
void update(int waitTimeout = 0);
void reset();
void makeRequest(const Request_ptr& r);
void cancelRequest(const Request_ptr& r, std::string reason = std::string());
+34 -26
View File
@@ -49,6 +49,7 @@ namespace simgear
{
class HTTPDirectory;
using HTTPDirectory_ptr = std::unique_ptr<HTTPDirectory>;
class HTTPRepoGetRequest : public HTTP::Request
{
@@ -137,7 +138,7 @@ public:
SGPath basePath;
bool isUpdating;
HTTPRepository::ResultCode status;
HTTPDirectory* rootDir;
HTTPDirectory_ptr rootDir;
size_t totalDownloaded;
HTTP::Request_ptr updateFile(HTTPDirectory* dir, const std::string& name,
@@ -165,7 +166,7 @@ public:
HTTPDirectory* getOrCreateDirectory(const std::string& path);
bool deleteDirectory(const std::string& relPath, const SGPath& absPath);
typedef std::vector<HTTPDirectory*> DirectoryVector;
typedef std::vector<HTTPDirectory_ptr> DirectoryVector;
DirectoryVector directories;
SGPath installedCopyPath;
@@ -416,7 +417,7 @@ public:
SG_LOG(SG_TERRASYNC, SG_WARN, "updated file but not found in dir:" << _relativePath << " " << file);
} else {
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
// middle of a server-side update, the downloaded file may actually become valid.
_repository->failedToUpdateChild(_relativePath, HTTPRepository::REPO_ERROR_CHECKSUM);
@@ -592,7 +593,7 @@ private:
void removeChild(SGPath path)
{
bool ok;
SG_LOG(SG_TERRASYNC, SG_WARN, "Removing:" << path);
SG_LOG(SG_TERRASYNC, SG_INFO, "Removing:" << path);
std::string fpath = _relativePath + "/" + path.file();
if (path.isDir()) {
@@ -626,7 +627,7 @@ HTTPRepository::HTTPRepository(const SGPath& base, HTTP::Client *cl) :
{
_d->http = cl;
_d->basePath = base;
_d->rootDir = new HTTPDirectory(_d.get(), "");
_d->rootDir.reset(new HTTPDirectory(_d.get(), ""));
_d->parseHashCache();
}
@@ -663,7 +664,7 @@ void HTTPRepository::update()
_d->status = REPO_NO_ERROR;
_d->isUpdating = true;
_d->failures.clear();
_d->updateDir(_d->rootDir, std::string(), 0);
_d->updateDir(_d->rootDir.get(), {}, 0);
}
bool HTTPRepository::isDoingSync() const
@@ -931,10 +932,7 @@ HTTPRepository::failure() const
http->cancelRequest(*rq, "Repository object deleted");
}
DirectoryVector::iterator it;
for (it=directories.begin(); it != directories.end(); ++it) {
delete *it;
}
directories.clear(); // wil delete them all
}
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)
{
if (!p.exists())
return std::string();
return {};
sha1nfo 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;
SGBinaryFile f(p);
if (!f.open(SG_IO_IN)) {
free(buf);
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);
}
@@ -1097,7 +1103,7 @@ HTTPRepository::failure() const
{
public:
DirectoryWithPath(const std::string& p) : path(p) {}
bool operator()(const HTTPDirectory* entry) const
bool operator()(const HTTPDirectory_ptr& entry) const
{ return entry->relativePath() == path; }
private:
std::string path;
@@ -1106,14 +1112,13 @@ HTTPRepository::failure() const
HTTPDirectory* HTTPRepoPrivate::getOrCreateDirectory(const std::string& 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()) {
return *it;
return it->get();
}
HTTPDirectory* d = new HTTPDirectory(this, path);
directories.push_back(d);
return d;
directories.emplace_back(new HTTPDirectory(this, path));
return directories.back().get();
}
bool HTTPRepoPrivate::deleteDirectory(const std::string& relPath, const SGPath& absPath)
@@ -1121,10 +1126,9 @@ HTTPRepository::failure() const
DirectoryWithPath p(relPath);
auto it = std::find_if(directories.begin(), directories.end(), p);
if (it != directories.end()) {
HTTPDirectory* d = *it;
HTTPDirectory* d = it->get();
assert(d->absolutePath() == absPath);
directories.erase(it);
delete d;
} else {
// we encounter this code path when deleting an orphaned directory
}
@@ -1173,8 +1177,12 @@ HTTPRepository::failure() const
void HTTPRepoPrivate::failedToGetRootIndex(HTTPRepository::ResultCode st)
{
SG_LOG(SG_TERRASYNC, SG_WARN, "Failed to get root of repo:" << baseUrl << " " << st);
status = st;
if (st == HTTPRepository::REPO_ERROR_FILE_NOT_FOUND) {
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,
@@ -1201,15 +1209,15 @@ HTTPRepository::failure() const
} else if (fileStatus == HTTPRepository::REPO_ERROR_CANCELLED) {
// if we were cancelled, don't report or log
return;
} else {
SG_LOG(SG_TERRASYNC, SG_WARN, "failed to update entry:" << relativePath << " status/code: "
<< innerResultCodeAsString(fileStatus) << "/" << fileStatus);
}
Failure f;
f.path = relativePath;
f.error = fileStatus;
failures.push_back(f);
SG_LOG(SG_TERRASYNC, SG_WARN, "failed to update entry:" << relativePath << " status/code: "
<< innerResultCodeAsString(fileStatus) << "/" << fileStatus);
}
} // of namespace simgear
+24 -10
View File
@@ -193,13 +193,16 @@ void Request::onDone()
//------------------------------------------------------------------------------
void Request::onFail()
{
SG_LOG
(
SG_IO,
SG_INFO,
"request failed:" << url() << " : "
<< responseCode() << "/" << responseReason()
);
// log if we FAIELD< but not if we CANCELLED
if (_ready_state == FAILED) {
SG_LOG
(
SG_IO,
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)
{
// 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);
}
_responseStatus = code;
_responseReason = reason;
if( !isComplete() )
setReadyState(FAILED);
if( !isComplete() ) {
setReadyState(code < 0 ? CANCELLED : FAILED);
}
}
//------------------------------------------------------------------------------
@@ -373,6 +381,12 @@ void Request::setReadyState(ReadyState state)
_cb_fail(this);
}
else if (state == CANCELLED )
{
onFail(); // do this for compatability
onAlways();
_cb_fail(this);
}
else
return;
@@ -402,7 +416,7 @@ bool Request::serverSupportsPipelining() 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,
LOADING,
DONE,
FAILED
FAILED,
CANCELLED
};
virtual ~Request();
+7
View File
@@ -537,6 +537,13 @@ SGGeodesy::advanceRadM(const SGGeoc& geoc, double course, double distance,
}
}
SGGeoc SGGeodesy::advanceDegM(const SGGeoc &geoc, double course,
double distance) {
SGGeoc result;
advanceRadM(geoc, course * SG_DEGREES_TO_RADIANS, distance, result);
return result;
}
double
SGGeodesy::courseRad(const SGGeoc& from, const SGGeoc& to)
{
+3
View File
@@ -65,6 +65,9 @@ public:
// Geocentric course/distance computation
static void advanceRadM(const SGGeoc& geoc, double course, double distance,
SGGeoc& result);
static SGGeoc advanceDegM(const SGGeoc &geoc, double course, double distance);
static double courseRad(const SGGeoc& from, const SGGeoc& to);
static double distanceRad(const SGGeoc& from, const SGGeoc& to);
static double distanceM(const SGGeoc& from, const SGGeoc& to);
+11
View File
@@ -56,6 +56,15 @@ ResourceManager::~ResourceManager()
std::for_each(_providers.begin(), _providers.end(),
[](ResourceProvider* p) { delete p; });
}
void ResourceManager::reset()
{
if (static_manager) {
delete static_manager;
static_manager = nullptr;
}
}
/**
* 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");
return;
}
_providers.erase(it);
}
SGPath ResourceManager::findPath(const std::string& aResource, SGPath aContext)
+4 -2
View File
@@ -45,8 +45,10 @@ public:
PRIORITY_HIGH = 1000
} Priority;
static ResourceManager* instance();
static ResourceManager* instance();
static void reset();
/**
* add a simple fixed resource location, to resolve against
*/
+32
View File
@@ -267,6 +267,38 @@ namespace simgear {
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
stripTrailingNewlines_inplace(string& s)
{
+3 -1
View File
@@ -75,7 +75,9 @@ namespace simgear {
std::string rstrip( 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.
* Typically useful to clean a CR-terminated line obtained from
* std::getline() which, upon reading CRLF (\\r\\n), discards the Line
+7 -1
View File
@@ -737,6 +737,11 @@ void testDecodeHex()
SG_VERIFY(decoded == data1);
}
void test_makeStringSafeForPropertyName()
{
SG_CHECK_EQUAL(strutils::makeStringSafeForPropertyName(" ABC/01234\t:\\\"_*$"), "-ABC-01234-_-_-__");
}
int main(int argc, char* argv[])
{
test_strip();
@@ -761,6 +766,7 @@ int main(int argc, char* argv[])
test_formatGeod();
test_iequals();
testDecodeHex();
test_makeStringSafeForPropertyName();
return EXIT_SUCCESS;
}
+1 -1
View File
@@ -36,7 +36,7 @@ void naRuntimeError(naContext c, const char* fmt, ...)
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;
longjmp(subc->callParent->jumpHandle, 1);
}
+1
View File
@@ -21,6 +21,7 @@
#include "NasalString.hxx"
#include <cassert>
#include <stdexcept> // for std::runtime_error
namespace nasal
{
+1 -1
View File
@@ -7,7 +7,7 @@
#include "iolib.h"
static void ghostDestroy(void* g);
naGhostType naIOGhostType = { ghostDestroy, "iofile" };
naGhostType naIOGhostType = { ghostDestroy, "iofile", NULL, NULL };
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 {
arg = naNumValue(arg);
if(naIsNil(arg))
fout = dosprintf(fstr, "nil");
fout = dosprintf("nil");
else if(t=='d' || t=='i' || t=='c')
fout = dosprintf(fstr, (int)naNumValue(arg).num);
else if(t=='o' || t=='u' || t=='x' || t=='X')
+2 -2
View File
@@ -9,10 +9,10 @@
#include "code.h"
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 naGhostType SemType = { semDestroy };
static naGhostType SemType = { semDestroy, NULL, NULL, NULL };
typedef struct {
naContext ctx;
+10 -6
View File
@@ -595,13 +595,17 @@ void Catalog::setUserEnabled(bool b)
m_userEnabled = b;
SGPath disableMarkerFile = installRoot() / "_disabled_";
if (m_userEnabled) {
sg_ofstream of(disableMarkerFile);
of << "1\n"; // touch the file
if (m_userEnabled == false) {
sg_ofstream of(disableMarkerFile, std::ios::trunc | std::ios::out);
of << "1" << std::flush; // touch the file
of.close();
} else {
bool ok = disableMarkerFile.remove();
if (!ok) {
SG_LOG(SG_GENERAL, SG_ALERT, "Failed to remove catalog-disable marker file:" << disableMarkerFile);
if (disableMarkerFile.exists()) {
const bool ok = disableMarkerFile.remove();
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 {
Package::Package(const SGPropertyNode* aProps, CatalogRef aCatalog) :
m_catalog(aCatalog)
m_catalog(aCatalog.get())
{
initWithProps(aProps);
}
@@ -235,6 +235,11 @@ std::string Package::id() const
return m_id;
}
CatalogRef Package::catalog() const
{
return {m_catalog};
}
std::string Package::qualifiedId() const
{
return m_catalog->id() + "." + id();
+2 -3
View File
@@ -130,8 +130,7 @@ public:
size_t fileSizeBytes() const;
CatalogRef catalog() const
{ return m_catalog; }
CatalogRef catalog() const;
bool matches(const SGPropertyNode* aFilter) const;
@@ -236,7 +235,7 @@ private:
SGPropertyNode_ptr m_props;
std::string m_id;
string_set m_tags;
CatalogRef m_catalog;
Catalog* m_catalog = nullptr; // non-owning ref, explicitly
string_list m_variants;
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);
}
if ((aReason == Delegate::STATUS_REFRESHED) && (catIt == d->catalogs.end())) {
if (aCat->isUserEnabled() &&
(aReason == Delegate::STATUS_REFRESHED) &&
(catIt == d->catalogs.end()))
{
assert(!aCat->id().empty());
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(),
d->disabledCatalogs.end(),
aCat);
d->disabledCatalogs.end(),
aCat);
if (j != d->disabledCatalogs.end()) {
SG_LOG(SG_GENERAL, SG_INFO, "re-enabling disabled catalog:" << aCat->id());
d->disabledCatalogs.erase(j);
@@ -705,7 +708,7 @@ void Root::catalogRefreshStatus(CatalogRef aCat, Delegate::StatusCode aReason)
}
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(),
d->disabledCatalogs.end(),
aCat);
+119 -27
View File
@@ -48,6 +48,18 @@ using std::stringstream;
using namespace simgear;
struct SGPropertyNodeListeners
{
/* This keeps a count of the current number of nested invocations of
forEachListener(). If non-zero, other code higher up the stack is iterating
_items[] so for example code must not erase items in the vector. */
int _num_iterators = 0;
std::vector<SGPropertyChangeListener *> _items;
};
////////////////////////////////////////////////////////////////////////
// Local classes.
////////////////////////////////////////////////////////////////////////
@@ -968,7 +980,7 @@ SGPropertyNode::~SGPropertyNode ()
if (_listeners) {
vector<SGPropertyChangeListener*>::iterator it;
for (it = _listeners->begin(); it != _listeners->end(); ++it)
for (it = _listeners->_items.begin(); it != _listeners->_items.end(); ++it)
(*it)->unregister_property(this);
delete _listeners;
}
@@ -2392,8 +2404,21 @@ SGPropertyNode::addChangeListener (SGPropertyChangeListener * listener,
bool initial)
{
if (_listeners == 0)
_listeners = new vector<SGPropertyChangeListener*>;
_listeners->push_back(listener);
_listeners = new SGPropertyNodeListeners;
/* If there's a nullptr entry (a listener that was unregistered), we
overwrite it. This ensures that listeners that routinely unregister+register
themselves don't make _listeners->_items grow unnecessarily. Otherwise simply
append. */
auto it = std::find(_listeners->_items.begin(), _listeners->_items.end(),
(SGPropertyChangeListener*) nullptr
);
if (it == _listeners->_items.end()) {
_listeners->_items.push_back(listener);
}
else {
*it = listener;
}
listener->register_property(this);
if (initial)
listener->valueChanged(this);
@@ -2405,14 +2430,29 @@ SGPropertyNode::removeChangeListener (SGPropertyChangeListener * listener)
if (_listeners == 0)
return;
vector<SGPropertyChangeListener*>::iterator it =
find(_listeners->begin(), _listeners->end(), listener);
if (it != _listeners->end()) {
_listeners->erase(it);
listener->unregister_property(this);
if (_listeners->empty()) {
vector<SGPropertyChangeListener*>* tmp = _listeners;
_listeners = 0;
delete tmp;
find(_listeners->_items.begin(), _listeners->_items.end(), listener);
if (it != _listeners->_items.end()) {
if (_listeners->_num_iterators) {
/* _listeners._items is currently being iterated further up the stack in
this thread by one or more nested invocations of forEachListener(), so
we must be careful to not break these iterators. So we must not delete
_listeners. And we must not erase this entry from the vector because that
could cause the next listener to be missed out.
So we simply set this entry to nullptr (nullptr items are skipped by
forEachListener()). When all invocations of forEachListener() have
finished iterating and it is safe to modify things again, it will clean
up any nullptr entries in _listeners->_items. */
*it = nullptr;
listener->unregister_property(this);
}
else {
_listeners->_items.erase(it);
listener->unregister_property(this);
if (_listeners->_items.empty()) {
delete _listeners;
_listeners = 0;
}
}
}
}
@@ -2461,15 +2501,67 @@ SGPropertyNode::fireChildrenRemovedRecursive()
}
}
/* Calls <callback> for each item in _listeners. We are careful to skip nullptr
entries in _listeners->items[], which can be created if listeners are removed
while we are iterating. */
static void forEachListener(
SGPropertyNode* node,
SGPropertyNodeListeners*& _listeners,
std::function<void (SGPropertyChangeListener*)> callback
)
{
if (!_listeners) return;
_listeners->_num_iterators += 1;
/* We need to use an index here when iterating _listeners->_items, not an
iterator. This is because a listener may add new listeners, causing the
vector to be reallocated, which would invalidate any iterator. */
for (size_t i = 0; i < _listeners->_items.size(); ++i) {
auto listener = _listeners->_items[i];
if (listener) {
try {
callback(listener);
}
catch (std::exception& e) {
SG_LOG(SG_GENERAL, SG_ALERT, "Ignoring exception from property callback: " << e.what());
}
}
}
_listeners->_num_iterators -= 1;
if (_listeners->_num_iterators == 0) {
/* Remove any items that have been set to nullptr. */
_listeners->_items.erase(
std::remove(_listeners->_items.begin(), _listeners->_items.end(), (SGPropertyChangeListener*) nullptr),
_listeners->_items.end()
);
if (_listeners->_items.empty()) {
delete _listeners;
_listeners = nullptr;
}
}
}
int SGPropertyNode::nListeners() const
{
if (!_listeners) return 0;
int n = 0;
for (auto listener: _listeners->_items) {
if (listener) n += 1;
}
return n;
}
void
SGPropertyNode::fireValueChanged (SGPropertyNode * node)
{
if (_listeners != 0) {
for (unsigned int i = 0; i < _listeners->size(); i++) {
if ((*_listeners)[i])
(*_listeners)[i]->valueChanged(node);
}
}
forEachListener(
node,
_listeners,
[&](SGPropertyChangeListener* listener) { listener->valueChanged(node);}
);
if (_parent != 0)
_parent->fireValueChanged(node);
}
@@ -2478,11 +2570,11 @@ void
SGPropertyNode::fireChildAdded (SGPropertyNode * parent,
SGPropertyNode * child)
{
if (_listeners != 0) {
for (unsigned int i = 0; i < _listeners->size(); i++) {
(*_listeners)[i]->childAdded(parent, child);
}
}
forEachListener(
parent,
_listeners,
[&](SGPropertyChangeListener* listener) { listener->childAdded(parent, child);}
);
if (_parent != 0)
_parent->fireChildAdded(parent, child);
}
@@ -2491,11 +2583,11 @@ void
SGPropertyNode::fireChildRemoved (SGPropertyNode * parent,
SGPropertyNode * child)
{
if (_listeners != 0) {
for (unsigned int i = 0; i < _listeners->size(); i++) {
(*_listeners)[i]->childRemoved(parent, child);
}
}
forEachListener(
parent,
_listeners,
[&](SGPropertyChangeListener* listener) { listener->childRemoved(parent, child);}
);
if (_parent != 0)
_parent->fireChildRemoved(parent, child);
}
+11 -7
View File
@@ -759,6 +759,9 @@ private:
};
struct SGPropertyNodeListeners;
/**
* A node in a property tree.
*/
@@ -1713,7 +1716,7 @@ public:
/**
* Get the number of listeners.
*/
int nListeners () const { return _listeners ? (int)_listeners->size() : 0; }
int nListeners () const;
/**
@@ -1847,7 +1850,7 @@ private:
char * string_val;
} _local_val;
std::vector<SGPropertyChangeListener *> * _listeners;
SGPropertyNodeListeners* _listeners;
// Pass name as a pair of iterators
template<typename Itr>
@@ -2194,11 +2197,12 @@ public:
_property->addChangeListener(this,initial);
}
SGPropertyChangeCallback(const SGPropertyChangeCallback<T>& other) :
_obj(other._obj), _callback(other._callback), _property(other._property)
{
_property->addChangeListener(this,false);
}
SGPropertyChangeCallback(const SGPropertyChangeCallback<T>& other)
: SGPropertyChangeListener(other),
_obj(other._obj), _callback(other._callback), _property(other._property)
{
_property->addChangeListener(this,false);
}
virtual ~SGPropertyChangeCallback()
{
+44
View File
@@ -854,4 +854,48 @@ copyPropertiesWithAttribute(const SGPropertyNode *in, SGPropertyNode *out,
return true;
}
namespace {
// for normal recursion we want to call the predicate *before* creating children
bool _inner_copyPropertiesIf(const SGPropertyNode *in, SGPropertyNode *out,
PropertyPredicate predicate)
{
bool retval = copyPropertyValue(in, out);
if (!retval) {
return false;
}
out->setAttributes( in->getAttributes() );
int nChildren = in->nChildren();
for (int i = 0; i < nChildren; i++) {
const SGPropertyNode* in_child = in->getChild(i);
// skip this child
if (!predicate(in_child)) {
continue;
}
SGPropertyNode* out_child = out->getChild(in_child->getNameString(),
in_child->getIndex(),
true);
bool ok = copyPropertiesIf(in_child, out_child, predicate);
if (!ok) {
return false;
}
} // of children iteration
return true;
}
} // of anonymous namespace
bool copyPropertiesIf(const SGPropertyNode *in, SGPropertyNode *out,
PropertyPredicate predicate)
{
// allow the *entire* copy to be a no-op
bool doCopy = predicate(in);
if (!doCopy)
return true; // doesn't count as failure
return _inner_copyPropertiesIf(in, out, predicate);
}
// end of props_io.cxx
+11
View File
@@ -17,6 +17,7 @@
#include <string>
#include <iosfwd>
#include <functional>
/**
* Read properties from an XML input stream.
@@ -67,6 +68,16 @@ bool copyProperties (const SGPropertyNode *in, SGPropertyNode *out);
bool copyPropertiesWithAttribute(const SGPropertyNode *in, SGPropertyNode *out,
SGPropertyNode::Attribute attr);
using PropertyPredicate = std::function<bool (const SGPropertyNode* in)>;
/**
* Copy properties, if the predicate returns true for the in node.
* If a parent node returns false, descendats will <em>not</em> be
* checked
*/
bool copyPropertiesIf(const SGPropertyNode *in, SGPropertyNode *out,
PropertyPredicate predicate);
#endif // __PROPS_IO_HXX
// end of props_io.hxx
+14 -2
View File
@@ -451,13 +451,17 @@ void defineSamplePropertyTree(SGPropertyNode_ptr root)
class TestListener : public SGPropertyChangeListener
{
public:
TestListener(SGPropertyNode* root, bool recursive = false) :
_root(root) {}
TestListener(SGPropertyNode* root, bool recursive = false, bool self_unregister = false) :
_root(root), _self_unregister(self_unregister) {}
virtual void valueChanged(SGPropertyNode* node) override
{
std::cout << "saw value changed for:" << node->getPath() << std::endl;
valueChangeCount[node]++;
if (_self_unregister) {
std::cout << "valueChanged(): calling removeChangeListener() to self-remove\n";
node->removeChangeListener(this);
}
}
int checkValueChangeCount(const std::string& path) const
@@ -533,6 +537,7 @@ public:
}
private:
SGPropertyNode* _root;
bool _self_unregister;
std::map<SGPropertyNode*, unsigned int> valueChangeCount;
std::vector<ParentChange> adds;
@@ -942,6 +947,13 @@ void testDeleterListener()
SG_VERIFY(ensureNListeners(tree, 0));
}
// Self-unregister. prior to 2019-07-08 this would segv.
{
std::shared_ptr<TestListener> l1( new TestListener(tree.get(), true /* recursive */, true /*self_unregister*/));
tree->setFloatValue("position/body/sub/self-unregister", 0.1);
tree->getNode("position/body/sub/self-unregister")->addChangeListener(l1.get());
tree->setFloatValue("position/body/sub/self-unregister", 0.2);
}
}
int main (int ac, char ** av)
+34 -32
View File
@@ -28,7 +28,7 @@
#include <simgear/compiler.h>
#include <map>
#include <mutex>
#include <osg/AlphaFunc>
#include <osg/Group>
@@ -103,37 +103,39 @@ SGMatModel::get_model_count( SGPropertyNode *prop_root )
inline void
SGMatModel::load_models( SGPropertyNode *prop_root )
{
// Load model only on demand
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) {
// 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());
}
}
std::lock_guard<std::mutex> g(_loadMutex);
// Load model only on demand
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) {
// 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_loaded = true;
}
+2
View File
@@ -149,6 +149,8 @@ private:
double _spacing_m;
double _range_m;
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)
{
case(GL_DEPTH_COMPONENT): //intentionally fall through and execute the code for GL_LUMINANCE
case(GL_LUMINANCE): { *data++ = value.r()*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_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_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_LUMINANCE): { *data = value.r()*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_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_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; }
}
}
@@ -265,7 +265,13 @@ osg::Image* computeMipmap( osg::Image* image, MipMapTuple attrs )
{
bool computeMipmap = false;
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<2>(attrs) != AUTOMATIC || nbComponents < 3 ) &&
( std::get<3>(attrs) != AUTOMATIC || nbComponents < 4 ) )
@@ -283,9 +289,7 @@ osg::Image* computeMipmap( osg::Image* image, MipMapTuple attrs )
if ( computeMipmap )
{
osg::ref_ptr<osg::Image> mipmaps = new osg::Image();
int s = image->s(),
t = image->t(),
r = image->r();
int r = image->r();
int nb = osg::Image::computeNumberOfMipmapLevels(s, t, r);
osg::Image::MipmapDataType mipmapOffsets;
unsigned int offset = 0;
+7
View File
@@ -359,6 +359,8 @@ ModelRegistry::readImage(const string& fileName,
if (res.validImage()) {
osg::ref_ptr<osg::Image> srcImage = res.getImage();
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 isNormalMap = false;
bool isEffect = false;
@@ -367,6 +369,11 @@ ModelRegistry::readImage(const string& fileName,
*/
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();
// use the new file origin to determine any special processing
+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;
particle_nodes = props->getChildren("particlesystem");
for (unsigned i = 0; i < particle_nodes.size(); ++i) {
@@ -503,9 +504,9 @@ sgLoad3DModel_internal(const SGPath& path,
options2->setDatabasePath(texturepath.utf8Str());
}
group->addChild(Particles::appendParticles(particle_nodes[i],
prop_root,
options2.get()));
group->addChild(particlesManager->appendParticles(particle_nodes[i],
prop_root,
options2.get()));
}
}
+319 -124
View File
@@ -17,9 +17,12 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifdef HAVE_CONFIG_H
# include <simgear_config.h>
#endif
#include <simgear_config.h>
#include "particles.hxx"
#include <mutex>
#include <simgear/misc/sg_path.hxx>
#include <simgear/props/props.hxx>
@@ -42,53 +45,122 @@
#include <osg/MatrixTransform>
#include <osg/Node>
#include "particles.hxx"
#include <simgear/scene/model/animation.hxx>
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,
// "wind vector:" << w[0] << "," <<w[1] << "," << w[2]);
class ParticlesGlobalManager::ParticlesGlobalManagerPrivate : public osg::NodeCallback
{
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();
}
//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)
void ParticlesGlobalManager::clear()
{
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>
class PointerGuard{
public:
PointerGuard() : _ptr(0) {}
Object* get() { return _ptr; }
Object* operator () ()
{
@@ -97,24 +169,9 @@ public:
return _ptr;
}
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,
const osg::Matrix& mat)
{
@@ -129,10 +186,182 @@ void transformParticles(osgParticle::ParticleSystem* particleSys,
}
}
osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot,
const osgDB::Options*
options)
void Particles::operator()(osg::Node* node, osg::NodeVisitor* nv)
{
auto globalManager = ParticlesGlobalManager::instance();
//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,
"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)
<< std::noboolalpha);
osgParticle::ParticleSystem *particleSys;
osg::ref_ptr<osgParticle::ParticleSystem> particleSys;
//create a generic particle system
std::string type = configNode->getStringValue("type", "normal");
@@ -160,11 +389,10 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
particleSys = new osgParticle::ParticleSystem;
else
particleSys = new osgParticle::ConnectedParticleSystem;
//may not be used depending on the configuration
PointerGuard<Particles> callback;
getPSU()->addParticleSystem(particleSys);
getPSU()->setUpdateCallback(new GlobalParticleCallback(modelRoot));
//contains counter, placer and shooter by default
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)
// 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;
res_matrix.makeRotate(
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/z-m", 0.0));
align->setMatrix(res_matrix * tmat);
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);
//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;
g->addDrawable(particleSys);
callback()->particleFrame->addChild(g);
getCommonRoot()->addChild(callback()->particleFrame.get());
}
std::string textureFile;
if (configNode->hasValue("texture")) {
@@ -505,66 +728,38 @@ osg::Group * Particles::appendParticles(const SGPropertyNode* configNode,
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;
}
void Particles::operator()(osg::Node* node, osg::NodeVisitor* nv)
void ParticlesGlobalManager::setSwitchNode(const SGPropertyNode* n)
{
//SG_LOG(SG_PARTICLES, SG_ALERT, "callback!\n");
this->particleSys->setFrozen(_frozen);
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);
std::lock_guard<std::mutex> g(d->_lock);
d->_enabledNode = n;
}
void ParticlesGlobalManager::setFrozen(bool b)
{
std::lock_guard<std::mutex> g(d->_lock);
d->_frozen = b;
}
} // namespace simgear
+58 -185
View File
@@ -1,5 +1,5 @@
// 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
// modify it under the terms of the GNU General Public License as
@@ -53,133 +53,37 @@ class ParticleSystemUpdater;
#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
{
class GlobalParticleCallback : public osg::NodeCallback
{
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 ParticlesGlobalManager;
class Particles : public osg::NodeCallback
{
public:
Particles();
Particles() = default;
static osg::Group * appendParticles(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, const osgDB::Options* options);
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
void operator()(osg::Node* node, osg::NodeVisitor* nv) override;
void setupShooterSpeedData(const SGPropertyNode* configNode,
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);
}
SGPropertyNode* modelRoot);
void setupCounterData(const SGPropertyNode* configNode,
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);
}
SGPropertyNode* modelRoot);
void setupCounterCondition(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot)
{
counterCond = sgReadCondition(modelRoot, configNode);
}
SGPropertyNode* modelRoot);
void setupCounterCondition(float counterStaticValue,
float counterStaticExtraRange)
{
this->counterStaticValue = counterStaticValue;
this->counterStaticExtraRange = counterStaticExtraRange;
}
float counterStaticExtraRange);
void setupStartSizeData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot)
{
startSizeValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if(!startSizeValue)
{
SG_LOG(SG_GENERAL, SG_ALERT, "startSizeValue error!\n");
}
}
SGPropertyNode* modelRoot);
void setupEndSizeData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot)
{
endSizeValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if(!endSizeValue){
SG_LOG(SG_GENERAL, SG_ALERT, "startSizeValue error!\n");
}
}
SGPropertyNode* modelRoot);
void setupLifeData(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot)
{
lifeValue = read_value(configNode, modelRoot, "-m",
-SGLimitsd::max(), SGLimitsd::max());
if(!lifeValue){
SG_LOG(SG_GENERAL, SG_ALERT, "lifeValue error!\n");
}
}
SGPropertyNode* modelRoot);
void setupStaticSizeData(float startSize, float endSize)
{
@@ -212,60 +116,14 @@ public:
//component: r=0, g=1, ... ; color: 0=start color, 1=end color
void setupColorComponent(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot, int color,
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
}
int component);
void 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 setupStaticColorComponent(float r1, float g1, float b1, float a1,
float r2, float g2, float b2, float 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:
friend class ParticlesGlobalManager;
float shooterExtraRange;
float counterExtraRange;
SGSharedPtr<SGExpressiond> shooterValue;
@@ -285,39 +143,54 @@ protected:
osg::ref_ptr<osgParticle::ParticleSystem> particleSys;
osg::ref_ptr<osgParticle::FluidProgram> program;
osg::ref_ptr<osg::MatrixTransform> particleFrame;
bool useGravity;
bool useWind;
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;
bool useGravity = false;
bool useWind = false;
};
}
#endif
/*
class CustomModularEmitter : public osgParticle::ModularEmitter, public osg::Observer
class ParticlesGlobalManager
{
public:
~ParticlesGlobalManager();
virtual void objectDeleted (void *)
{
SG_LOG(SG_GENERAL, SG_ALERT, "object deleted!\n");
static ParticlesGlobalManager* instance();
static void clear();
SGParticles::getCommonRoot()->removeChild(this->getParticleSystem()->getParent(0));
SGParticles::getPSU()->removeParticleSystem(this->getParticleSystem());
}
bool isEnabled() const;
osg::ref_ptr<osg::Group> appendParticles(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, const osgDB::Options* options);
// ~CustomModularEmitter()
// {
// SG_LOG(SG_GENERAL, SG_ALERT, "object deleted!\n");
//
// SGParticles::getCommonRoot()->removeChild(this->getParticleSystem()->getParent(0));
// SGParticles::getPSU()->removeParticleSystem(this->getParticleSystem());
// }
osg::Group* getCommonRoot();
osg::Geode* getCommonGeode();
osgParticle::ParticleSystemUpdater* getPSU();
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())
group->addChild(signBuilder.getSignsGroup());
if (_buildingList.size() > 0) {
if (!_buildingList.empty()) {
SGMaterialLibPtr matlib = _options->getMaterialLib();
bool useVBOs = (_options->getPluginStringData("SimGear::USE_VBOS") == "ON");
if (!matlib) {
SG_LOG( SG_TERRAIN, SG_ALERT, "Unable to get materials definition for buildings");
} 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
SGGeod geodPos = SGGeod::fromDegM(i->_lon, i->_lat, 0.0);
SGMaterial* mat = matlib->find(i->_material_name, geodPos);
SGPath path = SGPath(i->_filename);
SGGeod geodPos = SGGeod::fromDegM(b._lon, b._lat, 0.0);
SGSharedPtr<SGMaterial> mat = matlib->find(b._material_name, geodPos);
// 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);
SGBuildingBinList buildingBinList;
buildingBinList.push_back(buildingBin);
SGBuildingBinList bbList;
bbList.push_back(buildingBin);
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->setDataVariance(osg::Object::STATIC);
matrixTransform->addChild(createRandomBuildings(buildingBinList, osg::Matrix::identity(), _options));
matrixTransform->addChild(createRandomBuildings(bbList, osg::Matrix::identity(), _options));
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)
// 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.
float x, y, z, r, w, d, h, p;
int b, s, o, f, wt, rt;
in >> x >> y >> z >> r >> b >> w >> d >> h >> p >> s >> o >> f >> wt >> rt;
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 = 0, s = 0, o = 0, f = 0, wt = 0, rt = 0;
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 );
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
SGBuildingBin::SGBuildingBin(const SGMaterial *mat, bool useVBOs) {
material_name = new std::string(mat->get_names()[0]);
SG_LOG(SG_TERRAIN, SG_DEBUG, "Building material " << material_name);
material = mat;
texture = new std::string(mat->get_building_texture());
lightMap = new std::string(mat->get_building_lightmap());
buildingRange = mat->get_building_range();
SG_LOG(SG_TERRAIN, SG_DEBUG, "Building texture " << texture);
SGBuildingBin::SGBuildingBin(const SGMaterial* mat, bool useVBOs) : _material(const_cast<SGMaterial*>(mat))
{
const auto& materialNames = mat->get_names();
if (materialNames.empty()) {
SG_LOG(SG_TERRAIN, SG_DEV_ALERT, "SGBuildingBin: material has zero names defined");
} else {
_materialName = materialNames.front();
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() {
@@ -644,15 +658,15 @@ typedef QuadTreeBuilder<LOD*, SGBuildingBin::BuildingInstance, MakeBuildingLeaf,
if (buildingtype == SGBuildingBin::SMALL) {
// Small building
// 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());
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()));
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());
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));
// Small buildings are never deeper than they are wide.
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) {
roof_shape = 0;
@@ -663,18 +677,18 @@ typedef QuadTreeBuilder<LOD*, SGBuildingBin::BuildingInstance, MakeBuildingLeaf,
}
} else if (buildingtype == SGBuildingBin::MEDIUM) {
// 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());
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()));
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());
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));
while ((height > width) && (floors > material->get_building_medium_min_floors())) {
// Ensure that medium buildings aren't taller than they are wide
floors--;
height = floors * (2.8 + mt_rand(&seed));
while ((height > width) && (floors > _material->get_building_medium_min_floors())) {
// Ensure that medium buildings aren't taller than they are wide
floors--;
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) {
roof_shape = 0;
@@ -685,11 +699,11 @@ typedef QuadTreeBuilder<LOD*, SGBuildingBin::BuildingInstance, MakeBuildingLeaf,
}
} else {
// LARGE BUILDING
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());
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()));
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());
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));
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) {
roof_shape = 0;
@@ -718,36 +732,35 @@ typedef QuadTreeBuilder<LOD*, SGBuildingBin::BuildingInstance, MakeBuildingLeaf,
}
SGBuildingBin::BuildingType SGBuildingBin::getBuildingType(float roll) {
if (roll < _material->get_building_small_fraction()) {
return SGBuildingBin::SMALL;
}
if (roll < material->get_building_small_fraction()) {
return SGBuildingBin::SMALL;
}
if (roll < (material->get_building_small_fraction() + material->get_building_medium_fraction())) {
return SGBuildingBin::MEDIUM;
}
if (roll < (_material->get_building_small_fraction() + _material->get_building_medium_fraction())) {
return SGBuildingBin::MEDIUM;
}
return SGBuildingBin::LARGE;
}
float SGBuildingBin::getBuildingMaxRadius(BuildingType type) {
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::LARGE) return material->get_building_large_max_width();
return 0;
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::LARGE) return _material->get_building_large_max_width();
return 0;
}
float SGBuildingBin::getBuildingMaxDepth(BuildingType type) {
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::LARGE) return material->get_building_large_max_depth();
return 0;
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::LARGE) return _material->get_building_large_max_depth();
return 0;
}
ref_ptr<Group> SGBuildingBin::createBuildingsGroup(Matrix transInv, const SGReaderWriterOptions* options)
{
ref_ptr<Effect> effect;
EffectMap::iterator iter = buildingEffectMap.find(*texture);
auto iter = buildingEffectMap.find(_textureName);
if ((iter == buildingEffectMap.end())||
(!iter->second.lock(effect)))
@@ -756,16 +769,14 @@ typedef QuadTreeBuilder<LOD*, SGBuildingBin::BuildingInstance, MakeBuildingLeaf,
makeChild(effectProp, "inherits-from")->setStringValue("Effects/building");
SGPropertyNode* params = makeChild(effectProp, "parameters");
// Main texture - n=0
params->getChild("texture", 0, true)->getChild("image", 0, true)
->setStringValue(*texture);
params->getChild("texture", 0, true)->getChild("image", 0, true)->setStringValue(_textureName);
// Light map - n=3
params->getChild("texture", 3, true)->getChild("image", 0, true)
->setStringValue(*lightMap);
params->getChild("texture", 3, true)->getChild("image", 0, true)->setStringValue(_lightMapName);
effect = makeEffect(effectProp, true, options);
if (iter == buildingEffectMap.end())
buildingEffectMap.insert(EffectMap::value_type(*texture, effect));
buildingEffectMap.insert(EffectMap::value_type(_textureName, effect));
else
iter->second = effect; // update existing, but empty observer
}
+10 -11
View File
@@ -145,20 +145,19 @@ public:
};
private:
const SGSharedPtr<SGMaterial> _material;
const SGMaterial *material;
std::string _materialName;
std::string _textureName;
std::string _lightMapName;
std::string* material_name;
std::string* texture;
std::string* lightMap;
// Visibility range for buildings
float buildingRange;
// Visibility range for buildings
float buildingRange;
// Information for an instance of a building - position and orientation
typedef std::vector<BuildingInstance> BuildingInstanceList;
BuildingInstanceList buildingLocations;
// Information for an instance of a building - position and orientation
typedef std::vector<BuildingInstance> BuildingInstanceList;
BuildingInstanceList buildingLocations;
public:
@@ -187,7 +186,7 @@ public:
bool checkMinDist (SGVec3f p, float radius);
std::string* getMaterialName() { return material_name; }
const std::string& getMaterialName() const { return _materialName; }
BuildingType getBuildingType(float roll);
float getBuildingMaxRadius(BuildingType);
+5 -5
View File
@@ -428,12 +428,12 @@ public:
return 0;
// FIXME: do not include all values here ...
osg::Vec3Array* vertices = new osg::Vec3Array;
osg::Vec3Array* normals = new osg::Vec3Array;
osg::Vec2Array* priTexCoords = new osg::Vec2Array;
osg::Vec2Array* secTexCoords = new osg::Vec2Array;
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array;
osg::ref_ptr<osg::Vec2Array> priTexCoords = 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));
osg::Geometry* geometry = new osg::Geometry;
+17 -5
View File
@@ -151,14 +151,26 @@ Geometry* makeSharedTreeGeometry(int numQuads)
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)
{
if (!sharedTreeGeometry)
sharedTreeGeometry = makeSharedTreeGeometry(1600);
Geometry* quadGeom = simgear::clone(sharedTreeGeometry.get(),
CopyOp::SHALLOW_COPY);
Geometry* quadGeom = nullptr;
{
std::lock_guard<std::mutex> g(static_sharedGeometryMutex);
if (!sharedTreeGeometry)
sharedTreeGeometry = makeSharedTreeGeometry(1600);
quadGeom = simgear::clone(sharedTreeGeometry.get(),
CopyOp::SHALLOW_COPY);
}
Vec3Array* params = new Vec3Array;
params->push_back(Vec3(width, height, (float)varieties));
quadGeom->setNormalArray(params);
+19 -10
View File
@@ -35,11 +35,14 @@ namespace simgear
{
class TreeBin {
public:
struct Tree {
SGVec3f position;
SGVec3f tnormal;
Tree(const SGVec3f& p, const SGVec3f& t) : position(p),tnormal(t)
{ }
~TreeBin() = default;
struct Tree {
SGVec3f position;
SGVec3f tnormal;
Tree(const SGVec3f& p, const SGVec3f& t) : position(p), tnormal(t)
{
}
};
typedef std::vector<Tree> TreeList;
@@ -55,19 +58,25 @@ public:
{ _trees.push_back(t); }
void insert(const SGVec3f& p, const SGVec3f& tnorm)
{insert(Tree(p,tnorm));}
{
_trees.emplace_back(p, tnorm);
}
unsigned getNumTrees() const
{ return _trees.size(); }
const Tree& getTree(unsigned i) const
{ return _trees[i]; }
{
assert(i < _trees.size());
return _trees.at(i);
}
TreeList _trees;
~TreeBin() {
_trees.clear();
}
};
void clearSharedTreeGeometry();
typedef std::list<TreeBin*> SGTreeBinList;
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->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
static SGSceneFeatures* sceneFeatures = SGSceneFeatures::instance();
bool useTriangles = sceneFeatures->getEnableTriangleDirectionalLights();
osg::DrawArrays* drawArrays;
drawArrays = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES,
0, vertices->size());
if (useTriangles)
drawArrays = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES,
0, vertices->size());
else
drawArrays = new osg::DrawArrays(osg::PrimitiveSet::POINTS,
0, vertices->size());
geometry->addPrimitiveSet(drawArrays);
return geometry;
}
+224 -145
View File
@@ -46,6 +46,7 @@
#include <fstream>
#include <string>
#include <map>
#include <algorithm>
#include <simgear/version.h>
@@ -155,20 +156,16 @@ public:
class SyncSlot
{
public:
SyncSlot() :
isNewDirectory(false),
busy(false),
pendingKBytes(0)
{}
SyncSlot() = default;
SyncItem currentItem;
bool isNewDirectory;
std::queue<SyncItem> queue;
bool isNewDirectory = false;
std::deque<SyncItem> queue;
std::unique_ptr<HTTPRepository> repository;
SGTimeStamp stamp;
bool busy; ///< is the slot working or idle
unsigned int pendingKBytes;
unsigned int nextWarnTimeout;
bool busy = false; ///< is the slot working or idle
unsigned int pendingKBytes = 0;
unsigned int nextWarnTimeout = 0;
};
static const int SYNC_SLOT_TILES = 0; ///< Terrain and Objects sync
@@ -205,7 +202,6 @@ struct TerrasyncThreadState
_updated_tile_count(0),
_success_count(0),
_consecutive_errors(0),
_allowed_errors(6),
_cache_hits(0),
_transfer_rate(0),
_total_kb_downloaded(0),
@@ -219,7 +215,6 @@ struct TerrasyncThreadState
int _updated_tile_count;
int _success_count;
int _consecutive_errors;
int _allowed_errors;
int _cache_hits;
int _transfer_rate;
// kbytes, not bytes, because bytes might overflow 2^31
@@ -306,13 +301,6 @@ public:
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)
{
std::lock_guard<std::mutex> g(_stateLock);
@@ -328,7 +316,12 @@ public:
}
return st;
}
private:
bool isDirActive(const std::string& path) const;
void setCachePath(const SGPath &p) { _persistentCachePath = p; }
private:
void incrementCacheHits()
{
std::lock_guard<std::mutex> g(_stateLock);
@@ -341,15 +334,18 @@ private:
void runInternal();
void updateSyncSlot(SyncSlot& slot);
void drainWaitingTiles();
// commond helpers between both internal and external models
SyncItem::Status isPathCached(const SyncItem& next) const;
void initCompletedTilesPersistentCache();
void writeCompletedTilesPersistentCache() const;
void updated(SyncItem item, bool isNewDirectory);
void fail(SyncItem failedItem);
void notFound(SyncItem notFoundItem);
void initCompletedTilesPersistentCache();
void writeCompletedTilesPersistentCache() const;
HTTP::Client _http;
SyncSlot _syncSlots[NUM_SYNC_SLOTS];
@@ -370,7 +366,7 @@ private:
string _dnsdn;
TerrasyncThreadState _state;
std::mutex _stateLock;
mutable std::mutex _stateLock;
};
SGTerraSync::WorkerThread::WorkerThread() :
@@ -398,6 +394,18 @@ void SGTerraSync::WorkerThread::stop()
SyncItem w(string(), SyncItem::Stop);
request(w);
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()
@@ -417,12 +425,18 @@ bool SGTerraSync::WorkerThread::start()
SGPath path(_local_dir);
if (!path.exists())
{
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;
const SGPath parentDir = path.dirPath();
if (parentDir.exists()) {
// attempt to create terraSync dir ourselves
bool ok = path.create_dir(0755);
if (!ok) {
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");
@@ -439,10 +453,8 @@ bool SGTerraSync::WorkerThread::start()
_stop = false;
_state = TerrasyncThreadState(); // clean state
// not really an alert - but we want to (always) see this message, so user is
// aware we're downloading scenery (and using bandwidth).
SG_LOG(SG_TERRASYNC,SG_ALERT,
"Starting automatic scenery download/synchronization to '"<< _local_dir << "'.");
SG_LOG(SG_TERRASYNC, SG_MANDATORY_INFO,
"Starting automatic scenery download/synchronization to '" << _local_dir << "'.");
SGThread::start();
return true;
@@ -524,7 +536,6 @@ void SGTerraSync::WorkerThread::run()
}
initCompletedTilesPersistentCache();
runInternal();
{
@@ -541,7 +552,7 @@ void SGTerraSync::WorkerThread::updateSyncSlot(SyncSlot &slot)
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, "HTTP request count:" << _http.hasActiveRequests());
slot.nextWarnTimeout += 10000;
slot.nextWarnTimeout += 30 * 1000;
}
#endif
// convert bytes to kbytes here
@@ -551,6 +562,7 @@ void SGTerraSync::WorkerThread::updateSyncSlot(SyncSlot &slot)
// check result
HTTPRepository::ResultCode res = slot.repository->failure();
if (res == HTTPRepository::REPO_ERROR_NOT_FOUND) {
notFound(slot.currentItem);
} else if (res != HTTPRepository::REPO_NO_ERROR) {
@@ -565,12 +577,13 @@ void SGTerraSync::WorkerThread::updateSyncSlot(SyncSlot &slot)
slot.busy = false;
slot.repository.reset();
slot.pendingKBytes = 0;
slot.currentItem = {};
}
// init and start sync of the next repository
if (!slot.queue.empty()) {
slot.currentItem = slot.queue.front();
slot.queue.pop();
slot.queue.pop_front();
SGPath path(_local_dir);
path.append(slot.currentItem._dir);
@@ -605,7 +618,7 @@ void SGTerraSync::WorkerThread::updateSyncSlot(SyncSlot &slot)
return;
}
slot.nextWarnTimeout = 20000;
slot.nextWarnTimeout = 30 * 1000;
slot.stamp.stamp();
slot.busy = true;
slot.pendingKBytes = slot.repository->bytesToDownload();
@@ -616,21 +629,23 @@ void SGTerraSync::WorkerThread::updateSyncSlot(SyncSlot &slot)
void SGTerraSync::WorkerThread::runInternal()
{
unsigned dnsRetryCount = 0;
while (!_stop) {
// try to find a terrasync server
if( !hasServer() ) {
if( ++dnsRetryCount > 5 ) {
SG_LOG(SG_TERRASYNC, SG_WARN, "Can't find a terrasync server. TS disabled.");
break;
}
if( hasServer( findServer() ) ) {
SG_LOG(SG_TERRASYNC, SG_INFO, "terrasync scenery provider of the day is '" << _httpServer << "'");
}
continue;
const auto haveServer = findServer();
if (haveServer) {
hasServer(true);
std::lock_guard<std::mutex> g(_stateLock);
_state._consecutive_errors = 0;
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 {
_http.update(10);
@@ -648,21 +663,7 @@ void SGTerraSync::WorkerThread::runInternal()
if (_stop)
break;
// drain the waiting tiles queue into the sync slot queues.
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);
}
drainWaitingTiles();
bool anySlotBusy = false;
unsigned int newPendingCount = 0;
@@ -691,7 +692,7 @@ void SGTerraSync::WorkerThread::runInternal()
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()) {
ii = _notFoundItems.find( next._dir );
// Invalid means 'not cached', otherwise we want to return to
@@ -760,68 +761,117 @@ void SGTerraSync::WorkerThread::updated(SyncItem item, bool isNewDirectory)
writeCompletedTilesPersistentCache();
}
void SGTerraSync::WorkerThread::initCompletedTilesPersistentCache()
void SGTerraSync::WorkerThread::drainWaitingTiles()
{
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 " << 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) {
// drain the waiting tiles queue into the sync slot queues.
while (!waitingTiles.empty()) {
SyncItem next = waitingTiles.pop_front();
SyncItem::Status cacheStatus = isPathCached(next);
if (cacheStatus != SyncItem::Invalid) {
incrementCacheHits();
SG_LOG(SG_TERRASYNC, SG_BULK, "\nTerraSync Cache hit for: '" << next._dir << "'");
next._status = cacheStatus;
_freshTiles.push_back(next);
continue;
}
if (isNotFound) {
_completedTiles[tileName] = stamp;
} else {
_notFoundItems[tileName] = stamp;
}
const auto slot = syncSlotForType(next._type);
_syncSlots[slot].queue.push_back(next);
}
}
void SGTerraSync::WorkerThread::writeCompletedTilesPersistentCache() const
bool SGTerraSync::WorkerThread::isDirActive(const std::string& path) const
{
// cache is disabled
if (_persistentCachePath.isNull()) {
return;
// check waiting tiles first. we have to copy it to check safely,
// but since it's normally empty, this is not a big deal.
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);
if (!f.is_open()) {
return;
// check each sync slot in turn
std::lock_guard<std::mutex> g(_stateLock);
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);
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);
if (isNotFound) {
_notFoundItems[tileName] = stamp;
} else {
_completedTiles[tileName] = stamp;
}
}
}
it = _notFoundItems.begin();
for (; it != _notFoundItems.end(); ++it) {
SGPropertyNode* entry = cacheRoot->addChild("not-found");
entry->setStringValue("path", it->first);
entry->setIntValue("stamp", it->second);
}
void SGTerraSync::WorkerThread::writeCompletedTilesPersistentCache() const {
// cache is disabled
if (_persistentCachePath.isNull()) {
return;
}
writeProperties(f, cacheRoot, true /* write_all */);
f.close();
sg_ofstream f(_persistentCachePath, std::ios::trunc);
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)
{
if (!root) {
_terraRoot.clear();
_renderingRoot.clear();
return;
}
_terraRoot = root->getNode("/sim/terrasync",true);
_renderingRoot = root->getNode("/sim/rendering", true);
}
@@ -897,11 +953,17 @@ void SGTerraSync::reinit()
#else
_workerThread->setDNSDN( _terraRoot->getStringValue("dnsdn","terrasync.flightgear.org") );
#endif
_workerThread->setLocalDir(_terraRoot->getStringValue("scenery-dir",""));
SGPath sceneryRoot{_terraRoot->getStringValue("scenery-dir", "")};
_workerThread->setLocalDir(sceneryRoot.utf8Str());
SGPath installPath(_terraRoot->getStringValue("installation-dir"));
_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));
if (_workerThread->start())
@@ -944,12 +1006,7 @@ void SGTerraSync::bind()
_downloadedKBtesNode = _terraRoot->getNode("downloaded-kbytes", true);
_enabledNode = _terraRoot->getNode("enabled", true);
_availableNode = _terraRoot->getNode("available", true);
//_busyNode->setAttribute(SGPropertyNode::WRITE, false);
//_activeNode->setAttribute(SGPropertyNode::WRITE, false);
//_updateCountNode->setAttribute(SGPropertyNode::WRITE, false);
//_errorCountNode->setAttribute(SGPropertyNode::WRITE, false);
//_tileCountNode->setAttribute(SGPropertyNode::WRITE, false);
_maxErrorsNode = _terraRoot->getNode("max-errors", true);
}
void SGTerraSync::unbind()
@@ -961,7 +1018,20 @@ void SGTerraSync::unbind()
_terraRoot.clear();
_stalledNode.clear();
_activeNode.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)
@@ -969,16 +1039,21 @@ void SGTerraSync::update(double)
auto enabled = _enabledNode->getBoolValue();
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.
if (enabled && !worker_running)
{
reinit();
SG_LOG(SG_TERRASYNC, SG_ALERT, "Terrasync started");
SG_LOG(SG_TERRASYNC, SG_MANDATORY_INFO, "Terrasync started");
}
else if (!enabled && worker_running)
{
reinit();
SG_LOG(SG_TERRASYNC, SG_ALERT, "Terrasync stopped");
SG_LOG(SG_TERRASYNC, SG_MANDATORY_INFO, "Terrasync stopped");
}
TerrasyncThreadState copiedState(_workerThread->threadsafeCopyState());
@@ -994,14 +1069,21 @@ void SGTerraSync::update(double)
_stalledNode->setBoolValue(_workerThread->isStalled());
_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())
{
SyncItem next = _workerThread->getNewTile();
if ((next._type == SyncItem::Tile) || (next._type == SyncItem::AIData)) {
_activeTileDirs.erase(next._dir);
}
} // of freshly synced items
// ensure they are popped
_workerThread->getNewTile();
}
}
bool SGTerraSync::isIdle() {return _workerThread->isIdle();}
@@ -1046,17 +1128,16 @@ string_list SGTerraSync::getSceneryPathSuffixes() const
void SGTerraSync::syncAreaByPath(const std::string& aPath)
{
string_list scenerySuffixes = getSceneryPathSuffixes();
string_list::const_iterator it = scenerySuffixes.begin();
if (!_workerThread->isRunning()) {
return;
}
for (; it != scenerySuffixes.end(); ++it)
{
std::string dir = *it + "/" + aPath;
if (_activeTileDirs.find(dir) != _activeTileDirs.end()) {
for (const auto& suffix : getSceneryPathSuffixes()) {
const auto dir = suffix + "/" + aPath;
if (_workerThread->isDirActive(dir)) {
continue;
}
_activeTileDirs.insert(dir);
SyncItem w(dir, SyncItem::Tile);
_workerThread->request( w );
}
@@ -1075,13 +1156,9 @@ bool SGTerraSync::isTileDirPending(const std::string& sceneryDir) const
return false;
}
string_list scenerySuffixes = getSceneryPathSuffixes();
string_list::const_iterator it = scenerySuffixes.begin();
for (; it != scenerySuffixes.end(); ++it)
{
string s = *it + "/" + sceneryDir;
if (_activeTileDirs.find(s) != _activeTileDirs.end()) {
for (const auto& suffix : getSceneryPathSuffixes()) {
const auto s = suffix + "/" + sceneryDir;
if (_workerThread->isDirActive(s)) {
return true;
}
}
@@ -1091,14 +1168,16 @@ bool SGTerraSync::isTileDirPending(const std::string& sceneryDir) const
void SGTerraSync::scheduleDataDir(const std::string& dataDir)
{
if (_activeTileDirs.find(dataDir) != _activeTileDirs.end()) {
if (!_workerThread->isRunning()) {
return;
}
if (_workerThread->isDirActive(dataDir)) {
return;
}
_activeTileDirs.insert(dataDir);
SyncItem w(dataDir, SyncItem::AIData);
_workerThread->request( w );
}
bool SGTerraSync::isDataDirPending(const std::string& dataDir) const
@@ -1107,7 +1186,7 @@ bool SGTerraSync::isDataDirPending(const std::string& dataDir) const
return false;
}
return (_activeTileDirs.find(dataDir) != _activeTileDirs.end());
return _workerThread->isDirActive(dataDir);
}
void SGTerraSync::reposition()
+5 -2
View File
@@ -107,6 +107,7 @@ private:
SGPropertyNode_ptr _transferRateBytesSecNode;
SGPropertyNode_ptr _pendingKbytesNode;
SGPropertyNode_ptr _downloadedKBtesNode;
SGPropertyNode_ptr _maxErrorsNode;
// we manually bind+init TerraSync during early startup
// to get better overlap of slow operations (Shared Models sync
@@ -117,8 +118,10 @@ private:
simgear::TiedPropertyList _tiedProperties;
BufferedLogCallback* _log;
typedef std::set<std::string> string_set;
string_set _activeTileDirs;
/// if we disabled TerraSync due to errors, this is the time at which we will restart it
/// automatically.
SGTimeStamp _retryTime;
unsigned int _retryBackOffSeconds = 0;
};
}
+4 -2
View File
@@ -6,7 +6,6 @@ set(HEADERS
xmlsound.hxx
soundmgr.hxx
filters.hxx
readwav.hxx
)
set(SOURCES
@@ -14,7 +13,6 @@ set(SOURCES
sample_group.cxx
xmlsound.cxx
filters.cxx
readwav.cxx
)
if (USE_AEONWAVE)
@@ -22,8 +20,12 @@ if (USE_AEONWAVE)
soundmgr_aeonwave.cxx
)
else()
set(HEADERS ${HEADERS}
readwav.hxx
)
set(SOURCES ${SOURCES}
soundmgr_openal.cxx
readwav.cxx
)
endif()
+1
View File
@@ -594,6 +594,7 @@ unsigned int SGSoundMgr::request_buffer(SGSoundSample *sample)
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.");
format = AL_FORMAT_STEREO8;
break;
default:
SG_LOG(SG_SOUND, SG_ALERT, "unsupported audio format");
return buffer;
+18 -12
View File
@@ -414,9 +414,11 @@ SGXmlSound::update (double dt)
double v = 1.0;
if (_volume[i].expr) {
v = _volume[i].expr->getValue(nullptr);
expr = true;
continue;
double expression_value = _volume[i].expr->getValue(nullptr);
if (expression_value >= 0)
volume *= expression_value;
expr = true;
continue;
}
if (_volume[i].prop) {
@@ -437,19 +439,23 @@ SGXmlSound::update (double dt)
if (_volume[i].max && (v > _volume[i].max))
v = _volume[i].max;
else if (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 {
volume_offset += _volume[i].offset;
volume *= v;
if (v >= 0) {
volume_offset += _volume[i].offset;
volume = volume * v;
}
}
}
//
// Update the pitch
//
@@ -462,9 +468,9 @@ SGXmlSound::update (double dt)
double p = 1.0;
if (_pitch[i].expr) {
p = _pitch[i].expr->getValue(nullptr);
expr = true;
continue;
pitch *= _pitch[i].expr->getValue(nullptr);
expr = true;
continue;
}
if (_pitch[i].prop) {
+23 -33
View File
@@ -18,24 +18,16 @@
#include <simgear/structure/exception.hxx>
SGBinding::SGBinding()
: _command(0),
_arg(new SGPropertyNode),
_setting(0)
: _arg(new SGPropertyNode)
{
}
SGBinding::SGBinding(const std::string& commandName)
: _command(0),
_arg(0),
_setting(0)
{
_command_name = commandName;
}
SGBinding::SGBinding(const SGPropertyNode* node, SGPropertyNode* root)
: _command(0),
_arg(0),
_setting(0)
{
read(node, root);
}
@@ -43,8 +35,8 @@ SGBinding::SGBinding(const SGPropertyNode* node, SGPropertyNode* root)
void
SGBinding::clear()
{
_command = NULL;
_arg.clear();
_root.clear();
_setting.clear();
}
@@ -57,14 +49,12 @@ SGBinding::read(const SGPropertyNode* node, SGPropertyNode* root)
_command_name = node->getStringValue("command", "");
if (_command_name.empty()) {
SG_LOG(SG_INPUT, SG_WARN, "No command supplied for binding.");
_command = 0;
SG_LOG(SG_INPUT, SG_DEV_ALERT, "No command supplied for binding.");
}
_arg = const_cast<SGPropertyNode*>(node);
_root = const_cast<SGPropertyNode*>(root);
_setting = 0;
_setting.clear();
}
void
@@ -78,30 +68,29 @@ SGBinding::fire() const
void
SGBinding::innerFire () const
{
if (_command == 0)
_command = SGCommandMgr::instance()->getCommand(_command_name);
if (_command == 0) {
SG_LOG(SG_INPUT, SG_WARN, "No command attached to binding:" << _command_name);
} else {
try {
if (!(*_command)(_arg, _root)) {
SG_LOG(SG_INPUT, SG_ALERT, "Failed to execute command "
<< _command_name);
}
} catch (sg_exception& e) {
auto cmd = SGCommandMgr::instance()->getCommand(_command_name);
if (!cmd) {
SG_LOG(SG_INPUT, SG_WARN, "No command found for binding:" << _command_name);
return;
}
try {
if (!(*cmd)(_arg, _root)) {
SG_LOG(SG_INPUT, SG_ALERT, "Failed to execute command " << _command_name);
}
} catch (sg_exception& e) {
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
SGBinding::fire (SGPropertyNode* params) const
{
if (test()) {
if (params != NULL) {
copyProperties(params, _arg);
}
if (params != nullptr) {
copyProperties(params, _arg);
}
innerFire();
}
@@ -122,8 +111,9 @@ SGBinding::fire (double setting) const
if (test()) {
// A value is automatically added to
// the args
if (_setting == 0) // save the setting node for efficiency
_setting = _arg->getChild("setting", 0, true);
if (!_setting) { // save the setting node for efficiency
_setting = _arg->getChild("setting", 0, true);
}
_setting->setDoubleValue(setting);
innerFire();
}
-11
View File
@@ -77,16 +77,6 @@ public:
*/
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.
*
@@ -140,7 +130,6 @@ private:
SGBinding (const SGBinding &binding);
std::string _command_name;
mutable SGCommandMgr::Command* _command;
mutable SGPropertyNode_ptr _arg;
mutable SGPropertyNode_ptr _setting;
mutable SGPropertyNode_ptr _root;
+31 -60
View File
@@ -13,6 +13,8 @@
#include <simgear/misc/sg_path.hxx>
static ThrowCallback static_callback;
////////////////////////////////////////////////////////////////////////
// Implementation of sg_location class.
////////////////////////////////////////////////////////////////////////
@@ -49,19 +51,7 @@ sg_location::sg_location (const char* path, int line, int column)
setPath(path);
}
sg_location::~sg_location ()
{
}
const char*
sg_location::getPath () const
{
return _path;
}
void
sg_location::setPath (const char* path)
{
void sg_location::setPath(const char *path) {
if (path) {
strncpy(_path, path, max_path);
_path[max_path -1] = '\0';
@@ -70,17 +60,9 @@ sg_location::setPath (const char* path)
}
}
int
sg_location::getLine () const
{
return _line;
}
const char *sg_location::getPath() const { return _path; }
void
sg_location::setLine (int line)
{
_line = line;
}
int sg_location::getLine() const { return _line; }
int
sg_location::getColumn () const
@@ -88,11 +70,6 @@ sg_location::getColumn () const
return _column;
}
void
sg_location::setColumn (int column)
{
_column = column;
}
int
sg_location::getByte () const
@@ -100,12 +77,6 @@ sg_location::getByte () const
return _byte;
}
void
sg_location::setByte (int byte)
{
_byte = byte;
}
std::string
sg_location::asString () const
{
@@ -126,8 +97,8 @@ sg_location::asString () const
return out.str();
}
bool sg_location::isValid() const { return strlen(_path) > 0; }
////////////////////////////////////////////////////////////////////////
// Implementation of sg_throwable class.
////////////////////////////////////////////////////////////////////////
@@ -138,10 +109,14 @@ sg_throwable::sg_throwable ()
_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);
setOrigin(origin);
if (static_callback) {
static_callback(_message, _origin, loc);
}
}
sg_throwable::~sg_throwable ()
@@ -228,16 +203,13 @@ sg_exception::sg_exception ()
{
}
sg_exception::sg_exception (const char* message, const char* origin)
: sg_throwable(message, origin)
{
}
sg_exception::sg_exception(const char *message, const char *origin,
const sg_location &loc)
: sg_throwable(message, origin, loc) {}
sg_exception::sg_exception( const std::string& message,
const std::string& origin )
: sg_throwable(message.c_str(), origin.c_str())
{
}
sg_exception::sg_exception(const std::string &message,
const std::string &origin, const sg_location &loc)
: sg_throwable(message.c_str(), origin.c_str(), loc) {}
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,
const sg_location &location,
const char* origin)
: sg_exception(message, origin),
_location(location)
{
}
sg_io_exception::sg_io_exception(const char *message,
const sg_location &location,
const char *origin)
: sg_exception(message, origin, location), _location(location) {}
sg_io_exception::sg_io_exception( const std::string& message,
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,
const sg_location &location,
const std::string& origin )
: sg_exception(message, origin),
_location(location)
{
}
sg_io_exception::sg_io_exception(const std::string &message,
const sg_location &location,
const std::string &origin)
: sg_exception(message, origin, location), _location(location) {}
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 ()
{
}
////////////////////////////////////////////////////////////////////////
void setThrowCallback(ThrowCallback cb) { static_callback = cb; }
// end of exception.cxx
+33 -14
View File
@@ -11,6 +11,7 @@
#define __SIMGEAR_MISC_EXCEPTION_HXX 1
#include <exception>
#include <functional>
#include <simgear/compiler.h>
#include <string>
@@ -31,17 +32,20 @@ public:
sg_location(const std::string& 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);
virtual ~sg_location();
virtual const char* getPath() const;
virtual void setPath (const char* path);
virtual int getLine () const;
virtual void setLine (int line);
virtual int getColumn () const;
virtual void setColumn (int column);
virtual int getByte () const;
virtual void setByte (int byte);
virtual std::string asString () const;
~sg_location() = default;
const char *getPath() const;
int getLine() const;
int getColumn() const;
int getByte() const;
std::string asString() const;
bool isValid() const;
private:
void setPath(const char *p);
char _path[max_path];
int _line;
int _column;
@@ -57,7 +61,9 @@ class sg_throwable : public std::exception
public:
enum {MAX_TEXT_LEN = 1024};
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 const char* getMessage () const;
virtual const std::string getFormattedMessage () const;
@@ -86,7 +92,7 @@ class sg_error : public sg_throwable
public:
sg_error ();
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 ();
};
@@ -109,8 +115,10 @@ class sg_exception : public sg_throwable
{
public:
sg_exception ();
sg_exception (const char* message, const char* origin = 0);
sg_exception (const std::string& message, const std::string& = "");
sg_exception(const char *message, const char *origin = 0,
const sg_location &loc = {});
sg_exception(const std::string &message, const std::string & = {},
const sg_location &loc = {});
virtual ~sg_exception ();
};
@@ -193,6 +201,17 @@ public:
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
// end of exception.hxx
+62 -44
View File
@@ -272,30 +272,35 @@ template<class T>
class SGBlockingDeque
{
public:
using value_type = T;
using container_type = std::deque<T>;
/**
* Create a new SGBlockingDequeue.
*/
SGBlockingDeque() {}
SGBlockingDeque() = default;
/**
* Destroy this dequeue.
*/
virtual ~SGBlockingDeque() {}
~SGBlockingDeque() = default;
/**
*
*/
virtual void clear() {
std::lock_guard<std::mutex> g(mutex);
this->queue.clear();
void clear()
{
std::lock_guard<std::mutex> g(mutex);
this->queue.clear();
}
/**
*
*/
virtual bool empty() {
std::lock_guard<std::mutex> g(mutex);
return this->queue.empty();
bool empty() const
{
std::lock_guard<std::mutex> g(mutex);
return this->queue.empty();
}
/**
@@ -303,10 +308,11 @@ public:
*
* @param item The object to add.
*/
virtual void push_front( const T& item ) {
std::lock_guard<std::mutex> g(mutex);
this->queue.push_front( item );
not_empty.signal();
void push_front(const T& item)
{
std::lock_guard<std::mutex> g(mutex);
this->queue.push_front(item);
not_empty.signal();
}
/**
@@ -314,10 +320,11 @@ public:
*
* @param item The object to add.
*/
virtual void push_back( const T& item ) {
std::lock_guard<std::mutex> g(mutex);
this->queue.push_back( item );
not_empty.signal();
void push_back(const T& item)
{
std::lock_guard<std::mutex> g(mutex);
this->queue.push_back(item);
not_empty.signal();
}
/**
@@ -326,14 +333,15 @@ public:
*
* @return The next available object.
*/
virtual T front() {
std::lock_guard<std::mutex> g(mutex);
T front() const
{
std::lock_guard<std::mutex> g(mutex);
assert(this->queue.empty() != true);
//if (queue.empty()) throw ??
assert(this->queue.empty() != true);
//if (queue.empty()) throw ??
T item = this->queue.front();
return item;
T item = this->queue.front();
return item;
}
/**
@@ -342,18 +350,19 @@ public:
*
* @return The next available object.
*/
virtual T pop_front() {
std::lock_guard<std::mutex> g(mutex);
T pop_front()
{
std::lock_guard<std::mutex> g(mutex);
while (this->queue.empty())
not_empty.wait(mutex);
while (this->queue.empty())
not_empty.wait(mutex);
assert(this->queue.empty() != true);
//if (queue.empty()) throw ??
assert(this->queue.empty() != true);
//if (queue.empty()) throw ??
T item = this->queue.front();
this->queue.pop_front();
return item;
T item = this->queue.front();
this->queue.pop_front();
return item;
}
/**
@@ -362,18 +371,19 @@ public:
*
* @return The next available object.
*/
virtual T pop_back() {
std::lock_guard<std::mutex> g(mutex);
T pop_back()
{
std::lock_guard<std::mutex> g(mutex);
while (this->queue.empty())
not_empty.wait(mutex);
while (this->queue.empty())
not_empty.wait(mutex);
assert(this->queue.empty() != true);
//if (queue.empty()) throw ??
assert(this->queue.empty() != true);
//if (queue.empty()) throw ??
T item = this->queue.back();
this->queue.pop_back();
return item;
T item = this->queue.back();
this->queue.pop_back();
return item;
}
/**
@@ -381,8 +391,9 @@ public:
*
* @return Size of queue.
*/
virtual size_t size() {
std::lock_guard<std::mutex> g(mutex);
size_t size() const
{
std::lock_guard<std::mutex> g(mutex);
return this->queue.size();
}
@@ -391,12 +402,19 @@ public:
while (this->queue.empty())
not_empty.wait(mutex);
}
container_type copy() const
{
std::lock_guard<std::mutex> g(mutex);
return queue;
}
private:
/**
* Mutex to serialise access.
*/
std::mutex mutex;
mutable std::mutex mutex;
/**
* Condition to signal when queue not empty.
@@ -409,7 +427,7 @@ private:
SGBlockingDeque& operator=( const SGBlockingDeque& );
protected:
std::deque<T> queue;
container_type queue;
};
#endif // SGQUEUE_HXX_INCLUDED
-1
View File
@@ -1 +0,0 @@
2020.1.3