From abe5136de0b90d32d2902cc6212b8f8ed2f0befe Mon Sep 17 00:00:00 2001 From: Oleg Semyonov Date: Fri, 16 Sep 2011 23:35:43 +0300 Subject: [PATCH 01/19] uavobjgenerator: enable '+' character in object field names Useful for names like "Pitch+Roll" or similar. Will be removed from symbolic indentifiers. --- ground/uavobjgenerator/generators/generator_common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ground/uavobjgenerator/generators/generator_common.h b/ground/uavobjgenerator/generators/generator_common.h index 0ae6f470e..719a6c3af 100644 --- a/ground/uavobjgenerator/generators/generator_common.h +++ b/ground/uavobjgenerator/generators/generator_common.h @@ -31,7 +31,7 @@ #include "generator_io.h" // These special chars (regexp) will be removed from C/java identifiers -#define ENUM_SPECIAL_CHARS "[\\.\\-\\s/]" +#define ENUM_SPECIAL_CHARS "[\\.\\-\\s\\+/]" void replaceCommonTags(QString& out, ObjectInfo* info); void replaceCommonTags(QString& out); From d923117c25f1306b9fc8ffc653ce21d96b50ecc6 Mon Sep 17 00:00:00 2001 From: Oleg Semyonov Date: Fri, 16 Sep 2011 23:36:44 +0300 Subject: [PATCH 02/19] TxPID: optional module to tune PID settings using R/C transmitter This module will periodically update values of stabilization PID settings depending on configured input control channels. New values of stabilization settings are not saved to flash, but updated in RAM. It is expected that the module will be enabled only for tuning. When desired values are found, they can be read via GCS and saved permanently. Then this module should be disabled again. --- flight/CopterControl/Makefile | 3 +- flight/CopterControl/System/coptercontrol.c | 4 + flight/Modules/TxPID/inc/txpid.h | 42 +++ flight/Modules/TxPID/txpid.c | 248 ++++++++++++++++++ .../src/plugins/uavobjects/uavobjects.pro | 2 + shared/uavobjectdefinition/hwsettings.xml | 2 +- shared/uavobjectdefinition/txpidsettings.xml | 25 ++ 7 files changed, 324 insertions(+), 2 deletions(-) create mode 100644 flight/Modules/TxPID/inc/txpid.h create mode 100644 flight/Modules/TxPID/txpid.c create mode 100644 shared/uavobjectdefinition/txpidsettings.xml diff --git a/flight/CopterControl/Makefile b/flight/CopterControl/Makefile index ea6cab01c..8b9b6dbcd 100644 --- a/flight/CopterControl/Makefile +++ b/flight/CopterControl/Makefile @@ -65,7 +65,7 @@ endif FLASH_TOOL = OPENOCD # List of modules to include -MODULES = Attitude Stabilization Actuator ManualControl FirmwareIAP CameraStab +MODULES = Attitude Stabilization Actuator ManualControl FirmwareIAP CameraStab TxPID # Telemetry must be last to grab the optional modules MODULES += Telemetry @@ -172,6 +172,7 @@ SRC += $(OPUAVSYNTHDIR)/receiveractivity.c SRC += $(OPUAVSYNTHDIR)/taskinfo.c SRC += $(OPUAVSYNTHDIR)/mixerstatus.c SRC += $(OPUAVSYNTHDIR)/ratedesired.c +SRC += $(OPUAVSYNTHDIR)/txpidsettings.c endif diff --git a/flight/CopterControl/System/coptercontrol.c b/flight/CopterControl/System/coptercontrol.c index 33cb096c2..ee2132671 100644 --- a/flight/CopterControl/System/coptercontrol.c +++ b/flight/CopterControl/System/coptercontrol.c @@ -37,6 +37,7 @@ #include "uavobjectsinit.h" #include "hwsettings.h" #include "camerastab.h" +#include "txpid.h" #include "systemmod.h" /* Task Priorities */ @@ -81,6 +82,9 @@ int main() if(optionalModules[HWSETTINGS_OPTIONALMODULES_CAMERASTABILIZATION] == HWSETTINGS_OPTIONALMODULES_ENABLED) { CameraStabInitialize(); } + if(optionalModules[HWSETTINGS_OPTIONALMODULES_TXPID] == HWSETTINGS_OPTIONALMODULES_ENABLED) { + TxPIDInitialize(); + } /* swap the stack to use the IRQ stack */ Stack_Change(); diff --git a/flight/Modules/TxPID/inc/txpid.h b/flight/Modules/TxPID/inc/txpid.h new file mode 100644 index 000000000..c5a2e4fa3 --- /dev/null +++ b/flight/Modules/TxPID/inc/txpid.h @@ -0,0 +1,42 @@ +/** + ****************************************************************************** + * @addtogroup OpenPilotModules OpenPilot Modules + * @{ + * @addtogroup TxPIDModule TxPID Module + * @{ + * + * @file txpid.h + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2011. + * @brief Optional module to tune PID settings using R/C transmitter. + * + * @see The GNU Public License (GPL) Version 3 + * + *****************************************************************************/ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef TXPID_H +#define TXPID_H + +#include "openpilot.h" + +int32_t TxPIDInitialize(void); + +#endif // TXPID_H + +/** + * @} + * @} + */ diff --git a/flight/Modules/TxPID/txpid.c b/flight/Modules/TxPID/txpid.c new file mode 100644 index 000000000..7cf477037 --- /dev/null +++ b/flight/Modules/TxPID/txpid.c @@ -0,0 +1,248 @@ +/** + ****************************************************************************** + * @addtogroup OpenPilotModules OpenPilot Modules + * @{ + * @addtogroup TxPIDModule TxPID Module + * @brief Optional module to tune PID settings using R/C transmitter. + * Updates PID settings in RAM in real-time using configured Accessory channels as controllers. + * @{ + * + * @file txpid.c + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2011. + * @brief Optional module to tune PID settings using R/C transmitter. + * + * @see The GNU Public License (GPL) Version 3 + * + *****************************************************************************/ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** + * Output object: StabilizationSettings + * + * This module will periodically update values of stabilization PID settings + * depending on configured input control channels. New values of stabilization + * settings are not saved to flash, but updated in RAM. It is expected that the + * module will be enabled only for tuning. When desired values are found, they + * can be read via GCS and saved permanently. Then this module should be + * disabled again. + * + * UAVObjects are automatically generated by the UAVObjectGenerator from + * the object definition XML file. + * + * Modules have no API, all communication to other modules is done through UAVObjects. + * However modules may use the API exposed by shared libraries. + * See the OpenPilot wiki for more details. + * http://wiki.openpilot.org/display/Doc/OpenPilot+Architecture + * + */ + +#include "openpilot.h" + +#include "txpidsettings.h" +#include "accessorydesired.h" +#include "stabilizationsettings.h" + +// +// Configuration +// +#define SAMPLE_PERIOD_MS 200 +#define TELEMETRY_UPDATE_PERIOD_MS 1000 // 0 = update on change + +// Sanity checks +#if (TXPIDSETTINGS_PIDS_NUMELEM != TXPIDSETTINGS_INPUTS_NUMELEM) || \ + (TXPIDSETTINGS_PIDS_NUMELEM != TXPIDSETTINGS_MIN_NUMELEM) || \ + (TXPIDSETTINGS_PIDS_NUMELEM != TXPIDSETTINGS_MAX_NUMELEM) +#error Invalid TxPID UAVObject definition (inconsistent number of field elements) +#endif + +// Private types + +// Private variables + +// Private functions +static void updatePIDs(UAVObjEvent* ev); +static uint8_t update(float *var, float val); +static float scale(float val, float min, float max); + +/** + * Initialise the module, called on startup + * \returns 0 on success or -1 if initialisation failed + */ +int32_t TxPIDInitialize(void) +{ + static UAVObjEvent ev; + ev.obj = AccessoryDesiredHandle(); + ev.instId = 0; + ev.event = 0; + + // Initialize settings object + TxPIDSettingsInitialize(); + +#if (TELEMETRY_UPDATE_PERIOD_MS != 0) + // Change StabilizationSettings update rate from OnChange to periodic + // to prevent telemetry link flooding with frequent updates in case of + // control channel jitter. + UAVObjMetadata metadata; + StabilizationSettingsGetMetadata(&metadata); + metadata.telemetryAcked = 0; + metadata.telemetryUpdateMode = UPDATEMODE_PERIODIC; + metadata.telemetryUpdatePeriod = TELEMETRY_UPDATE_PERIOD_MS; + StabilizationSettingsSetMetadata(&metadata); +#endif + + // Setup the callback function + EventPeriodicCallbackCreate(&ev, updatePIDs, SAMPLE_PERIOD_MS / portTICK_RATE_MS); + + return 0; +} + +/** + * Update PIDs callback function + */ +static void updatePIDs(UAVObjEvent* ev) +{ + if (ev->obj != AccessoryDesiredHandle()) + return; + + TxPIDSettingsData inst; + TxPIDSettingsGet(&inst); + StabilizationSettingsData stab; + StabilizationSettingsGet(&stab); + AccessoryDesiredData accessory; + + uint8_t needsUpdate = 0; + + // Loop through every enabled instance + for (uint8_t i = 0; i < TXPIDSETTINGS_PIDS_NUMELEM; i++) { + if (inst.PIDs[i] != TXPIDSETTINGS_PIDS_DISABLED) { + if (AccessoryDesiredInstGet(inst.Inputs[i] - TXPIDSETTINGS_INPUTS_ACCESSORY0, &accessory) == 0) { + float value = scale(accessory.AccessoryVal, inst.Min[i], inst.Max[i]); + + switch (inst.PIDs[i]) { + case TXPIDSETTINGS_PIDS_ROLLRATEKP: + needsUpdate |= update(&stab.RollRatePID[STABILIZATIONSETTINGS_ROLLRATEPID_KP], value); + break; + case TXPIDSETTINGS_PIDS_ROLLRATEKI: + needsUpdate |= update(&stab.RollRatePID[STABILIZATIONSETTINGS_ROLLRATEPID_KI], value); + break; + case TXPIDSETTINGS_PIDS_ROLLRATEKD: + needsUpdate |= update(&stab.RollRatePID[STABILIZATIONSETTINGS_ROLLRATEPID_KD], value); + break; + case TXPIDSETTINGS_PIDS_ROLLATTITUDEKP: + needsUpdate |= update(&stab.RollPI[STABILIZATIONSETTINGS_ROLLPI_KP], value); + break; + case TXPIDSETTINGS_PIDS_ROLLATTITUDEKI: + needsUpdate |= update(&stab.RollPI[STABILIZATIONSETTINGS_ROLLPI_KI], value); + break; + case TXPIDSETTINGS_PIDS_PITCHRATEKP: + needsUpdate |= update(&stab.PitchRatePID[STABILIZATIONSETTINGS_PITCHRATEPID_KP], value); + break; + case TXPIDSETTINGS_PIDS_PITCHRATEKI: + needsUpdate |= update(&stab.PitchRatePID[STABILIZATIONSETTINGS_PITCHRATEPID_KI], value); + break; + case TXPIDSETTINGS_PIDS_PITCHRATEKD: + needsUpdate |= update(&stab.PitchRatePID[STABILIZATIONSETTINGS_PITCHRATEPID_KD], value); + break; + case TXPIDSETTINGS_PIDS_PITCHATTITUDEKP: + needsUpdate |= update(&stab.PitchPI[STABILIZATIONSETTINGS_PITCHPI_KP], value); + break; + case TXPIDSETTINGS_PIDS_PITCHATTITUDEKI: + needsUpdate |= update(&stab.PitchPI[STABILIZATIONSETTINGS_PITCHPI_KI], value); + break; + case TXPIDSETTINGS_PIDS_ROLLPITCHRATEKP: + needsUpdate |= update(&stab.RollRatePID[STABILIZATIONSETTINGS_ROLLRATEPID_KP], value); + needsUpdate |= update(&stab.PitchRatePID[STABILIZATIONSETTINGS_PITCHRATEPID_KP], value); + break; + case TXPIDSETTINGS_PIDS_ROLLPITCHRATEKI: + needsUpdate |= update(&stab.RollRatePID[STABILIZATIONSETTINGS_ROLLRATEPID_KI], value); + needsUpdate |= update(&stab.PitchRatePID[STABILIZATIONSETTINGS_PITCHRATEPID_KI], value); + break; + case TXPIDSETTINGS_PIDS_ROLLPITCHRATEKD: + needsUpdate |= update(&stab.RollRatePID[STABILIZATIONSETTINGS_ROLLRATEPID_KD], value); + needsUpdate |= update(&stab.PitchRatePID[STABILIZATIONSETTINGS_PITCHRATEPID_KD], value); + break; + case TXPIDSETTINGS_PIDS_ROLLPITCHATTITUDEKP: + needsUpdate |= update(&stab.RollPI[STABILIZATIONSETTINGS_ROLLPI_KP], value); + needsUpdate |= update(&stab.PitchPI[STABILIZATIONSETTINGS_PITCHPI_KP], value); + break; + case TXPIDSETTINGS_PIDS_ROLLPITCHATTITUDEKI: + needsUpdate |= update(&stab.RollPI[STABILIZATIONSETTINGS_ROLLPI_KI], value); + needsUpdate |= update(&stab.PitchPI[STABILIZATIONSETTINGS_PITCHPI_KI], value); + break; + case TXPIDSETTINGS_PIDS_YAWRATEKP: + needsUpdate |= update(&stab.YawRatePID[STABILIZATIONSETTINGS_YAWRATEPID_KP], value); + break; + case TXPIDSETTINGS_PIDS_YAWRATEKI: + needsUpdate |= update(&stab.YawRatePID[STABILIZATIONSETTINGS_YAWRATEPID_KI], value); + break; + case TXPIDSETTINGS_PIDS_YAWRATEKD: + needsUpdate |= update(&stab.YawRatePID[STABILIZATIONSETTINGS_YAWRATEPID_KD], value); + break; + case TXPIDSETTINGS_PIDS_YAWATTITUDEKP: + needsUpdate |= update(&stab.YawPI[STABILIZATIONSETTINGS_YAWPI_KP], value); + break; + case TXPIDSETTINGS_PIDS_YAWATTITUDEKI: + needsUpdate |= update(&stab.YawPI[STABILIZATIONSETTINGS_YAWPI_KI], value); + break; + } + } + } + } + if (needsUpdate) + StabilizationSettingsSet(&stab); +} + +/** + * Scale input value to [min..max] (min can be greater than max) + * \returns scaled value + */ +static float scale(float val, float min, float max) { + float v; + + if (min > max) { + val = -val; + v = min; + min = max; + max = v; + } + + v = min + (max - min) * (val + 1.0) * 0.5; + if (v > max) v = max; + if (v < min) v = min; + + return v; +} + +/** + * Updates value + * \returns 1 if update needed or 0 if not + */ +static uint8_t update(float *var, float val) { + if (*var != val) { + *var = val; + return 1; + } + return 0; +} + +/** + * @} + */ + +/** + * @} + */ diff --git a/ground/openpilotgcs/src/plugins/uavobjects/uavobjects.pro b/ground/openpilotgcs/src/plugins/uavobjects/uavobjects.pro index 63eefa5bf..a4663e399 100644 --- a/ground/openpilotgcs/src/plugins/uavobjects/uavobjects.pro +++ b/ground/openpilotgcs/src/plugins/uavobjects/uavobjects.pro @@ -73,6 +73,7 @@ HEADERS += $$UAVOBJECT_SYNTHETICS/accessorydesired.h \ $$UAVOBJECT_SYNTHETICS/gcsreceiver.h \ $$UAVOBJECT_SYNTHETICS/receiveractivity.h \ $$UAVOBJECT_SYNTHETICS/attitudesettings.h \ + $$UAVOBJECT_SYNTHETICS/txpidsettings.h \ $$UAVOBJECT_SYNTHETICS/cameradesired.h SOURCES += $$UAVOBJECT_SYNTHETICS/accessorydesired.cpp \ @@ -126,4 +127,5 @@ SOURCES += $$UAVOBJECT_SYNTHETICS/accessorydesired.cpp \ $$UAVOBJECT_SYNTHETICS/gcsreceiver.cpp \ $$UAVOBJECT_SYNTHETICS/receiveractivity.cpp \ $$UAVOBJECT_SYNTHETICS/attitudesettings.cpp \ + $$UAVOBJECT_SYNTHETICS/txpidsettings.cpp \ $$UAVOBJECT_SYNTHETICS/cameradesired.cpp diff --git a/shared/uavobjectdefinition/hwsettings.xml b/shared/uavobjectdefinition/hwsettings.xml index 228d50b5e..6fdd75a21 100644 --- a/shared/uavobjectdefinition/hwsettings.xml +++ b/shared/uavobjectdefinition/hwsettings.xml @@ -9,7 +9,7 @@ - + diff --git a/shared/uavobjectdefinition/txpidsettings.xml b/shared/uavobjectdefinition/txpidsettings.xml new file mode 100644 index 000000000..5fd01aaad --- /dev/null +++ b/shared/uavobjectdefinition/txpidsettings.xml @@ -0,0 +1,25 @@ + + + Settings used by @ref TxPID optional module to tune PID settings using R/C transmitter + + + + + + + + + + + From aad263bdd82469cced89d3fed074a668e5607257 Mon Sep 17 00:00:00 2001 From: Oleg Semyonov Date: Sun, 18 Sep 2011 14:38:50 +0300 Subject: [PATCH 03/19] TxPID: enable ramp-shaped throttle-dependent PIDs Besides of knob PID tuning it is now possible to use throttle channel to ramp-shape PID coefficients. This can be used to lower some PIDs on VTOL while sinking to prevent wobble. To use the feature select throttle as control input, choose throttle range max and min values, assign the instance to particular PID coefficient and define a range for it. When throttle is lower than defined throttle range min value (or higher than max), then min and max PID values will be used accordingly. Changing throttle from throttle min to max will linearly scale PID value. Note that it is possible to set MinPID > MaxPID. In that case increasing control input value will decrease the PID coefficient. Up to 3 independent instances can be configured. The number can be increased changing the UAVO definition, but at the cost of extra RAM. --- flight/Modules/TxPID/txpid.c | 201 ++++++++++--------- shared/uavobjectdefinition/txpidsettings.xml | 13 +- 2 files changed, 118 insertions(+), 96 deletions(-) diff --git a/flight/Modules/TxPID/txpid.c b/flight/Modules/TxPID/txpid.c index 7cf477037..c0cdec462 100644 --- a/flight/Modules/TxPID/txpid.c +++ b/flight/Modules/TxPID/txpid.c @@ -54,18 +54,19 @@ #include "txpidsettings.h" #include "accessorydesired.h" +#include "manualcontrolcommand.h" #include "stabilizationsettings.h" // // Configuration // -#define SAMPLE_PERIOD_MS 200 -#define TELEMETRY_UPDATE_PERIOD_MS 1000 // 0 = update on change +#define SAMPLE_PERIOD_MS 100 +#define TELEMETRY_UPDATE_PERIOD_MS 1000 // 0 = update on change (default) // Sanity checks #if (TXPIDSETTINGS_PIDS_NUMELEM != TXPIDSETTINGS_INPUTS_NUMELEM) || \ - (TXPIDSETTINGS_PIDS_NUMELEM != TXPIDSETTINGS_MIN_NUMELEM) || \ - (TXPIDSETTINGS_PIDS_NUMELEM != TXPIDSETTINGS_MAX_NUMELEM) + (TXPIDSETTINGS_PIDS_NUMELEM != TXPIDSETTINGS_MINPID_NUMELEM) || \ + (TXPIDSETTINGS_PIDS_NUMELEM != TXPIDSETTINGS_MAXPID_NUMELEM) #error Invalid TxPID UAVObject definition (inconsistent number of field elements) #endif @@ -76,7 +77,7 @@ // Private functions static void updatePIDs(UAVObjEvent* ev); static uint8_t update(float *var, float val); -static float scale(float val, float min, float max); +static float scale(float val, float inMin, float inMax, float outMin, float outMax); /** * Initialise the module, called on startup @@ -129,76 +130,86 @@ static void updatePIDs(UAVObjEvent* ev) // Loop through every enabled instance for (uint8_t i = 0; i < TXPIDSETTINGS_PIDS_NUMELEM; i++) { if (inst.PIDs[i] != TXPIDSETTINGS_PIDS_DISABLED) { - if (AccessoryDesiredInstGet(inst.Inputs[i] - TXPIDSETTINGS_INPUTS_ACCESSORY0, &accessory) == 0) { - float value = scale(accessory.AccessoryVal, inst.Min[i], inst.Max[i]); - switch (inst.PIDs[i]) { - case TXPIDSETTINGS_PIDS_ROLLRATEKP: - needsUpdate |= update(&stab.RollRatePID[STABILIZATIONSETTINGS_ROLLRATEPID_KP], value); - break; - case TXPIDSETTINGS_PIDS_ROLLRATEKI: - needsUpdate |= update(&stab.RollRatePID[STABILIZATIONSETTINGS_ROLLRATEPID_KI], value); - break; - case TXPIDSETTINGS_PIDS_ROLLRATEKD: - needsUpdate |= update(&stab.RollRatePID[STABILIZATIONSETTINGS_ROLLRATEPID_KD], value); - break; - case TXPIDSETTINGS_PIDS_ROLLATTITUDEKP: - needsUpdate |= update(&stab.RollPI[STABILIZATIONSETTINGS_ROLLPI_KP], value); - break; - case TXPIDSETTINGS_PIDS_ROLLATTITUDEKI: - needsUpdate |= update(&stab.RollPI[STABILIZATIONSETTINGS_ROLLPI_KI], value); - break; - case TXPIDSETTINGS_PIDS_PITCHRATEKP: - needsUpdate |= update(&stab.PitchRatePID[STABILIZATIONSETTINGS_PITCHRATEPID_KP], value); - break; - case TXPIDSETTINGS_PIDS_PITCHRATEKI: - needsUpdate |= update(&stab.PitchRatePID[STABILIZATIONSETTINGS_PITCHRATEPID_KI], value); - break; - case TXPIDSETTINGS_PIDS_PITCHRATEKD: - needsUpdate |= update(&stab.PitchRatePID[STABILIZATIONSETTINGS_PITCHRATEPID_KD], value); - break; - case TXPIDSETTINGS_PIDS_PITCHATTITUDEKP: - needsUpdate |= update(&stab.PitchPI[STABILIZATIONSETTINGS_PITCHPI_KP], value); - break; - case TXPIDSETTINGS_PIDS_PITCHATTITUDEKI: - needsUpdate |= update(&stab.PitchPI[STABILIZATIONSETTINGS_PITCHPI_KI], value); - break; - case TXPIDSETTINGS_PIDS_ROLLPITCHRATEKP: - needsUpdate |= update(&stab.RollRatePID[STABILIZATIONSETTINGS_ROLLRATEPID_KP], value); - needsUpdate |= update(&stab.PitchRatePID[STABILIZATIONSETTINGS_PITCHRATEPID_KP], value); - break; - case TXPIDSETTINGS_PIDS_ROLLPITCHRATEKI: - needsUpdate |= update(&stab.RollRatePID[STABILIZATIONSETTINGS_ROLLRATEPID_KI], value); - needsUpdate |= update(&stab.PitchRatePID[STABILIZATIONSETTINGS_PITCHRATEPID_KI], value); - break; - case TXPIDSETTINGS_PIDS_ROLLPITCHRATEKD: - needsUpdate |= update(&stab.RollRatePID[STABILIZATIONSETTINGS_ROLLRATEPID_KD], value); - needsUpdate |= update(&stab.PitchRatePID[STABILIZATIONSETTINGS_PITCHRATEPID_KD], value); - break; - case TXPIDSETTINGS_PIDS_ROLLPITCHATTITUDEKP: - needsUpdate |= update(&stab.RollPI[STABILIZATIONSETTINGS_ROLLPI_KP], value); - needsUpdate |= update(&stab.PitchPI[STABILIZATIONSETTINGS_PITCHPI_KP], value); - break; - case TXPIDSETTINGS_PIDS_ROLLPITCHATTITUDEKI: - needsUpdate |= update(&stab.RollPI[STABILIZATIONSETTINGS_ROLLPI_KI], value); - needsUpdate |= update(&stab.PitchPI[STABILIZATIONSETTINGS_PITCHPI_KI], value); - break; - case TXPIDSETTINGS_PIDS_YAWRATEKP: - needsUpdate |= update(&stab.YawRatePID[STABILIZATIONSETTINGS_YAWRATEPID_KP], value); - break; - case TXPIDSETTINGS_PIDS_YAWRATEKI: - needsUpdate |= update(&stab.YawRatePID[STABILIZATIONSETTINGS_YAWRATEPID_KI], value); - break; - case TXPIDSETTINGS_PIDS_YAWRATEKD: - needsUpdate |= update(&stab.YawRatePID[STABILIZATIONSETTINGS_YAWRATEPID_KD], value); - break; - case TXPIDSETTINGS_PIDS_YAWATTITUDEKP: - needsUpdate |= update(&stab.YawPI[STABILIZATIONSETTINGS_YAWPI_KP], value); - break; - case TXPIDSETTINGS_PIDS_YAWATTITUDEKI: - needsUpdate |= update(&stab.YawPI[STABILIZATIONSETTINGS_YAWPI_KI], value); - break; - } + float value; + if (inst.Inputs[i] == TXPIDSETTINGS_INPUTS_THROTTLE) { + ManualControlCommandThrottleGet(&value); + value = scale(value, + inst.ThrottleRange[TXPIDSETTINGS_THROTTLERANGE_MIN], + inst.ThrottleRange[TXPIDSETTINGS_THROTTLERANGE_MAX], + inst.MinPID[i], inst.MaxPID[i]); + } else if (AccessoryDesiredInstGet(inst.Inputs[i] - TXPIDSETTINGS_INPUTS_ACCESSORY0, &accessory) == 0) { + value = scale(accessory.AccessoryVal, -1.0, 1.0, inst.MinPID[i], inst.MaxPID[i]); + } else { + continue; + } + + switch (inst.PIDs[i]) { + case TXPIDSETTINGS_PIDS_ROLLRATEKP: + needsUpdate |= update(&stab.RollRatePID[STABILIZATIONSETTINGS_ROLLRATEPID_KP], value); + break; + case TXPIDSETTINGS_PIDS_ROLLRATEKI: + needsUpdate |= update(&stab.RollRatePID[STABILIZATIONSETTINGS_ROLLRATEPID_KI], value); + break; + case TXPIDSETTINGS_PIDS_ROLLRATEKD: + needsUpdate |= update(&stab.RollRatePID[STABILIZATIONSETTINGS_ROLLRATEPID_KD], value); + break; + case TXPIDSETTINGS_PIDS_ROLLATTITUDEKP: + needsUpdate |= update(&stab.RollPI[STABILIZATIONSETTINGS_ROLLPI_KP], value); + break; + case TXPIDSETTINGS_PIDS_ROLLATTITUDEKI: + needsUpdate |= update(&stab.RollPI[STABILIZATIONSETTINGS_ROLLPI_KI], value); + break; + case TXPIDSETTINGS_PIDS_PITCHRATEKP: + needsUpdate |= update(&stab.PitchRatePID[STABILIZATIONSETTINGS_PITCHRATEPID_KP], value); + break; + case TXPIDSETTINGS_PIDS_PITCHRATEKI: + needsUpdate |= update(&stab.PitchRatePID[STABILIZATIONSETTINGS_PITCHRATEPID_KI], value); + break; + case TXPIDSETTINGS_PIDS_PITCHRATEKD: + needsUpdate |= update(&stab.PitchRatePID[STABILIZATIONSETTINGS_PITCHRATEPID_KD], value); + break; + case TXPIDSETTINGS_PIDS_PITCHATTITUDEKP: + needsUpdate |= update(&stab.PitchPI[STABILIZATIONSETTINGS_PITCHPI_KP], value); + break; + case TXPIDSETTINGS_PIDS_PITCHATTITUDEKI: + needsUpdate |= update(&stab.PitchPI[STABILIZATIONSETTINGS_PITCHPI_KI], value); + break; + case TXPIDSETTINGS_PIDS_ROLLPITCHRATEKP: + needsUpdate |= update(&stab.RollRatePID[STABILIZATIONSETTINGS_ROLLRATEPID_KP], value); + needsUpdate |= update(&stab.PitchRatePID[STABILIZATIONSETTINGS_PITCHRATEPID_KP], value); + break; + case TXPIDSETTINGS_PIDS_ROLLPITCHRATEKI: + needsUpdate |= update(&stab.RollRatePID[STABILIZATIONSETTINGS_ROLLRATEPID_KI], value); + needsUpdate |= update(&stab.PitchRatePID[STABILIZATIONSETTINGS_PITCHRATEPID_KI], value); + break; + case TXPIDSETTINGS_PIDS_ROLLPITCHRATEKD: + needsUpdate |= update(&stab.RollRatePID[STABILIZATIONSETTINGS_ROLLRATEPID_KD], value); + needsUpdate |= update(&stab.PitchRatePID[STABILIZATIONSETTINGS_PITCHRATEPID_KD], value); + break; + case TXPIDSETTINGS_PIDS_ROLLPITCHATTITUDEKP: + needsUpdate |= update(&stab.RollPI[STABILIZATIONSETTINGS_ROLLPI_KP], value); + needsUpdate |= update(&stab.PitchPI[STABILIZATIONSETTINGS_PITCHPI_KP], value); + break; + case TXPIDSETTINGS_PIDS_ROLLPITCHATTITUDEKI: + needsUpdate |= update(&stab.RollPI[STABILIZATIONSETTINGS_ROLLPI_KI], value); + needsUpdate |= update(&stab.PitchPI[STABILIZATIONSETTINGS_PITCHPI_KI], value); + break; + case TXPIDSETTINGS_PIDS_YAWRATEKP: + needsUpdate |= update(&stab.YawRatePID[STABILIZATIONSETTINGS_YAWRATEPID_KP], value); + break; + case TXPIDSETTINGS_PIDS_YAWRATEKI: + needsUpdate |= update(&stab.YawRatePID[STABILIZATIONSETTINGS_YAWRATEPID_KI], value); + break; + case TXPIDSETTINGS_PIDS_YAWRATEKD: + needsUpdate |= update(&stab.YawRatePID[STABILIZATIONSETTINGS_YAWRATEPID_KD], value); + break; + case TXPIDSETTINGS_PIDS_YAWATTITUDEKP: + needsUpdate |= update(&stab.YawPI[STABILIZATIONSETTINGS_YAWPI_KP], value); + break; + case TXPIDSETTINGS_PIDS_YAWATTITUDEKI: + needsUpdate |= update(&stab.YawPI[STABILIZATIONSETTINGS_YAWPI_KI], value); + break; } } } @@ -207,31 +218,41 @@ static void updatePIDs(UAVObjEvent* ev) } /** - * Scale input value to [min..max] (min can be greater than max) + * Scales input val from [inMin..inMax] range to [outMin..outMax]. + * If val is out of input range (inMin <= inMax), it will be bound. + * (outMin > outMax) is ok, in that case output will be decreasing. + * * \returns scaled value */ -static float scale(float val, float min, float max) { - float v; +static float scale(float val, float inMin, float inMax, float outMin, float outMax) +{ + // bound input value + if (val > inMax) val = inMax; + if (val < inMin) val = inMin; - if (min > max) { - val = -val; - v = min; - min = max; - max = v; + // normalize input value to [0..1] + if (inMax <= inMin) + val = 0.0; + else + val = (val - inMin) / (inMax - inMin); + + // update output bounds + if (outMin > outMax) { + float t = outMin; + outMin = outMax; + outMax = t; + val = 1.0 - val; } - v = min + (max - min) * (val + 1.0) * 0.5; - if (v > max) v = max; - if (v < min) v = min; - - return v; + return (outMax - outMin) * val + outMin; } /** - * Updates value - * \returns 1 if update needed or 0 if not + * Updates var using val if needed. + * \returns 1 if updated, 0 otherwise */ -static uint8_t update(float *var, float val) { +static uint8_t update(float *var, float val) +{ if (*var != val) { *var = val; return 1; diff --git a/shared/uavobjectdefinition/txpidsettings.xml b/shared/uavobjectdefinition/txpidsettings.xml index 5fd01aaad..4a024541a 100644 --- a/shared/uavobjectdefinition/txpidsettings.xml +++ b/shared/uavobjectdefinition/txpidsettings.xml @@ -1,6 +1,11 @@ Settings used by @ref TxPID optional module to tune PID settings using R/C transmitter + + - - - + + From a9137e9db1225974fa7d5494af712779e663ec92 Mon Sep 17 00:00:00 2001 From: Oleg Semyonov Date: Mon, 19 Sep 2011 16:16:28 +0300 Subject: [PATCH 04/19] TxPID: add update mode option as a workaround for GCS interaction --- flight/Modules/TxPID/txpid.c | 18 ++++++++++++++++-- shared/uavobjectdefinition/txpidsettings.xml | 1 + 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/flight/Modules/TxPID/txpid.c b/flight/Modules/TxPID/txpid.c index c0cdec462..3247435d0 100644 --- a/flight/Modules/TxPID/txpid.c +++ b/flight/Modules/TxPID/txpid.c @@ -56,12 +56,13 @@ #include "accessorydesired.h" #include "manualcontrolcommand.h" #include "stabilizationsettings.h" +#include "flightstatus.h" // // Configuration // -#define SAMPLE_PERIOD_MS 100 -#define TELEMETRY_UPDATE_PERIOD_MS 1000 // 0 = update on change (default) +#define SAMPLE_PERIOD_MS 200 +#define TELEMETRY_UPDATE_PERIOD_MS 0 // 0 = update on change (default) // Sanity checks #if (TXPIDSETTINGS_PIDS_NUMELEM != TXPIDSETTINGS_INPUTS_NUMELEM) || \ @@ -97,6 +98,9 @@ int32_t TxPIDInitialize(void) // Change StabilizationSettings update rate from OnChange to periodic // to prevent telemetry link flooding with frequent updates in case of // control channel jitter. + // Warning: saving to flash with this code active will change the + // StabilizationSettings update rate permanently. Use Metadata via + // browser to reset to defaults (telemetryAcked=true, OnChange). UAVObjMetadata metadata; StabilizationSettingsGetMetadata(&metadata); metadata.telemetryAcked = 0; @@ -121,6 +125,16 @@ static void updatePIDs(UAVObjEvent* ev) TxPIDSettingsData inst; TxPIDSettingsGet(&inst); + + if (inst.UpdateMode == TXPIDSETTINGS_UPDATEMODE_NEVER) + return; + + uint8_t armed; + FlightStatusArmedGet(&armed); + if ((inst.UpdateMode == TXPIDSETTINGS_UPDATEMODE_WHENARMED) && + (armed == FLIGHTSTATUS_ARMED_DISARMED)) + return; + StabilizationSettingsData stab; StabilizationSettingsGet(&stab); AccessoryDesiredData accessory; diff --git a/shared/uavobjectdefinition/txpidsettings.xml b/shared/uavobjectdefinition/txpidsettings.xml index 4a024541a..0cbba9ca5 100644 --- a/shared/uavobjectdefinition/txpidsettings.xml +++ b/shared/uavobjectdefinition/txpidsettings.xml @@ -1,6 +1,7 @@ Settings used by @ref TxPID optional module to tune PID settings using R/C transmitter + Date: Sat, 24 Dec 2011 15:37:14 +0200 Subject: [PATCH 05/19] TxPID: initialize the AccessoryDesired object before use its handle Also save few bytes of static RAM and add PIOS_Assert(0) for unhandled cases. --- flight/Modules/TxPID/txpid.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/flight/Modules/TxPID/txpid.c b/flight/Modules/TxPID/txpid.c index 28898208d..b3e8377a2 100644 --- a/flight/Modules/TxPID/txpid.c +++ b/flight/Modules/TxPID/txpid.c @@ -87,8 +87,6 @@ static float scale(float val, float inMin, float inMax, float outMin, float outM */ int32_t TxPIDInitialize(void) { - static UAVObjEvent ev; - bool txPIDEnabled; uint8_t optionalModules[HWSETTINGS_OPTIONALMODULES_NUMELEM]; @@ -101,12 +99,16 @@ int32_t TxPIDInitialize(void) txPIDEnabled = false; if (txPIDEnabled) { - ev.obj = AccessoryDesiredHandle(); - ev.instId = 0; - ev.event = 0; - // Initialize settings object TxPIDSettingsInitialize(); + AccessoryDesiredInitialize(); + + UAVObjEvent ev = { + .obj = AccessoryDesiredHandle(), + .instId = 0, + .event = 0, + }; + EventPeriodicCallbackCreate(&ev, updatePIDs, SAMPLE_PERIOD_MS / portTICK_RATE_MS); #if (TELEMETRY_UPDATE_PERIOD_MS != 0) // Change StabilizationSettings update rate from OnChange to periodic @@ -116,6 +118,7 @@ int32_t TxPIDInitialize(void) // StabilizationSettings update rate permanently. Use Metadata via // browser to reset to defaults (telemetryAcked=true, OnChange). UAVObjMetadata metadata; + StabilizationSettingsInitialize(); StabilizationSettingsGetMetadata(&metadata); metadata.telemetryAcked = 0; metadata.telemetryUpdateMode = UPDATEMODE_PERIODIC; @@ -123,9 +126,6 @@ int32_t TxPIDInitialize(void) StabilizationSettingsSetMetadata(&metadata); #endif - // Setup the callback function - EventPeriodicCallbackCreate(&ev, updatePIDs, SAMPLE_PERIOD_MS / portTICK_RATE_MS); - return 0; } @@ -249,6 +249,8 @@ static void updatePIDs(UAVObjEvent* ev) case TXPIDSETTINGS_PIDS_YAWATTITUDEKI: needsUpdate |= update(&stab.YawPI[STABILIZATIONSETTINGS_YAWPI_KI], value); break; + default: + PIOS_Assert(0); } } } From 887a8e89ec490432d02bbaa21f6988a698ca42e2 Mon Sep 17 00:00:00 2001 From: Oleg Semyonov Date: Sat, 14 Jan 2012 17:38:17 +0200 Subject: [PATCH 06/19] TxPID: fix units for ThrottleRange --- shared/uavobjectdefinition/txpidsettings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/uavobjectdefinition/txpidsettings.xml b/shared/uavobjectdefinition/txpidsettings.xml index 0cbba9ca5..700db43fa 100644 --- a/shared/uavobjectdefinition/txpidsettings.xml +++ b/shared/uavobjectdefinition/txpidsettings.xml @@ -6,7 +6,7 @@ elementnames="Instance1,Instance2,Instance3" options="Throttle,Accessory0,Accessory1,Accessory2,Accessory3,Accessory4,Accessory5" defaultvalue="Throttle,Accessory0,Accessory1"/> - + insertTab(ConfigGadgetWidget::camerastabilization, qwd, QIcon(":/configgadget/images/camera.png"), QString("Camera Stab")); + qwd = new ConfigTxPIDWidget(this); + ftw->insertTab(ConfigGadgetWidget::txpid, qwd, QIcon(":/configgadget/images/txpid.png"), QString("TxPID")); // qwd = new ConfigPipXtremeWidget(this); // ftw->insertTab(5, qwd, QIcon(":/configgadget/images/PipXtreme.png"), QString("PipXtreme")); diff --git a/ground/openpilotgcs/src/plugins/config/configgadgetwidget.h b/ground/openpilotgcs/src/plugins/config/configgadgetwidget.h index 633253507..1a650ded6 100644 --- a/ground/openpilotgcs/src/plugins/config/configgadgetwidget.h +++ b/ground/openpilotgcs/src/plugins/config/configgadgetwidget.h @@ -50,7 +50,7 @@ class ConfigGadgetWidget: public QWidget public: ConfigGadgetWidget(QWidget *parent = 0); ~ConfigGadgetWidget(); - enum widgetTabs {hardware=0, aircraft, input, output, ins, stabilization, camerastabilization}; + enum widgetTabs { hardware = 0, aircraft, input, output, ins, stabilization, camerastabilization, txpid }; public slots: void onAutopilotConnect(); diff --git a/ground/openpilotgcs/src/plugins/config/configtxpidwidget.cpp b/ground/openpilotgcs/src/plugins/config/configtxpidwidget.cpp new file mode 100644 index 000000000..2c50095d8 --- /dev/null +++ b/ground/openpilotgcs/src/plugins/config/configtxpidwidget.cpp @@ -0,0 +1,105 @@ +/** + ****************************************************************************** + * + * @file configtxpidswidget.cpp + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012. + * @addtogroup GCSPlugins GCS Plugins + * @{ + * @addtogroup ConfigPlugin Config Plugin + * @{ + * @brief The Configuration Gadget used to configure TxPID module + *****************************************************************************/ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "configtxpidwidget.h" +#include "txpidsettings.h" +#include "hwsettings.h" + +ConfigTxPIDWidget::ConfigTxPIDWidget(QWidget *parent) : ConfigTaskWidget(parent) +{ + m_txpid = new Ui_TxPIDWidget(); + m_txpid->setupUi(this); + + setupButtons(m_txpid->Apply, m_txpid->Save); + + // Cannot use addUAVObjectToWidgetRelation() for OptionaModules enum because + // QCheckBox returns bool (0 or -1) and this value is then set to enum instead + // or enum options + connect(HwSettings::GetInstance(getObjectManager()), SIGNAL(objectUpdated(UAVObject *)), this, SLOT(refreshValues())); + connect(m_txpid->Apply, SIGNAL(clicked()), this, SLOT(applySettings())); + connect(m_txpid->Save, SIGNAL(clicked()), this, SLOT(saveSettings())); + + addUAVObjectToWidgetRelation("TxPIDSettings", "PIDs", m_txpid->PID1, TxPIDSettings::PIDS_INSTANCE1); + addUAVObjectToWidgetRelation("TxPIDSettings", "PIDs", m_txpid->PID2, TxPIDSettings::PIDS_INSTANCE2); + addUAVObjectToWidgetRelation("TxPIDSettings", "PIDs", m_txpid->PID3, TxPIDSettings::PIDS_INSTANCE3); + + addUAVObjectToWidgetRelation("TxPIDSettings", "Inputs", m_txpid->Input1, TxPIDSettings::INPUTS_INSTANCE1); + addUAVObjectToWidgetRelation("TxPIDSettings", "Inputs", m_txpid->Input2, TxPIDSettings::INPUTS_INSTANCE2); + addUAVObjectToWidgetRelation("TxPIDSettings", "Inputs", m_txpid->Input3, TxPIDSettings::INPUTS_INSTANCE3); + + addUAVObjectToWidgetRelation("TxPIDSettings", "MinPID", m_txpid->MinPID1, TxPIDSettings::MINPID_INSTANCE1); + addUAVObjectToWidgetRelation("TxPIDSettings", "MinPID", m_txpid->MinPID2, TxPIDSettings::MINPID_INSTANCE2); + addUAVObjectToWidgetRelation("TxPIDSettings", "MinPID", m_txpid->MinPID3, TxPIDSettings::MINPID_INSTANCE3); + + addUAVObjectToWidgetRelation("TxPIDSettings", "MaxPID", m_txpid->MaxPID1, TxPIDSettings::MAXPID_INSTANCE1); + addUAVObjectToWidgetRelation("TxPIDSettings", "MaxPID", m_txpid->MaxPID2, TxPIDSettings::MAXPID_INSTANCE2); + addUAVObjectToWidgetRelation("TxPIDSettings", "MaxPID", m_txpid->MaxPID3, TxPIDSettings::MAXPID_INSTANCE3); + + addUAVObjectToWidgetRelation("TxPIDSettings", "ThrottleRange", m_txpid->ThrottleMin, TxPIDSettings::THROTTLERANGE_MIN); + addUAVObjectToWidgetRelation("TxPIDSettings", "ThrottleRange", m_txpid->ThrottleMax, TxPIDSettings::THROTTLERANGE_MAX); + + addUAVObjectToWidgetRelation("TxPIDSettings", "UpdateMode", m_txpid->UpdateMode); + + enableControls(false); + populateWidgets(); + refreshWidgetsValues(); +} + +ConfigTxPIDWidget::~ConfigTxPIDWidget() +{ + // Do nothing +} + +void ConfigTxPIDWidget::refreshValues() +{ + HwSettings *hwSettings = HwSettings::GetInstance(getObjectManager()); + HwSettings::DataFields hwSettingsData = hwSettings->getData(); + m_txpid->TxPIDEnable->setChecked( + hwSettingsData.OptionalModules[HwSettings::OPTIONALMODULES_TXPID] == HwSettings::OPTIONALMODULES_ENABLED); +} + +void ConfigTxPIDWidget::applySettings() +{ + HwSettings *hwSettings = HwSettings::GetInstance(getObjectManager()); + HwSettings::DataFields hwSettingsData = hwSettings->getData(); + hwSettingsData.OptionalModules[HwSettings::OPTIONALMODULES_TXPID] = + m_txpid->TxPIDEnable->isChecked() ? HwSettings::OPTIONALMODULES_ENABLED : HwSettings::OPTIONALMODULES_DISABLED; + hwSettings->setData(hwSettingsData); +} + +void ConfigTxPIDWidget::saveSettings() +{ + applySettings(); + UAVObject *obj = HwSettings::GetInstance(getObjectManager()); + saveObjectToSD(obj); +} + + +/** + @} + @} + */ diff --git a/ground/openpilotgcs/src/plugins/config/configtxpidwidget.h b/ground/openpilotgcs/src/plugins/config/configtxpidwidget.h new file mode 100644 index 000000000..9723e141f --- /dev/null +++ b/ground/openpilotgcs/src/plugins/config/configtxpidwidget.h @@ -0,0 +1,50 @@ +/** + ****************************************************************************** + * + * @file configtxpidwidget.h + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012. + * @addtogroup GCSPlugins GCS Plugins + * @{ + * @addtogroup ConfigPlugin Config Plugin + * @{ + * @brief The Configuration Gadget used to configure TxPID module + *****************************************************************************/ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef CONFIGTXPIDWIDGET_H +#define CONFIGTXPIDWIDGET_H + +#include "ui_txpid.h" +#include "configtaskwidget.h" + +class ConfigTxPIDWidget : public ConfigTaskWidget +{ + Q_OBJECT + +public: + ConfigTxPIDWidget(QWidget *parent = 0); + ~ConfigTxPIDWidget(); + +private: + Ui_TxPIDWidget *m_txpid; + +private slots: + void refreshValues(); + void applySettings(); + void saveSettings(); +}; + +#endif // CONFIGTXPIDWIDGET_H diff --git a/ground/openpilotgcs/src/plugins/config/images/txpid.png b/ground/openpilotgcs/src/plugins/config/images/txpid.png new file mode 100644 index 0000000000000000000000000000000000000000..7b2f6f035344d7250b5eb4c821cbc410bdc3289b GIT binary patch literal 149296 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 + + TxPIDWidget + + + + 0 + 0 + 720 + 567 + + + + Form + + + + + + QFrame::NoFrame + + + true + + + + + 0 + 0 + 702 + 497 + + + + + + + This module will periodically update values of stabilization PID settings +depending on configured input control channels. New values of stabilization +settings are not saved to flash, but updated in RAM. It is expected that the +module will be enabled only for tuning. When desired values are found, they +can be read via GCS and saved permanently. Then this module should be +disabled again. + +Up to 3 separate PID options (or option pairs) can be selected and updated. + + + Enable TxPID module + + + + + + + After enabling the module, you must power cycle before using and configuring. + + + + + + + Qt::Horizontal + + + + + + + Qt::Vertical + + + QSizePolicy::Preferred + + + + 20 + 10 + + + + + + + + + 0 + 0 + + + + + 0 + 100 + + + + Module Settings + + + + + + PID option + + + Qt::AlignCenter + + + + + + + Control Source + + + Qt::AlignCenter + + + + + + + Min + + + Qt::AlignCenter + + + + + + + Max + + + Qt::AlignCenter + + + + + + + Instance 1 + + + + + + + Select PID option or option pair to update. +Set to Disabled if not used. + + + + + + + Select input used as a control source for this instance. +It can be one of Accessory channels or Throttle channel. + +If Accessory channel is chosen then its range [0..1] will be mapped +to PID range [Min..Max] defined for this instance. + +If Throttle channel is chosen then Throttle range [Min..Max] will +be mapped to PID range [Min..Max] defined for this instance. If +Throttle is out of bounds then PID Min and Max values will be used +accordingly. + +Note that it is possible to set PID Min > Max. In that case increasing +control input value will decrease the PID option value. This can be +used, for instance, to decrease PID value when increasing Throttle. + + + + + + + Minimum PID value mapped to Accessory channel = 0 or +Throttle channel lesser or equal to Throttle Min value. + + + 6 + + + 0.000100000000000 + + + + + + + Maximum PID value mapped to Accessory channel = 1 or +Throttle channel greater or equal to Throttle Max value. + + + 6 + + + 0.000100000000000 + + + + + + + Instance 2 + + + + + + + Select PID option or option pair to update. +Set to Disabled if not used. + + + + + + + Select input used as a control source for this instance. +It can be one of Accessory channels or Throttle channel. + +If Accessory channel is chosen then its range [0..1] will be mapped +to PID range [Min..Max] defined for this instance. + +If Throttle channel is chosen then Throttle range [Min..Max] will +be mapped to PID range [Min..Max] defined for this instance. If +Throttle is out of bounds then PID Min and Max values will be used +accordingly. + +Note that it is possible to set PID Min > Max. In that case increasing +control input value will decrease the PID option value. This can be +used, for instance, to decrease PID value when increasing Throttle. + + + + + + + Minimum PID value mapped to Accessory channel = 0 or +Throttle channel lesser or equal to Throttle Min value. + + + 6 + + + 0.000100000000000 + + + + + + + Maximum PID value mapped to Accessory channel = 1 or +Throttle channel greater or equal to Throttle Max value. + + + 6 + + + 0.000100000000000 + + + + + + + Instance 3 + + + + + + + Select PID option or option pair to update. +Set to Disabled if not used. + + + + + + + Select input used as a control source for this instance. +It can be one of Accessory channels or Throttle channel. + +If Accessory channel is chosen then its range [0..1] will be mapped +to PID range [Min..Max] defined for this instance. + +If Throttle channel is chosen then Throttle range [Min..Max] will +be mapped to PID range [Min..Max] defined for this instance. If +Throttle is out of bounds then PID Min and Max values will be used +accordingly. + +Note that it is possible to set PID Min > Max. In that case increasing +control input value will decrease the PID option value. This can be +used, for instance, to decrease PID value when increasing Throttle. + + + + + + + Minimum PID value mapped to Accessory channel = 0 or +Throttle channel lesser or equal to Throttle Min value. + + + 6 + + + 0.000100000000000 + + + + + + + Maximum PID value mapped to Accessory channel = 1 or +Throttle channel greater or equal to Throttle Max value. + + + 6 + + + 0.000100000000000 + + + + + + + Update Mode + + + + + + + PID values update mode which can be set to: +- Never: this disables PID updates (but module still will be run if enabled), +- When Armed: PID updated only when system is armed, +- Always: PID updated always regardless of arm state. + +Since the GCS updates GUI PID values in real time on change, could be +tricky to change other PID values from the GUI if the module is enabled +and constantly updates stabilization settings object. As a workaround, +this option can be used to temporarily disable updates or enable them +only when system is armed without disabling the module. + + + + + + + Throttle Range + + + + + + + Throttle channel lower bound mapped to PID Min value + + + 1.000000000000000 + + + 0.010000000000000 + + + + + + + Throttle channel upper bound mapped to PID Max value + + + 1.000000000000000 + + + 0.010000000000000 + + + + + + + Min + + + Qt::AlignCenter + + + + + + + Max + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + QSizePolicy::Preferred + + + + 20 + 5 + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Send settings to the board but do not save to the non-volatile memory + + + Apply + + + + + + + Send settings to the board and save to the non-volatile memory + + + Save + + + false + + + + + + + + + + + TxPIDEnable + Apply + Save + scrollArea + + + + From 4fcd0f97c56eaff22cb2f1c595591a74d72b48d1 Mon Sep 17 00:00:00 2001 From: Oleg Semyonov Date: Sat, 14 Jan 2012 22:58:51 +0200 Subject: [PATCH 08/19] ConfigTaskWidget: add QDoubleSpinBox to addUAVObjectToWidgetRelation() --- .../src/plugins/config/configtaskwidget.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ground/openpilotgcs/src/plugins/config/configtaskwidget.cpp b/ground/openpilotgcs/src/plugins/config/configtaskwidget.cpp index c7a082507..05955f7d1 100644 --- a/ground/openpilotgcs/src/plugins/config/configtaskwidget.cpp +++ b/ground/openpilotgcs/src/plugins/config/configtaskwidget.cpp @@ -193,6 +193,10 @@ void ConfigTaskWidget::populateWidgets() { cb->setValue(ow->field->getValue(ow->index).toInt()/ow->scale); } + else if (QDoubleSpinBox * cb = qobject_cast(ow->widget)) + { + cb->setValue(ow->field->getValue(ow->index).toDouble()/ow->scale); + } else if(QSlider * cb=qobject_cast(ow->widget)) { cb->setValue(ow->field->getValue(ow->index).toInt()/ow->scale); @@ -226,6 +230,10 @@ void ConfigTaskWidget::refreshWidgetsValues() { cb->setValue(ow->field->getValue(ow->index).toInt()/ow->scale); } + else if (QDoubleSpinBox * cb = qobject_cast(ow->widget)) + { + cb->setValue(ow->field->getValue(ow->index).toDouble()/ow->scale); + } else if(QSlider * cb=qobject_cast(ow->widget)) { cb->setValue(ow->field->getValue(ow->index).toInt()/ow->scale); @@ -258,6 +266,10 @@ void ConfigTaskWidget::updateObjectsFromWidgets() { ow->field->setValue(cb->value()* ow->scale,ow->index); } + else if (QDoubleSpinBox * cb = qobject_cast(ow->widget)) + { + ow->field->setValue(cb->value()* ow->scale,ow->index); + } else if(QSlider * cb=qobject_cast(ow->widget)) { ow->field->setValue(cb->value()* ow->scale,ow->index); From 5c20f1e37d09975966089956756d490bf87fd3e1 Mon Sep 17 00:00:00 2001 From: Oleg Semyonov Date: Sat, 14 Jan 2012 23:00:23 +0200 Subject: [PATCH 09/19] ConfigTaskWidget: add some qDebug() statements for unhandled widget types --- .../src/plugins/config/configtaskwidget.cpp | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/ground/openpilotgcs/src/plugins/config/configtaskwidget.cpp b/ground/openpilotgcs/src/plugins/config/configtaskwidget.cpp index 05955f7d1..fe890984b 100644 --- a/ground/openpilotgcs/src/plugins/config/configtaskwidget.cpp +++ b/ground/openpilotgcs/src/plugins/config/configtaskwidget.cpp @@ -70,8 +70,10 @@ void ConfigTaskWidget::addUAVObjectToWidgetRelation(QString object, QString fiel connect(obj, SIGNAL(objectUpdated(UAVObject*)), this, SLOT(refreshWidgetsValues())); } //smartsave->addObject(obj); - if(!field.isEmpty() && obj) + if(!field.isEmpty() && obj) { _field = obj->getField(QString(field)); + qDebug() << "Non-existent object field " << object + "." + field << " in " << __func__; + } objectToWidget * ow=new objectToWidget(); ow->field=_field; ow->object=obj; @@ -117,6 +119,10 @@ void ConfigTaskWidget::addUAVObjectToWidgetRelation(QString object, QString fiel { connect(cb,SIGNAL(clicked()),this,SLOT(widgetsContentsChanged())); } + else + { + qDebug() << "Unhandled widget type in " << __func__; + } } @@ -205,6 +211,10 @@ void ConfigTaskWidget::populateWidgets() { cb->setChecked(ow->field->getValue(ow->index).toBool()); } + else + { + qDebug() << "Unhandled widget type in " << __func__; + } } setDirty(dirtyBack); } @@ -242,6 +252,10 @@ void ConfigTaskWidget::refreshWidgetsValues() { cb->setChecked(ow->field->getValue(ow->index).toBool()); } + else + { + qDebug() << "Unhandled widget type in " << __func__; + } } setDirty(dirtyBack); } @@ -278,6 +292,10 @@ void ConfigTaskWidget::updateObjectsFromWidgets() { ow->field->setValue((cb->isChecked()?"TRUE":"FALSE"),ow->index); } + else + { + qDebug() << "Unhandled widget type in " << __func__; + } } } From 213a915f67b34a9ed555a2878d6fc5c4933c8892 Mon Sep 17 00:00:00 2001 From: David Ankers Date: Wed, 1 Feb 2012 20:09:43 +1100 Subject: [PATCH 10/19] Remove flightplanner screen --- .../src/plugins/coreplugin/OpenPilotGCS.xml | 79 ++++--------------- 1 file changed, 16 insertions(+), 63 deletions(-) diff --git a/ground/openpilotgcs/src/plugins/coreplugin/OpenPilotGCS.xml b/ground/openpilotgcs/src/plugins/coreplugin/OpenPilotGCS.xml index fb6187949..7394a661f 100644 --- a/ground/openpilotgcs/src/plugins/coreplugin/OpenPilotGCS.xml +++ b/ground/openpilotgcs/src/plugins/coreplugin/OpenPilotGCS.xml @@ -2650,53 +2650,6 @@ UAVGadgetManagerV1 - false - - - OPMapGadget - - default - - uavGadget - - - - ModelViewGadget - - Test Quad X - - uavGadget - - - - DialGadget - - Attitude - - uavGadget - - - DialGadget - - Compass - - uavGadget - - 1 - @Variant(AAAACQAAAAA=) - splitter - - 2 - @Variant(AAAACQAAAAIAAAACAAABiwAAAAIAAADs) - splitter - - 1 - @Variant(AAAACQAAAAIAAAACAAAD1AAAAAIAAAGB) - splitter - - UAVGadgetManagerV1 - - false @@ -2751,8 +2704,8 @@ splitter UAVGadgetManagerV1 - - + + false @@ -2822,8 +2775,8 @@ splitter UAVGadgetManagerV1 - - + + false @@ -2869,30 +2822,30 @@ splitter UAVGadgetManagerV1 - + @ByteArray(AAAA/wAAAAD9AAAAAAAABQAAAALCAAAABAAAAAQAAAABAAAACPwAAAAA) :/core/images/ah.png - :/core/images/openpilot_logo_64.png :/core/images/config.png - :/core/images/world.png - :/core/images/scopes.png - :/core/images/joystick.png - :/core/images/cog.png + :/core/images/scopes.png + :/core/images/joystick.png + :/core/images/cog.png + :/core/images/openpilot_logo_64.png :/core/images/openpilot_logo_64.png :/core/images/openpilot_logo_64.png :/core/images/openpilot_logo_64.png - 6 + :/core/images/openpilot_logo_64.png + 5 Flight data - Workspace10 Configuration - Flight Planner - Scopes - HITL - Firmware + Scopes + HITL + Firmware + Workspace6 Workspace7 Workspace8 Workspace9 + Workspace10 From 403e9cd22003df858b7e7aab1362c01eaa09007b Mon Sep 17 00:00:00 2001 From: Oleg Semyonov Date: Tue, 21 Feb 2012 19:57:10 +0200 Subject: [PATCH 11/19] TxPID module: add ILimit values to the list of settings --- flight/Modules/TxPID/txpid.c | 26 ++++++++++++++++++++ shared/uavobjectdefinition/txpidsettings.xml | 4 ++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/flight/Modules/TxPID/txpid.c b/flight/Modules/TxPID/txpid.c index b3e8377a2..cff9c1d9e 100644 --- a/flight/Modules/TxPID/txpid.c +++ b/flight/Modules/TxPID/txpid.c @@ -193,12 +193,18 @@ static void updatePIDs(UAVObjEvent* ev) case TXPIDSETTINGS_PIDS_ROLLRATEKD: needsUpdate |= update(&stab.RollRatePID[STABILIZATIONSETTINGS_ROLLRATEPID_KD], value); break; + case TXPIDSETTINGS_PIDS_ROLLRATEILIMIT: + needsUpdate |= update(&stab.RollRatePID[STABILIZATIONSETTINGS_ROLLRATEPID_ILIMIT], value); + break; case TXPIDSETTINGS_PIDS_ROLLATTITUDEKP: needsUpdate |= update(&stab.RollPI[STABILIZATIONSETTINGS_ROLLPI_KP], value); break; case TXPIDSETTINGS_PIDS_ROLLATTITUDEKI: needsUpdate |= update(&stab.RollPI[STABILIZATIONSETTINGS_ROLLPI_KI], value); break; + case TXPIDSETTINGS_PIDS_ROLLATTITUDEILIMIT: + needsUpdate |= update(&stab.RollPI[STABILIZATIONSETTINGS_ROLLPI_ILIMIT], value); + break; case TXPIDSETTINGS_PIDS_PITCHRATEKP: needsUpdate |= update(&stab.PitchRatePID[STABILIZATIONSETTINGS_PITCHRATEPID_KP], value); break; @@ -208,12 +214,18 @@ static void updatePIDs(UAVObjEvent* ev) case TXPIDSETTINGS_PIDS_PITCHRATEKD: needsUpdate |= update(&stab.PitchRatePID[STABILIZATIONSETTINGS_PITCHRATEPID_KD], value); break; + case TXPIDSETTINGS_PIDS_PITCHRATEILIMIT: + needsUpdate |= update(&stab.PitchRatePID[STABILIZATIONSETTINGS_PITCHRATEPID_ILIMIT], value); + break; case TXPIDSETTINGS_PIDS_PITCHATTITUDEKP: needsUpdate |= update(&stab.PitchPI[STABILIZATIONSETTINGS_PITCHPI_KP], value); break; case TXPIDSETTINGS_PIDS_PITCHATTITUDEKI: needsUpdate |= update(&stab.PitchPI[STABILIZATIONSETTINGS_PITCHPI_KI], value); break; + case TXPIDSETTINGS_PIDS_PITCHATTITUDEILIMIT: + needsUpdate |= update(&stab.PitchPI[STABILIZATIONSETTINGS_PITCHPI_ILIMIT], value); + break; case TXPIDSETTINGS_PIDS_ROLLPITCHRATEKP: needsUpdate |= update(&stab.RollRatePID[STABILIZATIONSETTINGS_ROLLRATEPID_KP], value); needsUpdate |= update(&stab.PitchRatePID[STABILIZATIONSETTINGS_PITCHRATEPID_KP], value); @@ -226,6 +238,10 @@ static void updatePIDs(UAVObjEvent* ev) needsUpdate |= update(&stab.RollRatePID[STABILIZATIONSETTINGS_ROLLRATEPID_KD], value); needsUpdate |= update(&stab.PitchRatePID[STABILIZATIONSETTINGS_PITCHRATEPID_KD], value); break; + case TXPIDSETTINGS_PIDS_ROLLPITCHRATEILIMIT: + needsUpdate |= update(&stab.RollRatePID[STABILIZATIONSETTINGS_ROLLRATEPID_ILIMIT], value); + needsUpdate |= update(&stab.PitchRatePID[STABILIZATIONSETTINGS_PITCHRATEPID_ILIMIT], value); + break; case TXPIDSETTINGS_PIDS_ROLLPITCHATTITUDEKP: needsUpdate |= update(&stab.RollPI[STABILIZATIONSETTINGS_ROLLPI_KP], value); needsUpdate |= update(&stab.PitchPI[STABILIZATIONSETTINGS_PITCHPI_KP], value); @@ -234,6 +250,10 @@ static void updatePIDs(UAVObjEvent* ev) needsUpdate |= update(&stab.RollPI[STABILIZATIONSETTINGS_ROLLPI_KI], value); needsUpdate |= update(&stab.PitchPI[STABILIZATIONSETTINGS_PITCHPI_KI], value); break; + case TXPIDSETTINGS_PIDS_ROLLPITCHATTITUDEILIMIT: + needsUpdate |= update(&stab.RollPI[STABILIZATIONSETTINGS_ROLLPI_ILIMIT], value); + needsUpdate |= update(&stab.PitchPI[STABILIZATIONSETTINGS_PITCHPI_ILIMIT], value); + break; case TXPIDSETTINGS_PIDS_YAWRATEKP: needsUpdate |= update(&stab.YawRatePID[STABILIZATIONSETTINGS_YAWRATEPID_KP], value); break; @@ -243,12 +263,18 @@ static void updatePIDs(UAVObjEvent* ev) case TXPIDSETTINGS_PIDS_YAWRATEKD: needsUpdate |= update(&stab.YawRatePID[STABILIZATIONSETTINGS_YAWRATEPID_KD], value); break; + case TXPIDSETTINGS_PIDS_YAWRATEILIMIT: + needsUpdate |= update(&stab.YawRatePID[STABILIZATIONSETTINGS_YAWRATEPID_ILIMIT], value); + break; case TXPIDSETTINGS_PIDS_YAWATTITUDEKP: needsUpdate |= update(&stab.YawPI[STABILIZATIONSETTINGS_YAWPI_KP], value); break; case TXPIDSETTINGS_PIDS_YAWATTITUDEKI: needsUpdate |= update(&stab.YawPI[STABILIZATIONSETTINGS_YAWPI_KI], value); break; + case TXPIDSETTINGS_PIDS_YAWATTITUDEILIMIT: + needsUpdate |= update(&stab.YawPI[STABILIZATIONSETTINGS_YAWPI_ILIMIT], value); + break; default: PIOS_Assert(0); } diff --git a/shared/uavobjectdefinition/txpidsettings.xml b/shared/uavobjectdefinition/txpidsettings.xml index 700db43fa..876ffccc7 100644 --- a/shared/uavobjectdefinition/txpidsettings.xml +++ b/shared/uavobjectdefinition/txpidsettings.xml @@ -13,8 +13,10 @@ Roll Rate.Kp, Pitch Rate.Kp, Roll+Pitch Rate.Kp, Yaw Rate.Kp, Roll Rate.Ki, Pitch Rate.Ki, Roll+Pitch Rate.Ki, Yaw Rate.Ki, Roll Rate.Kd, Pitch Rate.Kd, Roll+Pitch Rate.Kd, Yaw Rate.Kd, + Roll Rate.ILimit, Pitch Rate.ILimit, Roll+Pitch Rate.ILimit, Yaw Rate.ILimit, Roll Attitude.Kp, Pitch Attitude.Kp, Roll+Pitch Attitude.Kp, Yaw Attitude.Kp, - Roll Attitude.Ki, Pitch Attitude.Ki, Roll+Pitch Attitude.Ki, Yaw Attitude.Ki" + Roll Attitude.Ki, Pitch Attitude.Ki, Roll+Pitch Attitude.Ki, Yaw Attitude.Ki, + Roll Attitude.ILimit, Pitch Attitude.ILimit, Roll+Pitch Attitude.ILimit, Yaw Attitude.ILimit" defaultvalue="Disabled"/> From 3782cdc58ed62035c51e9f56d53b5cd967a1375e Mon Sep 17 00:00:00 2001 From: Stacey Sheldon Date: Sat, 10 Mar 2012 20:41:15 -0500 Subject: [PATCH 12/19] bu: drop CC and PipX from BU targets since they don't build The CC and PipX bootloader updater (BU) builds don't currently work due to some recent changes in how LEDs are handled. Remove them from the default BU targets so that the all_flight target can build clean again. Also fix a linker warning in OP build. --- Makefile | 12 ++++++++++-- flight/PiOS/STM32F10x/link_STM3210E_OP_sections.ld | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 72adb05be..14e1b641b 100644 --- a/Makefile +++ b/Makefile @@ -448,8 +448,16 @@ BU_BOARDS := $(ALL_BOARDS) # FIXME: The INS build doesn't have a bootloader or bootloader # updater yet so we need to filter them out to prevent errors. -BL_BOARDS := $(filter-out ins, $(ALL_BOARDS)) -BU_BOARDS := $(filter-out ins, $(ALL_BOARDS)) +BL_BOARDS := $(filter-out ins, $(BL_BOARDS)) +BU_BOARDS := $(filter-out ins, $(BU_BOARDS)) + +# FIXME: The CC bootloader updaters don't work anymore due to +# differences between CC and CC3D +BU_BOARDS := $(filter-out coptercontrol, $(BU_BOARDS)) + +# FIXME: PipXtreme bootloader updater doesn't work due to missing +# definitions for LEDs +BU_BOARDS := $(filter-out pipxtreme, $(BU_BOARDS)) # Generate the targets for whatever boards are left in each list FW_TARGETS := $(addprefix fw_, $(FW_BOARDS)) diff --git a/flight/PiOS/STM32F10x/link_STM3210E_OP_sections.ld b/flight/PiOS/STM32F10x/link_STM3210E_OP_sections.ld index f23ed327f..d3b33f955 100644 --- a/flight/PiOS/STM32F10x/link_STM3210E_OP_sections.ld +++ b/flight/PiOS/STM32F10x/link_STM3210E_OP_sections.ld @@ -275,7 +275,7 @@ SECTIONS _init_stack_top = . - 4 ; } > RAM - _eram = ORIGIN(SRAM) + LENGTH(SRAM) ; + _eram = ORIGIN(RAM) + LENGTH(RAM) ; _ebss = _eram ; /* keep the heap section at the end of the SRAM From 5eeafa227c46637e347498e9774f04326b1bb1f9 Mon Sep 17 00:00:00 2001 From: Laurent Ribon Date: Wed, 14 Mar 2012 19:57:11 +0100 Subject: [PATCH 13/19] Replace GLC_lib OpenPilot version to the latest GLC_lib version. --- .../openpilotgcs/src/libs/glc_lib/.gitignore | 1 + .../src/libs/glc_lib/3rdparty/lib3ds/light.c | 2 + .../src/libs/glc_lib/geometry/glc_3drep.cpp | 49 +- .../src/libs/glc_lib/geometry/glc_3drep.h | 5 + .../src/libs/glc_lib/geometry/glc_box.cpp | 4 +- .../src/libs/glc_lib/geometry/glc_bsrep.cpp | 9 +- .../src/libs/glc_lib/geometry/glc_bsrep.h | 3 + .../src/libs/glc_lib/geometry/glc_cone.cpp | 3 +- .../libs/glc_lib/geometry/glc_cylinder.cpp | 2 + .../src/libs/glc_lib/geometry/glc_disc.cpp | 4 +- .../libs/glc_lib/geometry/glc_geometry.cpp | 29 +- .../src/libs/glc_lib/geometry/glc_geometry.h | 29 +- .../src/libs/glc_lib/geometry/glc_line.cpp | 70 +- .../src/libs/glc_lib/geometry/glc_line.h | 25 +- .../src/libs/glc_lib/geometry/glc_lod.cpp | 71 +- .../src/libs/glc_lib/geometry/glc_lod.h | 28 +- .../src/libs/glc_lib/geometry/glc_mesh.cpp | 274 ++++-- .../src/libs/glc_lib/geometry/glc_mesh.h | 21 +- .../libs/glc_lib/geometry/glc_meshdata.cpp | 257 ++++-- .../src/libs/glc_lib/geometry/glc_meshdata.h | 43 +- .../src/libs/glc_lib/geometry/glc_point.cpp | 59 +- .../src/libs/glc_lib/geometry/glc_point.h | 10 +- .../libs/glc_lib/geometry/glc_pointcloud.cpp | 17 + .../libs/glc_lib/geometry/glc_pointcloud.h | 9 +- .../libs/glc_lib/geometry/glc_pointsprite.cpp | 53 +- .../libs/glc_lib/geometry/glc_pointsprite.h | 10 +- .../libs/glc_lib/geometry/glc_polylines.cpp | 32 + .../src/libs/glc_lib/geometry/glc_polylines.h | 10 +- .../glc_lib/geometry/glc_primitivegroup.cpp | 46 +- .../glc_lib/geometry/glc_primitivegroup.h | 6 +- .../src/libs/glc_lib/geometry/glc_sphere.cpp | 19 +- .../src/libs/glc_lib/geometry/glc_sphere.h | 11 + .../libs/glc_lib/geometry/glc_wiredata.cpp | 360 ++++++-- .../src/libs/glc_lib/geometry/glc_wiredata.h | 66 +- .../src/libs/glc_lib/glc_cachemanager.cpp | 1 + .../src/libs/glc_lib/glc_context.cpp | 289 +++++++ .../src/libs/glc_lib/glc_context.h | 196 +++++ .../src/libs/glc_lib/glc_contextmanager.cpp | 88 ++ .../src/libs/glc_lib/glc_contextmanager.h | 104 +++ ...nglstate.cpp => glc_contextshareddata.cpp} | 13 +- ...statemanager.h => glc_contextshareddata.h} | 16 +- .../openpilotgcs/src/libs/glc_lib/glc_ext.cpp | 43 +- .../openpilotgcs/src/libs/glc_lib/glc_ext.h | 30 - .../src/libs/glc_lib/glc_factory.cpp | 34 +- .../src/libs/glc_lib/glc_factory.h | 10 +- .../src/libs/glc_lib/glc_global.h | 2 +- .../openpilotgcs/src/libs/glc_lib/glc_lib.pro | 55 +- .../openpilotgcs/src/libs/glc_lib/glc_lib.qrc | 6 + .../src/libs/glc_lib/glc_state.cpp | 26 +- .../openpilotgcs/src/libs/glc_lib/glc_state.h | 6 + .../libs/glc_lib/glc_uniformshaderdata.cpp | 101 +++ .../src/libs/glc_lib/glc_uniformshaderdata.h | 68 ++ .../src/libs/glc_lib/glu/glc_glu.h | 77 ++ .../src/libs/glc_lib/glu/glc_project.cpp | 379 +++++++++ .../src/libs/glc_lib/include/GLC_Context | 1 + .../libs/glc_lib/include/GLC_ContextManager | 1 + .../src/libs/glc_lib/include/GLC_Glu | 1 + .../src/libs/glc_lib/include/GLC_Octree | 2 +- .../src/libs/glc_lib/include/GLC_OctreeNode | 2 +- .../src/libs/glc_lib/include/GLC_OpenGLState | 1 - .../src/libs/glc_lib/include/GLC_RenderState | 1 + .../glc_lib/include/GLC_SpacePartitioning | 2 +- .../libs/glc_lib/include/GLC_StructInstance | 2 +- .../libs/glc_lib/include/GLC_StructOccurence | 2 +- .../libs/glc_lib/include/GLC_StructReference | 2 +- .../src/libs/glc_lib/include/GLC_TsrMover | 1 + .../src/libs/glc_lib/include/GLC_UserInput | 1 + .../src/libs/glc_lib/io/glc_3dstoworld.cpp | 5 +- .../src/libs/glc_lib/io/glc_3dstoworld.h | 5 +- .../src/libs/glc_lib/io/glc_3dxmltoworld.cpp | 792 +++++++++++------- .../src/libs/glc_lib/io/glc_3dxmltoworld.h | 89 +- .../libs/glc_lib/io/glc_colladatoworld.cpp | 18 +- .../src/libs/glc_lib/io/glc_colladatoworld.h | 5 +- .../src/libs/glc_lib/io/glc_fileloader.cpp | 11 +- .../src/libs/glc_lib/io/glc_fileloader.h | 4 +- .../src/libs/glc_lib/io/glc_objmtlloader.cpp | 5 +- .../src/libs/glc_lib/io/glc_objmtlloader.h | 5 +- .../src/libs/glc_lib/io/glc_objtoworld.cpp | 5 +- .../src/libs/glc_lib/io/glc_objtoworld.h | 5 +- .../src/libs/glc_lib/io/glc_worldto3dxml.cpp | 175 +++- .../src/libs/glc_lib/io/glc_worldto3dxml.h | 8 +- .../src/libs/glc_lib/io/glc_xmlutil.h | 0 .../src/libs/glc_lib/maths/glc_geomtools.cpp | 115 ++- .../src/libs/glc_lib/maths/glc_geomtools.h | 34 +- .../libs/glc_lib/maths/glc_interpolator.cpp | 114 +-- .../src/libs/glc_lib/maths/glc_interpolator.h | 53 +- .../src/libs/glc_lib/maths/glc_line3d.h | 2 +- .../src/libs/glc_lib/maths/glc_matrix4x4.h | 11 - .../src/libs/glc_lib/maths/glc_vector2d.h | 92 +- .../src/libs/glc_lib/maths/glc_vector3d.h | 69 +- .../src/libs/glc_lib/maths/glc_vector4d.h | 8 +- .../sceneGraph/glc_3dviewcollection.cpp | 138 +-- .../glc_lib/sceneGraph/glc_3dviewcollection.h | 64 +- .../glc_lib/sceneGraph/glc_3dviewinstance.cpp | 20 +- .../glc_lib/sceneGraph/glc_3dviewinstance.h | 13 +- .../glc_lib/sceneGraph/glc_structinstance.cpp | 9 + .../glc_lib/sceneGraph/glc_structinstance.h | 5 +- .../sceneGraph/glc_structoccurence.cpp | 132 ++- .../glc_lib/sceneGraph/glc_structoccurence.h | 14 +- .../src/libs/glc_lib/sceneGraph/glc_world.cpp | 5 +- .../glc_lib/sceneGraph/glc_worldhandle.cpp | 8 +- .../libs/glc_lib/sceneGraph/glc_worldhandle.h | 2 +- .../src/libs/glc_lib/shading/glc_light.cpp | 298 ++++--- .../src/libs/glc_lib/shading/glc_light.h | 56 +- .../src/libs/glc_lib/shading/glc_material.cpp | 48 +- .../src/libs/glc_lib/shading/glc_material.h | 4 +- .../glc_lib/shading/glc_renderproperties.cpp | 2 +- .../glc_lib/shading/glc_renderproperties.h | 1 + .../glc_lib/shading/glc_selectionmaterial.cpp | 89 +- .../glc_lib/shading/glc_selectionmaterial.h | 22 +- .../src/libs/glc_lib/shading/glc_shader.cpp | 137 ++- .../src/libs/glc_lib/shading/glc_shader.h | 135 ++- .../src/libs/glc_lib/shading/glc_texture.cpp | 27 +- .../src/libs/glc_lib/shading/glc_texture.h | 10 +- .../libs/glc_lib/shading/shaders/default.frag | 0 .../libs/glc_lib/shading/shaders/default.vert | 12 + .../src/libs/glc_lib/viewport/glc_camera.cpp | 21 +- .../libs/glc_lib/viewport/glc_flymover.cpp | 12 +- .../src/libs/glc_lib/viewport/glc_flymover.h | 4 +- .../libs/glc_lib/viewport/glc_imageplane.cpp | 47 +- .../libs/glc_lib/viewport/glc_imageplane.h | 9 +- .../src/libs/glc_lib/viewport/glc_mover.h | 15 +- .../glc_lib/viewport/glc_movercontroller.cpp | 4 +- .../glc_lib/viewport/glc_movercontroller.h | 15 +- .../libs/glc_lib/viewport/glc_panmover.cpp | 8 +- .../src/libs/glc_lib/viewport/glc_panmover.h | 4 +- .../glc_lib/viewport/glc_repcrossmover.cpp | 86 +- .../libs/glc_lib/viewport/glc_repcrossmover.h | 9 + .../libs/glc_lib/viewport/glc_repflymover.cpp | 32 +- .../viewport/glc_reptrackballmover.cpp | 35 +- .../glc_lib/viewport/glc_reptrackballmover.h | 4 + .../glc_lib/viewport/glc_settargetmover.cpp | 10 +- .../glc_lib/viewport/glc_settargetmover.h | 4 +- .../glc_lib/viewport/glc_trackballmover.cpp | 22 +- .../glc_lib/viewport/glc_trackballmover.h | 7 +- .../libs/glc_lib/viewport/glc_tsrmover.cpp | 144 ++++ .../glc_tsrmover.h} | 47 +- .../glc_lib/viewport/glc_turntablemover.cpp | 12 +- .../glc_lib/viewport/glc_turntablemover.h | 4 +- .../glc_userinput.cpp} | 41 +- .../src/libs/glc_lib/viewport/glc_userinput.h | 133 +++ .../libs/glc_lib/viewport/glc_viewport.cpp | 85 +- .../src/libs/glc_lib/viewport/glc_viewport.h | 12 + .../libs/glc_lib/viewport/glc_zoommover.cpp | 8 +- .../src/libs/glc_lib/viewport/glc_zoommover.h | 4 +- 145 files changed, 5034 insertions(+), 1885 deletions(-) create mode 100644 ground/openpilotgcs/src/libs/glc_lib/.gitignore create mode 100644 ground/openpilotgcs/src/libs/glc_lib/glc_context.cpp create mode 100644 ground/openpilotgcs/src/libs/glc_lib/glc_context.h create mode 100644 ground/openpilotgcs/src/libs/glc_lib/glc_contextmanager.cpp create mode 100644 ground/openpilotgcs/src/libs/glc_lib/glc_contextmanager.h rename ground/openpilotgcs/src/libs/glc_lib/{glc_openglstate.cpp => glc_contextshareddata.cpp} (73%) rename ground/openpilotgcs/src/libs/glc_lib/{glc_openglstatemanager.h => glc_contextshareddata.h} (76%) create mode 100644 ground/openpilotgcs/src/libs/glc_lib/glc_lib.qrc create mode 100644 ground/openpilotgcs/src/libs/glc_lib/glc_uniformshaderdata.cpp create mode 100644 ground/openpilotgcs/src/libs/glc_lib/glc_uniformshaderdata.h create mode 100644 ground/openpilotgcs/src/libs/glc_lib/glu/glc_glu.h create mode 100644 ground/openpilotgcs/src/libs/glc_lib/glu/glc_project.cpp create mode 100644 ground/openpilotgcs/src/libs/glc_lib/include/GLC_Context create mode 100644 ground/openpilotgcs/src/libs/glc_lib/include/GLC_ContextManager create mode 100644 ground/openpilotgcs/src/libs/glc_lib/include/GLC_Glu delete mode 100644 ground/openpilotgcs/src/libs/glc_lib/include/GLC_OpenGLState create mode 100644 ground/openpilotgcs/src/libs/glc_lib/include/GLC_RenderState create mode 100644 ground/openpilotgcs/src/libs/glc_lib/include/GLC_TsrMover create mode 100644 ground/openpilotgcs/src/libs/glc_lib/include/GLC_UserInput mode change 100644 => 100755 ground/openpilotgcs/src/libs/glc_lib/io/glc_xmlutil.h mode change 100644 => 100755 ground/openpilotgcs/src/libs/glc_lib/maths/glc_vector2d.h mode change 100644 => 100755 ground/openpilotgcs/src/libs/glc_lib/maths/glc_vector3d.h create mode 100644 ground/openpilotgcs/src/libs/glc_lib/shading/shaders/default.frag create mode 100644 ground/openpilotgcs/src/libs/glc_lib/shading/shaders/default.vert create mode 100644 ground/openpilotgcs/src/libs/glc_lib/viewport/glc_tsrmover.cpp rename ground/openpilotgcs/src/libs/glc_lib/{glc_openglstate.h => viewport/glc_tsrmover.h} (66%) rename ground/openpilotgcs/src/libs/glc_lib/{glc_openglstatemanager.cpp => viewport/glc_userinput.cpp} (53%) create mode 100644 ground/openpilotgcs/src/libs/glc_lib/viewport/glc_userinput.h diff --git a/ground/openpilotgcs/src/libs/glc_lib/.gitignore b/ground/openpilotgcs/src/libs/glc_lib/.gitignore new file mode 100644 index 000000000..e2f6ef3be --- /dev/null +++ b/ground/openpilotgcs/src/libs/glc_lib/.gitignore @@ -0,0 +1 @@ +/qrc_glc_lib.cpp diff --git a/ground/openpilotgcs/src/libs/glc_lib/3rdparty/lib3ds/light.c b/ground/openpilotgcs/src/libs/glc_lib/3rdparty/lib3ds/light.c index 2eee44859..311f1d623 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/3rdparty/lib3ds/light.c +++ b/ground/openpilotgcs/src/libs/glc_lib/3rdparty/lib3ds/light.c @@ -30,6 +30,8 @@ /*! * \defgroup light Lights */ +/*! + /*! * \ingroup light diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_3drep.cpp b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_3drep.cpp index 98908afe4..387abe5bb 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_3drep.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_3drep.cpp @@ -28,7 +28,6 @@ // Class chunk id quint32 GLC_3DRep::m_ChunkId= 0xA702; -// Default constructor GLC_3DRep::GLC_3DRep() : GLC_Rep() , m_pGeomList(new QList) @@ -37,7 +36,6 @@ GLC_3DRep::GLC_3DRep() } -// Construct a 3DRep with a geometry GLC_3DRep::GLC_3DRep(GLC_Geometry* pGeom) : GLC_Rep() , m_pGeomList(new QList) @@ -48,7 +46,6 @@ GLC_3DRep::GLC_3DRep(GLC_Geometry* pGeom) setName(pGeom->name()); } -// Copy Constructor GLC_3DRep::GLC_3DRep(const GLC_3DRep& rep) : GLC_Rep(rep) , m_pGeomList(rep.m_pGeomList) @@ -57,7 +54,6 @@ GLC_3DRep::GLC_3DRep(const GLC_3DRep& rep) } -// Assignement operator GLC_3DRep& GLC_3DRep::operator=(const GLC_Rep& rep) { const GLC_3DRep* p3DRep= dynamic_cast(&rep); @@ -73,13 +69,11 @@ GLC_3DRep& GLC_3DRep::operator=(const GLC_Rep& rep) return *this; } -// Clone the representation GLC_Rep* GLC_3DRep::clone() const { return new GLC_3DRep(*this); } -// Make a deep copy of the 3DRep GLC_Rep* GLC_3DRep::deepCopy() const { GLC_3DRep* pCloneRep= new GLC_3DRep; @@ -95,7 +89,6 @@ GLC_Rep* GLC_3DRep::deepCopy() const return pCloneRep; } -// Destructor GLC_3DRep::~GLC_3DRep() { clear(); @@ -104,13 +97,11 @@ GLC_3DRep::~GLC_3DRep() ////////////////////////////////////////////////////////////////////// // Get functions ////////////////////////////////////////////////////////////////////// -// Return the class Chunk ID quint32 GLC_3DRep::chunckID() { return m_ChunkId; } -// Return the type of representation int GLC_3DRep::type() const { return (*m_pType); @@ -120,7 +111,6 @@ int GLC_3DRep::type() const // Get functions ////////////////////////////////////////////////////////////////////// -// Return true if the rep bounding box is valid bool GLC_3DRep::boundingBoxIsValid() const { bool result= !m_pGeomList->isEmpty(); @@ -134,7 +124,6 @@ bool GLC_3DRep::boundingBoxIsValid() const return result; } -// Return the 3DRep bounding Box GLC_BoundingBox GLC_3DRep::boundingBox() const { GLC_BoundingBox resultBox; @@ -146,7 +135,6 @@ GLC_BoundingBox GLC_3DRep::boundingBox() const return resultBox; } -// Get number of faces unsigned int GLC_3DRep::faceCount() const { unsigned int result= 0; @@ -162,7 +150,6 @@ unsigned int GLC_3DRep::faceCount() const return result; } -// Get number of vertex unsigned int GLC_3DRep::vertexCount() const { unsigned int result= 0; @@ -178,7 +165,6 @@ unsigned int GLC_3DRep::vertexCount() const return result; } -// Get number of materials unsigned int GLC_3DRep::materialCount() const { unsigned int result= 0; @@ -194,7 +180,6 @@ unsigned int GLC_3DRep::materialCount() const return result; } -// Get materials List QSet GLC_3DRep::materialSet() const { QSet result; @@ -210,7 +195,18 @@ QSet GLC_3DRep::materialSet() const return result; } -// Remove empty geometries +double GLC_3DRep::volume() const +{ + double resultVolume= 0.0; + const int geomCount= m_pGeomList->count(); + for (int i= 0; i < geomCount; ++i) + { + resultVolume+= m_pGeomList->at(i)->volume(); + } + + return resultVolume; +} + void GLC_3DRep::clean() { QList::iterator iGeomList= m_pGeomList->begin(); @@ -229,7 +225,6 @@ void GLC_3DRep::clean() } } -// Reverse geometries normals void GLC_3DRep::reverseNormals() { const int size= m_pGeomList->size(); @@ -239,7 +234,6 @@ void GLC_3DRep::reverseNormals() } } -// Load the representation bool GLC_3DRep::load() { bool loadSucces= false; @@ -274,7 +268,7 @@ bool GLC_3DRep::load() return loadSucces; } -// Replace the representation + void GLC_3DRep::replace(GLC_Rep* pRep) { GLC_3DRep* p3DRep= dynamic_cast(pRep); @@ -295,7 +289,6 @@ void GLC_3DRep::replace(GLC_Rep* pRep) } } -// Replace the specified material by a new one void GLC_3DRep::replaceMaterial(GLC_uint oldId, GLC_Material* pNewMaterial) { //qDebug() << "GLC_3DRep::replaceMaterial"; @@ -315,7 +308,6 @@ void GLC_3DRep::replaceMaterial(GLC_uint oldId, GLC_Material* pNewMaterial) } } -// Merge this 3Drep with another 3DRep void GLC_3DRep::merge(const GLC_3DRep* pRep) { // Get the number of geometry of pRep @@ -361,7 +353,6 @@ void GLC_3DRep::transformSubGeometries(const GLC_Matrix4x4& matrix) { // Get the number of geometry of pRep const int repCount= m_pGeomList->size(); - qDebug() << "repCount " << repCount; for (int i= 0; i < repCount; ++i) { GLC_Mesh* pCurrentMesh= dynamic_cast(geomAt(i)); @@ -372,7 +363,16 @@ void GLC_3DRep::transformSubGeometries(const GLC_Matrix4x4& matrix) } } -// UnLoad the representation +void GLC_3DRep::setVboUsage(bool usage) +{ + // Get the number of geometry of pRep + const int repCount= m_pGeomList->size(); + for (int i= 0; i < repCount; ++i) + { + m_pGeomList->at(i)->setVboUsage(usage); + } +} + bool GLC_3DRep::unload() { bool unloadSucess= false; @@ -404,7 +404,6 @@ bool GLC_3DRep::unload() // private services functions ////////////////////////////////////////////////////////////////////// -// Clear the 3D representation void GLC_3DRep::clear() { if (isTheLast()) @@ -422,7 +421,6 @@ void GLC_3DRep::clear() } } // Non Member methods -// Non-member stream operator QDataStream &operator<<(QDataStream & stream, const GLC_3DRep & rep) { quint32 chunckId= GLC_3DRep::m_ChunkId; @@ -457,6 +455,7 @@ QDataStream &operator<<(QDataStream & stream, const GLC_3DRep & rep) return stream; } + QDataStream &operator>>(QDataStream & stream, GLC_3DRep & rep) { Q_ASSERT(rep.isEmpty()); diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_3drep.h b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_3drep.h index 5da9a1ccc..27c6f4038 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_3drep.h +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_3drep.h @@ -121,6 +121,9 @@ public: //! Return materials Set of this 3DRep QSet materialSet() const; + //! Return the volume of this 3DRep + double volume() const; + //@} ////////////////////////////////////////////////////////////////////// @@ -168,6 +171,8 @@ public: //! Transform 3DRep sub mesh vertice with the given matrix void transformSubGeometries(const GLC_Matrix4x4& matrix); + //! Set VBO usage + void setVboUsage(bool usage); //@} diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_box.cpp b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_box.cpp index 540509f0a..2b6e510a2 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_box.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_box.cpp @@ -36,7 +36,7 @@ GLC_Box::GLC_Box(double dLx, double dLy, double dlz) , m_dLgY(dLy) , m_dLgZ(dlz) { - + createMeshAndWire(); } // Copy constructor GLC_Box::GLC_Box(const GLC_Box& box) @@ -45,7 +45,7 @@ GLC_Box::GLC_Box(const GLC_Box& box) , m_dLgY(box.m_dLgY) , m_dLgZ(box.m_dLgZ) { - + createMeshAndWire(); } GLC_Box::~GLC_Box() { diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_bsrep.cpp b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_bsrep.cpp index 82c208774..edfc9ffb9 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_bsrep.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_bsrep.cpp @@ -32,7 +32,7 @@ const QString GLC_BSRep::m_Suffix("BSRep"); const QUuid GLC_BSRep::m_Uuid("{d6f97789-36a9-4c2e-b667-0e66c27f839f}"); // The binary rep version -const quint32 GLC_BSRep::m_Version= 102; +const quint32 GLC_BSRep::m_Version= 103; // Mutex used by compression QMutex GLC_BSRep::m_CompressionMutex; @@ -181,6 +181,11 @@ QString GLC_BSRep::suffix() return m_Suffix; } +quint32 GLC_BSRep::version() +{ + return m_Version; +} + ////////////////////////////////////////////////////////////////////// //name Set Functions ////////////////////////////////////////////////////////////////////// @@ -322,7 +327,7 @@ bool GLC_BSRep::headerIsOk() // Set the version of the data stream m_DataStream.setVersion(QDataStream::Qt_4_6); - bool headerOk= (uuid == m_Uuid) && (version == m_Version) && writeFinished; + bool headerOk= (uuid == m_Uuid) && (version <= m_Version) && (version > 101) && writeFinished; return headerOk; } diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_bsrep.h b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_bsrep.h index 3c6923588..f1dbe4955 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_bsrep.h +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_bsrep.h @@ -75,6 +75,9 @@ public: //! Return bsrep suffix static QString suffix(); + + //! Return bsrep version + static quint32 version(); //@} ////////////////////////////////////////////////////////////////////// diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_cone.cpp b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_cone.cpp index a0cb882fa..78b540341 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_cone.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_cone.cpp @@ -33,6 +33,7 @@ GLC_Cone::GLC_Cone(double dRadius, double dLength) , m_Discret(glc::GLC_POLYDISCRET) // Default discretion { Q_ASSERT((m_Radius > 0.0) && (m_Length > 0.0)); + createMeshAndWire(); } GLC_Cone::GLC_Cone(const GLC_Cone& sourceCone) @@ -41,7 +42,7 @@ GLC_Cone::GLC_Cone(const GLC_Cone& sourceCone) , m_Length(sourceCone.m_Length) , m_Discret(sourceCone.m_Discret) { - + createMeshAndWire(); } GLC_Cone::~GLC_Cone() diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_cylinder.cpp b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_cylinder.cpp index 16c90cd88..be33e93fb 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_cylinder.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_cylinder.cpp @@ -44,6 +44,7 @@ GLC_Cylinder::GLC_Cylinder(double dRadius, double dLength) , m_EndedIsCaped(true) // Cylinder ended are closed { Q_ASSERT((m_Radius > 0.0) && (m_Length > 0.0)); + createMeshAndWire(); } GLC_Cylinder::GLC_Cylinder(const GLC_Cylinder& sourceCylinder) @@ -54,6 +55,7 @@ GLC_Cylinder::GLC_Cylinder(const GLC_Cylinder& sourceCylinder) , m_EndedIsCaped(sourceCylinder.m_EndedIsCaped) { Q_ASSERT((m_Radius > 0.0) && (m_Length > 0.0) && (m_Discret > 0)); + createMeshAndWire(); } GLC_Cylinder::~GLC_Cylinder() diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_disc.cpp b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_disc.cpp index 69420d439..66983437b 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_disc.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_disc.cpp @@ -30,7 +30,7 @@ GLC_Disc::GLC_Disc(double radius, double angle) , m_Angle(angle) , m_Step(0) { - + createMeshAndWire(); } GLC_Disc::GLC_Disc(const GLC_Disc& disc) @@ -40,7 +40,7 @@ GLC_Disc::GLC_Disc(const GLC_Disc& disc) , m_Angle(disc.m_Angle) , m_Step(disc.m_Step) { - + createMeshAndWire(); } GLC_Disc::~GLC_Disc() diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_geometry.cpp b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_geometry.cpp index 24ab7030f..77880818c 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_geometry.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_geometry.cpp @@ -25,6 +25,7 @@ #include "../shading/glc_selectionmaterial.h" #include "../glc_openglexception.h" #include "../glc_state.h" +#include "../glc_context.h" #include "glc_geometry.h" ////////////////////////////////////////////////////////////////////// @@ -44,6 +45,7 @@ GLC_Geometry::GLC_Geometry(const QString& name, const bool typeIsWire) , m_TransparentMaterialNumber(0) , m_Id(glc::GLC_GenGeomID()) , m_Name(name) +, m_UseVbo(false) { } @@ -61,6 +63,7 @@ GLC_Geometry::GLC_Geometry(const GLC_Geometry& sourceGeom) , m_TransparentMaterialNumber(sourceGeom.m_TransparentMaterialNumber) , m_Id(glc::GLC_GenGeomID()) , m_Name(sourceGeom.m_Name) +, m_UseVbo(sourceGeom.m_UseVbo) { // Add this mesh to inner material MaterialHash::const_iterator i= sourceGeom.m_MaterialHash.constBegin(); @@ -95,6 +98,7 @@ GLC_Geometry& GLC_Geometry::operator=(const GLC_Geometry& sourceGeom) m_TransparentMaterialNumber= sourceGeom.m_TransparentMaterialNumber; m_Id= glc::GLC_GenGeomID(); m_Name= sourceGeom.m_Name; + m_UseVbo= sourceGeom.m_UseVbo; } return *this; } @@ -134,6 +138,11 @@ unsigned int GLC_Geometry::VertexCount() const return 0; } +double GLC_Geometry::volume() +{ + return 0.0; +} + ///////////////////////////////////////////////////////////////////// // Set Functions ////////////////////////////////////////////////////////////////////// @@ -179,7 +188,6 @@ void GLC_Geometry::updateTransparentMaterialNumber() } if (m_WireColor.alpha() != 255) { - qDebug() << "GLC_Geometry::updateTransparentMaterialNumber()"; ++m_TransparentMaterialNumber; } } @@ -187,8 +195,6 @@ void GLC_Geometry::updateTransparentMaterialNumber() // Add material to mesh void GLC_Geometry::addMaterial(GLC_Material* pMaterial) { - Q_ASSERT(!m_IsWire); - if (pMaterial != NULL) { const GLC_uint materialID= pMaterial->id(); @@ -234,6 +240,15 @@ void GLC_Geometry::releaseVboClientSide(bool update) m_WireData.releaseVboClientSide(update); } +void GLC_Geometry::setVboUsage(bool usage) +{ + m_UseVbo= usage; + if (!usage || (usage && GLC_State::vboSupported())) + { + m_WireData.setVboUsage(m_UseVbo); + } +} + ////////////////////////////////////////////////////////////////////// // OpenGL Functions ////////////////////////////////////////////////////////////////////// @@ -283,7 +298,7 @@ void GLC_Geometry::render(const GLC_RenderProperties& renderProperties) GLenum error= glGetError(); if (error != GL_NO_ERROR) { - GLC_OpenGlException OpenGlException("GLC_Geometry::glExecute " + name(), error); + GLC_OpenGlException OpenGlException("GLC_Geometry::render " + name(), error); throw(OpenGlException); } } @@ -297,7 +312,7 @@ void GLC_Geometry::glPropGeom(const GLC_RenderProperties& renderProperties) if(m_IsWire) { glLineWidth(m_LineWidth); - glDisable(GL_LIGHTING); + GLC_Context::current()->glcEnableLighting(false);; if (!renderProperties.isSelected()) { // Set polyline colors @@ -318,13 +333,13 @@ void GLC_Geometry::glPropGeom(const GLC_RenderProperties& renderProperties) GLC_Material* pCurrentMaterial= m_MaterialHash.begin().value(); if (pCurrentMaterial->hasTexture()) { - glEnable(GL_LIGHTING); + GLC_Context::current()->glcEnableLighting(true); pCurrentMaterial->glExecute(); if (renderProperties.isSelected()) GLC_SelectionMaterial::glExecute(); } else { - glEnable(GL_LIGHTING); + GLC_Context::current()->glcEnableLighting(true); if (renderProperties.isSelected()) GLC_SelectionMaterial::glExecute(); else pCurrentMaterial->glExecute(); } diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_geometry.h b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_geometry.h index 2d40a5b3f..ee35cba5b 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_geometry.h +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_geometry.h @@ -29,7 +29,7 @@ #include "glc_wiredata.h" #include "../glc_boundingbox.h" -#include "glc_config.h" +#include "../glc_config.h" typedef QHash MaterialHash; typedef QHash MaterialHashMap; @@ -68,19 +68,13 @@ class GLC_LIB_EXPORT GLC_Geometry ////////////////////////////////////////////////////////////////////// public: //! Default constructor - /*! - * QString Name - * const bool typeIsWire - */ - GLC_Geometry(const QString &, const bool); + GLC_Geometry(const QString &name, const bool type); + //! Copy constructor - /*! - * const GLC_VboGeom geometry to copy - */ - GLC_Geometry(const GLC_Geometry&); + GLC_Geometry(const GLC_Geometry& sourceGeom); //! Overload "=" operator - GLC_Geometry& operator=(const GLC_Geometry&); + GLC_Geometry& operator=(const GLC_Geometry& sourceGeom); //! Destructor virtual ~GLC_Geometry(); @@ -197,6 +191,13 @@ public: inline GLsizei wirePolylineSize(int index) const {return m_WireData.verticeGroupSize(index);} + //! Return the volume of this geometry + virtual double volume(); + + //! Return true if this geometry will try to use VBO + inline bool vboIsUsed() const + {return m_UseVbo;} + //@} ////////////////////////////////////////////////////////////////////// @@ -259,6 +260,9 @@ public: //! Release client VBO virtual void releaseVboClientSide(bool update= false); + //! Set VBO usage + virtual void setVboUsage(bool usage); + //@} ////////////////////////////////////////////////////////////////////// /*! \name OpenGL Functions*/ @@ -357,6 +361,9 @@ private: //! Name of geometry QString m_Name; + + //! VBO usage flag + bool m_UseVbo; }; #endif /*GLC_GEOMETRY_H_*/ diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_line.cpp b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_line.cpp index 1fc0e234d..78c0d32b8 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_line.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_line.cpp @@ -28,19 +28,19 @@ ////////////////////////////////////////////////////////////////////// GLC_Line::GLC_Line(const GLC_Point3d & point1, const GLC_Point3d & point2) -: GLC_Geometry("Line", true) +: GLC_Polylines() , m_Point1(point1) , m_Point2(point2) { - + createWire(); } GLC_Line::GLC_Line(const GLC_Line& line) -: GLC_Geometry(line) +: GLC_Polylines(line) , m_Point1(line.m_Point1) , m_Point2(line.m_Point2) { - + createWire(); } GLC_Line::~GLC_Line() @@ -54,15 +54,7 @@ GLC_Line::~GLC_Line() const GLC_BoundingBox& GLC_Line::boundingBox(void) { - - if (NULL == m_pBoundingBox) - { - m_pBoundingBox= new GLC_BoundingBox(); - - m_pBoundingBox->combine(m_Point1); - m_pBoundingBox->combine(m_Point2); - } - return *m_pBoundingBox; + return GLC_Polylines::boundingBox(); } GLC_Geometry* GLC_Line::clone() const @@ -74,32 +66,44 @@ GLC_Geometry* GLC_Line::clone() const ////////////////////////////////////////////////////////////////////// // Set Functions ////////////////////////////////////////////////////////////////////// -void GLC_Line::setColor(const QColor& color) +void GLC_Line::setCoordinate(const GLC_Point3d &point1, const GLC_Point3d &point2) { - m_WireColor= color; - if (GLC_Geometry::hasMaterial()) - { - GLC_Geometry::firstMaterial()->setDiffuseColor(color); - } + m_Point1= point1; + m_Point2= point2; + clear(); + createWire(); } + +GLC_Line& GLC_Line::operator=(const GLC_Line& line) +{ + if (this != &line) + { + m_Point1= line.m_Point1; + m_Point2= line.m_Point2; + GLC_Polylines::operator=(line); + } + return *this; +} + ////////////////////////////////////////////////////////////////////// // OpenGL Functions ////////////////////////////////////////////////////////////////////// -void GLC_Line::glDraw(const GLC_RenderProperties&) +void GLC_Line::glDraw(const GLC_RenderProperties& renderProperties) { - // Point Display - glBegin(GL_LINES); - glVertex3dv(m_Point1.data()); - glVertex3dv(m_Point2.data()); - glEnd(); - - // OpenGL error handler - GLenum error= glGetError(); - if (error != GL_NO_ERROR) - { - GLC_OpenGlException OpenGlException("GLC_Line::GlDraw ", error); - throw(OpenGlException); - } + GLC_Polylines::glDraw(renderProperties); } + +////////////////////////////////////////////////////////////////////// +// Private services Functions +////////////////////////////////////////////////////////////////////// +void GLC_Line::createWire() +{ + QList points; + points.append(m_Point1); + points.append(m_Point2); + GLC_Polylines::addPolyline(points); +} + + diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_line.h b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_line.h index b2d527903..7091d6e0a 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_line.h +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_line.h @@ -23,7 +23,7 @@ #ifndef GLC_LINE_H_ #define GLC_LINE_H_ -#include "glc_geometry.h" +#include "glc_polylines.h" #include "../glc_config.h" @@ -34,7 +34,7 @@ /*! An GLC_Line is just a simple renderable 3D Line*/ ////////////////////////////////////////////////////////////////////// -class GLC_LIB_EXPORT GLC_Line : public GLC_Geometry +class GLC_LIB_EXPORT GLC_Line : public GLC_Polylines { ////////////////////////////////////////////////////////////////////// /*! @name Constructor / Destructor */ @@ -79,12 +79,15 @@ public: //@{ ////////////////////////////////////////////////////////////////////// public: - //! Set Line coordinate by 4D point - void setCoordinate(const GLC_Point3d &, const GLC_Point3d &); + //! Set Line coordinate by 3D point + void setCoordinate(const GLC_Point3d &point1, const GLC_Point3d &point2); - //! Set this line color - inline void setColor(const QColor& color); + //! Clear the content of this line Data and makes it empty + inline void clear() + {GLC_Polylines::clear();} + //! Set this line from the given line and return a reference of this line + GLC_Line& operator=(const GLC_Line& line); //@} @@ -100,6 +103,16 @@ private: //@} +////////////////////////////////////////////////////////////////////// +/*! \name Private services Functions*/ +//@{ +////////////////////////////////////////////////////////////////////// +private: + //! Create the wire + void createWire(); + +//@} + ////////////////////////////////////////////////////////////////////// // Private Member ////////////////////////////////////////////////////////////////////// diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_lod.cpp b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_lod.cpp index ec6f4deeb..050ff1a09 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_lod.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_lod.cpp @@ -22,7 +22,7 @@ //! \file glc_lod.cpp implementation of the GLC_Lod class. - +#include "../glc_exception.h" #include "glc_lod.h" // Class chunk id @@ -31,7 +31,7 @@ quint32 GLC_Lod::m_ChunkId= 0xA708; GLC_Lod::GLC_Lod() : m_Accuracy(0.0) -, m_IboId(0) +, m_IndexBuffer(QGLBuffer::IndexBuffer) , m_IndexVector() , m_IndexSize(0) , m_TrianglesCount(0) @@ -42,7 +42,7 @@ GLC_Lod::GLC_Lod() GLC_Lod::GLC_Lod(double accuracy) : m_Accuracy(accuracy) -, m_IboId(0) +, m_IndexBuffer(QGLBuffer::IndexBuffer) , m_IndexVector() , m_IndexSize(0) , m_TrianglesCount(0) @@ -53,7 +53,7 @@ GLC_Lod::GLC_Lod(double accuracy) GLC_Lod::GLC_Lod(const GLC_Lod& lod) : m_Accuracy(lod.m_Accuracy) -, m_IboId(0) +, m_IndexBuffer(QGLBuffer::IndexBuffer) , m_IndexVector(lod.indexVector()) , m_IndexSize(lod.m_IndexSize) , m_TrianglesCount(lod.m_TrianglesCount) @@ -68,7 +68,7 @@ GLC_Lod& GLC_Lod::operator=(const GLC_Lod& lod) if (this != &lod) { m_Accuracy= lod.m_Accuracy; - m_IboId= 0; + m_IndexBuffer.destroy(); m_IndexVector= lod.indexVector(); m_IndexSize= lod.m_IndexSize; m_TrianglesCount= lod.m_TrianglesCount; @@ -79,11 +79,7 @@ GLC_Lod& GLC_Lod::operator=(const GLC_Lod& lod) GLC_Lod::~GLC_Lod() { - // Delete IBO - if (0 != m_IboId) - { - glDeleteBuffers(1, &m_IboId); - } + } ////////////////////////////////////////////////////////////////////// @@ -98,18 +94,18 @@ quint32 GLC_Lod::chunckID() QVector GLC_Lod::indexVector() const { - if (0 != m_IboId) + if (m_IndexBuffer.isCreated()) { // VBO created get data from VBO const int sizeOfIbo= m_IndexSize; const GLsizeiptr dataSize= sizeOfIbo * sizeof(GLuint); QVector indexVector(sizeOfIbo); - useIBO(); - GLvoid* pIbo = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_READ_ONLY); + const_cast(m_IndexBuffer).bind(); + GLvoid* pIbo = const_cast(m_IndexBuffer).map(QGLBuffer::ReadOnly); memcpy(indexVector.data(), pIbo, dataSize); - glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + const_cast(m_IndexBuffer).unmap(); + const_cast(m_IndexBuffer).release(); return indexVector; } else @@ -121,7 +117,7 @@ QVector GLC_Lod::indexVector() const void GLC_Lod::copyIboToClientSide() { - if ((0 != m_IboId) && (m_IndexVector.isEmpty())) + if (m_IndexBuffer.isCreated() && (m_IndexVector.isEmpty())) { m_IndexVector= indexVector(); } @@ -130,22 +126,57 @@ void GLC_Lod::copyIboToClientSide() void GLC_Lod::releaseIboClientSide(bool update) { - if((0 != m_IboId) && !m_IndexVector.isEmpty()) + if(m_IndexBuffer.isCreated() && !m_IndexVector.isEmpty()) { if (update) { // Copy index from client side to serveur - useIBO(); + m_IndexBuffer.bind(); const GLsizei indexNbr= static_cast(m_IndexVector.size()); const GLsizeiptr indexSize = indexNbr * sizeof(GLuint); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexSize, m_IndexVector.data(), GL_STATIC_DRAW); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + m_IndexBuffer.allocate(m_IndexVector.data(), indexSize); + m_IndexBuffer.release(); } + m_IndexSize= m_IndexVector.size(); m_IndexVector.clear(); } } +void GLC_Lod::setIboUsage(bool usage) +{ + if (usage && !m_IndexVector.isEmpty()) + { + createIBO(); + // Copy index from client side to serveur + m_IndexBuffer.bind(); + + const GLsizei indexNbr= static_cast(m_IndexVector.size()); + const GLsizeiptr indexSize = indexNbr * sizeof(GLuint); + m_IndexBuffer.allocate(m_IndexVector.data(), indexSize); + m_IndexBuffer.release(); + + m_IndexSize= m_IndexVector.size(); + m_IndexVector.clear(); + + } + else if (!usage && m_IndexBuffer.isCreated()) + { + m_IndexVector= indexVector(); + m_IndexBuffer.destroy(); + } +} + +void GLC_Lod::useIBO() const +{ + Q_ASSERT(m_IndexBuffer.isCreated()); + if (!const_cast(m_IndexBuffer).bind()) + { + GLC_Exception exception("GLC_Lod::useIBO Failed to bind index buffer"); + throw(exception); + } +} + QDataStream &operator<<(QDataStream &stream, const GLC_Lod &lod) { diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_lod.h b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_lod.h index 3ff4c18a0..02856a737 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_lod.h +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_lod.h @@ -26,6 +26,8 @@ #define GLC_LOD_H_ #include +#include + #include "../glc_ext.h" #include "../glc_config.h" @@ -88,7 +90,7 @@ public: * - Triangles Fans index */ inline QVector* indexVectorHandle() - { return &m_IndexVector;} + {return &m_IndexVector;} //! Return the size of the index Vector inline int indexVectorSize() const @@ -111,12 +113,6 @@ public: //! Release client IBO void releaseIboClientSide(bool update= false); - //! The mesh wich use this lod is finished - inline void finishVbo() - { - m_IndexSize= m_IndexVector.size(); - m_IndexVector.clear(); - } //! Set accuracy of the LOD inline void setAccuracy(const double& accuracy) {m_Accuracy= accuracy;} @@ -127,6 +123,9 @@ public: m_TrianglesCount+= count; } + //! Set IBO usage + void setIboUsage(bool usage); + //@} @@ -138,15 +137,18 @@ public: //! IBO creation inline void createIBO() { - if (0 == m_IboId && !m_IndexVector.isEmpty()) + if (!m_IndexBuffer.isCreated() && !m_IndexVector.isEmpty()) { - glGenBuffers(1, &m_IboId); + m_IndexBuffer.create(); } } //! Ibo Usage - inline void useIBO() const - {glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IboId);} + void useIBO() const; + + //! Fill IBO + inline void fillIbo() + {releaseIboClientSide(true);} //@} @@ -158,8 +160,8 @@ private: //! The accuracy of the LOD double m_Accuracy; - //! The IBO ID - GLuint m_IboId; + //! The Index Buffer + QGLBuffer m_IndexBuffer; //! The Index Vector QVector m_IndexVector; diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_mesh.cpp b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_mesh.cpp index 25fd6e755..caa5d550c 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_mesh.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_mesh.cpp @@ -24,6 +24,7 @@ #include "glc_mesh.h" #include "../glc_renderstatistics.h" +#include "../glc_context.h" // Class chunk id quint32 GLC_Mesh::m_ChunkId= 0xA701; @@ -167,7 +168,7 @@ const GLC_BoundingBox& GLC_Mesh::boundingBox() if (m_MeshData.positionVectorHandle()->isEmpty()) { - qDebug() << "GLC_ExtendedMesh::getBoundingBox empty m_Positions"; + qDebug() << "GLC_Mesh::boundingBox empty m_Positions"; } else { @@ -210,7 +211,7 @@ QVector GLC_Mesh::getTrianglesIndex(int lod, GLC_uint materialId) const GLC_PrimitiveGroup* pPrimitiveGroup= m_PrimitiveGroups.value(lod)->value(materialId); int offset= 0; - if (GLC_State::vboUsed()) + if (vboIsUsed()) { offset= static_cast(reinterpret_cast(pPrimitiveGroup->trianglesIndexOffset()) / sizeof(GLuint)); } @@ -258,7 +259,7 @@ QList > GLC_Mesh::getStripsIndex(int lod, GLC_uint materialId) c QList sizes; int stripsCount; - if (GLC_State::vboUsed()) + if (vboIsUsed()) { stripsCount= pPrimitiveGroup->stripsOffset().size(); for (int i= 0; i < stripsCount; ++i) @@ -331,7 +332,7 @@ QList > GLC_Mesh::getFansIndex(int lod, GLC_uint materialId) con QList sizes; int fansCount; - if (GLC_State::vboUsed()) + if (vboIsUsed()) { fansCount= pPrimitiveGroup->fansOffset().size(); for (int i= 0; i < fansCount; ++i) @@ -455,6 +456,59 @@ GLC_Mesh& GLC_Mesh::transformVertice(const GLC_Matrix4x4& matrix) return *this; } +double GLC_Mesh::volume() +{ + double resultVolume= 0.0; + + if (!m_MeshData.isEmpty()) + { + IndexList triangleIndex; + QList materials= materialSet().toList(); + const int materialsCount= materials.count(); + for (int i= 0; i < materialsCount; ++i) + { + GLC_uint materialId= materials.at(i)->id(); + if (containsTriangles(0, materialId)) + { + triangleIndex.append(getTrianglesIndex(0, materialId).toList()); + } + if (containsStrips(0, materialId)) + { + triangleIndex.append(equivalentTrianglesIndexOfstripsIndex(0, materialId)); + } + if (containsFans(0, materialId)) + { + triangleIndex.append(equivalentTrianglesIndexOfFansIndex(0, materialId)); + } + } + + GLfloatVector vertices= m_MeshData.positionVector(); + Q_ASSERT((triangleIndex.count() % 3) == 0); + const int triangleCount= triangleIndex.count() / 3; + for (int i= 0; i < triangleCount; ++i) + { + const int index= i * 3; + const double v1x= vertices.at(triangleIndex.at(index) * 3); + const double v1y= vertices.at(triangleIndex.at(index) * 3 + 1); + const double v1z= vertices.at(triangleIndex.at(index) * 3 + 2); + + const double v2x= vertices.at(triangleIndex.at(index + 1) * 3); + const double v2y= vertices.at(triangleIndex.at(index + 1) * 3 + 1); + const double v2z= vertices.at(triangleIndex.at(index + 1) * 3 + 2); + + const double v3x= vertices.at(triangleIndex.at(index + 2) * 3); + const double v3y= vertices.at(triangleIndex.at(index + 2) * 3 + 1); + const double v3z= vertices.at(triangleIndex.at(index + 2) * 3 + 2); + + resultVolume+= ((v2y - v1y) * (v3z - v1z) - (v2z - v1z) * (v3y - v1y)) * (v1x + v2x + v3x); + } + + resultVolume= resultVolume / 6.0; + } + + return resultVolume; +} + ////////////////////////////////////////////////////////////////////// // Set Functions ////////////////////////////////////////////////////////////////////// @@ -581,8 +635,11 @@ void GLC_Mesh::reverseNormals() { (*pNormalVector)[i]= - pNormalVector->at(i); } - // Invalid the geometry - m_GeometryIsValid = false; + if (vboIsUsed()) + { + m_MeshData.fillVbo(GLC_MeshData::GLC_Normal); + m_MeshData.useVBO(false, GLC_MeshData::GLC_Normal); + } } // Copy index list in a vector for Vertex Array Use @@ -592,14 +649,7 @@ void GLC_Mesh::finish() m_MeshData.finishLod(); - if (GLC_State::vboUsed()) - { - finishVbo(); - } - else - { - finishNonVbo(); - } + moveIndexToMeshDataLod(); //qDebug() << "Mesh mem size= " << memmorySize(); } @@ -692,6 +742,15 @@ void GLC_Mesh::releaseVboClientSide(bool update) GLC_Geometry::releaseVboClientSide(update); } +void GLC_Mesh::setVboUsage(bool usage) +{ + if (!isEmpty()) + { + GLC_Geometry::setVboUsage(usage); + m_MeshData.setVboUsage(usage); + } +} + // Load the mesh from binary data stream void GLC_Mesh::loadFromDataStream(QDataStream& stream, const MaterialHash& materialHash, const QHash& materialIdMap) { @@ -749,7 +808,6 @@ void GLC_Mesh::loadFromDataStream(QDataStream& stream, const MaterialHash& mater stream >> m_NumberOfNormals; finishSerialized(); - //qDebug() << "Mesh mem size= " << memmorySize(); } // Save the mesh to binary data stream @@ -802,9 +860,10 @@ void GLC_Mesh::saveToDataStream(QDataStream& stream) const // Virtual interface for OpenGL Geometry set up. void GLC_Mesh::glDraw(const GLC_RenderProperties& renderProperties) { - Q_ASSERT(m_GeometryIsValid || !m_MeshData.normalVectorHandle()->isEmpty()); - const bool vboIsUsed= GLC_State::vboUsed(); + Q_ASSERT(m_GeometryIsValid || !m_MeshData.positionSizeIsSet()); + + const bool vboIsUsed= GLC_Geometry::vboIsUsed() && GLC_State::vboSupported(); if (m_IsSelected && (renderProperties.renderingMode() == glc::PrimitiveSelected) && !GLC_State::isInSelectionMode() && !renderProperties.setOfSelectedPrimitiveIdIsEmpty()) @@ -817,27 +876,20 @@ void GLC_Mesh::glDraw(const GLC_RenderProperties& renderProperties) m_MeshData.createVBOs(); // Create VBO and IBO - if (!m_GeometryIsValid && !m_MeshData.positionVectorHandle()->isEmpty()) + if (!m_GeometryIsValid && !m_MeshData.positionSizeIsSet()) { fillVbosAndIbos(); } - else if (!m_GeometryIsValid && !m_MeshData.normalVectorHandle()->isEmpty()) - { - // Normals has been inversed update normal vbo - m_MeshData.useVBO(true, GLC_MeshData::GLC_Normal); - - GLfloatVector* pNormalVector= m_MeshData.normalVectorHandle(); - const GLsizei dataNbr= static_cast(pNormalVector->size()); - const GLsizeiptr dataSize= dataNbr * sizeof(GLfloat); - glBufferData(GL_ARRAY_BUFFER, dataSize, pNormalVector->data(), GL_STATIC_DRAW); - m_MeshData.normalVectorHandle()->clear(); - } // Activate mesh VBOs and IBO of the current LOD activateVboAndIbo(); } else { + if (!m_GeometryIsValid) + { + m_MeshData.initPositionSize(); + } activateVertexArray(); } @@ -893,6 +945,9 @@ void GLC_Mesh::glDraw(const GLC_RenderProperties& renderProperties) case glc::OverwriteTransparency: OverwriteTransparencyRenderLoop(renderProperties, vboIsUsed); break; + case glc::OverwriteTransparencyAndMaterial: + OverwriteTransparencyAndMaterialRenderLoop(renderProperties, vboIsUsed); + break; case glc::OverwritePrimitiveMaterial: if ((m_CurrentLod == 0) && !renderProperties.hashOfOverwritePrimitiveMaterialsIsEmpty()) primitiveRenderLoop(renderProperties, vboIsUsed); @@ -907,11 +962,6 @@ void GLC_Mesh::glDraw(const GLC_RenderProperties& renderProperties) // Restore client state - if (vboIsUsed) - { - m_MeshData.useIBO(false); - m_MeshData.useVBO(false, GLC_MeshData::GLC_Normal); - } if (m_ColorPearVertex && !m_IsSelected && !GLC_State::isInSelectionMode()) { @@ -923,12 +973,18 @@ void GLC_Mesh::glDraw(const GLC_RenderProperties& renderProperties) glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); + if (vboIsUsed) + { + QGLBuffer::release(QGLBuffer::IndexBuffer); + QGLBuffer::release(QGLBuffer::VertexBuffer); + } + // Draw mesh's wire if necessary if ((renderProperties.renderingFlag() == glc::WireRenderFlag) && !m_WireData.isEmpty() && !GLC_Geometry::typeIsWire()) { if (!GLC_State::isInSelectionMode()) { - glDisable(GL_LIGHTING); + GLC_Context::current()->glcEnableLighting(false); // Set polyline colors GLfloat color[4]= {static_cast(m_WireColor.redF()), static_cast(m_WireColor.greenF()), @@ -937,7 +993,7 @@ void GLC_Mesh::glDraw(const GLC_RenderProperties& renderProperties) glColor4fv(color); m_WireData.glDraw(renderProperties, GL_LINE_STRIP); - glEnable(GL_LIGHTING); + GLC_Context::current()->glcEnableLighting(true); } else { @@ -1011,54 +1067,38 @@ GLC_uint GLC_Mesh::setCurrentMaterial(GLC_Material* pMaterial, int lod, double a // Fill VBOs and IBOs void GLC_Mesh::fillVbosAndIbos() { - // Create VBO of vertices + // Fill VBO of vertices m_MeshData.fillVbo(GLC_MeshData::GLC_Vertex); - // Create VBO of normals + // Fill VBO of normals m_MeshData.fillVbo(GLC_MeshData::GLC_Normal); - // Create VBO of texel if needed + // Fill VBO of texel if needed m_MeshData.fillVbo(GLC_MeshData::GLC_Texel); - // Create VBO of color if needed + // Fill VBO of color if needed m_MeshData.fillVbo(GLC_MeshData::GLC_Color); - const int lodNumber= m_MeshData.lodCount(); - for (int i= 0; i < lodNumber; ++i) - { - //Create LOD IBO - if (!m_MeshData.indexVectorHandle(i)->isEmpty()) - { - QVector* pIndexVector= m_MeshData.indexVectorHandle(i); - m_MeshData.useIBO(true, i); - const GLsizei indexNbr= static_cast(pIndexVector->size()); - const GLsizeiptr indexSize = indexNbr * sizeof(GLuint); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexSize, pIndexVector->data(), GL_STATIC_DRAW); - } - } - // Remove client side data - m_MeshData.finishVbo(); + // Fill a lod IBO + m_MeshData.fillLodIbo(); } // set primitive group offset void GLC_Mesh::finishSerialized() { - if (GLC_State::vboUsed()) + PrimitiveGroupsHash::iterator iGroups= m_PrimitiveGroups.begin(); + while (iGroups != m_PrimitiveGroups.constEnd()) { - PrimitiveGroupsHash::iterator iGroups= m_PrimitiveGroups.begin(); - while (iGroups != m_PrimitiveGroups.constEnd()) + LodPrimitiveGroups::iterator iGroup= iGroups.value()->begin(); + while (iGroup != iGroups.value()->constEnd()) { - LodPrimitiveGroups::iterator iGroup= iGroups.value()->begin(); - while (iGroup != iGroups.value()->constEnd()) - { - iGroup.value()->changeToVboMode(); - ++iGroup; - } - ++iGroups; + iGroup.value()->computeVboOffset(); + ++iGroup; } + ++iGroups; } } - +/* // Move Indexs from the primitive groups to the mesh Data LOD and Set IBOs offsets void GLC_Mesh::finishVbo() { @@ -1097,11 +1137,12 @@ void GLC_Mesh::finishVbo() } } +*/ // Move Indexs from the primitive groups to the mesh Data LOD and Set Index offsets -void GLC_Mesh::finishNonVbo() +void GLC_Mesh::moveIndexToMeshDataLod() { - //qDebug() << "GLC_Mesh::finishNonVbo()"; + //qDebug() << "GLC_Mesh::moveIndexToMeshDataLod()"; PrimitiveGroupsHash::iterator iGroups= m_PrimitiveGroups.begin(); while (iGroups != m_PrimitiveGroups.constEnd()) { @@ -1130,6 +1171,7 @@ void GLC_Mesh::finishNonVbo() (*m_MeshData.indexVectorHandle(currentLod))+= iGroup.value()->fansIndex().toVector(); } + iGroup.value()->computeVboOffset(); iGroup.value()->finish(); ++iGroup; } @@ -1166,9 +1208,13 @@ void GLC_Mesh::normalRenderLoop(const GLC_RenderProperties& renderProperties, bo { if (vboIsUsed) + { vboDrawPrimitivesOf(pCurrentGroup); + } else + { vertexArrayDrawPrimitivesOf(pCurrentGroup); + } } ++iGroup; @@ -1243,6 +1289,40 @@ void GLC_Mesh::OverwriteTransparencyRenderLoop(const GLC_RenderProperties& rende } } +void GLC_Mesh::OverwriteTransparencyAndMaterialRenderLoop(const GLC_RenderProperties& renderProperties, bool vboIsUsed) +{ + // Get transparency value + const float alpha= renderProperties.overwriteTransparency(); + Q_ASSERT(-1.0f != alpha); + + // Get the overwrite material + GLC_Material* pOverwriteMaterial= renderProperties.overwriteMaterial(); + Q_ASSERT(NULL != pOverwriteMaterial); + pOverwriteMaterial->glExecute(alpha); + if (m_IsSelected) GLC_SelectionMaterial::glExecute(); + + LodPrimitiveGroups::iterator iGroup= m_PrimitiveGroups.value(m_CurrentLod)->begin(); + while (iGroup != m_PrimitiveGroups.value(m_CurrentLod)->constEnd()) + { + GLC_PrimitiveGroup* pCurrentGroup= iGroup.value(); + + // Test if the current material is renderable + bool materialIsrenderable = (renderProperties.renderingFlag() == glc::TransparentRenderFlag); + + // Choose the primitives to render + if (m_IsSelected || materialIsrenderable) + { + + if (vboIsUsed) + vboDrawPrimitivesOf(pCurrentGroup); + else + vertexArrayDrawPrimitivesOf(pCurrentGroup); + } + + ++iGroup; + } +} + // The body selection render loop void GLC_Mesh::bodySelectionRenderLoop(bool vboIsUsed) { @@ -1475,3 +1555,61 @@ void GLC_Mesh::copyBulkData(GLC_Mesh* pLodMesh, const QHash& tag tempFloatVector.clear(); } } + +IndexList GLC_Mesh::equivalentTrianglesIndexOfstripsIndex(int lodIndex, GLC_uint materialId) +{ + IndexList trianglesIndex; + if (containsStrips(lodIndex, materialId)) + { + const QList > stripsIndex= getStripsIndex(lodIndex, materialId); + const int stripCount= stripsIndex.count(); + for (int i= 0; i < stripCount; ++i) + { + const QVector currentStripIndex= stripsIndex.at(i); + + trianglesIndex.append(currentStripIndex.at(0)); + trianglesIndex.append(currentStripIndex.at(1)); + trianglesIndex.append(currentStripIndex.at(2)); + const int stripSize= currentStripIndex.size(); + for (int j= 3; j < stripSize; ++j) + { + if ((j % 2) != 0) + { + trianglesIndex.append(currentStripIndex.at(j)); + trianglesIndex.append(currentStripIndex.at(j - 1)); + trianglesIndex.append(currentStripIndex.at(j - 2)); + } + else + { + trianglesIndex.append(currentStripIndex.at(j)); + trianglesIndex.append(currentStripIndex.at(j - 2)); + trianglesIndex.append(currentStripIndex.at(j - 1)); + } + } + } + } + return trianglesIndex; +} + +IndexList GLC_Mesh::equivalentTrianglesIndexOfFansIndex(int lodIndex, GLC_uint materialId) +{ + IndexList trianglesIndex; + if (containsFans(lodIndex, materialId)) + { + const QList > fanIndex= getFansIndex(lodIndex, materialId); + const int fanCount= fanIndex.count(); + for (int i= 0; i < fanCount; ++i) + { + const QVector currentFanIndex= fanIndex.at(i); + const int size= currentFanIndex.size(); + for (int j= 1; j < size - 1; ++j) + { + trianglesIndex.append(currentFanIndex.first()); + trianglesIndex.append(currentFanIndex.at(j)); + trianglesIndex.append(currentFanIndex.at(j + 1)); + } + } + } + return trianglesIndex; +} + diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_mesh.h b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_mesh.h index 389a1dd3e..98061167f 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_mesh.h +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_mesh.h @@ -183,10 +183,12 @@ public: //! Create a mesh from the given LOD index GLC_Mesh* createMeshFromGivenLod(int lodIndex); - //! Transform mesh vertice by the given matrix GLC_Mesh& transformVertice(const GLC_Matrix4x4& matrix); + //! Return the volume of this mesh + virtual double volume(); + //@} ////////////////////////////////////////////////////////////////////// /*! \name Set Functions*/ @@ -264,6 +266,9 @@ public: //! Release client VBO virtual void releaseVboClientSide(bool update); + //! Set VBO usage + virtual void setVboUsage(bool usage); + //@} ////////////////////////////////////////////////////////////////////// @@ -309,10 +314,10 @@ private: void finishSerialized(); //! Move Indexs from the primitive groups to the mesh Data LOD and Set IBOs offsets - void finishVbo(); + //void finishVbo(); //! Move Indexs from the primitive groups to the mesh Data LOD and Set Index offsets - void finishNonVbo(); + void moveIndexToMeshDataLod(); //! Use VBO to Draw primitives from the specified GLC_PrimitiveGroup inline void vboDrawPrimitivesOf(GLC_PrimitiveGroup*); @@ -353,6 +358,9 @@ private: //! The overwrite transparency render loop void OverwriteTransparencyRenderLoop(const GLC_RenderProperties&, bool); + //! The overwrite transparency and material render loop + void OverwriteTransparencyAndMaterialRenderLoop(const GLC_RenderProperties&, bool); + //! The body selection render loop void bodySelectionRenderLoop(bool); @@ -371,6 +379,13 @@ private: //! Copy Bulk data void copyBulkData(GLC_Mesh* pLodMesh, const QHash& tagetToSourceIndexMap, int maxIndex); + //! Return the equivalent triangles index of the strips index of given LOD and material ID + IndexList equivalentTrianglesIndexOfstripsIndex(int lodIndex, GLC_uint materialId); + + //! Return the equivalent triangles index of the fan index of given LOD and material ID + IndexList equivalentTrianglesIndexOfFansIndex(int lodIndex, GLC_uint materialId); + + //@} diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_meshdata.cpp b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_meshdata.cpp index 1cfb5c7ca..2c96c1a74 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_meshdata.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_meshdata.cpp @@ -22,6 +22,7 @@ //! \file glc_meshdata.cpp Implementation for the GLC_MeshData class. +#include "../glc_exception.h" #include "glc_meshdata.h" #include "../glc_state.h" @@ -30,36 +31,38 @@ quint32 GLC_MeshData::m_ChunkId= 0xA704; // Default constructor GLC_MeshData::GLC_MeshData() -: m_VboId(0) +: m_VertexBuffer() , m_Positions() , m_Normals() , m_Texels() , m_Colors() -, m_NormalVboId(0) -, m_TexelVboId(0) -, m_ColorVboId(0) +, m_NormalBuffer() +, m_TexelBuffer() +, m_ColorBuffer() , m_LodList() -, m_PositionSize(0) -, m_TexelsSize(0) -, m_ColorSize(0) +, m_PositionSize(-1) +, m_TexelsSize(-1) +, m_ColorSize(-1) +, m_UseVbo(false) { } // Copy constructor GLC_MeshData::GLC_MeshData(const GLC_MeshData& meshData) -: m_VboId(0) +: m_VertexBuffer() , m_Positions(meshData.positionVector()) , m_Normals(meshData.normalVector()) , m_Texels(meshData.texelVector()) , m_Colors(meshData.colorVector()) -, m_NormalVboId(0) -, m_TexelVboId(0) -, m_ColorVboId(0) +, m_NormalBuffer() +, m_TexelBuffer() +, m_ColorBuffer() , m_LodList() , m_PositionSize(meshData.m_PositionSize) , m_TexelsSize(meshData.m_TexelsSize) , m_ColorSize(meshData.m_ColorSize) +, m_UseVbo(meshData.m_UseVbo) { // Copy meshData LOD list const int size= meshData.m_LodList.size(); @@ -85,6 +88,7 @@ GLC_MeshData& GLC_MeshData::operator=(const GLC_MeshData& meshData) m_PositionSize= meshData.m_PositionSize; m_TexelsSize= meshData.m_TexelsSize; m_ColorSize= meshData.m_ColorSize; + m_UseVbo= meshData.m_UseVbo; // Copy meshData LOD list const int size= meshData.m_LodList.size(); @@ -112,18 +116,22 @@ quint32 GLC_MeshData::chunckID() // Return the Position Vector GLfloatVector GLC_MeshData::positionVector() const { - if (0 != m_VboId) + if (m_VertexBuffer.isCreated()) { // VBO created get data from VBO const int sizeOfVbo= m_PositionSize; const GLsizeiptr dataSize= sizeOfVbo * sizeof(float); GLfloatVector positionVector(sizeOfVbo); - glBindBuffer(GL_ARRAY_BUFFER, m_VboId); - GLvoid* pVbo = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY); + if (!const_cast(m_VertexBuffer).bind()) + { + GLC_Exception exception("GLC_MeshData::positionVector() Failed to bind vertex buffer"); + throw(exception); + } + GLvoid* pVbo = const_cast(m_VertexBuffer).map(QGLBuffer::ReadOnly); memcpy(positionVector.data(), pVbo, dataSize); - glUnmapBuffer(GL_ARRAY_BUFFER); - glBindBuffer(GL_ARRAY_BUFFER, 0); + const_cast(m_VertexBuffer).unmap(); + const_cast(m_VertexBuffer).release(); return positionVector; } else @@ -135,18 +143,18 @@ GLfloatVector GLC_MeshData::positionVector() const // Return the normal Vector GLfloatVector GLC_MeshData::normalVector() const { - if (0 != m_NormalVboId) + if (m_NormalBuffer.isCreated()) { // VBO created get data from VBO const int sizeOfVbo= m_PositionSize; const GLsizeiptr dataSize= sizeOfVbo * sizeof(GLfloat); GLfloatVector normalVector(sizeOfVbo); - glBindBuffer(GL_ARRAY_BUFFER, m_NormalVboId); - GLvoid* pVbo = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY); + const_cast(m_NormalBuffer).bind(); + GLvoid* pVbo = const_cast(m_NormalBuffer).map(QGLBuffer::ReadOnly); memcpy(normalVector.data(), pVbo, dataSize); - glUnmapBuffer(GL_ARRAY_BUFFER); - glBindBuffer(GL_ARRAY_BUFFER, 0); + const_cast(m_NormalBuffer).unmap(); + const_cast(m_NormalBuffer).release(); return normalVector; } else @@ -158,18 +166,18 @@ GLfloatVector GLC_MeshData::normalVector() const // Return the texel Vector GLfloatVector GLC_MeshData::texelVector() const { - if (0 != m_TexelVboId) + if (m_TexelBuffer.isCreated()) { // VBO created get data from VBO const int sizeOfVbo= m_TexelsSize; const GLsizeiptr dataSize= sizeOfVbo * sizeof(GLfloat); GLfloatVector texelVector(sizeOfVbo); - glBindBuffer(GL_ARRAY_BUFFER, m_TexelVboId); - GLvoid* pVbo = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY); + const_cast(m_TexelBuffer).bind(); + GLvoid* pVbo = const_cast(m_TexelBuffer).map(QGLBuffer::ReadOnly); memcpy(texelVector.data(), pVbo, dataSize); - glUnmapBuffer(GL_ARRAY_BUFFER); - glBindBuffer(GL_ARRAY_BUFFER, 0); + const_cast(m_TexelBuffer).unmap(); + const_cast(m_TexelBuffer).release(); return texelVector; } else @@ -181,18 +189,18 @@ GLfloatVector GLC_MeshData::texelVector() const // Return the color Vector GLfloatVector GLC_MeshData::colorVector() const { - if (0 != m_ColorVboId) + if (m_ColorBuffer.isCreated()) { // VBO created get data from VBO const int sizeOfVbo= m_ColorSize; const GLsizeiptr dataSize= sizeOfVbo * sizeof(GLfloat); GLfloatVector normalVector(sizeOfVbo); - glBindBuffer(GL_ARRAY_BUFFER, m_ColorVboId); - GLvoid* pVbo = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY); + const_cast(m_ColorBuffer).bind(); + GLvoid* pVbo = const_cast(m_ColorBuffer).map(QGLBuffer::ReadOnly); memcpy(normalVector.data(), pVbo, dataSize); - glUnmapBuffer(GL_ARRAY_BUFFER); - glBindBuffer(GL_ARRAY_BUFFER, 0); + const_cast(m_ColorBuffer).unmap(); + const_cast(m_ColorBuffer).release(); return normalVector; } else @@ -205,25 +213,6 @@ GLfloatVector GLC_MeshData::colorVector() const // Set Functions ////////////////////////////////////////////////////////////////////// -// The mesh wich use the data is finished -void GLC_MeshData::finishVbo() -{ - m_PositionSize= m_Positions.size(); - m_Positions.clear(); - m_Normals.clear(); - m_TexelsSize= m_Texels.size(); - m_Texels.clear(); - m_ColorSize= m_Colors.size(); - m_Colors.clear(); - - // Finish the LOD - const int size= m_LodList.size(); - for (int i= 0; i < size; ++i) - { - m_LodList[i]->finishVbo(); - } -} - // If the there is more than 2 LOD Swap the first and last void GLC_MeshData::finishLod() { @@ -244,35 +233,31 @@ void GLC_MeshData::clear() m_Normals.clear(); m_Texels.clear(); m_Colors.clear(); - m_PositionSize= 0; - m_TexelsSize= 0; - m_ColorSize= 0; + m_PositionSize= -1; + m_TexelsSize= -1; + m_ColorSize= -1; // Delete Main Vbo ID - if (0 != m_VboId) + if (m_VertexBuffer.isCreated()) { - glDeleteBuffers(1, &m_VboId); - m_VboId= 0; + m_VertexBuffer.destroy(); } // Delete Normal VBO - if (0 != m_NormalVboId) + if (m_NormalBuffer.isCreated()) { - glDeleteBuffers(1, &m_NormalVboId); - m_NormalVboId= 0; + m_NormalBuffer.destroy(); } // Delete Texel VBO - if (0 != m_TexelVboId) + if (m_TexelBuffer.isCreated()) { - glDeleteBuffers(1, &m_TexelVboId); - m_TexelVboId= 0; + m_TexelBuffer.destroy(); } // Delete color index - if (0 != m_ColorVboId) + if (m_ColorBuffer.isCreated()) { - glDeleteBuffers(1, &m_ColorVboId); - m_ColorVboId= 0; + m_ColorBuffer.destroy(); } const int size= m_LodList.size(); @@ -286,16 +271,16 @@ void GLC_MeshData::clear() void GLC_MeshData::copyVboToClientSide() { - if ((0 != m_VboId) && m_Positions.isEmpty()) + if (m_VertexBuffer.isCreated() && m_Positions.isEmpty()) { - Q_ASSERT(0 != m_NormalVboId); + Q_ASSERT(m_NormalBuffer.isCreated()); m_Positions= positionVector(); m_Normals= normalVector(); - if (0 != m_TexelVboId) + if (m_TexelBuffer.isCreated()) { m_Texels= texelVector(); } - if (0 != m_ColorVboId) + if (m_ColorBuffer.isCreated()) { m_Colors= colorVector(); } @@ -304,7 +289,7 @@ void GLC_MeshData::copyVboToClientSide() void GLC_MeshData::releaseVboClientSide(bool update) { - if ((0 != m_VboId) && !m_Positions.isEmpty()) + if (m_VertexBuffer.isCreated() && !m_Positions.isEmpty()) { if (update) { @@ -324,28 +309,82 @@ void GLC_MeshData::releaseVboClientSide(bool update) } } +void GLC_MeshData::setVboUsage(bool usage) +{ + if (usage && (m_PositionSize != -1) && (!m_Positions.isEmpty()) && (!m_VertexBuffer.isCreated())) + { + createVBOs(); + + fillVbo(GLC_MeshData::GLC_Vertex); + fillVbo(GLC_MeshData::GLC_Normal); + fillVbo(GLC_MeshData::GLC_Texel); + fillVbo(GLC_MeshData::GLC_Color); + useVBO(false, GLC_MeshData::GLC_Color); + + const int lodCount= m_LodList.count(); + for (int i= 0; i < lodCount; ++i) + { + m_LodList.at(i)->setIboUsage(usage); + } + + } + else if (!usage && m_VertexBuffer.isCreated()) + { + m_Positions= positionVector(); + m_PositionSize= m_Positions.size(); + m_VertexBuffer.destroy(); + + m_Normals= normalVector(); + m_NormalBuffer.destroy(); + + if (m_TexelBuffer.isCreated()) + { + m_Texels= texelVector(); + m_TexelsSize= m_Texels.size(); + m_TexelBuffer.destroy(); + } + if (m_ColorBuffer.isCreated()) + { + m_Colors= colorVector(); + m_ColorSize= m_Colors.size(); + m_ColorBuffer.destroy(); + } + + const int lodCount= m_LodList.count(); + for (int i= 0; i < lodCount; ++i) + { + m_LodList.at(i)->setIboUsage(usage); + } + } + m_UseVbo= usage; + +} + ////////////////////////////////////////////////////////////////////// // OpenGL Functions ////////////////////////////////////////////////////////////////////// // Vbo creation void GLC_MeshData::createVBOs() { + // Create position VBO - if (0 == m_VboId) + if (!m_VertexBuffer.isCreated()) { - glGenBuffers(1, &m_VboId); - glGenBuffers(1, &m_NormalVboId); + Q_ASSERT((NULL != QGLContext::currentContext()) && QGLContext::currentContext()->isValid()); + + m_VertexBuffer.create(); + m_NormalBuffer.create(); // Create Texel VBO - if (0 == m_TexelVboId && !m_Texels.isEmpty()) + if (!m_TexelBuffer.isCreated() && !m_Texels.isEmpty()) { - glGenBuffers(1, &m_TexelVboId); + m_TexelBuffer.create(); } // Create Color VBO - if (0 == m_ColorVboId && !m_Colors.isEmpty()) + if (!m_ColorBuffer.isCreated() && !m_Colors.isEmpty()) { - glGenBuffers(1, &m_ColorVboId); + m_ColorBuffer.create(); } const int size= m_LodList.size(); @@ -357,7 +396,7 @@ void GLC_MeshData::createVBOs() } // Ibo Usage -bool GLC_MeshData::useVBO(bool use, GLC_MeshData::VboType type) const +bool GLC_MeshData::useVBO(bool use, GLC_MeshData::VboType type) { bool result= true; if (use) @@ -365,19 +404,35 @@ bool GLC_MeshData::useVBO(bool use, GLC_MeshData::VboType type) const // Chose the right VBO if (type == GLC_MeshData::GLC_Vertex) { - glBindBuffer(GL_ARRAY_BUFFER, m_VboId); + if (!m_VertexBuffer.bind()) + { + GLC_Exception exception("GLC_MeshData::useVBO Failed to bind vertex buffer"); + throw(exception); + } } else if (type == GLC_MeshData::GLC_Normal) { - glBindBuffer(GL_ARRAY_BUFFER, m_NormalVboId); + if (!m_NormalBuffer.bind()) + { + GLC_Exception exception("GLC_MeshData::useVBO Failed to bind normal buffer"); + throw(exception); + } } - else if ((type == GLC_MeshData::GLC_Texel) && (0 != m_TexelVboId)) + else if ((type == GLC_MeshData::GLC_Texel) && m_TexelBuffer.isCreated()) { - glBindBuffer(GL_ARRAY_BUFFER, m_TexelVboId); + if (!m_TexelBuffer.bind()) + { + GLC_Exception exception("GLC_MeshData::useVBO Failed to bind texel buffer"); + throw(exception); + } } - else if ((type == GLC_MeshData::GLC_Color) && (0 != m_ColorVboId)) + else if ((type == GLC_MeshData::GLC_Color) && m_ColorBuffer.isCreated()) { - glBindBuffer(GL_ARRAY_BUFFER, m_ColorVboId); + if (!m_ColorBuffer.bind()) + { + GLC_Exception exception("GLC_MeshData::useVBO Failed to bind color buffer"); + throw(exception); + } } else result= false; @@ -385,7 +440,7 @@ bool GLC_MeshData::useVBO(bool use, GLC_MeshData::VboType type) const else { // Unbind VBO - glBindBuffer(GL_ARRAY_BUFFER, 0); + QGLBuffer::release(QGLBuffer::VertexBuffer); } return result; } @@ -398,28 +453,48 @@ void GLC_MeshData::fillVbo(GLC_MeshData::VboType type) useVBO(true, type); const GLsizei dataNbr= static_cast(m_Positions.size()); const GLsizeiptr dataSize= dataNbr * sizeof(GLfloat); - glBufferData(GL_ARRAY_BUFFER, dataSize, m_Positions.data(), GL_STATIC_DRAW); + m_VertexBuffer.allocate(m_Positions.data(), dataSize); + + m_PositionSize= m_Positions.size(); + m_Positions.clear(); } else if (type == GLC_MeshData::GLC_Normal) { useVBO(true, type); const GLsizei dataNbr= static_cast(m_Normals.size()); const GLsizeiptr dataSize= dataNbr * sizeof(GLfloat); - glBufferData(GL_ARRAY_BUFFER, dataSize, m_Normals.data(), GL_STATIC_DRAW); + m_NormalBuffer.allocate(m_Normals.data(), dataSize); + + m_Normals.clear(); } - else if ((type == GLC_MeshData::GLC_Texel) && (0 != m_TexelVboId)) + else if ((type == GLC_MeshData::GLC_Texel) && m_TexelBuffer.isCreated()) { useVBO(true, type); const GLsizei dataNbr= static_cast(m_Texels.size()); const GLsizeiptr dataSize= dataNbr * sizeof(GLfloat); - glBufferData(GL_ARRAY_BUFFER, dataSize, m_Texels.data(), GL_STATIC_DRAW); + m_TexelBuffer.allocate(m_Texels.data(), dataSize); + + m_TexelsSize= m_Texels.size(); + m_Texels.clear(); } - else if ((type == GLC_MeshData::GLC_Color) && (0 != m_ColorVboId)) + else if ((type == GLC_MeshData::GLC_Color) && m_ColorBuffer.isCreated()) { useVBO(true, type); const GLsizei dataNbr= static_cast(m_Colors.size()); const GLsizeiptr dataSize= dataNbr * sizeof(GLfloat); - glBufferData(GL_ARRAY_BUFFER, dataSize, m_Colors.data(), GL_STATIC_DRAW); + m_ColorBuffer.allocate(m_Colors.data(), dataSize); + + m_ColorSize= m_Colors.size(); + m_Colors.clear(); + } +} + +void GLC_MeshData::fillLodIbo() +{ + const int lodCount= m_LodList.count(); + for (int i= 0; i < lodCount; ++i) + { + m_LodList.at(i)->fillIbo(); } } // Non Member methods diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_meshdata.h b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_meshdata.h index 04c33e526..f9c5160ba 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_meshdata.h +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_meshdata.h @@ -26,6 +26,7 @@ #define GLC_MESHDATA_H_ #include +#include #include "glc_lod.h" #include "../glc_global.h" @@ -140,7 +141,7 @@ public: //! Return true if the mesh data doesn't contains vertice inline bool isEmpty() const - {return (0 == m_PositionSize) && (0 == m_Positions.size());} + {return (1 > m_PositionSize) && (0 == m_Positions.size());} //! Return the number of triangle from the given lod index inline unsigned int trianglesCount(int lod) const @@ -149,6 +150,10 @@ public: return m_LodList.at(lod)->trianglesCount(); } + //! Return true if the position size is set + inline bool positionSizeIsSet() const + {return m_PositionSize != -1;} + //@} ////////////////////////////////////////////////////////////////////// @@ -160,9 +165,6 @@ public: inline void appendLod(double accuracy= 0.0) {m_LodList.append(new GLC_Lod(accuracy));} - //! The mesh wich use the data is finished and VBO is used - void finishVbo(); - //! If the there is more than 2 LOD Swap the first and last void finishLod(); @@ -183,6 +185,13 @@ public: m_LodList.at(lod)->trianglesAdded(number); } + //! Set VBO usage + void setVboUsage(bool usage); + + //! Init the position size + inline void initPositionSize() + {m_PositionSize= m_Positions.size();} + //@} ////////////////////////////////////////////////////////////////////// @@ -194,15 +203,18 @@ public: void createVBOs(); //! Ibo Usage - bool useVBO(bool, GLC_MeshData::VboType) const; + bool useVBO(bool, GLC_MeshData::VboType); //! Ibo Usage inline void useIBO(bool use, const int currentLod= 0) { if (use) m_LodList.at(currentLod)->useIBO(); - else glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + else QGLBuffer::release(QGLBuffer::IndexBuffer); } + //! Fill all LOD IBO + void fillLodIbo(); + //! Fill the VBO of the given type void fillVbo(GLC_MeshData::VboType vboType); @@ -213,8 +225,8 @@ public: ////////////////////////////////////////////////////////////////////// private: - //! Main VBO ID - GLuint m_VboId; + //! The vertex Buffer + QGLBuffer m_VertexBuffer; //! Vertex Position Vector GLfloatVector m_Positions; @@ -228,14 +240,14 @@ private: //! Color index GLfloatVector m_Colors; - //! Normals VBO ID - GLuint m_NormalVboId; + //! Normals Buffer + QGLBuffer m_NormalBuffer; - //! Texture VBO ID - GLuint m_TexelVboId; + //! Texture Buffer + QGLBuffer m_TexelBuffer; - //! Color VBO ID - GLuint m_ColorVboId; + //! Color Buffer + QGLBuffer m_ColorBuffer; //! The list of LOD QList m_LodList; @@ -249,6 +261,9 @@ private: //! The size of Color VBO int m_ColorSize; + //! Use VBO + bool m_UseVbo; + //! Class chunk id static quint32 m_ChunkId; }; diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_point.cpp b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_point.cpp index 4cd7f8543..a89dd6e25 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_point.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_point.cpp @@ -32,18 +32,27 @@ using namespace glc; GLC_Point::GLC_Point(const GLC_Point3d &setCoord) -:GLC_Geometry("Point", true) +:GLC_PointCloud() , m_Coordinate(setCoord) , m_Size(1.0f) { - + setCoordinate(m_Coordinate); } -//! Construct an GLC_Point + GLC_Point::GLC_Point(double x, double y, double z) -:GLC_Geometry("Point", true) +:GLC_PointCloud() , m_Coordinate(x, y, z) , m_Size(1.0f) { + setCoordinate(m_Coordinate); +} + +GLC_Point::GLC_Point(const GLC_Point& point) +:GLC_PointCloud(point) +, m_Coordinate(point.m_Coordinate) +, m_Size(point.m_Size) +{ + } ////////////////////////////////////////////////////////////////////// @@ -56,26 +65,6 @@ GLC_Point3d GLC_Point::coordinate(void) const return m_Coordinate; } -// return the point bounding box -const GLC_BoundingBox& GLC_Point::boundingBox(void) -{ - - if (NULL == m_pBoundingBox) - { - m_pBoundingBox= new GLC_BoundingBox(); - const double delta= 1e-2; - GLC_Point3d lower(m_Coordinate.x() - delta, - m_Coordinate.y() - delta, - m_Coordinate.z() - delta); - GLC_Point3d upper(m_Coordinate.x() + delta, - m_Coordinate.y() + delta, - m_Coordinate.z() + delta); - m_pBoundingBox->combine(lower); - m_pBoundingBox->combine(upper); - } - return *m_pBoundingBox; -} - // Return a copy of the current geometry GLC_Geometry* GLC_Point::clone() const { @@ -90,32 +79,26 @@ GLC_Geometry* GLC_Point::clone() const void GLC_Point::setCoordinate(const GLC_Point3d &point) { m_Coordinate= point; + GLC_PointCloud::clear(); + QList points; + points.append(m_Coordinate); + GLC_PointCloud::addPoint(points); + } // Set Point coordinate by 3 double void GLC_Point::setCoordinate(double x, double y, double z) { - m_Coordinate.setVect(x, y, z); + setCoordinate(GLC_Point3d(x, y, z)); } ////////////////////////////////////////////////////////////////////// // OpenGL Functions ////////////////////////////////////////////////////////////////////// -void GLC_Point::glDraw(const GLC_RenderProperties&) +void GLC_Point::glDraw(const GLC_RenderProperties& renderProperties) { glPointSize(m_Size); // Point Display - glBegin(GL_POINTS); - glVertex3dv(m_Coordinate.data()); - glEnd(); - glPointSize(1.0f); - - // OpenGL error handler - GLenum error= glGetError(); - if (error != GL_NO_ERROR) - { - GLC_OpenGlException OpenGlException("GLC_Point::GlDraw ", error); - throw(OpenGlException); - } + GLC_PointCloud::glDraw(renderProperties); } diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_point.h b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_point.h index d50b40dc0..ba9bf6cfb 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_point.h +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_point.h @@ -25,7 +25,7 @@ #ifndef GLC_POINT_H_ #define GLC_POINT_H_ -#include "glc_geometry.h" +#include "glc_pointcloud.h" #include "../glc_config.h" @@ -36,7 +36,7 @@ /*! An GLC_Point is just a simple 3D Point*/ ////////////////////////////////////////////////////////////////////// -class GLC_LIB_EXPORT GLC_Point : public GLC_Geometry +class GLC_LIB_EXPORT GLC_Point : public GLC_PointCloud { ////////////////////////////////////////////////////////////////////// /*! @name Constructor / Destructor */ @@ -49,6 +49,9 @@ public: //! Construct an GLC_Point GLC_Point(double, double, double); + //! Copy constructor + GLC_Point(const GLC_Point& point); + //@} ////////////////////////////////////////////////////////////////////// @@ -60,9 +63,6 @@ public: //! Return a GLC_Point3d of coordinate GLC_Point3d coordinate(void) const; - //! Return the point bounding box - virtual const GLC_BoundingBox& boundingBox(void); - //! Return a copy of the geometry virtual GLC_Geometry* clone() const; diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_pointcloud.cpp b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_pointcloud.cpp index 48bdabbcf..2e8535102 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_pointcloud.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_pointcloud.cpp @@ -96,6 +96,23 @@ GLC_uint GLC_PointCloud::addPoint(const QList& pointsList) return GLC_Geometry::m_WireData.addVerticeGroup(data); } +void GLC_PointCloud::addColors(const QList& colors) +{ + const int colorCount= colors.count(); + const int size= colorCount * 4; + GLfloatVector data(size); + for (int i= 0; i < colorCount; ++i) + { + QColor color= colors.at(i); + data[i * 4]= static_cast(color.redF()); + data[i * 4 + 1]= static_cast(color.greenF()); + data[i * 4 + 2]= static_cast(color.blueF()); + data[i * 4 + 3]= static_cast(color.alphaF()); + } + + GLC_Geometry::m_WireData.addColors(data); +} + GLC_PointCloud& GLC_PointCloud::operator=(const GLC_PointCloud& pointCloud) { if (this != &pointCloud) diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_pointcloud.h b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_pointcloud.h index b9e49c7d3..41462957f 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_pointcloud.h +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_pointcloud.h @@ -86,6 +86,13 @@ public: //! Add the given list of points to this cloud and returns its id if id are managed GLC_uint addPoint(const QList& pointsList); + //! Add Colors + inline void addColors(const GLfloatVector& colors) + {GLC_Geometry::m_WireData.addColors(colors);} + + //! Add Colors + void addColors(const QList& colors); + //! Set this point cloud from the given point cloud and return a reference of this point cloud GLC_PointCloud& operator=(const GLC_PointCloud& pointcloud); @@ -99,7 +106,7 @@ public: /*! \name OpenGL Functions*/ //@{ ////////////////////////////////////////////////////////////////////// -private: +protected: //! Virtual interface for OpenGL Geometry set up. /*! This Virtual function is implemented here.\n diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_pointsprite.cpp b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_pointsprite.cpp index a90b1e8ef..560bb6048 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_pointsprite.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_pointsprite.cpp @@ -27,13 +27,14 @@ #include "../glc_state.h" #include "../glc_ext.h" #include "../shading/glc_selectionmaterial.h" +#include "../glc_context.h" // The maximum point size float GLC_PointSprite::m_MaxSize= -1.0f; // Default constructor GLC_PointSprite::GLC_PointSprite(float size, GLC_Material* pMaterial) -:GLC_Geometry("PointSprite", false) +:GLC_PointCloud() , m_Size(size) , m_DistanceAttenuation(3) , m_FadeThresoldSize(60.0f) @@ -46,6 +47,19 @@ GLC_PointSprite::GLC_PointSprite(float size, GLC_Material* pMaterial) m_DistanceAttenuation[0]= 1.0f; m_DistanceAttenuation[1]= 0.0f; m_DistanceAttenuation[2]= 0.0f; + + QList points; + points.append(GLC_Point3d(0.0, 0.0, 0.0)); + GLC_PointCloud::addPoint(points); +} + +GLC_PointSprite::GLC_PointSprite(const GLC_PointSprite& point) +: GLC_PointCloud(point) +, m_Size(point.m_Size) +, m_DistanceAttenuation(point.m_DistanceAttenuation) +, m_FadeThresoldSize(point.m_FadeThresoldSize) +{ + } GLC_PointSprite::~GLC_PointSprite() @@ -53,26 +67,6 @@ GLC_PointSprite::~GLC_PointSprite() } -// return the point bounding box -const GLC_BoundingBox& GLC_PointSprite::boundingBox(void) -{ - - if (NULL == m_pBoundingBox) - { - m_pBoundingBox= new GLC_BoundingBox(); - const double epsilon= 1e-2; - GLC_Point3d lower( - epsilon, - - epsilon, - - epsilon); - GLC_Point3d upper( epsilon, - epsilon, - epsilon); - m_pBoundingBox->combine(lower); - m_pBoundingBox->combine(upper); - } - return *m_pBoundingBox; -} - // Return a copy of the current geometry GLC_Geometry* GLC_PointSprite::clone() const { @@ -116,6 +110,7 @@ void GLC_PointSprite::render(const GLC_RenderProperties& renderProperties) glEnable(GL_TEXTURE_2D); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_LIGHTING); + //GLC_Context::current()->glcEnableLighting(false); if(m_MaterialHash.size() == 1) { @@ -143,6 +138,7 @@ void GLC_PointSprite::render(const GLC_RenderProperties& renderProperties) glDisable(GL_BLEND); glDisable(GL_TEXTURE_2D); glDisable(GL_LIGHTING); + //GLC_Context::current()->glcEnableLighting(false); } @@ -182,19 +178,8 @@ void GLC_PointSprite::render(const GLC_RenderProperties& renderProperties) } // Point sprite set up -void GLC_PointSprite::glDraw(const GLC_RenderProperties&) +void GLC_PointSprite::glDraw(const GLC_RenderProperties& renderProperties) { - // Point Display - glBegin(GL_POINTS); - glVertex3f(0.0f,0.0f,0.0f); - glEnd(); - - // OpenGL error handler - GLenum error= glGetError(); - if (error != GL_NO_ERROR) - { - GLC_OpenGlException OpenGlException("GLC_PointSprite::GlDraw ", error); - throw(OpenGlException); - } + GLC_PointCloud::glDraw(renderProperties); } diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_pointsprite.h b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_pointsprite.h index 62cf2f623..fc74f05b9 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_pointsprite.h +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_pointsprite.h @@ -22,7 +22,7 @@ *****************************************************************************/ //! \file glc_pointsprite.h interface for the GLC_PointSprite class. -#include "glc_geometry.h" +#include "glc_pointcloud.h" #include #include "../glc_config.h" @@ -37,7 +37,7 @@ /*! An GLC_PointSprite is just a simple 3D Sprite Point*/ ////////////////////////////////////////////////////////////////////// -class GLC_LIB_EXPORT GLC_PointSprite : public GLC_Geometry +class GLC_LIB_EXPORT GLC_PointSprite : public GLC_PointCloud { public: ////////////////////////////////////////////////////////////////////// @@ -48,6 +48,9 @@ public: /*! The material must exist and had texture*/ GLC_PointSprite(float, GLC_Material*); + //! Copy constructor + GLC_PointSprite(const GLC_PointSprite& point); + //! Default destructor virtual ~GLC_PointSprite(); //@} @@ -61,9 +64,6 @@ public: inline float size() const {return m_Size;} - //! Return the point bounding box - virtual const GLC_BoundingBox& boundingBox(void); - //! Return a copy of the geometry virtual GLC_Geometry* clone() const; diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_polylines.cpp b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_polylines.cpp index 2c1336d7b..89e73e9ae 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_polylines.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_polylines.cpp @@ -64,6 +64,38 @@ GLC_Geometry* GLC_Polylines::clone() const ////////////////////////////////////////////////////////////////////// // Set Functions ////////////////////////////////////////////////////////////////////// + + +GLC_uint GLC_Polylines::addPolyline(const QList& pointsList) +{ + const int pointCount= pointsList.size(); + const int size= pointCount * 3; + GLfloatVector data(size); + for (int i= 0; i < pointCount; ++i) + { + const GLC_Point3d currentPoint(pointsList.at(i)); + data[i * 3]= static_cast(currentPoint.x()); + data[i * 3 + 1]= static_cast(currentPoint.y()); + data[i * 3 + 2]= static_cast(currentPoint.z()); + } + return GLC_Geometry::m_WireData.addVerticeGroup(data); +} + +GLC_uint GLC_Polylines::addPolyline(const QList& pointsList) +{ + const int pointCount= pointsList.size(); + const int size= pointCount * 3; + GLfloatVector data(size); + for (int i= 0; i < pointCount; ++i) + { + const GLC_Point3df currentPoint(pointsList.at(i)); + data[i * 3]= currentPoint.x(); + data[i * 3 + 1]= currentPoint.y(); + data[i * 3 + 2]= currentPoint.z(); + } + return GLC_Geometry::m_WireData.addVerticeGroup(data); +} + GLC_Polylines& GLC_Polylines::operator=(const GLC_Polylines& polyline) { if (this != &polyline) diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_polylines.h b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_polylines.h index 9655c5cb2..545d5382a 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_polylines.h +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_polylines.h @@ -74,10 +74,16 @@ public: //@{ ////////////////////////////////////////////////////////////////////// public: - //! Add a Polyline to this wire and returns its id if id are managed + //! Add a Polyline to this polylines and returns its id if id are managed inline GLC_uint addPolyline(const GLfloatVector& data) {return GLC_Geometry::m_WireData.addVerticeGroup(data);} + //! Add polyline with the given list of points to this polylines and returns its id if id are managed + GLC_uint addPolyline(const QList& pointsList); + + //! Add polyline with the given list of points to this polylines and returns its id if id are managed + GLC_uint addPolyline(const QList& pointsList); + //! Set this polylines from the given polylines and return a reference of this polylines GLC_Polylines& operator=(const GLC_Polylines& polyline); @@ -91,7 +97,7 @@ public: /*! \name OpenGL Functions*/ //@{ ////////////////////////////////////////////////////////////////////// -private: +protected: //! Virtual interface for OpenGL Geometry set up. /*! This Virtual function is implemented here.\n diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_primitivegroup.cpp b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_primitivegroup.cpp index db13e07e3..bae3546f6 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_primitivegroup.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_primitivegroup.cpp @@ -189,13 +189,12 @@ void GLC_PrimitiveGroup::addTrianglesStrip(const IndexList& input, GLC_uint id) // Set the triangle index offset void GLC_PrimitiveGroup::setTrianglesOffset(GLvoid* pOffset) { - m_TrianglesGroupOffseti.pop_back(); + //m_TrianglesGroupOffseti.pop_back(); const int size= m_TrianglesGroupOffseti.size(); for (int i= 0; i < size; ++i) { m_TrianglesGroupOffset.append(BUFFER_OFFSET(static_cast(m_TrianglesGroupOffseti[i]) * sizeof(GLuint) + reinterpret_cast(pOffset))); } - m_TrianglesGroupOffseti.clear(); } // Set the triangle index offset @@ -212,13 +211,12 @@ void GLC_PrimitiveGroup::setTrianglesOffseti(int offset) // Set base triangle strip offset void GLC_PrimitiveGroup::setBaseTrianglesStripOffset(GLvoid* pOffset) { - m_StripIndexOffseti.pop_back(); + //m_StripIndexOffseti.pop_back(); const int size= m_StripIndexOffseti.size(); for (int i= 0; i < size; ++i) { m_StripIndexOffset.append(BUFFER_OFFSET(static_cast(m_StripIndexOffseti[i]) * sizeof(GLuint) + reinterpret_cast(pOffset))); } - m_StripIndexOffseti.clear(); } // Set base triangle strip offset @@ -257,13 +255,12 @@ void GLC_PrimitiveGroup::addTrianglesFan(const IndexList& input, GLC_uint id) // Set base triangle fan offset void GLC_PrimitiveGroup::setBaseTrianglesFanOffset(GLvoid* pOffset) { - m_FanIndexOffseti.pop_back(); + //m_FanIndexOffseti.pop_back(); const int size= m_FanIndexOffseti.size(); for (int i= 0; i < size; ++i) { m_FanIndexOffset.append(BUFFER_OFFSET(static_cast(m_FanIndexOffseti[i]) * sizeof(GLuint) + reinterpret_cast(pOffset))); } - m_FanIndexOffseti.clear(); } // Set base triangle fan offset @@ -278,16 +275,14 @@ void GLC_PrimitiveGroup::setBaseTrianglesFanOffseti(int offset) } // Change index to VBO mode -void GLC_PrimitiveGroup::changeToVboMode() +void GLC_PrimitiveGroup::computeVboOffset() { - m_TrianglesGroupOffset.clear(); const int triangleOffsetSize= m_TrianglesGroupOffseti.size(); for (int i= 0; i < triangleOffsetSize; ++i) { m_TrianglesGroupOffset.append(BUFFER_OFFSET(static_cast(m_TrianglesGroupOffseti.at(i)) * sizeof(GLuint))); } - m_TrianglesGroupOffseti.clear(); m_StripIndexOffset.clear(); const int stripOffsetSize= m_StripIndexOffseti.size(); @@ -295,7 +290,6 @@ void GLC_PrimitiveGroup::changeToVboMode() { m_StripIndexOffset.append(BUFFER_OFFSET(static_cast(m_StripIndexOffseti.at(i)) * sizeof(GLuint))); } - m_StripIndexOffseti.clear(); m_FanIndexOffset.clear(); const int fanOffsetSize= m_FanIndexOffseti.size(); @@ -303,7 +297,6 @@ void GLC_PrimitiveGroup::changeToVboMode() { m_FanIndexOffset.append(BUFFER_OFFSET(static_cast(m_FanIndexOffseti.at(i)) * sizeof(GLuint))); } - m_FanIndexOffseti.clear(); } // Clear the group @@ -345,35 +338,10 @@ QDataStream &operator<<(QDataStream &stream, const GLC_PrimitiveGroup &primitive OffsetVectori fanIndexOffseti; // Get triangles, strips and fans offset - if (GLC_State::vboUsed()) - { - // Convert offset to index - // Triangles offset - const int triangleIndexOffsetSize= primitiveGroup.m_TrianglesGroupOffset.size(); - for (int i= 0; i < triangleIndexOffsetSize; ++i) - { - trianglesGroupOffseti.append(static_cast(reinterpret_cast(primitiveGroup.m_TrianglesGroupOffset.at(i)) / sizeof(GLuint))); - } + trianglesGroupOffseti= primitiveGroup.m_TrianglesGroupOffseti; + stripIndexOffseti= primitiveGroup.m_StripIndexOffseti; + fanIndexOffseti= primitiveGroup.m_FanIndexOffseti; - // Trips offsets - const int stripIndexOffsetSize= primitiveGroup.m_StripIndexOffset.size(); - for (int i= 0; i < stripIndexOffsetSize; ++i) - { - stripIndexOffseti.append(static_cast(reinterpret_cast(primitiveGroup.m_StripIndexOffset.at(i)) / sizeof(GLuint))); - } - // Fans offsets - const int fanIndexOffsetSize= primitiveGroup.m_FanIndexOffset.size(); - for (int i= 0; i < fanIndexOffsetSize; ++i) - { - fanIndexOffseti.append(static_cast(reinterpret_cast(primitiveGroup.m_FanIndexOffset.at(i)) / sizeof(GLuint))); - } - } - else - { - trianglesGroupOffseti= primitiveGroup.m_TrianglesGroupOffseti; - stripIndexOffseti= primitiveGroup.m_StripIndexOffseti; - fanIndexOffseti= primitiveGroup.m_FanIndexOffseti; - } // Triangles index stream << primitiveGroup.m_TrianglesIndexSize; stream << trianglesGroupOffseti; diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_primitivegroup.h b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_primitivegroup.h index f367ca17d..93ec228a6 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_primitivegroup.h +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_primitivegroup.h @@ -28,7 +28,7 @@ #include "../glc_ext.h" #include "../glc_global.h" -#include "glc_config.h" +#include "../glc_config.h" ////////////////////////////////////////////////////////////////////// //! \class GLC_PrimitiveGroup @@ -231,8 +231,8 @@ public: //! Set base triangle fan offset void setBaseTrianglesFanOffseti(int); - //! Change index to VBO mode - void changeToVboMode(); + //! Compute VBO offset + void computeVboOffset(); //! The mesh wich use this group is finished inline void finish() diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_sphere.cpp b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_sphere.cpp index fdd7f70e2..5c2a70400 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_sphere.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_sphere.cpp @@ -36,7 +36,7 @@ GLC_Sphere::GLC_Sphere(double radius) , m_PhiMin(-glc::PI / 2.0) , m_PhiMax(glc::PI / 2.0) { - + createMesh(); } @@ -49,7 +49,7 @@ GLC_Sphere::GLC_Sphere(const GLC_Sphere & sphere) , m_PhiMin(sphere.m_PhiMin) , m_PhiMax(sphere.m_PhiMax) { - + createMesh(); } GLC_Sphere::~GLC_Sphere() @@ -92,8 +92,19 @@ void GLC_Sphere::setDiscretion(int TargetDiscret) } } +void GLC_Sphere::glDraw(const GLC_RenderProperties& renderProperties) +{ + if (GLC_Mesh::isEmpty()) + { + createMesh(); + } + + GLC_Mesh::glDraw(renderProperties); +} + void GLC_Sphere::createMesh() { + Q_ASSERT(GLC_Mesh::isEmpty()); GLfloatVector verticeFloat; @@ -153,8 +164,8 @@ void GLC_Sphere::createMesh() xf= m_Radius * cost * cospp; yf= m_Radius * sint * cospp; - verticeFloat << xi << yi << zi << xf << yf << zf; - normalsFloat << cost * cosp << sint * cosp << sinp << cost * cospp << sint * cospp << sinpp ; + verticeFloat << xf << yf << zf << xi << yi << zi; + normalsFloat << cost * cospp << sint * cospp << sinpp << cost * cosp << sint * cosp << sinp; texelVector << static_cast(t) * 1.0 / static_cast(nbThetaSteps) << static_cast(p) * 1.0 / static_cast(nbPhiSteps) << static_cast(t) * 1.0 / static_cast(nbThetaSteps) diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_sphere.h b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_sphere.h index 2b1cafc04..4523f88e5 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_sphere.h +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_sphere.h @@ -90,6 +90,17 @@ public: //@} +////////////////////////////////////////////////////////////////////// +/*! \name OpenGL Functions*/ +//@{ +////////////////////////////////////////////////////////////////////// +private: + //! Virtual interface for OpenGL Geometry set up. + /*! This Virtual function is implemented here.\n + * Throw GLC_OpenGlException*/ + virtual void glDraw(const GLC_RenderProperties&); +//@} + ////////////////////////////////////////////////////////////////////// /*! \name Private services Functions*/ //@{ diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_wiredata.cpp b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_wiredata.cpp index c2c8bcfbe..69b61121e 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_wiredata.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_wiredata.cpp @@ -23,38 +23,55 @@ //! \file glc_wiredata.cpp Implementation for the GLC_WireData class. #include "glc_wiredata.h" +#include "glc_bsrep.h" #include "../glc_ext.h" #include "../glc_state.h" +#include "../glc_exception.h" // Class chunk id -quint32 GLC_WireData::m_ChunkId= 0xA706; +// Old chunkId = 0xA706 +quint32 GLC_WireData::m_ChunkId= 0xA711; GLC_WireData::GLC_WireData() -: m_VboId(0) +: m_VerticeBuffer() , m_NextPrimitiveLocalId(1) , m_Positions() +, m_ColorBuffer() +, m_Colors() +, m_IndexBuffer(QGLBuffer::IndexBuffer) +, m_IndexVector() , m_PositionSize(0) +, m_ColorSize(0) , m_pBoundingBox(NULL) , m_VerticeGrouprSizes() +, m_VerticeGroupOffseti() , m_VerticeGroupOffset() , m_VerticeGroupId() , m_VerticeGroupCount(0) +, m_UseVbo(false) { } GLC_WireData::GLC_WireData(const GLC_WireData& data) -: m_VboId(0) +: m_VerticeBuffer() , m_NextPrimitiveLocalId(data.m_NextPrimitiveLocalId) , m_Positions(data.positionVector()) +, m_ColorBuffer() +, m_Colors(data.colorVector()) +, m_IndexBuffer(QGLBuffer::IndexBuffer) +, m_IndexVector(data.indexVector()) , m_PositionSize(data.m_PositionSize) +, m_ColorSize(data.m_ColorSize) , m_pBoundingBox(NULL) , m_VerticeGrouprSizes(data.m_VerticeGrouprSizes) +, m_VerticeGroupOffseti(data.m_VerticeGroupOffseti) , m_VerticeGroupOffset(data.m_VerticeGroupOffset) , m_VerticeGroupId(data.m_VerticeGroupId) , m_VerticeGroupCount(data.m_VerticeGroupCount) +, m_UseVbo(data.m_UseVbo) { if (NULL != data.m_pBoundingBox) { @@ -70,15 +87,20 @@ GLC_WireData& GLC_WireData::operator=(const GLC_WireData& data) clear(); m_NextPrimitiveLocalId= data.m_NextPrimitiveLocalId; m_Positions= data.positionVector(); + m_Colors= data.colorVector(); + m_IndexVector= data.indexVector(); m_PositionSize= data.m_PositionSize; + m_ColorSize= data.m_ColorSize; if (NULL != data.m_pBoundingBox) { m_pBoundingBox= new GLC_BoundingBox(*(data.m_pBoundingBox)); } m_VerticeGrouprSizes= data.m_VerticeGrouprSizes; + m_VerticeGroupOffseti= data.m_VerticeGroupOffseti; m_VerticeGroupOffset= data.m_VerticeGroupOffset; m_VerticeGroupId= data.m_VerticeGroupId; m_VerticeGroupCount= data.m_VerticeGroupCount; + m_UseVbo= data.m_UseVbo; } return *this; } @@ -86,13 +108,6 @@ GLC_WireData& GLC_WireData::operator=(const GLC_WireData& data) GLC_WireData::~GLC_WireData() { clear(); - - // Delete Main Vbo ID - if (0 != m_VboId) - { - glDeleteBuffers(1, &m_VboId); - m_VboId= 0; - } } ////////////////////////////////////////////////////////////////////// // Get Functions @@ -107,18 +122,19 @@ quint32 GLC_WireData::chunckID() GLfloatVector GLC_WireData::positionVector() const { - if (0 != m_VboId) + if (m_VerticeBuffer.isCreated()) { + Q_ASSERT((NULL != QGLContext::currentContext()) && QGLContext::currentContext()->isValid()); // VBO created get data from VBO const int sizeOfVbo= m_PositionSize; const GLsizeiptr dataSize= sizeOfVbo * sizeof(float); GLfloatVector positionVector(sizeOfVbo); - glBindBuffer(GL_ARRAY_BUFFER, m_VboId); - GLvoid* pVbo = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY); + const_cast(m_VerticeBuffer).bind(); + GLvoid* pVbo = const_cast(m_VerticeBuffer).map(QGLBuffer::ReadOnly); memcpy(positionVector.data(), pVbo, dataSize); - glUnmapBuffer(GL_ARRAY_BUFFER); - glBindBuffer(GL_ARRAY_BUFFER, 0); + const_cast(m_VerticeBuffer).unmap(); + const_cast(m_VerticeBuffer).release(); return positionVector; } else @@ -127,6 +143,51 @@ GLfloatVector GLC_WireData::positionVector() const } } +// Return the color Vector +GLfloatVector GLC_WireData::colorVector() const +{ + if (m_ColorBuffer.isCreated()) + { + // VBO created get data from VBO + const int sizeOfVbo= m_ColorSize; + const GLsizeiptr dataSize= sizeOfVbo * sizeof(GLfloat); + GLfloatVector normalVector(sizeOfVbo); + + const_cast(m_ColorBuffer).bind(); + GLvoid* pVbo = const_cast(m_ColorBuffer).map(QGLBuffer::ReadOnly); + memcpy(normalVector.data(), pVbo, dataSize); + const_cast(m_ColorBuffer).unmap(); + const_cast(m_ColorBuffer).release(); + return normalVector; + } + else + { + return m_Colors; + } +} + +QVector GLC_WireData::indexVector() const +{ + if (m_IndexBuffer.isCreated()) + { + // VBO created get data from VBO + const int sizeOfIbo= m_PositionSize / 3; + const GLsizeiptr dataSize= sizeOfIbo * sizeof(GLuint); + QVector indexVector(sizeOfIbo); + + const_cast(m_IndexBuffer).bind(); + GLvoid* pIbo = const_cast(m_IndexBuffer).map(QGLBuffer::ReadOnly); + memcpy(indexVector.data(), pIbo, dataSize); + const_cast(m_IndexBuffer).unmap(); + const_cast(m_IndexBuffer).release(); + return indexVector; + } + else + { + return m_IndexVector; + } +} + GLC_BoundingBox& GLC_WireData::boundingBox() { @@ -141,10 +202,26 @@ GLC_BoundingBox& GLC_WireData::boundingBox() else { const int max= m_Positions.size(); - for (int i= 0; i < max; i= i + 3) + if (max == 3) // Only One point { - GLC_Point3d point(m_Positions[i], m_Positions[i + 1], m_Positions[i + 2]); - m_pBoundingBox->combine(point); + const double delta= 1e-2; + GLC_Point3d lower(m_Positions[0] - delta, + m_Positions[1] - delta, + m_Positions[2] - delta); + GLC_Point3d upper(m_Positions[0] + delta, + m_Positions[1] + delta, + m_Positions[2] + delta); + m_pBoundingBox->combine(lower); + m_pBoundingBox->combine(upper); + + } + else + { + for (int i= 0; i < max; i= i + 3) + { + GLC_Point3d point(m_Positions[i], m_Positions[i + 1], m_Positions[i + 2]); + m_pBoundingBox->combine(point); + } } } @@ -166,12 +243,12 @@ GLC_uint GLC_WireData::addVerticeGroup(const GLfloatVector& floatVector) m_VerticeGrouprSizes.append(static_cast(floatVector.size() / 3)); - if (m_VerticeGroupOffset.isEmpty()) + if (m_VerticeGroupOffseti.isEmpty()) { - m_VerticeGroupOffset.append(0); + m_VerticeGroupOffseti.append(0); } - int offset= m_VerticeGroupOffset.last() + m_VerticeGrouprSizes.last(); - m_VerticeGroupOffset.append(offset); + int offset= m_VerticeGroupOffseti.last() + m_VerticeGrouprSizes.last(); + m_VerticeGroupOffseti.append(offset); // The Polyline id m_VerticeGroupId.append(m_NextPrimitiveLocalId); @@ -180,6 +257,7 @@ GLC_uint GLC_WireData::addVerticeGroup(const GLfloatVector& floatVector) void GLC_WireData::clear() { + m_VerticeBuffer.destroy(); m_NextPrimitiveLocalId= 1; m_Positions.clear(); m_PositionSize= 0; @@ -187,27 +265,54 @@ void GLC_WireData::clear() m_pBoundingBox= NULL; m_VerticeGrouprSizes.clear(); - m_VerticeGroupOffset.clear(); + m_VerticeGroupOffseti.clear(); m_VerticeGroupId.clear(); m_VerticeGroupCount= 0; } void GLC_WireData::copyVboToClientSide() { - if ((0 != m_VboId) && m_Positions.isEmpty()) + if (m_VerticeBuffer.isCreated() && m_Positions.isEmpty()) { m_Positions= positionVector(); + + if (m_ColorBuffer.isCreated() && m_Colors.isEmpty()) + { + m_Colors= colorVector(); + } + m_IndexVector= indexVector(); } + } void GLC_WireData::releaseVboClientSide(bool update) { - if ((0 != m_VboId) && !m_Positions.isEmpty()) + if (m_VerticeBuffer.isCreated() && !m_Positions.isEmpty()) { if (update) finishVbo(); } } +void GLC_WireData::setVboUsage(bool usage) +{ + m_UseVbo= usage; + if (!isEmpty()) + { + if (m_UseVbo && (m_PositionSize != 0) && (!m_Positions.isEmpty())&& (!m_VerticeBuffer.isCreated())) + { + finishVbo(); + } + else if (!m_UseVbo && m_VerticeBuffer.isCreated()) + { + m_Positions= positionVector(); + m_VerticeBuffer.destroy(); + m_Colors= colorVector(); + m_ColorBuffer.destroy(); + m_IndexVector= indexVector(); + m_IndexBuffer.destroy(); + } + } +} ////////////////////////////////////////////////////////////////////// // OpenGL Functions @@ -215,82 +320,212 @@ void GLC_WireData::releaseVboClientSide(bool update) void GLC_WireData::finishVbo() { - createVBOs(); - useVBO(true); + Q_ASSERT((NULL != QGLContext::currentContext()) && QGLContext::currentContext()->isValid()); + if (!m_VerticeBuffer.isCreated()) + { + m_VerticeBuffer.create(); + } + if ((m_Colors.size() > 0) && !m_ColorBuffer.isCreated()) + { + m_ColorBuffer.create(); + } + if (!m_IndexBuffer.isCreated()) + { + m_IndexBuffer.create(); + } fillVBOs(); - useVBO(false); m_PositionSize= m_Positions.size(); m_Positions.clear(); + + m_IndexVector.clear(); + + if (m_ColorBuffer.isCreated()) + { + m_ColorSize= m_Colors.size(); + m_Colors.clear(); + } } -void GLC_WireData::useVBO(bool use) +void GLC_WireData::useVBO(GLC_WireData::VboType type, bool use) { if (use) { - glBindBuffer(GL_ARRAY_BUFFER, m_VboId); } + + // Chose the right VBO + if (type == GLC_WireData::GLC_Vertex) + { + if (!m_VerticeBuffer.bind()) + { + GLC_Exception exception("GLC_WireData::useVBO Failed to bind vertex buffer"); + throw(exception); + } + } + else if (type == GLC_WireData::GLC_Color) + { + Q_ASSERT(m_ColorSize > 0); + if (!m_ColorBuffer.bind()) + { + GLC_Exception exception("GLC_WireData::useVBO Failed to bind color buffer"); + throw(exception); + } + } + else if ((type == GLC_WireData::GLC_Index) && m_IndexBuffer.isCreated()) + { + if (!m_IndexBuffer.bind()) + { + GLC_Exception exception("GLC_WireData::useVBO Failed to bind index buffer"); + throw(exception); + } + } + } else { - // Unbind VBO - glBindBuffer(GL_ARRAY_BUFFER, 0); + QGLBuffer::release(QGLBuffer::VertexBuffer); + QGLBuffer::release(QGLBuffer::IndexBuffer); } } void GLC_WireData::glDraw(const GLC_RenderProperties&, GLenum mode) { + Q_ASSERT((NULL != QGLContext::currentContext()) && QGLContext::currentContext()->isValid()); Q_ASSERT(!isEmpty()); - const bool vboIsUsed= GLC_State::vboUsed(); - if (vboIsUsed && ((m_PositionSize == 0) || (0 == m_VboId))) + const bool vboIsUsed= m_UseVbo && GLC_State::vboSupported(); + + if (vboIsUsed && ((m_PositionSize == 0) || !m_VerticeBuffer.isCreated())) { + finishOffset(); + buidIndex(); finishVbo(); } - else if (m_PositionSize == 0) + else if (!vboIsUsed && (m_PositionSize == 0)) { + finishOffset(); + buidIndex(); m_PositionSize= m_Positions.size(); + m_ColorSize= m_Colors.size(); } // Activate VBO or Vertex Array if (vboIsUsed) { - useVBO(true); + activateVboAndIbo(); glVertexPointer(3, GL_FLOAT, 0, 0); + glEnableClientState(GL_VERTEX_ARRAY); + if (m_ColorSize > 0) + { + glColorPointer(4, GL_FLOAT, 0, 0); + glEnableClientState(GL_COLOR_ARRAY); + } + + // Render polylines + for (int i= 0; i < m_VerticeGroupCount; ++i) + { + glDrawElements(mode, m_VerticeGrouprSizes.at(i), GL_UNSIGNED_INT, m_VerticeGroupOffset.at(i)); + } + + useVBO(GLC_WireData::GLC_Index, false); } else { glVertexPointer(3, GL_FLOAT, 0, m_Positions.data()); - } - glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); + if (m_ColorSize > 0) + { + glColorPointer(4, GL_FLOAT, 0, m_Colors.data()); + glEnableClientState(GL_COLOR_ARRAY); + } + // Render polylines + for (int i= 0; i < m_VerticeGroupCount; ++i) + { + glDrawElements(mode, m_VerticeGrouprSizes.at(i), GL_UNSIGNED_INT, &(m_IndexVector.data()[m_VerticeGroupOffseti.at(i)])); + } - // Render polylines - for (int i= 0; i < m_VerticeGroupCount; ++i) - { - glDrawArrays(mode, m_VerticeGroupOffset.at(i), m_VerticeGrouprSizes.at(i)); } - // Desactivate VBO or Vertex Array - if (vboIsUsed) + if (m_ColorSize > 0) { - useVBO(false); + glDisableClientState(GL_COLOR_ARRAY); } glDisableClientState(GL_VERTEX_ARRAY); -} -void GLC_WireData::createVBOs() -{ - // Create position VBO - if (0 == m_VboId) + if (vboIsUsed) { - glGenBuffers(1, &m_VboId); + QGLBuffer::release(QGLBuffer::IndexBuffer); + QGLBuffer::release(QGLBuffer::VertexBuffer); } } + void GLC_WireData::fillVBOs() { - const GLsizei dataNbr= static_cast(m_Positions.size()); - const GLsizeiptr dataSize= dataNbr * sizeof(GLfloat); - glBufferData(GL_ARRAY_BUFFER, dataSize, m_Positions.data(), GL_STATIC_DRAW); + { + Q_ASSERT(m_VerticeBuffer.isCreated()); + useVBO(GLC_WireData::GLC_Vertex, true); + const GLsizei dataNbr= static_cast(m_Positions.size()); + const GLsizeiptr dataSize= dataNbr * sizeof(GLfloat); + m_VerticeBuffer.allocate(m_Positions.data(), dataSize); + } + + { + Q_ASSERT(m_IndexBuffer.isCreated()); + useVBO(GLC_WireData::GLC_Index, true); + const GLsizei dataNbr= static_cast(m_IndexVector.size()); + const GLsizeiptr dataSize= dataNbr * sizeof(GLuint); + m_IndexBuffer.allocate(m_IndexVector.data(), dataSize); + } + + if (m_ColorBuffer.isCreated()) + { + useVBO(GLC_WireData::GLC_Color, true); + const GLsizei dataNbr= static_cast(m_Colors.size()); + const GLsizeiptr dataSize= dataNbr * sizeof(GLfloat); + m_ColorBuffer.allocate(m_Colors.data(), dataSize); + } +} + +void GLC_WireData::buidIndex() +{ + const int size= m_Positions.size(); + m_IndexVector.resize(size); + for (int i= 0; i < size; ++i) + { + m_IndexVector[i]= i; + } +} + +void GLC_WireData::activateVboAndIbo() +{ + // Activate Vertices VBO + useVBO(GLC_WireData::GLC_Vertex, true); + glVertexPointer(3, GL_FLOAT, 0, 0); + glEnableClientState(GL_VERTEX_ARRAY); + + // Activate Color VBO if needed + if (m_ColorSize > 0) + { + useVBO(GLC_WireData::GLC_Color, true); + glEnable(GL_COLOR_MATERIAL); + glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE); + glColorPointer(4, GL_FLOAT, 0, 0); + glEnableClientState(GL_COLOR_ARRAY); + } + + // Activate index Buffer object + useVBO(GLC_WireData::GLC_Index, true); +} + +void GLC_WireData::finishOffset() +{ + m_VerticeGroupOffseti.remove(m_VerticeGroupOffseti.size() - 1); + m_VerticeGroupOffset.clear(); + const int offsetSize= m_VerticeGroupOffseti.size(); + for (int i= 0; i < offsetSize; ++i) + { + m_VerticeGroupOffset.append(BUFFER_OFFSET(static_cast(m_VerticeGroupOffseti.at(i)) * sizeof(GLuint))); + } } QDataStream &operator<<(QDataStream &stream, const GLC_WireData &wireData) @@ -303,10 +538,14 @@ QDataStream &operator<<(QDataStream &stream, const GLC_WireData &wireData) stream << wireData.m_PositionSize; stream << wireData.m_VerticeGrouprSizes; - stream << wireData.m_VerticeGroupOffset; + stream << wireData.m_VerticeGroupOffseti; stream << wireData.m_VerticeGroupId; stream << wireData.m_VerticeGroupCount; + // New version Data + stream << wireData.colorVector(); + stream << wireData.m_ColorSize; + return stream; } @@ -314,7 +553,7 @@ QDataStream &operator>>(QDataStream &stream, GLC_WireData &wireData) { quint32 chunckId; stream >> chunckId; - Q_ASSERT(chunckId == GLC_WireData::m_ChunkId); + Q_ASSERT((chunckId == GLC_WireData::m_ChunkId) || chunckId == 0xA706); wireData.clear(); stream >> wireData.m_NextPrimitiveLocalId; @@ -322,9 +561,16 @@ QDataStream &operator>>(QDataStream &stream, GLC_WireData &wireData) stream >> wireData.m_PositionSize; stream >> wireData.m_VerticeGrouprSizes; - stream >> wireData.m_VerticeGroupOffset; + stream >> wireData.m_VerticeGroupOffseti; stream >> wireData.m_VerticeGroupId; stream >> wireData.m_VerticeGroupCount; + if (chunckId == GLC_WireData::m_ChunkId) + { + // New version Data + stream >> wireData.m_Colors; + stream >> wireData.m_ColorSize; + } + return stream; } diff --git a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_wiredata.h b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_wiredata.h index 438d0a534..b4b384c72 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_wiredata.h +++ b/ground/openpilotgcs/src/libs/glc_lib/geometry/glc_wiredata.h @@ -25,6 +25,8 @@ #define GLC_WIREDATA_H_ #include +#include + #include "../glc_global.h" #include "../glc_boundingbox.h" #include "../shading/glc_renderproperties.h" @@ -40,6 +42,14 @@ class GLC_LIB_EXPORT GLC_WireData friend GLC_LIB_EXPORT QDataStream &operator<<(QDataStream &, const GLC_WireData &); friend GLC_LIB_EXPORT QDataStream &operator>>(QDataStream &, GLC_WireData &); + //! Enum of VBO TYPE + enum VboType + { + GLC_Vertex= 30, + GLC_Color, + GLC_Index + }; + ////////////////////////////////////////////////////////////////////// /*! @name Constructor / Destructor */ //@{ @@ -69,6 +79,12 @@ public: //! Return this wire data Position Vector GLfloatVector positionVector() const; + //! Return the color Vector + GLfloatVector colorVector() const; + + //! Return the unique index vector + QVector indexVector() const; + //! Return true if this wire data is empty inline bool isEmpty() const {return ((m_PositionSize == 0) && m_Positions.isEmpty());} @@ -82,12 +98,15 @@ public: //! Return the vertice group offset from the given index inline GLuint verticeGroupOffset(int index) const - {return m_VerticeGroupOffset.at(index);} + {return m_VerticeGroupOffseti.at(index);} //! Return the vertice group size from the given index inline GLsizei verticeGroupSize(int index) const {return m_VerticeGrouprSizes.at(index);} + //! Return true if this wire data use indexed colors + inline bool useIndexdColors() const + {return (m_ColorSize > 0) || (m_Colors.size() > 0);} //@} ////////////////////////////////////////////////////////////////////// @@ -98,6 +117,10 @@ public: //! Add a Polyline to this wire and returns its id if id are managed GLC_uint addVerticeGroup(const GLfloatVector&); + //! Add Colors + inline void addColors(const GLfloatVector& colors) + {m_Colors+= colors;} + //! Clear the content of this wire Data and makes it empty void clear(); @@ -107,6 +130,9 @@ public: //! Release client VBO void releaseVboClientSide(bool update= false); + //! Set VBO usage + void setVboUsage(bool usage); + //@} ////////////////////////////////////////////////////////////////////// @@ -118,18 +144,25 @@ public: void finishVbo(); //! Set vbo usage of this wire data - void useVBO(bool usage); + void useVBO(GLC_WireData::VboType type, bool usage); //! Render this wire data using Opengl /*! The mode can be : GL_POINTS, GL_LINE_STRIP, GL_LINE_LOOP GL_LINES*/ void glDraw(const GLC_RenderProperties&, GLenum mode); private: - //! Create this wire data VBO id - void createVBOs(); //! Fill this wire data VBO from memmory void fillVBOs(); + + //! Built index + void buidIndex(); + + //! Activate VBO and IBO + void activateVboAndIbo(); + + //! Finish offset + void finishOffset(); //@} ////////////////////////////////////////////////////////////////////// @@ -137,7 +170,7 @@ private: ////////////////////////////////////////////////////////////////////// private: //! VBO ID - GLuint m_VboId; + QGLBuffer m_VerticeBuffer; //! The next primitive local id GLC_uint m_NextPrimitiveLocalId; @@ -145,9 +178,24 @@ private: //! Vertex Position Vector GLfloatVector m_Positions; + //! Color Buffer + QGLBuffer m_ColorBuffer; + + //! Color index + GLfloatVector m_Colors; + + //! The Index Buffer + QGLBuffer m_IndexBuffer; + + //! The Index Vector + QVector m_IndexVector; + //! The size of the VBO int m_PositionSize; + //! The size of Color VBO + int m_ColorSize; + //! Wire data bounding box GLC_BoundingBox* m_pBoundingBox; @@ -155,7 +203,10 @@ private: IndexSizes m_VerticeGrouprSizes; //! Vector of vertice group offset - OffsetVectori m_VerticeGroupOffset; + OffsetVectori m_VerticeGroupOffseti; + + //! VBO Vector of vertice group offset + OffsetVector m_VerticeGroupOffset; //! Vertice groups id QList m_VerticeGroupId; @@ -163,6 +214,9 @@ private: //! The number of vertice group int m_VerticeGroupCount; + //! VBO usage + bool m_UseVbo; + //! Class chunk id static quint32 m_ChunkId; }; diff --git a/ground/openpilotgcs/src/libs/glc_lib/glc_cachemanager.cpp b/ground/openpilotgcs/src/libs/glc_lib/glc_cachemanager.cpp index 4e33c40c2..a644c9be6 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/glc_cachemanager.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/glc_cachemanager.cpp @@ -19,6 +19,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *****************************************************************************/ +//! \file glc_cachemanager.cpp implementation of the GLC_CacheManager class. #include "glc_cachemanager.h" #include diff --git a/ground/openpilotgcs/src/libs/glc_lib/glc_context.cpp b/ground/openpilotgcs/src/libs/glc_lib/glc_context.cpp new file mode 100644 index 000000000..48c43b9fc --- /dev/null +++ b/ground/openpilotgcs/src/libs/glc_lib/glc_context.cpp @@ -0,0 +1,289 @@ +/**************************************************************************** + + This file is part of the GLC-lib library. + Copyright (C) 2005-2008 Laurent Ribon (laumaya@users.sourceforge.net) + http://glc-lib.sourceforge.net + + GLC-lib is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + GLC-lib 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with GLC-lib; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + *****************************************************************************/ +//! \file glc_context.cpp implementation of the GLC_Context class. + +#include "glc_context.h" +#include "glc_contextmanager.h" +#include "shading/glc_shader.h" + +#include "glc_state.h" + +GLC_Context* GLC_Context::m_pCurrentContext= NULL; + +GLC_Context::GLC_Context(const QGLFormat& format) +: QGLContext(format) +, m_CurrentMatrixMode() +, m_MatrixStackHash() +, m_ContextSharedData() +, m_UniformShaderData() +, m_LightingIsEnable() +{ + qDebug() << "GLC_Context::GLC_Context"; + GLC_ContextManager::instance()->addContext(this); + init(); +} + +GLC_Context::~GLC_Context() +{ + qDebug() << "GLC_Context::~GLC_Context()"; + GLC_ContextManager::instance()->remove(this); + QHash* >::iterator iStack= m_MatrixStackHash.begin(); + while (iStack != m_MatrixStackHash.end()) + { + delete iStack.value(); + ++iStack; + } +} + + +////////////////////////////////////////////////////////////////////// +// Get Functions +////////////////////////////////////////////////////////////////////// + +GLC_Context* GLC_Context::current() +{ + return m_pCurrentContext; +} + +////////////////////////////////////////////////////////////////////// +// OpenGL Functions +////////////////////////////////////////////////////////////////////// +void GLC_Context::glcMatrixMode(GLenum mode) +{ + Q_ASSERT(QGLContext::isValid()); + Q_ASSERT((mode == GL_MODELVIEW) || (mode == GL_PROJECTION)); + + m_CurrentMatrixMode= mode; +#ifdef GLC_OPENGL_ES_2 + +#else + glMatrixMode(m_CurrentMatrixMode); +#endif + +} + +void GLC_Context::glcLoadIdentity() +{ + Q_ASSERT(QGLContext::isValid()); + m_MatrixStackHash.value(m_CurrentMatrixMode)->top().setToIdentity(); + +#ifdef GLC_OPENGL_ES_2 + m_UniformShaderData.setModelViewProjectionMatrix(m_MatrixStackHash.value(GL_MODELVIEW)->top(), m_MatrixStackHash.value(GL_PROJECTION)->top()); +#else + if (GLC_Shader::hasActiveShader()) + { + m_UniformShaderData.setModelViewProjectionMatrix(m_MatrixStackHash.value(GL_MODELVIEW)->top(), m_MatrixStackHash.value(GL_PROJECTION)->top()); + } + glLoadIdentity(); +#endif + +} + +void GLC_Context::glcPushMatrix() +{ + Q_ASSERT(QGLContext::isValid()); + m_MatrixStackHash.value(m_CurrentMatrixMode)->push(m_MatrixStackHash.value(m_CurrentMatrixMode)->top()); + +#ifndef GLC_OPENGL_ES_2 + glPushMatrix(); +#endif + +} + +void GLC_Context::glcPopMatrix() +{ + Q_ASSERT(QGLContext::isValid()); + m_MatrixStackHash.value(m_CurrentMatrixMode)->pop(); + +#ifdef GLC_OPENGL_ES_2 + this->glcLoadMatrix(m_MatrixStackHash.value(m_CurrentMatrixMode)->top()); +#else + if (GLC_Shader::hasActiveShader()) + { + this->glcLoadMatrix(m_MatrixStackHash.value(m_CurrentMatrixMode)->top()); + } + glPopMatrix(); +#endif + +} + + +void GLC_Context::glcLoadMatrix(const GLC_Matrix4x4& matrix) +{ + m_MatrixStackHash.value(m_CurrentMatrixMode)->top()= matrix; + +#ifdef GLC_OPENGL_ES_2 + m_UniformShaderData.setModelViewProjectionMatrix(m_MatrixStackHash.value(GL_MODELVIEW)->top(), m_MatrixStackHash.value(GL_PROJECTION)->top()); +#else + if (GLC_Shader::hasActiveShader()) + { + m_UniformShaderData.setModelViewProjectionMatrix(m_MatrixStackHash.value(GL_MODELVIEW)->top(), m_MatrixStackHash.value(GL_PROJECTION)->top()); + } + ::glLoadMatrixd(matrix.getData()); +#endif + +} + +void GLC_Context::glcMultMatrix(const GLC_Matrix4x4& matrix) +{ + const GLC_Matrix4x4 current= m_MatrixStackHash.value(m_CurrentMatrixMode)->top(); + m_MatrixStackHash.value(m_CurrentMatrixMode)->top()= m_MatrixStackHash.value(m_CurrentMatrixMode)->top() * matrix; +#ifdef GLC_OPENGL_ES_2 + m_UniformShaderData.setModelViewProjectionMatrix(m_MatrixStackHash.value(GL_MODELVIEW)->top(), m_MatrixStackHash.value(GL_PROJECTION)->top()); +#else + if (GLC_Shader::hasActiveShader()) + { + m_UniformShaderData.setModelViewProjectionMatrix(m_MatrixStackHash.value(GL_MODELVIEW)->top(), m_MatrixStackHash.value(GL_PROJECTION)->top()); + } + ::glMultMatrixd(matrix.getData()); +#endif + +} + +void GLC_Context::glcScaled(double x, double y, double z) +{ + GLC_Matrix4x4 scale; + scale.setMatScaling(x, y, z); + glcMultMatrix(scale); +} + +void GLC_Context::glcOrtho(double left, double right, double bottom, double top, double nearVal, double farVal) +{ + GLC_Matrix4x4 orthoMatrix; + double* m= orthoMatrix.setData(); + + const double tx= - (right + left) / (right - left); + const double ty= - (top + bottom) / (top - bottom); + const double tz= - (farVal + nearVal) / (farVal - nearVal); + m[0]= 2.0 / (right - left); + m[5]= 2.0 / (top - bottom); + m[10]= -2.0 / (farVal - nearVal); + m[12]= tx; + m[13]= ty; + m[14]= tz; + + glcMultMatrix(orthoMatrix); +} + +void GLC_Context::glcFrustum(double left, double right, double bottom, double top, double nearVal, double farVal) +{ + GLC_Matrix4x4 perspMatrix; + double* m= perspMatrix.setData(); + + const double a= (right + left) / (right - left); + const double b= (top + bottom) / (top - bottom); + const double c= - (farVal + nearVal) / (farVal - nearVal); + const double d= - (2.0 * farVal * nearVal) / (farVal - nearVal); + + m[0]= (2.0 * nearVal) / (right - left); + m[5]= (2.0 * nearVal) / (top - bottom); + m[8]= a; + m[9]= b; + m[10]= c; + m[11]= -1.0; + m[14]= d; + m[15]= 0.0; + + glcMultMatrix(perspMatrix); +} + +void GLC_Context::glcEnableLighting(bool enable) +{ + if (enable != m_LightingIsEnable.top()) + { + m_LightingIsEnable.top()= enable; + +#ifdef GLC_OPENGL_ES_2 + + m_UniformShaderData.setLightingState(m_LightingIsEnable); +#else + if (GLC_Shader::hasActiveShader()) + { + m_UniformShaderData.setLightingState(m_LightingIsEnable.top()); + } + if (m_LightingIsEnable.top()) ::glEnable(GL_LIGHTING); + else ::glDisable(GL_LIGHTING); +#endif + + } +} + +////////////////////////////////////////////////////////////////////// +// Set Functions +////////////////////////////////////////////////////////////////////// + +void GLC_Context::makeCurrent() +{ + QGLContext::makeCurrent(); + if (!GLC_State::isValid()) + { + GLC_State::init(); + } + GLC_ContextManager::instance()->setCurrent(this); + m_pCurrentContext= this; +} + +void GLC_Context::doneCurrent() +{ + QGLContext::doneCurrent(); + GLC_ContextManager::instance()->setCurrent(NULL); + m_pCurrentContext= NULL; +} + +bool GLC_Context::chooseContext(const QGLContext* shareContext) +{ + qDebug() << "GLC_Context::chooseContext"; + const bool success= QGLContext::chooseContext(shareContext); + if (!success) + { + qDebug() << "enable to create context " << this; + } + else if (NULL != shareContext) + { + GLC_Context* pContext= const_cast(dynamic_cast(shareContext)); + Q_ASSERT(NULL != pContext); + m_ContextSharedData= pContext->m_ContextSharedData; + } + else + { + m_ContextSharedData= QSharedPointer(new GLC_ContextSharedData()); + } + + return success; +} + +////////////////////////////////////////////////////////////////////// +// Private services Functions +////////////////////////////////////////////////////////////////////// +void GLC_Context::init() +{ + QStack* pStack1= new QStack(); + pStack1->push(GLC_Matrix4x4()); + m_MatrixStackHash.insert(GL_MODELVIEW, pStack1); + + QStack* pStack2= new QStack(); + pStack2->push(GLC_Matrix4x4()); + m_MatrixStackHash.insert(GL_PROJECTION, pStack2); + + m_LightingIsEnable.push(false); +} + diff --git a/ground/openpilotgcs/src/libs/glc_lib/glc_context.h b/ground/openpilotgcs/src/libs/glc_lib/glc_context.h new file mode 100644 index 000000000..da3378db8 --- /dev/null +++ b/ground/openpilotgcs/src/libs/glc_lib/glc_context.h @@ -0,0 +1,196 @@ +/**************************************************************************** + + This file is part of the GLC-lib library. + Copyright (C) 2005-2008 Laurent Ribon (laumaya@users.sourceforge.net) + http://glc-lib.sourceforge.net + + GLC-lib is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + GLC-lib 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with GLC-lib; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + *****************************************************************************/ +//! \file glc_context.h interface for the GLC_Context class. + +#ifndef GLC_CONTEXT_H_ +#define GLC_CONTEXT_H_ + +#include +#include +#include +#include +#include + +#include "glc_config.h" +#include "maths/glc_matrix4x4.h" +#include "glc_contextshareddata.h" +#include "glc_uniformshaderdata.h" + +class GLC_ContextSharedData; + +// OpenGL ES define +#if defined(QT_OPENGL_ES_2) +#define GLC_OPENGL_ES_2 1 + +#define GL_MODELVIEW 0x1700 +#define GL_PROJECTION 0x1701 +#endif + + +//#define GLC_OPENGL_ES_2 1 + +////////////////////////////////////////////////////////////////////// +//! \class GLC_Context +/*! \brief GLC_Context : Encapsulates OpenGL rendering context*/ + +/*! The GLC_Context class store all GLC state associated to an OpenGL rendering context. + * This class is also used to simplified OpenGL and OpenGL-ES interoperability + */ +////////////////////////////////////////////////////////////////////// +class GLC_LIB_EXPORT GLC_Context : public QGLContext +{ +////////////////////////////////////////////////////////////////////// +/*! @name Constructor / Destructor */ +//@{ +////////////////////////////////////////////////////////////////////// + +public: + GLC_Context(const QGLFormat& format); + virtual ~GLC_Context(); + +//@} +////////////////////////////////////////////////////////////////////// +/*! \name Get Functions*/ +//@{ +////////////////////////////////////////////////////////////////////// +public: + //! Return the current context + static GLC_Context* current(); + + //! Return the model view matrix + inline GLC_Matrix4x4 modelViewMatrix() const + {Q_ASSERT(m_MatrixStackHash.contains(GL_MODELVIEW)); return m_MatrixStackHash.value(GL_MODELVIEW)->top();} + + //! Return the projection matrix + inline GLC_Matrix4x4 projectionMatrix() const + {Q_ASSERT(m_MatrixStackHash.contains(GL_PROJECTION)); return m_MatrixStackHash.value(GL_PROJECTION)->top();} + + //! Return lighting enable state + inline bool lightingIsEnable() const + {return m_LightingIsEnable.top();} +//@} +////////////////////////////////////////////////////////////////////// +/*! \name OpenGL Functions*/ +//@{ +////////////////////////////////////////////////////////////////////// +public: + //! Set the matrix mode + void glcMatrixMode(GLenum mode); + + //! Replace the current matrix with the identity + void glcLoadIdentity(); + + //! push and pop the current matrix stack + void glcPushMatrix(); + void glcPopMatrix(); + + //! Replace the current matrix with the specified matrix + void glcLoadMatrix(const GLC_Matrix4x4& matrix); + + //! Multiply the current matrix with the specified matrix + void glcMultMatrix(const GLC_Matrix4x4& matrix); + + //! Multiply the current matrix by a translation matrix + inline void glcTranslated(double x, double y, double z) + {glcMultMatrix(GLC_Matrix4x4(x, y, z));} + + //! Multiply the current matrix by a general scaling matrix + void glcScaled(double x, double y, double z); + + //! Multiply the current matrix with an orthographic matrix + void glcOrtho(double left, double right, double bottom, double top, double nearVal, double farVal); + + //! Multiply the current matrix by a perspective matrix + void glcFrustum(double left, double right, double bottom, double top, double nearVal, double farVal); + + //! Enable lighting + void glcEnableLighting(bool enable); + +//@} +////////////////////////////////////////////////////////////////////// +/*! \name Set Functions*/ +//@{ +////////////////////////////////////////////////////////////////////// +public: + + //! Make this context the current one + virtual void makeCurrent(); + + //! Make no context to be the current one + virtual void doneCurrent(); + + //! Update uniform variable + inline void updateUniformVariables() + {m_UniformShaderData.updateAll(this);} + +//@} +////////////////////////////////////////////////////////////////////// +/*! \name Private services Functions*/ +//@{ +////////////////////////////////////////////////////////////////////// +protected: +//@{ + + virtual bool chooseContext(const QGLContext* shareContext= 0); +//@} + +////////////////////////////////////////////////////////////////////// +/*! \name Private services Functions*/ +//@{ +////////////////////////////////////////////////////////////////////// +private: +//@{ + + //! Init this context state + void init(); +//@} + + +////////////////////////////////////////////////////////////////////// +// Private members +////////////////////////////////////////////////////////////////////// +private: + + //! The current matrix mode + GLenum m_CurrentMatrixMode; + + //! Mapping between matrixMode and matrix stack + QHash* > m_MatrixStackHash; + + //! The context shared data + QSharedPointer m_ContextSharedData; + + //! The uniform data of the current shader + GLC_UniformShaderData m_UniformShaderData; + + //! The current context + static GLC_Context* m_pCurrentContext; + + //! Enable lighting state + QStack m_LightingIsEnable; + + //! Lights enable state + QHash m_LightsEnableState; + +}; + +#endif /* GLC_CONTEXT_H_ */ diff --git a/ground/openpilotgcs/src/libs/glc_lib/glc_contextmanager.cpp b/ground/openpilotgcs/src/libs/glc_lib/glc_contextmanager.cpp new file mode 100644 index 000000000..d0fbcb7e6 --- /dev/null +++ b/ground/openpilotgcs/src/libs/glc_lib/glc_contextmanager.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** + + This file is part of the GLC-lib library. + Copyright (C) 2005-2008 Laurent Ribon (laumaya@users.sourceforge.net) + http://glc-lib.sourceforge.net + + GLC-lib is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + GLC-lib 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with GLC-lib; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + *****************************************************************************/ +//! \file glc_contextmanager.cpp implementation of the GLC_ContextManager class. + +#include + +#include "glc_contextmanager.h" +#include "glc_state.h" + +GLC_ContextManager* GLC_ContextManager::m_pContextManager= NULL; + +GLC_ContextManager::GLC_ContextManager() +: m_pCurrentContext(NULL) +, m_SetOfContext() +{ + + +} + +GLC_ContextManager::~GLC_ContextManager() +{ + +} + +////////////////////////////////////////////////////////////////////// +// Get Functions +////////////////////////////////////////////////////////////////////// +GLC_ContextManager* GLC_ContextManager::instance() +{ + if (NULL == m_pContextManager) + { + m_pContextManager= new GLC_ContextManager(); + } + + return m_pContextManager; +} + +GLC_Context* GLC_ContextManager::currentContext() const +{ + return m_pCurrentContext; +} + +////////////////////////////////////////////////////////////////////// +// Set Functions +////////////////////////////////////////////////////////////////////// +void GLC_ContextManager::addContext(GLC_Context* pContext) +{ + Q_ASSERT(!m_SetOfContext.contains(pContext)); + m_SetOfContext.insert(pContext); +} + +void GLC_ContextManager::remove(GLC_Context* pContext) +{ + Q_ASSERT(m_SetOfContext.contains(pContext)); + m_SetOfContext.remove(pContext); + if (m_pCurrentContext == pContext) + { + m_pCurrentContext= NULL; + } +} + +void GLC_ContextManager::setCurrent(GLC_Context* pContext) +{ + + Q_ASSERT((NULL == pContext) || m_SetOfContext.contains(pContext)); + m_pCurrentContext= pContext; +} + + diff --git a/ground/openpilotgcs/src/libs/glc_lib/glc_contextmanager.h b/ground/openpilotgcs/src/libs/glc_lib/glc_contextmanager.h new file mode 100644 index 000000000..7165dd13f --- /dev/null +++ b/ground/openpilotgcs/src/libs/glc_lib/glc_contextmanager.h @@ -0,0 +1,104 @@ +/**************************************************************************** + + This file is part of the GLC-lib library. + Copyright (C) 2005-2008 Laurent Ribon (laumaya@users.sourceforge.net) + http://glc-lib.sourceforge.net + + GLC-lib is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + GLC-lib 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with GLC-lib; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + *****************************************************************************/ +//! \file glc_contextmanager.h interface for the GLC_ContextManager class. + +#ifndef GLC_CONTEXTMANAGER_H_ +#define GLC_CONTEXTMANAGER_H_ + +#include + +#include "glc_config.h" + + +class GLC_Context; + +////////////////////////////////////////////////////////////////////// +//! \class GLC_ContextManager +/*! \brief GLC_ContextManager : Manager a set of GLC_Context*/ +////////////////////////////////////////////////////////////////////// +class GLC_LIB_EXPORT GLC_ContextManager +{ +private: + GLC_ContextManager(); +public: + virtual ~GLC_ContextManager(); + +//@} +////////////////////////////////////////////////////////////////////// +/*! \name Get Functions*/ +//@{ +////////////////////////////////////////////////////////////////////// +public: + //! Return the unique instance of context manager + static GLC_ContextManager* instance(); + + //! Return the current context + GLC_Context* currentContext() const; + + //! Return true if there is a current context + inline bool currentContextExists() const + {return (NULL != m_pCurrentContext);} + + //! Return true if this manager has context + inline bool hasContext() const + {return !m_SetOfContext.isEmpty();} + +//@} +////////////////////////////////////////////////////////////////////// +/*! \name Set Functions*/ +//@{ +////////////////////////////////////////////////////////////////////// +public: + //! Add the given context + void addContext(GLC_Context* pContext); + + //! Remove the given context + void remove(GLC_Context* pContext); + + //! Set the current the given context + void setCurrent(GLC_Context* pContext); + +////////////////////////////////////////////////////////////////////// +/*! \name Private services Functions*/ +//@{ +////////////////////////////////////////////////////////////////////// +private: +//@{ + +//@} + + +////////////////////////////////////////////////////////////////////// +// Private members +////////////////////////////////////////////////////////////////////// +private: + //! The unique instance of the context manager + static GLC_ContextManager* m_pContextManager; + + //! The current context + GLC_Context* m_pCurrentContext; + + //! The Set of context to manage + QSet m_SetOfContext; +}; + +#endif /* GLC_CONTEXTMANAGER_H_ */ diff --git a/ground/openpilotgcs/src/libs/glc_lib/glc_openglstate.cpp b/ground/openpilotgcs/src/libs/glc_lib/glc_contextshareddata.cpp similarity index 73% rename from ground/openpilotgcs/src/libs/glc_lib/glc_openglstate.cpp rename to ground/openpilotgcs/src/libs/glc_lib/glc_contextshareddata.cpp index 0331cd8a9..0c108036e 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/glc_openglstate.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/glc_contextshareddata.cpp @@ -19,16 +19,19 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *****************************************************************************/ -//! \file glc_openglstate.cpp implementation of the GLC_OpenGLState class. +//! \file glc_contextshareddata.cpp implementation of the GLC_ContextSharedData class. -#include "glc_openglstate.h" +#include -GLC_OpenGLState::GLC_OpenGLState() +#include "glc_contextshareddata.h" + +GLC_ContextSharedData::GLC_ContextSharedData() { + qDebug() << "GLC_ContextSharedData::GLC_ContextSharedData()"; } -GLC_OpenGLState::~GLC_OpenGLState() +GLC_ContextSharedData::~GLC_ContextSharedData() { - + qDebug() << "GLC_ContextSharedData::~GLC_ContextSharedData()"; } diff --git a/ground/openpilotgcs/src/libs/glc_lib/glc_openglstatemanager.h b/ground/openpilotgcs/src/libs/glc_lib/glc_contextshareddata.h similarity index 76% rename from ground/openpilotgcs/src/libs/glc_lib/glc_openglstatemanager.h rename to ground/openpilotgcs/src/libs/glc_lib/glc_contextshareddata.h index c9bc66272..8c6640384 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/glc_openglstatemanager.h +++ b/ground/openpilotgcs/src/libs/glc_lib/glc_contextshareddata.h @@ -19,16 +19,18 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *****************************************************************************/ -//! \file glc_openglstatemanager.h interface for the GLC_OpenGLStateManager class. +//! \file glc_contextshareddata.h interface for the GLC_ContextSharedData class. -#ifndef GLC_OPENGLSTATEMANAGER_H_ -#define GLC_OPENGLSTATEMANAGER_H_ +#ifndef GLC_CONTEXTSHAREDDATA_H_ +#define GLC_CONTEXTSHAREDDATA_H_ -class GLC_OpenGLStateManager +#include "glc_config.h" + +class GLC_LIB_EXPORT GLC_ContextSharedData { public: - GLC_OpenGLStateManager(); - virtual ~GLC_OpenGLStateManager(); + GLC_ContextSharedData(); + virtual ~GLC_ContextSharedData(); }; -#endif /* GLC_OPENGLSTATEMANAGER_H_ */ +#endif /* GLC_CONTEXTSHAREDDATA_H_ */ diff --git a/ground/openpilotgcs/src/libs/glc_lib/glc_ext.cpp b/ground/openpilotgcs/src/libs/glc_lib/glc_ext.cpp index 6d2742bdd..8275a085f 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/glc_ext.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/glc_ext.cpp @@ -26,25 +26,9 @@ #include #include #include +#include #if !defined(Q_OS_MAC) -// ARB_vertex_buffer_object -PFNGLBINDBUFFERARBPROC glBindBuffer = NULL; -PFNGLDELETEBUFFERSARBPROC glDeleteBuffers = NULL; -PFNGLGENBUFFERSARBPROC glGenBuffers = NULL; -PFNGLISBUFFERARBPROC glIsBuffer = NULL; -PFNGLBUFFERDATAARBPROC glBufferData = NULL; -PFNGLBUFFERSUBDATAARBPROC glBufferSubData = NULL; -PFNGLGETBUFFERSUBDATAARBPROC glGetBufferSubData = NULL; -PFNGLMAPBUFFERARBPROC glMapBuffer = NULL; -PFNGLUNMAPBUFFERARBPROC glUnmapBuffer = NULL; -PFNGLGETBUFFERPARAMETERIVARBPROC glGetBufferParameteriv = NULL; -PFNGLGETBUFFERPOINTERVARBPROC glGetBufferPointerv = NULL; -// glDrawRangElement -//PFNGLDRAWRANGEELEMENTSPROC glDrawRangeElements = NULL; - -// glMultiDrawElement -PFNGLMULTIDRAWELEMENTSPROC glMultiDrawElements = NULL; // GL_point_parameters Point Sprite PFNGLPOINTPARAMETERFARBPROC glPointParameterf = NULL; @@ -52,7 +36,6 @@ PFNGLPOINTPARAMETERFVARBPROC glPointParameterfv = NULL; #endif -//const QString glExtension(reinterpret_cast(glGetString(GL_EXTENSIONS))); // Return true if the extension is supported bool glc::extensionIsSupported(const QString& extension) @@ -64,28 +47,10 @@ bool glc::extensionIsSupported(const QString& extension) // Return true if VBO extension is succesfully loaded bool glc::loadVboExtension() { - bool result= true; -#if !defined(Q_OS_MAC) - const QGLContext* pContext= QGLContext::currentContext(); - glBindBuffer = (PFNGLBINDBUFFERARBPROC)pContext->getProcAddress(QLatin1String("glBindBuffer")); - glDeleteBuffers = (PFNGLDELETEBUFFERSARBPROC)pContext->getProcAddress(QLatin1String("glDeleteBuffers")); - glGenBuffers = (PFNGLGENBUFFERSARBPROC)pContext->getProcAddress(QLatin1String("glGenBuffers")); - glIsBuffer = (PFNGLISBUFFERARBPROC)pContext->getProcAddress(QLatin1String("glIsBuffer")); - glBufferData = (PFNGLBUFFERDATAARBPROC)pContext->getProcAddress(QLatin1String("glBufferData")); - glBufferSubData = (PFNGLBUFFERSUBDATAARBPROC)pContext->getProcAddress(QLatin1String("glBufferSubData")); - glGetBufferSubData = (PFNGLGETBUFFERSUBDATAARBPROC)pContext->getProcAddress(QLatin1String("glGetBufferSubData")); - glMapBuffer = (PFNGLMAPBUFFERARBPROC)pContext->getProcAddress(QLatin1String("glMapBuffer")); - glUnmapBuffer = (PFNGLUNMAPBUFFERARBPROC)pContext->getProcAddress(QLatin1String("glUnmapBuffer")); - glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVARBPROC)pContext->getProcAddress(QLatin1String("glGetBufferParameteriv")); - glGetBufferPointerv = (PFNGLGETBUFFERPOINTERVARBPROC)pContext->getProcAddress(QLatin1String("glGetBufferPointerv")); - //glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC)pContext->getProcAddress(QLatin1String("glDrawRangeElements")); - glMultiDrawElements = (PFNGLMULTIDRAWELEMENTSPROC)pContext->getProcAddress(QLatin1String("glMultiDrawElements")); - - result= glBindBuffer && glDeleteBuffers && glGenBuffers && glIsBuffer && glBufferData && glBufferSubData && - glGetBufferSubData && glMapBuffer && glUnmapBuffer && glGetBufferParameteriv && glGetBufferPointerv && glMultiDrawElements;// and glDrawRangeElements; -#endif + QGLBuffer buffer; + bool result= buffer.create(); + buffer.destroy(); return result; - } // Load GLSL extensions diff --git a/ground/openpilotgcs/src/libs/glc_lib/glc_ext.h b/ground/openpilotgcs/src/libs/glc_lib/glc_ext.h index 38c3d6154..d4ab75224 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/glc_ext.h +++ b/ground/openpilotgcs/src/libs/glc_lib/glc_ext.h @@ -30,37 +30,7 @@ // Buffer offset used by VBO #define BUFFER_OFFSET(i) ((char*)NULL + (i)) -#if defined(Q_OS_MAC) -#include "gl.h" -#include "glu.h" -#endif - -#if defined(Q_OS_WIN32) -#include "GL/gl.h" -#include "GL/glu.h" -#endif - -#if defined(Q_OS_LINUX) -#include "GL/glu.h" -#endif - #if !defined(Q_OS_MAC) -// ARB_vertex_buffer_object -extern PFNGLBINDBUFFERARBPROC glBindBuffer; -extern PFNGLDELETEBUFFERSARBPROC glDeleteBuffers; -extern PFNGLGENBUFFERSARBPROC glGenBuffers; -extern PFNGLISBUFFERARBPROC glIsBuffer; -extern PFNGLBUFFERDATAARBPROC glBufferData; -extern PFNGLBUFFERSUBDATAARBPROC glBufferSubData; -extern PFNGLGETBUFFERSUBDATAARBPROC glGetBufferSubData; -extern PFNGLMAPBUFFERARBPROC glMapBuffer; -extern PFNGLUNMAPBUFFERARBPROC glUnmapBuffer; -extern PFNGLGETBUFFERPARAMETERIVARBPROC glGetBufferParameteriv; -extern PFNGLGETBUFFERPOINTERVARBPROC glGetBufferPointerv; -// glDrawRangElement -//extern PFNGLDRAWRANGEELEMENTSPROC glDrawRangeElements; -// glMultiDrawElement -extern PFNGLMULTIDRAWELEMENTSPROC glMultiDrawElements; // GL_point_parameters Point Sprite extern PFNGLPOINTPARAMETERFARBPROC glPointParameterf; diff --git a/ground/openpilotgcs/src/libs/glc_lib/glc_factory.cpp b/ground/openpilotgcs/src/libs/glc_lib/glc_factory.cpp index 73e335912..8ff879d91 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/glc_factory.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/glc_factory.cpp @@ -37,6 +37,7 @@ #include "viewport/glc_reptrackballmover.h" #include "viewport/glc_flymover.h" #include "viewport/glc_repflymover.h" +#include "viewport/glc_tsrmover.h" #include "maths/glc_line3d.h" #include "maths/glc_geomtools.h" @@ -44,7 +45,6 @@ // init static member GLC_Factory* GLC_Factory::m_pFactory= NULL; -QGLContext* GLC_Factory::m_pQGLContext= NULL; QList GLC_Factory::m_WorldReaderPluginList; QSet GLC_Factory::m_SupportedExtensionSet; @@ -52,15 +52,11 @@ QSet GLC_Factory::m_SupportedExtensionSet; // static method ////////////////////////////////////////////////////////////////////// // Return the unique instance of the factory -GLC_Factory* GLC_Factory::instance(const QGLContext *pContext) +GLC_Factory* GLC_Factory::instance() { if(m_pFactory == NULL) { - m_pFactory= new GLC_Factory(pContext); - } - else if ((NULL != pContext) && (m_pQGLContext != pContext)) - { - m_pQGLContext= const_cast(pContext); + m_pFactory= new GLC_Factory(); } return m_pFactory; } @@ -70,9 +66,8 @@ GLC_Factory* GLC_Factory::instance(const QGLContext *pContext) ////////////////////////////////////////////////////////////////////// // Protected constructor -GLC_Factory::GLC_Factory(const QGLContext *pContext) +GLC_Factory::GLC_Factory() { - m_pQGLContext= (const_cast(pContext)); loadPlugins(); } @@ -231,7 +226,7 @@ GLC_World GLC_Factory::createWorldStructureFrom3dxml(QFile &file, bool GetExtRef if (QFileInfo(file).suffix().toLower() == "3dxml") { - GLC_3dxmlToWorld d3dxmlToWorld(m_pQGLContext); + GLC_3dxmlToWorld d3dxmlToWorld; connect(&d3dxmlToWorld, SIGNAL(currentQuantum(int)), this, SIGNAL(currentQuantum(int))); pWorld= d3dxmlToWorld.createWorldFrom3dxml(file, true, GetExtRefName); } @@ -253,9 +248,9 @@ GLC_3DRep GLC_Factory::create3DRepFromFile(const QString& fileName) const { GLC_3DRep rep; - if ((QFileInfo(fileName).suffix().toLower() == "3dxml") || (QFileInfo(fileName).suffix().toLower() == "3drep")) + if ((QFileInfo(fileName).suffix().toLower() == "3dxml") || (QFileInfo(fileName).suffix().toLower() == "3drep") || (QFileInfo(fileName).suffix().toLower() == "xml")) { - GLC_3dxmlToWorld d3dxmlToWorld(m_pQGLContext); + GLC_3dxmlToWorld d3dxmlToWorld; connect(&d3dxmlToWorld, SIGNAL(currentQuantum(int)), this, SIGNAL(currentQuantum(int))); rep= d3dxmlToWorld.create3DrepFrom3dxmlRep(fileName); } @@ -266,7 +261,7 @@ GLC_3DRep GLC_Factory::create3DRepFromFile(const QString& fileName) const GLC_FileLoader* GLC_Factory::createFileLoader() const { - return new GLC_FileLoader(m_pQGLContext); + return new GLC_FileLoader; } GLC_Material* GLC_Factory::createMaterial() const @@ -303,12 +298,12 @@ GLC_Material* GLC_Factory::createMaterial(const QImage &image) const GLC_Texture* GLC_Factory::createTexture(const QString &textureFullFileName) const { - return new GLC_Texture(m_pQGLContext, textureFullFileName); + return new GLC_Texture(textureFullFileName); } GLC_Texture* GLC_Factory::createTexture(const QImage & image, const QString& imageFileName) const { - return new GLC_Texture(m_pQGLContext, image, imageFileName); + return new GLC_Texture(image, imageFileName); } GLC_MoverController GLC_Factory::createDefaultMoverController(const QColor& color, GLC_Viewport* pViewport) @@ -371,6 +366,7 @@ GLC_MoverController GLC_Factory::createDefaultMoverController(const QColor& colo pMover= new GLC_TurnTableMover(pViewport); // Add the Turn Table Mover to the controller defaultController.addMover(pMover, GLC_MoverController::TurnTable); + ////////////////////////////////////////////////////////////////////// // Fly Mover ////////////////////////////////////////////////////////////////////// @@ -383,6 +379,14 @@ GLC_MoverController GLC_Factory::createDefaultMoverController(const QColor& colo // Add the fly mover to the controller defaultController.addMover(pMover, GLC_MoverController::Fly); + ////////////////////////////////////////////////////////////////////// + // Translation, rotation and scaling Mover + ////////////////////////////////////////////////////////////////////// + // Create the Turn Table Mover + pMover= new GLC_TsrMover(pViewport); + // Add the Turn Table Mover to the controller + defaultController.addMover(pMover, GLC_MoverController::TSR); + return defaultController; } diff --git a/ground/openpilotgcs/src/libs/glc_lib/glc_factory.h b/ground/openpilotgcs/src/libs/glc_lib/glc_factory.h index d0436fe46..89ebd39eb 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/glc_factory.h +++ b/ground/openpilotgcs/src/libs/glc_lib/glc_factory.h @@ -70,11 +70,11 @@ class GLC_LIB_EXPORT GLC_Factory : public QObject public: //! Get unique instance of the factory - static GLC_Factory* instance(const QGLContext * pContext= NULL); + static GLC_Factory* instance(); protected: //! Constructor - GLC_Factory(const QGLContext *); + GLC_Factory(); public: //! Destructor ~GLC_Factory(); @@ -84,9 +84,6 @@ public: //@{ ////////////////////////////////////////////////////////////////////// public: - //! Return the current factory context - inline QGLContext* context() const - {return m_pQGLContext;} //! Create a GLC_Point GLC_3DRep createPoint(const GLC_Point3d &coord) const; @@ -201,9 +198,6 @@ private: //! The unique instance of the factory static GLC_Factory* m_pFactory; - //! The QGLContext attached to the factory (rendering context) - static QGLContext* m_pQGLContext; - //! The list off worldReader plugins static QList m_WorldReaderPluginList; diff --git a/ground/openpilotgcs/src/libs/glc_lib/glc_global.h b/ground/openpilotgcs/src/libs/glc_lib/glc_global.h index 23cdc158b..ca108b63e 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/glc_global.h +++ b/ground/openpilotgcs/src/libs/glc_lib/glc_global.h @@ -120,7 +120,7 @@ namespace glc GLC_LIB_EXPORT QString archiveEntryFileName(const QString& archiveString); // GLC_Lib version - const QString version("2.1.0"); + const QString version("2.2.0"); const QString description("GLC_lib is a Open Source C++ class library that enables the quick creation of an OpenGL application based on QT4."); }; diff --git a/ground/openpilotgcs/src/libs/glc_lib/glc_lib.pro b/ground/openpilotgcs/src/libs/glc_lib/glc_lib.pro index c7952b1e7..6966f94b5 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/glc_lib.pro +++ b/ground/openpilotgcs/src/libs/glc_lib/glc_lib.pro @@ -9,11 +9,10 @@ QT += opengl \ core -#CONFIG += exceptions \ -# release \ -# warn_on +CONFIG += exceptions \ + warn_on #TARGET = GLC_lib -#VERSION = 2.1.0 +VERSION = 2.2.0 DEFINES += CREATE_GLC_LIB_DLL DEFINES += LIB3DS_EXPORTS @@ -28,6 +27,8 @@ DEPENDPATH += . INCLUDEPATH += . INCLUDEPATH += ./3rdparty/zlib +RESOURCES += glc_lib.qrc + # Input HEADERS_QUAZIP += 3rdparty/quazip/crypt.h \ 3rdparty/quazip/ioapi.h \ @@ -145,8 +146,9 @@ HEADERS_GLC_VIEWPORT += viewport/glc_camera.h \ viewport/glc_turntablemover.h \ viewport/glc_frustum.h \ viewport/glc_flymover.h \ - viewport/glc_repflymover.h - + viewport/glc_repflymover.h \ + viewport/glc_userinput.h \ + viewport/glc_tsrmover.h HEADERS_GLC += glc_global.h \ glc_object.h \ @@ -163,7 +165,10 @@ HEADERS_GLC += glc_global.h \ glc_log.h \ glc_errorlog.h \ glc_tracelog.h \ - glc_openglstate.h + glc_context.h \ + glc_contextmanager.h \ + glc_contextshareddata.h \ + glc_uniformshaderdata.h HEADERS_GLC_3DWIDGET += 3DWidget/glc_3dwidget.h \ 3DWidget/glc_cuttingplane.h \ @@ -174,10 +179,11 @@ HEADERS_GLC_3DWIDGET += 3DWidget/glc_3dwidget.h \ 3DWidget/glc_rotationmanipulator.h \ 3DWidget/glc_axis.h +HEADERS_GLC_GLU += glu/glc_glu.h HEADERS += $${HEADERS_QUAZIP} $${HEADERS_LIB3DS} $${HEADERS_GLC_MATHS} $${HEADERS_GLC_IO} HEADERS += $${HEADERS_GLC} $${HEADERS_GLEXT} $${HEADERS_GLC_SCENEGRAPH} $${HEADERS_GLC_GEOMETRY} -HEADERS += $${HEADERS_GLC_SHADING} $${HEADERS_GLC_VIEWPORT} $${HEADERS_GLC_3DWIDGET} +HEADERS += $${HEADERS_GLC_SHADING} $${HEADERS_GLC_VIEWPORT} $${HEADERS_GLC_3DWIDGET} $${HEADERS_GLC_GLU} SOURCES += 3rdparty/zlib/adler32.c \ 3rdparty/zlib/compress.c \ @@ -294,7 +300,9 @@ SOURCES += viewport/glc_camera.cpp \ viewport/glc_turntablemover.cpp \ viewport/glc_frustum.cpp \ viewport/glc_flymover.cpp \ - viewport/glc_repflymover.cpp + viewport/glc_repflymover.cpp \ + viewport/glc_userinput.cpp \ + viewport/glc_tsrmover.cpp SOURCES += glc_global.cpp \ glc_object.cpp \ @@ -310,7 +318,10 @@ SOURCES += glc_global.cpp \ glc_log.cpp \ glc_errorlog.cpp \ glc_tracelog.cpp \ - glc_openglstate.cpp + glc_context.cpp \ + glc_contextmanager.cpp \ + glc_contextshareddata.cpp \ + glc_uniformshaderdata.cpp SOURCES += 3DWidget/glc_3dwidget.cpp \ 3DWidget/glc_cuttingplane.cpp \ @@ -321,7 +332,8 @@ SOURCES += 3DWidget/glc_3dwidget.cpp \ 3DWidget/glc_rotationmanipulator.cpp \ 3DWidget/glc_axis.cpp - +SOURCES += glu/glc_project.cpp + # Windows compilation configuration win32:CONFIG *= dll @@ -413,12 +425,17 @@ HEADERS_INST = include/GLC_BoundingBox \ include/GLC_ErrorLog \ include/GLC_TraceLog \ include/glcXmlUtil \ - include/GLC_OpenGLState \ + include/GLC_RenderState \ include/GLC_FileLoader \ include/GLC_WorldReaderPlugin \ include/GLC_WorldReaderHandler \ include/GLC_PointCloud \ - include/GLC_SelectionSet + include/GLC_SelectionSet \ + include/GLC_UserInput \ + include/GLC_TsrMover \ + include/GLC_Glu \ + include/GLC_Context \ + include/GLC_ContextManager # Linux and macx install configuration @@ -439,6 +456,7 @@ unix { include_glc_shading.path = $${INCLUDE_DIR}/GLC_lib/shading include_glc_viewport.path = $${INCLUDE_DIR}/GLC_lib/viewport include_glc_3dwidget.path = $${INCLUDE_DIR}/GLC_lib/3DWidget + include_glc_glu.path = $${INCLUDE_DIR}/GLC_lib/glu } # Windows Install configuration @@ -457,6 +475,7 @@ win32 { include_glc_shading.path = $${INCLUDE_DIR}/shading include_glc_viewport.path = $${INCLUDE_DIR}/viewport include_glc_3dwidget.path = $${INCLUDE_DIR}/3DWidget + include_glc_glu.path = $${INCLUDE_DIR}/glu } include.files = $${HEADERS_GLC} $${HEADERS_INST} @@ -470,6 +489,7 @@ include_glc_geometry.files= $${HEADERS_GLC_GEOMETRY} include_glc_shading.files = $${HEADERS_GLC_SHADING} include_glc_viewport.files = $${HEADERS_GLC_VIEWPORT} include_glc_3dwidget.files = $${HEADERS_GLC_3DWIDGET} +include_glc_glu.files = $${HEADERS_GLC_GLU} # install library target.path = $${LIB_DIR} @@ -477,8 +497,15 @@ target.path = $${LIB_DIR} # "make install" configuration options INSTALLS += include_lib3ds include_glext include_quazip include_glc_maths include_glc_io INSTALLS += include_glc_scengraph include_glc_geometry include_glc_shading include_glc_viewport -INSTALLS += include_glc_3dwidget +INSTALLS += include_glc_3dwidget include_glc_glu INSTALLS += target INSTALLS +=include +OTHER_FILES += \ + qtc_packaging/debian_harmattan/rules \ + qtc_packaging/debian_harmattan/README \ + qtc_packaging/debian_harmattan/copyright \ + qtc_packaging/debian_harmattan/control \ + qtc_packaging/debian_harmattan/compat \ + qtc_packaging/debian_harmattan/changelog diff --git a/ground/openpilotgcs/src/libs/glc_lib/glc_lib.qrc b/ground/openpilotgcs/src/libs/glc_lib/glc_lib.qrc new file mode 100644 index 000000000..d74f34bc9 --- /dev/null +++ b/ground/openpilotgcs/src/libs/glc_lib/glc_lib.qrc @@ -0,0 +1,6 @@ + + + shading/shaders/default.frag + shading/shaders/default.vert + + diff --git a/ground/openpilotgcs/src/libs/glc_lib/glc_state.cpp b/ground/openpilotgcs/src/libs/glc_lib/glc_state.cpp index 755436ffd..6f03b3609 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/glc_state.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/glc_state.cpp @@ -48,6 +48,7 @@ GLC_CacheManager GLC_State::m_CacheManager; bool GLC_State::m_IsSpacePartitionningActivated= false; bool GLC_State::m_IsFrustumCullingActivated= false; +bool GLC_State::m_IsValid= false; GLC_State::~GLC_State() { @@ -145,13 +146,24 @@ bool GLC_State::isFrustumCullingActivated() void GLC_State::init() { - setVboSupport(); - setGlslSupport(); - setPointSpriteSupport(); - setFrameBufferSupport(); - m_Version= (char *) glGetString(GL_VERSION); - m_Vendor= (char *) glGetString(GL_VENDOR); - m_Renderer= (char *) glGetString(GL_RENDERER); + if (!m_IsValid) + { + Q_ASSERT((NULL != QGLContext::currentContext()) && QGLContext::currentContext()->isValid()); + setVboSupport(); + setGlslSupport(); + setPointSpriteSupport(); + setFrameBufferSupport(); + m_Version= (char *) glGetString(GL_VERSION); + m_Vendor= (char *) glGetString(GL_VENDOR); + m_Renderer= (char *) glGetString(GL_RENDERER); + + m_IsValid= true; + } +} + +bool GLC_State::isValid() +{ + return m_IsValid; } void GLC_State::setVboSupport() diff --git a/ground/openpilotgcs/src/libs/glc_lib/glc_state.h b/ground/openpilotgcs/src/libs/glc_lib/glc_state.h index 187ba389c..c6b52d7e7 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/glc_state.h +++ b/ground/openpilotgcs/src/libs/glc_lib/glc_state.h @@ -103,6 +103,9 @@ public: //! Return true if frustum culling is activated static bool isFrustumCullingActivated(); + + //! Return true valid + static bool isValid(); //@} ////////////////////////////////////////////////////////////////////// @@ -209,6 +212,9 @@ private: //! Frame buffer supported static bool m_IsFrameBufferSupported; + //! State valid flag + static bool m_IsValid; + }; #endif /*GLC_STATE_H_*/ diff --git a/ground/openpilotgcs/src/libs/glc_lib/glc_uniformshaderdata.cpp b/ground/openpilotgcs/src/libs/glc_lib/glc_uniformshaderdata.cpp new file mode 100644 index 000000000..f994998de --- /dev/null +++ b/ground/openpilotgcs/src/libs/glc_lib/glc_uniformshaderdata.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** + + This file is part of the GLC-lib library. + Copyright (C) 2005-2008 Laurent Ribon (laumaya@users.sourceforge.net) + http://glc-lib.sourceforge.net + + GLC-lib is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + GLC-lib 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with GLC-lib; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + *****************************************************************************/ +//! \file glc_uniformshaderdata.cpp implementation of the GLC_UniformShaderData class. + +#include + +#include "shading/glc_shader.h" +#include "glc_context.h" +#include "glc_uniformshaderdata.h" + + +GLC_UniformShaderData::GLC_UniformShaderData() +{ + + +} + +GLC_UniformShaderData::~GLC_UniformShaderData() +{ + +} + +////////////////////////////////////////////////////////////////////// +// Set Functions +////////////////////////////////////////////////////////////////////// +void GLC_UniformShaderData::setLightValues(const GLC_Light& light) +{ + +} + +void GLC_UniformShaderData::setLightingState(bool enable) +{ + GLC_Shader* pCurrentShader= GLC_Shader::currentShaderHandle(); + pCurrentShader->programShaderHandle()->setUniformValue(pCurrentShader->enableLightingId(), enable); +} + +void GLC_UniformShaderData::setModelViewProjectionMatrix(const GLC_Matrix4x4& modelView, const GLC_Matrix4x4& projection) +{ + // Set model view matrix + const double* pMvmatrixData= modelView.getData(); + GLfloat mvFloatMatrix[4][4]; + GLfloat* pData= &(mvFloatMatrix[0][0]); + for (int i= 0; i < 16; ++i) + { + pData[i]= static_cast(pMvmatrixData[i]); + } + + // Set model view projection matrix + GLC_Matrix4x4 modelViewProjectionMatrix= projection * modelView; + const double* pMvpmatrixData= modelViewProjectionMatrix.getData(); + GLfloat mvpFloatMatrix[4][4]; + pData= &(mvpFloatMatrix[0][0]); + for (int i= 0; i < 16; ++i) + { + pData[i]= static_cast(pMvpmatrixData[i]); + } + + // Set the transpose of inv model view matrix (For normal computation) + GLC_Matrix4x4 invTransposeModelView= modelView.inverted(); + invTransposeModelView.transpose(); + GLfloat invTmdv[3][3]; + { + const double* data= invTransposeModelView.getData(); + + invTmdv[0][0]= static_cast(data[0]); invTmdv[1][0]= static_cast(data[4]); invTmdv[2][0]= static_cast(data[8]); + invTmdv[0][1]= static_cast(data[1]); invTmdv[1][1]= static_cast(data[5]); invTmdv[2][1]= static_cast(data[9]); + invTmdv[0][2]= static_cast(data[2]); invTmdv[1][2]= static_cast(data[6]); invTmdv[2][2]= static_cast(data[10]); + } + + Q_ASSERT(GLC_Shader::hasActiveShader()); + + GLC_Shader* pCurrentShader= GLC_Shader::currentShaderHandle(); + pCurrentShader->programShaderHandle()->setUniformValue(pCurrentShader->modelViewLocationId(), mvFloatMatrix); + pCurrentShader->programShaderHandle()->setUniformValue(pCurrentShader->mvpLocationId(), mvpFloatMatrix); + pCurrentShader->programShaderHandle()->setUniformValue(pCurrentShader->invModelViewLocationId(), invTmdv); +} + +void GLC_UniformShaderData::updateAll(const GLC_Context* pContext) +{ + setModelViewProjectionMatrix(pContext->modelViewMatrix(), pContext->projectionMatrix()); + setLightingState(pContext->lightingIsEnable()); +} diff --git a/ground/openpilotgcs/src/libs/glc_lib/glc_uniformshaderdata.h b/ground/openpilotgcs/src/libs/glc_lib/glc_uniformshaderdata.h new file mode 100644 index 000000000..c30e205b6 --- /dev/null +++ b/ground/openpilotgcs/src/libs/glc_lib/glc_uniformshaderdata.h @@ -0,0 +1,68 @@ +/**************************************************************************** + + This file is part of the GLC-lib library. + Copyright (C) 2005-2008 Laurent Ribon (laumaya@users.sourceforge.net) + http://glc-lib.sourceforge.net + + GLC-lib is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + GLC-lib 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with GLC-lib; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + *****************************************************************************/ +//! \file glc_uniformshaderdata.h interface for the GLC_UniformShaderData class. + +#ifndef GLC_UNIFORMSHADERDATA_H_ +#define GLC_UNIFORMSHADERDATA_H_ + +#include + +#include "maths/glc_matrix4x4.h" +#include "shading/glc_light.h" + +#include "glc_config.h" + +class GLC_Context; + +class GLC_LIB_EXPORT GLC_UniformShaderData +{ +public: + GLC_UniformShaderData(); + virtual ~GLC_UniformShaderData(); + +////////////////////////////////////////////////////////////////////// +/*! \name Set Functions*/ +//@{ +////////////////////////////////////////////////////////////////////// +public: + //! Set Light values from the given light + void setLightValues(const GLC_Light& light); + + //! Set lighting enbale state + void setLightingState(bool enable); + + //! Set the model view matrix + void setModelViewProjectionMatrix(const GLC_Matrix4x4& modelView, const GLC_Matrix4x4& projection); + + //! Update all uniform variables + void updateAll(const GLC_Context* pContext); + +//@} + +////////////////////////////////////////////////////////////////////// +// private members +////////////////////////////////////////////////////////////////////// +private: + +}; + +#endif /* GLC_UNIFORMSHADERDATA_H_ */ diff --git a/ground/openpilotgcs/src/libs/glc_lib/glu/glc_glu.h b/ground/openpilotgcs/src/libs/glc_lib/glu/glc_glu.h new file mode 100644 index 000000000..218f0742b --- /dev/null +++ b/ground/openpilotgcs/src/libs/glc_lib/glu/glc_glu.h @@ -0,0 +1,77 @@ +/**************************************************************************** + + This file is part of the GLC-lib library. + Copyright (C) 2005-2008 Laurent Ribon (laumaya@users.sourceforge.net) + http://glc-lib.sourceforge.net + + GLC-lib is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + GLC-lib 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with GLC-lib; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*****************************************************************************/ + +//! \file glc_glu.h declaration of glu functions + + + +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ + + +#ifndef GLC_GLU_H_ +#define GLC_GLU_H_ + +#include + +#include "../glc_config.h" + +namespace glc +{ + GLC_LIB_EXPORT void gluLookAt (GLdouble eyeX, GLdouble eyeY, GLdouble eyeZ, GLdouble centerX, GLdouble centerY, GLdouble centerZ, GLdouble upX, GLdouble upY, GLdouble upZ); + GLC_LIB_EXPORT void gluOrtho2D (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top); + GLC_LIB_EXPORT void gluPerspective (GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar); + GLC_LIB_EXPORT void gluPickMatrix (GLdouble x, GLdouble y, GLdouble delX, GLdouble delY, GLint *viewport); + GLC_LIB_EXPORT GLint gluProject (GLdouble objX, GLdouble objY, GLdouble objZ, const GLdouble *model, const GLdouble *proj, const GLint *view, GLdouble* winX, GLdouble* winY, GLdouble* winZ); + GLC_LIB_EXPORT GLint gluUnProject (GLdouble winX, GLdouble winY, GLdouble winZ, const GLdouble *model, const GLdouble *proj, const GLint *view, GLdouble* objX, GLdouble* objY, GLdouble* objZ); + GLC_LIB_EXPORT GLint gluUnProject4 (GLdouble winX, GLdouble winY, GLdouble winZ, GLdouble clipW, const GLdouble *model, const GLdouble *proj, const GLint *view, GLdouble nearVal, GLdouble farVal, GLdouble* objX, GLdouble* objY, GLdouble* objZ, GLdouble* objW); +}; + + +#endif /*GLC_GLU_H_*/ diff --git a/ground/openpilotgcs/src/libs/glc_lib/glu/glc_project.cpp b/ground/openpilotgcs/src/libs/glc_lib/glu/glc_project.cpp new file mode 100644 index 000000000..cf76ea12b --- /dev/null +++ b/ground/openpilotgcs/src/libs/glc_lib/glu/glc_project.cpp @@ -0,0 +1,379 @@ +/**************************************************************************** + + This file is part of the GLC-lib library. + Copyright (C) 2005-2008 Laurent Ribon (laumaya@users.sourceforge.net) + http://glc-lib.sourceforge.net + + GLC-lib is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + GLC-lib 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with GLC-lib; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*****************************************************************************/ + +//! \file glc_project implementation of glu project function + + +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ + +#include "../glc_context.h" +#include "glc_glu.h" + +/* +** Make m an identity matrix +*/ +static void __gluMakeIdentityd(GLdouble m[16]) +{ + m[0+4*0] = 1; m[0+4*1] = 0; m[0+4*2] = 0; m[0+4*3] = 0; + m[1+4*0] = 0; m[1+4*1] = 1; m[1+4*2] = 0; m[1+4*3] = 0; + m[2+4*0] = 0; m[2+4*1] = 0; m[2+4*2] = 1; m[2+4*3] = 0; + m[3+4*0] = 0; m[3+4*1] = 0; m[3+4*2] = 0; m[3+4*3] = 1; +} + +static void __gluMakeIdentityf(GLfloat m[16]) +{ + m[0+4*0] = 1; m[0+4*1] = 0; m[0+4*2] = 0; m[0+4*3] = 0; + m[1+4*0] = 0; m[1+4*1] = 1; m[1+4*2] = 0; m[1+4*3] = 0; + m[2+4*0] = 0; m[2+4*1] = 0; m[2+4*2] = 1; m[2+4*3] = 0; + m[3+4*0] = 0; m[3+4*1] = 0; m[3+4*2] = 0; m[3+4*3] = 1; +} + +void glc::gluOrtho2D(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top) +{ + GLC_Context::current()->glcOrtho(left, right, bottom, top, -1, 1); +} + +#define __glPi 3.14159265358979323846 + +void glc::gluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar) +{ + GLdouble m[4][4]; + double sine, cotangent, deltaZ; + double radians = fovy / 2 * __glPi / 180; + + deltaZ = zFar - zNear; + sine = sin(radians); + if ((deltaZ == 0) || (sine == 0) || (aspect == 0)) { + return; + } + cotangent = cos(radians) / sine; + + __gluMakeIdentityd(&m[0][0]); + m[0][0] = cotangent / aspect; + m[1][1] = cotangent; + m[2][2] = -(zFar + zNear) / deltaZ; + m[2][3] = -1; + m[3][2] = -2 * zNear * zFar / deltaZ; + m[3][3] = 0; + GLC_Context::current()->glcMultMatrix(GLC_Matrix4x4(&m[0][0])); +} + +static void normalize(float v[3]) +{ + float r; + + r = sqrt( v[0]*v[0] + v[1]*v[1] + v[2]*v[2] ); + if (r == 0.0) return; + + v[0] /= r; + v[1] /= r; + v[2] /= r; +} + +static void cross(float v1[3], float v2[3], float result[3]) +{ + result[0] = v1[1]*v2[2] - v1[2]*v2[1]; + result[1] = v1[2]*v2[0] - v1[0]*v2[2]; + result[2] = v1[0]*v2[1] - v1[1]*v2[0]; +} + +void glc::gluLookAt(GLdouble eyex, GLdouble eyey, GLdouble eyez, GLdouble centerx, + GLdouble centery, GLdouble centerz, GLdouble upx, GLdouble upy, + GLdouble upz) +{ + float forward[3], side[3], up[3]; + GLfloat m[4][4]; + + forward[0] = centerx - eyex; + forward[1] = centery - eyey; + forward[2] = centerz - eyez; + + up[0] = upx; + up[1] = upy; + up[2] = upz; + + normalize(forward); + + /* Side = forward x up */ + cross(forward, up, side); + normalize(side); + + /* Recompute up as: up = side x forward */ + cross(side, forward, up); + + __gluMakeIdentityf(&m[0][0]); + m[0][0] = side[0]; + m[1][0] = side[1]; + m[2][0] = side[2]; + + m[0][1] = up[0]; + m[1][1] = up[1]; + m[2][1] = up[2]; + + m[0][2] = -forward[0]; + m[1][2] = -forward[1]; + m[2][2] = -forward[2]; + + GLC_Matrix4x4 translate; + translate.setMatTranslate(-eyex, -eyey, -eyez); + GLC_Matrix4x4 result= GLC_Matrix4x4(&m[0][0]) * translate; + GLC_Context::current()->glcMultMatrix(result); +} + +static void __gluMultMatrixVecd(const GLdouble matrix[16], const GLdouble in[4], + GLdouble out[4]) +{ + int i; + + for (i=0; i<4; i++) { + out[i] = + in[0] * matrix[0*4+i] + + in[1] * matrix[1*4+i] + + in[2] * matrix[2*4+i] + + in[3] * matrix[3*4+i]; + } +} + +/* +** Invert 4x4 matrix. +** Contributed by David Moore (See Mesa bug #6748) +*/ +static int __gluInvertMatrixd(const GLdouble m[16], GLdouble invOut[16]) +{ + double inv[16], det; + int i; + + inv[0] = m[5]*m[10]*m[15] - m[5]*m[11]*m[14] - m[9]*m[6]*m[15] + + m[9]*m[7]*m[14] + m[13]*m[6]*m[11] - m[13]*m[7]*m[10]; + inv[4] = -m[4]*m[10]*m[15] + m[4]*m[11]*m[14] + m[8]*m[6]*m[15] + - m[8]*m[7]*m[14] - m[12]*m[6]*m[11] + m[12]*m[7]*m[10]; + inv[8] = m[4]*m[9]*m[15] - m[4]*m[11]*m[13] - m[8]*m[5]*m[15] + + m[8]*m[7]*m[13] + m[12]*m[5]*m[11] - m[12]*m[7]*m[9]; + inv[12] = -m[4]*m[9]*m[14] + m[4]*m[10]*m[13] + m[8]*m[5]*m[14] + - m[8]*m[6]*m[13] - m[12]*m[5]*m[10] + m[12]*m[6]*m[9]; + inv[1] = -m[1]*m[10]*m[15] + m[1]*m[11]*m[14] + m[9]*m[2]*m[15] + - m[9]*m[3]*m[14] - m[13]*m[2]*m[11] + m[13]*m[3]*m[10]; + inv[5] = m[0]*m[10]*m[15] - m[0]*m[11]*m[14] - m[8]*m[2]*m[15] + + m[8]*m[3]*m[14] + m[12]*m[2]*m[11] - m[12]*m[3]*m[10]; + inv[9] = -m[0]*m[9]*m[15] + m[0]*m[11]*m[13] + m[8]*m[1]*m[15] + - m[8]*m[3]*m[13] - m[12]*m[1]*m[11] + m[12]*m[3]*m[9]; + inv[13] = m[0]*m[9]*m[14] - m[0]*m[10]*m[13] - m[8]*m[1]*m[14] + + m[8]*m[2]*m[13] + m[12]*m[1]*m[10] - m[12]*m[2]*m[9]; + inv[2] = m[1]*m[6]*m[15] - m[1]*m[7]*m[14] - m[5]*m[2]*m[15] + + m[5]*m[3]*m[14] + m[13]*m[2]*m[7] - m[13]*m[3]*m[6]; + inv[6] = -m[0]*m[6]*m[15] + m[0]*m[7]*m[14] + m[4]*m[2]*m[15] + - m[4]*m[3]*m[14] - m[12]*m[2]*m[7] + m[12]*m[3]*m[6]; + inv[10] = m[0]*m[5]*m[15] - m[0]*m[7]*m[13] - m[4]*m[1]*m[15] + + m[4]*m[3]*m[13] + m[12]*m[1]*m[7] - m[12]*m[3]*m[5]; + inv[14] = -m[0]*m[5]*m[14] + m[0]*m[6]*m[13] + m[4]*m[1]*m[14] + - m[4]*m[2]*m[13] - m[12]*m[1]*m[6] + m[12]*m[2]*m[5]; + inv[3] = -m[1]*m[6]*m[11] + m[1]*m[7]*m[10] + m[5]*m[2]*m[11] + - m[5]*m[3]*m[10] - m[9]*m[2]*m[7] + m[9]*m[3]*m[6]; + inv[7] = m[0]*m[6]*m[11] - m[0]*m[7]*m[10] - m[4]*m[2]*m[11] + + m[4]*m[3]*m[10] + m[8]*m[2]*m[7] - m[8]*m[3]*m[6]; + inv[11] = -m[0]*m[5]*m[11] + m[0]*m[7]*m[9] + m[4]*m[1]*m[11] + - m[4]*m[3]*m[9] - m[8]*m[1]*m[7] + m[8]*m[3]*m[5]; + inv[15] = m[0]*m[5]*m[10] - m[0]*m[6]*m[9] - m[4]*m[1]*m[10] + + m[4]*m[2]*m[9] + m[8]*m[1]*m[6] - m[8]*m[2]*m[5]; + + det = m[0]*inv[0] + m[1]*inv[4] + m[2]*inv[8] + m[3]*inv[12]; + if (det == 0) + return GL_FALSE; + + det = 1.0 / det; + + for (i = 0; i < 16; i++) + invOut[i] = inv[i] * det; + + return GL_TRUE; +} + +static void __gluMultMatricesd(const GLdouble a[16], const GLdouble b[16], + GLdouble r[16]) +{ + int i, j; + + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + r[i*4+j] = + a[i*4+0]*b[0*4+j] + + a[i*4+1]*b[1*4+j] + + a[i*4+2]*b[2*4+j] + + a[i*4+3]*b[3*4+j]; + } + } +} + +GLint glc::gluProject(GLdouble objx, GLdouble objy, GLdouble objz, + const GLdouble modelMatrix[16], + const GLdouble projMatrix[16], + const GLint viewport[4], + GLdouble *winx, GLdouble *winy, GLdouble *winz) +{ + double in[4]; + double out[4]; + + in[0]=objx; + in[1]=objy; + in[2]=objz; + in[3]=1.0; + __gluMultMatrixVecd(modelMatrix, in, out); + __gluMultMatrixVecd(projMatrix, out, in); + if (in[3] == 0.0) return(GL_FALSE); + in[0] /= in[3]; + in[1] /= in[3]; + in[2] /= in[3]; + /* Map x, y and z to range 0-1 */ + in[0] = in[0] * 0.5 + 0.5; + in[1] = in[1] * 0.5 + 0.5; + in[2] = in[2] * 0.5 + 0.5; + + /* Map x,y to viewport */ + in[0] = in[0] * viewport[2] + viewport[0]; + in[1] = in[1] * viewport[3] + viewport[1]; + + *winx=in[0]; + *winy=in[1]; + *winz=in[2]; + return(GL_TRUE); +} + +GLint glc::gluUnProject(GLdouble winx, GLdouble winy, GLdouble winz, + const GLdouble modelMatrix[16], + const GLdouble projMatrix[16], + const GLint viewport[4], + GLdouble *objx, GLdouble *objy, GLdouble *objz) +{ + double finalMatrix[16]; + double in[4]; + double out[4]; + + __gluMultMatricesd(modelMatrix, projMatrix, finalMatrix); + if (!__gluInvertMatrixd(finalMatrix, finalMatrix)) return(GL_FALSE); + + in[0]=winx; + in[1]=winy; + in[2]=winz; + in[3]=1.0; + + /* Map x and y from window coordinates */ + in[0] = (in[0] - viewport[0]) / viewport[2]; + in[1] = (in[1] - viewport[1]) / viewport[3]; + + /* Map to range -1 to 1 */ + in[0] = in[0] * 2 - 1; + in[1] = in[1] * 2 - 1; + in[2] = in[2] * 2 - 1; + + __gluMultMatrixVecd(finalMatrix, in, out); + if (out[3] == 0.0) return(GL_FALSE); + out[0] /= out[3]; + out[1] /= out[3]; + out[2] /= out[3]; + *objx = out[0]; + *objy = out[1]; + *objz = out[2]; + return(GL_TRUE); +} + +GLint glc::gluUnProject4(GLdouble winx, GLdouble winy, GLdouble winz, GLdouble clipw, + const GLdouble modelMatrix[16], + const GLdouble projMatrix[16], + const GLint viewport[4], + GLclampd nearVal, GLclampd farVal, + GLdouble *objx, GLdouble *objy, GLdouble *objz, + GLdouble *objw) +{ + double finalMatrix[16]; + double in[4]; + double out[4]; + + __gluMultMatricesd(modelMatrix, projMatrix, finalMatrix); + if (!__gluInvertMatrixd(finalMatrix, finalMatrix)) return(GL_FALSE); + + in[0]=winx; + in[1]=winy; + in[2]=winz; + in[3]=clipw; + + /* Map x and y from window coordinates */ + in[0] = (in[0] - viewport[0]) / viewport[2]; + in[1] = (in[1] - viewport[1]) / viewport[3]; + in[2] = (in[2] - nearVal) / (farVal - nearVal); + + /* Map to range -1 to 1 */ + in[0] = in[0] * 2 - 1; + in[1] = in[1] * 2 - 1; + in[2] = in[2] * 2 - 1; + + __gluMultMatrixVecd(finalMatrix, in, out); + if (out[3] == 0.0) return(GL_FALSE); + *objx = out[0]; + *objy = out[1]; + *objz = out[2]; + *objw = out[3]; + return(GL_TRUE); +} + +void glc::gluPickMatrix(GLdouble x, GLdouble y, GLdouble deltax, GLdouble deltay, + GLint viewport[4]) +{ + if (deltax <= 0 || deltay <= 0) { + return; + } + + /* Translate and scale the picked region to the entire window */ + GLC_Matrix4x4 translate; + translate.setMatTranslate((viewport[2] - 2 * (x - viewport[0])) / deltax, (viewport[3] - 2 * (y - viewport[1])) / deltay, 0.0); + + GLC_Matrix4x4 scaling; + scaling.setMatScaling(viewport[2] / deltax, viewport[3] / deltay, 0.0); + GLC_Context::current()->glcMultMatrix(translate * scaling); +} diff --git a/ground/openpilotgcs/src/libs/glc_lib/include/GLC_Context b/ground/openpilotgcs/src/libs/glc_lib/include/GLC_Context new file mode 100644 index 000000000..d204f90bb --- /dev/null +++ b/ground/openpilotgcs/src/libs/glc_lib/include/GLC_Context @@ -0,0 +1 @@ +#include "glc_context.h" diff --git a/ground/openpilotgcs/src/libs/glc_lib/include/GLC_ContextManager b/ground/openpilotgcs/src/libs/glc_lib/include/GLC_ContextManager new file mode 100644 index 000000000..8e7985eb4 --- /dev/null +++ b/ground/openpilotgcs/src/libs/glc_lib/include/GLC_ContextManager @@ -0,0 +1 @@ +#include "glc_contextmanager.h" diff --git a/ground/openpilotgcs/src/libs/glc_lib/include/GLC_Glu b/ground/openpilotgcs/src/libs/glc_lib/include/GLC_Glu new file mode 100644 index 000000000..38ada69e1 --- /dev/null +++ b/ground/openpilotgcs/src/libs/glc_lib/include/GLC_Glu @@ -0,0 +1 @@ +#include "glu/glc_glu.h" diff --git a/ground/openpilotgcs/src/libs/glc_lib/include/GLC_Octree b/ground/openpilotgcs/src/libs/glc_lib/include/GLC_Octree index d9acc2869..28c84762c 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/include/GLC_Octree +++ b/ground/openpilotgcs/src/libs/glc_lib/include/GLC_Octree @@ -1 +1 @@ -#include +#include "sceneGraph/glc_octree.h" diff --git a/ground/openpilotgcs/src/libs/glc_lib/include/GLC_OctreeNode b/ground/openpilotgcs/src/libs/glc_lib/include/GLC_OctreeNode index a22b7056a..abfdb83e9 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/include/GLC_OctreeNode +++ b/ground/openpilotgcs/src/libs/glc_lib/include/GLC_OctreeNode @@ -1 +1 @@ -#include +#include "sceneGraph/glc_octreenode.h" diff --git a/ground/openpilotgcs/src/libs/glc_lib/include/GLC_OpenGLState b/ground/openpilotgcs/src/libs/glc_lib/include/GLC_OpenGLState deleted file mode 100644 index b2f516dac..000000000 --- a/ground/openpilotgcs/src/libs/glc_lib/include/GLC_OpenGLState +++ /dev/null @@ -1 +0,0 @@ -#include "glc_openglstate.h diff --git a/ground/openpilotgcs/src/libs/glc_lib/include/GLC_RenderState b/ground/openpilotgcs/src/libs/glc_lib/include/GLC_RenderState new file mode 100644 index 000000000..aa799f8fe --- /dev/null +++ b/ground/openpilotgcs/src/libs/glc_lib/include/GLC_RenderState @@ -0,0 +1 @@ +#include "glc_renderstate.h diff --git a/ground/openpilotgcs/src/libs/glc_lib/include/GLC_SpacePartitioning b/ground/openpilotgcs/src/libs/glc_lib/include/GLC_SpacePartitioning index 320e5e46e..6ddabbadd 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/include/GLC_SpacePartitioning +++ b/ground/openpilotgcs/src/libs/glc_lib/include/GLC_SpacePartitioning @@ -1 +1 @@ -#include +#include "sceneGraph/glc_spacepertitionning.h" diff --git a/ground/openpilotgcs/src/libs/glc_lib/include/GLC_StructInstance b/ground/openpilotgcs/src/libs/glc_lib/include/GLC_StructInstance index 22fe9472b..7287ef0a0 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/include/GLC_StructInstance +++ b/ground/openpilotgcs/src/libs/glc_lib/include/GLC_StructInstance @@ -1 +1 @@ -#include +#include "sceneGraph/glc_structinstance.h" diff --git a/ground/openpilotgcs/src/libs/glc_lib/include/GLC_StructOccurence b/ground/openpilotgcs/src/libs/glc_lib/include/GLC_StructOccurence index cd63dc059..295022968 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/include/GLC_StructOccurence +++ b/ground/openpilotgcs/src/libs/glc_lib/include/GLC_StructOccurence @@ -1 +1 @@ -#include +#include "sceneGraph/glc_structoccurence.h" diff --git a/ground/openpilotgcs/src/libs/glc_lib/include/GLC_StructReference b/ground/openpilotgcs/src/libs/glc_lib/include/GLC_StructReference index 28b7846ec..cc1d1983e 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/include/GLC_StructReference +++ b/ground/openpilotgcs/src/libs/glc_lib/include/GLC_StructReference @@ -1 +1 @@ -#include +#include "sceneGraph/glc_structreference.h" diff --git a/ground/openpilotgcs/src/libs/glc_lib/include/GLC_TsrMover b/ground/openpilotgcs/src/libs/glc_lib/include/GLC_TsrMover new file mode 100644 index 000000000..d55ac1b43 --- /dev/null +++ b/ground/openpilotgcs/src/libs/glc_lib/include/GLC_TsrMover @@ -0,0 +1 @@ +#include "viewport/tsrmover.h" diff --git a/ground/openpilotgcs/src/libs/glc_lib/include/GLC_UserInput b/ground/openpilotgcs/src/libs/glc_lib/include/GLC_UserInput new file mode 100644 index 000000000..49e7b16f0 --- /dev/null +++ b/ground/openpilotgcs/src/libs/glc_lib/include/GLC_UserInput @@ -0,0 +1 @@ +#include "viewport/glc_userinput.h" diff --git a/ground/openpilotgcs/src/libs/glc_lib/io/glc_3dstoworld.cpp b/ground/openpilotgcs/src/libs/glc_lib/io/glc_3dstoworld.cpp index e638bcbba..4b7ac0542 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/io/glc_3dstoworld.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/io/glc_3dstoworld.cpp @@ -45,10 +45,9 @@ #include #include -GLC_3dsToWorld::GLC_3dsToWorld(const QGLContext *pContext) +GLC_3dsToWorld::GLC_3dsToWorld() : m_pWorld(NULL) , m_FileName() -, m_pQGLContext(pContext) , m_pCurrentMesh(NULL) , m_pLib3dsFile(NULL) , m_Materials() @@ -387,7 +386,7 @@ void GLC_3dsToWorld::loadMaterial(Lib3dsMaterial* p3dsMaterial) if (textureFile.open(QIODevice::ReadOnly)) { // Create the texture and assign it to the material - GLC_Texture *pTexture = new GLC_Texture(m_pQGLContext, textureFile); + GLC_Texture *pTexture = new GLC_Texture(textureFile); pMaterial->setTexture(pTexture); m_ListOfAttachedFileName << textureFileName; textureFile.close(); diff --git a/ground/openpilotgcs/src/libs/glc_lib/io/glc_3dstoworld.h b/ground/openpilotgcs/src/libs/glc_lib/io/glc_3dstoworld.h index b5460f993..9aa7330c3 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/io/glc_3dstoworld.h +++ b/ground/openpilotgcs/src/libs/glc_lib/io/glc_3dstoworld.h @@ -72,7 +72,7 @@ class GLC_LIB_EXPORT GLC_3dsToWorld : public QObject ////////////////////////////////////////////////////////////////////// public: - GLC_3dsToWorld(const QGLContext*); + GLC_3dsToWorld(); virtual ~GLC_3dsToWorld(); //@} @@ -125,9 +125,6 @@ private: //! The 3DS File name QString m_FileName; - //! OpenGL Context - const QGLContext* m_pQGLContext; - //! The current mesh GLC_Mesh* m_pCurrentMesh; diff --git a/ground/openpilotgcs/src/libs/glc_lib/io/glc_3dxmltoworld.cpp b/ground/openpilotgcs/src/libs/glc_lib/io/glc_3dxmltoworld.cpp index dc30a45fb..fcea092de 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/io/glc_3dxmltoworld.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/io/glc_3dxmltoworld.cpp @@ -45,9 +45,8 @@ QMutex GLC_3dxmlToWorld::m_ZipMutex; static qint64 chunckSize= 10000000; -GLC_3dxmlToWorld::GLC_3dxmlToWorld(const QGLContext* pContext) +GLC_3dxmlToWorld::GLC_3dxmlToWorld() : QObject() -, m_pQGLContext(pContext) , m_pStreamReader(NULL) , m_FileName() , m_p3dxmlArchive(NULL) @@ -72,9 +71,11 @@ GLC_3dxmlToWorld::GLC_3dxmlToWorld(const QGLContext* pContext) , m_SetOfAttachedFileName() , m_CurrentFileName() , m_CurrentDateTime() -, m_OccurenceAttrib() +, m_V3OccurenceAttribHash() +, m_V4OccurenceAttribList() , m_GetExternalRef3DName(false) , m_ByteArrayList() +, m_IsVersion3(false) { } @@ -90,13 +91,18 @@ GLC_3dxmlToWorld::~GLC_3dxmlToWorld() clearMaterialHash(); // Clear specific attributes hash table - QHash::iterator iAttrib= m_OccurenceAttrib.begin(); - while (m_OccurenceAttrib.constEnd() != iAttrib) + QHash::iterator iAttrib= m_V3OccurenceAttribHash.begin(); + while (m_V3OccurenceAttribHash.constEnd() != iAttrib) { delete iAttrib.value(); ++iAttrib; } - m_OccurenceAttrib.clear(); + + const int v4OccurenceAttribCount= m_V4OccurenceAttribList.count(); + for (int i= 0; i < v4OccurenceAttribCount; ++i) + { + delete m_V4OccurenceAttribList.at(i); + } } ////////////////////////////////////////////////////////////////////// @@ -144,6 +150,9 @@ GLC_World* GLC_3dxmlToWorld::createWorldFrom3dxml(QFile &file, bool structureOnl loadCatMaterialRef(); } + // Read the header + readHeader(); + // Load the product structure loadProductStructure(); @@ -179,7 +188,7 @@ GLC_3DRep GLC_3dxmlToWorld::create3DrepFrom3dxmlRep(const QString& fileName) m_CurrentFileName= glc::archiveEntryFileName(fileName); // Get the 3DXML time stamp - m_CurrentDateTime= QFileInfo(QFileInfo(m_FileName).absolutePath() + QDir::separator() + QFileInfo(fileName).fileName()).lastModified(); + m_CurrentDateTime= QFileInfo(QFileInfo(m_FileName)).lastModified(); } else if (glc::isFileString(fileName)) { @@ -231,7 +240,7 @@ GLC_3DRep GLC_3dxmlToWorld::create3DrepFrom3dxmlRep(const QString& fileName) } } } - else if (QFileInfo(m_CurrentFileName).suffix().toLower() == "3drep") + else if ((QFileInfo(m_CurrentFileName).suffix().toLower() == "3drep") || (QFileInfo(m_CurrentFileName).suffix().toLower() == "xml")) { if (GLC_State::cacheIsUsed() && GLC_State::currentCacheManager().isUsable(m_CurrentDateTime, QFileInfo(m_FileName).baseName(), QFileInfo(m_CurrentFileName).fileName())) { @@ -310,7 +319,7 @@ void GLC_3dxmlToWorld::goToRepId(const QString& id) while(!m_pStreamReader->atEnd() && !((QXmlStreamReader::StartElement == m_pStreamReader->tokenType()) && (m_pStreamReader->name() == "Representation") && (m_pStreamReader->attributes().value("id").toString() == id))) { - readNext();; + readNext(); } } @@ -324,7 +333,7 @@ void GLC_3dxmlToWorld::gotToPolygonalRepType() { //qDebug() << m_pStreamReader->name(); //qDebug() << m_pStreamReader->attributes().value("xsi:type").toString(); - readNext();; + readNext(); } } @@ -347,10 +356,34 @@ QString GLC_3dxmlToWorld::readAttribute(const QString& name, bool required) return attributeValue; } +void GLC_3dxmlToWorld::readHeader() +{ + setStreamReaderToFile(m_RootName); + + goToElement(m_pStreamReader, "Header"); + if (m_pStreamReader->atEnd() || m_pStreamReader->hasError()) + { + QString message(QString("GLC_3dxmlToWorld::readHeader Element Header Not found in ") + m_FileName); + GLC_FileFormatException fileFormatException(message, m_FileName, GLC_FileFormatException::WrongFileFormat); + clear(); + throw(fileFormatException); + } + + while(endElementNotReached(m_pStreamReader, "Header")) + { + if ((QXmlStreamReader::StartElement == m_pStreamReader->tokenType()) && (m_pStreamReader->name() == "SchemaVersion")) + { + QString version= getContent(m_pStreamReader, "SchemaVersion"); + m_IsVersion3= version.startsWith('3'); + } + readNext(); + } +} + // Load the product structure void GLC_3dxmlToWorld::loadProductStructure() { - setStreamReaderToFile(m_RootName); + goToElement(m_pStreamReader, "ProductStructure"); if (m_pStreamReader->atEnd() || m_pStreamReader->hasError()) { @@ -373,7 +406,7 @@ void GLC_3dxmlToWorld::loadProductStructure() else loadInstanceRep(); } - readNext();; + readNext(); } // Load Default view properties @@ -382,11 +415,11 @@ void GLC_3dxmlToWorld::loadProductStructure() if ((QXmlStreamReader::StartElement == m_pStreamReader->tokenType()) && ((m_pStreamReader->name() == "DefaultView") || (m_pStreamReader->name() == "GeometricRepresentationSet"))) { - if (m_pStreamReader->name() == "DefaultView") loadGraphicsProperties(); + if (m_pStreamReader->name() == "DefaultView") loadDefaultView(); else if (m_pStreamReader->name() == "GeometricRepresentationSet") loadLocalRepresentations(); } - readNext();; + readNext(); } // Check if an error Occur @@ -478,17 +511,17 @@ void GLC_3dxmlToWorld::loadProductStructure() // Update occurence number m_pWorld->rootOccurence()->updateOccurenceNumber(1); - // Change occurence attributes - if (! m_OccurenceAttrib.isEmpty()) + // Change occurence attributes for 3DXML V3 + if (! m_V3OccurenceAttribHash.isEmpty()) { - //qDebug() << "Not visible occurence= " << m_OccurenceAttrib.size(); + //qDebug() << "Not visible occurence= " << m_V3OccurenceAttribHash.size(); QList occurenceList= m_pWorld->listOfOccurence(); const int size= occurenceList.size(); for (int i= 0; i < size; ++i) { - if (m_OccurenceAttrib.contains(occurenceList.at(i)->occurenceNumber())) + if (m_V3OccurenceAttribHash.contains(occurenceList.at(i)->occurenceNumber())) { - OccurenceAttrib* pOccurenceAttrib= m_OccurenceAttrib.value(occurenceList.at(i)->occurenceNumber()); + V3OccurenceAttrib* pOccurenceAttrib= m_V3OccurenceAttribHash.value(occurenceList.at(i)->occurenceNumber()); occurenceList.at(i)->setVisibility(pOccurenceAttrib->m_IsVisible); if (NULL != pOccurenceAttrib->m_pRenderProperties) { @@ -497,6 +530,27 @@ void GLC_3dxmlToWorld::loadProductStructure() } } } + + // Change occurence attributes for 3DXML V4 + if (!m_V4OccurenceAttribList.isEmpty()) + { + QHash instanceToIdHash; + const int assyCount= m_AssyLinkList.count(); + for (int i= 0; i < assyCount; ++i) + { + AssyLink assyLink= m_AssyLinkList.at(i); + instanceToIdHash.insert(assyLink.m_pChildInstance, assyLink.m_InstanceId); + } + + const int attribCount= m_V4OccurenceAttribList.count(); + for (int i= 0; i < attribCount; ++i) + { + V4OccurenceAttrib* pCurrentV4OccurenceAttrib= m_V4OccurenceAttribList.at(i); + //qDebug() << pCurrentV4OccurenceAttrib->m_Path; + applyV4Attribute(m_pWorld->rootOccurence(), pCurrentV4OccurenceAttrib, instanceToIdHash); + } + } + // Check usage of Instance InstanceOfExtRefHash::const_iterator iInstance= m_InstanceOfExtRefHash.constBegin(); while (m_InstanceOfExtRefHash.constEnd() != iInstance) @@ -570,10 +624,10 @@ void GLC_3dxmlToWorld::loadReference3D() } userAttributes.insert(name, value); } - readNext();; + readNext(); } } - readNext();; + readNext(); } if (!userAttributes.isEmpty()) { @@ -591,6 +645,7 @@ void GLC_3dxmlToWorld::loadInstance3D() const unsigned int instanceId= readAttribute("id", true).toUInt(); const QString instName(readAttribute("name", false)); + goToElement(m_pStreamReader, "IsAggregatedBy"); const unsigned int aggregatedById= getContent(m_pStreamReader, "IsAggregatedBy").toUInt(); QString instanceOf= getContent(m_pStreamReader, "IsInstanceOf"); const QString matrixString= getContent(m_pStreamReader, "RelativeMatrix"); @@ -614,10 +669,10 @@ void GLC_3dxmlToWorld::loadInstance3D() QString value= readAttribute("value", true); userAttributes.insert(name, value); } - readNext();; + readNext(); } } - readNext();; + readNext(); } if (!userAttributes.isEmpty()) { @@ -687,7 +742,7 @@ void GLC_3dxmlToWorld::loadInstanceRep() { const QString local= "urn:3DXML:Reference:loc:"; - //const QString instName(readAttribute("name", true)); + goToElement(m_pStreamReader, "IsAggregatedBy"); const unsigned int aggregatedById= getContent(m_pStreamReader, "IsAggregatedBy").toUInt(); QString instanceOf= getContent(m_pStreamReader, "IsInstanceOf"); @@ -745,7 +800,6 @@ void GLC_3dxmlToWorld::loadExternalRef3D() GLC_3DRep* pRep= new GLC_3DRep(binaryRep.loadRep()); setRepresentationFileName(pRep); - factorizeMaterial(pRep); GLC_StructReference* pCurrentRef= new GLC_StructReference(pRep); pCurrentRef->setName(QFileInfo(m_CurrentFileName).baseName()); @@ -952,103 +1006,7 @@ GLC_StructReference* GLC_3dxmlToWorld::createReferenceRep(QString repId, GLC_3DR currentMesh3DRep.addGeom(pMesh); } - // Get the master lod accuracy - double masterLodAccuracy= readAttribute("accuracy", false).toDouble(); - - loadLOD(pMesh); - if (m_pStreamReader->atEnd() || m_pStreamReader->hasError()) - { - QStringList stringList(m_FileName); - stringList.append(m_CurrentFileName); - stringList.append("Master LOD not found"); - GLC_ErrorLog::addError(stringList); - return new GLC_StructReference("Empty Rep"); - } - - // Load Faces index data - while (endElementNotReached(m_pStreamReader, "Faces")) - { - readNext();; - if ( m_pStreamReader->name() == "Face") - { - loadFace(pMesh, 0, masterLodAccuracy); - } - } - checkForXmlError("End of Faces not found"); - - while (startElementNotReached(m_pStreamReader, "Edges") && startElementNotReached(m_pStreamReader, "VertexBuffer")) - { - readNext();; - } - - checkForXmlError("Element VertexBuffer not found"); - if (m_pStreamReader->name() == "Edges") - { - while (endElementNotReached(m_pStreamReader, "Edges")) - { - readNext();; - if ( m_pStreamReader->name() == "Polyline") - { - loadPolyline(pMesh); - readNext();; - } - } - } - - - { - QString verticePosition= getContent(m_pStreamReader, "Positions").replace(',', ' '); - //qDebug() << "Position " << verticePosition; - checkForXmlError("Error while retrieving Position ContentVertexBuffer"); - // Load Vertice position - QTextStream verticeStream(&verticePosition); - QList verticeValues; - QString buff; - while ((!verticeStream.atEnd())) - { - verticeStream >> buff; - verticeValues.append(buff.toFloat()); - } - pMesh->addVertice(verticeValues.toVector()); - - } - - { - QString normals= getContent(m_pStreamReader, "Normals").replace(',', ' '); - //qDebug() << "Normals " << normals; - checkForXmlError("Error while retrieving Normals values"); - // Load Vertice Normals - QTextStream normalsStream(&normals); - QList normalValues; - QString buff; - while ((!normalsStream.atEnd())) - { - normalsStream >> buff; - normalValues.append(buff.toFloat()); - } - pMesh->addNormals(normalValues.toVector()); - } - - // Try to find texture coordinate - while (endElementNotReached(m_pStreamReader, "VertexBuffer")) - { - //qDebug() << "Try to find texture coordinate " << m_pStreamReader->name() << " " << m_pStreamReader->lineNumber(); - if ((QXmlStreamReader::StartElement == m_pStreamReader->tokenType()) && (m_pStreamReader->name() == "TextureCoordinates")) - { - QString texels= getContent(m_pStreamReader, "TextureCoordinates").replace(',', ' '); - checkForXmlError("Error while retrieving Texture coordinates"); - QTextStream texelStream(&texels); - QList texelValues; - QString buff; - while ((!texelStream.atEnd())) - { - texelStream >> buff; - texelValues.append(buff.toFloat()); - } - pMesh->addTexels(texelValues.toVector()); - } - readNext();; - } + loadRep(pMesh); ++numberOfMesh; } @@ -1166,8 +1124,6 @@ void GLC_3dxmlToWorld::createUnfoldedTree() ++iLink; } - m_AssyLinkList.clear(); - // Check the assembly structure occurence ReferenceHash::const_iterator iRef= m_ReferenceHash.constBegin(); while (m_ReferenceHash.constEnd() != iRef) @@ -1239,34 +1195,7 @@ void GLC_3dxmlToWorld::checkForXmlError(const QString& info) throw(fileFormatException); } } -// Go to the master LOD -void GLC_3dxmlToWorld::loadLOD(GLC_Mesh* pMesh) -{ - int lodIndex= 1; - while(!m_pStreamReader->atEnd() && !((QXmlStreamReader::StartElement == m_pStreamReader->tokenType()) && (m_pStreamReader->name() == "Faces"))) - { - readNext();; - if ((QXmlStreamReader::StartElement == m_pStreamReader->tokenType()) && (m_pStreamReader->name() == "SurfaceAttributes")) - { - m_pCurrentMaterial= loadSurfaceAttributes(); - } - else if ((QXmlStreamReader::StartElement == m_pStreamReader->tokenType()) && (m_pStreamReader->name() == "PolygonalLOD")) - { - double accuracy= readAttribute("accuracy", true).toDouble(); - // Load Faces index data - while (endElementNotReached(m_pStreamReader, "Faces")) - { - readNext();; - if ( m_pStreamReader->name() == "Face") - { - loadFace(pMesh, lodIndex, accuracy); - } - } - checkForXmlError("End of Faces not found"); - ++lodIndex; - } - } -} + // Load a face void GLC_3dxmlToWorld::loadFace(GLC_Mesh* pMesh, const int lod, double accuracy) { @@ -1292,7 +1221,7 @@ void GLC_3dxmlToWorld::loadFace(GLC_Mesh* pMesh, const int lod, double accuracy) { pCurrentMaterial= loadSurfaceAttributes(); } - readNext();; + readNext(); } if (NULL == pCurrentMaterial) { @@ -1365,14 +1294,30 @@ void GLC_3dxmlToWorld::loadPolyline(GLC_Mesh* pMesh) data.replace(',', ' '); QTextStream dataStream(&data); - GLfloatVector dataVector; + QList values; QString buff; while ((!dataStream.atEnd())) { dataStream >> buff; - dataVector.append(buff.toFloat()); + values.append(buff.toFloat()); } - pMesh->addVerticeGroup(dataVector); + if ((values.size() % 3) == 0) + { + pMesh->addVerticeGroup(values.toVector()); + } + else + { + QString message(QString("polyline buffer is not a multiple of 3 ") + m_CurrentFileName); + + QStringList stringList(message); + GLC_ErrorLog::addError(stringList); + + GLC_FileFormatException fileFormatException(message, m_FileName, GLC_FileFormatException::WrongFileFormat); + clear(); + throw(fileFormatException); + } + + } // Clear material hash @@ -1404,7 +1349,7 @@ GLC_Material* GLC_3dxmlToWorld::loadSurfaceAttributes() { while (endElementNotReached(m_pStreamReader, "MaterialApplication")) { - readNext();; + readNext(); if ((QXmlStreamReader::StartElement == m_pStreamReader->tokenType()) && (m_pStreamReader->name() == "MaterialId")) { checkForXmlError("Material ID not found"); @@ -1414,7 +1359,7 @@ GLC_Material* GLC_3dxmlToWorld::loadSurfaceAttributes() } } - readNext();; + readNext(); } return pMaterial; @@ -1566,7 +1511,7 @@ void GLC_3dxmlToWorld::loadLocalRepresentations() } delete pRef; } - readNext();; + readNext(); } //qDebug() << "Local rep loaded"; @@ -1586,7 +1531,7 @@ void GLC_3dxmlToWorld::loadLocalRepresentations() } } -void GLC_3dxmlToWorld::loadGraphicsProperties() +void GLC_3dxmlToWorld::loadDefaultView() { if (m_pStreamReader->atEnd() || m_pStreamReader->hasError()) { @@ -1601,10 +1546,11 @@ void GLC_3dxmlToWorld::loadGraphicsProperties() { if ((QXmlStreamReader::StartElement == m_pStreamReader->tokenType()) && (m_pStreamReader->name() == "DefaultViewProperty")) { - loadDefaultViewProperty(); + if (m_IsVersion3) loadV3DefaultViewProperty(); + else loadV4DefaultViewProperty(); } - readNext();; + readNext(); } // Check if an error Occur @@ -1618,7 +1564,7 @@ void GLC_3dxmlToWorld::loadGraphicsProperties() } -void GLC_3dxmlToWorld::loadDefaultViewProperty() +void GLC_3dxmlToWorld::loadV3DefaultViewProperty() { goToElement(m_pStreamReader, "OccurenceId"); unsigned int occurenceId= getContent(m_pStreamReader, "OccurenceId").toUInt(); @@ -1635,13 +1581,13 @@ void GLC_3dxmlToWorld::loadDefaultViewProperty() QString visibleString= readAttribute("visible", true); if (visibleString != "true") { - if (!m_OccurenceAttrib.contains(occurenceId)) + if (!m_V3OccurenceAttribHash.contains(occurenceId)) { - OccurenceAttrib* pOccurenceAttrib= new OccurenceAttrib(); + V3OccurenceAttrib* pOccurenceAttrib= new V3OccurenceAttrib(); pOccurenceAttrib->m_IsVisible= false; - m_OccurenceAttrib.insert(occurenceId, pOccurenceAttrib); + m_V3OccurenceAttribHash.insert(occurenceId, pOccurenceAttrib); } - else m_OccurenceAttrib.value(occurenceId)->m_IsVisible= false; + else m_V3OccurenceAttribHash.value(occurenceId)->m_IsVisible= false; } } else if ((QXmlStreamReader::StartElement == m_pStreamReader->tokenType()) && (m_pStreamReader->name() == "SurfaceAttributes")) @@ -1669,21 +1615,21 @@ void GLC_3dxmlToWorld::loadDefaultViewProperty() pRenderProperties->setOverwriteTransparency(static_cast(alpha)); pRenderProperties->setRenderingMode(glc::OverwriteTransparency); } - if (!m_OccurenceAttrib.contains(occurenceId)) + if (!m_V3OccurenceAttribHash.contains(occurenceId)) { - OccurenceAttrib* pOccurenceAttrib= new OccurenceAttrib(); + V3OccurenceAttrib* pOccurenceAttrib= new V3OccurenceAttrib(); pOccurenceAttrib->m_pRenderProperties= pRenderProperties; - m_OccurenceAttrib.insert(occurenceId, pOccurenceAttrib); + m_V3OccurenceAttribHash.insert(occurenceId, pOccurenceAttrib); } - else m_OccurenceAttrib.value(occurenceId)->m_pRenderProperties= pRenderProperties; + else m_V3OccurenceAttribHash.value(occurenceId)->m_pRenderProperties= pRenderProperties; } - readNext();; + readNext(); } } - readNext();; + readNext(); } // Check if an error Occur @@ -1696,6 +1642,120 @@ void GLC_3dxmlToWorld::loadDefaultViewProperty() } } + +void GLC_3dxmlToWorld::loadV4DefaultViewProperty() +{ + V4OccurenceAttrib* pV4OccurenceAttrib= new V4OccurenceAttrib(); + + while(endElementNotReached(m_pStreamReader, "DefaultViewProperty")) + { + if ((QXmlStreamReader::StartElement == m_pStreamReader->tokenType()) && (m_pStreamReader->name() == "OccurenceId")) + { + pV4OccurenceAttrib->m_Path= loadOccurencePath(); + } + else if ((QXmlStreamReader::StartElement == m_pStreamReader->tokenType()) && (m_pStreamReader->name() == "RelativePosition")) + { + const QString matrix= getContent(m_pStreamReader, "RelativePosition"); + pV4OccurenceAttrib->m_pMatrix= new GLC_Matrix4x4(loadMatrix(matrix)); + } + else if ((QXmlStreamReader::StartElement == m_pStreamReader->tokenType()) && (m_pStreamReader->name() == "GraphicProperties")) + { + loadGraphicProperties(pV4OccurenceAttrib); + } + readNext(); + } + + if(!pV4OccurenceAttrib->m_Path.isEmpty()) + { + m_V4OccurenceAttribList.append(pV4OccurenceAttrib); + } + else + { + delete pV4OccurenceAttrib; + } + + // Check if an error Occur + if (m_pStreamReader->hasError()) + { + QString message(QString("GLC_3dxmlToWorld::loadV4DefaultViewProperty An error occur in ") + m_FileName); + GLC_FileFormatException fileFormatException(message, m_FileName, GLC_FileFormatException::WrongFileFormat); + clear(); + throw(fileFormatException); + } + +} + +QList GLC_3dxmlToWorld::loadOccurencePath() +{ + QList path; + while(endElementNotReached(m_pStreamReader, "OccurenceId")) + { + if ((QXmlStreamReader::StartElement == m_pStreamReader->tokenType()) && (m_pStreamReader->name() == "id")) + { + QString instanceId= getContent(m_pStreamReader, "id"); + instanceId= instanceId.right(instanceId.length() - 1 - instanceId.indexOf('#')); + path.append(instanceId.toUInt()); + } + readNext(); + } + + // Check if an error Occur + if (m_pStreamReader->hasError() || path.contains(0)) + { + QString message(QString("GLC_3dxmlToWorld::loadOccurencePath An error occur in ") + m_FileName); + GLC_FileFormatException fileFormatException(message, m_FileName, GLC_FileFormatException::WrongFileFormat); + clear(); + throw(fileFormatException); + } + + return path; +} + +void GLC_3dxmlToWorld::loadGraphicProperties(V4OccurenceAttrib* pAttrib) +{ + while(endElementNotReached(m_pStreamReader, "GraphicProperties")) + { + if ((QXmlStreamReader::StartElement == m_pStreamReader->tokenType()) && (m_pStreamReader->name() == "GeneralAttributes")) + { + QString visibleString= readAttribute("visible", true); + if (visibleString != "true") + { + pAttrib->m_IsVisible= false; + } + } + else if ((QXmlStreamReader::StartElement == m_pStreamReader->tokenType()) && (m_pStreamReader->name() == "SurfaceAttributes")) + { + goToElement(m_pStreamReader, "Color"); + const double red= readAttribute("red", true).toDouble(); + const double green= readAttribute("green", true).toDouble(); + const double blue= readAttribute("blue", true).toDouble(); + double alpha= 1.0; + QString alphaString= readAttribute("alpha", false); + if (!alphaString.isEmpty()) alpha= alphaString.toDouble(); + + GLC_RenderProperties* pRenderProperties= new GLC_RenderProperties(); + if (red != -1.0f) + { + QColor diffuseColor; + diffuseColor.setRgbF(red, green, blue, alpha); + GLC_Material* pMaterial= new GLC_Material(); + pMaterial->setDiffuseColor(diffuseColor); + pRenderProperties->setOverwriteMaterial(pMaterial); + pRenderProperties->setRenderingMode(glc::OverwriteMaterial); + } + else if (alpha < 1.0f) + { + pRenderProperties->setOverwriteTransparency(static_cast(alpha)); + pRenderProperties->setRenderingMode(glc::OverwriteTransparency); + } + + pAttrib->m_pRenderProperties= pRenderProperties; + } + + readNext(); + } +} + // Load the extern representation void GLC_3dxmlToWorld::loadExternRepresentations() { @@ -1734,7 +1794,6 @@ void GLC_3dxmlToWorld::loadExternRepresentations() GLC_BSRep binaryRep= cacheManager.binary3DRep(QFileInfo(m_FileName).baseName(), m_CurrentFileName); representation= binaryRep.loadRep(); setRepresentationFileName(&representation); - factorizeMaterial(&representation); } else { @@ -1853,110 +1912,8 @@ GLC_3DRep GLC_3dxmlToWorld::loadCurrentExtRep() currentMeshRep.addGeom(pMesh); } - // Get the master lod accuracy - double masteLodAccuracy= readAttribute("accuracy", false).toDouble(); + loadRep(pMesh); - loadLOD(pMesh); - if (m_pStreamReader->atEnd() || m_pStreamReader->hasError()) - { - QStringList stringList(m_FileName); - stringList.append(m_CurrentFileName); - stringList.append("Master LOD not found"); - GLC_ErrorLog::addError(stringList); - - pMesh->finish(); - currentMeshRep.clean(); - - if (GLC_State::cacheIsUsed()) - { - GLC_CacheManager currentManager= GLC_State::currentCacheManager(); - currentManager.addToCache(QFileInfo(m_FileName).baseName(), currentMeshRep); - } - - return currentMeshRep; - } - - // Load Faces index data - while (endElementNotReached(m_pStreamReader, "Faces")) - { - readNext();; - if ( m_pStreamReader->name() == "Face") - { - loadFace(pMesh, 0, masteLodAccuracy); - } - } - checkForXmlError("End of Faces not found"); - - while (startElementNotReached(m_pStreamReader, "Edges") && startElementNotReached(m_pStreamReader, "VertexBuffer")) - { - readNext();; - } - - checkForXmlError("Element VertexBuffer not found"); - if (m_pStreamReader->name() == "Edges") - { - while (endElementNotReached(m_pStreamReader, "Edges")) - { - readNext();; - if ( m_pStreamReader->name() == "Polyline") - { - loadPolyline(pMesh); - readNext();; - } - } - } - - { - QString verticePosition= getContent(m_pStreamReader, "Positions").replace(',', ' '); - //qDebug() << "Position " << verticePosition; - checkForXmlError("Error while retrieving Position ContentVertexBuffer"); - // Load Vertice position - QTextStream verticeStream(&verticePosition); - QList verticeValues; - QString buff; - while ((!verticeStream.atEnd())) - { - verticeStream >> buff; - verticeValues.append(buff.toFloat()); - } - pMesh->addVertice(verticeValues.toVector()); - - } - - { - QString normals= getContent(m_pStreamReader, "Normals").replace(',', ' '); - //qDebug() << "Normals " << normals; - checkForXmlError("Error while retrieving Normals values"); - // Load Vertice Normals - QTextStream normalsStream(&normals); - QList normalValues; - QString buff; - while ((!normalsStream.atEnd())) - { - normalsStream >> buff; - normalValues.append(buff.toFloat()); - } - pMesh->addNormals(normalValues.toVector()); - } - // Try to find texture coordinate - while (endElementNotReached(m_pStreamReader, "VertexBuffer")) - { - if ((QXmlStreamReader::StartElement == m_pStreamReader->tokenType()) && (m_pStreamReader->name() == "TextureCoordinates")) - { - QString texels= getContent(m_pStreamReader, "TextureCoordinates").replace(',', ' '); - checkForXmlError("Error while retrieving Texture coordinates"); - QTextStream texelStream(&texels); - QList texelValues; - QString buff; - while ((!texelStream.atEnd())) - { - texelStream >> buff; - texelValues.append(buff.toFloat()); - } - pMesh->addTexels(texelValues.toVector()); - } - readNext();; - } ++numberOfMesh; } @@ -2001,7 +1958,7 @@ void GLC_3dxmlToWorld::loadCatMaterialRef() //qDebug() << "Material " << currentMaterial.m_Name << " Added"; } } - readNext();; + readNext(); } } // Load material files @@ -2024,7 +1981,7 @@ void GLC_3dxmlToWorld::loadMaterialDef(const MaterialRef& materialRef) checkForXmlError(QString("Element Osm not found in file : ") + materialRef.m_AssociatedFile); while (endElementNotReached(m_pStreamReader, "Osm")) { - readNext();; + readNext(); if ((QXmlStreamReader::StartElement == m_pStreamReader->tokenType()) && m_pStreamReader->name() == "Attr") { const QString currentName= readAttribute("Name", true); @@ -2121,7 +2078,7 @@ void GLC_3dxmlToWorld::loadCatRepImage() m_TextureImagesHash.insert(id,associatedFile); } } - readNext();; + readNext(); } //qDebug() << "CATRepImage.3dxml Load"; } @@ -2187,7 +2144,7 @@ GLC_Texture* GLC_3dxmlToWorld::loadTexture(QString fileName) GLC_Texture* pTexture= NULL; if (!resultImage.isNull()) { - pTexture= new GLC_Texture(m_pQGLContext, resultImage, resultImageFileName); + pTexture= new GLC_Texture(resultImage, resultImageFileName); } else { @@ -2199,47 +2156,6 @@ GLC_Texture* GLC_3dxmlToWorld::loadTexture(QString fileName) return pTexture; } -// Factorize material use -void GLC_3dxmlToWorld::factorizeMaterial(GLC_3DRep* pRep) -{ - //qDebug() << "GLC_3dxmlToWorld::factorizeMaterial"; - // Get the Set of materials of the rep - QSet repMaterialSet= pRep->materialSet(); - //! The hash table of rep material - QHash repMaterialHash; - // Construct the map of material String Hash and Id - QHash materialMap; - - { // Fill the map of material - QSet::const_iterator iMat= repMaterialSet.constBegin(); - while(repMaterialSet.constEnd() != iMat) - { - GLC_Material* pCurrentMat= *iMat; - materialMap.insert(QString::number(pCurrentMat->hashCode()), pCurrentMat->id()); - repMaterialHash.insert(pCurrentMat->id(), pCurrentMat); - ++iMat; - } - } - - // Make the factorization - QHash::iterator iMat= materialMap.begin(); - while (materialMap.constEnd() != iMat) - { - if (m_MaterialHash.contains(iMat.key())) - { - //qDebug() << "Replace Mat :" << iMat.key() << " " << iMat.value(); - pRep->replaceMaterial(iMat.value(), m_MaterialHash.value(iMat.key())); - } - else - { - //qDebug() << "Indert mat " << iMat.key() << " " << iMat.value(); - m_MaterialHash.insert(iMat.key(), repMaterialHash.value(iMat.value())); - } - ++iMat; - } - -} - void GLC_3dxmlToWorld::setRepresentationFileName(GLC_3DRep* pRep) { if (m_IsInArchive) @@ -2267,3 +2183,235 @@ void GLC_3dxmlToWorld::checkFileValidity(QIODevice* pIODevice) pIODevice->seek(0); } } + +void GLC_3dxmlToWorld::applyV4Attribute(GLC_StructOccurence* pOccurence, V4OccurenceAttrib* pV4OccurenceAttrib, QHash& instanceToIdHash) +{ + Q_ASSERT(pOccurence->hasChild() && !pV4OccurenceAttrib->m_Path.isEmpty()); + unsigned int id= pV4OccurenceAttrib->m_Path.takeFirst(); + + const int childCount= pOccurence->childCount(); + bool occurenceFound= false; + int i= 0; + while (!occurenceFound && (i < childCount)) + { + GLC_StructOccurence* pChildOccurence= pOccurence->child(i); + if (instanceToIdHash.contains(pChildOccurence->structInstance()) && (instanceToIdHash.value(pChildOccurence->structInstance()) == id)) + { + Q_ASSERT(id == instanceToIdHash.value(pChildOccurence->structInstance())); + occurenceFound= true; + + if (pV4OccurenceAttrib->m_Path.isEmpty()) + { + pChildOccurence->setVisibility(pV4OccurenceAttrib->m_IsVisible); + if (NULL != pV4OccurenceAttrib->m_pRenderProperties) + { + pChildOccurence->setRenderProperties(*(pV4OccurenceAttrib->m_pRenderProperties)); + } + if (pV4OccurenceAttrib->m_pMatrix != NULL) + { + pChildOccurence->makeFlexible(*(pV4OccurenceAttrib->m_pMatrix)); + } + } + else + { + applyV4Attribute(pChildOccurence, pV4OccurenceAttrib, instanceToIdHash); + } + } + else + { + ++i; + } + } + if (!occurenceFound) + { + qDebug() << "GLC_3dxmlToWorld::applyV4Attribute Occurrence not found" << id; + } + +} + +void GLC_3dxmlToWorld::loadRep(GLC_Mesh* pMesh) +{ + double masteLodAccuracy= readAttribute("accuracy", false).toDouble(); + int lodIndex= 1; + + bool masterLodFound= false; + bool vertexBufferFound= false; + + while (endElementNotReached(m_pStreamReader, "Rep") && endElementNotReached(m_pStreamReader, "Root")) + { + if ((QXmlStreamReader::StartElement == m_pStreamReader->tokenType())) + { + if (m_pStreamReader->name() == "SurfaceAttributes") + { + m_pCurrentMaterial= loadSurfaceAttributes(); + } + else if (m_pStreamReader->name() == "PolygonalLOD") + { + double accuracy= readAttribute("accuracy", true).toDouble(); + while (endElementNotReached(m_pStreamReader, "Faces")) + { + readNext(); + if ( m_pStreamReader->name() == "Face") + { + loadFace(pMesh, lodIndex, accuracy); + } + } + checkForXmlError("End of Faces not found"); + ++lodIndex; + } + else if (m_pStreamReader->name() == "Faces") + { + masterLodFound= true; + while (endElementNotReached(m_pStreamReader, "Faces")) + { + readNext(); + if ( m_pStreamReader->name() == "Face") + { + loadFace(pMesh, 0, masteLodAccuracy); + } + } + checkForXmlError("End of Faces not found"); + } + else if (m_pStreamReader->name() == "Edges") + { + while (endElementNotReached(m_pStreamReader, "Edges")) + { + readNext(); + if ( m_pStreamReader->name() == "Polyline") + { + loadPolyline(pMesh); + readNext(); + } + } + } + else if (m_pStreamReader->name() == "VertexBuffer") + { + vertexBufferFound= true; + loadVertexBuffer(pMesh); + } + else readNext(); + } + else + { + readNext(); + } + } + checkForXmlError("End of Rep or Root not found"); + + if (!masterLodFound || !vertexBufferFound) + { + QString message; + if (!masterLodFound) + { + message= QString("Master LOD not found in file ") + m_FileName; + } + else + { + message= QString("Vertex Buffer not found in file ") + m_FileName; + } + GLC_FileFormatException fileFormatException(message, m_FileName, GLC_FileFormatException::WrongFileFormat); + clear(); + throw(fileFormatException); + } + + +} + +void GLC_3dxmlToWorld::loadVertexBuffer(GLC_Mesh* pMesh) +{ + { + QString verticePosition= getContent(m_pStreamReader, "Positions").replace(',', ' '); + //qDebug() << "Position " << verticePosition; + checkForXmlError("Error while retrieving Position ContentVertexBuffer"); + // Load Vertice position + QTextStream verticeStream(&verticePosition); + QList verticeValues; + QString buff; + while ((!verticeStream.atEnd())) + { + verticeStream >> buff; + verticeValues.append(buff.toFloat()); + } + if ((verticeValues.size() % 3) == 0) + { + pMesh->addVertice(verticeValues.toVector()); + } + else + { + QString message(QString("Vertice buffer is not a multiple of 3 ") + m_CurrentFileName); + + QStringList stringList(message); + GLC_ErrorLog::addError(stringList); + + GLC_FileFormatException fileFormatException(message, m_FileName, GLC_FileFormatException::WrongFileFormat); + clear(); + throw(fileFormatException); + } + } + + { + QString normals= getContent(m_pStreamReader, "Normals").replace(',', ' '); + //qDebug() << "Normals " << normals; + checkForXmlError("Error while retrieving Normals values"); + // Load Vertice Normals + QTextStream normalsStream(&normals); + QList normalValues; + QString buff; + while ((!normalsStream.atEnd())) + { + normalsStream >> buff; + normalValues.append(buff.toFloat()); + } + if ((normalValues.size() % 3) == 0) + { + pMesh->addNormals(normalValues.toVector()); + } + else + { + QString message(QString("Normal buffer is not a multiple of 3 ") + m_CurrentFileName); + + QStringList stringList(message); + GLC_ErrorLog::addError(stringList); + + GLC_FileFormatException fileFormatException(message, m_FileName, GLC_FileFormatException::WrongFileFormat); + clear(); + throw(fileFormatException); + } + + } + // Try to find texture coordinate + while (endElementNotReached(m_pStreamReader, "VertexBuffer")) + { + if ((QXmlStreamReader::StartElement == m_pStreamReader->tokenType()) && (m_pStreamReader->name() == "TextureCoordinates")) + { + QString texels= getContent(m_pStreamReader, "TextureCoordinates").replace(',', ' '); + checkForXmlError("Error while retrieving Texture coordinates"); + QTextStream texelStream(&texels); + QList texelValues; + QString buff; + while ((!texelStream.atEnd())) + { + texelStream >> buff; + texelValues.append(buff.toFloat()); + } + + if ((texelValues.size() % 2) == 0) + { + pMesh->addTexels(texelValues.toVector()); + } + else + { + QString message(QString("Texel buffer is not a multiple of 2 ") + m_CurrentFileName); + + QStringList stringList(message); + GLC_ErrorLog::addError(stringList); + + GLC_FileFormatException fileFormatException(message, m_FileName, GLC_FileFormatException::WrongFileFormat); + clear(); + throw(fileFormatException); + } + } + readNext(); + } + checkForXmlError("VertexBuffer not found"); +} diff --git a/ground/openpilotgcs/src/libs/glc_lib/io/glc_3dxmltoworld.h b/ground/openpilotgcs/src/libs/glc_lib/io/glc_3dxmltoworld.h index 6bf002b54..1b2c094dd 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/io/glc_3dxmltoworld.h +++ b/ground/openpilotgcs/src/libs/glc_lib/io/glc_3dxmltoworld.h @@ -34,7 +34,7 @@ #include "../maths/glc_matrix4x4.h" #include "../sceneGraph/glc_3dviewinstance.h" -#include "glc_config.h" +#include "../glc_config.h" class GLC_World; class QGLContext; @@ -42,6 +42,7 @@ class QuaZip; class QuaZipFile; class GLC_StructReference; class GLC_StructInstance; +class GLC_StructOccurence; class GLC_Mesh; ////////////////////////////////////////////////////////////////////// @@ -78,15 +79,15 @@ class GLC_LIB_EXPORT GLC_3dxmlToWorld : public QObject QString m_AssociatedFile; }; - //! \class OccurenceAttrib - /*! \brief OccurenceAttrib : Specifique occurence attribute */ - struct OccurenceAttrib + //! \class V3OccurenceAttrib + /*! \brief V3OccurenceAttrib : Specifique occurence attribute */ + struct V3OccurenceAttrib { - inline OccurenceAttrib() + inline V3OccurenceAttrib() : m_IsVisible(true) , m_pRenderProperties(NULL) {} - inline ~OccurenceAttrib() + inline ~V3OccurenceAttrib() {delete m_pRenderProperties;} //! Visibility attribute @@ -95,6 +96,32 @@ class GLC_LIB_EXPORT GLC_3dxmlToWorld : public QObject GLC_RenderProperties* m_pRenderProperties; }; + //! \class V3OccurenceAttrib + /*! \brief V3OccurenceAttrib : Specifique occurence attribute */ + struct V4OccurenceAttrib + { + inline V4OccurenceAttrib() + : m_IsVisible(true) + , m_pRenderProperties(NULL) + , m_pMatrix(NULL) + , m_Path() + {} + inline ~V4OccurenceAttrib() + { + delete m_pRenderProperties; + delete m_pMatrix; + } + + //! Visibility attribute + bool m_IsVisible; + //! Render properties attribute + GLC_RenderProperties* m_pRenderProperties; + //! Relative matrix + GLC_Matrix4x4* m_pMatrix; + //! The path of this attrib + QList m_Path; + }; + typedef QHash ReferenceHash; typedef QHash InstanceOfHash; typedef QHash InstanceOfExtRefHash; @@ -111,7 +138,7 @@ class GLC_LIB_EXPORT GLC_3dxmlToWorld : public QObject ////////////////////////////////////////////////////////////////////// public: //! Default constructor - GLC_3dxmlToWorld(const QGLContext*); + GLC_3dxmlToWorld(); virtual ~GLC_3dxmlToWorld(); //@} @@ -159,6 +186,9 @@ private: //! Read the specified attribute QString readAttribute(const QString&, bool required= false); + //! Read the Header + void readHeader(); + //! Load the product structure void loadProductStructure(); @@ -190,9 +220,6 @@ private: //! Throw ecxeption if error occur void checkForXmlError(const QString&); - //! Load Level of detail - void loadLOD(GLC_Mesh*); - //! Load a face void loadFace(GLC_Mesh*, const int lod, double accuracy); @@ -211,11 +238,20 @@ private: //! Set the stream reader to the specified file bool setStreamReaderToFile(QString, bool test= false); - //! Load graphics properties - void loadGraphicsProperties(); + //! Load default view element + void loadDefaultView(); - //! Load default view property - void loadDefaultViewProperty(); + //! Load 3DXML V3 default view property + void loadV3DefaultViewProperty(); + + //! Load 3DXML V4 default view property + void loadV4DefaultViewProperty(); + + //! Return the occurence path of the current DefaultViewProperty + QList loadOccurencePath(); + + //! Load Graphics properties element + void loadGraphicProperties(V4OccurenceAttrib* pAttrib); //! Load the local representation void loadLocalRepresentations(); @@ -238,9 +274,6 @@ private: //! Try to construct a texture with the specified fileName GLC_Texture* loadTexture(QString); - //! Factorize material use - void factorizeMaterial(GLC_3DRep*); - //! Set fileName of the given 3DRep void setRepresentationFileName(GLC_3DRep* pRep); @@ -268,15 +301,21 @@ private: //! Check if the given file is binary void checkFileValidity(QIODevice* pIODevice); + //! Apply the given attribute to the right occurence from the given occurence + void applyV4Attribute(GLC_StructOccurence* pOccurence, V4OccurenceAttrib* pV4OccurenceAttrib, QHash& InstanceToIdHash); + + //! Load representation from 3DRep file + void loadRep(GLC_Mesh* pMesh); + + //! Load The 3DXML vertex buffer + void loadVertexBuffer(GLC_Mesh* pMesh); + //@} ////////////////////////////////////////////////////////////////////// // Private members ////////////////////////////////////////////////////////////////////// private: - //! OpenGL Context - const QGLContext* m_pQGLContext; - //! Xml Reader QXmlStreamReader* m_pStreamReader; @@ -349,8 +388,11 @@ private: //! The current file time and date QDateTime m_CurrentDateTime; - //! Hash table of occurence specific attributes - QHash m_OccurenceAttrib; + //! Hash table of occurence specific attributes for 3DXML V3 + QHash m_V3OccurenceAttribHash; + + //! List of occurence specific attributes for 3DXML V4 + QList m_V4OccurenceAttribList; //! bool get external ref 3D name bool m_GetExternalRef3DName; @@ -359,6 +401,9 @@ private: QList m_ByteArrayList; + //! Flag to know if the 3DXML is in version 3.x + bool m_IsVersion3; + }; QXmlStreamReader::TokenType GLC_3dxmlToWorld::readNext() diff --git a/ground/openpilotgcs/src/libs/glc_lib/io/glc_colladatoworld.cpp b/ground/openpilotgcs/src/libs/glc_lib/io/glc_colladatoworld.cpp index 6bc787274..c863be11b 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/io/glc_colladatoworld.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/io/glc_colladatoworld.cpp @@ -33,13 +33,12 @@ static int currentNodeId= 0; using namespace glcXmlUtil; // Default constructor -GLC_ColladaToWorld::GLC_ColladaToWorld(const QGLContext* pContext) +GLC_ColladaToWorld::GLC_ColladaToWorld() : QObject() , m_pWorld(NULL) -, m_pQGLContext(pContext) , m_pStreamReader(NULL) , m_FileName() -, m_pFile() +, m_pFile(NULL) , m_ImageFileHash() , m_MaterialLibHash() , m_SurfaceImageHash() @@ -68,7 +67,7 @@ GLC_ColladaToWorld::GLC_ColladaToWorld(const QGLContext* pContext) // Destructor GLC_ColladaToWorld::~GLC_ColladaToWorld() { - // Normal ends, wold has not to be deleted + // Normal ends, world has not to be deleted m_pWorld= NULL; clear(); } @@ -138,6 +137,10 @@ GLC_World* GLC_ColladaToWorld::CreateWorldFromCollada(QFile &file) m_pStreamReader->readNext(); } + + m_pFile->close(); + m_pFile= NULL; + // Link the textures to materials linkTexturesToMaterials(); @@ -233,10 +236,11 @@ void GLC_ColladaToWorld::clear() delete m_pWorld; m_pWorld= NULL; + delete m_pStreamReader; m_pStreamReader= NULL; - if (NULL != m_pFile) m_pFile->close(); + if (m_pFile != NULL) m_pFile->close(); m_pFile= NULL; m_ImageFileHash.clear(); @@ -1792,7 +1796,7 @@ void GLC_ColladaToWorld::linkTexturesToMaterials() if (QFileInfo(fullImageFileName).exists()) { m_ListOfAttachedFileName << fullImageFileName; - GLC_Texture* pTexture= new GLC_Texture(m_pQGLContext, fullImageFileName); + GLC_Texture* pTexture= new GLC_Texture(fullImageFileName); pCurrentMaterial->setTexture(pTexture); } else if (QFileInfo(m_FileName).absolutePath() != QFileInfo(fullImageFileName).absolutePath()) @@ -1802,7 +1806,7 @@ void GLC_ColladaToWorld::linkTexturesToMaterials() if (QFileInfo(fullImageFileName).exists()) { m_ListOfAttachedFileName << fullImageFileName; - GLC_Texture* pTexture= new GLC_Texture(m_pQGLContext, fullImageFileName); + GLC_Texture* pTexture= new GLC_Texture(fullImageFileName); pCurrentMaterial->setTexture(pTexture); } else diff --git a/ground/openpilotgcs/src/libs/glc_lib/io/glc_colladatoworld.h b/ground/openpilotgcs/src/libs/glc_lib/io/glc_colladatoworld.h index 70c3a2042..9139305e2 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/io/glc_colladatoworld.h +++ b/ground/openpilotgcs/src/libs/glc_lib/io/glc_colladatoworld.h @@ -161,7 +161,7 @@ private: ////////////////////////////////////////////////////////////////////// public: //! Default constructor - GLC_ColladaToWorld(const QGLContext*); + GLC_ColladaToWorld(); //! Destructor virtual ~GLC_ColladaToWorld(); @@ -368,9 +368,6 @@ private: //! The world to built GLC_World* m_pWorld; - //! OpenGL Context - const QGLContext* m_pQGLContext; - //! Xml Reader QXmlStreamReader* m_pStreamReader; diff --git a/ground/openpilotgcs/src/libs/glc_lib/io/glc_fileloader.cpp b/ground/openpilotgcs/src/libs/glc_lib/io/glc_fileloader.cpp index 0801cfb98..49e364a53 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/io/glc_fileloader.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/io/glc_fileloader.cpp @@ -42,8 +42,7 @@ ////////////////////////////////////////////////////////////////////// // Constructor ////////////////////////////////////////////////////////////////////// -GLC_FileLoader::GLC_FileLoader(const QGLContext *pContext) -: m_pQGLContext(pContext) +GLC_FileLoader::GLC_FileLoader() { } @@ -89,7 +88,7 @@ GLC_World GLC_FileLoader::createWorldFromFile(QFile &file, QStringList* pAttache GLC_World* pWorld= NULL; if (QFileInfo(file).suffix().toLower() == "obj") { - GLC_ObjToWorld objToWorld(m_pQGLContext); + GLC_ObjToWorld objToWorld; connect(&objToWorld, SIGNAL(currentQuantum(int)), this, SIGNAL(currentQuantum(int))); pWorld= objToWorld.CreateWorldFromObj(file); if (NULL != pAttachedFileName) @@ -111,7 +110,7 @@ GLC_World GLC_FileLoader::createWorldFromFile(QFile &file, QStringList* pAttache } else if (QFileInfo(file).suffix().toLower() == "3ds") { - GLC_3dsToWorld studioToWorld(m_pQGLContext); + GLC_3dsToWorld studioToWorld; connect(&studioToWorld, SIGNAL(currentQuantum(int)), this, SIGNAL(currentQuantum(int))); pWorld= studioToWorld.CreateWorldFrom3ds(file); if (NULL != pAttachedFileName) @@ -121,7 +120,7 @@ GLC_World GLC_FileLoader::createWorldFromFile(QFile &file, QStringList* pAttache } else if (QFileInfo(file).suffix().toLower() == "3dxml") { - GLC_3dxmlToWorld d3dxmlToWorld(m_pQGLContext); + GLC_3dxmlToWorld d3dxmlToWorld; connect(&d3dxmlToWorld, SIGNAL(currentQuantum(int)), this, SIGNAL(currentQuantum(int))); pWorld= d3dxmlToWorld.createWorldFrom3dxml(file, false); if (NULL != pAttachedFileName) @@ -131,7 +130,7 @@ GLC_World GLC_FileLoader::createWorldFromFile(QFile &file, QStringList* pAttache } else if (QFileInfo(file).suffix().toLower() == "dae") { - GLC_ColladaToWorld colladaToWorld(m_pQGLContext); + GLC_ColladaToWorld colladaToWorld; connect(&colladaToWorld, SIGNAL(currentQuantum(int)), this, SIGNAL(currentQuantum(int))); pWorld= colladaToWorld.CreateWorldFromCollada(file); if (NULL != pAttachedFileName) diff --git a/ground/openpilotgcs/src/libs/glc_lib/io/glc_fileloader.h b/ground/openpilotgcs/src/libs/glc_lib/io/glc_fileloader.h index e951fc527..199316b87 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/io/glc_fileloader.h +++ b/ground/openpilotgcs/src/libs/glc_lib/io/glc_fileloader.h @@ -56,7 +56,7 @@ class GLC_LIB_EXPORT GLC_FileLoader : public QObject ////////////////////////////////////////////////////////////////////// public: - GLC_FileLoader(const QGLContext*); + GLC_FileLoader(); virtual ~GLC_FileLoader(); //@} ////////////////////////////////////////////////////////////////////// @@ -79,8 +79,6 @@ public: // Private members ////////////////////////////////////////////////////////////////////// private: - //! OpenGL Context - const QGLContext* m_pQGLContext; }; #endif /*GLC_FILELOADER_H_*/ diff --git a/ground/openpilotgcs/src/libs/glc_lib/io/glc_objmtlloader.cpp b/ground/openpilotgcs/src/libs/glc_lib/io/glc_objmtlloader.cpp index 442dedd01..7a3ae5cda 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/io/glc_objmtlloader.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/io/glc_objmtlloader.cpp @@ -32,12 +32,11 @@ #include #include -GLC_ObjMtlLoader::GLC_ObjMtlLoader(const QGLContext *pContext, const QString& fileName) +GLC_ObjMtlLoader::GLC_ObjMtlLoader(const QString& fileName) : m_FileName(fileName) , m_pCurrentMaterial(NULL) , m_Materials() , m_LoadStatus() -, m_pQGLContext(pContext) , m_ListOfAttachedFileName() { } @@ -214,7 +213,7 @@ void GLC_ObjMtlLoader::extractTextureFileName(QString &ligne) { m_ListOfAttachedFileName << textureFileName; // Create the texture and assign it to current material - GLC_Texture *pTexture = new GLC_Texture(m_pQGLContext, textureFile); + GLC_Texture *pTexture = new GLC_Texture(textureFile); m_pCurrentMaterial->setTexture(pTexture); //qDebug() << "Texture File is : " << valueString; } diff --git a/ground/openpilotgcs/src/libs/glc_lib/io/glc_objmtlloader.h b/ground/openpilotgcs/src/libs/glc_lib/io/glc_objmtlloader.h index 18bd5f929..9c9f34c5d 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/io/glc_objmtlloader.h +++ b/ground/openpilotgcs/src/libs/glc_lib/io/glc_objmtlloader.h @@ -50,7 +50,7 @@ class GLC_LIB_EXPORT GLC_ObjMtlLoader //@{ ////////////////////////////////////////////////////////////////////// public: - GLC_ObjMtlLoader(const QGLContext*, const QString&); + GLC_ObjMtlLoader(const QString&); virtual ~GLC_ObjMtlLoader(); //@} @@ -123,9 +123,6 @@ private: //! the Load status QString m_LoadStatus; - //! OpenGL Context - const QGLContext *m_pQGLContext; - //! The list of attached file name QSet m_ListOfAttachedFileName; diff --git a/ground/openpilotgcs/src/libs/glc_lib/io/glc_objtoworld.cpp b/ground/openpilotgcs/src/libs/glc_lib/io/glc_objtoworld.cpp index d9694aa0e..66b6ec224 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/io/glc_objtoworld.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/io/glc_objtoworld.cpp @@ -40,10 +40,9 @@ ////////////////////////////////////////////////////////////////////// // Constructor ////////////////////////////////////////////////////////////////////// -GLC_ObjToWorld::GLC_ObjToWorld(const QGLContext *pContext) +GLC_ObjToWorld::GLC_ObjToWorld() : m_pWorld(NULL) , m_FileName() -, m_pQGLContext(pContext) , m_pMtlLoader(NULL) , m_CurrentLineNumber(0) , m_pCurrentObjMesh(NULL) @@ -131,7 +130,7 @@ GLC_World* GLC_ObjToWorld::CreateWorldFromObj(QFile &file) QString mtlLibFileName(getMtlLibFileName(mtlLibLine)); if (!mtlLibFileName.isEmpty()) { - m_pMtlLoader= new GLC_ObjMtlLoader(m_pQGLContext, mtlLibFileName); + m_pMtlLoader= new GLC_ObjMtlLoader(mtlLibFileName); if (!m_pMtlLoader->loadMaterials()) { delete m_pMtlLoader; diff --git a/ground/openpilotgcs/src/libs/glc_lib/io/glc_objtoworld.h b/ground/openpilotgcs/src/libs/glc_lib/io/glc_objtoworld.h index 33e4f37aa..5d8585e7d 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/io/glc_objtoworld.h +++ b/ground/openpilotgcs/src/libs/glc_lib/io/glc_objtoworld.h @@ -150,7 +150,7 @@ public: ////////////////////////////////////////////////////////////////////// public: - GLC_ObjToWorld(const QGLContext*); + GLC_ObjToWorld(); virtual ~GLC_ObjToWorld(); //@} @@ -227,9 +227,6 @@ private: //! The Obj File name QString m_FileName; - //! OpenGL Context - const QGLContext* m_pQGLContext; - //! the Obj Mtl loader GLC_ObjMtlLoader* m_pMtlLoader; diff --git a/ground/openpilotgcs/src/libs/glc_lib/io/glc_worldto3dxml.cpp b/ground/openpilotgcs/src/libs/glc_lib/io/glc_worldto3dxml.cpp index 979c84bee..9270a42c6 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/io/glc_worldto3dxml.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/io/glc_worldto3dxml.cpp @@ -23,8 +23,8 @@ #include "glc_worldto3dxml.h" // Quazip library -#include "../quazip/quazip.h" -#include "../quazip/quazipfile.h" +#include "../3rdparty/quazip/quazip.h" +#include "../3rdparty/quazip/quazipfile.h" #include "../glc_exception.h" #include "../geometry/glc_mesh.h" @@ -405,7 +405,7 @@ void GLC_WorldTo3dxml::exportAssemblyFromOccurence(const GLC_StructOccurence* pO GLC_3DViewInstance* pInstance= m_World.collection()->instanceHandle(pOccurence->id()); Q_ASSERT(NULL != pInstance); const bool isVisible= pInstance->isVisible(); - const bool isOverload= !isVisible || !pInstance->renderPropertiesHandle()->isDefault(); + const bool isOverload= !isVisible || !pInstance->renderPropertiesHandle()->isDefault() || pOccurence->isFlexible(); if (isOverload) { m_ListOfOverLoadedOccurence.append(pOccurence); @@ -419,22 +419,22 @@ QString GLC_WorldTo3dxml::matrixString(const GLC_Matrix4x4& matrix) QString resultMatrix; const QChar spaceChar(' '); // Rotation - resultMatrix+= QString::number(matrix.getData()[0]) + spaceChar; - resultMatrix+= QString::number(matrix.getData()[1]) + spaceChar; - resultMatrix+= QString::number(matrix.getData()[2]) + spaceChar; + resultMatrix+= QString::number(matrix.getData()[0], 'g', 16) + spaceChar; + resultMatrix+= QString::number(matrix.getData()[1], 'g', 16) + spaceChar; + resultMatrix+= QString::number(matrix.getData()[2], 'g', 16) + spaceChar; - resultMatrix+= QString::number(matrix.getData()[4]) + spaceChar; - resultMatrix+= QString::number(matrix.getData()[5]) + spaceChar; - resultMatrix+= QString::number(matrix.getData()[6]) + spaceChar; + resultMatrix+= QString::number(matrix.getData()[4], 'g', 16) + spaceChar; + resultMatrix+= QString::number(matrix.getData()[5], 'g', 16) + spaceChar; + resultMatrix+= QString::number(matrix.getData()[6], 'g', 16) + spaceChar; - resultMatrix+= QString::number(matrix.getData()[8]) + spaceChar; - resultMatrix+= QString::number(matrix.getData()[9]) + spaceChar; - resultMatrix+= QString::number(matrix.getData()[10]) + spaceChar; + resultMatrix+= QString::number(matrix.getData()[8], 'g', 16) + spaceChar; + resultMatrix+= QString::number(matrix.getData()[9], 'g', 16) + spaceChar; + resultMatrix+= QString::number(matrix.getData()[10], 'g', 16) + spaceChar; // Translation - resultMatrix+= QString::number(matrix.getData()[12]) + spaceChar; - resultMatrix+= QString::number(matrix.getData()[13]) + spaceChar; - resultMatrix+= QString::number(matrix.getData()[14]); + resultMatrix+= QString::number(matrix.getData()[12], 'g', 16) + spaceChar; + resultMatrix+= QString::number(matrix.getData()[13], 'g', 16) + spaceChar; + resultMatrix+= QString::number(matrix.getData()[14], 'g', 16); return resultMatrix; } @@ -772,7 +772,7 @@ void GLC_WorldTo3dxml::writeMaterial(const GLC_Material* pMaterial) } else { - materialName= pMaterial->name(); + materialName= symplifyName(pMaterial->name()); } @@ -1105,17 +1105,30 @@ void GLC_WorldTo3dxml::addImageTextureTo3dxml(const QImage& image, const QString } } -QString GLC_WorldTo3dxml::xmlFileName(const QString& fileName) +QString GLC_WorldTo3dxml::xmlFileName(QString fileName) { - QString newName; - if (!m_3dxmlFileSet.contains(fileName)) + QString prefix; + if (fileName.contains("urn:3DXML:")) { + prefix= "urn:3DXML:"; + fileName.remove(prefix); + } + + fileName= symplifyName(fileName); + + + + QString newName; + if (!m_3dxmlFileSet.contains(prefix + fileName)) + { + fileName.prepend(prefix); m_3dxmlFileSet << fileName; newName= fileName; } else { newName= QFileInfo(fileName).completeBaseName() + QString::number(++m_FileNameIncrement) + '.' + QFileInfo(fileName).suffix(); + newName.prepend(prefix); } return newName; } @@ -1142,40 +1155,87 @@ void GLC_WorldTo3dxml::writeExtensionAttributes(GLC_Attributes* pAttributes) void GLC_WorldTo3dxml::writeOccurenceDefaultViewProperty(const GLC_StructOccurence* pOccurence) { + QList path= instancePath(pOccurence); + GLC_3DViewInstance* pInstance= m_World.collection()->instanceHandle(pOccurence->id()); Q_ASSERT(NULL != pInstance); const bool isVisible= pOccurence->isVisible(); - const unsigned int occurrenceId= pOccurence->occurenceNumber(); m_pOutStream->writeStartElement("DefaultViewProperty"); - m_pOutStream->writeTextElement("OccurenceId", QString::number(occurrenceId)); - m_pOutStream->writeStartElement("GraphicProperties"); - m_pOutStream->writeAttribute("xsi:type", "GraphicPropertiesType"); - if (! isVisible) + m_pOutStream->writeStartElement("OccurenceId"); + const QString prefix= "urn:3DXML:" + QFileInfo(m_FileName).fileName() + "#"; + const int pathSize= path.size(); + for (int i= 0; i < pathSize; ++i) { - m_pOutStream->writeStartElement("GeneralAttributes"); - m_pOutStream->writeAttribute("xsi:type", "GeneralAttributesType"); - m_pOutStream->writeAttribute("visible", "false"); - m_pOutStream->writeAttribute("selectable", "true"); - m_pOutStream->writeEndElement(); // GeneralAttributes + m_pOutStream->writeTextElement("id", prefix + QString::number(path.at(i))); } - if (!pInstance->renderPropertiesHandle()->isDefault()) + m_pOutStream->writeEndElement(); // OccurenceId + + if (pOccurence->isFlexible()) { - const GLC_RenderProperties* pProperties= pInstance->renderPropertiesHandle(); - if (pProperties->overwriteTransparency() != -1.0f) + m_pOutStream->writeTextElement("RelativePosition", matrixString(pOccurence->occurrenceRelativeMatrix())); + } + + if (!isVisible || !pInstance->renderPropertiesHandle()->isDefault()) + { + qDebug() << "(!isVisible || !pInstance->renderPropertiesHandle()->isDefault())"; + m_pOutStream->writeStartElement("GraphicProperties"); + m_pOutStream->writeAttribute("xsi:type", "GraphicPropertiesType"); + if (! isVisible) { - m_pOutStream->writeStartElement("SurfaceAttributes"); - m_pOutStream->writeAttribute("xsi:type", "SurfaceAttributesType"); - m_pOutStream->writeStartElement("Color"); - m_pOutStream->writeAttribute("xsi:type", "RGBAColorType"); - m_pOutStream->writeAttribute("red", "-1"); - m_pOutStream->writeAttribute("green", "-1"); - m_pOutStream->writeAttribute("blue", "-1"); - m_pOutStream->writeAttribute("alpha", QString::number(pProperties->overwriteTransparency())); - m_pOutStream->writeEndElement(); // Color - m_pOutStream->writeEndElement(); // SurfaceAttributes + m_pOutStream->writeStartElement("GeneralAttributes"); + m_pOutStream->writeAttribute("xsi:type", "GeneralAttributesType"); + m_pOutStream->writeAttribute("visible", "false"); + m_pOutStream->writeAttribute("selectable", "true"); + m_pOutStream->writeEndElement(); // GeneralAttributes } + if (!pInstance->renderPropertiesHandle()->isDefault()) + { + const GLC_RenderProperties* pProperties= pInstance->renderPropertiesHandle(); + if ((pProperties->renderingMode() == glc::OverwriteTransparency)) + { + m_pOutStream->writeStartElement("SurfaceAttributes"); + m_pOutStream->writeAttribute("xsi:type", "SurfaceAttributesType"); + m_pOutStream->writeStartElement("Color"); + m_pOutStream->writeAttribute("xsi:type", "RGBAColorType"); + m_pOutStream->writeAttribute("red", "-1"); + m_pOutStream->writeAttribute("green", "-1"); + m_pOutStream->writeAttribute("blue", "-1"); + m_pOutStream->writeAttribute("alpha", QString::number(pProperties->overwriteTransparency())); + m_pOutStream->writeEndElement(); // Color + m_pOutStream->writeEndElement(); // SurfaceAttributes + } + else if ((pProperties->renderingMode() == glc::OverwriteTransparencyAndMaterial)) + { + GLC_Material* pMaterial= pProperties->overwriteMaterial(); + m_pOutStream->writeStartElement("SurfaceAttributes"); + m_pOutStream->writeAttribute("xsi:type", "SurfaceAttributesType"); + m_pOutStream->writeStartElement("Color"); + m_pOutStream->writeAttribute("xsi:type", "RGBAColorType"); + m_pOutStream->writeAttribute("red", QString::number(pMaterial->diffuseColor().redF())); + m_pOutStream->writeAttribute("green", QString::number(pMaterial->diffuseColor().greenF())); + m_pOutStream->writeAttribute("blue", QString::number(pMaterial->diffuseColor().blueF())); + m_pOutStream->writeAttribute("alpha", QString::number(pProperties->overwriteTransparency())); + m_pOutStream->writeEndElement(); // Color + m_pOutStream->writeEndElement(); // SurfaceAttributes + } + else if ((pProperties->renderingMode() == glc::OverwriteMaterial)) + { + GLC_Material* pMaterial= pProperties->overwriteMaterial(); + m_pOutStream->writeStartElement("SurfaceAttributes"); + m_pOutStream->writeAttribute("xsi:type", "SurfaceAttributesType"); + m_pOutStream->writeStartElement("Color"); + m_pOutStream->writeAttribute("xsi:type", "RGBAColorType"); + m_pOutStream->writeAttribute("red", QString::number(pMaterial->diffuseColor().redF())); + m_pOutStream->writeAttribute("green", QString::number(pMaterial->diffuseColor().greenF())); + m_pOutStream->writeAttribute("blue", QString::number(pMaterial->diffuseColor().blueF())); + m_pOutStream->writeAttribute("alpha", QString::number(pMaterial->opacity())); + m_pOutStream->writeEndElement(); // Color + m_pOutStream->writeEndElement(); // SurfaceAttributes + } + + } + m_pOutStream->writeEndElement(); // GraphicProperties } - m_pOutStream->writeEndElement(); // GraphicProperties m_pOutStream->writeEndElement(); // DefaultViewProperty } @@ -1191,3 +1251,32 @@ bool GLC_WorldTo3dxml::continu() } return continuValue; } + +QString GLC_WorldTo3dxml::symplifyName(QString name) +{ + const int nameSize= name.size(); + for (int i= 0; i < nameSize; ++i) + { + if (!name.at(i).isLetterOrNumber() && (name.at(i) != '.')) + { + name.replace(i, 1, '_'); + } + } + + return name; +} + +QList GLC_WorldTo3dxml::instancePath(const GLC_StructOccurence* pOccurence) +{ + QList path; + if (!pOccurence->isOrphan()) + { + GLC_StructInstance* pInstance= pOccurence->structInstance(); + Q_ASSERT(m_InstanceToIdHash.contains(pInstance)); + path.prepend(m_InstanceToIdHash.value(pInstance)); + QList subPath(instancePath(pOccurence->parent())); + subPath.append(path); + path= subPath; + } + return path; +} diff --git a/ground/openpilotgcs/src/libs/glc_lib/io/glc_worldto3dxml.h b/ground/openpilotgcs/src/libs/glc_lib/io/glc_worldto3dxml.h index d9721f556..c9344e2a9 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/io/glc_worldto3dxml.h +++ b/ground/openpilotgcs/src/libs/glc_lib/io/glc_worldto3dxml.h @@ -166,7 +166,7 @@ private: void addImageTextureTo3dxml(const QImage& image, const QString& fileName); //! Transform the given name to the 3DXML name (no double) - QString xmlFileName(const QString& fileName); + QString xmlFileName(QString fileName); //! Write extension attributes to 3DXML void writeExtensionAttributes(GLC_Attributes* pAttributes); @@ -177,6 +177,12 @@ private: //! return true if export must continu bool continu(); + //! Return the simplified name of the given name + QString symplifyName(QString name); + + //! Return the path of the given occurence + QList instancePath(const GLC_StructOccurence* pOccurence); + //@} ////////////////////////////////////////////////////////////////////// diff --git a/ground/openpilotgcs/src/libs/glc_lib/io/glc_xmlutil.h b/ground/openpilotgcs/src/libs/glc_lib/io/glc_xmlutil.h old mode 100644 new mode 100755 diff --git a/ground/openpilotgcs/src/libs/glc_lib/maths/glc_geomtools.cpp b/ground/openpilotgcs/src/libs/glc_lib/maths/glc_geomtools.cpp index 8d88aa754..a9b2ace04 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/maths/glc_geomtools.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/maths/glc_geomtools.cpp @@ -27,6 +27,9 @@ #include + +double glc::comparedPrecision= glc::defaultPrecision; + ////////////////////////////////////////////////////////////////////// //Tools Functions ////////////////////////////////////////////////////////////////////// @@ -520,12 +523,112 @@ bool glc::lineIntersectPlane(const GLC_Line3d& line, const GLC_Plane& plane, GLC GLC_Point3d glc::project(const GLC_Point3d& point, const GLC_Line3d& line) { - // Create the plane from the point with normal define by the line direction - const GLC_Plane plane(line.direction().normalize(), point); - GLC_Point3d intersection; - const bool intersect= lineIntersectPlane(line, plane, &intersection); - Q_ASSERT(intersect == true); - return intersection; + const GLC_Vector3d lineDirection(line.direction().normalize()); + double t= lineDirection * (point - line.startingPoint()); + GLC_Point3d projectedPoint= line.startingPoint() + (t * lineDirection); + return projectedPoint; } +double glc::pointLineDistance(const GLC_Point3d& point, const GLC_Line3d& line) +{ + const GLC_Vector3d lineDirection(line.direction().normalize()); + double t= lineDirection * (point - line.startingPoint()); + GLC_Point3d projectedPoint= line.startingPoint() + (t * lineDirection); + return (point - projectedPoint).length(); +} +bool glc::pointsAreCollinear(const GLC_Point3d& p1, const GLC_Point3d& p2, const GLC_Point3d& p3) +{ + bool subject= false; + if (compare(p1, p2) || compare(p1, p3) || compare(p2, p3)) + { + subject= true; + } + else + { + GLC_Vector3d p1p2= (p2 - p1).setLength(1.0); + GLC_Vector3d p2p3= (p3 - p2).setLength(1.0); + subject= (compare(p1p2, p2p3) || compare(p1p2, p2p3.inverted())); + } + return subject; +} + +bool glc::compare(double p1, double p2) +{ + return qAbs(p1 - p2) <= comparedPrecision; +} + +bool glc::compareAngle(double p1, double p2) +{ + const double anglePrecision= toRadian(comparedPrecision); + return qAbs(p1 - p2) <= anglePrecision; +} + +bool glc::compare(const GLC_Vector3d& v1, const GLC_Vector3d& v2) +{ + bool compareResult= (qAbs(v1.x() - v2.x()) <= comparedPrecision); + compareResult= compareResult && (qAbs(v1.y() - v2.y()) <= comparedPrecision); + compareResult= compareResult && (qAbs(v1.z() - v2.z()) <= comparedPrecision); + + return compareResult; +} + +bool glc::compare(const GLC_Vector2d& v1, const GLC_Vector2d& v2) +{ + bool compareResult= (qAbs(v1.getX() - v2.getX()) <= comparedPrecision); + return compareResult && (qAbs(v1.getY() - v2.getY()) <= comparedPrecision); +} + +bool glc::compare(const QPointF& v1, const QPointF& v2) +{ + bool compareResult= (qAbs(v1.x() - v2.x()) <= comparedPrecision); + return compareResult && (qAbs(v1.y() - v2.y()) <= comparedPrecision); +} + +bool glc::pointInPolygon(const GLC_Point2d& point, const QList& polygon) +{ + const int polygonSize= polygon.size(); + bool inside= false; + int i= 0; + int j= polygonSize - 1; + + while (i < polygonSize) + { + const GLC_Point2d point0= polygon.at(i); + const GLC_Point2d point1= polygon.at(j); + if (point.getY() < point1.getY()) + { + //point1 above ray + if (point0.getY() <= point.getY()) + { + //point2 on or below ray + const double val1= (point.getY() - point0.getY()) * (point1.getX() - point0.getX()); + const double val2= (point.getX() - point0.getX()) * (point1.getY() - point0.getY()); + if (val1 > val2) inside= !inside; + } + } + else if (point.getY() < point0.getY()) + { + // point 1 on or below ray, point0 above ray + const double val1= (point.getY() - point0.getY()) * (point1.getX() - point0.getX()); + const double val2= (point.getX() - point0.getX()) * (point1.getY() - point0.getY()); + if (val1 < val2) inside= !inside; + } + j= i; + ++i; + } + return inside; +} + +double glc::zeroTo2PIAngle(double angle) +{ + if (qFuzzyCompare(fabs(angle), glc::PI)) + { + angle= glc::PI; + } + else if (angle < 0) + { + angle= (2.0 * glc::PI) + angle; + } + return angle; +} diff --git a/ground/openpilotgcs/src/libs/glc_lib/maths/glc_geomtools.h b/ground/openpilotgcs/src/libs/glc_lib/maths/glc_geomtools.h index 94f66ed66..0f8628ab9 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/maths/glc_geomtools.h +++ b/ground/openpilotgcs/src/libs/glc_lib/maths/glc_geomtools.h @@ -27,6 +27,8 @@ #include #include +#include + #include "glc_vector3d.h" #include "glc_line3d.h" #include "glc_plane.h" @@ -37,7 +39,8 @@ namespace glc { - + const double defaultPrecision= 0.01; + extern double comparedPrecision; ////////////////////////////////////////////////////////////////////// /*! \name Tools Functions*/ //@{ @@ -94,10 +97,33 @@ namespace glc //! Return the midpoint of the two given points inline GLC_Point3d midPoint(const GLC_Point3d& point1, const GLC_Point3d point2) - { - return point1 + (point2 - point1) * 0.5; - } + {return point1 + (point2 - point1) * 0.5;} + //! Return the perpendicular 2D vector of the given 2D vector + inline GLC_Vector2d perpVector(const GLC_Vector2d& vect) + {return GLC_Vector2d(-vect.getY(), vect.getX());} + + //! Return the distance between the given point and line + GLC_LIB_EXPORT double pointLineDistance(const GLC_Point3d& point, const GLC_Line3d& line); + + //! Return true if the given 3 points are collinear + GLC_LIB_EXPORT bool pointsAreCollinear(const GLC_Point3d& p1, const GLC_Point3d& p2, const GLC_Point3d& p3); + + GLC_LIB_EXPORT bool compare(double p1, double p2); + + GLC_LIB_EXPORT bool compareAngle(double p1, double p2); + + GLC_LIB_EXPORT bool compare(const GLC_Vector3d& v1, const GLC_Vector3d& v2); + + GLC_LIB_EXPORT bool compare(const GLC_Vector2d& v1, const GLC_Vector2d& v2); + + GLC_LIB_EXPORT bool compare(const QPointF& v1, const QPointF& v2); + + //! Return true if the given 2d point is inside the given polygon + GLC_LIB_EXPORT bool pointInPolygon(const GLC_Point2d& point, const QList& polygon); + + //! Return the angle from 0 to 2PI from an given angle from -PI to PI + GLC_LIB_EXPORT double zeroTo2PIAngle(double angle); //@} diff --git a/ground/openpilotgcs/src/libs/glc_lib/maths/glc_interpolator.cpp b/ground/openpilotgcs/src/libs/glc_lib/maths/glc_interpolator.cpp index 04912e448..e42d170a8 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/maths/glc_interpolator.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/maths/glc_interpolator.cpp @@ -25,121 +25,99 @@ #include "glc_interpolator.h" using namespace glc; -////////////////////////////////////////////////////////////////////// -// Construction/Destruction -////////////////////////////////////////////////////////////////////// -// Contructeur par défaut Interpolation Linéaire GLC_Interpolator::GLC_Interpolator() : m_InterpolType(INTERPOL_LINEAIRE) -, m_nNbrPas(1) +, m_StepCount(1) { } ////////////////////////////////////////////////////////////////////// -// Fonction Set +// Set Function ////////////////////////////////////////////////////////////////////// -// Défini la matrice d'interpolation void GLC_Interpolator::SetInterpolMat(int NbrPas, const GLC_Vector3d &VectDepart, const GLC_Vector3d &VectArrive , INTERPOL_TYPE Interpolation) { - // Mise à jour des données membre m_InterpolType= Interpolation; - if (!NbrPas) - { - //TRACE("GLC_Interpolator::SetInterpolMat -> NbrPas == 0 \n"); - } - else m_nNbrPas= NbrPas; + if (NbrPas != 0) + m_StepCount= NbrPas; + + m_StartPoint= VectDepart; + m_EndPoint= VectArrive; - m_VectDepart= VectDepart; - m_VectArrive= VectArrive; - // Calcul de la matrice d'interpolation CalcInterpolMat(); } -// Type d'interpolation + void GLC_Interpolator::SetType(INTERPOL_TYPE Interpolation) { if (m_InterpolType != Interpolation) { m_InterpolType= Interpolation; - // Calcul de la matrice d'interpolation + CalcInterpolMat(); } } -// Nombre de pas + void GLC_Interpolator::SetNbrPas(int NbrPas) { - if (!NbrPas) - { - //TRACE("GLC_Interpolator::SetNbrPas -> NbrPas == 0 \n"); - return; - } - if (m_nNbrPas != NbrPas) + if ((NbrPas != 0) && (m_StepCount != NbrPas)) { - m_nNbrPas= NbrPas; - // Calcul de la matrice d'interpolation + m_StepCount= NbrPas; + CalcInterpolMat(); } } -// Vecteur d'arrivée et de depart + void GLC_Interpolator::SetVecteurs(const GLC_Vector3d &VectDepart, const GLC_Vector3d &VectArrive) { - m_VectDepart= VectDepart; - m_VectArrive= VectArrive; + m_StartPoint= VectDepart; + m_EndPoint= VectArrive; + - // Calcul de la matrice d'interpolation CalcInterpolMat(); } ////////////////////////////////////////////////////////////////////// -// Fonction Get +// Private sevices functions ////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////// -// Fonctions de Service privée -////////////////////////////////////////////////////////////////////// -// Calcul La matrice d'interpolation bool GLC_Interpolator::CalcInterpolMat(void) { - // Verifie que les vecteur d'arrivé et de départ sont différent - if (m_VectDepart == m_VectArrive) + + if (m_StartPoint != m_EndPoint) { - //TRACE("GLC_Interpolator::CalcInterpolMat : Depart == Arrive\n"); - return false; + switch (m_InterpolType) + { + case INTERPOL_LINEAIRE: + return CalcInterpolLineaireMat(); + break; + + case INTERPOL_ANGULAIRE: + return CalcInterpolAngulaireMat(); + break; + + case INTERPOL_HOMOTETIE: + return false; + break; + + default: + return false; + } } + else return false; - switch (m_InterpolType) - { - case INTERPOL_LINEAIRE: - return CalcInterpolLineaireMat(); - break; - - case INTERPOL_ANGULAIRE: - return CalcInterpolAngulaireMat(); - break; - - case INTERPOL_HOMOTETIE: - return false; - break; - - default: - //TRACE("GLC_Interpolator::CalcInterpolMat : Type d'interpolation non valide\n"); - return false; - } } -// Calcul la matrice d'interpolation linéaire + bool GLC_Interpolator::CalcInterpolLineaireMat(void) { - // Calcul la matrice de translation - const GLC_Vector3d VectTrans= (m_VectArrive - m_VectDepart) * (1.0 / m_nNbrPas); + const GLC_Vector3d VectTrans= (m_EndPoint - m_StartPoint) * (1.0 / m_StepCount); if(VectTrans.isNull()) { - //TRACE("GLC_Interpolator::CalcInterpolLineaireMat -> Translation NULL\n"); m_InterpolMat.setToIdentity(); return false; } @@ -150,17 +128,15 @@ bool GLC_Interpolator::CalcInterpolLineaireMat(void) } } -// Calcul la matrice d'interpolation angulaire bool GLC_Interpolator::CalcInterpolAngulaireMat(void) { - // Calcul de l'axe de rotation - const GLC_Vector3d AxeRot(m_VectDepart ^ m_VectArrive); - // Calcul de l'angle entre les vecteurs - const double Angle= m_VectArrive.angleWithVect(m_VectDepart) / m_nNbrPas; - // Calcul de la matrice de rotation + + const GLC_Vector3d AxeRot(m_StartPoint ^ m_EndPoint); + + const double Angle= m_EndPoint.angleWithVect(m_StartPoint) / m_StepCount; + if (qFuzzyCompare(Angle, 0.0)) { - //TRACE("GLC_Interpolator::CalcInterpolAngulaireMat -> Rotation NULL\n"); m_InterpolMat.setToIdentity(); return false; } diff --git a/ground/openpilotgcs/src/libs/glc_lib/maths/glc_interpolator.h b/ground/openpilotgcs/src/libs/glc_lib/maths/glc_interpolator.h index f0cec314f..83db355cf 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/maths/glc_interpolator.h +++ b/ground/openpilotgcs/src/libs/glc_lib/maths/glc_interpolator.h @@ -47,66 +47,65 @@ enum INTERPOL_TYPE class GLC_LIB_EXPORT GLC_Interpolator { -////////////////////////////////////////////////////////////////////// -// Construction -////////////////////////////////////////////////////////////////////// public: - // Contructeur par défaut Interpolation Linéaire + //! Default linear interpolation constructor GLC_Interpolator(); ////////////////////////////////////////////////////////////////////// -// Fonctions Set +// Set Function ////////////////////////////////////////////////////////////////////// public: - // Défini la matrice d'interpolation + //! Set interpolation matrix void SetInterpolMat(int NbrPas, const GLC_Vector3d &VectDepart, const GLC_Vector3d &VectArrive , INTERPOL_TYPE Interpolation = INTERPOL_LINEAIRE); - // Type d'interpolation + + //! Set interpolation type void SetType(INTERPOL_TYPE Interpolation); - // Nombre de pas + + // Number of step void SetNbrPas(int NbrPas); - // Vecteur d'arrivée et de depart + + //! Set start and end vector void SetVecteurs(const GLC_Vector3d &VectDepart, const GLC_Vector3d &VectArrive); ////////////////////////////////////////////////////////////////////// -// Fonctions Get +// Get Function ////////////////////////////////////////////////////////////////////// public: - // Retourne la matrice d'interpolation - GLC_Matrix4x4 GetInterpolMat(void) const - { - return m_InterpolMat; - } + //! Return th interpolation matrix + inline GLC_Matrix4x4 GetInterpolMat(void) const + {return m_InterpolMat;} ////////////////////////////////////////////////////////////////////// -// Fonctions de Service privée +// Private services functions ////////////////////////////////////////////////////////////////////// private: - // Calcul La matrice d'interpolation + //! Compute interpolation matrix bool CalcInterpolMat(void); - // Calcul la matrice d'interpolation linéaire + //! Compute linear interolation matrix bool CalcInterpolLineaireMat(void); - // Calcul la matrice d'interpolation angulaire + //! Compute angular interpolation matrix bool CalcInterpolAngulaireMat(void); ////////////////////////////////////////////////////////////////////// // Membres privés ////////////////////////////////////////////////////////////////////// private: - // Vecteur de départ - GLC_Vector3d m_VectDepart; - // Vecteur d'arriver - GLC_Vector3d m_VectArrive; + //! Start Point + GLC_Point3d m_StartPoint; - // Type d'interpolation courante + //! End Point + GLC_Point3d m_EndPoint; + + //! Interpolation type INTERPOL_TYPE m_InterpolType; - // Nombre de pas d'interpolation - int m_nNbrPas; + //! Interpolation step count + int m_StepCount; - // Matrice d'interpolation + //! Interpolation matrix GLC_Matrix4x4 m_InterpolMat; }; diff --git a/ground/openpilotgcs/src/libs/glc_lib/maths/glc_line3d.h b/ground/openpilotgcs/src/libs/glc_lib/maths/glc_line3d.h index 835c1debd..85a0e123a 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/maths/glc_line3d.h +++ b/ground/openpilotgcs/src/libs/glc_lib/maths/glc_line3d.h @@ -26,7 +26,7 @@ #include "glc_vector3d.h" -#include "glc_config.h" +#include "../glc_config.h" ////////////////////////////////////////////////////////////////////// //! \class GLC_Line3d diff --git a/ground/openpilotgcs/src/libs/glc_lib/maths/glc_matrix4x4.h b/ground/openpilotgcs/src/libs/glc_lib/maths/glc_matrix4x4.h index 8faa2d2b9..651657166 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/maths/glc_matrix4x4.h +++ b/ground/openpilotgcs/src/libs/glc_lib/maths/glc_matrix4x4.h @@ -278,17 +278,6 @@ the matrix : }; -//! Return the determinant of the given Matrix 3X3 -inline double getDeterminant3x3(const double *Mat3x3) -{ - double Determinant; - - Determinant= Mat3x3[0] * ( Mat3x3[4] * Mat3x3[8] - Mat3x3[7] * Mat3x3[5]); - Determinant+= - Mat3x3[3] * ( Mat3x3[1] * Mat3x3[8] - Mat3x3[7] * Mat3x3[2]); - Determinant+= Mat3x3[6] * ( Mat3x3[1] * Mat3x3[5] - Mat3x3[4] * Mat3x3[2]); - - return Determinant; -} ////////////////////////////////////////////////////////////////////// // Constructor/Destructor diff --git a/ground/openpilotgcs/src/libs/glc_lib/maths/glc_vector2d.h b/ground/openpilotgcs/src/libs/glc_lib/maths/glc_vector2d.h old mode 100644 new mode 100755 index 0840db1e0..a85baf6e7 --- a/ground/openpilotgcs/src/libs/glc_lib/maths/glc_vector2d.h +++ b/ground/openpilotgcs/src/libs/glc_lib/maths/glc_vector2d.h @@ -26,10 +26,12 @@ #define GLC_VECTOR2D_H_ #include +#include + #include "glc_utils_maths.h" #include "glc_vector2df.h" -#include "glc_config.h" +#include "../glc_config.h" ////////////////////////////////////////////////////////////////////// // definition global @@ -51,7 +53,7 @@ class GLC_LIB_EXPORT GLC_Vector2d /*! Overload unary "-" operator*/ inline friend GLC_Vector2d operator - (const GLC_Vector2d &Vect) { - return GLC_Vector2d(-Vect.dVecteur[0], -Vect.dVecteur[1]); + return GLC_Vector2d(-Vect.m_Vector[0], -Vect.m_Vector[1]); } @@ -67,15 +69,15 @@ public: */ inline GLC_Vector2d() { - dVecteur[0]= 0.0; - dVecteur[1]= 0.0; + m_Vector[0]= 0.0; + m_Vector[1]= 0.0; } /*! Standard constructor With x, y = 0.0*/ inline GLC_Vector2d(const double &dX, const double &dY) { - dVecteur[0]= dX; - dVecteur[1]= dY; + m_Vector[0]= dX; + m_Vector[1]= dY; } /*! Recopy constructor @@ -86,9 +88,14 @@ public: */ inline GLC_Vector2d(const GLC_Vector2d &Vect) { - dVecteur[0]= Vect.dVecteur[0]; - dVecteur[1]= Vect.dVecteur[1]; + m_Vector[0]= Vect.m_Vector[0]; + m_Vector[1]= Vect.m_Vector[1]; } + + //! Return the QPointF of this GLC_Vector + inline QPointF toQPointF() const + {return QPointF(m_Vector[0], m_Vector[1]);} + //@} ////////////////////////////////////////////////////////////////////// /*! @name Operator Overload */ @@ -99,7 +106,7 @@ public: /*! Overload binary "+" operator*/ inline GLC_Vector2d operator + (const GLC_Vector2d &Vect) const { - GLC_Vector2d VectResult(dVecteur[0] + Vect.dVecteur[0], dVecteur[1] + Vect.dVecteur[1]); + GLC_Vector2d VectResult(m_Vector[0] + Vect.m_Vector[0], m_Vector[1] + Vect.m_Vector[1]); return VectResult; } @@ -107,8 +114,8 @@ public: /*! Overload "=" operator*/ inline GLC_Vector2d& operator = (const GLC_Vector2d &Vect) { - dVecteur[0]= Vect.dVecteur[0]; - dVecteur[1]= Vect.dVecteur[1]; + m_Vector[0]= Vect.m_Vector[0]; + m_Vector[1]= Vect.m_Vector[1]; return *this; } @@ -116,8 +123,8 @@ public: /*! Overload "=" operator*/ inline GLC_Vector2d& operator = (const GLC_Vector2df &Vect) { - dVecteur[0]= static_cast(Vect.vector[0]); - dVecteur[1]= static_cast(Vect.vector[1]); + m_Vector[0]= static_cast(Vect.vector[0]); + m_Vector[1]= static_cast(Vect.vector[1]); return *this; } @@ -134,7 +141,7 @@ public: /*! Overload binary "-" operator*/ inline GLC_Vector2d operator - (const GLC_Vector2d &Vect) const { - GLC_Vector2d VectResult(dVecteur[0] - Vect.dVecteur[0], dVecteur[1] - Vect.dVecteur[1]); + GLC_Vector2d VectResult(m_Vector[0] - Vect.m_Vector[0], m_Vector[1] - Vect.m_Vector[1]); return VectResult; } @@ -149,27 +156,27 @@ public: /*! Overload dot product "^" operator*/ inline double operator ^ (const GLC_Vector2d &Vect) const { - return dVecteur[0] * Vect.dVecteur[1] - dVecteur[1] * Vect.dVecteur[0]; + return m_Vector[0] * Vect.m_Vector[1] - m_Vector[1] * Vect.m_Vector[0]; } /*! Overload scalar product "*" operator between 2 vector*/ inline double operator * (const GLC_Vector2d &Vect) const { - return dVecteur[0] * Vect.dVecteur[0] + dVecteur[1] * Vect.dVecteur[1]; + return m_Vector[0] * Vect.m_Vector[0] + m_Vector[1] * Vect.m_Vector[1]; } /*! Overload scalar product "*" operator between 1 vector and one scalar*/ inline GLC_Vector2d operator * (double Scalaire) const { - return GLC_Vector2d(dVecteur[0] * Scalaire, dVecteur[1] * Scalaire);; + return GLC_Vector2d(m_Vector[0] * Scalaire, m_Vector[1] * Scalaire);; } /*! Overload equality "==" operator*/ inline bool operator == (const GLC_Vector2d &Vect) const { - bool bResult= qFuzzyCompare(dVecteur[0], Vect.dVecteur[0]); - bResult= bResult && qFuzzyCompare(dVecteur[1], Vect.dVecteur[1]); + bool bResult= qFuzzyCompare(m_Vector[0], Vect.m_Vector[0]); + bResult= bResult && qFuzzyCompare(m_Vector[1], Vect.m_Vector[1]); return bResult; } @@ -190,33 +197,40 @@ public: /*! X Composante*/ inline GLC_Vector2d& setX(const double &dX) { - dVecteur[0]= dX; + m_Vector[0]= dX; return *this; } /*! Y Composante*/ inline GLC_Vector2d& setY(const double &dY) { - dVecteur[1]= dY; + m_Vector[1]= dY; return *this; } /*! All Composante*/ inline GLC_Vector2d& setVect(const double &dX, const double &dY) { - dVecteur[0]= dX; - dVecteur[1]= dY; + m_Vector[0]= dX; + m_Vector[1]= dY; return *this; } /*! From another Vector*/ inline GLC_Vector2d& setVect(const GLC_Vector2d &Vect) { - dVecteur[0]= Vect.dVecteur[0]; - dVecteur[1]= Vect.dVecteur[1]; + m_Vector[0]= Vect.m_Vector[0]; + m_Vector[1]= Vect.m_Vector[1]; return *this; } + //! Set vector lenght from the given scalar and return a reference of this vector + inline GLC_Vector2d& setLength(double); + + //! Normalize this vector and return a reference to it + inline GLC_Vector2d& normalize() + {return setLength(1.0);} + //@} ////////////////////////////////////////////////////////////////////// @@ -227,32 +241,32 @@ public: /*! X Composante*/ inline double getX(void) const { - return dVecteur[0]; + return m_Vector[0]; } /*! Y Composante*/ inline double getY(void) const { - return dVecteur[1]; + return m_Vector[1]; } /*! retourne un pointeur constant vers le tableau du vecteur.*/ inline const double *return_dVect(void) const { - return dVecteur; + return m_Vector; } /*! Return true if the vector is null*/ inline bool isNull(void) const { - return qFuzzyCompare(dVecteur[0], 0.0) && qFuzzyCompare(dVecteur[1], 0.0); + return qFuzzyCompare(m_Vector[0], 0.0) && qFuzzyCompare(m_Vector[1], 0.0); } //! return the string representation of vector inline QString toString() const { - return QString("[") + QString::number(dVecteur[0]) + QString(" , ") + QString::number(dVecteur[1]) + QString("]"); + return QString("[") + QString::number(m_Vector[0]) + QString(" , ") + QString::number(m_Vector[1]) + QString("]"); } //! return a vector perpendicular to this inline GLC_Vector2d perp() const { - return GLC_Vector2d(-dVecteur[1], dVecteur[0]); + return GLC_Vector2d(-m_Vector[1], m_Vector[0]); } //@} @@ -264,11 +278,25 @@ private: * vector[0] X \n * vector[1] Y \n */ - double dVecteur[2]; + double m_Vector[2]; }; //class GLC_Vector2d //! Define GLC_Point2D typedef GLC_Vector2d GLC_Point2d; +inline GLC_Vector2d& GLC_Vector2d::setLength(double norme) +{ + const double normCur= sqrt( m_Vector[0] * m_Vector[0] + m_Vector[1] * m_Vector[1]); + + if (normCur != 0.0f) + { + const double Coef = norme / normCur; + + m_Vector[0] = m_Vector[0] * Coef; + m_Vector[1] = m_Vector[1] * Coef; + } + return *this; +} + #endif /*GLC_VECTOR2D_H_*/ diff --git a/ground/openpilotgcs/src/libs/glc_lib/maths/glc_vector3d.h b/ground/openpilotgcs/src/libs/glc_lib/maths/glc_vector3d.h old mode 100644 new mode 100755 index 5c53b4159..c43db6c71 --- a/ground/openpilotgcs/src/libs/glc_lib/maths/glc_vector3d.h +++ b/ground/openpilotgcs/src/libs/glc_lib/maths/glc_vector3d.h @@ -76,6 +76,9 @@ public: //! Construct a 3d vector from another 3d float vector inline GLC_Vector3d(const GLC_Vector3df &vector); + //! Construct a 3d vector from a 2d float vector + inline GLC_Vector3d(const GLC_Vector2d &vector); + //@} ////////////////////////////////////////////////////////////////////// @@ -111,9 +114,12 @@ public: /*! retrieve component corresponding to mask vector NULL component*/ inline GLC_Vector2d toVector2d(const GLC_Vector3d& mask) const; - //! Return the Angle from this vector to the given vector + //! Return the Angle from this vector to the given vector (from 0 to PI) inline double angleWithVect(GLC_Vector3d Vect) const; + //! Return the signed angle from this vector to th given vector with the given direction (from 0 to -PI and 0 to PI) + inline double signedAngleWithVect(GLC_Vector3d Vect, const GLC_Vector3d& dir) const; + //! Return the float 3D vector from this vector inline GLC_Vector3df toVector3df() const {return GLC_Vector3df(static_cast(m_Vector[0]), static_cast(m_Vector[1]), static_cast(m_Vector[2]));} @@ -289,6 +295,18 @@ inline QDataStream &operator>>(QDataStream &stream, GLC_Vector3d &vector) return stream; } +//! Return the determinant of the given Matrix 3X3 +inline double getDeterminant3x3(const double *Mat3x3) +{ + double Determinant; + + Determinant= Mat3x3[0] * ( Mat3x3[4] * Mat3x3[8] - Mat3x3[7] * Mat3x3[5]); + Determinant+= - Mat3x3[3] * ( Mat3x3[1] * Mat3x3[8] - Mat3x3[7] * Mat3x3[2]); + Determinant+= Mat3x3[6] * ( Mat3x3[1] * Mat3x3[5] - Mat3x3[4] * Mat3x3[2]); + + return Determinant; +} + ////////////////////////////////////////////////////////////////////// // inline method implementation ////////////////////////////////////////////////////////////////////// @@ -314,6 +332,13 @@ GLC_Vector3d::GLC_Vector3d(const GLC_Vector3df &vector) m_Vector[2]= static_cast(vector.m_Vector[2]); } +GLC_Vector3d::GLC_Vector3d(const GLC_Vector2d &vector) +{ + m_Vector[0]= vector.getX(); + m_Vector[1]= vector.getY(); + m_Vector[2]= 0.0; +} + GLC_Vector3d& GLC_Vector3d::operator = (const GLC_Vector3df &Vect) { m_Vector[0]= static_cast(Vect.m_Vector[0]); @@ -419,6 +444,48 @@ double GLC_Vector3d::angleWithVect(GLC_Vector3d Vect) const else return 0.0; } +double GLC_Vector3d::signedAngleWithVect(GLC_Vector3d Vect, const GLC_Vector3d& dir) const +{ + double angle= 0.0; + + GLC_Vector3d ThisVect(*this); + ThisVect.normalize(); + Vect.normalize(); + if (Vect == ThisVect.inverted()) + { + angle= glc::PI; + } + else if (Vect != ThisVect) + { + // Rotation axis + const GLC_Vector3d VectAxeRot(ThisVect ^ Vect); + // Check if the rotation axis vector is null + if (!VectAxeRot.isNull()) + { + double mat3x3[9]; + mat3x3[0]= ThisVect.m_Vector[0]; + mat3x3[1]= ThisVect.m_Vector[1]; + mat3x3[2]= ThisVect.m_Vector[2]; + + mat3x3[3]= Vect.m_Vector[0]; + mat3x3[4]= Vect.m_Vector[1]; + mat3x3[5]= Vect.m_Vector[2]; + + mat3x3[6]= dir.m_Vector[0]; + mat3x3[7]= dir.m_Vector[1]; + mat3x3[8]= dir.m_Vector[2]; + + double det= getDeterminant3x3(mat3x3); + + double sign= 1.0; + if (det != 0) sign= fabs(det) / det; + angle= acos(ThisVect * Vect) * sign; + } + } + + return angle; +} + QString GLC_Vector3d::toString() const { QString result("["); diff --git a/ground/openpilotgcs/src/libs/glc_lib/maths/glc_vector4d.h b/ground/openpilotgcs/src/libs/glc_lib/maths/glc_vector4d.h index 332481416..fee674224 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/maths/glc_vector4d.h +++ b/ground/openpilotgcs/src/libs/glc_lib/maths/glc_vector4d.h @@ -110,8 +110,8 @@ public: //! Copy from an GLC_Vector3d inline GLC_Vector4d(const GLC_Vector2d &Vect) { - vector[0]= Vect.dVecteur[0]; - vector[1]= Vect.dVecteur[1]; + vector[0]= Vect.m_Vector[0]; + vector[1]= Vect.m_Vector[1]; vector[2]= 0.0; vector[3]= 1.0; } @@ -168,8 +168,8 @@ public: //! Overload "=" operator inline GLC_Vector4d& operator = (const GLC_Vector2d &Vect) { - vector[0]= Vect.dVecteur[0]; - vector[1]= Vect.dVecteur[1]; + vector[0]= Vect.m_Vector[0]; + vector[1]= Vect.m_Vector[1]; vector[2]= 0.0; vector[3]= 1.0; diff --git a/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_3dviewcollection.cpp b/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_3dviewcollection.cpp index 5cdab1d51..274f540fa 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_3dviewcollection.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_3dviewcollection.cpp @@ -40,7 +40,6 @@ GLC_3DViewCollection::GLC_3DViewCollection() : m_3DViewInstanceHash() -, m_pBoundingBox(NULL) , m_SelectedInstances() , m_ShadedPointerViewInstanceHash() , m_ShaderGroup() @@ -50,6 +49,7 @@ GLC_3DViewCollection::GLC_3DViewCollection() , m_pViewport(NULL) , m_pSpacePartitioning(NULL) , m_UseSpacePartitioning(false) +, m_IsViewable(true) { } @@ -62,7 +62,7 @@ GLC_3DViewCollection::~GLC_3DViewCollection() // Set Functions ////////////////////////////////////////////////////////////////////// -bool GLC_3DViewCollection::bindShader(GLuint shaderId) +bool GLC_3DViewCollection::bindShader(GLC_uint shaderId) { if (m_ShadedPointerViewInstanceHash.contains(shaderId)) { @@ -76,7 +76,7 @@ bool GLC_3DViewCollection::bindShader(GLuint shaderId) } } -bool GLC_3DViewCollection::unBindShader(GLuint shaderId) +bool GLC_3DViewCollection::unBindShader(GLC_uint shaderId) { bool result= false; if (m_ShadedPointerViewInstanceHash.contains(shaderId)) @@ -127,7 +127,7 @@ bool GLC_3DViewCollection::unBindAllShader() return result; } -bool GLC_3DViewCollection::add(const GLC_3DViewInstance& node, GLuint shaderID) +bool GLC_3DViewCollection::add(const GLC_3DViewInstance& node, GLC_uint shaderID) { bool result= false; const GLC_uint key= node.id(); @@ -170,19 +170,10 @@ bool GLC_3DViewCollection::add(const GLC_3DViewInstance& node, GLuint shaderID) result=true; } - if(result) - { - // Bounding box validity - if (NULL != m_pBoundingBox) - { - delete m_pBoundingBox; - m_pBoundingBox= NULL; - } - } return result; } -void GLC_3DViewCollection::changeShadingGroup(GLC_uint instanceId, GLuint shaderId) +void GLC_3DViewCollection::changeShadingGroup(GLC_uint instanceId, GLC_uint shaderId) { // Test if the specified instance exist Q_ASSERT(m_3DViewInstanceHash.contains(instanceId)); @@ -254,13 +245,6 @@ bool GLC_3DViewCollection::remove(GLC_uint Key) m_3DViewInstanceHash.remove(Key); // Delete the conteneur - // Bounding box validity - if (NULL != m_pBoundingBox) - { - delete m_pBoundingBox; - m_pBoundingBox= NULL; - } - //qDebug("GLC_3DViewCollection::removeNode : Element succesfuly deleted"); return true; @@ -293,13 +277,6 @@ void GLC_3DViewCollection::clear(void) // Clear main Hash table m_3DViewInstanceHash.clear(); - // delete the boundingBox - if (m_pBoundingBox != NULL) - { - delete m_pBoundingBox; - m_pBoundingBox= NULL; - } - // delete the space partitioning delete m_pSpacePartitioning; } @@ -438,12 +415,6 @@ void GLC_3DViewCollection::setVisibility(const GLC_uint key, const bool visibili if (iNode != m_3DViewInstanceHash.end()) { // Ok, the key exist iNode.value().setVisibility(visibility); - // Bounding box validity - if (NULL != m_pBoundingBox) - { - delete m_pBoundingBox; - m_pBoundingBox= NULL; - } } } @@ -457,14 +428,6 @@ void GLC_3DViewCollection::showAll() iEntry.value().setVisibility(true); iEntry++; } - - // Bounding box validity - if (NULL != m_pBoundingBox) - { - delete m_pBoundingBox; - m_pBoundingBox= NULL; - } - } void GLC_3DViewCollection::hideAll() @@ -478,13 +441,6 @@ void GLC_3DViewCollection::hideAll() iEntry++; } - // Bounding box validity - if (NULL != m_pBoundingBox) - { - delete m_pBoundingBox; - m_pBoundingBox= NULL; - } - } void GLC_3DViewCollection::bindSpacePartitioning(GLC_SpacePartitioning* pSpacePartitioning) @@ -526,6 +482,17 @@ void GLC_3DViewCollection::updateInstanceViewableState(const GLC_Frustum& frustu m_pSpacePartitioning->updateViewableInstances(frustum); } +void GLC_3DViewCollection::setVboUsage(bool usage) +{ + ViewInstancesHash::iterator iEntry= m_3DViewInstanceHash.begin(); + + while (iEntry != m_3DViewInstanceHash.constEnd()) + { + iEntry.value().setVboUsage(usage); + iEntry++; + } +} + QList GLC_3DViewCollection::instancesHandle() { QList instancesList; @@ -583,48 +550,22 @@ GLC_3DViewInstance* GLC_3DViewCollection::instanceHandle(GLC_uint Key) GLC_BoundingBox GLC_3DViewCollection::boundingBox(bool allObject) { - // If the bounding box is not valid delete it - if (allObject) - { - delete m_pBoundingBox; - m_pBoundingBox= NULL; - } - else - { - setBoundingBoxValidity(); - } - + GLC_BoundingBox boundingBox; // Check if the bounding box have to be updated - if ((m_pBoundingBox == NULL) && !m_3DViewInstanceHash.isEmpty()) + if (!m_3DViewInstanceHash.isEmpty()) { - // There is objects in the collection and the collection or bounding box is not valid - m_pBoundingBox= new GLC_BoundingBox(); - ViewInstancesHash::iterator iEntry= m_3DViewInstanceHash.begin(); while (iEntry != m_3DViewInstanceHash.constEnd()) { if(allObject || iEntry.value().isVisible() == m_IsInShowSate) { // Combine Collection BoundingBox with element Bounding Box - m_pBoundingBox->combine(iEntry.value().boundingBox()); + boundingBox.combine(iEntry.value().boundingBox()); } ++iEntry; } } - - if (NULL == m_pBoundingBox) m_pBoundingBox= new GLC_BoundingBox; - - if (allObject) - { - GLC_BoundingBox allBoundingBox(*m_pBoundingBox); - delete m_pBoundingBox; - m_pBoundingBox= NULL; - return allBoundingBox; - } - else - { - return *m_pBoundingBox; - } + return boundingBox; } int GLC_3DViewCollection::drawableObjectsSize() const @@ -672,7 +613,7 @@ int GLC_3DViewCollection::numberOfUsedShadingGroup() const void GLC_3DViewCollection::render(GLuint groupId, glc::RenderFlag renderFlag) { - if (!isEmpty()) + if (!isEmpty() && m_IsViewable) { if (renderFlag == glc::WireRenderFlag) { @@ -682,12 +623,12 @@ void GLC_3DViewCollection::render(GLuint groupId, glc::RenderFlag renderFlag) if (GLC_State::isInSelectionMode()) { glDisable(GL_BLEND); - glDisable(GL_LIGHTING); + GLC_Context::current()->glcEnableLighting(false); glDisable(GL_TEXTURE_2D); } else { - glEnable(GL_LIGHTING); + GLC_Context::current()->glcEnableLighting(true); } glDraw(groupId, renderFlag); @@ -699,12 +640,12 @@ void GLC_3DViewCollection::render(GLuint groupId, glc::RenderFlag renderFlag) } void GLC_3DViewCollection::renderShaderGroup(glc::RenderFlag renderFlag) { - if (!isEmpty()) + if (!isEmpty() && m_IsViewable) { if (GLC_State::isInSelectionMode()) { glDisable(GL_BLEND); - glDisable(GL_LIGHTING); + GLC_Context::current()->glcEnableLighting(false); glDisable(GL_TEXTURE_2D); } @@ -717,7 +658,7 @@ void GLC_3DViewCollection::renderShaderGroup(glc::RenderFlag renderFlag) } } -void GLC_3DViewCollection::glDraw(GLuint groupId, glc::RenderFlag renderFlag) +void GLC_3DViewCollection::glDraw(GLC_uint groupId, glc::RenderFlag renderFlag) { // Set render Mode and OpenGL state if (!GLC_State::isInSelectionMode() && (groupId == 0)) @@ -773,30 +714,3 @@ void GLC_3DViewCollection::glDraw(GLuint groupId, glc::RenderFlag renderFlag) glEnable(GL_DEPTH_TEST); } } - -void GLC_3DViewCollection::setBoundingBoxValidity(void) -{ - if (NULL != m_pBoundingBox) - { - if (!m_3DViewInstanceHash.isEmpty()) - { - // Check instance bounding box validity - ViewInstancesHash::iterator iEntry= m_3DViewInstanceHash.begin(); - while (iEntry != m_3DViewInstanceHash.constEnd()) - { - if (!iEntry.value().boundingBoxValidity()) - { - delete m_pBoundingBox; - m_pBoundingBox= NULL; - return; - } - iEntry++; - } - } - else if (!m_pBoundingBox->isEmpty()) - { - delete m_pBoundingBox; - m_pBoundingBox= NULL; - } - } -} diff --git a/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_3dviewcollection.h b/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_3dviewcollection.h index f4bd55c34..9fc6d4384 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_3dviewcollection.h +++ b/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_3dviewcollection.h @@ -45,10 +45,10 @@ typedef QHash< GLC_uint, GLC_3DViewInstance> ViewInstancesHash; typedef QHash PointerViewInstanceHash; //! Hash table of GLC_3DViewInstance Hash table which use a shader -typedef QHash HashList; +typedef QHash HashList; //! Map Shader id to instances id (instances which use the shader) -typedef QHash ShaderIdToInstancesId; +typedef QHash ShaderIdToInstancesId; ////////////////////////////////////////////////////////////////////// //! \class GLC_3DViewCollection @@ -132,7 +132,7 @@ public: int drawableObjectsSize() const; //! Return the element shading group - inline GLuint shadingGroup(GLC_uint key) const + inline GLC_uint shadingGroup(GLC_uint key) const { return m_ShaderGroup.value(key);} //! Return true if the element is in a shading group @@ -153,6 +153,9 @@ public: inline GLC_SpacePartitioning* spacePartitioningHandle() {return m_pSpacePartitioning;} + //! Return true if the collection is viewable + inline bool isViewable() const + {return m_IsViewable;} //@} @@ -164,11 +167,11 @@ public: //! Bind the specified shader to the collection /* return true if success false if shader is already bind*/ - bool bindShader(GLuint); + bool bindShader(GLC_uint shaderId); //! Unbind the specified shader from the collection /* return true if success false otherwise*/ - bool unBindShader(GLuint); + bool unBindShader(GLC_uint shaderId); //! Unbind All shader bool unBindAllShader(); @@ -176,13 +179,13 @@ public: //! Add a GLC_3DViewInstance in the collection /*! return true if success false otherwise * If shading group is specified, add instance in desire shading group*/ - bool add(const GLC_3DViewInstance& ,GLuint shaderID=0); + bool add(const GLC_3DViewInstance& ,GLC_uint shaderID=0); //! Change instance shading group /* Move the specified instances into * the specified shading group * Return true if success false otherwise*/ - void changeShadingGroup(GLC_uint, GLuint); + void changeShadingGroup(GLC_uint instanceId, GLC_uint shaderId); //! Remove a GLC_Geometry from the collection and delete it /*! - Find the GLC_Geometry in the collection @@ -192,28 +195,28 @@ public: * - Remove the Display list container from collection * - Invalidate the collection * return true if success false otherwise*/ - bool remove(GLC_uint Key); + bool remove(GLC_uint instanceId); //! Remove and delete all GLC_Geometry from the collection void clear(void); //! Select a Instance - bool select(GLC_uint, bool primitive= false); + bool select(GLC_uint instanceId, bool primitive= false); //! Select all instances in current show state or in all show state void selectAll(bool allShowState= false); //! unselect a Instance - bool unselect(GLC_uint); + bool unselect(GLC_uint instanceId); //! unselect all Instance void unselectAll(); //! Set the polygon mode for all Instance - void setPolygonModeForAll(GLenum, GLenum); + void setPolygonModeForAll(GLenum face, GLenum mode); //! Set Instance visibility - void setVisibility(const GLC_uint, const bool); + void setVisibility(const GLC_uint instanceId, const bool visible); //! Show all instances of the collection void showAll(); @@ -223,15 +226,7 @@ public: //! Set the Show or noShow state inline void swapShowState() - { - m_IsInShowSate= !m_IsInShowSate; - // Bounding box validity - if (NULL != m_pBoundingBox) - { - delete m_pBoundingBox; - m_pBoundingBox= NULL; - } - } + {m_IsInShowSate= !m_IsInShowSate;} //! Set the LOD usage inline void setLodUsage(const bool usage, GLC_Viewport* pView) @@ -261,6 +256,14 @@ public: //! Set the attached viewport of this collection inline void setAttachedViewport(GLC_Viewport* pViewport) {m_pViewport= pViewport;} + + //! Set the collection viewable state + inline void setViewable(bool viewable) + {m_IsViewable= viewable;} + + //! Set VBO usage + void setVboUsage(bool usage); + //@} ////////////////////////////////////////////////////////////////////// @@ -287,24 +290,13 @@ public: private: //! Display collection's member - void glDraw(GLuint, glc::RenderFlag); + void glDraw(GLC_uint groupID, glc::RenderFlag renderFlag); //! Draw instances of a PointerViewInstanceHash inline void glDrawInstancesOf(PointerViewInstanceHash*, glc::RenderFlag); //@} -////////////////////////////////////////////////////////////////////// -/*! \name Privates services Functions*/ -//@{ -////////////////////////////////////////////////////////////////////// - -private: - //! Set the Bounding box validity - void setBoundingBoxValidity(void); - -//@} - ////////////////////////////////////////////////////////////////////// // Private members ////////////////////////////////////////////////////////////////////// @@ -312,9 +304,6 @@ private: //! GLC_3DViewInstance Hash Table ViewInstancesHash m_3DViewInstanceHash; - //! BoundingBox of the collection - GLC_BoundingBox* m_pBoundingBox; - //! Selected Node Hash Table PointerViewInstanceHash m_SelectedInstances; @@ -342,6 +331,9 @@ private: //! The space partition usage bool m_UseSpacePartitioning; + //! Viewable state + bool m_IsViewable; + }; diff --git a/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_3dviewinstance.cpp b/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_3dviewinstance.cpp index cd3a4c98b..567774d54 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_3dviewinstance.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_3dviewinstance.cpp @@ -192,11 +192,17 @@ void GLC_3DViewInstance::setGlobalDefaultLod(int lod) m_GlobalDefaultLOD= lod; } +void GLC_3DViewInstance::setVboUsage(bool usage) +{ + m_3DRep.setVboUsage(usage); +} + // Clone the instance GLC_3DViewInstance GLC_3DViewInstance::deepCopy() const { GLC_3DRep* pRep= dynamic_cast(m_3DRep.deepCopy()); + Q_ASSERT(NULL != pRep); GLC_3DRep newRep(*pRep); delete pRep; GLC_3DViewInstance cloneInstance(newRep); @@ -231,7 +237,7 @@ GLC_3DViewInstance GLC_3DViewInstance::instanciate() // Set the instance Geometry -bool GLC_3DViewInstance::setGeometry(GLC_Geometry* pGeom) +bool GLC_3DViewInstance::addGeometry(GLC_Geometry* pGeom) { if (m_3DRep.contains(pGeom)) { @@ -299,7 +305,7 @@ void GLC_3DViewInstance::render(glc::RenderFlag renderFlag, bool useLod, GLC_Vie m_RenderProperties.setRenderingFlag(renderFlag); // Save current OpenGL Matrix - glPushMatrix(); + GLC_Context::current()->glcPushMatrix(); OpenglVisProperties(); // Change front face orientation if this instance absolute matrix is indirect @@ -350,7 +356,7 @@ void GLC_3DViewInstance::render(glc::RenderFlag renderFlag, bool useLod, GLC_Vie } } // Restore OpenGL Matrix - glPopMatrix(); + GLC_Context::current()->glcPopMatrix(); // Restore front face orientation if this instance absolute matrix is indirect if (m_AbsoluteMatrix.type() == GLC_Matrix4x4::Indirect) @@ -371,7 +377,7 @@ void GLC_3DViewInstance::renderForBodySelection() m_RenderProperties.setRenderingMode(glc::BodySelection); // Save current OpenGL Matrix - glPushMatrix(); + GLC_Context::current()->glcPushMatrix(); OpenglVisProperties(); GLubyte colorId[4]; @@ -389,7 +395,7 @@ void GLC_3DViewInstance::renderForBodySelection() // Restore rendering mode m_RenderProperties.setRenderingMode(previousRenderMode); // Restore OpenGL Matrix - glPopMatrix(); + GLC_Context::current()->glcPopMatrix(); } // Display the instance in Primitive selection mode and return the body index @@ -402,7 +408,7 @@ int GLC_3DViewInstance::renderForPrimitiveSelection(GLC_uint bodyId) m_RenderProperties.setRenderingMode(glc::PrimitiveSelection); // Save current OpenGL Matrix - glPushMatrix(); + GLC_Context::current()->glcPushMatrix(); OpenglVisProperties(); const int size= m_3DRep.numberOfBody(); @@ -423,7 +429,7 @@ int GLC_3DViewInstance::renderForPrimitiveSelection(GLC_uint bodyId) m_RenderProperties.setRenderingMode(previousRenderMode); // Restore OpenGL Matrix - glPopMatrix(); + GLC_Context::current()->glcPopMatrix(); return i; } diff --git a/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_3dviewinstance.h b/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_3dviewinstance.h index 3ec626417..257f08874 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_3dviewinstance.h +++ b/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_3dviewinstance.h @@ -32,10 +32,11 @@ #include "../glc_state.h" #include "../geometry/glc_3drep.h" #include "../shading/glc_renderproperties.h" +#include "../glc_context.h" #include -#include "glc_config.h" +#include "../glc_config.h" class GLC_Viewport; @@ -197,10 +198,7 @@ public: public: //! Set the instance Geometry - /*! - * instance must be null - */ - bool setGeometry(GLC_Geometry* pGeom); + bool addGeometry(GLC_Geometry* pGeom); //! Remove empty geometries inline void removeEmptyGeometry() @@ -274,6 +272,9 @@ public: inline void setRenderProperties(const GLC_RenderProperties& renderProperties) {m_RenderProperties= renderProperties;} + //! Set VBO usage + void setVboUsage(bool usage); + //@} ////////////////////////////////////////////////////////////////////// @@ -298,7 +299,7 @@ private: // Polygons display mode glPolygonMode(m_RenderProperties.polyFaceMode(), m_RenderProperties.polygonMode()); // Change the current matrix - glMultMatrixd(m_AbsoluteMatrix.getData()); + GLC_Context::current()->glcMultMatrix(m_AbsoluteMatrix); } diff --git a/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_structinstance.cpp b/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_structinstance.cpp index 11ff8c483..0137c5def 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_structinstance.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_structinstance.cpp @@ -180,3 +180,12 @@ GLC_StructInstance::~GLC_StructInstance() else qDebug() << "GLC_StructInstance::~GLC_StructInstance() of empty instance"; } + +void GLC_StructInstance::updateOccurencesAbsoluteMatrix() +{ + const int occurenceCount= m_ListOfOccurences.count(); + for (int i= 0; i < occurenceCount; ++i) + { + m_ListOfOccurences.at(i)->updateChildrenAbsoluteMatrix(); + } +} diff --git a/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_structinstance.h b/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_structinstance.h index 4c43fb000..3ae653965 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_structinstance.h +++ b/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_structinstance.h @@ -166,12 +166,15 @@ public: {m_Name= name;} //! Set the instance attributes - void setAttributes(const GLC_Attributes& attr) + inline void setAttributes(const GLC_Attributes& attr) { delete m_pAttributes; m_pAttributes= new GLC_Attributes(attr); } + //! Update absolute matrix off children and all occurences of this instance + void updateOccurencesAbsoluteMatrix(); + //@} diff --git a/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_structoccurence.cpp b/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_structoccurence.cpp index 6a18155bb..2c9c6d939 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_structoccurence.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_structoccurence.cpp @@ -41,6 +41,7 @@ GLC_StructOccurence::GLC_StructOccurence() , m_IsVisible(true) , m_pRenderProperties(NULL) , m_AutomaticCreationOf3DViewInstance(true) +, m_pRelativeMatrix(NULL) { // Update instance m_pStructInstance->structOccurenceCreated(this); @@ -60,6 +61,7 @@ GLC_StructOccurence::GLC_StructOccurence(GLC_StructInstance* pStructInstance, GL , m_IsVisible(true) , m_pRenderProperties(NULL) , m_AutomaticCreationOf3DViewInstance(true) +, m_pRelativeMatrix(NULL) { // Update the number of occurences if (pStructInstance->hasStructOccurence()) @@ -107,6 +109,7 @@ GLC_StructOccurence::GLC_StructOccurence(GLC_3DRep* pRep) , m_IsVisible(true) , m_pRenderProperties(NULL) , m_AutomaticCreationOf3DViewInstance(true) +, m_pRelativeMatrix(NULL) { m_pStructInstance= new GLC_StructInstance(pRep); setName(m_pStructInstance->name()); @@ -128,6 +131,7 @@ GLC_StructOccurence::GLC_StructOccurence(GLC_WorldHandle* pWorldHandle, const GL , m_IsVisible(structOccurence.m_IsVisible) , m_pRenderProperties(NULL) , m_AutomaticCreationOf3DViewInstance(structOccurence.m_AutomaticCreationOf3DViewInstance) +, m_pRelativeMatrix(NULL) { if (shareInstance) { @@ -170,7 +174,7 @@ GLC_StructOccurence::GLC_StructOccurence(GLC_WorldHandle* pWorldHandle, const GL if (NULL != m_pWorldHandle) { m_pWorldHandle->addOccurence(this, instanceIsSelected, shaderId); - if (NULL != m_pRenderProperties) + if (NULL != m_pRenderProperties && this->has3DViewInstance()) { m_pWorldHandle->collection()->instanceHandle(id())->setRenderProperties(*m_pRenderProperties); delete m_pRenderProperties; @@ -178,6 +182,12 @@ GLC_StructOccurence::GLC_StructOccurence(GLC_WorldHandle* pWorldHandle, const GL } } + // Check flexibility + if (NULL != structOccurence.m_pRelativeMatrix) + { + m_pRelativeMatrix= new GLC_Matrix4x4(*(structOccurence.m_pRelativeMatrix)); + } + // Update Absolute matrix updateAbsoluteMatrix(); @@ -236,12 +246,23 @@ GLC_StructOccurence::~GLC_StructOccurence() } delete m_pRenderProperties; + delete m_pRelativeMatrix; } ////////////////////////////////////////////////////////////////////// // Get Functions ////////////////////////////////////////////////////////////////////// +GLC_Matrix4x4 GLC_StructOccurence::occurrenceRelativeMatrix() const +{ + GLC_Matrix4x4 matrix; + if (NULL != m_pRelativeMatrix) + { + matrix= *m_pRelativeMatrix; + } + return matrix; +} + bool GLC_StructOccurence::hasRepresentation() const { if ((NULL != m_pStructInstance) && (m_pStructInstance->hasStructOccurence())) @@ -288,14 +309,11 @@ QList GLC_StructOccurence::subOccurenceList() const for (int i= 0; i < childCount; ++i) { GLC_StructOccurence* pCurrentChild= m_Childs.at(i); + subOccurence.append(pCurrentChild); if (pCurrentChild->hasChild()) { subOccurence.append(pCurrentChild->subOccurenceList()); } - else - { - subOccurence.append(pCurrentChild); - } } return subOccurence; @@ -484,13 +502,23 @@ QSet GLC_StructOccurence::parentsReferences(const GLC_Stru // Update the absolute matrix GLC_StructOccurence* GLC_StructOccurence::updateAbsoluteMatrix() { - if (NULL != m_pParent) + GLC_Matrix4x4 relativeMatrix; + if (NULL == m_pRelativeMatrix) { - m_AbsoluteMatrix= m_pParent->absoluteMatrix() * m_pStructInstance->relativeMatrix(); + relativeMatrix= m_pStructInstance->relativeMatrix(); } else { - m_AbsoluteMatrix= m_pStructInstance->relativeMatrix(); + relativeMatrix= *m_pRelativeMatrix; + } + + if (NULL != m_pParent) + { + m_AbsoluteMatrix= m_pParent->absoluteMatrix() * relativeMatrix; + } + else + { + m_AbsoluteMatrix= relativeMatrix; } // If the occurence have a representation, update it. @@ -563,33 +591,6 @@ bool GLC_StructOccurence::removeChild(GLC_StructOccurence* pChild) return m_Childs.removeOne(pChild); } -// Detach the occurence from the GLC_World -void GLC_StructOccurence::detach() -{ - if (NULL != m_pWorldHandle) - { - // retrieve renderProperties if needed - if (m_pWorldHandle->collection()->contains(m_Uid)) - { - GLC_3DViewInstance* pInstance= m_pWorldHandle->collection()->instanceHandle(m_Uid); - if (!pInstance->renderPropertiesHandle()->isDefault()) - { - Q_ASSERT(NULL == m_pRenderProperties); - m_pRenderProperties= new GLC_RenderProperties(*(pInstance->renderPropertiesHandle())); - } - } - m_pWorldHandle->removeOccurence(this); - m_pWorldHandle= NULL; - if (!m_Childs.isEmpty()) - { - const int size= m_Childs.size(); - for (int i= 0; i < size; ++i) - { - m_Childs[i]->detach(); - } - } - } -} // Reverse Normals of this Occurence and childs void GLC_StructOccurence::reverseNormals() @@ -611,10 +612,23 @@ bool GLC_StructOccurence::create3DViewInstance() { GLC_3DViewInstance instance(*p3DRep); instance.setName(name()); + // Force instance representation id instance.setId(id()); + + if (NULL != m_pRenderProperties) + { + instance.setRenderProperties(*m_pRenderProperties); + delete m_pRenderProperties; + m_pRenderProperties= NULL; + } + creationSuccess= m_pWorldHandle->collection()->add(instance); m_pWorldHandle->collection()->setVisibility(m_Uid, m_IsVisible); + if (m_pWorldHandle->selectionSetHandle()->contains(m_Uid)) + { + m_pWorldHandle->collection()->select(m_Uid); + } } } return creationSuccess; @@ -814,3 +828,51 @@ void GLC_StructOccurence::setReference(GLC_StructReference* pRef) m_pStructInstance->setReference(pRef); } + +void GLC_StructOccurence::makeFlexible(const GLC_Matrix4x4& relativeMatrix) +{ + delete m_pRelativeMatrix; + m_pRelativeMatrix= new GLC_Matrix4x4(relativeMatrix); + + updateChildrenAbsoluteMatrix(); +} + +void GLC_StructOccurence::makeRigid() +{ + delete m_pRelativeMatrix; + m_pRelativeMatrix= NULL; + + updateChildrenAbsoluteMatrix(); +} + + +////////////////////////////////////////////////////////////////////// +// Private services function +////////////////////////////////////////////////////////////////////// + +void GLC_StructOccurence::detach() +{ + if (NULL != m_pWorldHandle) + { + // retrieve renderProperties if needed + if (m_pWorldHandle->collection()->contains(m_Uid)) + { + GLC_3DViewInstance* pInstance= m_pWorldHandle->collection()->instanceHandle(m_Uid); + if (!pInstance->renderPropertiesHandle()->isDefault()) + { + Q_ASSERT(NULL == m_pRenderProperties); + m_pRenderProperties= new GLC_RenderProperties(*(pInstance->renderPropertiesHandle())); + } + } + m_pWorldHandle->removeOccurence(this); + m_pWorldHandle= NULL; + if (!m_Childs.isEmpty()) + { + const int size= m_Childs.size(); + for (int i= 0; i < size; ++i) + { + m_Childs[i]->detach(); + } + } + } +} diff --git a/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_structoccurence.h b/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_structoccurence.h index fb2bdfe65..8bbc14668 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_structoccurence.h +++ b/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_structoccurence.h @@ -80,6 +80,9 @@ public: inline GLC_Matrix4x4 absoluteMatrix() const { return m_AbsoluteMatrix;} + //! Return the surcharged relative matrix + GLC_Matrix4x4 occurrenceRelativeMatrix() const; + //! Return true if this occurence is orphan inline bool isOrphan() const { return NULL == m_pParent;} @@ -178,7 +181,9 @@ public: inline bool useAutomatic3DViewInstanceCreation() const {return m_AutomaticCreationOf3DViewInstance;} - + //! Return true if this occurence is flexible + inline bool isFlexible() const + {return (m_pRelativeMatrix != NULL);} //@} ////////////////////////////////////////////////////////////////////// /*! \name Set Functions*/ @@ -250,7 +255,11 @@ public: inline void setAutomatic3DViewInstanceCreationUsage(bool usage) {m_AutomaticCreationOf3DViewInstance= usage;} + //! Make this occurence a flexible occurence + void makeFlexible(const GLC_Matrix4x4& relativeMatrix); + //! Make this occurence rigid + void makeRigid(); //@} ////////////////////////////////////////////////////////////////////// @@ -297,6 +306,9 @@ private: //! Automatique crŽation of 3DViewInstance bool m_AutomaticCreationOf3DViewInstance; + //! The relative matrix of this occurence if this occurence is flexible + GLC_Matrix4x4* m_pRelativeMatrix; + }; #endif /* GLC_STRUCTOCCURENCE_H_ */ diff --git a/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_world.cpp b/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_world.cpp index cbb82e9bb..24dcafe0f 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_world.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_world.cpp @@ -32,7 +32,6 @@ GLC_World::GLC_World() , m_pRoot(new GLC_StructOccurence()) { m_pRoot->setWorldHandle(m_pWorldHandle); - //qDebug() << "GLC_World::GLC_World() : " << (*m_pNumberOfWorld) << " " << this; } // Copy constructor @@ -43,14 +42,13 @@ GLC_World::GLC_World(const GLC_World& world) //qDebug() << "GLC_World::GLC_World() : " << (*m_pNumberOfWorld) << " " << this; // Increment the number of world m_pWorldHandle->increment(); + } GLC_World::~GLC_World() { - // Decrement the number of world m_pWorldHandle->decrement(); - //qDebug() << "GLC_World::GLC_World() : " << (*m_pNumberOfWorld) << " " << this; if (m_pWorldHandle->isOrphan()) { // this is the last World, delete the root product and collection @@ -63,7 +61,6 @@ GLC_World::~GLC_World() // Merge this world with another world void GLC_World::mergeWithAnotherWorld(GLC_World& anotherWorld) { - qDebug() << "GLC_World::mergeWithAnotherWorld"; GLC_StructOccurence* pAnotherRoot= anotherWorld.rootOccurence(); if (pAnotherRoot->childCount() > 0) { diff --git a/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_worldhandle.cpp b/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_worldhandle.cpp index 561053207..e170aaafb 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_worldhandle.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_worldhandle.cpp @@ -131,14 +131,14 @@ void GLC_WorldHandle::removeOccurence(GLC_StructOccurence* pOccurence) m_Collection.remove(pOccurence->id()); } -void GLC_WorldHandle::select(GLC_uint occurenceId, bool propagate) +void GLC_WorldHandle::select(GLC_uint occurenceId) { Q_ASSERT(m_OccurenceHash.contains(occurenceId)); m_SelectionSet.insert(occurenceId); m_Collection.select(occurenceId); const GLC_StructOccurence* pSelectedOccurence= m_OccurenceHash.value(occurenceId); - if (propagate && pSelectedOccurence->hasChild()) + if (pSelectedOccurence->hasChild()) { QList subOccurenceList= pSelectedOccurence->subOccurenceList(); const int subOccurenceCount= subOccurenceList.size(); @@ -147,8 +147,8 @@ void GLC_WorldHandle::select(GLC_uint occurenceId, bool propagate) const GLC_uint currentOccurenceId= subOccurenceList.at(i)->id(); if (m_Collection.contains(currentOccurenceId)) { - GLC_3DViewInstance* pInstance= m_Collection.instanceHandle(currentOccurenceId); - m_Collection.select(currentOccurenceId); } + m_Collection.select(currentOccurenceId); + } } } } diff --git a/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_worldhandle.h b/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_worldhandle.h index 9e835785d..bfc10195a 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_worldhandle.h +++ b/ground/openpilotgcs/src/libs/glc_lib/sceneGraph/glc_worldhandle.h @@ -143,7 +143,7 @@ public: //! Select the given occurence id /*! The given occurence id must belong to this worldhandle*/ - void select(GLC_uint occurenceId, bool propagate= true); + void select(GLC_uint occurenceId); //! Unselect the given occurence id /*! The given occurence id must belong to this worldhandle*/ diff --git a/ground/openpilotgcs/src/libs/glc_lib/shading/glc_light.cpp b/ground/openpilotgcs/src/libs/glc_lib/shading/glc_light.cpp index 16b932c65..c08708c59 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/shading/glc_light.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/shading/glc_light.cpp @@ -22,23 +22,23 @@ //! \file glc_light.cpp implementation of the GLC_Light class. -#include "glc_light.h" -#include "../glc_openglexception.h" - +#include #include -GLint GLC_Light::m_MaxLight= 0; -QSet GLC_Light::m_FreeLightSet; +#include "glc_light.h" +#include "../glc_openglexception.h" +#include "../glc_context.h" + +GLint GLC_Light::m_MaxLight= 8; +QHash > GLC_Light::m_ContextToFreeLightSet; ////////////////////////////////////////////////////////////////////// // Constructor Destructor ////////////////////////////////////////////////////////////////////// -GLC_Light::GLC_Light(const QColor& color) +GLC_Light::GLC_Light(const QGLContext* pContext, const QColor& color) :GLC_Object("Light") , m_LightID(-1) , m_LightType(LightPosition) -, m_ListID(0) -, m_ListIsValid(false) , m_AmbientColor(Qt::black) , m_DiffuseColor(color) , m_SpecularColor(Qt::white) @@ -50,19 +50,16 @@ GLC_Light::GLC_Light(const QColor& color) , m_LinearAttenuation(0.0f) , m_QuadraticAttenuation(0.0f) , m_TwoSided(false) +, m_pContext(const_cast(pContext)) +, m_IsValid(false) { - if (0 == m_MaxLight) init(); - Q_ASSERT(!m_FreeLightSet.isEmpty()); - m_LightID= *(m_FreeLightSet.constBegin()); - m_FreeLightSet.remove(m_LightID); + addNewLight(); } -GLC_Light::GLC_Light(LightType lightType, const QColor& color) +GLC_Light::GLC_Light(LightType lightType, const QGLContext* pContext, const QColor& color) :GLC_Object("Light") , m_LightID(-1) , m_LightType(lightType) -, m_ListID(0) -, m_ListIsValid(false) , m_AmbientColor(Qt::black) , m_DiffuseColor(color) , m_SpecularColor(Qt::white) @@ -74,19 +71,16 @@ GLC_Light::GLC_Light(LightType lightType, const QColor& color) , m_LinearAttenuation(0.0f) , m_QuadraticAttenuation(0.0f) , m_TwoSided(false) +, m_pContext(const_cast(pContext)) +, m_IsValid(false) { - if (0 == m_MaxLight) init(); - Q_ASSERT(!m_FreeLightSet.isEmpty()); - m_LightID= *(m_FreeLightSet.constBegin()); - m_FreeLightSet.remove(m_LightID); + addNewLight(); } GLC_Light::GLC_Light(const GLC_Light& light) :GLC_Object(light) , m_LightID(-1) , m_LightType(light.m_LightType) -, m_ListID(0) -, m_ListIsValid(false) , m_AmbientColor(light.m_AmbientColor) , m_DiffuseColor(light.m_DiffuseColor) , m_SpecularColor(light.m_SpecularColor) @@ -98,17 +92,15 @@ GLC_Light::GLC_Light(const GLC_Light& light) , m_LinearAttenuation(light.m_LinearAttenuation) , m_QuadraticAttenuation(light.m_QuadraticAttenuation) , m_TwoSided(light.m_TwoSided) +, m_pContext(light.m_pContext) +, m_IsValid(false) { - if (0 == m_MaxLight) init(); - Q_ASSERT(!m_FreeLightSet.isEmpty()); - m_LightID= *(m_FreeLightSet.constBegin()); - m_FreeLightSet.remove(m_LightID); + addNewLight(); } GLC_Light::~GLC_Light(void) { - deleteList(); - m_FreeLightSet << m_LightID; + removeThisLight(); } ///////////////////////////////////////////////////////////////////// @@ -120,199 +112,130 @@ int GLC_Light::maxLightCount() return m_MaxLight; } -int GLC_Light::builtAbleLightCount() +int GLC_Light::builtAbleLightCount(QGLContext* pContext) { - return m_FreeLightSet.size(); + if (m_ContextToFreeLightSet.contains(pContext)) + { + return m_ContextToFreeLightSet.value(pContext).size(); + } + else return m_MaxLight; } ///////////////////////////////////////////////////////////////////// // Set Functions ////////////////////////////////////////////////////////////////////// -void GLC_Light::init() +void GLC_Light::initForThisContext() { - m_MaxLight= 8; for (int i= 0; i < m_MaxLight; ++i) { - m_FreeLightSet.insert(GL_LIGHT0 + i); + m_ContextToFreeLightSet[m_pContext].insert(GL_LIGHT0 + i); } } void GLC_Light::setPosition(const GLC_Point3d &pos) { m_Position= pos; - m_ListIsValid = false; } void GLC_Light::setPosition(GLfloat x, GLfloat y, GLfloat z) { m_Position.setVect(static_cast(x), static_cast(y), static_cast(z)); - m_ListIsValid = false; } void GLC_Light::setAmbientColor(const QColor& color) { m_AmbientColor= color; - m_ListIsValid = false; + m_IsValid= false; } void GLC_Light::setDiffuseColor(const QColor& color) { m_DiffuseColor= color; - m_ListIsValid = false; + m_IsValid= false; } void GLC_Light::setSpecularColor(const QColor& color) { m_SpecularColor= color; - m_ListIsValid = false; + m_IsValid= false; } void GLC_Light::setTwoSided(const bool mode) { - if (m_TwoSided != mode) - { - m_TwoSided= mode; - m_ListIsValid = false; - } + m_TwoSided= mode; + m_IsValid= false; } void GLC_Light::setConstantAttenuation(GLfloat constantAttenuation) { m_ConstantAttenuation= constantAttenuation; - m_ListIsValid = false; + m_IsValid= false; } void GLC_Light::setLinearAttenuation(GLfloat linearAttenuation) { m_LinearAttenuation= linearAttenuation; - m_ListIsValid = false; + m_IsValid= false; } void GLC_Light::setQuadraticAttenuation(GLfloat quadraticAttenuation) { m_QuadraticAttenuation= quadraticAttenuation; - m_ListIsValid = false; + m_IsValid= false; } void GLC_Light::setSpotDirection(const GLC_Vector3d& direction) { m_SpotDirection= direction; - m_ListIsValid = false; + m_IsValid= false; } void GLC_Light::setSpotCutoffAngle(GLfloat cutoffAngle) { m_SpotCutoffAngle= cutoffAngle; - m_ListIsValid = false; + m_IsValid= false; } void GLC_Light::setSpotEponent(GLfloat exponent) { m_SpotExponent= exponent; - m_ListIsValid = false; + m_IsValid= false; } ////////////////////////////////////////////////////////////////////// // OpenGL Functions ////////////////////////////////////////////////////////////////////// -void GLC_Light::creationList(GLenum Mode) + +void GLC_Light::disable() { - if(0 == m_ListID) // OpenGL list not created + if (NULL != m_pContext) { - m_ListID= glGenLists(1); - - if (0 == m_ListID) // OpenGL List Id not get - { - glDraw(); - qDebug("GLC_Light::CreationListe Display list not create"); - } - } - if (0 != m_ListID) - { - // OpenGL list creation and execution - glNewList(m_ListID, Mode); - - // Light execution - glDraw(); - - glEndList(); - - m_ListIsValid= true; - } - - // OpenGL error handler - GLenum error= glGetError(); - if (error != GL_NO_ERROR) - { - GLC_OpenGlException OpenGlException("GLC_Light::CreationList ", error); - throw(OpenGlException); + glDisable(m_LightID); } } -void GLC_Light::glExecute(GLenum Mode) + +void GLC_Light::glExecute() { - - if (!m_ListIsValid) + if (NULL == m_pContext) { - // OpenGL list is not valid - /* - GLfloat value; - glGetFloatv(GL_CONSTANT_ATTENUATION, &value); - qDebug() << "GL_CONSTANT_ATTENUATION " << value; - glGetFloatv(GL_LINEAR_ATTENUATION, &value); - qDebug() << "GL_LINEAR_ATTENUATION " << value; - glGetFloatv(GL_QUADRATIC_ATTENUATION, &value); - qDebug() << "GL_QUADRATIC_ATTENUATION " << value; - */ - creationList(Mode); - } - else - { - glCallList(m_ListID); + m_pContext= const_cast(QGLContext::currentContext()); + Q_ASSERT(NULL != m_pContext); + addNewLight(); } - // OpenGL error handler - GLenum error= glGetError(); - if (error != GL_NO_ERROR) + GLC_Context::current()->glcEnableLighting(true); + glEnable(m_LightID); + + if (m_pContext != QGLContext::currentContext()) { - GLC_OpenGlException OpenGlException("GLC_Light::GlExecute ", error); - throw(OpenGlException); + Q_ASSERT(QGLContext::areSharing(m_pContext, QGLContext::currentContext())); + m_IsValid= false; } + Q_ASSERT(m_pContext->isValid()); -} - -void GLC_Light::glDraw(void) -{ - // Set the lighting model - if (m_TwoSided) - { - glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1); - } - else - { - glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 0); - } - - // Color - GLfloat setArray[4]= {static_cast(m_AmbientColor.redF()), - static_cast(m_AmbientColor.greenF()), - static_cast(m_AmbientColor.blueF()), - static_cast(m_AmbientColor.alphaF())}; - glLightfv(m_LightID, GL_AMBIENT, setArray); // Setup The Ambient Light - - setArray[0]= static_cast(m_DiffuseColor.redF()); - setArray[1]= static_cast(m_DiffuseColor.greenF()); - setArray[2]= static_cast(m_DiffuseColor.blueF()); - setArray[3]= static_cast(m_DiffuseColor.alphaF()); - glLightfv(m_LightID, GL_DIFFUSE, setArray); // Setup The Diffuse Light - - - setArray[0]= static_cast(m_SpecularColor.redF()); - setArray[1]= static_cast(m_SpecularColor.greenF()); - setArray[2]= static_cast(m_SpecularColor.blueF()); - setArray[3]= static_cast(m_SpecularColor.alphaF()); - glLightfv(m_LightID, GL_SPECULAR, setArray); // Setup The specular Light + GLfloat setArray[4]; // Position setArray[0]= static_cast(m_Position.x()); @@ -328,30 +251,67 @@ void GLC_Light::glDraw(void) { setArray[3]= 1.0f; glLightfv(m_LightID, GL_POSITION, setArray); // Position of the Light - glLightf(m_LightID, GL_CONSTANT_ATTENUATION, m_ConstantAttenuation); - glLightf(m_LightID, GL_LINEAR_ATTENUATION, m_LinearAttenuation); - glLightf(m_LightID, GL_QUADRATIC_ATTENUATION, m_QuadraticAttenuation); - } - // Spot light parameters - if (LightSpot == m_LightType) + + if (!m_IsValid) { - // Spot Direction - setArray[0]= static_cast(m_SpotDirection.x()); - setArray[1]= static_cast(m_SpotDirection.y()); - setArray[2]= static_cast(m_SpotDirection.z()); - glLightfv(m_LightID, GL_SPOT_DIRECTION, setArray); - glLightf(m_LightID, GL_SPOT_EXPONENT, m_SpotExponent); - glLightf(m_LightID, GL_SPOT_CUTOFF, m_SpotCutoffAngle); - } + // Set the lighting model + if (m_TwoSided) + { + glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1); + } + else + { + glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 0); + } + // Color + setArray[0]= static_cast(m_AmbientColor.redF()); + setArray[1]= static_cast(m_AmbientColor.greenF()); + setArray[2]= static_cast(m_AmbientColor.blueF()); + setArray[3]= static_cast(m_AmbientColor.alphaF()); + glLightfv(m_LightID, GL_AMBIENT, setArray); // Setup The Ambient Light + + setArray[0]= static_cast(m_DiffuseColor.redF()); + setArray[1]= static_cast(m_DiffuseColor.greenF()); + setArray[2]= static_cast(m_DiffuseColor.blueF()); + setArray[3]= static_cast(m_DiffuseColor.alphaF()); + glLightfv(m_LightID, GL_DIFFUSE, setArray); // Setup The Diffuse Light + + + setArray[0]= static_cast(m_SpecularColor.redF()); + setArray[1]= static_cast(m_SpecularColor.greenF()); + setArray[2]= static_cast(m_SpecularColor.blueF()); + setArray[3]= static_cast(m_SpecularColor.alphaF()); + glLightfv(m_LightID, GL_SPECULAR, setArray); // Setup The specular Light + + if (LightDirection != m_LightType) + glLightf(m_LightID, GL_CONSTANT_ATTENUATION, m_ConstantAttenuation); + glLightf(m_LightID, GL_LINEAR_ATTENUATION, m_LinearAttenuation); + glLightf(m_LightID, GL_QUADRATIC_ATTENUATION, m_QuadraticAttenuation); + + // Spot light parameters + if (LightSpot == m_LightType) + { + // Spot Direction + setArray[0]= static_cast(m_SpotDirection.x()); + setArray[1]= static_cast(m_SpotDirection.y()); + setArray[2]= static_cast(m_SpotDirection.z()); + glLightfv(m_LightID, GL_SPOT_DIRECTION, setArray); + glLightf(m_LightID, GL_SPOT_EXPONENT, m_SpotExponent); + glLightf(m_LightID, GL_SPOT_CUTOFF, m_SpotCutoffAngle); + } + + m_IsValid= true; + } // OpenGL error handler GLenum error= glGetError(); if (error != GL_NO_ERROR) { - GLC_OpenGlException OpenGlException("GLC_Light::GlDraw ", error); + qDebug() << "GLC_Light::glExecute Exception, id= " << m_LightID; + GLC_OpenGlException OpenGlException("GLC_Light::glExecute ", error); throw(OpenGlException); } @@ -361,12 +321,44 @@ void GLC_Light::glDraw(void) // Private services Functions ////////////////////////////////////////////////////////////////////// -void GLC_Light::deleteList(void) + +////////////////////////////////////////////////////////////////////// +// Private services fonction +////////////////////////////////////////////////////////////////////// +void GLC_Light::addNewLight() { - // if the list is valid, the list is deleted - if (glIsList(m_ListID)) + if (NULL != m_pContext) { - disable(); - glDeleteLists(m_ListID, 1); + if (!m_ContextToFreeLightSet.contains(m_pContext)) + { + m_ContextToFreeLightSet.insert(m_pContext, QSet()); + initForThisContext(); + } + + // Some OpenGL driver support only Light0 ??? + if (m_ContextToFreeLightSet.value(m_pContext).size() == m_MaxLight) + { + m_LightID= GL_LIGHT0; + } + else + { + m_LightID= *(m_ContextToFreeLightSet[m_pContext].constBegin()); + } + + m_ContextToFreeLightSet[m_pContext].remove(m_LightID); + } +} + +void GLC_Light::removeThisLight() +{ + if (NULL != m_pContext) + { + Q_ASSERT(m_ContextToFreeLightSet.contains(m_pContext)); + Q_ASSERT(!m_ContextToFreeLightSet[m_pContext].contains(m_LightID)); + m_ContextToFreeLightSet[m_pContext].insert(m_LightID); + if (m_ContextToFreeLightSet[m_pContext].size() == m_MaxLight) + { + m_ContextToFreeLightSet.remove(m_pContext); + } } } diff --git a/ground/openpilotgcs/src/libs/glc_lib/shading/glc_light.h b/ground/openpilotgcs/src/libs/glc_lib/shading/glc_light.h index 6714f3a33..761e76d2f 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/shading/glc_light.h +++ b/ground/openpilotgcs/src/libs/glc_lib/shading/glc_light.h @@ -27,11 +27,15 @@ #include #include +#include + #include "../glc_object.h" #include "../maths/glc_vector3d.h" #include "../glc_config.h" +class QGLContext; + ////////////////////////////////////////////////////////////////////// //! \class GLC_Light /*! \brief GLC_Light : OpenGL Point Light*/ @@ -57,11 +61,11 @@ public: public: //! Construct a default GLC_Light /*! By default, ambient color is black, diffuse Color is white and specular color is white*/ - GLC_Light(const QColor& color= Qt::white); + GLC_Light(const QGLContext* pContext= NULL, const QColor& color= Qt::white); //! Construct a default GLC_Light /*! By default, ambient color is black, diffuse Color is white and specular color is white*/ - GLC_Light(LightType lightType, const QColor& color= Qt::white); + GLC_Light(LightType lightType, const QGLContext* pContext= NULL, const QColor& color= Qt::white); //! Copy constructor GLC_Light(const GLC_Light& light); @@ -79,7 +83,7 @@ public: static int maxLightCount(); //! Return the number of builtable light - static int builtAbleLightCount(); + static int builtAbleLightCount(QGLContext* pContext); //! Return a GLC_Point3d representing light position inline GLC_Point3d position(void) const @@ -139,8 +143,8 @@ public: //@{ ////////////////////////////////////////////////////////////////////// public: - //! Init Max number of light - static void init(); + //! Init Max number of light for this light context + void initForThisContext(); //! Set lihgt's position by a point void setPosition(const GLC_Point3d &pos); @@ -185,16 +189,12 @@ public: //@{ ////////////////////////////////////////////////////////////////////// public: - //! Enable the light - inline void enable() const - {glEnable(m_LightID);} // Disable the light - inline void disable() const - {glDisable(m_LightID);} + void disable(); //! Execute OpenGL light - virtual void glExecute(GLenum Mode= GL_COMPILE_AND_EXECUTE); + virtual void glExecute(); //@} ////////////////////////////////////////////////////////////////////// @@ -203,21 +203,23 @@ public: ////////////////////////////////////////////////////////////////////// private: - //! OpenGL light set up - void glDraw(); - //! Display List creation - void creationList(GLenum Mode); - - //! Delete OpenGL Display list - void deleteList(); //@} +////////////////////////////////////////////////////////////////////// +// Private services fonction +////////////////////////////////////////////////////////////////////// +private: + //! Add context new light + void addNewLight(); + + //! Remove contetx light + void removeThisLight(); + ////////////////////////////////////////////////////////////////////// // Private Members ////////////////////////////////////////////////////////////////////// - private: //! OpenGL light ID GLenum m_LightID; @@ -225,12 +227,6 @@ private: //! The Light type LightType m_LightType; - //! OpenGL Display list ID - GLuint m_ListID; - - //! OpenGL list validity - bool m_ListIsValid; - //! Light ambiant color QColor m_AmbientColor; //! Light diffuse color @@ -266,7 +262,13 @@ private: //! Maximum number of light static GLint m_MaxLight; - //! Free light set - static QSet m_FreeLightSet; + //! The context of this light + QGLContext* m_pContext; + + //! Flag to know if this light is valid + bool m_IsValid; + + //! Mapping between context and light set + static QHash > m_ContextToFreeLightSet; }; #endif //GLC_LIGHT_H_ diff --git a/ground/openpilotgcs/src/libs/glc_lib/shading/glc_material.cpp b/ground/openpilotgcs/src/libs/glc_lib/shading/glc_material.cpp index 412a00008..a60e1b94c 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/shading/glc_material.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/shading/glc_material.cpp @@ -25,6 +25,7 @@ #include "glc_material.h" #include "../geometry/glc_geometry.h" #include "../glc_factory.h" +#include "../glc_openglexception.h" #include @@ -100,8 +101,8 @@ GLC_Material::GLC_Material(const QString& name ,const GLfloat *pDiffuseColor) // Others initOtherColor(); } -GLC_Material::GLC_Material(GLC_Texture* pTexture, const char *pName) -:GLC_Object(pName) +GLC_Material::GLC_Material(GLC_Texture* pTexture, const QString& name) +:GLC_Object(name) , m_AmbientColor() , m_DiffuseColor() , m_SpecularColor() @@ -460,11 +461,15 @@ void GLC_Material::setOpacity(const qreal alpha) ////////////////////////////////////////////////////////////////////// // Load the texture -void GLC_Material::glLoadTexture(void) +void GLC_Material::glLoadTexture(QGLContext* pContext) { if (m_pTexture != NULL) { - m_pTexture->glLoadTexture(); + m_pTexture->glLoadTexture(pContext); + } + else + { + qDebug() << "GLC_Material::glLoadTexture : Material without texture !"; } } @@ -492,9 +497,10 @@ void GLC_Material::glExecute() emissiveColor().blueF(), emissiveColor().alphaF()}; + const bool textureIsEnable= glIsEnabled(GL_TEXTURE_2D); if (m_pTexture != NULL) { - glEnable(GL_TEXTURE_2D); + if (!textureIsEnable) glEnable(GL_TEXTURE_2D); m_pTexture->glcBindTexture(); if (GLC_State::glslUsed()) { @@ -511,13 +517,13 @@ void GLC_Material::glExecute() if (GLC_State::glslUsed() && GLC_Shader::hasActiveShader()) { - glEnable(GL_TEXTURE_2D); + if (!textureIsEnable) glEnable(GL_TEXTURE_2D); GLC_Shader::currentShaderHandle()->programShaderHandle()->setUniformValue("tex", GLint(0)); GLC_Shader::currentShaderHandle()->programShaderHandle()->setUniformValue("useTexture", false); } else { - glDisable(GL_TEXTURE_2D); + if (textureIsEnable) glDisable(GL_TEXTURE_2D); } } @@ -530,14 +536,15 @@ void GLC_Material::glExecute() glColor4fv(pDiffuseColor); + // OpenGL Error handler - GLenum errCode; - if ((errCode= glGetError()) != GL_NO_ERROR) + GLenum error= glGetError(); + if (error != GL_NO_ERROR) { - const GLubyte* errString; - errString = gluErrorString(errCode); - qDebug("GLC_Material::GlExecute OpenGL Error %s", errString); + GLC_OpenGlException OpenGlException("GLC_Material::glExecute() ", error); + throw(OpenGlException); } + } // Execute OpenGL Material @@ -563,9 +570,11 @@ void GLC_Material::glExecute(float overwriteTransparency) emissiveColor().blueF(), overwriteTransparency}; + const bool textureIsEnable= glIsEnabled(GL_TEXTURE_2D); + if (m_pTexture != NULL) { - glEnable(GL_TEXTURE_2D); + if (!textureIsEnable) glEnable(GL_TEXTURE_2D); m_pTexture->glcBindTexture(); if (GLC_State::glslUsed()) { @@ -578,7 +587,7 @@ void GLC_Material::glExecute(float overwriteTransparency) } else { - glDisable(GL_TEXTURE_2D); + if (textureIsEnable) glDisable(GL_TEXTURE_2D); if (GLC_State::glslUsed()) { if (GLC_Shader::hasActiveShader()) @@ -598,12 +607,11 @@ void GLC_Material::glExecute(float overwriteTransparency) glColor4fv(pDiffuseColor); // OpenGL Error handler - GLenum errCode; - if ((errCode= glGetError()) != GL_NO_ERROR) + GLenum error= glGetError(); + if (error != GL_NO_ERROR) { - const GLubyte* errString; - errString = gluErrorString(errCode); - qDebug("GLC_Material::glExecute(float) OpenGL Error %s", errString); + GLC_OpenGlException OpenGlException("GLC_Material::glExecute(float overwriteTransparency) ", error); + throw(OpenGlException); } } @@ -687,7 +695,7 @@ QDataStream &operator>>(QDataStream &stream, GLC_Material &material) stream >> hasTexture; if (hasTexture) { - GLC_Texture texture(GLC_Factory::instance()->context()); + GLC_Texture texture; stream >> texture; material.setTexture(new GLC_Texture(texture)); } diff --git a/ground/openpilotgcs/src/libs/glc_lib/shading/glc_material.h b/ground/openpilotgcs/src/libs/glc_lib/shading/glc_material.h index 759f3953a..668b47aae 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/shading/glc_material.h +++ b/ground/openpilotgcs/src/libs/glc_lib/shading/glc_material.h @@ -68,7 +68,7 @@ public: GLC_Material(const QString& name, const GLfloat *); //! Construct textured GLC_Material - GLC_Material(GLC_Texture* pTexture, const char *pName); + GLC_Material(GLC_Texture* pTexture, const QString& name= QString()); //! Copy constructor /*! Hast usage table are not copying @@ -215,7 +215,7 @@ public: public: //! Load the texture - void glLoadTexture(void); + void glLoadTexture(QGLContext* pContext= NULL); //! Execute OpenGL Material virtual void glExecute(); diff --git a/ground/openpilotgcs/src/libs/glc_lib/shading/glc_renderproperties.cpp b/ground/openpilotgcs/src/libs/glc_lib/shading/glc_renderproperties.cpp index c6c974958..184bbc77c 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/shading/glc_renderproperties.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/shading/glc_renderproperties.cpp @@ -178,7 +178,7 @@ bool GLC_RenderProperties::needToRenderWithTransparency() const Q_ASSERT(NULL != m_pOverwriteMaterial); renderWithTransparency= m_pOverwriteMaterial->isTransparent(); } - else if (m_RenderMode == glc::OverwriteTransparency) + else if ((m_RenderMode == glc::OverwriteTransparency) || (m_RenderMode == glc::OverwriteTransparencyAndMaterial)) { Q_ASSERT(-1.0f != m_OverwriteOpacity); renderWithTransparency= (m_OverwriteOpacity < 1.0f); diff --git a/ground/openpilotgcs/src/libs/glc_lib/shading/glc_renderproperties.h b/ground/openpilotgcs/src/libs/glc_lib/shading/glc_renderproperties.h index 1b8c48e5d..40a23578c 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/shading/glc_renderproperties.h +++ b/ground/openpilotgcs/src/libs/glc_lib/shading/glc_renderproperties.h @@ -43,6 +43,7 @@ namespace glc NormalRenderMode, OverwriteMaterial, OverwriteTransparency, + OverwriteTransparencyAndMaterial, PrimitiveSelected, OverwritePrimitiveMaterial, BodySelection, diff --git a/ground/openpilotgcs/src/libs/glc_lib/shading/glc_selectionmaterial.cpp b/ground/openpilotgcs/src/libs/glc_lib/shading/glc_selectionmaterial.cpp index ff27e1e39..7b341e5d7 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/shading/glc_selectionmaterial.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/shading/glc_selectionmaterial.cpp @@ -21,10 +21,13 @@ *****************************************************************************/ //! \file glc_selectionmaterial.cpp implementation of the GLC_SelectionMaterial class. +#include + #include "glc_selectionmaterial.h" #include "glc_material.h" -GLC_Shader* GLC_SelectionMaterial::m_pSelectionShader= NULL; + +QHash GLC_SelectionMaterial::m_SelectionShaderHash; GLC_uint GLC_SelectionMaterial::m_SelectionMaterialId= 0; GLC_Material* GLC_SelectionMaterial::m_pMaterial= NULL; @@ -96,32 +99,82 @@ void GLC_SelectionMaterial::glExecute() } } -void GLC_SelectionMaterial::initShader() +void GLC_SelectionMaterial::initShader(const QGLContext* pContext) { - if (m_pSelectionShader == NULL) - { - m_pSelectionShader= new GLC_Shader; - } - m_pSelectionShader->createAndCompileProgrammShader(); + Q_ASSERT(m_SelectionShaderHash.contains(pContext)); + m_SelectionShaderHash.value(pContext)->createAndCompileProgrammShader(); } -void GLC_SelectionMaterial::setShaders(QFile& vertex, QFile& fragment) +void GLC_SelectionMaterial::setShaders(QFile& vertex, QFile& fragment, const QGLContext* pContext) { - if (m_pSelectionShader == NULL) + if (m_SelectionShaderHash.contains(pContext)) { - m_pSelectionShader= new GLC_Shader; + deleteShader(pContext); + } + GLC_Shader* pShader= new GLC_Shader; + + pShader->setVertexAndFragmentShader(vertex, fragment); + m_SelectionShaderHash.insert(pContext, pShader); +} + + +void GLC_SelectionMaterial::useShader() +{ + QGLContext* pContext= const_cast(QGLContext::currentContext()); + Q_ASSERT(NULL != pContext); + Q_ASSERT(pContext->isValid()); + if(!m_SelectionShaderHash.contains(pContext)) + { + Q_ASSERT(pContext->isSharing()); + pContext= sharingContext(pContext); + Q_ASSERT(NULL != pContext); } - m_pSelectionShader->setVertexAndFragmentShader(vertex, fragment); + m_SelectionShaderHash.value(pContext)->use(); +} + +void GLC_SelectionMaterial::unUseShader() +{ + QGLContext* pContext= const_cast(QGLContext::currentContext()); + Q_ASSERT(NULL != pContext); + Q_ASSERT(pContext->isValid()); + if(!m_SelectionShaderHash.contains(pContext)) + { + Q_ASSERT(pContext->isSharing()); + pContext= sharingContext(pContext); + Q_ASSERT(NULL != pContext); + } + + m_SelectionShaderHash.value(pContext)->unuse(); +} + +////////////////////////////////////////////////////////////////////// +// Private services fonction +////////////////////////////////////////////////////////////////////// +QGLContext* GLC_SelectionMaterial::sharingContext(const QGLContext* pContext) +{ + QGLContext* pSharingContext= NULL; + QHash::const_iterator iContext= m_SelectionShaderHash.constBegin(); + + while ((NULL == pSharingContext) && (iContext != m_SelectionShaderHash.constEnd())) + { + const QGLContext* pCurrentContext= iContext.key(); + if (QGLContext::areSharing(pContext, pCurrentContext)) + { + pSharingContext= const_cast(pCurrentContext); + } + ++iContext; + } + + return pSharingContext; } //! delete shader -void GLC_SelectionMaterial::deleteShader() +void GLC_SelectionMaterial::deleteShader(const QGLContext* pContext) { - if (NULL != m_pSelectionShader) - { - m_pSelectionShader->deleteShader(); - delete m_pSelectionShader; - m_pSelectionShader= NULL; - } + Q_ASSERT(m_SelectionShaderHash.contains(pContext)); + GLC_Shader* pShader= m_SelectionShaderHash.value(pContext); + pShader->deleteShader(); + delete pShader; + m_SelectionShaderHash.remove(pContext); } diff --git a/ground/openpilotgcs/src/libs/glc_lib/shading/glc_selectionmaterial.h b/ground/openpilotgcs/src/libs/glc_lib/shading/glc_selectionmaterial.h index 56a929541..01fd89f4d 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/shading/glc_selectionmaterial.h +++ b/ground/openpilotgcs/src/libs/glc_lib/shading/glc_selectionmaterial.h @@ -26,11 +26,14 @@ #include #include +#include + #include "../glc_ext.h" #include "glc_shader.h" #include "../glc_config.h" +class QGLContext; class GLC_Material; ////////////////////////////////////////////////////////////////////// @@ -67,25 +70,32 @@ public: //! Execute OpenGL Material static void glExecute(); //! Init shader - static void initShader(); + static void initShader(const QGLContext* pContext); //! delete shader - static void deleteShader(); + static void deleteShader(const QGLContext* pContext); //! Set shader - static void setShaders(QFile& vertex, QFile& fragment); + static void setShaders(QFile& vertex, QFile& fragment, const QGLContext* pContext); //! Use shader - inline static void useShader() {m_pSelectionShader->use();} + static void useShader(); //! Unused shader - inline static void unUseShader() {m_pSelectionShader->unuse();} + static void unUseShader(); //@} +////////////////////////////////////////////////////////////////////// +// Private services fonction +////////////////////////////////////////////////////////////////////// +private: + //! Return the sharing context of the given context + static QGLContext* sharingContext(const QGLContext* pContext); + ////////////////////////////////////////////////////////////////////// // Private members ////////////////////////////////////////////////////////////////////// private: //! Selection Shader - static GLC_Shader* m_pSelectionShader; + static QHash m_SelectionShaderHash; //! Selection material id static GLC_uint m_SelectionMaterialId; diff --git a/ground/openpilotgcs/src/libs/glc_lib/shading/glc_shader.cpp b/ground/openpilotgcs/src/libs/glc_lib/shading/glc_shader.cpp index e91352efa..437a18886 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/shading/glc_shader.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/shading/glc_shader.cpp @@ -27,6 +27,8 @@ #include #include "../glc_exception.h" #include "../glc_state.h" +#include "../glc_context.h" +#include "glc_light.h" // Static member initialization QStack GLC_Shader::m_ShadingGroupStack; @@ -39,7 +41,26 @@ GLC_Shader::GLC_Shader() , m_ProgramShader() , m_ProgramShaderId(glc::GLC_GenShaderGroupID()) , m_Name("Empty Shader") +, m_PositionAttributeId(-1) +, m_TextcoordAttributeId(-1) +, m_ColorAttributeId(-1) +, m_NormalAttributeId(-1) +, m_ModelViewLocationId(-1) +, m_MvpLocationId(-1) +, m_InvModelViewLocationId(-1) +, m_EnableLightingId(-1) +, m_LightsEnableStateId(-1) +, m_LightsPositionId() +, m_LightsAmbientColorId() +, m_LightsDiffuseColorId() +, m_LightsSpecularColorId() +, m_LightsSpotDirectionId() +, m_LightsAttenuationFactorsId() +, m_LightsSpotExponentId() +, m_LightsSpotCutoffAngleId() +, m_LightsComputeDistanceAttenuationId() { + initLightsUniformId(); m_ShaderProgramHash.insert(m_ProgramShaderId, this); } @@ -49,7 +70,26 @@ GLC_Shader::GLC_Shader(QFile& vertex, QFile& fragment) , m_ProgramShader() , m_ProgramShaderId(glc::GLC_GenShaderGroupID()) , m_Name("Empty Shader") +, m_PositionAttributeId(-1) +, m_TextcoordAttributeId(-1) +, m_ColorAttributeId(-1) +, m_NormalAttributeId(-1) +, m_ModelViewLocationId(-1) +, m_MvpLocationId(-1) +, m_InvModelViewLocationId(-1) +, m_EnableLightingId(-1) +, m_LightsEnableStateId(-1) +, m_LightsPositionId() +, m_LightsAmbientColorId() +, m_LightsDiffuseColorId() +, m_LightsSpecularColorId() +, m_LightsSpotDirectionId() +, m_LightsAttenuationFactorsId() +, m_LightsSpotExponentId() +, m_LightsSpotCutoffAngleId() +, m_LightsComputeDistanceAttenuationId() { + initLightsUniformId(); m_ShaderProgramHash.insert(m_ProgramShaderId, this); setVertexAndFragmentShader(vertex, fragment); } @@ -60,7 +100,26 @@ GLC_Shader::GLC_Shader(const GLC_Shader& shader) , m_ProgramShader() , m_ProgramShaderId(glc::GLC_GenShaderGroupID()) , m_Name(shader.m_Name) +, m_PositionAttributeId(-1) +, m_TextcoordAttributeId(-1) +, m_ColorAttributeId(-1) +, m_NormalAttributeId(-1) +, m_ModelViewLocationId(-1) +, m_MvpLocationId(-1) +, m_InvModelViewLocationId(-1) +, m_EnableLightingId(-1) +, m_LightsEnableStateId(-1) +, m_LightsPositionId() +, m_LightsAmbientColorId() +, m_LightsDiffuseColorId() +, m_LightsSpecularColorId() +, m_LightsSpotDirectionId() +, m_LightsAttenuationFactorsId() +, m_LightsSpotExponentId() +, m_LightsSpotCutoffAngleId() +, m_LightsComputeDistanceAttenuationId() { + initLightsUniformId(); m_ShaderProgramHash.insert(m_ProgramShaderId, this); if (shader.m_VertexShader.isCompiled()) @@ -130,11 +189,12 @@ void GLC_Shader::use() { m_CurrentShadingGroupId= m_ProgramShaderId; m_ShaderProgramHash.value(m_CurrentShadingGroupId)->m_ProgramShader.bind(); + GLC_Context::current()->updateUniformVariables(); } } -bool GLC_Shader::use(GLuint shaderId) +bool GLC_Shader::use(GLC_uint shaderId) { Q_ASSERT(0 != shaderId); if (GLC_State::isInSelectionMode()) return false; @@ -147,7 +207,9 @@ bool GLC_Shader::use(GLuint shaderId) { m_CurrentShadingGroupId= shaderId; m_ShaderProgramHash.value(m_CurrentShadingGroupId)->m_ProgramShader.bind(); + GLC_Context::current()->updateUniformVariables(); } + return true; } else @@ -178,6 +240,7 @@ void GLC_Shader::unuse() void GLC_Shader::createAndCompileProgrammShader() { + qDebug() << "GLC_Shader::createAndCompileProgrammShader()"; m_ProgramShader.addShader(&m_VertexShader); m_ProgramShader.addShader(&m_FragmentShader); @@ -187,6 +250,52 @@ void GLC_Shader::createAndCompileProgrammShader() GLC_Exception exception(message); throw(exception); } + else + { + m_PositionAttributeId= m_ProgramShader.attributeLocation("a_position"); + //qDebug() << "m_PositionAttributeId " << m_PositionAttributeId; + m_TextcoordAttributeId= m_ProgramShader.attributeLocation("a_textcoord0"); + //qDebug() << "m_TextcoordAttributeId " << m_TextcoordAttributeId; + m_ColorAttributeId= m_ProgramShader.attributeLocation("a_color"); + //qDebug() << "m_ColorAttributeId " << m_ColorAttributeId; + m_NormalAttributeId= m_ProgramShader.attributeLocation("a_normal"); + //qDebug() << "m_NormalAttributeId " << m_NormalAttributeId; + + m_ModelViewLocationId= m_ProgramShader.uniformLocation("modelview_matrix"); + //qDebug() << "m_ModelViewLocationId " << m_ModelViewLocationId; + m_MvpLocationId= m_ProgramShader.uniformLocation("mvp_matrix"); + //qDebug() << "m_MvpLocationId " << m_MvpLocationId; + m_InvModelViewLocationId= m_ProgramShader.uniformLocation("inv_modelview_matrix"); + //qDebug() << "m_InvModelViewLocationId " << m_InvModelViewLocationId; + m_EnableLightingId= m_ProgramShader.uniformLocation("enable_lighting"); + //qDebug() << "m_EnableLightingId " << m_EnableLightingId; + m_LightsEnableStateId= m_ProgramShader.uniformLocation("light_enable_state"); + //qDebug() << "m_LightsEnableStateId " << m_LightsEnableStateId; + const int size= GLC_Light::maxLightCount(); + for (int i= (GL_LIGHT0); i < (size + GL_LIGHT0); ++i) + { + m_LightsPositionId[i]= m_ProgramShader.uniformLocation("light_state[" + QString::number(i) + "].position"); + //qDebug() << "Position id " << m_LightsPositionId.value(i); + m_LightsAmbientColorId[i]= m_ProgramShader.uniformLocation("light_state[" + QString::number(i) + "].ambient_color"); + //qDebug() << "m_LightsAmbientColorId " << m_LightsAmbientColorId.value(i); + m_LightsDiffuseColorId[i]= m_ProgramShader.uniformLocation("light_state[" + QString::number(i) + "].diffuse_color"); + //qDebug() << "m_LightsDiffuseColorId " << m_LightsDiffuseColorId.value(i); + m_LightsSpecularColorId[i]= m_ProgramShader.uniformLocation("light_state[" + QString::number(i) + "].specular_color"); + //qDebug() << "m_LightsSpecularColorId " << m_LightsSpecularColorId.value(i); + m_LightsSpotDirectionId[i]= m_ProgramShader.uniformLocation("light_state[" + QString::number(i) + "].spot_direction"); + //qDebug() << "m_LightsSpotDirectionId " << m_LightsSpotDirectionId.value(i); + m_LightsAttenuationFactorsId[i]= m_ProgramShader.uniformLocation("light_state[" + QString::number(i) + "].attenuation_factors"); + //qDebug() << "m_LightsAttenuationFactorsId " << m_LightsAttenuationFactorsId.value(i); + m_LightsSpotExponentId[i]= m_ProgramShader.uniformLocation("light_state[" + QString::number(i) + "].spot_exponent"); + //qDebug() << "m_LightsSpotExponentId " << m_LightsSpotExponentId.value(i); + m_LightsSpotCutoffAngleId[i]= m_ProgramShader.uniformLocation("light_state[" + QString::number(i) + "].spot_cutoff_angle"); + //qDebug() << "m_LightsSpotCutoffAngleId " << m_LightsSpotCutoffAngleId.value(i); + m_LightsComputeDistanceAttenuationId[i]= m_ProgramShader.uniformLocation("light_state[" + QString::number(i) + "].compute_distance_attenuation"); + //qDebug() << "m_LightsComputeDistanceAttenuationId " << m_LightsComputeDistanceAttenuationId.value(i); + + + } + } } void GLC_Shader::deleteShader() @@ -255,3 +364,29 @@ void GLC_Shader::replaceShader(const GLC_Shader& sourceShader) } +void GLC_Shader::initLightsUniformId() +{ + m_LightsPositionId.clear(); + m_LightsAmbientColorId.clear(); + m_LightsDiffuseColorId.clear(); + m_LightsSpecularColorId.clear(); + m_LightsSpotDirectionId.clear(); + m_LightsAttenuationFactorsId.clear(); + m_LightsSpotExponentId.clear(); + m_LightsSpotCutoffAngleId.clear(); + m_LightsComputeDistanceAttenuationId.clear(); + + for (int i= 0; i < GLC_Light::maxLightCount(); ++i) + { + m_LightsPositionId.insert(GL_LIGHT0 + i, -1); + m_LightsAmbientColorId.insert(GL_LIGHT0 + i, -1); + m_LightsDiffuseColorId.insert(GL_LIGHT0 + i, -1); + m_LightsSpecularColorId.insert(GL_LIGHT0 + i, -1); + m_LightsSpotDirectionId.insert(GL_LIGHT0 + i, -1); + m_LightsAttenuationFactorsId.insert(GL_LIGHT0 + i, -1); + m_LightsSpotExponentId.insert(GL_LIGHT0 + i, -1); + m_LightsSpotCutoffAngleId.insert(GL_LIGHT0 + i, -1); + m_LightsComputeDistanceAttenuationId.insert(GL_LIGHT0 + i, -1); + } +} + diff --git a/ground/openpilotgcs/src/libs/glc_lib/shading/glc_shader.h b/ground/openpilotgcs/src/libs/glc_lib/shading/glc_shader.h index fac9dc79a..a08cc550c 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/shading/glc_shader.h +++ b/ground/openpilotgcs/src/libs/glc_lib/shading/glc_shader.h @@ -32,6 +32,7 @@ #include #include #include +#include #include "../glc_config.h" @@ -89,6 +90,22 @@ public: inline QGLShaderProgram* programShaderHandle() {return &m_ProgramShader;} + //! Return the position attribute id + inline int positionAttributeId() const + {return m_PositionAttributeId;} + + //! Return the texture coordinate attribute id + inline int textureAttributeId() const + {return m_TextcoordAttributeId;} + + //! Return the color attribute id + inline int colorAttributeId() const + {return m_ColorAttributeId;} + + //! Return the normal attribute id + inline int normalAttributeId() const + {return m_NormalAttributeId;} + //! Return the number of shader static int shaderCount(); @@ -130,6 +147,62 @@ public: inline void setName(const QString& name) {m_Name= name;} + //! Return the modelView location id + inline int modelViewLocationId() const + {return m_ModelViewLocationId;} + + //! Return the modelView Projection matrix id + inline int mvpLocationId() const + {return m_MvpLocationId;} + + //! Return the inverse modelView location id + inline int invModelViewLocationId() const + {return m_InvModelViewLocationId;} + + //! Return the enable lighting location id + inline int enableLightingId() const + {return m_EnableLightingId;} + + //! Return the lights enable state id + inline int lightsEnableStateId() const + {return m_LightsEnableStateId;} + + //! Return the light position id of the given light id + inline int lightPositionId(GLenum lightId) const + {return m_LightsPositionId.value(lightId);} + + //! Return the light ambient color id of the given light id + inline int lightAmbientColorId(GLenum lightId) const + {return m_LightsAmbientColorId.value(lightId);} + + //! Return the light diffuse color id of the given light id + inline int lightDiffuseColorId(GLenum lightId) const + {return m_LightsDiffuseColorId.value(lightId);} + + //! Return the light specular color id of the given light id + inline int lightSpecularColorId(GLenum lightId) const + {return m_LightsSpecularColorId.value(lightId);} + + //! Return the light spot direction id of the given light id + inline int lightSpotDirectionId(GLenum lightId) const + {return m_LightsSpotDirectionId.value(lightId);} + + //! Return the light attenuation factors id of the given light id + inline int lightAttebuationFactorsId(GLenum lightId) const + {return m_LightsAttenuationFactorsId.value(lightId);} + + //! Return the light spot exponent id of the given light id + inline int lightSpotExponentId(GLenum lightId) const + {return m_LightsSpotExponentId.value(lightId);} + + //! Return the light spot cutoff id of the given light id + inline int lightSpotCutoffId(GLenum lightId) const + {return m_LightsSpotCutoffAngleId.value(lightId);} + + //! Return the light compute distance attenuation id of the given light id + inline int lightComputeDistanceAttenuationId(GLenum lightId) const + {return m_LightsComputeDistanceAttenuationId.value(lightId);} + //@} ////////////////////////////////////////////////////////////////////// @@ -143,7 +216,7 @@ public: //! Use specified program shader /*! Return true if the given shading group id is usable*/ - static bool use(GLuint ShadingGroupId); + static bool use(GLC_uint ShadingGroupId); //! unuse programm shader static void unuse(); @@ -156,6 +229,12 @@ public: void deleteShader(); //@} +////////////////////////////////////////////////////////////////////// +// private services function +////////////////////////////////////////////////////////////////////// +private: + //! Init light uniform id + void initLightsUniformId(); ////////////////////////////////////////////////////////////////////// // private members ////////////////////////////////////////////////////////////////////// @@ -184,6 +263,60 @@ private: //! The Shader's name QString m_Name; + //! The position attribute id + int m_PositionAttributeId; + + //! The Texture coordinate attribute id + int m_TextcoordAttributeId; + + //! The color attribute id + int m_ColorAttributeId; + + //! The Normal attribute id + int m_NormalAttributeId; + + //! The modelView location matrix id + int m_ModelViewLocationId; + + //! The modelView Projection matrix id + int m_MvpLocationId; + + //! The inverse modelView location id + int m_InvModelViewLocationId; + + //! The enable lighting id + int m_EnableLightingId; + + //! Lights enable states id + int m_LightsEnableStateId; + + //! Lights positions id + QMap m_LightsPositionId; + + //! Lights ambient color id + QMap m_LightsAmbientColorId; + + //! Lights diffuse color id + QMap m_LightsDiffuseColorId; + + //! Lights specular color id + QMap m_LightsSpecularColorId; + + //! Lights spot direction id + QMap m_LightsSpotDirectionId; + + //! Lights attenuation factors id + QMap m_LightsAttenuationFactorsId; + + //! Lights spot exponent id + QMap m_LightsSpotExponentId; + + //! Lights spot cutoff angle id + QMap m_LightsSpotCutoffAngleId; + + //! Lights compute distance attenuation + QMap m_LightsComputeDistanceAttenuationId; + }; #endif /*GLC_SHADER_H_*/ diff --git a/ground/openpilotgcs/src/libs/glc_lib/shading/glc_texture.cpp b/ground/openpilotgcs/src/libs/glc_lib/shading/glc_texture.cpp index ea1b5a8d8..be525d55e 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/shading/glc_texture.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/shading/glc_texture.cpp @@ -46,8 +46,8 @@ QHash GLC_Texture::m_TextureIdUsage; ////////////////////////////////////////////////////////////////////// //! Default constructor -GLC_Texture::GLC_Texture(const QGLContext *pContext) -: m_pQGLContext(const_cast(pContext)) +GLC_Texture::GLC_Texture() +: m_pQGLContext(NULL) , m_FileName() , m_GlTextureID(0) , m_textureImage() @@ -58,8 +58,8 @@ GLC_Texture::GLC_Texture(const QGLContext *pContext) } // Constructor with fileName -GLC_Texture::GLC_Texture(const QGLContext *pContext, const QString &Filename) -: m_pQGLContext(const_cast(pContext)) +GLC_Texture::GLC_Texture(const QString &Filename) +: m_pQGLContext(NULL) , m_FileName(Filename) , m_GlTextureID(0) , m_textureImage(loadFromFile(m_FileName)) @@ -76,8 +76,8 @@ GLC_Texture::GLC_Texture(const QGLContext *pContext, const QString &Filename) } } // Constructor with QFile -GLC_Texture::GLC_Texture(const QGLContext *pContext, const QFile &file) -: m_pQGLContext(const_cast(pContext)) +GLC_Texture::GLC_Texture(const QFile &file) +: m_pQGLContext(NULL) , m_FileName(file.fileName()) , m_GlTextureID(0) , m_textureImage() @@ -96,8 +96,8 @@ GLC_Texture::GLC_Texture(const QGLContext *pContext, const QFile &file) } // Constructor with QImage -GLC_Texture::GLC_Texture(const QGLContext* pContext, const QImage& image, const QString& fileName) -: m_pQGLContext(const_cast(pContext)) +GLC_Texture::GLC_Texture(const QImage& image, const QString& fileName) +: m_pQGLContext(NULL) , m_FileName(fileName) , m_GlTextureID(0) , m_textureImage(image) @@ -191,14 +191,19 @@ void GLC_Texture::setMaxTextureSize(const QSize& size) // Private OpenGL functions ////////////////////////////////////////////////////////////////////// // Load the texture -void GLC_Texture::glLoadTexture(void) +void GLC_Texture::glLoadTexture(QGLContext* pContext) { if (m_GlTextureID == 0) { - if (NULL == m_pQGLContext) + if (NULL == pContext) { m_pQGLContext= const_cast(QGLContext::currentContext()); } + else + { + m_pQGLContext= pContext; + } + // Test image size if ((m_textureImage.height() > m_MaxTextureSize.height()) || (m_textureImage.width() > m_MaxTextureSize.width())) @@ -329,7 +334,7 @@ QDataStream &operator>>(QDataStream &stream, GLC_Texture &texture) { QString fileName; stream >> fileName; - texture= GLC_Texture(texture.context(), fileName); + texture= GLC_Texture(fileName); return stream; } diff --git a/ground/openpilotgcs/src/libs/glc_lib/shading/glc_texture.h b/ground/openpilotgcs/src/libs/glc_lib/shading/glc_texture.h index 84ffa3d45..86d1c52de 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/shading/glc_texture.h +++ b/ground/openpilotgcs/src/libs/glc_lib/shading/glc_texture.h @@ -47,16 +47,16 @@ class GLC_LIB_EXPORT GLC_Texture public: //! Default constructor - GLC_Texture(const QGLContext*); + GLC_Texture(); //! Constructor with fileName - GLC_Texture(const QGLContext*, const QString&); + GLC_Texture(const QString&); //! Constructor with QFile - GLC_Texture(const QGLContext*, const QFile&); + GLC_Texture(const QFile&); //! Constructor with QImage - GLC_Texture(const QGLContext*, const QImage&, const QString& fileName= QString()); + GLC_Texture(const QImage&, const QString& fileName= QString()); //! Copy constructor GLC_Texture(const GLC_Texture& TextureToCopy); @@ -126,7 +126,7 @@ public: ////////////////////////////////////////////////////////////////////// public: //! Load the texture - void glLoadTexture(void); + void glLoadTexture(QGLContext* pContext= NULL); //! Bind texture in 2D mode void glcBindTexture(void); diff --git a/ground/openpilotgcs/src/libs/glc_lib/shading/shaders/default.frag b/ground/openpilotgcs/src/libs/glc_lib/shading/shaders/default.frag new file mode 100644 index 000000000..e69de29bb diff --git a/ground/openpilotgcs/src/libs/glc_lib/shading/shaders/default.vert b/ground/openpilotgcs/src/libs/glc_lib/shading/shaders/default.vert new file mode 100644 index 000000000..30044d866 --- /dev/null +++ b/ground/openpilotgcs/src/libs/glc_lib/shading/shaders/default.vert @@ -0,0 +1,12 @@ +uniform mat4 modelview_matrix; +uniform mat4 mvp_matrix; + +attribute vec4 a_position; +attribute vec4 a_textcoord0; +attribute vec4 a_color; +attribute vec3 a_normal; + +void main() +{ + gl_Position= mvp_matrix * a_position; +} \ No newline at end of file diff --git a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_camera.cpp b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_camera.cpp index 937b3c3ce..c830a83c6 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_camera.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_camera.cpp @@ -24,20 +24,7 @@ //! \file glc_camera.cpp Implementation of the GLC_Camera class. #include "glc_camera.h" - -#if defined(Q_OS_MAC) -#include "gl.h" -#include "glu.h" -#endif - -#if defined(Q_OS_WIN32) -#include "GL/gl.h" -#include "GL/glu.h" -#endif - -#if defined(Q_OS_LINUX) -#include "GL/glu.h" -#endif +#include "../glc_context.h" #include @@ -276,7 +263,7 @@ GLC_Camera& GLC_Camera::setCam(GLC_Point3d Eye, GLC_Point3d Target, GLC_Vector3d //Q_ASSERT((Angle > EPSILON) && ((PI - Angle) > EPSILON)); if ( !qFuzzyCompare(Angle - (PI / 2), 0.0)) - { // Angle not equal to 90¡ + { // Angle not equal to 90 const GLC_Vector3d AxeRot(VectCam ^ Up); GLC_Matrix4x4 MatRot(AxeRot, PI / 2); Up= MatRot * VectCam; @@ -445,9 +432,7 @@ GLC_Camera GLC_Camera::isoView() const ////////////////////////////////////////////////////////////////////// void GLC_Camera::glExecute() { - gluLookAt(m_Eye.x(), m_Eye.y(), m_Eye.z(), - m_Target.x(), m_Target.y(), m_Target.z(), - m_VectUp.x(), m_VectUp.y(), m_VectUp.z()); + GLC_Context::current()->glcMultMatrix(modelViewMatrix()); } ////////////////////////////////////////////////////////////////////// diff --git a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_flymover.cpp b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_flymover.cpp index 0965c09ed..abda923bc 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_flymover.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_flymover.cpp @@ -68,10 +68,10 @@ GLC_Mover* GLC_FlyMover::clone() const //Set Functions ////////////////////////////////////////////////////////////////////// -void GLC_FlyMover::init(QMouseEvent * e) +void GLC_FlyMover::init(const GLC_UserInput& userInput) { - m_PreviousVector= mapForFlying(static_cast(e->x()), static_cast(e->y())); - GLC_Point3d point= m_pViewport->unProject(e->x(), e->y()); + m_PreviousVector= mapForFlying(static_cast(userInput.x()), static_cast(userInput.y())); + GLC_Point3d point= m_pViewport->unProject(userInput.x(), userInput.y()); const double distance= (point - m_pViewport->cameraHandle()->eye()).length(); m_pViewport->cameraHandle()->setDistTargetEye(distance); // 5 secondes to travel @@ -83,10 +83,10 @@ void GLC_FlyMover::init(QMouseEvent * e) m_TimerId= QObject::startTimer(m_TimerInterval); } -bool GLC_FlyMover::move(QMouseEvent * e) +bool GLC_FlyMover::move(const GLC_UserInput& userInput) { - m_PreviousVector= mapForFlying(static_cast(e->x()), static_cast(e->y())); - GLC_Point3d point= m_pViewport->unProject(e->x(), e->y()); + m_PreviousVector= mapForFlying(static_cast(userInput.x()), static_cast(userInput.y())); + GLC_Point3d point= m_pViewport->unProject(userInput.x(), userInput.y()); const double distance= (point - m_pViewport->cameraHandle()->eye()).length(); m_pViewport->cameraHandle()->setDistTargetEye(distance); diff --git a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_flymover.h b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_flymover.h index 8ed298d51..cd8f569d5 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_flymover.h +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_flymover.h @@ -71,10 +71,10 @@ public: ////////////////////////////////////////////////////////////////////// public: //! Initialized the mover - virtual void init(QMouseEvent * e); + virtual void init(const GLC_UserInput& userInput); //! Move the camera - virtual bool move(QMouseEvent * e); + virtual bool move(const GLC_UserInput& userInput); //! Ends this mover virtual void ends(); diff --git a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_imageplane.cpp b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_imageplane.cpp index 0e8077528..0f0e6eeca 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_imageplane.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_imageplane.cpp @@ -25,26 +25,27 @@ #include "glc_viewport.h" #include "../glc_openglexception.h" #include "../glc_factory.h" +#include "../glc_context.h" #include ////////////////////////////////////////////////////////////////////// // Constructor Destructor ////////////////////////////////////////////////////////////////////// -GLC_ImagePlane::GLC_ImagePlane(const QGLContext *pContext, const QString& ImageName) -: m_Material() +GLC_ImagePlane::GLC_ImagePlane(const QString& ImageName) +: m_Representation(GLC_Factory::instance()->createRectangle(2.0, 2.0)) { - GLC_Texture* pImgTexture= GLC_Factory::instance(pContext)->createTexture(ImageName); + GLC_Texture* pImgTexture= GLC_Factory::instance()->createTexture(ImageName); pImgTexture->setMaxTextureSize(pImgTexture->imageOfTexture().size()); - m_Material.setTexture(pImgTexture); + m_Representation.geomAt(0)->addMaterial(new GLC_Material(pImgTexture)); } -GLC_ImagePlane::GLC_ImagePlane(const QGLContext *pContext, const QImage& image) -: m_Material() +GLC_ImagePlane::GLC_ImagePlane(const QImage& image) +: m_Representation(GLC_Factory::instance()->createRectangle(2.0, 2.0)) { - GLC_Texture* pImgTexture= GLC_Factory::instance(pContext)->createTexture(image); + GLC_Texture* pImgTexture= GLC_Factory::instance()->createTexture(image); pImgTexture->setMaxTextureSize(image.size()); - m_Material.setTexture(pImgTexture); + m_Representation.geomAt(0)->addMaterial(new GLC_Material(pImgTexture)); } GLC_ImagePlane::~GLC_ImagePlane() @@ -58,34 +59,22 @@ GLC_ImagePlane::~GLC_ImagePlane() void GLC_ImagePlane::render() { - m_Material.glExecute(); - // Display info area - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(-1,1,-1,1,-1,1); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); + GLC_Context::current()->glcMatrixMode(GL_PROJECTION); + GLC_Context::current()->glcPushMatrix(); + GLC_Context::current()->glcLoadIdentity(); + GLC_Context::current()->glcOrtho(-1,1,-1,1,-1,1); + GLC_Context::current()->glcMatrixMode(GL_MODELVIEW); glDisable(GL_BLEND); glDisable(GL_DEPTH_TEST); glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glBegin(GL_QUADS); - glNormal3d(0.0, 0.0, 1.0); // Z - glTexCoord2f(0.0f, 0.0f); glVertex3d(-1.0, -1.0, 0.0); - glTexCoord2f(1.0f, 0.0f); glVertex3d(1.0, -1.0, 0.0); - glTexCoord2f(1.0f, 1.0f); glVertex3d(1.0, 1.0, 0.0); - glTexCoord2f(0.0f, 1.0f); glVertex3d(-1.0, 1.0, 0.0); + m_Representation.render(); - glEnd(); glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glEnable(GL_DEPTH_TEST); - glPopMatrix(); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); + GLC_Context::current()->glcMatrixMode(GL_PROJECTION); + GLC_Context::current()->glcPopMatrix(); + GLC_Context::current()->glcMatrixMode(GL_MODELVIEW); } diff --git a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_imageplane.h b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_imageplane.h index db5411e4d..51ce957a2 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_imageplane.h +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_imageplane.h @@ -26,6 +26,7 @@ #define GLC_IMAGEPLANE_H_ #include "../shading/glc_material.h" +#include "../sceneGraph/glc_3dviewinstance.h" #include "../glc_config.h" @@ -44,10 +45,10 @@ class GLC_LIB_EXPORT GLC_ImagePlane ////////////////////////////////////////////////////////////////////// public: //! Construct image plane from the given image file name and QGLContext - GLC_ImagePlane(const QGLContext *pContext, const QString& ImageName); + GLC_ImagePlane(const QString& ImageName); //! Construct image plane from the given image and QGLContext - GLC_ImagePlane(const QGLContext *pContext, const QImage& image); + GLC_ImagePlane(const QImage& image); ~GLC_ImagePlane(); //@} @@ -67,8 +68,8 @@ public: private: - //! The image plane material - GLC_Material m_Material; + //! The image representation + GLC_3DViewInstance m_Representation; }; diff --git a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_mover.h b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_mover.h index 3b81f20e1..047b2d8a2 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_mover.h +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_mover.h @@ -27,13 +27,13 @@ #include "glc_repmover.h" #include "../maths/glc_vector3d.h" +#include "glc_userinput.h" #include #include #include "../glc_config.h" -class QMouseEvent; class GLC_Viewport; ////////////////////////////////////////////////////////////////////// @@ -70,10 +70,10 @@ public: ////////////////////////////////////////////////////////////////////// public: //! Initialized the mover - virtual void init(QMouseEvent * e)= 0; + virtual void init(const GLC_UserInput& userInput)= 0; //! Move the camera - virtual bool move(QMouseEvent * e)= 0; + virtual bool move(const GLC_UserInput& userInput)= 0; //! Ends this mover virtual void ends(){} @@ -111,17 +111,12 @@ private: //! Clear mover representation void clearMoverRepresentation(); -////////////////////////////////////////////////////////////////////// -// Private Members -////////////////////////////////////////////////////////////////////// -private: - //! The mover representations list - QList m_RepMoverList; - ////////////////////////////////////////////////////////////////////// // Protected Members ////////////////////////////////////////////////////////////////////// protected: + //! The mover representations list + QList m_RepMoverList; //! The previous mover value GLC_Vector3d m_PreviousVector; diff --git a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_movercontroller.cpp b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_movercontroller.cpp index b2c58db30..e1b684cd4 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_movercontroller.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_movercontroller.cpp @@ -111,12 +111,12 @@ void GLC_MoverController::removeMover(const int id) } } -void GLC_MoverController::setActiveMover(const int id, QMouseEvent * e) +void GLC_MoverController::setActiveMover(const int id, const GLC_UserInput& userInput) { Q_ASSERT(m_MoverHash.contains(id)); m_ActiveMoverId= id; connect(m_MoverHash.value(m_ActiveMoverId), SIGNAL(updated()), this, SIGNAL(repaintNeeded())); - m_MoverHash.value(m_ActiveMoverId)->init(e); + m_MoverHash.value(m_ActiveMoverId)->init(userInput); } void GLC_MoverController::setNoMover() diff --git a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_movercontroller.h b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_movercontroller.h index af28d0cdd..08f8d4a28 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_movercontroller.h +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_movercontroller.h @@ -31,9 +31,9 @@ #include #include "../glc_config.h" +#include "glc_userinput.h" class QGLWidget; -class QMouseEvent; ////////////////////////////////////////////////////////////////////// //! \class GLC_MoverController @@ -54,7 +54,8 @@ public: TrackBall= 3, Target= 4, TurnTable= 5, - Fly= 6 + Fly= 6, + TSR= 7 }; public: @@ -85,6 +86,10 @@ public: inline GLC_Mover* activeMover() const {return m_MoverHash.value(m_ActiveMoverId);} + //! Return the mover of the given id + inline GLC_Mover* getMover(const int id) const + {return m_MoverHash.value(id);} + //@} ////////////////////////////////////////////////////////////////////// @@ -102,16 +107,16 @@ public: void removeMover(const int); //! Set the specified mover as active - void setActiveMover(const int id, QMouseEvent * e); + void setActiveMover(const int id, const GLC_UserInput& userInput); //! Set no mover as active void setNoMover(); //! Move with the active mover - inline bool move(QMouseEvent * e) + inline bool move(const GLC_UserInput& userInput) { Q_ASSERT(0 != m_ActiveMoverId); - return m_MoverHash.value(m_ActiveMoverId)->move(e); + return m_MoverHash.value(m_ActiveMoverId)->move(userInput); } //@} diff --git a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_panmover.cpp b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_panmover.cpp index ad6964d0c..40c0e6d26 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_panmover.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_panmover.cpp @@ -62,15 +62,15 @@ GLC_Mover* GLC_PanMover::clone() const ////////////////////////////////////////////////////////////////////// // Initialized the mover -void GLC_PanMover::init(QMouseEvent * e) +void GLC_PanMover::init(const GLC_UserInput& userInput) { - m_PreviousVector= m_pViewport->mapPosMouse(static_cast(e->x()), static_cast(e->y())); + m_PreviousVector= m_pViewport->mapPosMouse(static_cast(userInput.x()), static_cast(userInput.y())); } // Move the camera -bool GLC_PanMover::move(QMouseEvent * e) +bool GLC_PanMover::move(const GLC_UserInput& userInput) { - const GLC_Vector3d VectCur(m_pViewport->mapPosMouse(static_cast(e->x()), static_cast(e->y()))); + const GLC_Vector3d VectCur(m_pViewport->mapPosMouse(static_cast(userInput.x()), static_cast(userInput.y()))); const GLC_Vector3d VectPan= VectCur - m_PreviousVector; // moving Vector // Pan the camera diff --git a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_panmover.h b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_panmover.h index 0b2cd1b3d..c7204f00b 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_panmover.h +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_panmover.h @@ -60,10 +60,10 @@ public: ////////////////////////////////////////////////////////////////////// public: //! Initialized the mover - virtual void init(QMouseEvent * e); + virtual void init(const GLC_UserInput& userInput); //! Move the camera - virtual bool move(QMouseEvent * e); + virtual bool move(const GLC_UserInput& userInput); //@} }; diff --git a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_repcrossmover.cpp b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_repcrossmover.cpp index ae835f6d3..c10d88f68 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_repcrossmover.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_repcrossmover.cpp @@ -22,6 +22,7 @@ #include "glc_repcrossmover.h" #include "glc_viewport.h" +#include "../geometry/glc_polylines.h" // Default constructor GLC_RepCrossMover::GLC_RepCrossMover(GLC_Viewport* pViewport) @@ -59,6 +60,26 @@ GLC_RepMover* GLC_RepCrossMover::clone() const // Virtual interface for OpenGL Geometry set up. void GLC_RepCrossMover::glDraw() { + GLC_3DViewInstance crossInstance(createCrossInstance()); + + glDisable(GL_BLEND); + m_RenderProperties.setRenderingFlag(glc::WireRenderFlag); + crossInstance.render(glc::WireRenderFlag); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + m_RenderProperties.setRenderingFlag(glc::TransparentRenderFlag); + // Display arcs + crossInstance.render(glc::TransparentRenderFlag); +} + +////////////////////////////////////////////////////////////////////// +// Private services Functions +////////////////////////////////////////////////////////////////////// +GLC_3DViewInstance GLC_RepCrossMover::createCrossInstance() +{ + GLC_Polylines* pCross= new GLC_Polylines(); + int nLgAxe; const int winHSize= m_pViewport->viewHSize(); const int winVSize= m_pViewport->viewVSize(); @@ -78,41 +99,48 @@ void GLC_RepCrossMover::glDraw() // Axis length in OpenGL unit = length(Pixel) * (dimend GL / dimens Pixel) const double dLgAxe= ((double)nLgAxe * ChampsVision / (double)winVSize) / 7; const double dDecAxe= dLgAxe / 3; - glPushMatrix(); - glTranslated(m_pViewport->cameraHandle()->target().x(), m_pViewport->cameraHandle()->target().y(), - m_pViewport->cameraHandle()->target().z() ); + //X axis + { + GLC_Point3d p1(-dLgAxe, 0, 0); + GLC_Point3d p2(-dDecAxe, 0, 0); + GLC_Point3d p3(dDecAxe, 0, 0); + GLC_Point3d p4(dLgAxe, 0, 0); + QList points; + points << p1 << p2 << p3 << p4; + pCross->addPolyline(points); + } - // Graphic properties - glDisable(GL_TEXTURE_2D); - glDisable(GL_LIGHTING); - glColor4d(m_MainColor.redF(), m_MainColor.greenF(), m_MainColor.blueF(), m_MainColor.alphaF()); - glLineWidth(1.0); + //Y axis + { + GLC_Point3d p1(0, -dLgAxe, 0); + GLC_Point3d p2(0, -dDecAxe, 0); + GLC_Point3d p3(0, dDecAxe, 0); + GLC_Point3d p4(0, dLgAxe, 0); + QList points; + points << p1 << p2 << p3 << p4; + pCross->addPolyline(points); + } - // Display camera's target lines - glBegin(GL_LINES); - //X axis - glVertex3d(-dLgAxe, 0, 0); - glVertex3d(-dDecAxe, 0, 0); - glVertex3d(dDecAxe, 0, 0); - glVertex3d(dLgAxe, 0, 0); + //Z axis + { + GLC_Point3d p1(0, 0, -dLgAxe); + GLC_Point3d p2(0, 0, -dDecAxe); + GLC_Point3d p3(0, 0, dDecAxe); + GLC_Point3d p4(0, 0, dLgAxe); + QList points; + points << p1 << p2 << p3 << p4; + pCross->addPolyline(points); + } - //Y axis - glVertex3d(0, -dLgAxe, 0); - glVertex3d(0, -dDecAxe, 0); - glVertex3d(0, dDecAxe, 0); - glVertex3d(0, dLgAxe, 0); + pCross->setWireColor(m_MainColor); + GLC_3DViewInstance crossInstance(pCross); - //Z axis - glVertex3d(0, 0, -dLgAxe); - glVertex3d(0, 0, -dDecAxe); - glVertex3d(0, 0, dDecAxe); - glVertex3d(0, 0, dLgAxe); + GLC_Matrix4x4 translation; + translation.setMatTranslate(m_pViewport->cameraHandle()->target()); - glEnd(); - - glPopMatrix(); + crossInstance.setMatrix(translation); + return crossInstance; } - diff --git a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_repcrossmover.h b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_repcrossmover.h index 6f3c74b1d..8f570919f 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_repcrossmover.h +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_repcrossmover.h @@ -25,6 +25,8 @@ #include "glc_repmover.h" +#include "../sceneGraph/glc_3dviewinstance.h" + #include "../glc_config.h" ////////////////////////////////////////////////////////////////////// @@ -61,6 +63,13 @@ public: //@} +////////////////////////////////////////////////////////////////////// +// Private services Functions +////////////////////////////////////////////////////////////////////// +private: + //! Create and return the cross instance + GLC_3DViewInstance createCrossInstance(); + }; #endif /* GLC_REPCROSSMOVER_H_ */ diff --git a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_repflymover.cpp b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_repflymover.cpp index f68abcb25..bf5bc3001 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_repflymover.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_repflymover.cpp @@ -25,6 +25,8 @@ #include "glc_repflymover.h" #include "glc_viewport.h" #include "../geometry/glc_polylines.h" +#include "../glc_context.h" + #include GLC_RepFlyMover::GLC_RepFlyMover(GLC_Viewport* pViewport) @@ -107,16 +109,16 @@ void GLC_RepFlyMover::glDraw() const double vRatio= static_cast(m_pViewport->viewVSize()) / calibre; glDisable(GL_TEXTURE_2D); - glDisable(GL_LIGHTING); + GLC_Context::current()->glcEnableLighting(false); glDisable(GL_DEPTH_TEST); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(hRatio * -1.0 ,hRatio * 1.0 ,vRatio * -1.0 ,vRatio * 1.0 ,-1.0 ,1.0); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); + GLC_Context::current()->glcMatrixMode(GL_PROJECTION); + GLC_Context::current()->glcPushMatrix(); + GLC_Context::current()->glcLoadIdentity(); + GLC_Context::current()->glcOrtho(hRatio * -1.0 ,hRatio * 1.0 ,vRatio * -1.0 ,vRatio * 1.0 ,-1.0 ,1.0); + GLC_Context::current()->glcMatrixMode(GL_MODELVIEW); + GLC_Context::current()->glcPushMatrix(); + GLC_Context::current()->glcLoadIdentity(); m_CenterCircle.render(glc::WireRenderFlag); m_Hud.render(glc::WireRenderFlag); @@ -138,10 +140,10 @@ void GLC_RepFlyMover::glDraw() double posy= 2.0 * static_cast(txtHeight) / calibre; m_pViewport->qGLWidgetHandle()->renderText(- m_HudOffset.getX(), m_HudOffset.getY() - posy, 0.0, velocity, myFont); - glPopMatrix(); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); + GLC_Context::current()->glcPopMatrix(); + GLC_Context::current()->glcMatrixMode(GL_PROJECTION); + GLC_Context::current()->glcPopMatrix(); + GLC_Context::current()->glcMatrixMode(GL_MODELVIEW); glEnable(GL_DEPTH_TEST); } @@ -152,7 +154,7 @@ void GLC_RepFlyMover::createRepresentation() GLC_Circle* pCircle= new GLC_Circle(m_Radius); pCircle->setWireColor(GLC_RepMover::m_MainColor); pCircle->setLineWidth(GLC_RepMover::m_Thickness); - m_CenterCircle.setGeometry(pCircle); + m_CenterCircle.addGeometry(pCircle); GLC_Polylines* pPolylines= new GLC_Polylines(); GLfloatVector points; @@ -167,7 +169,7 @@ void GLC_RepFlyMover::createRepresentation() pPolylines->addPolyline(points); pPolylines->setWireColor(GLC_RepMover::m_MainColor); pPolylines->setLineWidth(GLC_RepMover::m_Thickness); - m_Hud.setGeometry(pPolylines); + m_Hud.addGeometry(pPolylines); // Plane creation pPolylines= new GLC_Polylines(); @@ -181,5 +183,5 @@ void GLC_RepFlyMover::createRepresentation() pPolylines->addPolyline(points); pPolylines->setWireColor(GLC_RepMover::m_MainColor); pPolylines->setLineWidth(GLC_RepMover::m_Thickness); - m_Plane.setGeometry(pPolylines); + m_Plane.addGeometry(pPolylines); } diff --git a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_reptrackballmover.cpp b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_reptrackballmover.cpp index 2a260cdbb..aac0d9ece 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_reptrackballmover.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_reptrackballmover.cpp @@ -25,6 +25,7 @@ #include "glc_reptrackballmover.h" #include "glc_viewport.h" #include "../glc_factory.h" +#include "../glc_context.h" #include using namespace glc; @@ -155,23 +156,18 @@ void GLC_RepTrackBallMover::glDraw() computeRadius(); const double aspectRatio= static_cast(m_pViewport->viewHSize())/static_cast(m_pViewport->viewVSize()); - // Save current state - GLboolean depthTest, blend; - glGetBooleanv(GL_DEPTH_TEST, &depthTest); - glGetBooleanv(GL_BLEND, &blend); - // orbit circle must be shown - if (depthTest) glDisable(GL_DEPTH_TEST); + glDisable(GL_DEPTH_TEST); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(aspectRatio * -1.0 ,aspectRatio * 1.0 ,-1.0 ,1.0 ,-1.0 ,1.0); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); + GLC_Context::current()->glcMatrixMode(GL_PROJECTION); + GLC_Context::current()->glcPushMatrix(); + GLC_Context::current()->glcLoadIdentity(); + GLC_Context::current()->glcOrtho(aspectRatio * -1.0 ,aspectRatio * 1.0 ,-1.0 ,1.0 ,-1.0 ,1.0); + GLC_Context::current()->glcMatrixMode(GL_MODELVIEW); + GLC_Context::current()->glcPushMatrix(); + GLC_Context::current()->glcLoadIdentity(); - if (blend) glDisable(GL_BLEND); + glDisable(GL_BLEND); m_RenderProperties.setRenderingFlag(glc::WireRenderFlag); // Display arcs m_Arc1.render(glc::WireRenderFlag); @@ -188,13 +184,10 @@ void GLC_RepTrackBallMover::glDraw() // Display base class (Main circle) m_MainCircle.render(m_RenderProperties); - glPopMatrix(); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - - if (depthTest) glEnable(GL_DEPTH_TEST); - if (!blend) glEnable(GL_BLEND); + GLC_Context::current()->glcPopMatrix(); + GLC_Context::current()->glcMatrixMode(GL_PROJECTION); + GLC_Context::current()->glcPopMatrix(); + GLC_Context::current()->glcMatrixMode(GL_MODELVIEW); } diff --git a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_reptrackballmover.h b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_reptrackballmover.h index fc6c22446..72271e55e 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_reptrackballmover.h +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_reptrackballmover.h @@ -70,6 +70,10 @@ public: //! Set representation main color virtual void setMainColor(const QColor& color); + //! Set representation screen ration + inline void setRatio(double ratio) + {m_Ratio= ratio;} + //@} ////////////////////////////////////////////////////////////////////// diff --git a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_settargetmover.cpp b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_settargetmover.cpp index 7d10c8c94..fce588103 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_settargetmover.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_settargetmover.cpp @@ -60,23 +60,23 @@ GLC_Mover* GLC_SetTargetMover::clone() const ////////////////////////////////////////////////////////////////////// // Initialized the mover -void GLC_SetTargetMover::init(QMouseEvent * e) +void GLC_SetTargetMover::init(const GLC_UserInput& userInput) { // Z Buffer component of selected point between 0 and 1 GLfloat Depth; // read selected point - glReadPixels(e->x(), m_pViewport->viewVSize() - e->y() , 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &Depth); + glReadPixels(userInput.x(), m_pViewport->viewVSize() - userInput.y() , 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &Depth); // Test if there is geometry under picking point if (!qFuzzyCompare(Depth, 1.0f)) { // Geometry find -> Update camera's target position - const GLC_Point3d target(m_pViewport->unProject(e->x(), e->y())); + const GLC_Point3d target(m_pViewport->unProject(userInput.x(), userInput.y())); m_pViewport->cameraHandle()->setTargetCam(target); } else - { // Geometrie not find -> panning + { // Geometry not find -> panning - const GLC_Point3d curPos(m_pViewport->mapPosMouse(e->x(), e->y())); + const GLC_Point3d curPos(m_pViewport->mapPosMouse(userInput.x(), userInput.y())); const GLC_Point3d prevPos(m_pViewport->mapPosMouse(m_pViewport->viewHSize() / 2, m_pViewport->viewVSize() / 2)); const GLC_Vector3d VectPan(curPos - prevPos); // panning vector // pan camera diff --git a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_settargetmover.h b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_settargetmover.h index b18c3b1a6..5d0ae637a 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_settargetmover.h +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_settargetmover.h @@ -58,10 +58,10 @@ public: ////////////////////////////////////////////////////////////////////// public: //! Initialized the mover - virtual void init(QMouseEvent * e); + virtual void init(const GLC_UserInput& userInput); //! Move the camera - virtual bool move(QMouseEvent *){return true;} + virtual bool move(const GLC_UserInput&){return true;} //@} }; diff --git a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_trackballmover.cpp b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_trackballmover.cpp index f54f25df4..766e32385 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_trackballmover.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_trackballmover.cpp @@ -23,6 +23,7 @@ #include "glc_trackballmover.h" #include "glc_viewport.h" +#include "glc_reptrackballmover.h" // Default constructor GLC_TrackBallMover::GLC_TrackBallMover(GLC_Viewport* pViewport, const QList& repsList) @@ -61,9 +62,9 @@ GLC_Mover* GLC_TrackBallMover::clone() const ////////////////////////////////////////////////////////////////////// // Initialized the mover -void GLC_TrackBallMover::init(QMouseEvent * e) +void GLC_TrackBallMover::init(const GLC_UserInput& userInput) { - GLC_Mover::m_PreviousVector.setVect(mapForTracking(static_cast(e->x()), static_cast(e->y()))); + GLC_Mover::m_PreviousVector.setVect(mapForTracking(static_cast(userInput.x()), static_cast(userInput.y()))); const double Angle= acos(glc::Z_AXIS * GLC_Mover::m_PreviousVector); const GLC_Vector3d AxeRot(glc::Z_AXIS ^ GLC_Mover::m_PreviousVector); @@ -77,9 +78,9 @@ void GLC_TrackBallMover::init(QMouseEvent * e) } // Move the camera -bool GLC_TrackBallMover::move(QMouseEvent * e) +bool GLC_TrackBallMover::move(const GLC_UserInput& userInput) { - const GLC_Vector3d VectCurOrbit(mapForTracking(static_cast(e->x()), static_cast(e->y()))); + const GLC_Vector3d VectCurOrbit(mapForTracking(static_cast(userInput.x()), static_cast(userInput.y()))); // Update camera position (orbit) GLC_Mover::m_pViewport->cameraHandle()->orbit(GLC_Mover::m_PreviousVector, VectCurOrbit); @@ -96,6 +97,19 @@ bool GLC_TrackBallMover::move(QMouseEvent * e) return true; } +void GLC_TrackBallMover::setRatio(double ratio) +{ + m_Ratio= ratio; + const int repCount= m_RepMoverList.count(); + for (int i= 0; i < repCount; ++i) + { + GLC_RepTrackBallMover* pRep= dynamic_cast(m_RepMoverList.at(i)); + if (NULL != pRep) + { + pRep->setRatio(ratio); + } + } +} ///////////////////////////////////////////////////////////////////// // Private services Functions ////////////////////////////////////////////////////////////////////// diff --git a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_trackballmover.h b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_trackballmover.h index 14f03a6e7..7a11b4ba0 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_trackballmover.h +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_trackballmover.h @@ -64,10 +64,13 @@ public: ////////////////////////////////////////////////////////////////////// public: //! Initialized the mover - virtual void init(QMouseEvent * e); + virtual void init(const GLC_UserInput& userInput); //! Move the camera - virtual bool move(QMouseEvent * e); + virtual bool move(const GLC_UserInput& userInput); + + //! Set this mover screen ratio + void setRatio(double ratio); //@} diff --git a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_tsrmover.cpp b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_tsrmover.cpp new file mode 100644 index 000000000..80f9bf608 --- /dev/null +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_tsrmover.cpp @@ -0,0 +1,144 @@ +/**************************************************************************** + + This file is part of the GLC-lib library. + Copyright (C) 2005-2008 Laurent Ribon (laumaya@users.sourceforge.net) + http://glc-lib.sourceforge.net + + GLC-lib is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + GLC-lib 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with GLC-lib; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + *****************************************************************************/ + +//! \file glc_tsrmover.cpp Implementation of the GLC_TsrMover class. + +#include "glc_tsrmover.h" +#include "glc_viewport.h" + +#include "../geometry/glc_point.h" + +// Default constructor +GLC_TsrMover::GLC_TsrMover(GLC_Viewport* pViewport, const QList& repsList) +: GLC_Mover(pViewport, repsList) +{ + +} + +// Copy constructor +GLC_TsrMover::GLC_TsrMover(const GLC_TsrMover& tsrMover) +: GLC_Mover(tsrMover) +{ + +} + + +GLC_TsrMover::~GLC_TsrMover() +{ + +} + +////////////////////////////////////////////////////////////////////// +// Get Functions +////////////////////////////////////////////////////////////////////// + +// Return a clone of the mover +GLC_Mover* GLC_TsrMover::clone() const +{ + return new GLC_TsrMover(*this); +} + + +////////////////////////////////////////////////////////////////////// +// Set Functions +////////////////////////////////////////////////////////////////////// + +// Initialized the mover +void GLC_TsrMover::init(const GLC_UserInput& userInput) +{ + m_PreviousVector= GLC_Point3d(userInput.normalyzeXTouchCenter(), userInput.normalyzeYTouchCenter(), 0.0); +} + +// Move the camera +bool GLC_TsrMover::move(const GLC_UserInput& userInput) +{ + if (!(userInput.normalyzeXTouchCenter() < 0.0) && !(userInput.normalyzeYTouchCenter() < 0.0)) + { + m_PreviousVector= GLC_Point3d(userInput.normalyzeXTouchCenter(), userInput.normalyzeYTouchCenter(), 0.0); + } + else + { + qDebug() << "Pas cool"; + if (!userInput.translation().isNull()) + { + m_PreviousVector= GLC_Vector3d(userInput.translation().getX(), userInput.translation().getY(), 0.0) + m_PreviousVector; + } + } + + const double x= m_PreviousVector.x(); + const double y= m_PreviousVector.y(); + //GLC_Point3d center2= m_pViewport->unProject(x * m_pViewport->viewHSize(), y * m_pViewport->viewVSize()); + + //qDebug() << "touch center= " << x << " , " << y; + + + if (!qFuzzyCompare(userInput.scaleFactor(), 0)) + { + GLC_Point dummy(m_pViewport->cameraHandle()->target()); + m_pViewport->setDistMinAndMax(dummy.boundingBox()); + + GLC_Point2d nPos= m_pViewport->mapNormalyzeToOpenGLScreen(x, y); + GLC_Point3d nPos3D(nPos.getX(), nPos.getY(), 1.0); + GLC_Point3d projected= m_pViewport->compositionMatrix().inverted() * nPos3D; + + m_pViewport->cameraHandle()->zoom(userInput.scaleFactor()); + + m_pViewport->setDistMinAndMax(dummy.boundingBox()); + GLC_Point3d projected2= m_pViewport->compositionMatrix().inverted() * nPos3D; + GLC_Vector3d delta= projected - projected2; + m_pViewport->cameraHandle()->translate(delta); + } + + if (!qFuzzyCompare(userInput.rotationAngle(), 0)) + { + GLC_Point dummy(m_pViewport->cameraHandle()->target()); + m_pViewport->setDistMinAndMax(dummy.boundingBox()); + + GLC_Point2d nPos= m_pViewport->mapNormalyzeToOpenGLScreen(x, y); + GLC_Point3d nPos3D(nPos.getX(), nPos.getY(), 1.0); + GLC_Point3d center= m_pViewport->compositionMatrix().inverted() * nPos3D; + + GLC_Vector3d axis= m_pViewport->cameraHandle()->forward(); + + m_pViewport->cameraHandle()->rotateAround(axis, userInput.rotationAngle(), center); + } + + if (!userInput.translation().isNull()) + { + double transX= userInput.translation().getX() * m_pViewport->viewHSize(); + double transY= userInput.translation().getY() * m_pViewport->viewVSize(); + + GLC_Vector3d mappedTranslation(-transX, -transY, 0.0); + // Compute the length of camera's field of view + const double ChampsVision = m_pViewport->cameraHandle()->distEyeTarget() * m_pViewport->viewTangent(); + + // the side of camera's square is mapped on Vertical length of window + // Ratio OpenGL/Pixel = dimend GL / dimens Pixel + const double Ratio= ChampsVision / static_cast(m_pViewport->viewVSize()); + + mappedTranslation= mappedTranslation * Ratio; + m_pViewport->cameraHandle()->pan(mappedTranslation); + } + + return true; +} + diff --git a/ground/openpilotgcs/src/libs/glc_lib/glc_openglstate.h b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_tsrmover.h similarity index 66% rename from ground/openpilotgcs/src/libs/glc_lib/glc_openglstate.h rename to ground/openpilotgcs/src/libs/glc_lib/viewport/glc_tsrmover.h index 792318553..f18604b97 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/glc_openglstate.h +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_tsrmover.h @@ -19,34 +19,39 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *****************************************************************************/ -//! \file glc_openglstate.h interface for the GLC_OpenGLState class. -#ifndef GLC_OPENGLSTATE_H_ -#define GLC_OPENGLSTATE_H_ +//! \file glc_tsrmover Interface for the GLC_TsrMover class. + +#ifndef GLC_TSRMOVER_H_ +#define GLC_TSRMOVER_H_ + +#include "glc_mover.h" + +#include "../glc_config.h" ////////////////////////////////////////////////////////////////////// -//! \class GLC_OpenGLState -/*! \brief GLC_OpenGLState is use to store and retrieve differential opengl state*/ +//! \class GLC_TsrMover +/*! \brief GLC_TsrMover : Translationn scaling and rotation interactive manipulation */ ////////////////////////////////////////////////////////////////////// -class GLC_OpenGLState +class GLC_LIB_EXPORT GLC_TsrMover : public GLC_Mover { public: -////////////////////////////////////////////////////////////////////// -/*! @name Constructor / Destructor */ -//@{ -////////////////////////////////////////////////////////////////////// + //! Default constructor + GLC_TsrMover(GLC_Viewport*, const QList& repsList= QList()); - GLC_OpenGLState(); - virtual ~GLC_OpenGLState(); + //! Copy constructor + GLC_TsrMover(const GLC_TsrMover&); -//@} + //! Destructor + virtual ~GLC_TsrMover(); ////////////////////////////////////////////////////////////////////// /*! \name Get Functions*/ //@{ ////////////////////////////////////////////////////////////////////// public: - + //! Return a clone of the mover + virtual GLC_Mover* clone() const; //@} ////////////////////////////////////////////////////////////////////// @@ -54,15 +59,11 @@ public: //@{ ////////////////////////////////////////////////////////////////////// public: - //! + //! Initialized the mover + virtual void init(const GLC_UserInput& userInput); + //! Move the camera + virtual bool move(const GLC_UserInput& userInput); //@} - -////////////////////////////////////////////////////////////////////// -// Private members -////////////////////////////////////////////////////////////////////// -private: - }; - -#endif /* GLC_OPENGLSTATE_H_ */ +#endif /* GLC_TSRMOVER_H_ */ diff --git a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_turntablemover.cpp b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_turntablemover.cpp index 69b873dd0..4d56ade13 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_turntablemover.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_turntablemover.cpp @@ -62,9 +62,9 @@ GLC_Mover* GLC_TurnTableMover::clone() const ////////////////////////////////////////////////////////////////////// // Initialized the mover -void GLC_TurnTableMover::init(QMouseEvent * e) +void GLC_TurnTableMover::init(const GLC_UserInput& userInput) { - GLC_Mover::m_PreviousVector.setVect(static_cast(e->x()), static_cast(e->y()),0.0); + GLC_Mover::m_PreviousVector.setVect(static_cast(userInput.x()), static_cast(userInput.y()),0.0); GLC_Camera* pCamera= GLC_Mover::m_pViewport->cameraHandle(); // Calculate angle sign m_Sign= pCamera->defaultUpVector() * pCamera->upVector(); @@ -81,7 +81,7 @@ void GLC_TurnTableMover::init(QMouseEvent * e) } -bool GLC_TurnTableMover::move(QMouseEvent * e) +bool GLC_TurnTableMover::move(const GLC_UserInput& userInput) { GLC_Camera* pCamera= GLC_Mover::m_pViewport->cameraHandle(); // Turn table rotation @@ -89,8 +89,8 @@ bool GLC_TurnTableMover::move(QMouseEvent * e) const double width= static_cast ( GLC_Mover::m_pViewport->viewVSize() ); const double height= static_cast ( GLC_Mover::m_pViewport->viewHSize() ); - const double alpha = -((static_cast(e->x()) - GLC_Mover::m_PreviousVector.x()) / width) * rotSpeed; - const double beta = ((static_cast(e->y()) - GLC_Mover::m_PreviousVector.y()) / height) * rotSpeed; + const double alpha = -((static_cast(userInput.x()) - GLC_Mover::m_PreviousVector.x()) / width) * rotSpeed; + const double beta = ((static_cast(userInput.y()) - GLC_Mover::m_PreviousVector.y()) / height) * rotSpeed; // Rotation around the screen vertical axis pCamera->rotateAroundTarget(pCamera->defaultUpVector(), alpha * m_Sign); @@ -103,7 +103,7 @@ bool GLC_TurnTableMover::move(QMouseEvent * e) pCamera->rotateAroundTarget(rightVector, beta); } - m_PreviousVector.setVect(static_cast(e->x()), static_cast(e->y()), 0.0); + m_PreviousVector.setVect(static_cast(userInput.x()), static_cast(userInput.y()), 0.0); return true; } diff --git a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_turntablemover.h b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_turntablemover.h index c6bfb4d51..7eb44621a 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_turntablemover.h +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_turntablemover.h @@ -59,10 +59,10 @@ public: ////////////////////////////////////////////////////////////////////// public: //! Initialized the mover - virtual void init(QMouseEvent * e); + virtual void init(const GLC_UserInput& userInput); //! Move the camera - virtual bool move(QMouseEvent * e); + virtual bool move(const GLC_UserInput& userInput); //@} diff --git a/ground/openpilotgcs/src/libs/glc_lib/glc_openglstatemanager.cpp b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_userinput.cpp similarity index 53% rename from ground/openpilotgcs/src/libs/glc_lib/glc_openglstatemanager.cpp rename to ground/openpilotgcs/src/libs/glc_lib/viewport/glc_userinput.cpp index fc68e5bb4..1788efbd4 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/glc_openglstatemanager.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_userinput.cpp @@ -19,16 +19,49 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *****************************************************************************/ -//! \file glc_openglstatemanager.cpp implementation of the GLC_OpenGLStateManager class. -#include "glc_openglstatemanager.h" +//! \file glc_mover.cpp Implementation of the GLC_Mover class. -GLC_OpenGLStateManager::GLC_OpenGLStateManager() +#include "glc_userinput.h" + +GLC_UserInput::GLC_UserInput(int x, int y) +: m_X(x) +, m_Y(y) +, m_NormalyzeX(0.0) +, m_NormalyzeY(0.0) +, m_Translation() +, m_Rotation(0.0) +, m_ScaleFactor(1.0) +, m_TransformationIsSet(false) { } -GLC_OpenGLStateManager::~GLC_OpenGLStateManager() +GLC_UserInput::~GLC_UserInput() { } + +////////////////////////////////////////////////////////////////////// +// Set Functions +////////////////////////////////////////////////////////////////////// +void GLC_UserInput::setPosition(int x, int y) +{ + m_X= x; + m_Y= y; +} + +void GLC_UserInput::setNormalyzeTouchCenterPosition(double x, double y) +{ + m_NormalyzeX= x; + m_NormalyzeY= y; +} + +void GLC_UserInput::setTransformation(const GLC_Vector2d& translation, double rotation, double scaleFactor) +{ + m_Translation= translation; + m_Rotation= rotation; + m_ScaleFactor= scaleFactor; + + m_TransformationIsSet= true; +} diff --git a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_userinput.h b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_userinput.h new file mode 100644 index 000000000..63fa8526a --- /dev/null +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_userinput.h @@ -0,0 +1,133 @@ +/**************************************************************************** + + This file is part of the GLC-lib library. + Copyright (C) 2005-2008 Laurent Ribon (laumaya@users.sourceforge.net) + http://glc-lib.sourceforge.net + + GLC-lib is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + GLC-lib 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with GLC-lib; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + *****************************************************************************/ + +//! \file glc_userinput.h Interface for the GLC_UserInput class. + +#ifndef GLC_USERINPUT_H_ +#define GLC_USERINPUT_H_ + +#include "../maths/glc_vector2d.h" + +#include "../glc_config.h" + +class GLC_LIB_EXPORT GLC_UserInput +{ +public: + GLC_UserInput(int x= 0, int y= 0); + virtual ~GLC_UserInput(); + + +////////////////////////////////////////////////////////////////////// +/*! \name Get Functions*/ +//@{ +////////////////////////////////////////////////////////////////////// +public: + //! return the x position + inline int x() const + {return m_X;} + + //! Return the y position + inline int y() const + {return m_Y;} + + //! Return normalyze x touch center + inline double normalyzeXTouchCenter() const + {return m_NormalyzeX;} + + //! Return normalyze x touch center + inline double normalyzeYTouchCenter() const + {return m_NormalyzeX;} + + //! Return the translation + inline GLC_Vector2d translation() const + {return m_Translation;} + + //! Return the rotation angle + inline double rotationAngle() const + {return m_Rotation;} + + //! Return the scale factor + inline double scaleFactor() const + {return m_ScaleFactor;} + + //! Return true if the transformation has been set + inline bool transformationIsSet() const + {return m_TransformationIsSet;} +//@} + +////////////////////////////////////////////////////////////////////// +/*! \name Set Functions*/ +//@{ +////////////////////////////////////////////////////////////////////// +public: + //! Set the position + void setPosition(int x, int y); + + //! Set the normalyze position of the center of touch + void setNormalyzeTouchCenterPosition(double x, double y); + + //! Set the transformation + void setTransformation(const GLC_Vector2d& translation, double rotation= 0.0, double scaleFactor= 1.0); + + //! Set translation + inline void setTranslation(const GLC_Vector2d& translation) + {m_Translation= translation;} + + //! Set rotation + inline void setRotation(double rotation) + {m_Rotation= rotation;} + + //! Set scaling + inline void setScaleFactor(double scaleFactor) + {m_ScaleFactor= scaleFactor;} +//@} + +////////////////////////////////////////////////////////////////////// +// Private Members +////////////////////////////////////////////////////////////////////// +private: + //! the x position of the user input + int m_X; + + //! The y position of the user input + int m_Y; + + //Normalize position of center of touchs + double m_NormalyzeX; + double m_NormalyzeY; + + // Transformation data + //! Translation vector + GLC_Vector2d m_Translation; + + //! Rotation angle + double m_Rotation; + + //! Scale factor + double m_ScaleFactor; + + //! Flag to know if a transformation has been set + bool m_TransformationIsSet; + +}; + +#endif /* GLC_USERINPUT_H_ */ diff --git a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_viewport.cpp b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_viewport.cpp index 08e1ae25d..0f2785fb3 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_viewport.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_viewport.cpp @@ -22,7 +22,9 @@ //! \file glc_viewport.cpp implementation of the GLC_Viewport class. +#include +#include "../glu/glc_glu.h" #include "glc_viewport.h" #include "../glc_openglexception.h" #include "../glc_ext.h" @@ -85,6 +87,38 @@ GLC_Viewport::~GLC_Viewport() ////////////////////////////////////////////////////////////////////// // Get Functions ////////////////////////////////////////////////////////////////////// +GLC_Point2d GLC_Viewport::normalyseMousePosition(int x, int y) +{ + double nX= 0.0; + double nY= 0.0; + if (m_WindowHSize != 0) + { + nX= static_cast(x) / static_cast(m_WindowHSize); + } + + if (m_WindowVSize != 0) + { + nY= static_cast(y) / static_cast(m_WindowVSize); + } + + return GLC_Point2d(nX, nY); +} + +GLC_Point2d GLC_Viewport::mapToOpenGLScreen(int x, int y) +{ + GLC_Point2d nPos= normalyseMousePosition(x, y); + + return mapNormalyzeToOpenGLScreen(nPos.getX(), nPos.getY()); +} + +GLC_Point2d GLC_Viewport::mapNormalyzeToOpenGLScreen(double x, double y) +{ + GLC_Point2d pos(x, y); + pos= pos * 2.0; + pos.setY(pos.getY() * -1.0); + pos= pos + GLC_Point2d(-1.0, 1.0); + return pos; +} GLC_Vector3d GLC_Viewport::mapPosMouse( GLdouble Posx, GLdouble Posy) const { @@ -106,13 +140,19 @@ GLC_Vector3d GLC_Viewport::mapPosMouse( GLdouble Posx, GLdouble Posy) const return VectMouse; } +GLC_Vector3d GLC_Viewport::mapNormalyzePosMouse(double Posx, double Posy) const +{ + double screenX= Posx * static_cast(m_WindowHSize); + double screenY= Posy * static_cast(m_WindowVSize); + return mapPosMouse(screenX, screenY); +} + ////////////////////////////////////////////////////////////////////// // Public OpenGL Functions ////////////////////////////////////////////////////////////////////// void GLC_Viewport::initGl() { - // OpenGL initialisation from NEHE production m_pQGLWidget->qglClearColor(m_BackgroundColor); // Background glClearDepth(1.0f); // Depth Buffer Setup glShadeModel(GL_SMOOTH); // Enable Smooth Shading @@ -120,24 +160,20 @@ void GLC_Viewport::initGl() glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Really Nice Perspective Calculation glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - - // Init GLC_State - GLC_State::init(); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glPolygonOffset (1.0f, 1.0f); } void GLC_Viewport::glExecuteCam(void) { - m_pViewCam->glExecute(); renderImagePlane(); + m_pViewCam->glExecute(); } void GLC_Viewport::updateProjectionMat(void) { - // Make opengl context attached the current One - m_pQGLWidget->makeCurrent(); - - glMatrixMode(GL_PROJECTION); // select The Projection Matrix - glLoadIdentity(); // Reset The Projection Matrix + GLC_Context::current()->glcMatrixMode(GL_PROJECTION); // select The Projection Matrix + GLC_Context::current()->glcLoadIdentity(); // Reset The Projection Matrix if (m_UseParallelProjection) { @@ -148,18 +184,21 @@ void GLC_Viewport::updateProjectionMat(void) const double right= -left; const double bottom= - height * 0.5; const double top= -bottom; - glOrtho(left, right, bottom, top, m_dDistanceMini, m_DistanceMax); + GLC_Context::current()->glcOrtho(left, right, bottom, top, m_dDistanceMini, m_DistanceMax); } else { - gluPerspective(m_ViewAngle, m_AspectRatio, m_dDistanceMini, m_DistanceMax); + const double yMax= m_dDistanceMini * tan(m_ViewAngle * glc::PI / 360.0); + const double yMin= -yMax; + const double xMax= yMax * m_AspectRatio; + const double xMin= -xMax; + GLC_Context::current()->glcFrustum(xMin, xMax, yMin, yMax, m_dDistanceMini, m_DistanceMax); } // Save the projection matrix - glGetDoublev(GL_PROJECTION_MATRIX, m_ProjectionMatrix.setData()); + m_ProjectionMatrix= GLC_Context::current()->projectionMatrix(); - glMatrixMode(GL_MODELVIEW); // select The Modelview Matrix - glLoadIdentity(); // Reset The Modelview Matrix + GLC_Context::current()->glcMatrixMode(GL_MODELVIEW); // select The Modelview Matrix } void GLC_Viewport::forceAspectRatio(double ratio) @@ -223,7 +262,7 @@ GLC_Point3d GLC_Viewport::unProject(int x, int y) const // OpenGL ccordinate of selected point GLdouble pX, pY, pZ; - gluUnProject((GLdouble) x, (GLdouble) (m_WindowVSize - y) , Depth + glc::gluUnProject((GLdouble) x, (GLdouble) (m_WindowVSize - y) , Depth , m_pViewCam->modelViewMatrix().getData(), m_ProjectionMatrix.getData(), Viewport, &pX, &pY, &pZ); return GLC_Point3d(pX, pY, pZ); @@ -250,7 +289,7 @@ QList GLC_Viewport::unproject(const QList& list)const const int y= m_WindowVSize - list.at(i + 1); glReadPixels(x, y , 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &Depth); - gluUnProject(static_cast(x), static_cast(y) , Depth , m_pViewCam->modelViewMatrix().getData() + glc::gluUnProject(static_cast(x), static_cast(y) , Depth , m_pViewCam->modelViewMatrix().getData() , m_ProjectionMatrix.getData(), Viewport, &pX, &pY, &pZ); unprojectedPoints.append(GLC_Point3d(pX, pY, pZ)); } @@ -331,13 +370,13 @@ GLC_uint GLC_Viewport::selectBody(GLC_3DViewInstance* pInstance, int x, int y) m_pQGLWidget->qglClearColor(Qt::black); GLC_State::setSelectionMode(true); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glLoadIdentity(); + GLC_Context::current()->glcLoadIdentity(); glExecuteCam(); // Draw the scene glDisable(GL_BLEND); - glDisable(GL_LIGHTING); + GLC_Context::current()->glcEnableLighting(false); glDisable(GL_TEXTURE_2D); pInstance->renderForBodySelection(); @@ -360,13 +399,13 @@ QPair GLC_Viewport::selectPrimitive(GLC_3DViewInstance* pInstance m_pQGLWidget->qglClearColor(Qt::black); GLC_State::setSelectionMode(true); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glLoadIdentity(); + GLC_Context::current()->glcLoadIdentity(); glExecuteCam(); // Draw the scene glDisable(GL_BLEND); - glDisable(GL_LIGHTING); + GLC_Context::current()->glcEnableLighting(false); glDisable(GL_TEXTURE_2D); pInstance->renderForBodySelection(); @@ -508,13 +547,13 @@ void GLC_Viewport::updateMinimumRatioSize() void GLC_Viewport::loadBackGroundImage(const QString& ImageFile) { delete m_pImagePlane; - m_pImagePlane= new GLC_ImagePlane(m_pQGLWidget->context(), ImageFile); + m_pImagePlane= new GLC_ImagePlane(ImageFile); } void GLC_Viewport::loadBackGroundImage(const QImage& image) { delete m_pImagePlane; - m_pImagePlane= new GLC_ImagePlane(m_pQGLWidget->context(), image); + m_pImagePlane= new GLC_ImagePlane(image); } void GLC_Viewport::deleteBackGroundImage() diff --git a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_viewport.h b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_viewport.h index 02bedf2af..33a86dea6 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_viewport.h +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_viewport.h @@ -94,9 +94,21 @@ public: inline double aspectRatio() const { return static_cast(m_WindowHSize) / static_cast(m_WindowVSize);} + //! Return the normalyse mouse position from screen coordinate + GLC_Point2d normalyseMousePosition(int x, int y); + + //! Map screen position to OpenGL screen position + GLC_Point2d mapToOpenGLScreen(int x, int y); + + //! Map normalyze screen position to OpenGL screen position + GLC_Point2d mapNormalyzeToOpenGLScreen(double x, double y); + //! Map Screen position to OpenGL position (On image Plane) according to this viewport GLC_Vector3d mapPosMouse( GLdouble Posx, GLdouble Posy) const; + //! Map normalyse Screen position to OpenGL position (On image Plane) according to this viewport + GLC_Vector3d mapNormalyzePosMouse(double Posx, double Posy) const; + //! Get this viewport's camera's angle of view inline double viewAngle() const { return m_ViewAngle;} diff --git a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_zoommover.cpp b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_zoommover.cpp index 2a5db1c85..0c288687a 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_zoommover.cpp +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_zoommover.cpp @@ -60,19 +60,19 @@ GLC_Mover* GLC_ZoomMover::clone() const ////////////////////////////////////////////////////////////////////// // Initialized the mover -void GLC_ZoomMover::init(QMouseEvent * e) +void GLC_ZoomMover::init(const GLC_UserInput& userInput) { // Change origine (view center) and cover between -1 and 1 const double vSize= static_cast(m_pViewport->viewVSize()); - m_PreviousVector.setY((vSize / 2.0 - e->y()) / ( vSize / 2.0)); + m_PreviousVector.setY((vSize / 2.0 - userInput.y()) / ( vSize / 2.0)); } // Move the camera -bool GLC_ZoomMover::move(QMouseEvent * e) +bool GLC_ZoomMover::move(const GLC_UserInput& userInput) { // Change origine (View Center) and cover (from -1 to 1) const double vSize= static_cast(m_pViewport->viewVSize()); - const double Posy= (vSize / 2.0 - e->y()) / ( vSize / 2.0); + const double Posy= (vSize / 2.0 - userInput.y()) / ( vSize / 2.0); // Compute zoom factor between (1 / MAXZOOMFACTOR) and (MAXZOOMFACTOR) double ZoomFactor= Posy - m_PreviousVector.y(); diff --git a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_zoommover.h b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_zoommover.h index d93cc1e55..862e33267 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_zoommover.h +++ b/ground/openpilotgcs/src/libs/glc_lib/viewport/glc_zoommover.h @@ -64,10 +64,10 @@ public: ////////////////////////////////////////////////////////////////////// public: //! Initialized the mover - virtual void init(QMouseEvent * e); + virtual void init(const GLC_UserInput& userInput); //! Move the camera - virtual bool move(QMouseEvent * e); + virtual bool move(const GLC_UserInput& userInput); //! Set the maximum zoom factor inline void setMaxZoomFactor(const double factor) From a636abef430ef2bb0f84605c6cab5f06cefb4b04 Mon Sep 17 00:00:00 2001 From: Laurent Ribon Date: Wed, 14 Mar 2012 19:57:50 +0100 Subject: [PATCH 14/19] Activate Modelview plugin with QT4.8 --- ground/openpilotgcs/src/plugins/plugins.pro | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ground/openpilotgcs/src/plugins/plugins.pro b/ground/openpilotgcs/src/plugins/plugins.pro index 8a7799e54..174ceba17 100644 --- a/ground/openpilotgcs/src/plugins/plugins.pro +++ b/ground/openpilotgcs/src/plugins/plugins.pro @@ -69,15 +69,13 @@ plugin_uavobjectbrowser.depends = plugin_coreplugin plugin_uavobjectbrowser.depends += plugin_uavobjects SUBDIRS += plugin_uavobjectbrowser -!contains(QT_VERSION, ^4\\.8\\..*) { -# Disable ModelView on 64-bit linux too -!linux-g++-64 { # ModelView UAVGadget plugin_modelview.subdir = modelview plugin_modelview.depends = plugin_coreplugin plugin_modelview.depends += plugin_uavobjects SUBDIRS += plugin_modelview -} + +!contains(QT_VERSION, ^4\\.8\\..*) { #Notify gadget plugin_notify.subdir = notify plugin_notify.depends = plugin_coreplugin From 8e3dc0a4d8c1ab95b6a494d2582435ad68c1693c Mon Sep 17 00:00:00 2001 From: Laurent Ribon Date: Wed, 14 Mar 2012 19:59:08 +0100 Subject: [PATCH 15/19] Update ignoring file list (Eclipse project specific files) --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 03797f84f..17b41c76c 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,5 @@ GPATH GRTAGS GSYMS GTAGS +/.cproject +/.project From 75d1f7409084efcce25b93c0b9d67c4888254a8d Mon Sep 17 00:00:00 2001 From: Laurent Ribon Date: Wed, 14 Mar 2012 20:03:39 +0100 Subject: [PATCH 16/19] Modify the modelview gadget widget in order to make it work with the latest vesrion of GLC_lib and Qt4.8 --- .../modelview/modelviewgadgetwidget.cpp | 144 +++++++++++++----- .../plugins/modelview/modelviewgadgetwidget.h | 21 +-- 2 files changed, 105 insertions(+), 60 deletions(-) diff --git a/ground/openpilotgcs/src/plugins/modelview/modelviewgadgetwidget.cpp b/ground/openpilotgcs/src/plugins/modelview/modelviewgadgetwidget.cpp index 6e1b2858d..96c89fe02 100644 --- a/ground/openpilotgcs/src/plugins/modelview/modelviewgadgetwidget.cpp +++ b/ground/openpilotgcs/src/plugins/modelview/modelviewgadgetwidget.cpp @@ -24,40 +24,40 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "QtDebug" + #include "modelviewgadgetwidget.h" #include "extensionsystem/pluginmanager.h" +#include "glc_context.h" +#include "glc_exception.h" +#include "glc_openglexception.h" +#include "viewport/glc_userinput.h" + #include ModelViewGadgetWidget::ModelViewGadgetWidget(QWidget *parent) - : QGLWidget(QGLFormat(QGL::SampleBuffers),parent) -// : QGLWidget(parent) -, m_pFactory(GLC_Factory::instance(this->context())) +: QGLWidget(new GLC_Context(QGLFormat(QGL::SampleBuffers)),parent) , m_Light() , m_World() , m_GlView(this) , m_MoverController() , m_ModelBoundingBox() , m_MotionTimer() +, acFilename() +, bgFilename() , vboEnable(false) +, loadError(true) +, mvInitGLSuccess(false) { - // Prevent crash on non-VBO enabled systems during GLC geometry creation - GLC_State::setVboUsage(vboEnable); setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); mvInitGLSuccess = false; CreateScene(); - m_Light.setPosition(4000.0, -40000.0, 80000.0); - m_Light.setAmbientColor(Qt::lightGray); - - m_GlView.cameraHandle()->setDefaultUpVector(glc::Z_AXIS); - m_GlView.cameraHandle()->setFrontView(); - m_GlView.setToOrtho(true); // orthogonal view - QColor repColor; repColor.setRgbF(1.0, 0.11372, 0.11372, 0.0); - m_MoverController= m_pFactory->createDefaultMoverController(repColor, &m_GlView); + m_MoverController= GLC_Factory::instance()->createDefaultMoverController(repColor, &m_GlView); // Get required UAVObjects ExtensionSystem::PluginManager* pm = ExtensionSystem::PluginManager::instance(); @@ -70,7 +70,31 @@ ModelViewGadgetWidget::ModelViewGadgetWidget(QWidget *parent) ModelViewGadgetWidget::~ModelViewGadgetWidget() { - //delete m_pFactory; + +} + + +void ModelViewGadgetWidget::setAcFilename(QString acf) +{ + if(QFile::exists(acf)) + acFilename = acf; + else + { + acFilename= acf= ":/modelview/models/warning_sign.obj"; + m_GlView.cameraHandle()->setFrontView(); // set to front camera to see/read the warning sign + } +} + +void ModelViewGadgetWidget::setBgFilename(QString bgf) +{ + if (QFile::exists(bgFilename)) + bgFilename = bgf; + else + { + qDebug() << "file " << bgf << " doesn't exists"; + bgFilename= ":/modelview/models/black.jpg"; // will put a black background if there's no background + } + } //// Public funcitons //// @@ -87,16 +111,21 @@ void ModelViewGadgetWidget::initializeGL() // OpenGL initialization m_GlView.initGl(); + m_Light.setPosition(4000.0, -40000.0, 80000.0); + m_Light.setAmbientColor(Qt::lightGray); + + m_GlView.cameraHandle()->setDefaultUpVector(glc::Z_AXIS); + m_GlView.cameraHandle()->setFrontView(); + m_GlView.setToOrtho(true); // orthogonal view + + /* // Enable VBO usage if configured (default is to disable) GLC_State::setVboUsage(vboEnable); if (!vboEnable) { qDebug("VBOs disabled. Enable for better performance if GPU supports it. (Most do)"); } - - m_GlView.reframe(m_ModelBoundingBox); - // Calculate camera depth of view - m_GlView.setDistMinAndMax(m_World.boundingBox()); + */ glEnable(GL_NORMALIZE); // Enable antialiasing glEnable(GL_MULTISAMPLE); @@ -107,36 +136,63 @@ void ModelViewGadgetWidget::initializeGL() void ModelViewGadgetWidget::paintGL() { - if (loadError) - return; - // Clear screen - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - // Load identity matrix - glLoadIdentity(); + try + { + // Clear screen + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - // Enable antialiasing - glEnable(GL_MULTISAMPLE); + // OpenGL error handler + { + GLenum error= glGetError(); + if (error != GL_NO_ERROR) + { + GLC_OpenGlException OpenGlException("ModelViewGadgetWidget::paintGL() ", error); + throw(OpenGlException); + } + } - // define the light - m_Light.enable(); + // Load identity matrix + glLoadIdentity(); - // define view matrix - m_GlView.glExecuteCam(); - m_Light.glExecute(); + // Enable antialiasing + glEnable(GL_MULTISAMPLE); - // Display the collection of GLC_Object - m_World.render(0, glc::TransparentRenderFlag); - m_World.render(0, glc::ShadingFlag); + // Calculate camera depth of view + m_GlView.setDistMinAndMax(m_World.boundingBox()); + + // define view matrix + m_Light.glExecute(); + m_GlView.glExecuteCam(); + + // Display the collection of GLC_Object + m_World.render(0, glc::ShadingFlag); + m_World.render(0, glc::TransparentRenderFlag); + + // Display UI Info (orbit circle) + m_MoverController.drawActiveMoverRep(); + mvInitGLSuccess = true; + } + catch (GLC_Exception &e) + { + qDebug() << e.what(); + } - // Display UI Info (orbit circle) - m_MoverController.drawActiveMoverRep(); - mvInitGLSuccess = true; } void ModelViewGadgetWidget::resizeGL(int width, int height) { m_GlView.setWinGLSize(width, height); // Compute window aspect ratio + // OpenGL error handler + { + GLenum error= glGetError(); + if (error != GL_NO_ERROR) + { + GLC_OpenGlException OpenGlException("ModelViewGadgetWidget::resizeGL() ", error); + throw(OpenGlException); + } + } + } // Create GLC_Object to display @@ -146,6 +202,7 @@ void ModelViewGadgetWidget::CreateScene() if (acFilename == ":/modelview/models/warning_sign.obj" or !QFile::exists(bgFilename)) bgFilename= ":/modelview/models/black.jpg"; + try { m_GlView.loadBackGroundImage(bgFilename); @@ -163,10 +220,11 @@ void ModelViewGadgetWidget::CreateScene() m_World= GLC_Factory::instance()->createWorldFromFile(aircraft); m_ModelBoundingBox= m_World.boundingBox(); m_GlView.reframe(m_ModelBoundingBox); // center 3D model in the scene - m_GlView.setDistMinAndMax(m_World.boundingBox()); loadError = false; + /* if (!mvInitGLSuccess) initializeGL(); + */ } else { loadError = true; } @@ -187,13 +245,14 @@ void ModelViewGadgetWidget::wheelEvent(QWheelEvent * e) void ModelViewGadgetWidget::mousePressEvent(QMouseEvent *e) { - if (m_MoverController.hasActiveMover()) return; + GLC_UserInput userInput(e->x(), e->y()); + if (m_MoverController.hasActiveMover()) return; switch (e->button()) { case (Qt::LeftButton): m_MotionTimer.stop(); - m_MoverController.setActiveMover(GLC_MoverController::TurnTable, e); + m_MoverController.setActiveMover(GLC_MoverController::TurnTable, userInput); updateGL(); break; case (Qt::RightButton): @@ -211,8 +270,9 @@ void ModelViewGadgetWidget::mousePressEvent(QMouseEvent *e) void ModelViewGadgetWidget::mouseMoveEvent(QMouseEvent * e) { - if (not m_MoverController.hasActiveMover()) return; - m_MoverController.move(e); + GLC_UserInput userInput(e->x(), e->y()); + if (not m_MoverController.hasActiveMover()) return; + m_MoverController.move(userInput); m_GlView.setDistMinAndMax(m_World.boundingBox()); updateGL(); } diff --git a/ground/openpilotgcs/src/plugins/modelview/modelviewgadgetwidget.h b/ground/openpilotgcs/src/plugins/modelview/modelviewgadgetwidget.h index 96b7b6d1f..2b5450cd2 100644 --- a/ground/openpilotgcs/src/plugins/modelview/modelviewgadgetwidget.h +++ b/ground/openpilotgcs/src/plugins/modelview/modelviewgadgetwidget.h @@ -50,23 +50,9 @@ class ModelViewGadgetWidget : public QGLWidget public: ModelViewGadgetWidget(QWidget *parent = 0); ~ModelViewGadgetWidget(); - void setAcFilename(QString acf) - { - if(QFile::exists(acf)) - acFilename = acf; - else - { - acFilename= acf= ":/modelview/models/warning_sign.obj"; - m_GlView.cameraHandle()->setFrontView(); // set to front camera to see/read the warning sign - } - } - void setBgFilename(QString bgf) - { - if (QFile::exists(bgFilename)) - bgFilename = bgf; - else - bgFilename= ":/modelview/models/black.jpg"; // will put a black background if there's no background - } + void setAcFilename(QString acf); + + void setBgFilename(QString bgf); void setVboEnable(bool eVbo) { vboEnable = eVbo; } void reloadScene(); void updateAttitude(int value); @@ -100,7 +86,6 @@ private: GLC_BoundingBox m_ModelBoundingBox; //! The timer used for motion QTimer m_MotionTimer; - int yMouseStart; QString acFilename; QString bgFilename; From 081769fb7b4cbb44715be233e3abf59ad5c20d27 Mon Sep 17 00:00:00 2001 From: Laurent Ribon Date: Mon, 19 Mar 2012 20:38:21 +0100 Subject: [PATCH 17/19] Make method setVboEnable not inline. There is more stuf to do. --- .../src/plugins/modelview/modelviewgadgetwidget.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ground/openpilotgcs/src/plugins/modelview/modelviewgadgetwidget.h b/ground/openpilotgcs/src/plugins/modelview/modelviewgadgetwidget.h index 2b5450cd2..a97bdfcda 100644 --- a/ground/openpilotgcs/src/plugins/modelview/modelviewgadgetwidget.h +++ b/ground/openpilotgcs/src/plugins/modelview/modelviewgadgetwidget.h @@ -53,7 +53,7 @@ public: void setAcFilename(QString acf); void setBgFilename(QString bgf); - void setVboEnable(bool eVbo) { vboEnable = eVbo; } + void setVboEnable(bool eVbo); void reloadScene(); void updateAttitude(int value); @@ -90,8 +90,6 @@ private: QString acFilename; QString bgFilename; bool vboEnable; - bool loadError; - bool mvInitGLSuccess; AttitudeActual* attActual; }; From b36be73ffb0ec082c7b7138cb0cc213d654eef81 Mon Sep 17 00:00:00 2001 From: Laurent Ribon Date: Mon, 19 Mar 2012 20:41:28 +0100 Subject: [PATCH 18/19] Add the possibility to set VBO usage when running.(No restart required) Change Zoomm sensibility (made it more sensitive) Remobe unecessary state variable : - load Error - init failed (An OpenGL init failed cause a Crash) --- .../modelview/modelviewgadgetwidget.cpp | 34 +++++-------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/ground/openpilotgcs/src/plugins/modelview/modelviewgadgetwidget.cpp b/ground/openpilotgcs/src/plugins/modelview/modelviewgadgetwidget.cpp index 96c89fe02..47e280e33 100644 --- a/ground/openpilotgcs/src/plugins/modelview/modelviewgadgetwidget.cpp +++ b/ground/openpilotgcs/src/plugins/modelview/modelviewgadgetwidget.cpp @@ -46,13 +46,9 @@ ModelViewGadgetWidget::ModelViewGadgetWidget(QWidget *parent) , acFilename() , bgFilename() , vboEnable(false) -, loadError(true) -, mvInitGLSuccess(false) { setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); - mvInitGLSuccess = false; - CreateScene(); QColor repColor; @@ -64,7 +60,6 @@ ModelViewGadgetWidget::ModelViewGadgetWidget(QWidget *parent) UAVObjectManager* objManager = pm->getObject(); attActual = AttitudeActual::GetInstance(objManager); - // Create objects to display connect(&m_MotionTimer, SIGNAL(timeout()), this, SLOT(updateAttitude())); } @@ -97,6 +92,12 @@ void ModelViewGadgetWidget::setBgFilename(QString bgf) } +void ModelViewGadgetWidget::setVboEnable(bool eVbo) +{ + vboEnable = eVbo; + m_World.collection()->setVboUsage(vboEnable); +} + //// Public funcitons //// void ModelViewGadgetWidget::reloadScene() { @@ -106,8 +107,6 @@ void ModelViewGadgetWidget::reloadScene() //// Private functions //// void ModelViewGadgetWidget::initializeGL() { - if (loadError) - return; // OpenGL initialization m_GlView.initGl(); @@ -118,14 +117,6 @@ void ModelViewGadgetWidget::initializeGL() m_GlView.cameraHandle()->setFrontView(); m_GlView.setToOrtho(true); // orthogonal view - /* - // Enable VBO usage if configured (default is to disable) - GLC_State::setVboUsage(vboEnable); - if (!vboEnable) - { - qDebug("VBOs disabled. Enable for better performance if GPU supports it. (Most do)"); - } - */ glEnable(GL_NORMALIZE); // Enable antialiasing glEnable(GL_MULTISAMPLE); @@ -170,7 +161,6 @@ void ModelViewGadgetWidget::paintGL() // Display UI Info (orbit circle) m_MoverController.drawActiveMoverRep(); - mvInitGLSuccess = true; } catch (GLC_Exception &e) { @@ -215,30 +205,24 @@ void ModelViewGadgetWidget::CreateScene() try { if(QFile::exists(acFilename)) - { + { QFile aircraft(acFilename); m_World= GLC_Factory::instance()->createWorldFromFile(aircraft); m_ModelBoundingBox= m_World.boundingBox(); m_GlView.reframe(m_ModelBoundingBox); // center 3D model in the scene - loadError = false; - /* - if (!mvInitGLSuccess) - initializeGL(); - */ } else { - loadError = true; + qDebug("ModelView: aircraft file not found."); } } catch(GLC_Exception e) { qDebug("ModelView: aircraft file loading failed."); - loadError = true; } } void ModelViewGadgetWidget::wheelEvent(QWheelEvent * e) { - double delta = m_GlView.cameraHandle()->distEyeTarget() - (e->delta()/120) ; + double delta = m_GlView.cameraHandle()->distEyeTarget() - (e->delta()/4) ; m_GlView.cameraHandle()->setDistEyeTarget(delta); m_GlView.setDistMinAndMax(m_World.boundingBox()); } From 299f9fd5d3c5dbbea8419b78ffc3fe4938d5b19f Mon Sep 17 00:00:00 2001 From: Laurent Ribon Date: Tue, 20 Mar 2012 00:10:31 +0100 Subject: [PATCH 19/19] Comment the line "VERSION = 2.2.0" --- ground/openpilotgcs/src/libs/glc_lib/glc_lib.pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ground/openpilotgcs/src/libs/glc_lib/glc_lib.pro b/ground/openpilotgcs/src/libs/glc_lib/glc_lib.pro index 6966f94b5..f66e54c68 100644 --- a/ground/openpilotgcs/src/libs/glc_lib/glc_lib.pro +++ b/ground/openpilotgcs/src/libs/glc_lib/glc_lib.pro @@ -12,7 +12,7 @@ QT += opengl \ CONFIG += exceptions \ warn_on #TARGET = GLC_lib -VERSION = 2.2.0 +#VERSION = 2.2.0 DEFINES += CREATE_GLC_LIB_DLL DEFINES += LIB3DS_EXPORTS