Merge pull request #1735 from AndrewMeadows/fix-particle-avatar-collisions

Fix crash on shutdown caused by duplicate delete on a QObject
This commit is contained in:
ZappoMan 2014-01-29 17:54:23 -08:00
commit c58f44f54a
6 changed files with 145 additions and 142 deletions

View file

@ -121,6 +121,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_wantToKillLocalVoxels(false), _wantToKillLocalVoxels(false),
_audioScope(256, 200, true), _audioScope(256, 200, true),
_avatarManager(), _avatarManager(),
_myAvatar(NULL),
_profile(QString()), _profile(QString()),
_mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)), _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)),
_mouseX(0), _mouseX(0),
@ -157,6 +158,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_logger(new FileLogger()), _logger(new FileLogger()),
_persistThread(NULL) _persistThread(NULL)
{ {
_myAvatar = _avatarManager.getMyAvatar();
_applicationStartupTime = startup_time; _applicationStartupTime = startup_time;
switchToResourcesParentIfRequired(); switchToResourcesParentIfRequired();
@ -327,6 +330,9 @@ Application::~Application() {
VoxelTreeElement::removeDeleteHook(&_voxels); // we don't need to do this processing on shutdown VoxelTreeElement::removeDeleteHook(&_voxels); // we don't need to do this processing on shutdown
Menu::getInstance()->deleteLater(); Menu::getInstance()->deleteLater();
_avatarManager.clear();
_myAvatar = NULL;
delete _logger; delete _logger;
delete _settings; delete _settings;
delete _glWidget; delete _glWidget;
@ -441,25 +447,25 @@ void Application::paintGL() {
_myCamera.setUpShift (0.0f); _myCamera.setUpShift (0.0f);
_myCamera.setDistance (0.0f); _myCamera.setDistance (0.0f);
_myCamera.setTightness (0.0f); // Camera is directly connected to head without smoothing _myCamera.setTightness (0.0f); // Camera is directly connected to head without smoothing
_myCamera.setTargetPosition(_myAvatar.getHead().calculateAverageEyePosition()); _myCamera.setTargetPosition(_myAvatar->getHead().calculateAverageEyePosition());
_myCamera.setTargetRotation(_myAvatar.getHead().getOrientation()); _myCamera.setTargetRotation(_myAvatar->getHead().getOrientation());
} else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { } else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
_myCamera.setTightness(0.0f); // In first person, camera follows head exactly without delay _myCamera.setTightness(0.0f); // In first person, camera follows head exactly without delay
_myCamera.setTargetPosition(_myAvatar.getHead().calculateAverageEyePosition()); _myCamera.setTargetPosition(_myAvatar->getHead().calculateAverageEyePosition());
_myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation()); _myCamera.setTargetRotation(_myAvatar->getHead().getCameraOrientation());
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
_myCamera.setTightness (0.0f); // Camera is directly connected to head without smoothing _myCamera.setTightness (0.0f); // Camera is directly connected to head without smoothing
_myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition()); _myCamera.setTargetPosition(_myAvatar->getUprightHeadPosition());
_myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation()); _myCamera.setTargetRotation(_myAvatar->getHead().getCameraOrientation());
} else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
_myCamera.setTightness(0.0f); _myCamera.setTightness(0.0f);
float headHeight = _myAvatar.getHead().calculateAverageEyePosition().y - _myAvatar.getPosition().y; float headHeight = _myAvatar->getHead().calculateAverageEyePosition().y - _myAvatar->getPosition().y;
_myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _myAvatar.getScale()); _myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _myAvatar->getScale());
_myCamera.setTargetPosition(_myAvatar.getPosition() + glm::vec3(0, headHeight, 0)); _myCamera.setTargetPosition(_myAvatar->getPosition() + glm::vec3(0, headHeight, 0));
_myCamera.setTargetRotation(_myAvatar.getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f))); _myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f)));
} }
// Update camera position // Update camera position
@ -516,22 +522,22 @@ void Application::paintGL() {
bool eyeRelativeCamera = false; bool eyeRelativeCamera = false;
if (_rearMirrorTools->getZoomLevel() == BODY) { if (_rearMirrorTools->getZoomLevel() == BODY) {
_mirrorCamera.setDistance(MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar.getScale()); _mirrorCamera.setDistance(MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale());
_mirrorCamera.setTargetPosition(_myAvatar.getChestPosition()); _mirrorCamera.setTargetPosition(_myAvatar->getChestPosition());
} else { // HEAD zoom level } else { // HEAD zoom level
_mirrorCamera.setDistance(MIRROR_REARVIEW_DISTANCE * _myAvatar.getScale()); _mirrorCamera.setDistance(MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale());
if (_myAvatar.getSkeletonModel().isActive() && _myAvatar.getHead().getFaceModel().isActive()) { if (_myAvatar->getSkeletonModel().isActive() && _myAvatar->getHead().getFaceModel().isActive()) {
// as a hack until we have a better way of dealing with coordinate precision issues, reposition the // as a hack until we have a better way of dealing with coordinate precision issues, reposition the
// face/body so that the average eye position lies at the origin // face/body so that the average eye position lies at the origin
eyeRelativeCamera = true; eyeRelativeCamera = true;
_mirrorCamera.setTargetPosition(glm::vec3()); _mirrorCamera.setTargetPosition(glm::vec3());
} else { } else {
_mirrorCamera.setTargetPosition(_myAvatar.getHead().calculateAverageEyePosition()); _mirrorCamera.setTargetPosition(_myAvatar->getHead().calculateAverageEyePosition());
} }
} }
_mirrorCamera.setTargetRotation(_myAvatar.getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f))); _mirrorCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f)));
_mirrorCamera.update(1.0f/_fps); _mirrorCamera.update(1.0f/_fps);
// set the bounds of rear mirror view // set the bounds of rear mirror view
@ -548,27 +554,27 @@ void Application::paintGL() {
glPushMatrix(); glPushMatrix();
if (eyeRelativeCamera) { if (eyeRelativeCamera) {
// save absolute translations // save absolute translations
glm::vec3 absoluteSkeletonTranslation = _myAvatar.getSkeletonModel().getTranslation(); glm::vec3 absoluteSkeletonTranslation = _myAvatar->getSkeletonModel().getTranslation();
glm::vec3 absoluteFaceTranslation = _myAvatar.getHead().getFaceModel().getTranslation(); glm::vec3 absoluteFaceTranslation = _myAvatar->getHead().getFaceModel().getTranslation();
// get the eye positions relative to the neck and use them to set the face translation // get the eye positions relative to the neck and use them to set the face translation
glm::vec3 leftEyePosition, rightEyePosition; glm::vec3 leftEyePosition, rightEyePosition;
_myAvatar.getHead().getFaceModel().setTranslation(glm::vec3()); _myAvatar->getHead().getFaceModel().setTranslation(glm::vec3());
_myAvatar.getHead().getFaceModel().getEyePositions(leftEyePosition, rightEyePosition); _myAvatar->getHead().getFaceModel().getEyePositions(leftEyePosition, rightEyePosition);
_myAvatar.getHead().getFaceModel().setTranslation((leftEyePosition + rightEyePosition) * -0.5f); _myAvatar->getHead().getFaceModel().setTranslation((leftEyePosition + rightEyePosition) * -0.5f);
// get the neck position relative to the body and use it to set the skeleton translation // get the neck position relative to the body and use it to set the skeleton translation
glm::vec3 neckPosition; glm::vec3 neckPosition;
_myAvatar.getSkeletonModel().setTranslation(glm::vec3()); _myAvatar->getSkeletonModel().setTranslation(glm::vec3());
_myAvatar.getSkeletonModel().getNeckPosition(neckPosition); _myAvatar->getSkeletonModel().getNeckPosition(neckPosition);
_myAvatar.getSkeletonModel().setTranslation(_myAvatar.getHead().getFaceModel().getTranslation() - _myAvatar->getSkeletonModel().setTranslation(_myAvatar->getHead().getFaceModel().getTranslation() -
neckPosition); neckPosition);
displaySide(_mirrorCamera, true); displaySide(_mirrorCamera, true);
// restore absolute translations // restore absolute translations
_myAvatar.getSkeletonModel().setTranslation(absoluteSkeletonTranslation); _myAvatar->getSkeletonModel().setTranslation(absoluteSkeletonTranslation);
_myAvatar.getHead().getFaceModel().setTranslation(absoluteFaceTranslation); _myAvatar->getHead().getFaceModel().setTranslation(absoluteFaceTranslation);
} else { } else {
displaySide(_mirrorCamera, true); displaySide(_mirrorCamera, true);
} }
@ -689,12 +695,12 @@ void Application::keyPressEvent(QKeyEvent* event) {
if (activeWindow() == _window) { if (activeWindow() == _window) {
if (_chatEntryOn) { if (_chatEntryOn) {
if (_chatEntry.keyPressEvent(event)) { if (_chatEntry.keyPressEvent(event)) {
_myAvatar.setKeyState(event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete ? _myAvatar->setKeyState(event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete ?
DELETE_KEY_DOWN : INSERT_KEY_DOWN); DELETE_KEY_DOWN : INSERT_KEY_DOWN);
_myAvatar.setChatMessage(string(_chatEntry.getContents().size(), SOLID_BLOCK_CHAR)); _myAvatar->setChatMessage(string(_chatEntry.getContents().size(), SOLID_BLOCK_CHAR));
} else { } else {
_myAvatar.setChatMessage(_chatEntry.getContents()); _myAvatar->setChatMessage(_chatEntry.getContents());
_chatEntry.clear(); _chatEntry.clear();
_chatEntryOn = false; _chatEntryOn = false;
setMenuShortcutsEnabled(true); setMenuShortcutsEnabled(true);
@ -738,10 +744,10 @@ void Application::keyPressEvent(QKeyEvent* event) {
if (_nudgeStarted) { if (_nudgeStarted) {
_nudgeGuidePosition.y += _mouseVoxel.s; _nudgeGuidePosition.y += _mouseVoxel.s;
} else { } else {
if (!_myAvatar.getDriveKeys(UP)) { if (!_myAvatar->getDriveKeys(UP)) {
_myAvatar.jump(); _myAvatar->jump();
} }
_myAvatar.setDriveKeys(UP, 1); _myAvatar->setDriveKeys(UP, 1);
} }
break; break;
@ -753,7 +759,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
if (_nudgeStarted) { if (_nudgeStarted) {
_nudgeGuidePosition.y -= _mouseVoxel.s; _nudgeGuidePosition.y -= _mouseVoxel.s;
} else { } else {
_myAvatar.setDriveKeys(DOWN, 1); _myAvatar->setDriveKeys(DOWN, 1);
} }
break; break;
@ -773,7 +779,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
} }
} }
} else { } else {
_myAvatar.setDriveKeys(FWD, 1); _myAvatar->setDriveKeys(FWD, 1);
} }
break; break;
@ -799,7 +805,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
} }
} }
} else { } else {
_myAvatar.setDriveKeys(BACK, 1); _myAvatar->setDriveKeys(BACK, 1);
} }
break; break;
@ -833,7 +839,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
} }
} }
} else { } else {
_myAvatar.setDriveKeys(ROT_LEFT, 1); _myAvatar->setDriveKeys(ROT_LEFT, 1);
} }
break; break;
@ -853,7 +859,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
} }
} }
} else { } else {
_myAvatar.setDriveKeys(ROT_RIGHT, 1); _myAvatar->setDriveKeys(ROT_RIGHT, 1);
} }
break; break;
@ -863,8 +869,8 @@ void Application::keyPressEvent(QKeyEvent* event) {
nudgeVoxels(); nudgeVoxels();
} else { } else {
_chatEntryOn = true; _chatEntryOn = true;
_myAvatar.setKeyState(NO_KEY_DOWN); _myAvatar->setKeyState(NO_KEY_DOWN);
_myAvatar.setChatMessage(string()); _myAvatar->setChatMessage(string());
setMenuShortcutsEnabled(false); setMenuShortcutsEnabled(false);
} }
break; break;
@ -887,7 +893,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
} else if (_nudgeStarted && isShifted) { } else if (_nudgeStarted && isShifted) {
_nudgeGuidePosition.y += _mouseVoxel.s; _nudgeGuidePosition.y += _mouseVoxel.s;
} else { } else {
_myAvatar.setDriveKeys(isShifted ? UP : FWD, 1); _myAvatar->setDriveKeys(isShifted ? UP : FWD, 1);
} }
break; break;
@ -909,7 +915,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
} else if (_nudgeStarted && isShifted) { } else if (_nudgeStarted && isShifted) {
_nudgeGuidePosition.y -= _mouseVoxel.s; _nudgeGuidePosition.y -= _mouseVoxel.s;
} else { } else {
_myAvatar.setDriveKeys(isShifted ? DOWN : BACK, 1); _myAvatar->setDriveKeys(isShifted ? DOWN : BACK, 1);
} }
break; break;
@ -929,7 +935,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
} }
} }
} else { } else {
_myAvatar.setDriveKeys(isShifted ? LEFT : ROT_LEFT, 1); _myAvatar->setDriveKeys(isShifted ? LEFT : ROT_LEFT, 1);
} }
break; break;
@ -949,7 +955,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
} }
} }
} else { } else {
_myAvatar.setDriveKeys(isShifted ? RIGHT : ROT_RIGHT, 1); _myAvatar->setDriveKeys(isShifted ? RIGHT : ROT_RIGHT, 1);
} }
break; break;
@ -1067,13 +1073,13 @@ void Application::keyPressEvent(QKeyEvent* event) {
} }
break; break;
case Qt::Key_Plus: case Qt::Key_Plus:
_myAvatar.increaseSize(); _myAvatar->increaseSize();
break; break;
case Qt::Key_Minus: case Qt::Key_Minus:
_myAvatar.decreaseSize(); _myAvatar->decreaseSize();
break; break;
case Qt::Key_Equal: case Qt::Key_Equal:
_myAvatar.resetSize(); _myAvatar->resetSize();
break; break;
case Qt::Key_1: case Qt::Key_1:
@ -1108,7 +1114,7 @@ void Application::keyReleaseEvent(QKeyEvent* event) {
if (activeWindow() == _window) { if (activeWindow() == _window) {
if (_chatEntryOn) { if (_chatEntryOn) {
_myAvatar.setKeyState(NO_KEY_DOWN); _myAvatar->setKeyState(NO_KEY_DOWN);
return; return;
} }
@ -1117,47 +1123,47 @@ void Application::keyReleaseEvent(QKeyEvent* event) {
_pasteMode = false; _pasteMode = false;
break; break;
case Qt::Key_E: case Qt::Key_E:
_myAvatar.setDriveKeys(UP, 0); _myAvatar->setDriveKeys(UP, 0);
break; break;
case Qt::Key_C: case Qt::Key_C:
_myAvatar.setDriveKeys(DOWN, 0); _myAvatar->setDriveKeys(DOWN, 0);
break; break;
case Qt::Key_W: case Qt::Key_W:
_myAvatar.setDriveKeys(FWD, 0); _myAvatar->setDriveKeys(FWD, 0);
break; break;
case Qt::Key_S: case Qt::Key_S:
_myAvatar.setDriveKeys(BACK, 0); _myAvatar->setDriveKeys(BACK, 0);
break; break;
case Qt::Key_A: case Qt::Key_A:
_myAvatar.setDriveKeys(ROT_LEFT, 0); _myAvatar->setDriveKeys(ROT_LEFT, 0);
break; break;
case Qt::Key_D: case Qt::Key_D:
_myAvatar.setDriveKeys(ROT_RIGHT, 0); _myAvatar->setDriveKeys(ROT_RIGHT, 0);
break; break;
case Qt::Key_Up: case Qt::Key_Up:
_myAvatar.setDriveKeys(FWD, 0); _myAvatar->setDriveKeys(FWD, 0);
_myAvatar.setDriveKeys(UP, 0); _myAvatar->setDriveKeys(UP, 0);
break; break;
case Qt::Key_Down: case Qt::Key_Down:
_myAvatar.setDriveKeys(BACK, 0); _myAvatar->setDriveKeys(BACK, 0);
_myAvatar.setDriveKeys(DOWN, 0); _myAvatar->setDriveKeys(DOWN, 0);
break; break;
case Qt::Key_Left: case Qt::Key_Left:
_myAvatar.setDriveKeys(LEFT, 0); _myAvatar->setDriveKeys(LEFT, 0);
_myAvatar.setDriveKeys(ROT_LEFT, 0); _myAvatar->setDriveKeys(ROT_LEFT, 0);
break; break;
case Qt::Key_Right: case Qt::Key_Right:
_myAvatar.setDriveKeys(RIGHT, 0); _myAvatar->setDriveKeys(RIGHT, 0);
_myAvatar.setDriveKeys(ROT_RIGHT, 0); _myAvatar->setDriveKeys(ROT_RIGHT, 0);
break; break;
default: default:
@ -1192,11 +1198,11 @@ void Application::mouseMoveEvent(QMouseEvent* event) {
// orbit behavior // orbit behavior
if (_mousePressed && !Menu::getInstance()->isVoxelModeActionChecked()) { if (_mousePressed && !Menu::getInstance()->isVoxelModeActionChecked()) {
if (_avatarManager.getLookAtTargetAvatar()) { if (_avatarManager.getLookAtTargetAvatar()) {
_myAvatar.orbit(_avatarManager.getLookAtTargetAvatar()->getPosition(), deltaX, deltaY); _myAvatar->orbit(_avatarManager.getLookAtTargetAvatar()->getPosition(), deltaX, deltaY);
return; return;
} }
if (_isHoverVoxel) { if (_isHoverVoxel) {
_myAvatar.orbit(getMouseVoxelWorldCoordinates(_hoverVoxel), deltaX, deltaY); _myAvatar->orbit(getMouseVoxelWorldCoordinates(_hoverVoxel), deltaX, deltaY);
return; return;
} }
} }
@ -1279,14 +1285,14 @@ void Application::mousePressEvent(QMouseEvent* event) {
const float PERCENTAGE_TO_MOVE_TOWARD = 0.90f; const float PERCENTAGE_TO_MOVE_TOWARD = 0.90f;
glm::vec3 newTarget = getMouseVoxelWorldCoordinates(_hoverVoxel); glm::vec3 newTarget = getMouseVoxelWorldCoordinates(_hoverVoxel);
glm::vec3 myPosition = _myAvatar.getPosition(); glm::vec3 myPosition = _myAvatar->getPosition();
// If there is not an action tool set (add, delete, color), move to this voxel // If there is not an action tool set (add, delete, color), move to this voxel
if (Menu::getInstance()->isOptionChecked(MenuOption::ClickToFly) && if (Menu::getInstance()->isOptionChecked(MenuOption::ClickToFly) &&
!(Menu::getInstance()->isOptionChecked(MenuOption::VoxelAddMode) || !(Menu::getInstance()->isOptionChecked(MenuOption::VoxelAddMode) ||
Menu::getInstance()->isOptionChecked(MenuOption::VoxelDeleteMode) || Menu::getInstance()->isOptionChecked(MenuOption::VoxelDeleteMode) ||
Menu::getInstance()->isOptionChecked(MenuOption::VoxelColorMode))) { Menu::getInstance()->isOptionChecked(MenuOption::VoxelColorMode))) {
_myAvatar.setMoveTarget(myPosition + (newTarget - myPosition) * PERCENTAGE_TO_MOVE_TOWARD); _myAvatar->setMoveTarget(myPosition + (newTarget - myPosition) * PERCENTAGE_TO_MOVE_TOWARD);
} }
} }
@ -1438,8 +1444,8 @@ void Application::timer() {
DataServerClient::resendUnmatchedPackets(); DataServerClient::resendUnmatchedPackets();
// give the MyAvatar object position, orientation to the Profile so it can propagate to the data-server // give the MyAvatar object position, orientation to the Profile so it can propagate to the data-server
_profile.updatePosition(_myAvatar.getPosition()); _profile.updatePosition(_myAvatar->getPosition());
_profile.updateOrientation(_myAvatar.getOrientation()); _profile.updateOrientation(_myAvatar->getOrientation());
} }
static glm::vec3 getFaceVector(BoxFace face) { static glm::vec3 getFaceVector(BoxFace face) {
@ -1734,7 +1740,7 @@ void Application::pasteVoxels() {
} }
void Application::findAxisAlignment() { void Application::findAxisAlignment() {
glm::vec3 direction = _myAvatar.getMouseRayDirection(); glm::vec3 direction = _myAvatar->getMouseRayDirection();
if (fabs(direction.z) > fabs(direction.x)) { if (fabs(direction.z) > fabs(direction.x)) {
_lookingAlongX = false; _lookingAlongX = false;
if (direction.z < 0) { if (direction.z < 0) {
@ -1820,12 +1826,10 @@ void Application::init() {
_headMouseY = _mouseY = _glWidget->height() / 2; _headMouseY = _mouseY = _glWidget->height() / 2;
QCursor::setPos(_headMouseX, _headMouseY); QCursor::setPos(_headMouseX, _headMouseY);
_myAvatar.init(); // TODO: move _myAvatar out of Application. Move relevant code to MyAvataar or AvatarManager
_myAvatar.setPosition(START_LOCATION); _avatarManager.init();
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON); _myCamera.setMode(CAMERA_MODE_FIRST_PERSON);
_myCamera.setModeShiftRate(1.0f); _myCamera.setModeShiftRate(1.0f);
_myAvatar.setDisplayingLookatVectors(false);
_avatarManager.setMyAvatar(&_myAvatar);
_mirrorCamera.setMode(CAMERA_MODE_MIRROR); _mirrorCamera.setMode(CAMERA_MODE_MIRROR);
_mirrorCamera.setAspectRatio((float)MIRROR_VIEW_WIDTH / (float)MIRROR_VIEW_HEIGHT); _mirrorCamera.setAspectRatio((float)MIRROR_VIEW_WIDTH / (float)MIRROR_VIEW_HEIGHT);
@ -1930,9 +1934,9 @@ const float HEAD_SPHERE_RADIUS = 0.07f;
bool Application::isLookingAtMyAvatar(Avatar* avatar) { bool Application::isLookingAtMyAvatar(Avatar* avatar) {
glm::vec3 theirLookat = avatar->getHead().getLookAtPosition(); glm::vec3 theirLookat = avatar->getHead().getLookAtPosition();
glm::vec3 myHeadPosition = _myAvatar.getHead().getPosition(); glm::vec3 myHeadPosition = _myAvatar->getHead().getPosition();
if (pointInSphere(theirLookat, myHeadPosition, HEAD_SPHERE_RADIUS * _myAvatar.getScale())) { if (pointInSphere(theirLookat, myHeadPosition, HEAD_SPHERE_RADIUS * _myAvatar->getScale())) {
return true; return true;
} }
return false; return false;
@ -1970,10 +1974,10 @@ void Application::updateMouseRay() {
} }
// tell my avatar if the mouse is being pressed... // tell my avatar if the mouse is being pressed...
_myAvatar.setMousePressed(_mousePressed); _myAvatar->setMousePressed(_mousePressed);
// tell my avatar the posiion and direction of the ray projected ino the world based on the mouse position // tell my avatar the posiion and direction of the ray projected ino the world based on the mouse position
_myAvatar.setMouseRay(_mouseRayOrigin, _mouseRayDirection); _myAvatar->setMouseRay(_mouseRayOrigin, _mouseRayDirection);
} }
void Application::updateFaceshift() { void Application::updateFaceshift() {
@ -1986,7 +1990,7 @@ void Application::updateFaceshift() {
// Copy angular velocity if measured by faceshift, to the head // Copy angular velocity if measured by faceshift, to the head
if (_faceshift.isActive()) { if (_faceshift.isActive()) {
_myAvatar.getHead().setAngularVelocity(_faceshift.getHeadAngularVelocity()); _myAvatar->getHead().setAngularVelocity(_faceshift.getHeadAngularVelocity());
} }
} }
@ -2010,14 +2014,14 @@ void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot) {
} }
if (_faceshift.isActive()) { if (_faceshift.isActive()) {
// deflect using Faceshift gaze data // deflect using Faceshift gaze data
glm::vec3 origin = _myAvatar.getHead().calculateAverageEyePosition(); glm::vec3 origin = _myAvatar->getHead().calculateAverageEyePosition();
float pitchSign = (_myCamera.getMode() == CAMERA_MODE_MIRROR) ? -1.0f : 1.0f; float pitchSign = (_myCamera.getMode() == CAMERA_MODE_MIRROR) ? -1.0f : 1.0f;
float deflection = Menu::getInstance()->getFaceshiftEyeDeflection(); float deflection = Menu::getInstance()->getFaceshiftEyeDeflection();
lookAtSpot = origin + _myCamera.getRotation() * glm::quat(glm::radians(glm::vec3( lookAtSpot = origin + _myCamera.getRotation() * glm::quat(glm::radians(glm::vec3(
_faceshift.getEstimatedEyePitch() * pitchSign * deflection, _faceshift.getEstimatedEyeYaw() * deflection, 0.0f))) * _faceshift.getEstimatedEyePitch() * pitchSign * deflection, _faceshift.getEstimatedEyeYaw() * deflection, 0.0f))) *
glm::inverse(_myCamera.getRotation()) * (lookAtSpot - origin); glm::inverse(_myCamera.getRotation()) * (lookAtSpot - origin);
} }
_myAvatar.getHead().setLookAtPosition(lookAtSpot); _myAvatar->getHead().setLookAtPosition(lookAtSpot);
} }
void Application::updateHoverVoxels(float deltaTime, float& distance, BoxFace& face) { void Application::updateHoverVoxels(float deltaTime, float& distance, BoxFace& face) {
@ -2075,9 +2079,9 @@ void Application::updateMouseVoxels(float deltaTime, float& distance, BoxFace& f
_mouseVoxel.s = 0.0f; _mouseVoxel.s = 0.0f;
bool wasInitialized = _mouseVoxelScaleInitialized; bool wasInitialized = _mouseVoxelScaleInitialized;
if (Menu::getInstance()->isVoxelModeActionChecked() && if (Menu::getInstance()->isVoxelModeActionChecked() &&
(fabs(_myAvatar.getVelocity().x) + (fabs(_myAvatar->getVelocity().x) +
fabs(_myAvatar.getVelocity().y) + fabs(_myAvatar->getVelocity().y) +
fabs(_myAvatar.getVelocity().z)) / 3 < MAX_AVATAR_EDIT_VELOCITY) { fabs(_myAvatar->getVelocity().z)) / 3 < MAX_AVATAR_EDIT_VELOCITY) {
if (_voxels.findRayIntersection(_mouseRayOrigin, _mouseRayDirection, _mouseVoxel, distance, face)) { if (_voxels.findRayIntersection(_mouseRayOrigin, _mouseRayDirection, _mouseVoxel, distance, face)) {
if (distance < MAX_VOXEL_EDIT_DISTANCE) { if (distance < MAX_VOXEL_EDIT_DISTANCE) {
@ -2209,16 +2213,16 @@ void Application::updateMyAvatarSimulation(float deltaTime) {
PerformanceWarning warn(showWarnings, "Application::updateMyAvatarSimulation()"); PerformanceWarning warn(showWarnings, "Application::updateMyAvatarSimulation()");
if (Menu::getInstance()->isOptionChecked(MenuOption::Gravity)) { if (Menu::getInstance()->isOptionChecked(MenuOption::Gravity)) {
_myAvatar.setGravity(_environment.getGravity(_myAvatar.getPosition())); _myAvatar->setGravity(_environment.getGravity(_myAvatar->getPosition()));
} }
else { else {
_myAvatar.setGravity(glm::vec3(0.0f, 0.0f, 0.0f)); _myAvatar->setGravity(glm::vec3(0.0f, 0.0f, 0.0f));
} }
if (Menu::getInstance()->isOptionChecked(MenuOption::TransmitterDrive) && _myTransmitter.isConnected()) { if (Menu::getInstance()->isOptionChecked(MenuOption::TransmitterDrive) && _myTransmitter.isConnected()) {
_myAvatar.simulate(deltaTime, &_myTransmitter); _myAvatar->simulate(deltaTime, &_myTransmitter);
} else { } else {
_myAvatar.simulate(deltaTime, NULL); _myAvatar->simulate(deltaTime, NULL);
} }
} }
@ -2246,8 +2250,8 @@ void Application::updateTransmitter(float deltaTime) {
// no transmitter drive implies transmitter pick // no transmitter drive implies transmitter pick
if (!Menu::getInstance()->isOptionChecked(MenuOption::TransmitterDrive) && _myTransmitter.isConnected()) { if (!Menu::getInstance()->isOptionChecked(MenuOption::TransmitterDrive) && _myTransmitter.isConnected()) {
_transmitterPickStart = _myAvatar.getChestPosition(); _transmitterPickStart = _myAvatar->getChestPosition();
glm::vec3 direction = _myAvatar.getOrientation() * glm::vec3 direction = _myAvatar->getOrientation() *
glm::quat(glm::radians(_myTransmitter.getEstimatedRotation())) * IDENTITY_FRONT; glm::quat(glm::radians(_myTransmitter.getEstimatedRotation())) * IDENTITY_FRONT;
// check against voxels, avatars // check against voxels, avatars
@ -2321,8 +2325,8 @@ void Application::updateAudio(float deltaTime) {
PerformanceWarning warn(showWarnings, "Application::updateAudio()"); PerformanceWarning warn(showWarnings, "Application::updateAudio()");
// Update audio stats for procedural sounds // Update audio stats for procedural sounds
_audio.setLastAcceleration(_myAvatar.getThrust()); _audio.setLastAcceleration(_myAvatar->getThrust());
_audio.setLastVelocity(_myAvatar.getVelocity()); _audio.setLastVelocity(_myAvatar->getVelocity());
} }
void Application::updateCursor(float deltaTime) { void Application::updateCursor(float deltaTime) {
@ -2394,16 +2398,16 @@ void Application::updateAvatar(float deltaTime) {
PerformanceWarning warn(showWarnings, "Application::updateAvatar()"); PerformanceWarning warn(showWarnings, "Application::updateAvatar()");
// rotate body yaw for yaw received from multitouch // rotate body yaw for yaw received from multitouch
_myAvatar.setOrientation(_myAvatar.getOrientation() _myAvatar->setOrientation(_myAvatar->getOrientation()
* glm::quat(glm::vec3(0, _yawFromTouch, 0))); * glm::quat(glm::vec3(0, _yawFromTouch, 0)));
_yawFromTouch = 0.f; _yawFromTouch = 0.f;
// apply pitch from touch // apply pitch from touch
_myAvatar.getHead().setPitch(_myAvatar.getHead().getPitch() + _pitchFromTouch); _myAvatar->getHead().setPitch(_myAvatar->getHead().getPitch() + _pitchFromTouch);
_pitchFromTouch = 0.0f; _pitchFromTouch = 0.0f;
// Update my avatar's state from gyros // Update my avatar's state from gyros
_myAvatar.updateFromGyros(Menu::getInstance()->isOptionChecked(MenuOption::TurnWithHead)); _myAvatar->updateFromGyros(Menu::getInstance()->isOptionChecked(MenuOption::TurnWithHead));
// Update head mouse from faceshift if active // Update head mouse from faceshift if active
if (_faceshift.isActive()) { if (_faceshift.isActive()) {
@ -2424,17 +2428,17 @@ void Application::updateAvatar(float deltaTime) {
float yaw, pitch, roll; float yaw, pitch, roll;
OculusManager::getEulerAngles(yaw, pitch, roll); OculusManager::getEulerAngles(yaw, pitch, roll);
_myAvatar.getHead().setYaw(yaw); _myAvatar->getHead().setYaw(yaw);
_myAvatar.getHead().setPitch(pitch); _myAvatar->getHead().setPitch(pitch);
_myAvatar.getHead().setRoll(roll); _myAvatar->getHead().setRoll(roll);
} }
// Get audio loudness data from audio input device // Get audio loudness data from audio input device
_myAvatar.getHead().setAudioLoudness(_audio.getLastInputLoudness()); _myAvatar->getHead().setAudioLoudness(_audio.getLastInputLoudness());
// send head/hand data to the avatar mixer and voxel server // send head/hand data to the avatar mixer and voxel server
QByteArray avatarData = byteArrayWithPopluatedHeader(PacketTypeAvatarData); QByteArray avatarData = byteArrayWithPopluatedHeader(PacketTypeAvatarData);
avatarData.append(_myAvatar.toByteArray()); avatarData.append(_myAvatar->toByteArray());
controlledBroadcastToNodes(avatarData, NodeSet() << NodeType::AvatarMixer); controlledBroadcastToNodes(avatarData, NodeSet() << NodeType::AvatarMixer);
@ -3319,7 +3323,7 @@ void Application::displayStats() {
horizontalOffset += 171; horizontalOffset += 171;
} }
glm::vec3 avatarPos = _myAvatar.getPosition(); glm::vec3 avatarPos = _myAvatar->getPosition();
lines = _statsExpanded ? 4 : 3; lines = _statsExpanded ? 4 : 3;
displayStatsBackground(backgroundColor, horizontalOffset, 0, _glWidget->width() - (mirrorEnabled ? 301 : 411) - horizontalOffset, lines * STATS_PELS_PER_LINE + 10); displayStatsBackground(backgroundColor, horizontalOffset, 0, _glWidget->width() - (mirrorEnabled ? 301 : 411) - horizontalOffset, lines * STATS_PELS_PER_LINE + 10);
@ -3334,9 +3338,9 @@ void Application::displayStats() {
sprintf(avatarPosition, "Position: %.3f, %.3f, %.3f", avatarPos.x, avatarPos.y, avatarPos.z); sprintf(avatarPosition, "Position: %.3f, %.3f, %.3f", avatarPos.x, avatarPos.y, avatarPos.z);
} }
char avatarVelocity[30]; char avatarVelocity[30];
sprintf(avatarVelocity, "Velocity: %.1f", glm::length(_myAvatar.getVelocity())); sprintf(avatarVelocity, "Velocity: %.1f", glm::length(_myAvatar->getVelocity()));
char avatarBodyYaw[30]; char avatarBodyYaw[30];
sprintf(avatarBodyYaw, "Yaw: %.2f", _myAvatar.getBodyYaw()); sprintf(avatarBodyYaw, "Yaw: %.2f", _myAvatar->getBodyYaw());
char avatarMixerStats[200]; char avatarMixerStats[200];
verticalOffset += STATS_PELS_PER_LINE; verticalOffset += STATS_PELS_PER_LINE;
@ -3957,10 +3961,10 @@ void Application::resetSensors() {
} }
QCursor::setPos(_headMouseX, _headMouseY); QCursor::setPos(_headMouseX, _headMouseY);
_myAvatar.reset(); _myAvatar->reset();
_myTransmitter.resetLevels(); _myTransmitter.resetLevels();
_myAvatar.setVelocity(glm::vec3(0,0,0)); _myAvatar->setVelocity(glm::vec3(0,0,0));
_myAvatar.setThrust(glm::vec3(0,0,0)); _myAvatar->setThrust(glm::vec3(0,0,0));
QMetaObject::invokeMethod(&_audio, "reset", Qt::QueuedConnection); QMetaObject::invokeMethod(&_audio, "reset", Qt::QueuedConnection);
} }
@ -4089,7 +4093,7 @@ void Application::nodeKilled(SharedNodePointer node) {
} else if (node->getType() == NodeType::AvatarMixer) { } else if (node->getType() == NodeType::AvatarMixer) {
// our avatar mixer has gone away - clear the hash of avatars // our avatar mixer has gone away - clear the hash of avatars
_avatarManager.clearHash(); _avatarManager.clearMixedAvatars();
} }
} }
@ -4204,7 +4208,7 @@ void Application::removeScriptName(const QString& fileNameString)
_activeScripts.removeOne(fileNameString); _activeScripts.removeOne(fileNameString);
} }
void Application::loadScript(const QString& fileNameString){ void Application::loadScript(const QString& fileNameString) {
_activeScripts.append(fileNameString); _activeScripts.append(fileNameString);
QByteArray fileNameAscii = fileNameString.toLocal8Bit(); QByteArray fileNameAscii = fileNameString.toLocal8Bit();
const char* fileName = fileNameAscii.data(); const char* fileName = fileNameAscii.data();
@ -4242,7 +4246,7 @@ void Application::loadScript(const QString& fileNameString){
scriptEngine->getParticlesScriptingInterface()->setParticleTree(_particles.getTree()); scriptEngine->getParticlesScriptingInterface()->setParticleTree(_particles.getTree());
// hook our avatar object into this script engine // hook our avatar object into this script engine
scriptEngine->setAvatarData(&_myAvatar, "MyAvatar"); scriptEngine->setAvatarData( static_cast<Avatar*>(_myAvatar), "MyAvatar");
QThread* workerThread = new QThread(this); QThread* workerThread = new QThread(this);
@ -4409,6 +4413,6 @@ void Application::takeSnapshot() {
player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath())); player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath()));
player->play(); player->play();
Snapshot::saveSnapshot(_glWidget, _profile.getUsername(), _myAvatar.getPosition()); Snapshot::saveSnapshot(_glWidget, _profile.getUsername(), _myAvatar->getPosition());
} }

View file

@ -142,7 +142,7 @@ public:
glm::vec3 getMouseVoxelWorldCoordinates(const VoxelDetail& mouseVoxel); glm::vec3 getMouseVoxelWorldCoordinates(const VoxelDetail& mouseVoxel);
QGLWidget* getGLWidget() { return _glWidget; } QGLWidget* getGLWidget() { return _glWidget; }
MyAvatar* getAvatar() { return &_myAvatar; } MyAvatar* getAvatar() { return _myAvatar; }
Audio* getAudio() { return &_audio; } Audio* getAudio() { return &_audio; }
Camera* getCamera() { return &_myCamera; } Camera* getCamera() { return &_myCamera; }
ViewFrustum* getViewFrustum() { return &_viewFrustum; } ViewFrustum* getViewFrustum() { return &_viewFrustum; }
@ -374,10 +374,10 @@ private:
VoxelQuery _voxelQuery; // NodeData derived class for querying voxels from voxel server VoxelQuery _voxelQuery; // NodeData derived class for querying voxels from voxel server
AvatarManager _avatarManager; AvatarManager _avatarManager;
MyAvatar _myAvatar; // The rendered avatar of oneself MyAvatar* _myAvatar; // TODO: move this and relevant code to AvatarManager (or MyAvatar as the case may be)
Profile _profile; // The data-server linked profile for this user Profile _profile; // The data-server linked profile for this user
Transmitter _myTransmitter; // Gets UDP data from transmitter app used to animate the avatar Transmitter _myTransmitter; // Gets UDP data from transmitter app used to animate the avatar
Faceshift _faceshift; Faceshift _faceshift;

View file

@ -23,21 +23,24 @@ AvatarManager::AvatarManager(QObject* parent) :
_lookAtTargetAvatar(), _lookAtTargetAvatar(),
_lookAtOtherPosition(), _lookAtOtherPosition(),
_lookAtIndicatorScale(1.0f), _lookAtIndicatorScale(1.0f),
_avatarFades(), _avatarFades() {
_myAvatar(NULL)
{
// register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar // register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar
qRegisterMetaType<QWeakPointer<Node> >("NodeWeakPointer"); qRegisterMetaType<QWeakPointer<Node> >("NodeWeakPointer");
_myAvatar = QSharedPointer<MyAvatar>(new MyAvatar());
} }
void AvatarManager::setMyAvatar(MyAvatar* myAvatar) { void AvatarManager::clear() {
if (!_myAvatar) { _lookAtTargetAvatar.clear();
// can only ever set this once _avatarFades.clear();
_myAvatar = myAvatar; _avatarHash.clear();
// add _myAvatar to the list _myAvatar.clear();
AvatarSharedPointer myPointer = AvatarSharedPointer(_myAvatar); }
_avatarHash.insert(MY_AVATAR_KEY, myPointer);
} void AvatarManager::init() {
_myAvatar->init();
_myAvatar->setPosition(START_LOCATION);
_myAvatar->setDisplayingLookatVectors(false);
_avatarHash.insert(MY_AVATAR_KEY, _myAvatar);
} }
void AvatarManager::updateLookAtTargetAvatar(glm::vec3 &eyePosition) { void AvatarManager::updateLookAtTargetAvatar(glm::vec3 &eyePosition) {
@ -52,7 +55,7 @@ void AvatarManager::updateLookAtTargetAvatar(glm::vec3 &eyePosition) {
foreach (const AvatarSharedPointer& avatarPointer, _avatarHash) { foreach (const AvatarSharedPointer& avatarPointer, _avatarHash) {
Avatar* avatar = static_cast<Avatar*>(avatarPointer.data()); Avatar* avatar = static_cast<Avatar*>(avatarPointer.data());
if (avatar != static_cast<Avatar*>(_myAvatar)) { if (avatar != static_cast<Avatar*>(_myAvatar.data())) {
float distance; float distance;
if (avatar->findRayIntersection(mouseOrigin, mouseDirection, distance)) { if (avatar->findRayIntersection(mouseOrigin, mouseDirection, distance)) {
// rescale to compensate for head embiggening // rescale to compensate for head embiggening
@ -86,7 +89,7 @@ void AvatarManager::updateAvatars(float deltaTime) {
AvatarHash::iterator avatarIterator = _avatarHash.begin(); AvatarHash::iterator avatarIterator = _avatarHash.begin();
while (avatarIterator != _avatarHash.end()) { while (avatarIterator != _avatarHash.end()) {
Avatar* avatar = static_cast<Avatar*>(avatarIterator.value().data()); Avatar* avatar = static_cast<Avatar*>(avatarIterator.value().data());
if (avatar == static_cast<Avatar*>(_myAvatar)) { if (avatar == static_cast<Avatar*>(_myAvatar.data())) {
// for now skip updates to _myAvatar because it is done explicitly in Application // for now skip updates to _myAvatar because it is done explicitly in Application
// TODO: update _myAvatar in this context // TODO: update _myAvatar in this context
++avatarIterator; ++avatarIterator;
@ -122,7 +125,7 @@ void AvatarManager::renderAvatars(bool forceRenderHead, bool selfAvatarOnly) {
if (!avatar->isInitialized()) { if (!avatar->isInitialized()) {
avatar->init(); avatar->init();
} }
if (avatar == static_cast<Avatar*>(_myAvatar)) { if (avatar == static_cast<Avatar*>(_myAvatar.data())) {
avatar->render(forceRenderHead); avatar->render(forceRenderHead);
} else { } else {
avatar->render(false); avatar->render(false);
@ -130,8 +133,8 @@ void AvatarManager::renderAvatars(bool forceRenderHead, bool selfAvatarOnly) {
avatar->setDisplayingLookatVectors(renderLookAtVectors); avatar->setDisplayingLookatVectors(renderLookAtVectors);
} }
renderAvatarFades(); renderAvatarFades();
} else if (_myAvatar) { } else {
// Render my own Avatar // just render myAvatar
_myAvatar->render(forceRenderHead); _myAvatar->render(forceRenderHead);
_myAvatar->setDisplayingLookatVectors(renderLookAtVectors); _myAvatar->setDisplayingLookatVectors(renderLookAtVectors);
} }
@ -261,10 +264,11 @@ AvatarHash::iterator AvatarManager::erase(const AvatarHash::iterator& iterator)
} }
} }
void AvatarManager::clearHash() { void AvatarManager::clearMixedAvatars() {
// clear the AvatarManager hash - typically happens on the removal of the avatar-mixer // clear any avatars that came from an avatar-mixer
AvatarHash::iterator removeAvatar = _avatarHash.begin(); AvatarHash::iterator removeAvatar = _avatarHash.begin();
while (removeAvatar != _avatarHash.end()) { while (removeAvatar != _avatarHash.end()) {
removeAvatar = erase(removeAvatar); removeAvatar = erase(removeAvatar);
} }
_lookAtTargetAvatar.clear();
} }

View file

@ -25,7 +25,11 @@ class AvatarManager : public QObject, public DataServerCallbackObject, public Av
public: public:
AvatarManager(QObject* parent = 0); AvatarManager(QObject* parent = 0);
void setMyAvatar(MyAvatar* myAvatar); void clear();
void init();
MyAvatar* getMyAvatar() { return _myAvatar.data(); }
AvatarData* getLookAtTargetAvatar() const { return _lookAtTargetAvatar.data(); } AvatarData* getLookAtTargetAvatar() const { return _lookAtTargetAvatar.data(); }
@ -34,8 +38,7 @@ public:
void updateAvatars(float deltaTime); void updateAvatars(float deltaTime);
void renderAvatars(bool forceRenderHead, bool selfAvatarOnly = false); void renderAvatars(bool forceRenderHead, bool selfAvatarOnly = false);
// virtual override void clearMixedAvatars();
void clearHash();
public slots: public slots:
void processDataServerResponse(const QString& userString, const QStringList& keyList, const QStringList& valueList); void processDataServerResponse(const QString& userString, const QStringList& keyList, const QStringList& valueList);
@ -57,7 +60,7 @@ private:
float _lookAtIndicatorScale; float _lookAtIndicatorScale;
QVector<AvatarSharedPointer> _avatarFades; QVector<AvatarSharedPointer> _avatarFades;
MyAvatar* _myAvatar; QSharedPointer<MyAvatar> _myAvatar;
}; };
#endif /* defined(__hifi__AvatarManager__) */ #endif /* defined(__hifi__AvatarManager__) */

View file

@ -17,13 +17,6 @@ void AvatarHashMap::insert(const QUuid& id, AvatarSharedPointer avatar) {
_avatarHash.insert(id, avatar); _avatarHash.insert(id, avatar);
} }
void AvatarHashMap::clearHash() {
AvatarHash::iterator removeAvatar = _avatarHash.begin();
while (removeAvatar != _avatarHash.end()) {
removeAvatar = erase(removeAvatar);
}
}
AvatarHash::iterator AvatarHashMap::erase(const AvatarHash::iterator& iterator) { AvatarHash::iterator AvatarHashMap::erase(const AvatarHash::iterator& iterator) {
return _avatarHash.erase(iterator); return _avatarHash.erase(iterator);
} }

View file

@ -26,7 +26,6 @@ public:
int size() const { return _avatarHash.size(); } int size() const { return _avatarHash.size(); }
virtual void insert(const QUuid& id, AvatarSharedPointer avatar); virtual void insert(const QUuid& id, AvatarSharedPointer avatar);
virtual void clearHash();
protected: protected:
virtual AvatarHash::iterator erase(const AvatarHash::iterator& iterator); virtual AvatarHash::iterator erase(const AvatarHash::iterator& iterator);