From bc8eadd526b70b4e0ca624b8d541e9ae21efd140 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 4 Jun 2013 10:22:58 -0700 Subject: [PATCH] More work on voxeltars; separated default pose from bind pose. --- interface/CMakeLists.txt | 4 +- interface/src/Application.cpp | 44 ++++++++++++++++++-- interface/src/Application.h | 9 +++++ interface/src/Avatar.h | 2 + interface/src/AvatarVoxelSystem.cpp | 63 ++++++++++++++++++++++------- interface/src/AvatarVoxelSystem.h | 23 +++++++++-- interface/src/Skeleton.cpp | 61 ++++++++++++++++++++-------- interface/src/Skeleton.h | 1 + interface/src/VoxelSystem.h | 4 +- 9 files changed, 171 insertions(+), 40 deletions(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index d5da2073fb..b2984b35bb 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -63,12 +63,12 @@ if (APPLE) endif (APPLE) -find_package(Qt4 REQUIRED QtCore QtGui QtOpenGL) +find_package(Qt4 REQUIRED QtCore QtGui QtNetwork QtOpenGL) include(${QT_USE_FILE}) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${QT_QTGUI_INCLUDE_DIR}") # run qt moc on qt-enabled headers -qt4_wrap_cpp(INTERFACE_SRCS src/Application.h) +qt4_wrap_cpp(INTERFACE_SRCS src/Application.h src/AvatarVoxelSystem.h) # create the executable, make it a bundle on OS X add_executable(${TARGET_NAME} MACOSX_BUNDLE ${INTERFACE_SRCS}) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e8aaaf9856..57f1b89969 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -21,16 +21,23 @@ #include #include +#include #include +#include #include +#include #include #include +#include #include #include #include +#include #include +#include #include #include +#include #include #include @@ -1122,6 +1129,31 @@ void Application::terminate() { } } +void Application::editPreferences() { + QDialog dialog(_glWidget); + dialog.setWindowTitle("Interface Preferences"); + QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom); + dialog.setLayout(layout); + + QFormLayout* form = new QFormLayout(); + layout->addLayout(form, 1); + + QLineEdit* avatarURL = new QLineEdit(_settings->value("avatarURL").toString()); + form->addRow("Avatar URL:", avatarURL); + + QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + dialog.connect(buttons, SIGNAL(accepted()), SLOT(accept())); + dialog.connect(buttons, SIGNAL(rejected()), SLOT(reject())); + layout->addWidget(buttons); + + if (dialog.exec() != QDialog::Accepted) { + return; + } + QUrl url(avatarURL->text()); + _settings->setValue("avatarURL", url); + _myAvatar.getVoxels()->loadVoxelsFromURL(url); +} + void Application::pair() { PairingHandler::sendPairRequest(); } @@ -1285,6 +1317,9 @@ void Application::initMenu() { QMenu* fileMenu = menuBar->addMenu("File"); fileMenu->addAction("Quit", this, SLOT(quit()), Qt::CTRL | Qt::Key_Q); + QMenu* editMenu = menuBar->addMenu("Edit"); + editMenu->addAction("Preferences...", this, SLOT(editPreferences())); + QMenu* pairMenu = menuBar->addMenu("Pair"); pairMenu->addAction("Pair", this, SLOT(pair())); @@ -1325,9 +1360,7 @@ void Application::initMenu() { renderMenu->addAction("First Person", this, SLOT(setRenderFirstPerson(bool)), Qt::Key_P)->setCheckable(true); QMenu* toolsMenu = menuBar->addMenu("Tools"); - (_renderStatsOn = toolsMenu->addAction("Stats"))->setCheckable(true); - _renderStatsOn->setShortcut(Qt::Key_Slash); (_logOn = toolsMenu->addAction("Log"))->setCheckable(true); _logOn->setChecked(false); @@ -1381,6 +1414,9 @@ void Application::initMenu() { debugMenu->addAction("Wants Res-In", this, SLOT(setWantsResIn(bool)))->setCheckable(true); debugMenu->addAction("Wants Monochrome", this, SLOT(setWantsMonochrome(bool)))->setCheckable(true); debugMenu->addAction("Wants View Delta Sending", this, SLOT(setWantsDelta(bool)))->setCheckable(true); + + _networkAccessManager = new QNetworkAccessManager(this); + _settings = new QSettings("High Fidelity", "Interface", this); } void Application::updateFrustumRenderModeAction() { @@ -1431,6 +1467,8 @@ void Application::init() { _myCamera.setModeShiftRate(1.0f); _myAvatar.setDisplayingLookatVectors(false); + _myAvatar.getVoxels()->loadVoxelsFromURL(_settings->value("avatarURL").toUrl()); + QCursor::setPos(_headMouseX, _headMouseY); OculusManager::connect(); @@ -1727,7 +1765,7 @@ void Application::displayOculus(Camera& whichCamera) { glPopMatrix(); } - + void Application::displaySide(Camera& whichCamera) { // transform by eye offset diff --git a/interface/src/Application.h b/interface/src/Application.h index c2918f1add..1ed24683ae 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -37,6 +37,8 @@ class QGLWidget; class QKeyEvent; class QMainWindow; class QMouseEvent; +class QNetworkAccessManager; +class QSettings; class QWheelEvent; class Agent; @@ -70,6 +72,8 @@ public: Environment* getEnvironment() { return &_environment; } bool shouldEchoAudio() { return _echoAudioMode->isChecked(); } + QNetworkAccessManager* getNetworkAccessManager() { return _networkAccessManager; } + /*! @fn getSettingBool @brief A function for getting boolean settings from the settings file. @@ -127,6 +131,8 @@ private slots: void idle(); void terminate(); + void editPreferences(); + void pair(); void setHead(bool head); @@ -227,6 +233,9 @@ private: QAction* _fullScreenMode; // whether we are in full screen mode QAction* _frustumRenderModeAction; + QNetworkAccessManager* _networkAccessManager; + QSettings* _settings; + SerialInterface _serialPort; bool _displayLevels; diff --git a/interface/src/Avatar.h b/interface/src/Avatar.h index c7561d3986..475fc0bc59 100644 --- a/interface/src/Avatar.h +++ b/interface/src/Avatar.h @@ -89,6 +89,8 @@ public: glm::quat getOrientation () const; glm::quat getWorldAlignedOrientation() const; + AvatarVoxelSystem* getVoxels() { return &_voxels; } + // Set what driving keys are being pressed to control thrust levels void setDriveKeys(int key, bool val) { _driveKeys[key] = val; }; bool getDriveKeys(int key) { return _driveKeys[key]; }; diff --git a/interface/src/AvatarVoxelSystem.cpp b/interface/src/AvatarVoxelSystem.cpp index 9e441c148c..5fdf974f71 100644 --- a/interface/src/AvatarVoxelSystem.cpp +++ b/interface/src/AvatarVoxelSystem.cpp @@ -7,6 +7,12 @@ #include +#include +#include + +#include + +#include "Application.h" #include "Avatar.h" #include "AvatarVoxelSystem.h" #include "renderer/ProgramObject.h" @@ -17,7 +23,7 @@ const int BONE_ELEMENTS_PER_VOXEL = BONE_ELEMENTS_PER_VERTEX * VERTICES_PER_VOXE AvatarVoxelSystem::AvatarVoxelSystem(Avatar* avatar) : VoxelSystem(AVATAR_TREE_SCALE, MAX_VOXELS_PER_AVATAR), - _avatar(avatar) { + _avatar(avatar), _voxelReply(0) { } AvatarVoxelSystem::~AvatarVoxelSystem() { @@ -52,16 +58,6 @@ void AvatarVoxelSystem::init() { glBindBuffer(GL_ARRAY_BUFFER, _vboBoneWeightsID); glBufferData(GL_ARRAY_BUFFER, BONE_ELEMENTS_PER_VOXEL * sizeof(GLfloat) * _maxVoxels, NULL, GL_DYNAMIC_DRAW); - for (int i = 0; i < 150; i++) { - int power = pow(2, randIntInRange(6, 8)); - float size = 1.0f / power; - _tree->createVoxel( - randIntInRange(0, power - 1) * size, - randIntInRange(0, power - 1) * size, - randIntInRange(0, power - 1) * size, size, 255, 0, 255, true); - } - setupNewVoxelsForDrawing(); - // load our skin program if this is the first avatar system to initialize if (_skinProgram != 0) { return; @@ -75,8 +71,25 @@ void AvatarVoxelSystem::init() { _boneWeightsLocation = _skinProgram->attributeLocation("boneWeights"); } -void AvatarVoxelSystem::render(bool texture) { - VoxelSystem::render(texture); +void AvatarVoxelSystem::removeOutOfView() { + // no-op for now +} + +void AvatarVoxelSystem::loadVoxelsFromURL(const QUrl& url) { + // cancel any current download + if (_voxelReply != 0) { + delete _voxelReply; + } + + killLocalVoxels(); + + // load the URL data asynchronously + if (!url.isValid()) { + return; + } + _voxelReply = Application::getInstance()->getNetworkAccessManager()->get(QNetworkRequest(url)); + connect(_voxelReply, SIGNAL(readyRead()), SLOT(readVoxelDataFromReply())); + connect(_voxelReply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleVoxelReplyError())); } void AvatarVoxelSystem::updateNodeInArrays(glBufferIndex nodeIndex, const glm::vec3& startVertex, @@ -168,6 +181,25 @@ void AvatarVoxelSystem::removeScaleAndReleaseProgram(bool texture) { _skinProgram->disableAttributeArray(_boneWeightsLocation); } +void AvatarVoxelSystem::readVoxelDataFromReply() { + // for now, just wait until we have the full business + if (!_voxelReply->isFinished()) { + return; + } + QByteArray entirety = _voxelReply->readAll(); + _voxelReply->deleteLater(); + _voxelReply = 0; + _tree->readBitstreamToTree((unsigned char*)entirety.data(), entirety.size(), WANT_COLOR, NO_EXISTS_BITS); + setupNewVoxelsForDrawing(); +} + +void AvatarVoxelSystem::handleVoxelReplyError() { + printLog("%s\n", _voxelReply->errorString().toAscii().constData()); + + _voxelReply->deleteLater(); + _voxelReply = 0; +} + class IndexDistance { public: IndexDistance(GLubyte index = 0, float distance = FLT_MAX) : index(index), distance(distance) { } @@ -183,7 +215,10 @@ void AvatarVoxelSystem::computeBoneIndicesAndWeights(const glm::vec3& vertex, Bo // find the nearest four joints (TODO: use a better data structure for the pose positions to speed this up) IndexDistance nearest[BONE_ELEMENTS_PER_VERTEX]; for (int i = 0; i < NUM_AVATAR_JOINTS; i++) { - float distance = glm::distance(jointVertex, _avatar->getSkeleton().joint[i].absoluteBindPosePosition); + AvatarJointID parent = _avatar->getSkeleton().joint[i].parent; + float distance = glm::length(computeVectorFromPointToSegment(jointVertex, + _avatar->getSkeleton().joint[parent == AVATAR_JOINT_NULL ? i : parent].absoluteBindPosePosition, + _avatar->getSkeleton().joint[i].absoluteBindPosePosition)); for (int j = 0; j < BONE_ELEMENTS_PER_VERTEX; j++) { if (distance < nearest[j].distance) { // move the rest of the indices down diff --git a/interface/src/AvatarVoxelSystem.h b/interface/src/AvatarVoxelSystem.h index e3a80c3817..edeeadb17c 100644 --- a/interface/src/AvatarVoxelSystem.h +++ b/interface/src/AvatarVoxelSystem.h @@ -9,22 +9,32 @@ #ifndef __interface__AvatarVoxelSystem__ #define __interface__AvatarVoxelSystem__ +#include + #include "VoxelSystem.h" const int BONE_ELEMENTS_PER_VERTEX = 4; typedef GLubyte BoneIndices[BONE_ELEMENTS_PER_VERTEX]; +class QNetworkReply; +class QUrl; + class Avatar; -class AvatarVoxelSystem : public VoxelSystem { +class AvatarVoxelSystem : public QObject, public VoxelSystem { + Q_OBJECT + public: - + AvatarVoxelSystem(Avatar* avatar); virtual ~AvatarVoxelSystem(); virtual void init(); - virtual void render(bool texture); + virtual void removeOutOfView(); + + void loadVoxelsFromURL(const QUrl& url); + protected: virtual void updateNodeInArrays(glBufferIndex nodeIndex, const glm::vec3& startVertex, @@ -33,6 +43,11 @@ protected: virtual void updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd); virtual void applyScaleAndBindProgram(bool texture); virtual void removeScaleAndReleaseProgram(bool texture); + +private slots: + + void readVoxelDataFromReply(); + void handleVoxelReplyError(); private: @@ -48,6 +63,8 @@ private: GLuint _vboBoneIndicesID; GLuint _vboBoneWeightsID; + QNetworkReply* _voxelReply; + static ProgramObject* _skinProgram; static int _boneMatricesLocation; static int _boneIndicesLocation; diff --git a/interface/src/Skeleton.cpp b/interface/src/Skeleton.cpp index 136cb6f619..a694b4032d 100644 --- a/interface/src/Skeleton.cpp +++ b/interface/src/Skeleton.cpp @@ -49,7 +49,7 @@ void Skeleton::initialize() { joint[ AVATAR_JOINT_RIGHT_HEEL ].parent = AVATAR_JOINT_RIGHT_KNEE; joint[ AVATAR_JOINT_RIGHT_TOES ].parent = AVATAR_JOINT_RIGHT_HEEL; - // specify the default pose position + // specify the bind pose position joint[ AVATAR_JOINT_PELVIS ].bindPosePosition = glm::vec3( 0.0, 0.0, 0.0 ); joint[ AVATAR_JOINT_TORSO ].bindPosePosition = glm::vec3( 0.0, 0.09, -0.01 ); joint[ AVATAR_JOINT_CHEST ].bindPosePosition = glm::vec3( 0.0, 0.09, -0.01 ); @@ -58,29 +58,58 @@ void Skeleton::initialize() { joint[ AVATAR_JOINT_LEFT_COLLAR ].bindPosePosition = glm::vec3( -0.06, 0.04, 0.01 ); joint[ AVATAR_JOINT_LEFT_SHOULDER ].bindPosePosition = glm::vec3( -0.05, 0.0, 0.01 ); - joint[ AVATAR_JOINT_LEFT_ELBOW ].bindPosePosition = glm::vec3( 0.0, -0.16, 0.0 ); - joint[ AVATAR_JOINT_LEFT_WRIST ].bindPosePosition = glm::vec3( 0.0, -0.117, 0.0 ); - joint[ AVATAR_JOINT_LEFT_FINGERTIPS ].bindPosePosition = glm::vec3( 0.0, -0.1, 0.0 ); + joint[ AVATAR_JOINT_LEFT_ELBOW ].bindPosePosition = glm::vec3( -0.16, 0.0, 0.0 ); + joint[ AVATAR_JOINT_LEFT_WRIST ].bindPosePosition = glm::vec3( -0.12, 0.0, 0.0 ); + joint[ AVATAR_JOINT_LEFT_FINGERTIPS ].bindPosePosition = glm::vec3( -0.1, 0.0, 0.0 ); joint[ AVATAR_JOINT_RIGHT_COLLAR ].bindPosePosition = glm::vec3( 0.06, 0.04, 0.01 ); joint[ AVATAR_JOINT_RIGHT_SHOULDER ].bindPosePosition = glm::vec3( 0.05, 0.0, 0.01 ); - joint[ AVATAR_JOINT_RIGHT_ELBOW ].bindPosePosition = glm::vec3( 0.0, -0.16, 0.0 ); - joint[ AVATAR_JOINT_RIGHT_WRIST ].bindPosePosition = glm::vec3( 0.0, -0.117, 0.0 ); - joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].bindPosePosition = glm::vec3( 0.0, -0.1, 0.0 ); + joint[ AVATAR_JOINT_RIGHT_ELBOW ].bindPosePosition = glm::vec3( 0.16, 0.0, 0.0 ); + joint[ AVATAR_JOINT_RIGHT_WRIST ].bindPosePosition = glm::vec3( 0.12, 0.0, 0.0 ); + joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].bindPosePosition = glm::vec3( 0.1, 0.0, 0.0 ); joint[ AVATAR_JOINT_LEFT_HIP ].bindPosePosition = glm::vec3( -0.05, 0.0, 0.02 ); - joint[ AVATAR_JOINT_LEFT_KNEE ].bindPosePosition = glm::vec3( 0.01, -0.25, -0.03 ); - joint[ AVATAR_JOINT_LEFT_HEEL ].bindPosePosition = glm::vec3( 0.01, -0.22, 0.08 ); - joint[ AVATAR_JOINT_LEFT_TOES ].bindPosePosition = glm::vec3( 0.00, -0.03, -0.05 ); + joint[ AVATAR_JOINT_LEFT_KNEE ].bindPosePosition = glm::vec3( 0.00, -0.25, 0.00 ); + joint[ AVATAR_JOINT_LEFT_HEEL ].bindPosePosition = glm::vec3( 0.00, -0.23, 0.00 ); + joint[ AVATAR_JOINT_LEFT_TOES ].bindPosePosition = glm::vec3( 0.00, 0.00, -0.06 ); joint[ AVATAR_JOINT_RIGHT_HIP ].bindPosePosition = glm::vec3( 0.05, 0.0, 0.02 ); - joint[ AVATAR_JOINT_RIGHT_KNEE ].bindPosePosition = glm::vec3( -0.01, -0.25, -0.03 ); - joint[ AVATAR_JOINT_RIGHT_HEEL ].bindPosePosition = glm::vec3( -0.01, -0.22, 0.08 ); - joint[ AVATAR_JOINT_RIGHT_TOES ].bindPosePosition = glm::vec3( 0.00, -0.03, -0.05 ); + joint[ AVATAR_JOINT_RIGHT_KNEE ].bindPosePosition = glm::vec3( 0.00, -0.25, 0.00 ); + joint[ AVATAR_JOINT_RIGHT_HEEL ].bindPosePosition = glm::vec3( 0.00, -0.23, 0.00 ); + joint[ AVATAR_JOINT_RIGHT_TOES ].bindPosePosition = glm::vec3( 0.00, 0.00, -0.06 ); + + // specify the default pose position + joint[ AVATAR_JOINT_PELVIS ].defaultPosePosition = glm::vec3( 0.0, 0.0, 0.0 ); + joint[ AVATAR_JOINT_TORSO ].defaultPosePosition = glm::vec3( 0.0, 0.09, -0.01 ); + joint[ AVATAR_JOINT_CHEST ].defaultPosePosition = glm::vec3( 0.0, 0.09, -0.01 ); + joint[ AVATAR_JOINT_NECK_BASE ].defaultPosePosition = glm::vec3( 0.0, 0.14, 0.01 ); + joint[ AVATAR_JOINT_HEAD_BASE ].defaultPosePosition = glm::vec3( 0.0, 0.04, 0.00 ); + + joint[ AVATAR_JOINT_LEFT_COLLAR ].defaultPosePosition = glm::vec3( -0.06, 0.04, 0.01 ); + joint[ AVATAR_JOINT_LEFT_SHOULDER ].defaultPosePosition = glm::vec3( -0.05, 0.0, 0.01 ); + joint[ AVATAR_JOINT_LEFT_ELBOW ].defaultPosePosition = glm::vec3( 0.0, -0.16, 0.0 ); + joint[ AVATAR_JOINT_LEFT_WRIST ].defaultPosePosition = glm::vec3( 0.0, -0.117, 0.0 ); + joint[ AVATAR_JOINT_LEFT_FINGERTIPS ].defaultPosePosition = glm::vec3( 0.0, -0.1, 0.0 ); + + joint[ AVATAR_JOINT_RIGHT_COLLAR ].defaultPosePosition = glm::vec3( 0.06, 0.04, 0.01 ); + joint[ AVATAR_JOINT_RIGHT_SHOULDER ].defaultPosePosition = glm::vec3( 0.05, 0.0, 0.01 ); + joint[ AVATAR_JOINT_RIGHT_ELBOW ].defaultPosePosition = glm::vec3( 0.0, -0.16, 0.0 ); + joint[ AVATAR_JOINT_RIGHT_WRIST ].defaultPosePosition = glm::vec3( 0.0, -0.117, 0.0 ); + joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].defaultPosePosition = glm::vec3( 0.0, -0.1, 0.0 ); + + joint[ AVATAR_JOINT_LEFT_HIP ].defaultPosePosition = glm::vec3( -0.05, 0.0, 0.02 ); + joint[ AVATAR_JOINT_LEFT_KNEE ].defaultPosePosition = glm::vec3( 0.01, -0.25, -0.03 ); + joint[ AVATAR_JOINT_LEFT_HEEL ].defaultPosePosition = glm::vec3( 0.01, -0.22, 0.08 ); + joint[ AVATAR_JOINT_LEFT_TOES ].defaultPosePosition = glm::vec3( 0.00, -0.03, -0.05 ); + + joint[ AVATAR_JOINT_RIGHT_HIP ].defaultPosePosition = glm::vec3( 0.05, 0.0, 0.02 ); + joint[ AVATAR_JOINT_RIGHT_KNEE ].defaultPosePosition = glm::vec3( -0.01, -0.25, -0.03 ); + joint[ AVATAR_JOINT_RIGHT_HEEL ].defaultPosePosition = glm::vec3( -0.01, -0.22, 0.08 ); + joint[ AVATAR_JOINT_RIGHT_TOES ].defaultPosePosition = glm::vec3( 0.00, -0.03, -0.05 ); - // calculate bone length, absolute positions/rotations + // calculate bone length, absolute bind positions/rotations for (int b = 0; b < NUM_AVATAR_JOINTS; b++) { - joint[b].length = glm::length(joint[b].bindPosePosition); + joint[b].length = glm::length(joint[b].defaultPosePosition); if (joint[b].parent == AVATAR_JOINT_NULL) { joint[b].absoluteBindPosePosition = joint[b].bindPosePosition; @@ -106,7 +135,7 @@ void Skeleton::update(float deltaTime, const glm::quat& orientation, glm::vec3 p joint[b].position = joint[ joint[b].parent ].position; } - glm::vec3 rotatedJointVector = joint[b].rotation * joint[b].bindPosePosition; + glm::vec3 rotatedJointVector = joint[b].rotation * joint[b].defaultPosePosition; joint[b].position += rotatedJointVector; } } diff --git a/interface/src/Skeleton.h b/interface/src/Skeleton.h index 78405b39ac..dd55c5fa68 100644 --- a/interface/src/Skeleton.h +++ b/interface/src/Skeleton.h @@ -61,6 +61,7 @@ public: { AvatarJointID parent; // which joint is this joint connected to? glm::vec3 position; // the position at the "end" of the joint - in global space + glm::vec3 defaultPosePosition; // the parent relative position when the avatar is in the default pose glm::vec3 bindPosePosition; // the parent relative position when the avatar is in the "T-pose" glm::vec3 absoluteBindPosePosition; // the absolute position when the avatar is in the "T-pose" glm::quat absoluteBindPoseRotation; // the absolute rotation when the avatar is in the "T-pose" diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index 3a46e19517..e310fdaf7c 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -33,7 +33,7 @@ public: virtual void init(); void simulate(float deltaTime) { }; - virtual void render(bool texture); + void render(bool texture); unsigned long getVoxelsUpdated() const {return _voxelsUpdated;}; unsigned long getVoxelsRendered() const {return _voxelsInReadArrays;}; @@ -59,7 +59,7 @@ public: void setRenderPipelineWarnings(bool on) { _renderWarningsOn = on; }; bool getRenderPipelineWarnings() const { return _renderWarningsOn; }; - void removeOutOfView(); + virtual void removeOutOfView(); bool hasViewChanged(); bool isViewChanging();