void Spring::updateForce(RigidBody* body, real duration) { // Calculate the two ends in world space Vector3 lws = body->getPointInWorldSpace(connectionPoint); Vector3 ows = other->getPointInWorldSpace(otherConnectionPoint); // Calculate the vector of the spring Vector3 force = lws - ows; // Calculate the magnitude of the force real magnitude = force.magnitude(); // @TODO: ask massimo if this is correct //magnitude = real_abs(magnitude - restLength); magnitude = magnitude - restLength; magnitude *= springConstant; // Calculate the final force and apply it force.normalise(); force *= -magnitude; body->addForceAtPoint(force, lws); }
float TerUtil::GetAngleAt(Terrain* terrain, float x, float z, float s) { //float* hf = terrain ? terrain->getHeightData() : sc->td.hfHeight; //Real t = sc->td.fTriangleSize * 2.f; //Vector3 vx(t, hf[a+1] - hf[a-1], 0); // x+1 - x-1 //Vector3 vz(0, hf[a+wx] - hf[a-wx], t); // z+1 - z-1 //Vector3 norm = -vx.crossProduct(vz); norm.normalise(); //Real ang = Math::ACos(norm.y).valueDegrees(); Real y0=0; Vector3 vx(x-s, y0, z), vz(x, y0, z-s); Vector3 vX(x+s, y0, z), vZ(x, y0, z+s); vx.y = terrain->getHeightAtWorldPosition(vx); vX.y = terrain->getHeightAtWorldPosition(vX); vz.y = terrain->getHeightAtWorldPosition(vz); vZ.y = terrain->getHeightAtWorldPosition(vZ); Vector3 v_x = vx-vX; //v_x.normalise(); Vector3 v_z = vz-vZ; //v_z.normalise(); Vector3 n = -v_x.crossProduct(v_z); n.normalise(); Real a = Math::ACos(n.y).valueDegrees(); return a; }
void Pod::update(float elapsed) { if (glowNode) { glowNode->setOrientation(head->_getDerivedOrientation()); } if (indicatorNode) { indicatorNode->roll(Degree(elapsed));; } if (moveSpeed != 0.0) { Vector3 dist = dest - entirePod->getPosition(); Vector3 norm = dist; norm.normalise(); Vector3 delta = dist * moveSpeed * elapsed; // if (delta.length() > dist.length()) if (delta.x * delta.x + delta.y * delta.y + delta.z * delta.z > dist.x * dist.x + dist.y * dist.y + dist.z * dist.z) delta = dist; move(delta); } if (rotateSpeed != Vector3::ZERO) { head->yaw(Degree(rotateSpeed.x)); head->pitch(Degree(rotateSpeed.y)); head->roll(Degree(rotateSpeed.z)); } if( bPFXNode ) { bPFX->getEmitter(0)->setParameter("width",Util::toStringFloat(bPFXwidth)); bPFX->getEmitter(0)->setColour(ColourValue(bPFXcolor,1.0f,1.0f)); if( bPFXwidth+0.75f < 4.0f ) bPFXwidth += 0.75f; else bPFXwidth = 4.0f; if( bPFXcolor-0.05f > 0.0f ) bPFXcolor -= 0.05f; else bPFXcolor = 0.0f; } }
bool CollisionTools::collidesWithEntity(const Vector3& fromPoint, const Vector3& toPoint, const float collisionRadius, const float rayHeightLevel, const uint32 queryMask) { Vector3 fromPointAdj(fromPoint.x, fromPoint.y + rayHeightLevel, fromPoint.z); Vector3 toPointAdj(toPoint.x, toPoint.y + rayHeightLevel, toPoint.z); Vector3 normal = toPointAdj - fromPointAdj; float distToDest = normal.normalise(); Vector3 myResult(0, 0, 0); Ogre::Entity* myObject = NULL; float distToColl = 0.0f; if (raycastFromPoint(fromPointAdj, normal, myResult, (unsigned long&)myObject, distToColl, queryMask)) { distToColl -= collisionRadius; return (distToColl <= distToDest); } else { return false; } }
//----------------------------------------------------------------------- void AlignAffector::_affect(ParticleTechnique* particleTechnique, Particle* particle, Real timeElapsed) { if (particle->particleType == Particle::PT_VISUAL) { // Set the orientation towards the previous particle, but rotation is undetermined. VisualParticle* visualParticle = static_cast<VisualParticle*>(particle); // Get difference Vector3 diff = (mPreviousParticle->position - particle->position); if (mResize) { visualParticle->setOwnDimensions (visualParticle->width, diff.length(), visualParticle->depth); } diff.normalise(); visualParticle->orientation.x = diff.x; visualParticle->orientation.y = diff.y; visualParticle->orientation.z = diff.z; } mPreviousParticle = particle; }
void CArrayGraphics::setTransform(){ //en un futuro simplemente se orientara la entidad en el 3ds o similar. // Obtengo la camara para posicionarla en esta posicion pero algo modificada Graphics::CCamera* camera = Graphics::CServer::getSingletonPtr()->getActiveScene()->getCamera(); Vector3 direction = /*camera->getTargetCameraPosition() -*/ camera->getCameraPosition(); direction.normalise(); //Vector3 posicionModificada = camera->getCameraPosition() - Vector3(0,2.5,0) + ((8.0f) * direction); Vector3 posicionModificada = camera->getCameraPosition() + ((8.0f) * direction); //Aqui establezco la rotacion (En un futuro se rotara el modelo) Quaternion yaw,pitch,roll; Math::rotate(Vector3::UNIT_Y,Ogre::Radian(_graphicsEntities[_currentWeapon].yaw),yaw); Math::rotate(Vector3::UNIT_X,Ogre::Radian(_graphicsEntities[_currentWeapon].pitch),pitch); Math::rotate(Vector3::UNIT_Z,Ogre::Radian(_graphicsEntities[_currentWeapon].roll),roll); _graphicsEntities[_currentWeapon]._graphicsEntity->setTransform(posicionModificada,yaw*pitch*roll); }// setTransform
Vector4* SnowTerrain::convertNormalsToFloats(PixelBox* terrainNormals, bool compressed) { const size_t srcPixelSize = PixelUtil::getNumElemBytes(terrainNormals->format); const size_t dstPixelSize = PixelUtil::getNumElemBytes(PF_FLOAT32_RGBA); size_t w,h,d; w = terrainNormals->getWidth(); h = terrainNormals->getHeight(); d = terrainNormals->getDepth(); assert(terrainNormals->getWidth() == mTerrainSize); size_t terrainNormalSize = terrainNormals->getWidth()*terrainNormals->getHeight(); Vector4* terrainNormalDataCorrected = OGRE_ALLOC_T(Vector4, terrainNormalSize, MEMCATEGORY_GENERAL); size_t i = 0; size_t j = 0; uint8* pixelsBuffer = static_cast<uint8*>(terrainNormals->data); for(; i < terrainNormalSize * srcPixelSize && j < terrainNormalSize * sizeof(Vector4); i+=srcPixelSize) { uint8 r,g,b,a; PixelUtil::unpackColour(&r,&g,&b,&a, terrainNormals->format, static_cast<void*>(&pixelsBuffer[i])); float fr,fg,fb; if(compressed) { //(signed) float packed/compressed into uint8, unpack fr = ((float)(r))/(0.5f * 255.0f) - 1.0f; fg = ((float)(g))/(0.5f * 255.0f) - 1.0f; fb = ((float)(b))/(0.5f * 255.0f) - 1.0f; } else { fr = ((float)(r))/255.0f; fg = ((float)(g))/255.0f; fb = ((float)(b))/255.0f; } Vector3 v = Vector3(fr, fg, fb); v.normalise(); terrainNormalDataCorrected[j++] = Vector4(v); } return terrainNormalDataCorrected; }
void utils3D::rotateVecByQuat(Vector3& vec, const Vector3& axis, const float angle) { float mag = vec.magnitude(); vec.normalise(); Quaternion vq(0, vec.x, vec.y, vec.z); Quaternion q; // HAUKAP - put this in quaternion class as another constructor Quaternion rot( cos(angle/2), axis.x * sin(angle/2), axis.y * sin(angle/2), axis.z * sin(angle/2) ); q = rot * q; q.normalise(); Quaternion qp(q.r, -q.x, -q.y, -q.z); Quaternion vp = qp * vq * q; vec.x = vp.x * mag; vec.y = vp.y * mag; vec.z = vp.z * mag; return; }
void GameInputHandler::handleMouse(float timeSinceLastFrame) { const OIS::MouseState &ms = inputManager->mouseState; static float delay = 0.0f; delay -= timeSinceLastFrame; if (game.getSubState() != SUBST_EDITOR) { windowManager.showCursor(game.getSubState() == SUBST_GAMESTART); return; } if (!inputManager->isMouseDown(OIS::MB_Middle)) { windowManager.showCursor(true); // Edit geometry with left and right clicks CEGUI::TabControl* tabWindow = (CEGUI::TabControl*)CEGUI::WindowManager::getSingleton().getWindow("EditorTabControl"); if (inputManager->processMouse && (tabWindow->getSelectedTabIndex() == 1) && (delay <= 0) && (inputManager->isMouseDown(OIS::MB_Left) || inputManager->isMouseDown(OIS::MB_Right))) { Ray r = camera->getCameraToViewportRay(CEGUI::MouseCursor::getSingleton().getPosition().d_x / settings.resolution_Width, CEGUI::MouseCursor::getSingleton().getPosition().d_y / settings.resolution_Height); Vector3 pos = camera->getDerivedPosition(); Vector3 dir = r.getDirection(); dir.normalise(); game.editLevel(inputManager->isMouseDown(OIS::MB_Left), pos, dir); delay = 0.12f; } } else { windowManager.showCursor(false); rotX = Degree(-ms.X.rel * settings.mouseSensitivity); rotY = Degree(-ms.Y.rel * settings.mouseSensitivity); } }
void ObjectControl::update() { using namespace Ogre; if( widget && node ) { Vector3 objPos = node->_getWorldAABB().getCenter(); mSceneMgr->getRootSceneNode()->removeChild( widget ); mSceneMgr->getRootSceneNode()->addChild( widget ); Vector3 raytoCam = objPos-mCamera->getRealPosition(); raytoCam.normalise(); raytoCam *= 20; widget->setPosition( mCamera->getRealPosition()+raytoCam ); widget->setVisible( true ); planeXNode->setPosition( objPos ); planeYNode->setPosition( objPos ); planeZNode->setPosition( objPos ); } }
// Draw objects to help visually debug the engine (collision points, normals, etc...) void RenderingDemo::drawPhysicsDebugObjects() { std::vector<PhysicsEngine::Collision> *collisionList = physicsEngine.getCollisionList(); for (unsigned int collisionIndex = 0; collisionIndex < collisionList->size(); collisionIndex++) { // Render the collision position Vector3 collisionPosition = (*collisionList)[collisionIndex].contactPoint; Rendering::drawSphere(collisionPosition, Vector3(1.0f, 1.0f, 0.0f), 0.3); // Now render the collision normal Vector3 collisionNormal = (*collisionList)[collisionIndex].contactNormal; collisionNormal.normalise(); Vector3 rotationAxis = collisionNormal.vectorProduct(Vector3(1.0f, 0.0f, 0.0f)); float rotationAngle = acosf(collisionNormal.scalarProduct(Vector3(1.0f, 0.0f, 0.0f))); collisionPosition = collisionPosition + (collisionNormal * .3f); // Create a transformation matrix corresponding to the collision normal's rotation and position Quaternion collisionRotation(cosf(rotationAngle / 2.0f), rotationAxis[0] * sinf(rotationAngle / 2.0f), rotationAxis[1] * sinf(rotationAngle / 2.0f), rotationAxis[2] * sinf(rotationAngle / 2.0f)); Matrix4 transformationMatrix; PhysicsEngine::RigidBody::_calculateTransformMatrix(transformationMatrix, collisionPosition, collisionRotation); GLfloat mat[16]; transformationMatrix.fillGLArray(mat); Rendering::drawBox(mat, Vector3(1.0f, 1.0f, 1.0f), Vector3(0.5f, 0.05f, 0.05f)); // Print out the collision info to the console std::cout << "Collision Position:" << std::endl; std::cout << collisionPosition[0] << " " << collisionPosition[1] << " " << collisionPosition[2] << std::endl; // Print out the collision normals to the console std::cout << "Collision Normal:" << std::endl; std::cout << collisionNormal[0] << " " << collisionNormal[1] << " " << collisionNormal[2] << std::endl; std::cout << "Rotation axis:" << std::endl; std::cout << rotationAxis[0] << " " << rotationAxis[1] << " " << rotationAxis[2] << std::endl; std::cout << "Rotation angle:" << std::endl; std::cout << rotationAngle << std::endl; } }
void Water::ShowWave(Vector3 refpos) { if (!m_waterplane_vert_buf_local) return; float xScaled = m_map_size.x * m_waterplane_mesh_scale; float zScaled = m_map_size.z * m_waterplane_mesh_scale; for (int pz = 0; pz < WAVEREZ + 1; pz++) { for (int px = 0; px < WAVEREZ + 1; px++) { m_waterplane_vert_buf_local[(pz * (WAVEREZ + 1) + px) * 8 + 1] = CalcWavesHeight(refpos + Vector3(xScaled * 0.5 - (float)px * xScaled / WAVEREZ, 0, (float)pz * zScaled / WAVEREZ - zScaled * 0.5)) - m_water_height; } } //normals for (int pz = 0; pz < WAVEREZ + 1; pz++) { for (int px = 0; px < WAVEREZ + 1; px++) { int left = std::max(0, px - 1); int right = std::min(px + 1, WAVEREZ); int up = std::max(0, pz - 1); int down = std::min(pz + 1, WAVEREZ); Vector3 normal = (Vector3(m_waterplane_vert_buf_local + ((pz * (WAVEREZ + 1) + left) * 8)) - Vector3(m_waterplane_vert_buf_local + ((pz * (WAVEREZ + 1) + right) * 8))).crossProduct(Vector3(m_waterplane_vert_buf_local + ((up * (WAVEREZ + 1) + px) * 8)) - Vector3(m_waterplane_vert_buf_local + ((down * (WAVEREZ + 1) + px) * 8))); normal.normalise(); m_waterplane_vert_buf_local[(pz * (WAVEREZ + 1) + px) * 8 + 3] = normal.x; m_waterplane_vert_buf_local[(pz * (WAVEREZ + 1) + px) * 8 + 4] = normal.y; m_waterplane_vert_buf_local[(pz * (WAVEREZ + 1) + px) * 8 + 5] = normal.z; } } m_waterplane_vert_buf->writeData(0, (WAVEREZ + 1) * (WAVEREZ + 1) * 32, m_waterplane_vert_buf_local, true); }
Missile* Player::fireMissile() { // 총알 생성 // Missile *missile = new Missile(mSceneMgr); // 총알의 발사 방향 // Vector3 direction = getDirection(); direction.normalise(); btVector3 velocity = btVector3(direction.x, direction.y, direction.z) * 5000; // 총알의 생성 위치 // Vector3 Rotation = mPlayerNode->getPosition() - Vector3(0, 3, 0); missile->makeMissile( Rotation ); missile->setVelocity( velocity ); mPhysicsWorld->addObject( missile->getMissilePhysics() ); // 불빛 // mFireFlash->create( mWeaponNode, mWeaponNode->getPosition() + Vector3(5, -14, 38)); mMissileTimer.reset(); return missile; }
Vector3 TerrainInfo::getNormalAt(float x, float z) const { int flip = 1; Vector3 here (x, getHeightAt(x, z), z); Vector3 left (x-1, getHeightAt(x-1, z), z); Vector3 down (x, getHeightAt(x, z+1), z+1); if (left.x < 0.0) { flip *= -1; left = Vector3(x+1, getHeightAt(x+1, z), z); } if (down.z >= mOffset.z + mScale.z*(mHeight-1)) { flip *= -1; down = Vector3(x, getHeightAt(x, z-1), z-1); } left -= here; down -= here; Vector3 norm = flip * left.crossProduct(down); norm.normalise(); return norm; }
Matrix4 Matrix4::buildViewMatrix(const Vector3 &from, const Vector3 &lookingAt, const Vector3 up) { Matrix4 r; r.setPositionVector(Vector3(-from.x, -from.y, -from.z)); Matrix4 m; Vector3 f = (lookingAt - from); f.normalise(); Vector3 s = Vector3::cross(f, up); Vector3 u = Vector3::cross(s, f); m.values[0] = s.x; m.values[4] = s.y; m.values[8] = s.z; m.values[1] = u.x; m.values[5] = u.y; m.values[9] = u.z; m.values[2] = -f.x; m.values[6] = -f.y; m.values[10] = -f.z; return m * r; }
void Water::showWave(Vector3 refpos) { if (!wbuffer) return; float xScaled = mapSize.x * mScale; float zScaled = mapSize.z * mScale; for (int pz=0; pz<WAVEREZ+1; pz++) { for (int px=0; px<WAVEREZ+1; px++) { wbuffer[(pz*(WAVEREZ+1)+px)*8+1] = getHeightWaves(refpos + Vector3(xScaled*0.5-(float)px * xScaled / WAVEREZ, 0, (float)pz * zScaled / WAVEREZ - zScaled * 0.5)) - wHeight; } } //normals for (int pz=0; pz<WAVEREZ+1; pz++) { for (int px=0; px<WAVEREZ+1; px++) { int left = std::max(0, px-1); int right = std::min(px+1, WAVEREZ); int up = std::max(0, pz-1); int down = std::min(pz+1, WAVEREZ); Vector3 normal = (Vector3(wbuffer+((pz*(WAVEREZ+1)+left)*8))-Vector3(wbuffer+((pz*(WAVEREZ+1)+right)*8))).crossProduct(Vector3(wbuffer+((up*(WAVEREZ+1)+px)*8))-Vector3(wbuffer+((down*(WAVEREZ+1)+px)*8))); normal.normalise(); wbuffer[(pz*(WAVEREZ+1)+px)*8+3] = normal.x; wbuffer[(pz*(WAVEREZ+1)+px)*8+4] = normal.y; wbuffer[(pz*(WAVEREZ+1)+px)*8+5] = normal.z; } } wbuf->writeData(0, (WAVEREZ+1)*(WAVEREZ+1)*32, wbuffer, true); }
unsigned CollisionDetector::boxAndBox( const CollisionBox &one, const CollisionBox &two, CollisionData *data ) { //if (!IntersectionTests::boxAndBox(one, two)) return 0; // Find the vector between the two centres Vector3 toCentre = two.getAxis(3) - one.getAxis(3); // We start assuming there is no contact real pen = REAL_MAX; unsigned best = 0xffffff; // Now we check each axes, returning if it gives us // a separating axis, and keeping track of the axis with // the smallest penetration otherwise. CHECK_OVERLAP(one.getAxis(0), 0); CHECK_OVERLAP(one.getAxis(1), 1); CHECK_OVERLAP(one.getAxis(2), 2); CHECK_OVERLAP(two.getAxis(0), 3); CHECK_OVERLAP(two.getAxis(1), 4); CHECK_OVERLAP(two.getAxis(2), 5); // Store the best axis-major, in case we run into almost // parallel edge collisions later unsigned bestSingleAxis = best; CHECK_OVERLAP(one.getAxis(0) % two.getAxis(0), 6); CHECK_OVERLAP(one.getAxis(0) % two.getAxis(1), 7); CHECK_OVERLAP(one.getAxis(0) % two.getAxis(2), 8); CHECK_OVERLAP(one.getAxis(1) % two.getAxis(0), 9); CHECK_OVERLAP(one.getAxis(1) % two.getAxis(1), 10); CHECK_OVERLAP(one.getAxis(1) % two.getAxis(2), 11); CHECK_OVERLAP(one.getAxis(2) % two.getAxis(0), 12); CHECK_OVERLAP(one.getAxis(2) % two.getAxis(1), 13); CHECK_OVERLAP(one.getAxis(2) % two.getAxis(2), 14); // Make sure we've got a result. assert(best != 0xffffff); // We now know there's a collision, and we know which // of the axes gave the smallest penetration. We now // can deal with it in different ways depending on // the case. if (best < 3) { // We've got a vertex of box two on a face of box one. fillPointFaceBoxBox(one, two, toCentre, data, best, pen); data->addContacts(1); return 1; } else if (best < 6) { // We've got a vertex of box one on a face of box two. // We use the same algorithm as above, but swap around // one and two (and therefore also the vector between their // centres). fillPointFaceBoxBox(two, one, toCentre*-1.0f, data, best-3, pen); data->addContacts(1); return 1; } else { // We've got an edge-edge contact. Find out which axes best -= 6; unsigned oneAxisIndex = best / 3; unsigned twoAxisIndex = best % 3; Vector3 oneAxis = one.getAxis(oneAxisIndex); Vector3 twoAxis = two.getAxis(twoAxisIndex); Vector3 axis = oneAxis % twoAxis; axis.normalise(); // The axis should point from box one to box two. if (axis * toCentre > 0) axis = axis * -1.0f; // We have the axes, but not the edges: each axis has 4 edges parallel // to it, we need to find which of the 4 for each object. We do // that by finding the point in the centre of the edge. We know // its component in the direction of the box's collision axis is zero // (its a mid-point) and we determine which of the extremes in each // of the other axes is closest. Vector3 ptOnOneEdge = one.halfSize; Vector3 ptOnTwoEdge = two.halfSize; for (unsigned i = 0; i < 3; i++) { if (i == oneAxisIndex) ptOnOneEdge[i] = 0; else if (one.getAxis(i) * axis > 0) ptOnOneEdge[i] = -ptOnOneEdge[i]; if (i == twoAxisIndex) ptOnTwoEdge[i] = 0; else if (two.getAxis(i) * axis < 0) ptOnTwoEdge[i] = -ptOnTwoEdge[i]; } // Move them into world coordinates (they are already oriented // correctly, since they have been derived from the axes). ptOnOneEdge = one.transform * ptOnOneEdge; ptOnTwoEdge = two.transform * ptOnTwoEdge; // So we have a point and a direction for the colliding edges. // We need to find out point of closest approach of the two // line-segments. Vector3 vertex = contactPoint( ptOnOneEdge, oneAxis, one.halfSize[oneAxisIndex], ptOnTwoEdge, twoAxis, two.halfSize[twoAxisIndex], bestSingleAxis > 2 ); // We can fill the contact. Contact* contact = data->contacts; contact->penetration = pen; contact->contactNormal = axis; contact->contactPoint = vertex; contact->setBodyData(one.body, two.body, data->friction, data->restitution); data->addContacts(1); return 1; } return 0; }
/// Implements the plane optimal shadow camera setup algorithm void PlaneOptimalShadowCameraSetup::getShadowCamera (const SceneManager *sm, const Camera *cam, const Viewport *vp, const Light *light, Camera *texCam, size_t iteration) const { // get the plane transformed by the parent node(s) // Also, make sure the plane is normalized Plane worldPlane = mPlane->_getDerivedPlane(); worldPlane.normalise(); // get camera's projection matrix Matrix4 camProjection = cam->getProjectionMatrix() * cam->getViewMatrix(); // get the world points to constrain vector<Vector4>::type vhull; cam->forwardIntersect(worldPlane, &vhull); if (vhull.size() < 4) return; // make sure the last point is a finite point (not point at infinity) if (vhull[3].w == 0.0) { int finiteIndex = -1; for (uint loopIndex = 0; loopIndex < vhull.size(); loopIndex++) { if (vhull[loopIndex].w != 0.0) { finiteIndex = loopIndex; break; } } if (finiteIndex == -1) { // there are no finite points, which means camera doesn't see plane of interest. // so we don't care what the shadow map matrix is // We'll map points off the shadow map so they aren't even stored Matrix4 crazyMat(0.0, 0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 1.0); texCam->setCustomViewMatrix(true, Matrix4::IDENTITY); texCam->setCustomProjectionMatrix(true, crazyMat); return; } // swap finite point to last point std::swap(vhull[3], vhull[finiteIndex]); } vhull.resize(4); // get the post-projective coordinate constraints vector<Vector2>::type constraint; for (int i=0; i<4; i++) { Vector4 postProjPt = camProjection * vhull[i]; postProjPt *= 1.0 / postProjPt.w; constraint.push_back(Vector2(postProjPt.x, postProjPt.y)); } // perturb one point so we don't have coplanarity const Vector4& pinhole = light->getAs4DVector(); const Vector4& oldPt = vhull.back(); Vector4 newPt; if (pinhole.w == 0) { // It's directional light static const Real NEAR_SCALE = 100.0; newPt = oldPt + (pinhole * (cam->getNearClipDistance() * NEAR_SCALE)); } else { // It's point or spotlight Vector4 displacement = oldPt - pinhole; Vector3 displace3 = Vector3(displacement.x, displacement.y, displacement.z); Real dotProd = fabs(displace3.dotProduct(worldPlane.normal)); static const Real NEAR_FACTOR = 0.05; newPt = pinhole + (displacement * (cam->getNearClipDistance() * NEAR_FACTOR / dotProd)); } vhull.back() = newPt; // solve for the matrix that stabilizes the plane Matrix4 customMatrix = computeConstrainedProjection(pinhole, vhull, constraint); if (pinhole.w == 0) { // TODO: factor into view and projection pieces. // Note: In fact, it's unnecessary to factor into view and projection pieces, // but if we do, we will more according with academic requirement :) texCam->setCustomViewMatrix(true, Matrix4::IDENTITY); texCam->setCustomProjectionMatrix(true, customMatrix); return; } Vector3 tempPos = Vector3(pinhole.x, pinhole.y, pinhole.z); // factor into view and projection pieces Matrix4 translation(1.0, 0.0, 0.0, tempPos.x, 0.0, 1.0, 0.0, tempPos.y, 0.0, 0.0, 1.0, tempPos.z, 0.0, 0.0, 0.0, 1.0); Matrix4 invTranslation(1.0, 0.0, 0.0, -tempPos.x, 0.0, 1.0, 0.0, -tempPos.y, 0.0, 0.0, 1.0, -tempPos.z, 0.0, 0.0, 0.0, 1.0); Matrix4 tempMatrix = customMatrix * translation; Vector3 zRow(-tempMatrix[3][0], -tempMatrix[3][1], -tempMatrix[3][2]); zRow.normalise(); Vector3 up; if (zRow.y == 1.0) up = Vector3(1,0,0); else up = Vector3(0,1,0); Vector3 xDir = up.crossProduct(zRow); xDir.normalise(); up = zRow.crossProduct(xDir); Matrix4 rotation(xDir.x, up.x, zRow.x, 0.0, xDir.y, up.y, zRow.y, 0.0, xDir.z, up.z, zRow.z, 0.0, 0.0, 0.0, 0.0, 1.0 ); Matrix4 customProj = tempMatrix * rotation; Matrix4 customView = rotation.transpose() * invTranslation; // note: now customProj * (0,0,0,1)^t = (0, 0, k, 0)^t for k some constant // note: also customProj's 4th row is (0, 0, c, 0) for some negative c. // set the shadow map camera texCam->setCustomViewMatrix(true, customView); texCam->setCustomProjectionMatrix(true, customProj); }
void AIInConeAttackStrategy::Step(unsigned timeMs) { IScenable *scen = Parent->GetScenable(); if (TargetID<0) { return; } if (TargetID==0) { if (Owner) { TargetID = Owner->SelectTargetID(); } if (TargetID<=0) { TargetID=-1; IEquipped *eq = Parent->GetEquipped(); eq->SetTargetPosition(Ogre::Vector3::ZERO); eq->SetShooting(false); return; } } IAAObject *obj = CommonDeclarations::GetIDObject(TargetID); if (NULL==obj) { TargetID=0; return; } IScenable* Target; Target = obj->GetScenable(); if (NULL==Target/* || phys->IsCollided()*/) return; if(RotationUnit.mRotating) { RotationUnit.Step(); if(RotationUnit.mRotating) // Process timed rotation { Ogre::Quaternion delta = RotationUnit.Slerp(); scen->SetOrientation(delta); } }// else { int actual_rotation_speed = RotationSpeed; IPhysical *phys = Parent->GetPhysical(); AICommander *commander = Owner->GetCommander(); Ogre::Vector3 src = Ogre::Vector3::ZERO; if (commander) { IAAObject *obj = commander->GetParent(); if (obj) { IScenable *scen = obj->GetScenable(); if (scen) { src = scen->GetOrientation()*Ogre::Vector3::NEGATIVE_UNIT_Z; } } } else { src = -phys->GetForwardDirection(); //OurOrientation * Ogre::Vector3::NEGATIVE_UNIT_Z; } Ogre::Vector3 direction = Target->GetPosition()-scen->GetPosition(); direction.normalise(); Ogre::Real cs = src.dotProduct(direction); if (src.isZeroLength() || cs>=AngleCosinusModule) { actual_rotation_speed = actual_rotation_speed/2; /*char log[100]; sprintf(log,"in cone %f %f %f\n",direction.x, direction.y, direction.z); Debugging::Log("icas",log);*/ } else { direction = src; /*char log[100]; sprintf(log,"not in cone %f %f %f\n",direction.x, direction.y, direction.z); Debugging::Log("icas",log);*/ } Ogre::Vector3 up =CommonDeclarations::GetUpVector(); Ogre::Quaternion OurOrientation = scen->GetOrientation(); Vector3 xVec = up.crossProduct(direction); xVec.normalise(); Vector3 yVec = direction.crossProduct(xVec); yVec.normalise(); Quaternion unitZToTarget = Quaternion(xVec, yVec, direction); Quaternion targetOrientation = Quaternion(-unitZToTarget.y, -unitZToTarget.z, unitZToTarget.w, unitZToTarget.x); RotationUnit.StartRotation(OurOrientation, targetOrientation, actual_rotation_speed); } std::pair<bool, Ogre::Real> intersection_res; IEquipped *eq = Parent->GetEquipped(); Ogre::Ray pl_ray = eq->GetSightRay(); intersection_res = pl_ray.intersects(Target->GetBoundingBox(true)); if (intersection_res.first) { eq->SetTargetPosition(Target->GetPosition(), intersection_res.second); eq->SetShooting(true); } else { eq->SetTargetPosition(Ogre::Vector3::ZERO); eq->SetShooting(false); } }
void FollowCamera::update(Real time, const PosInfo& posIn, PosInfo* posOut, COLLISION_WORLD* world, bool bounce) { if (!ca || !posOut) return; /// input from car posInfoIn Vector3 posGoal = posIn.pos; Quaternion orientGoal = posIn.rot; /// output saved back to car posInfoOut Quaternion camRotFinal; const static Quaternion qO = Quaternion(Degree(180),Vector3::UNIT_Z) * Quaternion(Degree(-90),Vector3::UNIT_Y), qR = Quaternion(Degree(90),Vector3(0,1,0)); Quaternion orient = orientGoal * qO; Vector3 ofs = orient * ca->mOffset, goalLook = posGoal + ofs; first = iFirst < 2; ///par few first frames after reset if (iFirst < 10) // after reset { ++iFirst; mDistReduce = 0.f; mATilt = 0.f; } /// Camera Tilt from terrain/road slope under car //------------------------------------------------------------------------------------------- const float // params . . . Rdist = 1.f, // dist from car to ray (front or back) HupCar = 1.5f, // car up dir dist - for pipes - so pos stays inside pipe Habove = 1.5f, // up axis dist, above car - for very high terrain angles HMaxDepth = 12.f; // how far down can the ray goes const static Radian r0(0.f), angMin = Degree(10.f), // below this angle, tilt has no effect - terrain bumps maxDiff = Degree(1.4f); // max diff of tilt - no sudden jumps const float smoothSpeed = 14.f; // how fast to apply tilt change bool bUseTilt = ca->mType == CAM_ExtAng || ca->mType == CAM_Follow; Radian tilt(0.f); if (pSet->cam_tilt && bUseTilt) { // car pos Vector3 carUp = posIn.pos - HupCar * posIn.carY; MATHVECTOR<float,3> pos(carUp.x, -carUp.z, carUp.y + Habove); // to vdr/blt const static MATHVECTOR<float,3> dir(0,0,-1); // cast rays down // car rot, yaw angle Quaternion q = posIn.rot * Quaternion(Degree(90),Vector3(0,1,0)); float angCarY = q.getYaw().valueRadians() + PI_d/2.f; float ax = cosf(angCarY)*Rdist, ay = sinf(angCarY)*Rdist; //LogO("pos: "+fToStr(pos[0],2,4)+" "+fToStr(pos[1],2,4)+" a: "+fToStr(angCarY,2,4)+" dir: "+fToStr(ax,2,4)+" "+fToStr(ay,2,4)); // cast 2 rays - 2 times, average 2 angles COLLISION_CONTACT ct0,ct1,ct20,ct21; MATHVECTOR<float,3> ofs(ax*0.5f,ay*0.5f,0),ofs2(ax,ay,0); world->CastRay(pos+ofs, dir, HMaxDepth,chassis, ct0, 0,0, true, true); world->CastRay(pos-ofs, dir, HMaxDepth,chassis, ct1, 0,0, true, true); world->CastRay(pos+ofs2,dir, HMaxDepth,chassis, ct20, 0,0, true, true); world->CastRay(pos-ofs2,dir, HMaxDepth,chassis, ct21, 0,0, true, true); #ifdef CAM_TILT_DBG MATHVECTOR<float,3> v; v = pos+ofs; posHit[0] = Vector3(v[0],v[2]- ct0.GetDepth(), -v[1]); v = pos-ofs; posHit[1] = Vector3(v[0],v[2]- ct1.GetDepth(), -v[1]); v = pos+ofs2; posHit[2] = Vector3(v[0],v[2]- ct20.GetDepth(),-v[1]); v = pos-ofs2; posHit[3] = Vector3(v[0],v[2]- ct21.GetDepth(),-v[1]); #endif if (ct0.GetColObj() && ct1.GetColObj() && ct20.GetColObj() && ct21.GetColObj() ) tilt = (GetAngle(Rdist, ct1.GetDepth() - ct0.GetDepth()) + GetAngle(2.f*Rdist, ct21.GetDepth() - ct20.GetDepth())) * 0.5f; //else LogO(String("no hit: ")+(ct0.col?"1":"0")+(ct1.col?" 1":" 0")); //if (tilt < angMin && tilt > -angMin) tilt = 0.f; if (tilt < r0 && tilt >-angMin) { Radian d = tilt-angMin; tilt = std::min(r0, tilt + d*d*5.f); } if (tilt > r0 && tilt < angMin) { Radian d =-angMin-tilt; tilt = std::max(r0, tilt - d*d*5.f); } //LogO("a "+fToStr(angCarY,3,5)+" d "+fToStr(ct0.GetDepth(),3,5)+" "+fToStr(ct1.GetDepth(),3,5)+" t "+fToStr(tilt.valueDegrees(),3,5)); } // smooth tilt angle mATilt += std::min(maxDiff, std::max(-maxDiff, tilt - mATilt)) * time * smoothSpeed; //------------------------------------------------------------------------------------------- if (ca->mType == CAM_Car) /* 3 Car - car pos & rot full */ { camPosFinal = goalLook; camRotFinal = orient; posOut->camPos = camPosFinal; // save result in out posInfo posOut->camRot = camRotFinal; return; } if (ca->mType == CAM_Follow) ofs = ca->mOffset; Vector3 pos,goalPos; pos = camPosFinal - ofs; goalPos = posGoal; Vector3 xyz; if (ca->mType != CAM_Arena) { Real x,y,z,xz; // pitch & yaw to direction vector Real ap = bUseTilt ? (ca->mPitch.valueRadians() + mATilt.valueRadians()) : ca->mPitch.valueRadians(), ay = ca->mYaw.valueRadians(); y = sin(ap), xz = cos(ap); x = sin(ay) * xz, z = cos(ay) * xz; xyz = Vector3(x,y,z); xyz *= ca->mDist; } bool manualOrient = false; switch (ca->mType) { case CAM_Arena: /* 2 Arena - free pos & rot */ goalPos = ca->mOffset - ofs; break; case CAM_Free: /* 1 Free - free rot, pos from car */ goalPos += xyz; break; case CAM_Follow: /* 0 Follow - car rotY & pos from behind car, smooth */ { Quaternion orient = orientGoal * qR; orient.FromAngleAxis(orient.getYaw(), Vector3::UNIT_Y); goalPos += orient * xyz; } break; case CAM_ExtAng: /* 4 Extended Angle - car in center, angle smooth */ { Quaternion orient = orientGoal * qR; Quaternion ory; ory.FromAngleAxis(orient.getYaw(), Vector3::UNIT_Y); if (first) { qq = ory; } else qq = orient.Slerp(ca->mSpeed * time, qq, ory, true); // smooth dist from vel #if 0 { if (first) { mPosNodeOld = posGoal; } Real vel = (posGoal - mPosNodeOld).length() / std::max(0.002f, std::min(0.1f, time)); mPosNodeOld = posGoal; if (first) mVel = 0.f; else mVel += (vel - mVel) * time * 8.f; //par- vel smooth speed if (!first) xyz *= 1.f + std::min(100.f, mVel) * 0.01f; //par- vel dist factor } #endif Quaternion qy = Quaternion(ca->mYaw,Vector3(0,1,0)); goalPos += qq * (xyz + ca->mOffset); camPosFinal = goalPos; camRotFinal = qq * qy * Quaternion(Degree(-ca->mPitch - mATilt), Vector3(1,0,0)); manualOrient = true; } break; } if (!manualOrient) // if !CAM_ExtAng { float dtmul = ca->mSpeed == 0 ? 1.0f : ca->mSpeed * time; if (ca->mType == CAM_Arena) { Vector3 Pos(0,0,0), goalPos = ca->mOffset; Pos = camPosFinal; //read last state (smooth) Pos += (goalPos - Pos) * dtmul; mAPitch += (ca->mPitch - mAPitch) * dtmul; mAYaw += (ca->mYaw - mAYaw) * dtmul; if (first) { Pos = goalPos; mAPitch = ca->mPitch; mAYaw = ca->mYaw; } camPosFinal = Pos; camRotFinal = Quaternion(Degree(mAYaw),Vector3(0,1,0)) * Quaternion(Degree(mAPitch),Vector3(1,0,0)); manualOrient = true; } else { if (first) pos = goalPos; Vector3 addPos,addLook; addPos = (goalPos - pos).normalisedCopy() * (goalPos - pos).length() * dtmul; if (addPos.squaredLength() > (goalPos - pos).squaredLength()) addPos = goalPos - pos; pos += addPos; camPosFinal = pos + ofs; goalLook = posGoal + ofs; if (first) { mLook = goalLook; } addLook = (goalLook - mLook) * dtmul;//Rot; mLook += addLook; } } //camLookFinal = mLook; if (!manualOrient) // CAM_Free or CAM_Follow { Vector3 zdir = camPosFinal - mLook; zdir.normalise(); Vector3 xVec = Vector3::UNIT_Y.crossProduct(zdir); xVec.normalise(); Vector3 yVec = zdir.crossProduct(xVec); yVec.normalise(); Quaternion q; q.FromAxes(xVec, yVec, zdir); camRotFinal = q; } // cast ray from car to camera, reduce dist if hit //------------------------------------------------------------------------------------------- Vector3 pp = camPosFinal; if (bounce) pp += posIn.camOfs * ca->mOfsMul * gPar.camBncScale * pSet->cam_bnc_mul; Vector3 p = posGoal; p.y += 1.f; //up //Vector3 d = camRotFinal * Vector3::UNIT_Z; d.normalise(); Vector3 d = pp - p; d.normalise(); if (!first && ca->mType != CAM_Arena) { MATHVECTOR<float,3> pos1(p.x,-p.z,p.y), dir(d.x,-d.z,d.y); //dir = dir.Normalize(); COLLISION_CONTACT ct; float maxLen = (p - pp).length(); //cam near world->CastRay(pos1, dir, maxLen,chassis, ct, 0,0, true, true, true/*+*/); //dbgLen = -maxLen; if (ct.GetColObj()) { float len = ct.GetDepth(); //dbgLen = len; len -= 0.2f + ct.GetNormal()[2]; ///par normal up, flat terrain, move closer if (len < maxLen) { Real dist = maxLen - len; if (dist > mDistReduce) mDistReduce = dist; } } } // save result in out posInfo posOut->camPos = mDistReduce > 0.0001f ? (pp - d * mDistReduce) : pp; posOut->camRot = camRotFinal; // smooth, back to normal dist if (mDistReduce > 0.f) mDistReduce -= time * 10.f; }
//----------------------------------------------------------------------- void PatchSurface::interpolateVertexData(void* lockedBuffer, size_t leftIdx, size_t rightIdx, size_t destIdx) { size_t vertexSize = mDeclaration->getVertexSize(0); const VertexElement* elemPos = mDeclaration->findElementBySemantic(VES_POSITION); const VertexElement* elemNorm = mDeclaration->findElementBySemantic(VES_NORMAL); const VertexElement* elemDiffuse = mDeclaration->findElementBySemantic(VES_DIFFUSE); const VertexElement* elemTex0 = mDeclaration->findElementBySemantic(VES_TEXTURE_COORDINATES, 0); const VertexElement* elemTex1 = mDeclaration->findElementBySemantic(VES_TEXTURE_COORDINATES, 1); float *pDestReal, *pLeftReal, *pRightReal; unsigned char *pDestChar, *pLeftChar, *pRightChar; unsigned char *pDest, *pLeft, *pRight; // Set up pointers & interpolate pDest = static_cast<unsigned char*>(lockedBuffer) + (vertexSize * destIdx); pLeft = static_cast<unsigned char*>(lockedBuffer) + (vertexSize * leftIdx); pRight = static_cast<unsigned char*>(lockedBuffer) + (vertexSize * rightIdx); // Position elemPos->baseVertexPointerToElement(pDest, &pDestReal); elemPos->baseVertexPointerToElement(pLeft, &pLeftReal); elemPos->baseVertexPointerToElement(pRight, &pRightReal); *pDestReal++ = (*pLeftReal++ + *pRightReal++) * 0.5f; *pDestReal++ = (*pLeftReal++ + *pRightReal++) * 0.5f; *pDestReal++ = (*pLeftReal++ + *pRightReal++) * 0.5f; if (elemNorm) { elemNorm->baseVertexPointerToElement(pDest, &pDestReal); elemNorm->baseVertexPointerToElement(pLeft, &pLeftReal); elemNorm->baseVertexPointerToElement(pRight, &pRightReal); Vector3 norm; norm.x = (*pLeftReal++ + *pRightReal++) * 0.5f; norm.y = (*pLeftReal++ + *pRightReal++) * 0.5f; norm.z = (*pLeftReal++ + *pRightReal++) * 0.5f; norm.normalise(); *pDestReal++ = norm.x; *pDestReal++ = norm.y; *pDestReal++ = norm.z; } if (elemDiffuse) { // Blend each byte individually elemDiffuse->baseVertexPointerToElement(pDest, &pDestChar); elemDiffuse->baseVertexPointerToElement(pLeft, &pLeftChar); elemDiffuse->baseVertexPointerToElement(pRight, &pRightChar); // 4 bytes to RGBA *pDestChar++ = static_cast<unsigned char>(((*pLeftChar++) + (*pRightChar++)) * 0.5); *pDestChar++ = static_cast<unsigned char>(((*pLeftChar++) + (*pRightChar++)) * 0.5); *pDestChar++ = static_cast<unsigned char>(((*pLeftChar++) + (*pRightChar++)) * 0.5); *pDestChar++ = static_cast<unsigned char>(((*pLeftChar++) + (*pRightChar++)) * 0.5); } if (elemTex0) { elemTex0->baseVertexPointerToElement(pDest, &pDestReal); elemTex0->baseVertexPointerToElement(pLeft, &pLeftReal); elemTex0->baseVertexPointerToElement(pRight, &pRightReal); for (size_t dim = 0; dim < VertexElement::getTypeCount(elemTex0->getType()); ++dim) *pDestReal++ = ((*pLeftReal++) + (*pRightReal++)) * 0.5f; } if (elemTex1) { elemTex1->baseVertexPointerToElement(pDest, &pDestReal); elemTex1->baseVertexPointerToElement(pLeft, &pLeftReal); elemTex1->baseVertexPointerToElement(pRight, &pRightReal); for (size_t dim = 0; dim < VertexElement::getTypeCount(elemTex1->getType()); ++dim) *pDestReal++ = ((*pLeftReal++) + (*pRightReal++)) * 0.5f; } }
//----------------------------------------------------------------------- void FocusedShadowCameraSetup::calculateShadowMappingMatrix(const SceneManager& sm, const Camera& cam, const Light& light, Matrix4 *out_view, Matrix4 *out_proj, Camera *out_cam) const { // get the shadow frustum's far distance Real shadowDist = light.getShadowFarDistance(); if (!shadowDist) { // need a shadow distance, make one up shadowDist = cam.getNearClipDistance() * 3000; } Real shadowOffset = shadowDist * sm.getShadowDirLightTextureOffset(); if (light.getType() == Light::LT_DIRECTIONAL) { // generate view matrix if requested if (out_view != NULL) { Vector3 pos; if (sm.getCameraRelativeRendering()) { pos = Vector3::ZERO; } else { pos = cam.getDerivedPosition(); } *out_view = buildViewMatrix(pos, light.getDerivedDirection(), cam.getDerivedUp()); } // generate projection matrix if requested if (out_proj != NULL) { *out_proj = Matrix4::getScale(1, 1, -1); //*out_proj = Matrix4::IDENTITY; } // set up camera if requested if (out_cam != NULL) { out_cam->setProjectionType(PT_ORTHOGRAPHIC); out_cam->setDirection(light.getDerivedDirection()); out_cam->setPosition(cam.getDerivedPosition()); out_cam->setFOVy(Degree(90)); out_cam->setNearClipDistance(shadowOffset); } } else if (light.getType() == Light::LT_POINT) { // target analogue to the default shadow textures // Calculate look at position // We want to look at a spot shadowOffset away from near plane // 0.5 is a little too close for angles Vector3 target = cam.getDerivedPosition() + (cam.getDerivedDirection() * shadowOffset); Vector3 lightDir = target - light.getDerivedPosition(); lightDir.normalise(); // generate view matrix if requested if (out_view != NULL) { *out_view = buildViewMatrix(light.getDerivedPosition(), lightDir, cam.getDerivedUp()); } // generate projection matrix if requested if (out_proj != NULL) { // set FOV to 120 degrees mTempFrustum->setFOVy(Degree(120)); mTempFrustum->setNearClipDistance(light._deriveShadowNearClipDistance(&cam)); mTempFrustum->setFarClipDistance(light._deriveShadowFarClipDistance(&cam)); *out_proj = mTempFrustum->getProjectionMatrix(); } // set up camera if requested if (out_cam != NULL) { out_cam->setProjectionType(PT_PERSPECTIVE); out_cam->setDirection(lightDir); out_cam->setPosition(light.getDerivedPosition()); out_cam->setFOVy(Degree(120)); out_cam->setNearClipDistance(light._deriveShadowNearClipDistance(&cam)); out_cam->setFarClipDistance(light._deriveShadowFarClipDistance(&cam)); } } else if (light.getType() == Light::LT_SPOTLIGHT) { // generate view matrix if requested if (out_view != NULL) { *out_view = buildViewMatrix(light.getDerivedPosition(), light.getDerivedDirection(), cam.getDerivedUp()); } // generate projection matrix if requested if (out_proj != NULL) { // set FOV slightly larger than spotlight range mTempFrustum->setFOVy(Ogre::Math::Clamp<Radian>(light.getSpotlightOuterAngle() * 1.2, Radian(0), Radian(Math::PI/2.0f))); mTempFrustum->setNearClipDistance(light._deriveShadowNearClipDistance(&cam)); mTempFrustum->setFarClipDistance(light._deriveShadowFarClipDistance(&cam)); *out_proj = mTempFrustum->getProjectionMatrix(); } // set up camera if requested if (out_cam != NULL) { out_cam->setProjectionType(PT_PERSPECTIVE); out_cam->setDirection(light.getDerivedDirection()); out_cam->setPosition(light.getDerivedPosition()); out_cam->setFOVy(Ogre::Math::Clamp<Radian>(light.getSpotlightOuterAngle() * 1.2, Radian(0), Radian(Math::PI/2.0f))); out_cam->setNearClipDistance(light._deriveShadowNearClipDistance(&cam)); out_cam->setFarClipDistance(light._deriveShadowFarClipDistance(&cam)); } } }
//----------------------------------------------------------------------- void SceneNode::setDirection(const Vector3& vec, TransformSpace relativeTo, const Vector3& localDirectionVector) { // Do nothing if given a zero vector if (vec == Vector3::ZERO) return; // The direction we want the local direction point to Vector3 targetDir = vec.normalisedCopy(); // Transform target direction to world space switch (relativeTo) { case TS_PARENT: if (mInheritOrientation) { if (mParent) { targetDir = mParent->_getDerivedOrientation() * targetDir; } } break; case TS_LOCAL: targetDir = _getDerivedOrientation() * targetDir; break; case TS_WORLD: // default orientation break; } // Calculate target orientation relative to world space Quaternion targetOrientation; if( mYawFixed ) { // Calculate the quaternion for rotate local Z to target direction Vector3 xVec = mYawFixedAxis.crossProduct(targetDir); xVec.normalise(); Vector3 yVec = targetDir.crossProduct(xVec); yVec.normalise(); Quaternion unitZToTarget = Quaternion(xVec, yVec, targetDir); if (localDirectionVector == Vector3::NEGATIVE_UNIT_Z) { // Specail case for avoid calculate 180 degree turn targetOrientation = Quaternion(-unitZToTarget.y, -unitZToTarget.z, unitZToTarget.w, unitZToTarget.x); } else { // Calculate the quaternion for rotate local direction to target direction Quaternion localToUnitZ = localDirectionVector.getRotationTo(Vector3::UNIT_Z); targetOrientation = unitZToTarget * localToUnitZ; } } else { const Quaternion& currentOrient = _getDerivedOrientation(); // Get current local direction relative to world space Vector3 currentDir = currentOrient * localDirectionVector; if ((currentDir+targetDir).squaredLength() < 0.00005f) { // Oops, a 180 degree turn (infinite possible rotation axes) // Default to yaw i.e. use current UP targetOrientation = Quaternion(-currentOrient.y, -currentOrient.z, currentOrient.w, currentOrient.x); } else { // Derive shortest arc to new direction Quaternion rotQuat = currentDir.getRotationTo(targetDir); targetOrientation = rotQuat * currentOrient; } } // Set target orientation, transformed to parent space if (mParent && mInheritOrientation) setOrientation(mParent->_getDerivedOrientation().UnitInverse() * targetOrientation); else setOrientation(targetOrientation); }
//----------------------------------------------------------------------- const PlaneBoundedVolumeList& Light::_getFrustumClipVolumes(const Camera* const cam) const { // Homogenous light position Vector4 lightPos = getAs4DVector(); // 3D version (not the same as _getDerivedPosition, is -direction for // directional lights) Vector3 lightPos3 = Vector3(lightPos.x, lightPos.y, lightPos.z); const Vector3 *clockwiseVerts[4]; // Get worldspace frustum corners const Vector3* corners = cam->getWorldSpaceCorners(); int winding = cam->isReflected() ? +1 : -1; bool infiniteViewDistance = (cam->getFarClipDistance() == 0); mFrustumClipVolumes.clear(); for (unsigned short n = 0; n < 6; ++n) { // Skip far plane if infinite view frustum if (infiniteViewDistance && n == FRUSTUM_PLANE_FAR) continue; const Plane& plane = cam->getFrustumPlane(n); Vector4 planeVec(plane.normal.x, plane.normal.y, plane.normal.z, plane.d); // planes face inwards, we need to know if light is on negative side Real d = planeVec.dotProduct(lightPos); if (d < -1e-06) { // Ok, this is a valid one // clockwise verts mean we can cross-product and always get normals // facing into the volume we create mFrustumClipVolumes.push_back(PlaneBoundedVolume()); PlaneBoundedVolume& vol = mFrustumClipVolumes.back(); switch(n) { case(FRUSTUM_PLANE_NEAR): clockwiseVerts[0] = corners + 3; clockwiseVerts[1] = corners + 2; clockwiseVerts[2] = corners + 1; clockwiseVerts[3] = corners + 0; break; case(FRUSTUM_PLANE_FAR): clockwiseVerts[0] = corners + 7; clockwiseVerts[1] = corners + 6; clockwiseVerts[2] = corners + 5; clockwiseVerts[3] = corners + 4; break; case(FRUSTUM_PLANE_LEFT): clockwiseVerts[0] = corners + 2; clockwiseVerts[1] = corners + 6; clockwiseVerts[2] = corners + 5; clockwiseVerts[3] = corners + 1; break; case(FRUSTUM_PLANE_RIGHT): clockwiseVerts[0] = corners + 7; clockwiseVerts[1] = corners + 3; clockwiseVerts[2] = corners + 0; clockwiseVerts[3] = corners + 4; break; case(FRUSTUM_PLANE_TOP): clockwiseVerts[0] = corners + 0; clockwiseVerts[1] = corners + 1; clockwiseVerts[2] = corners + 5; clockwiseVerts[3] = corners + 4; break; case(FRUSTUM_PLANE_BOTTOM): clockwiseVerts[0] = corners + 7; clockwiseVerts[1] = corners + 6; clockwiseVerts[2] = corners + 2; clockwiseVerts[3] = corners + 3; break; }; // Build a volume // Iterate over world points and form side planes Vector3 normal; Vector3 lightDir; for (unsigned int i = 0; i < 4; ++i) { // Figure out light dir lightDir = lightPos3 - (*(clockwiseVerts[i]) * lightPos.w); Vector3 edgeDir = *(clockwiseVerts[i]) - *(clockwiseVerts[(i+winding)%4]); // Cross with anticlockwise corner, therefore normal points in normal = edgeDir.crossProduct(lightDir); normal.normalise(); vol.planes.push_back(Plane(normal, *(clockwiseVerts[i]))); } // Now do the near plane (this is the plane of the side we're // talking about, with the normal inverted (d is already interpreted as -ve) vol.planes.push_back( Plane(-plane.normal, plane.d) ); // Finally, for a point/spot light we can add a sixth plane // This prevents false positives from behind the light if (mLightType != LT_DIRECTIONAL) { // re-use our own plane normal vol.planes.push_back(Plane(plane.normal, lightPos3)); } } } return mFrustumClipVolumes; }
bool App::frameStart(Real time) { PROFILER.beginBlock(" frameSt"); fLastFrameDT = time; // input for (int i=0; i<4; ++i) { boost::lock_guard<boost::mutex> lock(input->mPlayerInputStateMutex); for (int a = 0; a < NumPlayerActions; ++a) input->mPlayerInputState[i][a] = mInputCtrlPlayer[i]->getChannel(a)->getValue(); } if (imgBack && pGame) // show/hide background image { bool backImgVis = !bLoading && pGame->cars.empty(); imgBack->setVisible(backImgVis); } // multi thread if (pSet->multi_thr == 1 && pGame && !bLoading) { updatePoses(time); } /// graphs update -._/\_-. if (pSet->show_graphs && graphs.size() > 0) { GraphsNewVals(); UpdateGraphs(); } //................................................................... ///* tire edit */ if (pSet->graphs_type == Gh_TireEdit && carModels.size() > 0) { int k = (isKey(SDL_SCANCODE_1) || isKey(SDL_SCANCODE_KP_DIVIDE) ? -1 : 0) + (isKey(SDL_SCANCODE_2) || isKey(SDL_SCANCODE_KP_MULTIPLY) ? 1 : 0); if (k) { double mul = shift ? 0.2 : (ctrl ? 4.0 : 1.0); mul *= 0.005; // par CARDYNAMICS& cd = carModels[0]->pCar->dynamics; CARTIRE* tire = cd.GetTire(FRONT_LEFT); if (iEdTire == 1) // longit | { Dbl& val = tire->longitudinal[iCurLong]; // modify 1st val += mul*k * (1 + abs(val)); for (int i=1; i<4; ++i) cd.GetTire(WHEEL_POSITION(i))->longitudinal[iCurLong] = val; // copy for rest } else if (iEdTire == 0) // lateral -- { Dbl& val = tire->lateral[iCurLat]; val += mul*k * (1 + abs(val)); for (int i=1; i<4; ++i) cd.GetTire(WHEEL_POSITION(i))->lateral[iCurLat] = val; } else // align o { Dbl& val = tire->aligning[iCurAlign]; val += mul*k * (1 + abs(val)); for (int i=1; i<4; ++i) cd.GetTire(WHEEL_POSITION(i))->aligning[iCurAlign] = val; } // update hat, 1st tire->CalculateSigmaHatAlphaHat(); for (int i=1; i<4; ++i) // copy for rest { cd.GetTire(WHEEL_POSITION(i))->sigma_hat = tire->sigma_hat; cd.GetTire(WHEEL_POSITION(i))->alpha_hat = tire->alpha_hat; } iUpdTireGr = 1; } } //................................................................... /// gui gui->GuiUpdate(); if (bWindowResized) { bWindowResized = false; gcom->ResizeOptWnd(); gcom->SizeGUI(); gcom->updTrkListDim(); gui->updChampListDim(); // resize lists bRecreateHUD = true; if (mSplitMgr) // reassign car cameras from new viewports { std::list<Camera*>::iterator it = mSplitMgr->mCameras.begin(); for (int i=0; i < carModels.size(); ++i) if (carModels[i]->fCam && it != mSplitMgr->mCameras.end()) { carModels[i]->fCam->mCamera = *it; ++it; } } if (!mSplitMgr->mCameras.empty()) { Camera* cam1 = *mSplitMgr->mCameras.begin(); scn->mWaterRTT->setViewerCamera(cam1); if (scn->grass) scn->grass->setCamera(cam1); if (scn->trees) scn->trees->setCamera(cam1); } } // hud update sizes, after res change if (bRecreateHUD) { bRecreateHUD = false; hud->Destroy(); hud->Create(); } if (bSizeHUD) { bSizeHUD = false; hud->Size(); } if (bLoading) { NewGameDoLoad(); PROFILER.endBlock(" frameSt"); return true; } else { /// loading end ------ const int iFr = 3; if (iLoad1stFrames >= 0) { ++iLoad1stFrames; if (iLoad1stFrames == iFr) { LoadingOff(); // hide loading overlay mSplitMgr->mGuiViewport->setClearEveryFrame(true, FBT_DEPTH); gui->Ch_LoadEnd(); bLoadingEnd = true; iLoad1stFrames = -1; // for refl } }else if (iLoad1stFrames >= -1) { --iLoad1stFrames; // -2 end imgLoad->setVisible(false); // hide back imgs if (imgBack) imgBack->setVisible(false); } // input if (isFocGui && pSet->inMenu == MNU_Options && !pSet->isMain && mWndTabsOpts->getIndexSelected() == TABo_Input) gui->UpdateInputBars(); // keys up/dn, for lists static float dirU = 0.f,dirD = 0.f; if (isFocGui && !pSet->isMain && !isTweak()) { if (isKey(SDL_SCANCODE_UP) ||isKey(SDL_SCANCODE_KP_8)) dirD += time; else if (isKey(SDL_SCANCODE_DOWN)||isKey(SDL_SCANCODE_KP_2)) dirU += time; else { dirU = 0.f; dirD = 0.f; } int d = ctrl ? 4 : 1; if (dirU > 0.0f) { gui->LNext( d); dirU = -0.2f; } if (dirD > 0.0f) { gui->LNext(-d); dirD = -0.2f; } } /// Gui updates from Networking gui->UpdGuiNetw(); // replay forward,backward keys if (bRplPlay) { isFocRpl = ctrl; bool le = isKey(SDL_SCANCODE_LEFTBRACKET), ri = isKey(SDL_SCANCODE_RIGHTBRACKET), ctrlN = ctrl && (le || ri); int ta = ((le || gui->bRplBack) ? -2 : 0) + ((ri || gui->bRplFwd) ? 2 : 0); if (ta) { double tadd = ta; tadd *= (shift ? 0.2 : 1) * (ctrlN ? 4 : 1) * (alt ? 8 : 1); // multipliers if (!bRplPause) tadd -= 1; // play compensate double t = pGame->timer.GetReplayTime(0), len = replay.GetTimeLength(); t += tadd * time; // add if (t < 0.0) t += len; // cycle if (t > len) t -= len; pGame->timer.SetReplayTime(0, t); } } if (!pGame) { PROFILER.endBlock(" frameSt"); return false; } if (pSet->multi_thr == 0) DoNetworking(); // single thread, sim on draw bool ret = true; if (pSet->multi_thr == 0) { ret = pGame->OneLoop(time); if (!ret) mShutDown = true; updatePoses(time); } // align checkpoint arrow // move in front of camera if (pSet->check_arrow && hud->arrow.node && !bRplPlay && !carModels.empty()) { FollowCamera* cam = carModels[0]->fCam; Vector3 pos = cam->mCamera->getPosition(); Vector3 dir = cam->mCamera->getDirection(); dir.normalise(); Vector3 up = cam->mCamera->getUp(); up.normalise(); Vector3 arrowPos = pos + 10.0f * dir + 3.5f*up; hud->arrow.node->setPosition(arrowPos); // animate bool bFirstFrame = carModels.front()->bGetStPos; if (bFirstFrame) // 1st frame: dont animate hud->arrow.qCur = hud->arrow.qEnd; else hud->arrow.qCur = Quaternion::Slerp(time*5, hud->arrow.qStart, hud->arrow.qEnd, true); hud->arrow.nodeRot->setOrientation(hud->arrow.qCur); // look down -y a bit so we can see the arrow better hud->arrow.nodeRot->pitch(Degree(-20), SceneNode::TS_LOCAL); } // cam info text if (pSet->show_cam && !carModels.empty() && hud->txCamInfo) { FollowCamera* cam = carModels[0]->fCam; if (cam) { bool vis = cam->updInfo(time) && !isFocGui; if (vis) hud->txCamInfo->setCaption(String(cam->ss)); hud->txCamInfo->setVisible(vis); } } // update all cube maps PROFILER.beginBlock("g.refl"); for (std::vector<CarModel*>::iterator it=carModels.begin(); it!=carModels.end(); it++) if (!(*it)->isGhost() && (*it)->pReflect) (*it)->pReflect->Update(iLoad1stFrames == -1); PROFILER.endBlock("g.refl"); // trees PROFILER.beginBlock("g.veget"); if (scn->road) { if (scn->grass) scn->grass->update(); if (scn->trees) scn->trees->update(); } PROFILER.endBlock("g.veget"); // road upd lods if (scn->road) { //PROFILER.beginBlock("g.road"); // below 0.0 ms // more than 1: in pre viewport, each frame if (mSplitMgr->mNumViewports == 1) { roadUpdTm += time; if (roadUpdTm > 0.1f) // interval [sec] { roadUpdTm = 0.f; scn->road->UpdLodVis(pSet->road_dist); } } //PROFILER.endBlock("g.road"); } //** bullet bebug draw if (dbgdraw) { // DBG_DrawWireframe dbgdraw->setDebugMode(pSet->bltDebug ? 1 /*+(1<<13) 255*/ : 0); dbgdraw->step(); } /// terrain mtr from blend maps // now in CarModel::Update //UpdWhTerMtr(pCar); // stop rain/snow when paused if (scn->pr && scn->pr2 && pGame) { if (pGame->pause) { scn->pr->setSpeedFactor(0.f); scn->pr2->setSpeedFactor(0.f); } else{ scn->pr->setSpeedFactor(1.f); scn->pr2->setSpeedFactor(1.f); } } // update shader time mTimer += time; mFactory->setSharedParameter("windTimer", sh::makeProperty <sh::FloatValue>(new sh::FloatValue(mTimer))); mFactory->setSharedParameter("waterTimer", sh::makeProperty <sh::FloatValue>(new sh::FloatValue(mTimer))); ///() grass sphere pos if (!carModels.empty()) { Real r = 1.7; r *= r; //par const Vector3* p = &carModels[0]->posSph[0]; mFactory->setSharedParameter("posSph0", sh::makeProperty <sh::Vector4>(new sh::Vector4(p->x,p->y,p->z,r))); p = &carModels[0]->posSph[1]; mFactory->setSharedParameter("posSph1", sh::makeProperty <sh::Vector4>(new sh::Vector4(p->x,p->y,p->z,r))); }else { mFactory->setSharedParameter("posSph0", sh::makeProperty <sh::Vector4>(new sh::Vector4(0,0,500,-1))); mFactory->setSharedParameter("posSph1", sh::makeProperty <sh::Vector4>(new sh::Vector4(0,0,500,-1))); } // Signal loading finished to the peers if (mClient && bLoadingEnd) { bLoadingEnd = false; mClient->loadingFinished(); } PROFILER.endBlock(" frameSt"); return ret; } PROFILER.endBlock(" frameSt"); }
//----------------------------------------------------------------------- const PlaneBoundedVolume& Light::_getNearClipVolume(const Camera* const cam) const { // First check if the light is close to the near plane, since // in this case we have to build a degenerate clip volume mNearClipVolume.planes.clear(); mNearClipVolume.outside = Plane::NEGATIVE_SIDE; Real n = cam->getNearClipDistance(); // Homogenous position Vector4 lightPos = getAs4DVector(); // 3D version (not the same as _getDerivedPosition, is -direction for // directional lights) Vector3 lightPos3 = Vector3(lightPos.x, lightPos.y, lightPos.z); // Get eye-space light position // use 4D vector so directional lights still work Vector4 eyeSpaceLight = cam->getViewMatrix() * lightPos; // Find distance to light, project onto -Z axis Real d = eyeSpaceLight.dotProduct( Vector4(0, 0, -1, -n) ); #define THRESHOLD 1e-6 if (d > THRESHOLD || d < -THRESHOLD) { // light is not too close to the near plane // First find the worldspace positions of the corners of the viewport const Vector3 *corner = cam->getWorldSpaceCorners(); int winding = (d < 0) ^ cam->isReflected() ? +1 : -1; // Iterate over world points and form side planes Vector3 normal; Vector3 lightDir; for (unsigned int i = 0; i < 4; ++i) { // Figure out light dir lightDir = lightPos3 - (corner[i] * lightPos.w); // Cross with anticlockwise corner, therefore normal points in normal = (corner[i] - corner[(i+winding)%4]) .crossProduct(lightDir); normal.normalise(); mNearClipVolume.planes.push_back(Plane(normal, corner[i])); } // Now do the near plane plane normal = cam->getFrustumPlane(FRUSTUM_PLANE_NEAR).normal; if (d < 0) { // Behind near plane normal = -normal; } const Vector3& cameraPos = cam->getDerivedPosition(); mNearClipVolume.planes.push_back(Plane(normal, cameraPos)); // Finally, for a point/spot light we can add a sixth plane // This prevents false positives from behind the light if (mLightType != LT_DIRECTIONAL) { // Direction from light perpendicular to near plane mNearClipVolume.planes.push_back(Plane(-normal, lightPos3)); } } else { // light is close to being on the near plane // degenerate volume including the entire scene // we will always require light / dark caps mNearClipVolume.planes.push_back(Plane(Vector3::UNIT_Z, -n)); mNearClipVolume.planes.push_back(Plane(-Vector3::UNIT_Z, n)); } return mNearClipVolume; }
//---------------------------------------------------------------------() void Camera::getCameraToViewportBoxVolume(Real screenLeft, Real screenTop, Real screenRight, Real screenBottom, PlaneBoundedVolume* outVolume, bool includeFarPlane) { outVolume->planes.clear(); if (mProjType == PT_PERSPECTIVE) { // Use the corner rays to generate planes Ray ul = getCameraToViewportRay(screenLeft, screenTop); Ray ur = getCameraToViewportRay(screenRight, screenTop); Ray bl = getCameraToViewportRay(screenLeft, screenBottom); Ray br = getCameraToViewportRay(screenRight, screenBottom); Vector3 normal; // top plane normal = ul.getDirection().crossProduct(ur.getDirection()); normal.normalise(); outVolume->planes.push_back( Plane(normal, getDerivedPosition())); // right plane normal = ur.getDirection().crossProduct(br.getDirection()); normal.normalise(); outVolume->planes.push_back( Plane(normal, getDerivedPosition())); // bottom plane normal = br.getDirection().crossProduct(bl.getDirection()); normal.normalise(); outVolume->planes.push_back( Plane(normal, getDerivedPosition())); // left plane normal = bl.getDirection().crossProduct(ul.getDirection()); normal.normalise(); outVolume->planes.push_back( Plane(normal, getDerivedPosition())); } else { // ortho planes are parallel to frustum planes Ray ul = getCameraToViewportRay(screenLeft, screenTop); Ray br = getCameraToViewportRay(screenRight, screenBottom); updateFrustumPlanes(); outVolume->planes.push_back( Plane(mFrustumPlanes[FRUSTUM_PLANE_TOP].normal, ul.getOrigin())); outVolume->planes.push_back( Plane(mFrustumPlanes[FRUSTUM_PLANE_RIGHT].normal, br.getOrigin())); outVolume->planes.push_back( Plane(mFrustumPlanes[FRUSTUM_PLANE_BOTTOM].normal, br.getOrigin())); outVolume->planes.push_back( Plane(mFrustumPlanes[FRUSTUM_PLANE_LEFT].normal, ul.getOrigin())); } // near & far plane applicable to both projection types outVolume->planes.push_back(getFrustumPlane(FRUSTUM_PLANE_NEAR)); if (includeFarPlane) outVolume->planes.push_back(getFrustumPlane(FRUSTUM_PLANE_FAR)); }
//----------------------------------------------------------------------- void Camera::setDirection(const Vector3& vec) { // Do nothing if given a zero vector // (Replaced assert since this could happen with auto tracking camera and // camera passes through the lookAt point) if (vec == Vector3::ZERO) return; // Remember, camera points down -Z of local axes! // Therefore reverse direction of direction vector before determining local Z Vector3 zAdjustVec = -vec; zAdjustVec.normalise(); Quaternion targetWorldOrientation; if( mYawFixed ) { Vector3 xVec = mYawFixedAxis.crossProduct( zAdjustVec ); xVec.normalise(); Vector3 yVec = zAdjustVec.crossProduct( xVec ); yVec.normalise(); targetWorldOrientation.FromAxes( xVec, yVec, zAdjustVec ); } else { // Get axes from current quaternion Vector3 axes[3]; updateView(); mRealOrientation.ToAxes(axes); Quaternion rotQuat; if ( (axes[2]+zAdjustVec).squaredLength() < 0.00005f) { // Oops, a 180 degree turn (infinite possible rotation axes) // Default to yaw i.e. use current UP rotQuat.FromAngleAxis(Radian(Math::PI), axes[1]); } else { // Derive shortest arc to new direction rotQuat = axes[2].getRotationTo(zAdjustVec); } targetWorldOrientation = rotQuat * mRealOrientation; } // transform to parent space if (mParentNode) { mOrientation = mParentNode->_getDerivedOrientation().Inverse() * targetWorldOrientation; } else { mOrientation = targetWorldOrientation; } // TODO If we have a fixed yaw axis, we mustn't break it by using the // shortest arc because this will sometimes cause a relative yaw // which will tip the camera invalidateView(); }
//----------------------------------------------------------------------- Vector3 Math::calculateBasicFaceNormal(const Vector3& v1, const Vector3& v2, const Vector3& v3) { Vector3 normal = (v2 - v1).crossProduct(v3 - v1); normal.normalise(); return normal; }
//--------------------------------------------------------------------- Real ProgressiveMesh::computeEdgeCollapseCost(PMVertex *src, PMVertex *dest) { // if we collapse edge uv by moving src to dest then how // much different will the model change, i.e. how much "error". // The method of determining cost was designed in order // to exploit small and coplanar regions for // effective polygon reduction. Vector3 edgeVector = src->position - dest->position; Real cost; Real curvature = 0.001f; // find the "sides" triangles that are on the edge uv PMVertex::FaceList sides; PMVertex::FaceList::iterator srcface, srcfaceEnd; srcfaceEnd = src->face.end(); // Iterate over src's faces and find 'sides' of the shared edge which is being collapsed for(srcface = src->face.begin(); srcface != srcfaceEnd; ++srcface) { // Check if this tri also has dest in it (shared edge) if( (*srcface)->hasCommonVertex(dest) ) { sides.insert(*srcface); } } // Special cases // If we're looking at a border vertex if(src->isBorder()) { if (sides.size() > 1) { // src is on a border, but the src-dest edge has more than one tri on it // So it must be collapsing inwards // Mark as very high-value cost // curvature = 1.0f; cost = 1.0f; } else { // Collapsing ALONG a border // We can't use curvature to measure the effect on the model // Instead, see what effect it has on 'pulling' the other border edges // The more colinear, the less effect it will have // So measure the 'kinkiness' (for want of a better term) // Normally there can be at most 1 other border edge attached to this // However in weird cases there may be more, so find the worst Vector3 collapseEdge, otherBorderEdge; Real kinkiness, maxKinkiness; PMVertex::NeighborList::iterator n, nend; nend = src->neighbor.end(); maxKinkiness = 0.0f; edgeVector.normalise(); collapseEdge = edgeVector; for (n = src->neighbor.begin(); n != nend; ++n) { if (*n != dest && (*n)->isManifoldEdgeWith(src)) { otherBorderEdge = src->position - (*n)->position; otherBorderEdge.normalise(); // This time, the nearer the dot is to -1, the better, because that means // the edges are opposite each other, therefore less kinkiness // Scale into [0..1] kinkiness = (otherBorderEdge.dotProduct(collapseEdge) + 1.002f) * 0.5f; maxKinkiness = std::max(kinkiness, maxKinkiness); } } cost = maxKinkiness; } } else // not a border { // Standard inner vertex // Calculate curvature // use the triangle facing most away from the sides // to determine our curvature term // Iterate over src's faces again for(srcface = src->face.begin(); srcface != srcfaceEnd; ++srcface) { Real mincurv = 1.0f; // curve for face i and closer side to it // Iterate over the sides PMVertex::FaceList::iterator sidesFace, sidesFaceEnd; sidesFaceEnd = sides.end(); for(sidesFace = sides.begin(); sidesFace != sidesFaceEnd; ++sidesFace) { // Dot product of face normal gives a good delta angle Real dotprod = (*srcface)->normal.dotProduct( (*sidesFace)->normal ); // NB we do (1-..) to invert curvature where 1 is high curvature [0..1] // Whilst dot product is high when angle difference is low mincurv = std::min(mincurv,(1.002f - dotprod) * 0.5f); } curvature = std::max(curvature, mincurv); } cost = curvature; } // check for texture seam ripping if (src->seam && !dest->seam) { cost = 1.0f; } // Check for singular triangle destruction // If src and dest both only have 1 triangle (and it must be a shared one) // then this would destroy the shape, so don't do this if (src->face.size() == 1 && dest->face.size() == 1) { cost = NEVER_COLLAPSE_COST; } // Degenerate case check // Are we going to invert a face normal of one of the neighbouring faces? // Can occur when we have a very small remaining edge and collapse crosses it // Look for a face normal changing by > 90 degrees for(srcface = src->face.begin(); srcface != srcfaceEnd; ++srcface) { // Ignore the deleted faces (those including src & dest) if( !(*srcface)->hasCommonVertex(dest) ) { // Test the new face normal PMVertex *v0, *v1, *v2; // Replace src with dest wherever it is v0 = ( (*srcface)->vertex[0]->commonVertex == src) ? dest : (*srcface)->vertex[0]->commonVertex; v1 = ( (*srcface)->vertex[1]->commonVertex == src) ? dest : (*srcface)->vertex[1]->commonVertex; v2 = ( (*srcface)->vertex[2]->commonVertex == src) ? dest : (*srcface)->vertex[2]->commonVertex; // Cross-product 2 edges Vector3 e1 = v1->position - v0->position; Vector3 e2 = v2->position - v1->position; Vector3 newNormal = e1.crossProduct(e2); newNormal.normalise(); // Dot old and new face normal // If < 0 then more than 90 degree difference if (newNormal.dotProduct( (*srcface)->normal ) < 0.0f ) { // Don't do it! cost = NEVER_COLLAPSE_COST; break; // No point continuing } } } assert (cost >= 0); return cost; }