/** * When drawing in 2D, this method is invoked automatically to dynamically scale the * node so that it appears with the correct perspective. This is required because * when drawing as a 2D overlay, the node will not otherwise be drawn with the * perspective of the 3D billboard's location. */ void CC3Billboard::align2DToCamera( CC3Camera* camera ) { // Use the camera to project the 3D location of this node // into 2D and then set the billboard to that position camera->projectNode( this ); CCPoint pPos = getProjectedPosition(); m_pBillboard->setPosition( ccpAdd(pPos, m_offsetPosition) ); CCPoint newBBScale; // If only one non-zero scale is allowed (min == max), ensure that the billboard is set to that scale if (!m_minimumBillboardScale.equals(CCPointZero) && m_maximumBillboardScale.equals(m_minimumBillboardScale)) { newBBScale = m_minimumBillboardScale; //LogTrace(@"Projecting billboard %@ to %@ with fixed scaling %@", self, // NSStringFromCGPoint(pPos), NSStringFromCGPoint(newBBScale)); } else { // Calc how much to scale the billboard by comparing distance from camera to billboard // and camera to the defined unity-scale distance. Neither may be smaller than the near // clipping plane. GLfloat camNear = camera->getNearClippingDistance(); GLfloat camDist = MAX( getGlobalLocation().distance( camera->getGlobalLocation() ), camNear); GLfloat unityDist = MAX(getUnityScaleDistance(), camNear); GLfloat distScale = unityDist / camDist; newBBScale.x = distScale; newBBScale.y = distScale; // Ensure result is within any defined min and max scales newBBScale.x = MAX(newBBScale.x, m_minimumBillboardScale.x); newBBScale.y = MAX(newBBScale.y, m_minimumBillboardScale.y); newBBScale.x = (m_maximumBillboardScale.x != 0.0) ? MIN(newBBScale.x, m_maximumBillboardScale.x) : newBBScale.x; newBBScale.y = (m_maximumBillboardScale.y != 0.0) ? MIN(newBBScale.y, m_maximumBillboardScale.y) : newBBScale.y; // Factor in the scale of this CC3Billboard node. CC3Vector myScale = getScale(); newBBScale.x *= myScale.x; newBBScale.y *= myScale.y; //LogTrace(@"Projecting billboard %@ to %@, scaled to %@ using distance %.2f and unity distance %.2f", // self, NSStringFromCGPoint(pPos), NSStringFromCGPoint(newBBScale), camDist, unityDist); } // If consistency across devices is desired, adjust size of 2D billboard so that // it appears the same size relative to 3D artifacts across all device resolutions if (m_shouldNormalizeScaleToDevice) newBBScale = ccpMult(newBBScale, deviceScaleFactor()); // Set the new scale only if it has changed. if (m_pBillboard->getScaleX() != newBBScale.x) m_pBillboard->setScaleX( newBBScale.x ); if (m_pBillboard->getScaleY() != newBBScale.y) m_pBillboard->setScaleY( newBBScale.y ); }
CC3Ray CC3Camera::unprojectPoint( const CCPoint& cc2Point ) { // Scale from UI points to GL points CCPoint glPoint = ccpMult(cc2Point, CCDirector::sharedDirector()->getContentScaleFactor()); // Express the glPoint X & Y as proportion of the viewport dimensions. CC3Viewport vp = getViewport(); GLfloat xp = ((2.0f * glPoint.x) / vp.w) - 1; GLfloat yp = ((2.0f * glPoint.y) / vp.h) - 1; // Ensure that the camera's frustum is up to date, and then map the proportional point // on the viewport to its position on the near clipping rectangle. The Z-coordinate is // negative because the camera points down the negative Z axis in its local coordinates. buildProjection(); CC3Vector pointLocNear = cc3v(_frustum->getRight() * xp, _frustum->getTop() * yp, -_frustum->getNear()); CC3Ray ray; if ( isUsingParallelProjection() ) { // The location on the near clipping plane is relative to the camera's // local coordinates. Convert it to global coordinates before returning. // The ray direction is straight out from that global location in the // camera's globalForwardDirection. ray.startLocation = getGlobalTransformMatrix()->transformLocation( pointLocNear ); ray.direction = getGlobalForwardDirection(); } else { // The location on the near clipping plane is relative to the camera's local // coordinates. Since the camera's origin is zero in its local coordinates, // this point on the near clipping plane forms a directional vector from the // camera's origin. Rotate this directional vector with the camera's rotation // matrix to convert it to a global direction vector in global coordinates. // Thanks to Cocos3D forum user Rogs for suggesting the use of the globalRotationMatrix. ray.startLocation = getGlobalLocation(); ray.direction = getGlobalRotationMatrix()->transformDirection( pointLocNear ); } // Ensure the direction component is normalized before returning. ray.direction = ray.direction.normalize(); //LogTrace(@"%@ unprojecting point %@ to near plane location %@ and to ray starting at %@ and pointing towards %@", // [self class], NSStringFromCGPoint(glPoint), NSStringFromCC3Vector(pointLocNear), // NSStringFromCC3Vector(ray.startLocation), NSStringFromCC3Vector(ray.direction)); return ray; }
/** * When drawing in 3D, thd 2D node will automatically be drawn with the correct * perspective projection, but this method is invoked automatically to enforce * the minimum and maximum scales. */ void CC3Billboard::align3DToCamera( CC3Camera* camera ) { // Don't waste time if no min or max scale has been set. if (m_minimumBillboardScale.equals(CCPointZero) && m_maximumBillboardScale.equals(CCPointZero)) return; GLfloat camNear = camera->getNearClippingDistance(); GLfloat unityDist = MAX(getUnityScaleDistance(), camNear); GLfloat camDist = MAX(getGlobalLocation().distance(camera->getGlobalLocation()), camNear); CCPoint newBBScale = ccp(m_pBillboard->getScaleX(), m_pBillboard->getScaleY()); if (m_minimumBillboardScale.x > 0.0) { GLfloat minScaleDistX = unityDist / m_minimumBillboardScale.x; newBBScale.x = (camDist > minScaleDistX) ? (camDist / minScaleDistX) : 1.0f; } if (m_minimumBillboardScale.y > 0.0) { GLfloat minScaleDistY = unityDist / m_minimumBillboardScale.y; newBBScale.y = (camDist > minScaleDistY) ? (camDist / minScaleDistY) : 1.0f; } if (m_maximumBillboardScale.x > 0.0) { GLfloat maxScaleDistX = unityDist / m_maximumBillboardScale.x; newBBScale.x = (camDist < maxScaleDistX) ? (camDist / maxScaleDistX) : 1.0f; } if (m_maximumBillboardScale.y > 0.0) { GLfloat maxScaleDistY = unityDist / m_maximumBillboardScale.y; newBBScale.y = (camDist < maxScaleDistY) ? (camDist / maxScaleDistY) : 1.0f; } // Set the new scale only if it has changed. if (m_pBillboard->getScaleX() != newBBScale.x) m_pBillboard->setScaleX( newBBScale.x ); if (m_pBillboard->getScaleY() != newBBScale.y) m_pBillboard->setScaleY( newBBScale.y ); }
CC3Vector CC3Camera::getProjectLocation( const CC3Vector& a3DLocation ) { // Convert specified location to a 4D homogeneous location vector // and transform it using the modelview and projection matrices. CC3Vector4 hLoc; hLoc.fromLocation(a3DLocation); hLoc = getViewMatrix()->transformHomogeneousVector( hLoc ); hLoc = getProjectionMatrix()->transformHomogeneousVector( hLoc ); // Convert projected 4D vector back to 3D. CC3Vector projectedLoc = hLoc.homogenizedCC3Vector(); // The projected vector is in a projection coordinate space between -1 and +1 on all axes. // Normalize the vector so that each component is between 0 and 1 by calculating ( v = (v + 1) / 2 ). projectedLoc = projectedLoc.average( CC3Vector::kCC3VectorUnitCube ); CCAssert(_viewport.h > 0 && _viewport.w > 0, "%CC3Camera does not have a valid viewport"); // Map the X & Y components of the projected location (now between 0 and 1) to display coordinates. GLfloat g2p = 1.0f / CCDirector::sharedDirector()->getContentScaleFactor(); projectedLoc.x *= ((GLfloat)_viewport.w * g2p); projectedLoc.y *= ((GLfloat)_viewport.h * g2p); // Using the vector from the camera to the 3D location, determine whether or not the // 3D location is in front of the camera by using the dot-product of that vector and // the direction the camera is pointing. Set the Z-component of the projected location // to be the signed distance from the camera to the 3D location, with a positive sign // indicating the location is in front of the camera, and a negative sign indicating // the location is behind the camera. CC3Vector camToLocVector = a3DLocation - getGlobalLocation(); GLfloat camToLocDist = camToLocVector.length(); GLfloat frontOrBack = (GLfloat)SIGN( camToLocVector.dot( getGlobalForwardDirection() ) ); projectedLoc.z = frontOrBack * camToLocDist; //LogTrace(@"%@ projecting location %@ to %@ and orienting with device to %@ using viewport %@", // self, NSStringFromCC3Vector(a3DLocation), NSStringFromCC3Vector(projectedLoc), // NSStringFromCC3Vector(orientedLoc), NSStringFromCC3Viewport(_viewport)); return projectedLoc; }
// Overridden to take into consideration the isDirectionalOnly property CC3Vector4 CC3Light::getGlobalHomogeneousPosition() { return (isDirectionalOnly() ? CC3Vector4().fromDirection(getGlobalLocation()) : CC3Vector4().fromLocation(getGlobalLocation())); }
void CC3Camera::moveWithDurationLookAt( float t, CC3Node* aNode, const CC3Vector& targetLoc, GLfloat padding ) { ensureSceneUpdated( true ); CC3Vector moveDir = getGlobalLocation() - aNode->getGlobalLocation(); moveWithDuration( t, aNode, targetLoc, moveDir, padding, false ); }
void CC3Camera::moveWithDuration( float t, CC3Node* aNode, GLfloat padding ) { ensureSceneUpdated( true ); CC3Vector moveDir = getGlobalLocation() - aNode->getGlobalLocation(); moveWithDuration( t, aNode, CC3Vector::kCC3VectorNull, moveDir, padding, false ); }
void CC3Camera::moveToShowAllOfLookAt( CC3Node* aNode, const CC3Vector& targetLoc, GLfloat padding ) { ensureSceneUpdated( true ); CC3Vector moveDir = getGlobalLocation() - aNode->getGlobalLocation(); moveToShowAllOfLookAt( aNode, targetLoc, moveDir, padding, false ); }
void CC3Camera::moveToShowAllOf( CC3Node* aNode, GLfloat padding ) { ensureSceneUpdated( true ); CC3Vector moveDir = getGlobalLocation() - aNode->getGlobalLocation(); moveToShowAllOfLookAt( aNode, CC3Vector::kCC3VectorNull, moveDir, padding, false ); }