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