/**************************************************************************** Copyright (C) 2002-2008 Gilles Debunne. All rights reserved. This file is part of the QGLViewer library version 2.3.6. http://www.libqglviewer.com - contact@libqglviewer.com This file may be used under the terms of the GNU General Public License versions 2.0 or 3.0 as published by the Free Software Foundation and appearing in the LICENSE file included in the packaging of this file. In addition, as a special exception, Gilles Debunne gives you certain additional rights, described in the file GPL_EXCEPTION in this package. libQGLViewer uses dual licensing. Commercial/proprietary software must purchase a libQGLViewer Commercial License. This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *****************************************************************************/ #include "simpleViewer.h" #include "../interfaces/visitor.h" #include "../glnodes/scenegroup.h" #include "../glnodes/shapes.h" #include #include #include #include #include #include #include #include #include using namespace std; #define BUFFER_OFFSET(i) ((char *)NULL + (i)) #define GRID_SIZE 10; namespace { const int numVerticesCube = 12*3; const double frame_limit = 5; const double inc_mult = 5; const double inc_offset = 1.05; const int numRowSphere = 20; const int numColSphere = numRowSphere+2; const int numVerticesSphere = numColSphere * numRowSphere + 2; const int numTriSphere = numColSphere*(numRowSphere-1)*2 + 2*numColSphere; int m_lDirectionLocation; int m_normalMatrixLoc; QVector3D sun = QVector3D(0,-1,0); QMatrix4x4 sunRotate; SceneGroup* selection; int currentPoint = 0; // VERY lazy way of tracking light balls } class SkyboxCamera : public qglviewer::Camera { virtual qreal zNear() const { return 0.001; } virtual qreal zFar() const { return 1000.0; } }; Viewer::Viewer() { activeColor = new QColor(255, 255, 255, 255); activeCell = nullptr; activeShape = 0; angle_mult = 0.1; } Viewer::~Viewer() { cleanup(); } void Viewer::cleanup() { makeCurrent(); // Delete shaders delete m_program; m_program = 0; // Delete buffers glDeleteBuffers(NumBuffers, m_Buffers); glDeleteVertexArrays(NumVAOs, m_VAOs); doneCurrent(); } void Viewer::drawSkybox() { // Use the Skybox Shaders m_program->bind(); s_texture->bind(); // Get projection and camera transformations QMatrix4x4 projectionMatrix; QMatrix4x4 modelViewMatrix; camera()->getProjectionMatrix(projectionMatrix); camera()->getModelViewMatrix(modelViewMatrix); // Increase size of skybox modelViewMatrix.scale(100); float colorMult = 0.2 + std::fabs(0.8 * cos(std::fmod(angle_mult * frame + 300, 360) / 360 * M_PI)); m_program->setUniformValue(m_skyMultLoc, colorMult); m_program->setUniformValue(m_mvMatrixLocation, modelViewMatrix); m_program->setUniformValue(m_isSkyLoc, true); m_program->setUniformValue(m_drawTextLoc, true); m_program->setUniformValue(m_isLightLoc, false); m_program->setUniformValue(m_colorLocation, *(new QColor(0, 0, 0, 0))); glCullFace( GL_FRONT ); glBindVertexArray(m_VAOs[VAO_Cube]); glDrawArrays(GL_TRIANGLES, 0, 36); } void Viewer::draw() { // Bind our vertex/fragment shaders m_program->bind(); glClear(GL_COLOR_BUFFER_BIT); if(!isPickingActivated) drawSkybox(); glCullFace( GL_BACK ); // Get projection and camera transformations QMatrix4x4 projectionMatrix; QMatrix4x4 modelViewMatrix; camera()->getProjectionMatrix(projectionMatrix); camera()->getModelViewMatrix(modelViewMatrix); //modelViewMatrix.rotate(30,0,1,0); m_program->setUniformValue(m_projMatrixLocation, projectionMatrix); m_program->setUniformValue(m_mvMatrixLocation, modelViewMatrix); // Adjust sun position float rotAngle = std::fmod(angle_mult * frame, 360); sunRotate.rotate(rotAngle, 1, 0, 0); m_program->setUniformValue(m_lDirLoc, (modelViewMatrix * sunRotate * sun)); selection->transform.setToIdentity(); selection->transform.rotate(rotAngle, 0, 1, 0); // Traverse the Scene in order to draw its components modelStack.push(modelViewMatrix); root.accept(*this); frame += 2; sunRotate.setToIdentity(); //float rotAngle = (frame * angle_mult) % 360; update(); } void Viewer::mouseMoveEvent(QMouseEvent* e) { //cout << "Viewer::mouseMoveEvent(QMouseEvent* e)" << endl; // Normal QGLViewer behavior. QGLViewer::mouseMoveEvent(e); } void Viewer::mousePressEvent(QMouseEvent* e) { // TODO: figure out how to get this weird offset ↓↓ frame position maybe? int x = this->x() + e->x() - 10; int y = this->parentWidget()->height() - e->y() - 23; std::cout << "--------------------------------------------------\nPicking shape at " << x << " (" << this->x() << " + " << e->x() << "), " << y << endl; std::cout << "Window geom: " << this->window()->size().width() << "w, " << this->window()->size().height() << "h" << endl; QMatrix4x4 selectedPosition = pickGeom(x, y); if(!selectedPosition.isIdentity() && e->modifiers().testFlag(Qt::ShiftModifier)) { Cube* c = new Cube; c->setType(TEX_WOODFLOOR); SceneGroup* container = new SceneGroup; //selectedPosition.copyDataTo(container->transform.data()); container->transform = selectedPosition; container->addChild(c); root.addChild(container); } QGLViewer::mousePressEvent(e); } void Viewer::mouseReleaseEvent(QMouseEvent* e) { //cout << "Viewer::mouseReleaseEvent(QMouseEvent* e)" << endl; //m_program->setUniformValue(m_isPickingModeLoc, false); m_program->bind(); m_program->setUniformValue(m_isPickingModeLoc, false); isPickingActivated = false; QGLViewer::mouseReleaseEvent(e); } void Viewer::init() { identityMatrix.setToIdentity(); SkyboxCamera *_cam = new SkyboxCamera(); setCamera(_cam); //camera()->setType(qglviewer::Camera::PERSPECTIVE); //setMouseBinding(Qt::NoModifier, Qt::LeftButton, CAMERA, SCREEN_ROTATE); //setMouseBinding(Qt::AltModifier, Qt::LeftButton, CAMERA, NO_MOUSE_ACTION); //setMouseBinding(Qt::NoModifier, Qt::MouseButton(Qt::LeftButton + Qt::MidButton), CAMERA, NO_MOUSE_ACTION); //setMouseBinding(Qt::ControlModifier, Qt::MouseButton(Qt::LeftButton + Qt::MidButton), CAMERA, NO_MOUSE_ACTION); //setMouseBinding(Qt::ShiftModifier, Qt::MouseButton(Qt::LeftButton + Qt::MidButton), CAMERA, NO_MOUSE_ACTION); // Our scene will be from -5 to 5 in X and Y (the grid will be 10x10). setSceneRadius(5); showEntireScene(); // Init OpenGL objects connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &Viewer::cleanup); initializeOpenGLFunctions(); // Init shaders & geometry initShaders(); initGeometries(); initBuffers(); sunRotate.rotate(-15,0,0,1); sun = sunRotate * sun; { QColor* c1 = new QColor(255, 0, 255, 255); QColor* c2 = new QColor(0, 255, 255, 255); QColor* c3 = new QColor(255, 255, 0, 255); Shape* s1 = new Sphere(); Shape* s2 = new Sphere(); Shape* s3 = new Sphere(); selection = new SceneGroup(); selection->addChild(s1); selection->addChild(s2); selection->addChild(s3); //SceneGroup *sc1 = new SceneGroup(); //sc1->addChild(selection); //root.addChild(sc1); root.addChild(selection); s1->transform.rotate(360 * 1/3,0,1,0); s1->transform.translate(1,0,0); s1->transform.scale(0.05); s1->setColor(*c1); s2->transform.rotate(360 * 2/3,0,1,0); s2->transform.translate(1,0,0); s2->transform.scale(0.05); s2->setColor(*c2); s3->transform.rotate(360 * 3/3,0,1,0); s3->transform.translate(1,0,0); s3->transform.scale(0.05); s3->setColor(*c3); for(int i = 0; i < 10; i++){ for(int j = 0; j < 10; j++){ Shape* cube = new Cube(); SceneGroup *sc = new SceneGroup(); cube->setColor(*c1); cube->setType(1); sc->addChild(cube); sc->transform.translate(-5+i, -5, -5+j); root.addChild(sc); } } /* Shape* cube1 = new Cube(); Shape* cube2 = new Cube(); Shape* cube3 = new Cube(); cube1->setColor(*c1); cube2->setColor(*c2); cube3->setColor(*c3); cube2->transform.translate(3, 0, 0); cube3->transform.translate(-3, 0, 0); //*/ // uncomment this and the quaternion transformation in draw() // to see the cube rotate. This is how we can easily make the sun. // cube->transform.translate(3, 0, 0); } } void Viewer::initShaders() { // Load vertex and fragment shaders m_program = new QOpenGLShaderProgram; colorPickerShaderProgram = m_program; if (!m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, "src/shaders/basicShader.vert")) { cerr << "Unable to load Shader" << endl << "Log file:" << endl; qDebug() << m_program->log(); } if (!m_program->addShaderFromSourceFile(QOpenGLShader::Fragment, "src/shaders/basicShader.frag")) { cerr << "Unable to load Shader" << endl << "Log file:" << endl; qDebug() << m_program->log(); } m_program->link(); m_program->bind(); // Note: This is equivalent to glUseProgram(programId()); // Specify shader input paramters // The strings "vPosition", "mvMatrix", etc. have to match an attribute name in the vertex shader. if ((m_vPositionLocation = m_program->attributeLocation("vPosition")) < 0) qDebug() << "Unable to find shader location for " << "vPosition"; if ((m_colorLocation = m_program->uniformLocation("color")) < 0) qDebug() << "Unable to find shader location for " << "color"; if ((m_mvMatrixLocation = m_program->uniformLocation("mvMatrix")) < 0) qDebug() << "Unable to find shader location for " << "mvMatrix"; if ((m_projMatrixLocation = m_program->uniformLocation("projMatrix")) < 0) qDebug() << "Unable to find shader location for " << "projMatrix"; if ((m_lDirectionLocation = m_program->uniformLocation("lDirection")) < 0) qDebug() << "Unable to find m_shader location for" << "lDirection"; if ((m_normalMatrixLoc = m_program->uniformLocation("normalMatrix")) < 0) qDebug() << "Unable to find m_shader location for" << "normalMatrix"; if ((m_vNormalLocation = m_program->attributeLocation("vNormal")) < 0) qDebug() << "Unable to find m_shader location for" << "vNormal"; if ((s_vUvLocation = m_program->attributeLocation("vUv")) < 0) qDebug() << "Unable to find shader location for " << "vUv"; if ((m_isSkyLoc = m_program->uniformLocation("isSky")) < 0) qDebug() << "Unable to find m_shader location for" << "isSky"; if ((m_isPhongLoc = m_program->uniformLocation("isPhong")) < 0) qDebug() << "Unable to find m_shader location for" << "isPhong"; if ((m_lDirLoc = m_program->uniformLocation("lDirection")) < 0) qDebug() << "Unable to find m_shader location for" << "lDirection"; if ((m_skyMultLoc = m_program->uniformLocation("skyMult")) < 0) qDebug() << "Unable to find m_shader location for" << "skyMult"; if ((m_drawTextLoc = m_program->uniformLocation("drawTextures")) < 0) qDebug() << "Unable to find m_shader location for" << "drawTextures"; if ((m_isLightLoc = m_program->uniformLocation("isLightSource")) < 0) qDebug() << "Unable to find m_shader location for" << "isLightSource"; if ((m_point1Loc = m_program->uniformLocation("pointLight[0]")) < 0) qDebug() << "Unable to find m_shader location for" << "pointLight[0]"; if ((m_point2Loc = m_program->uniformLocation("pointLight[1]")) < 0) qDebug() << "Unable to find m_shader location for" << "pointLight[1]"; if ((m_point3Loc = m_program->uniformLocation("pointLight[2]")) < 0) qDebug() << "Unable to find m_shader location for" << "pointLight[2]"; if ((m_c1Loc = m_program->uniformLocation("pointLightCol[0]")) < 0) qDebug() << "Unable to find m_shader location for" << "pointLightCol[0]"; if ((m_c2Loc = m_program->uniformLocation("pointLightCol[1]")) < 0) qDebug() << "Unable to find m_shader location for" << "pointLightCol[1]"; if ((m_c3Loc = m_program->uniformLocation("pointLightCol[2]")) < 0) qDebug() << "Unable to find m_shader location for" << "pointLightCol[2]"; if ((m_isPickingModeLoc = m_program->uniformLocation("isPickingMode")) < 0) qDebug() << "Unable to find m_shader location for" << "isPickingMode" << m_program->log(); m_program->setUniformValue(m_isPhongLoc, true); m_program->setUniformValue(m_drawTextLoc, false); m_program->setUniformValue(m_isPickingModeLoc, false); s_texture = new QOpenGLTexture(QImage("src/data/skybox.jpg"));/*/ s_texture = new QOpenGLTexture(QImage("src/data/uvLayoutGrid.png"));//*/ s_texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear); s_texture->setMagnificationFilter(QOpenGLTexture::Linear); // load remaining textures for(int i = 0; isetMinificationFilter(QOpenGLTexture::LinearMipMapLinear); TexturePrograms[i]->setMagnificationFilter(QOpenGLTexture::Linear); }//*/ } // Creates the basic shapes in memory. We only have 3, so we just prep them all in advance. void Viewer::initGeometries() { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable( GL_BLEND ); glEnable( GL_CULL_FACE ); glFrontFace( GL_CCW ); glCullFace( GL_BACK ); glClearColor(0.0,0.0,0.0,0.0); // Create our VertexArrays Objects and VertexBuffer Objects glGenVertexArrays(NumVAOs, m_VAOs); glGenBuffers(NumBuffers, m_Buffers); // Create our pentagone object, store its vertices on the graphic card, and // bind the data to the vPosition attribute of the shader GLfloat verticesCube[numVerticesCube][3] = { // 12 triangles == 6 squares //Front, if Z is towards us { -0.5, 0.5, 0.5 }, { -0.5, -0.5, 0.5 }, { 0.5, -0.5, 0.5 }, { 0.5, -0.5, 0.5 }, { 0.5, 0.5, 0.5 }, { -0.5, 0.5, 0.5 }, //Back { -0.5, 0.5, -0.5 }, { 0.5, 0.5, -0.5 }, { 0.5, -0.5, -0.5 }, { 0.5, -0.5, -0.5 }, { -0.5, -0.5, -0.5 }, { -0.5, 0.5, -0.5 }, //Right { -0.5, 0.5, 0.5 }, { -0.5, 0.5, -0.5 }, { -0.5, -0.5, -0.5 }, { -0.5, -0.5, -0.5 }, { -0.5, -0.5, 0.5 }, { -0.5, 0.5, 0.5 }, //Left { 0.5, 0.5, 0.5 }, { 0.5, -0.5, -0.5 }, { 0.5, 0.5, -0.5 }, { 0.5, -0.5, -0.5 }, { 0.5, 0.5, 0.5 }, { 0.5, -0.5, 0.5 }, //Top { -0.5, 0.5, 0.5 }, { 0.5, 0.5, 0.5 }, { 0.5, 0.5, -0.5 }, { 0.5, 0.5, -0.5 }, { -0.5, 0.5, -0.5 }, { -0.5, 0.5, 0.5 }, //Bottom { -0.5, -0.5, 0.5 }, { 0.5, -0.5, -0.5 }, { 0.5, -0.5, 0.5 }, { 0.5, -0.5, -0.5 }, { -0.5, -0.5, 0.5 }, { -0.5, -0.5, -0.5 } }; GLfloat uvs[numVerticesCube][2] = { { 1.0, .25 }, { 1.0, .50 }, { .75, .50 }, { .75, .50 }, { .75, .25 }, { 1.0, .25 }, { .25, .25 }, { .50, .25 }, { .50, .50 }, { .50, .50 }, { .25, .50 }, { .25, .25 }, { .00, .25}, { .25, .25}, { .25, .50}, { .25, .50}, { .00, .50}, { .00, .25}, { .75, .25}, { .50, .50}, { .50, .25}, { .50, .50}, { .75, .25}, { .75, .50}, { .25, .00 }, { .49, .00 }, { .49, .25 }, { .49, .25 }, { .25, .25 }, { .25, .00 }, { .25, .50 }, { .49, .75 }, { .49, .50 }, { .49, .75 }, { .25, .50 }, { .25, .75 }, }; GLfloat normals[numVerticesCube][3] = { { 0.0, 0.0, 1.0 }, {0.0, 0.0, 1.0}, {0.0, 0.0, 1.0}, { 0.0, 0.0, 1.0 }, {0.0, 0.0, 1.0}, {0.0, 0.0, 1.0}, { 0.0, 0.0, -1.0 }, {0.0, 0.0, -1.0}, {0.0, 0.0, -1.0}, { 0.0, 0.0, -1.0 }, {0.0, 0.0, -1.0}, {0.0, 0.0, -1.0}, { -1.0, 0.0, 0.0 }, {-1.0, 0.0, 0.0}, {-1.0, 0.0, 0.0}, { -1.0, 0.0, 0.0 }, {-1.0, 0.0, 0.0}, {-1.0, 0.0, 0.0}, { 1.0, 0.0, 0.0}, {1.0, 0.0, 0.0}, {1.0, 0.0, 0.0}, { 1.0, 0.0, 0.0 }, {1.0, 0.0, 0.0}, {1.0, 0.0, 0.0}, { 0.0, 1.0, 0.0 }, {0.0, 1.0, 0.0}, {0.0, 1.0, 0.0}, { 0.0, 1.0, 0.0 }, {0.0, 1.0, 0.0}, {0.0, 1.0, 0.0}, { 0.0, -1.0, 0.0 }, {0.0, -1.0, 0.0}, {0.0, -1.0, 0.0}, { 0.0, -1.0, 0.0 }, {0.0, -1.0, 0.0}, {0.0, -1.0, 0.0} }; GLfloat sphereVertices[numVerticesSphere][3]; GLfloat sphereNormals[numVerticesSphere][3]; GLuint sphereIndices[numTriSphere * 3][3]; // Create Sphere // Generate surrounding vertices unsigned int v = 0; float thetaInc = 2.0f*3.14159265f / static_cast(numColSphere); float phiInc = 3.14159265f / static_cast(numRowSphere+1); for (int row=0; row(row+1) * phiInc); for (int col=0; colbind(); m_program->setUniformValue(m_isSkyLoc, false); m_program->setUniformValue(m_drawTextLoc, true); m_program->setUniformValue(m_isLightLoc, false); m_program->setUniformValue(m_mvMatrixLocation, modelViewMatrix); m_program->setUniformValue(m_normalMatrixLoc, modelViewMatrix.normalMatrix()); int faces = floor(numVerticesCube/6); for(int i =0; isetRgb(s.getColor().rgb() + i); m_program->setUniformValue(m_colorLocation, *faceColor); glDrawArrays(GL_TRIANGLES, i*6, 6); } delete faceColor; //glDrawArrays(GL_TRIANGLES, 0, 36); } void Viewer::visit(Sphere &s) { // std::cout << "Sphere found"; QMatrix4x4 modelViewMatrix = modelStack.top() * QMatrix4x4(s.transform); if(!isPickingActivated){ glBindVertexArray(m_VAOs[VAO_Sphere]); m_program->setUniformValue(m_isSkyLoc, false); m_program->setUniformValue(m_mvMatrixLocation, modelViewMatrix); m_program->setUniformValue(m_normalMatrixLoc, modelViewMatrix.normalMatrix()); m_program->setUniformValue(m_colorLocation, s.getColor()); m_program->setUniformValue(m_drawTextLoc, false); m_program->setUniformValue(m_isLightLoc, true); // This will save the current position of this light ball as the next point // This means point lights are technically 1 frame behind QVector3D point = QVector3D(0,0,0); point = modelViewMatrix * point; int pointLocs[3] = {m_point1Loc, m_point2Loc, m_point3Loc}; int pointCLocs[3] = {m_c1Loc, m_c2Loc, m_c3Loc}; m_program->setUniformValue(pointLocs[currentPoint], point); m_program->setUniformValue(pointCLocs[currentPoint], s.getColor()); currentPoint = (currentPoint + 1) % 3; glDrawElements(GL_TRIANGLES, numTriSphere*3, GL_UNSIGNED_INT, 0); } } void Viewer::visit(SceneGroup &s) { // Build compound transformation matrix QMatrix4x4 currentMatrix = modelStack.top() * QMatrix4x4(s.transform); modelStack.push(currentMatrix); while(s.hasNext()) { // Get next leaf GlNode* current = s.getChild(); // Draw/Traverse child current->accept(*this); } // Return model matrix to previous state modelStack.pop(); } void Viewer::setPhong(bool on) { m_program->bind(); m_program->setUniformValue(m_isPhongLoc, on); if(on) std::cout << "Phong ON\n"; else std::cout << "Phong OFF\n"; this->update(); } void Viewer::changeColor(QColor c){ if(activeCell->getChildren()->size()){ Shape* shape = dynamic_cast (activeCell->childAt(0)); shape->setColor(c); } activeColor = new QColor(c); this->update(); } QMatrix4x4 Viewer::pickGeom(int x, int y){ makeCurrent(); m_program->bind(); m_program->setUniformValue(m_isPickingModeLoc, true); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); isPickingActivated = true; QMap mapColorToPosition; QColor c; QRgb startColor = 1; // alpha must be 100%, glReadPixels doesn't resturn alpha unsigned char pixelData[3]; // Give each cube a color, let each cube manage their face-color assignment //std::cout << "Iterating through " << root.getChildren()->size() << " items"<size(); i++) { //std::cout << " iterating... " << i << endl; SceneGroup* current = dynamic_cast(root.childAt(i)); Shape* currentCube; if(current->getChildren()->size()) { currentCube = dynamic_cast(current->childAt(0)); } c.setRgb(startColor); currentCube->setColor(c); if(currentCube) for (int j = 0; j<3; j++) { for(int i = -1; i<=1; i+=2) { QMatrix4x4 currentTransform = QMatrix4x4(current->transform); int components[3] = {0, 0, 0}; components[j] = i; //std::cout << "Setting " << startColor << " to {" << components[0] << ", " << components[0] << ", " << components[1] << ", " << components[2] << "}\n"; currentTransform.translate(components[1], -components[2], -components[0]); mapColorToPosition.insert(startColor, currentTransform); startColor++; } } } root.accept(*this); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // for debugging purposes /*unsigned char pdata[512][512][4]; glReadPixels(x-256, y-256, 512, 512, GL_RGBA, GL_UNSIGNED_BYTE, pdata); QImage *im = new QImage(512, 512, QImage::Format_RGB32); for(int i = 0; i< 512; i++){ for(int j = 0; j< 512; j++){ QColor* pickedColor = new QColor(); pickedColor->setRgb(pdata[512-j][i][0], pdata[512-j][i][1], pdata[512-j][i][2]); if(i==256 && j == 256) pickedColor->setRgba(0x88FFFFFF); if(i==256 && j == 256) std::cout<<"--- Color under cursor: " << (int)pdata[j][i][0] <<" "<< (int)pdata[j][i][1] <<" "<< (int)pdata[j][i][2] << endl; pickedColor->setAlpha(255); im->setPixelColor(i, j, pickedColor->rgb()); } } im->save("./screenshot.bmp");//*/ glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixelData); QColor* pickedColor = new QColor(pixelData[0], pixelData[1], pixelData[2]); unsigned int pickedInt = pickedColor->rgb() % 0x1000000; // rgb returns ARGB. go figure. //std::cout << "Picked " << pickedInt << endl; QMatrix4x4 pickedPosition = mapColorToPosition.value(pickedInt, identityMatrix); mapColorToPosition.clear(); m_program->setUniformValue(m_isPickingModeLoc, false); isPickingActivated = false; doneCurrent(); return pickedPosition; }