コード例 #1
0
ファイル: VolumeRenderable.cpp プロジェクト: Argos86/dt2370
void VolumeRenderable::_notifyCurrentCamera( Camera* cam )
{
	MovableObject::_notifyCurrentCamera(cam);

	// Fake orientation toward camera
	Vector3 zVec = getParentNode()->_getDerivedPosition() - cam->getDerivedPosition();
	zVec.normalise();
	Vector3 fixedAxis = cam->getDerivedOrientation() * Vector3::UNIT_Y ;
	
	Vector3 xVec = fixedAxis.crossProduct( zVec );
	xVec.normalise();

	Vector3 yVec = zVec.crossProduct( xVec );
	yVec.normalise();
	
	Quaternion oriQuat;
	oriQuat.FromAxes( xVec, yVec, zVec );
	
	oriQuat.ToRotationMatrix(mFakeOrientation);
	
	Matrix3 tempMat;
	Quaternion q = getParentNode()->_getDerivedOrientation().UnitInverse() * oriQuat ;
	q.ToRotationMatrix(tempMat);
	
	Matrix4 rotMat = Matrix4::IDENTITY;
	rotMat = tempMat;
	rotMat.setTrans(Vector3(0.5f, 0.5f, 0.5f));
	mUnit->setTextureTransform(rotMat);
}
コード例 #2
0
bool Quaternion::FromLookRotation(const Vector3& direction, const Vector3& upDirection)
{
    Quaternion ret;
    Vector3 forward = direction.Normalized();

    Vector3 v = forward.CrossProduct(upDirection);
    // If direction & upDirection are parallel and crossproduct becomes zero, use FromRotationTo() fallback
    if (v.LengthSquared() >= M_EPSILON)
    {
        v.Normalize();
        Vector3 up = v.CrossProduct(forward);
        Vector3 right = up.CrossProduct(forward);
        ret.FromAxes(right, up, forward);
    }
    else
        ret.FromRotationTo(Vector3::FORWARD, forward);

    if (!ret.IsNaN())
    {
        (*this) = ret;
        return true;
    }
    else
        return false;
}
コード例 #3
0
    //-----------------------------------------------------------------------------
    const Matrix4& AutoParamDataSource::getSpotlightViewProjMatrix(size_t index) const
    {
        if (index < OGRE_MAX_SIMULTANEOUS_LIGHTS)
        {
            const Light& l = getLight(index);

            if (&l != &mBlankLight && 
                l.getType() == Light::LT_SPOTLIGHT &&
                mSpotlightViewProjMatrixDirty[index])
            {
                Frustum frust;
                SceneNode dummyNode(0);
                dummyNode.attachObject(&frust);

                frust.setProjectionType(PT_PERSPECTIVE);
                frust.setFOVy(l.getSpotlightOuterAngle());
                frust.setAspectRatio(1.0f);
                // set near clip the same as main camera, since they are likely
                // to both reflect the nature of the scene
                frust.setNearClipDistance(mCurrentCamera->getNearClipDistance());
                // Calculate position, which same as spotlight position, in camera-relative coords if required
                dummyNode.setPosition(l.getDerivedPosition(true));
                // Calculate direction, which same as spotlight direction
                Vector3 dir = - l.getDerivedDirection(); // backwards since point down -z
                dir.normalise();
                Vector3 up = Vector3::UNIT_Y;
                // Check it's not coincident with dir
                if (Math::Abs(up.dotProduct(dir)) >= 1.0f)
                {
                    // Use camera up
                    up = Vector3::UNIT_Z;
                }
                // cross twice to rederive, only direction is unaltered
                Vector3 left = dir.crossProduct(up);
                left.normalise();
                up = dir.crossProduct(left);
                up.normalise();
                // Derive quaternion from axes
                Quaternion q;
                q.FromAxes(left, up, dir);
                dummyNode.setOrientation(q);

                // The view matrix here already includes camera-relative changes if necessary
                // since they are built into the frustum position
                mSpotlightViewProjMatrix[index] = 
                    PROJECTIONCLIPSPACE2DTOIMAGESPACE_PERSPECTIVE * 
                    frust.getProjectionMatrixWithRSDepth() * 
                    frust.getViewMatrix();

                mSpotlightViewProjMatrixDirty[index] = false;
            }
            return mSpotlightViewProjMatrix[index];
        }
        else
            return Matrix4::IDENTITY;
    }
コード例 #4
0
ファイル: ThingRenderable.cpp プロジェクト: bsmr-c-cpp/ogre
void ThingRenderable::initialise()
{
    // Fill array with randomly oriented quads
    Vector3 ax, ay, az;

    Quaternion q;
    things.clear(); orbits.clear();
    for(size_t x=0; x<mCount; x++)
    {
        ax = Vector3(Math::SymmetricRandom(), Math::SymmetricRandom(), Math::SymmetricRandom());
        ay = Vector3(Math::SymmetricRandom(), Math::SymmetricRandom(), Math::SymmetricRandom());
        az = ax.crossProduct(ay);
        ay = az.crossProduct(ax);
        ax.normalise(); ay.normalise(); az.normalise();
        q.FromAxes(ax, ay, az);
        //std::cerr << ax.dotProduct(ay) << " " << ay.dotProduct(az) << " " << az.dotProduct(ax) << std::endl;
        things.push_back(q);
        
        ax = Vector3(Math::SymmetricRandom(), Math::SymmetricRandom(), Math::SymmetricRandom());
        ay = Vector3(Math::SymmetricRandom(), Math::SymmetricRandom(), Math::SymmetricRandom());
        az = ax.crossProduct(ay);
        ay = az.crossProduct(ax);
        ax.normalise(); ay.normalise(); az.normalise();
        q.FromAxes(ax, ay, az);
        orbits.push_back(q);
    }
    
    // Create buffers
    size_t nvertices = mCount*4; // n+1 planes
    //size_t elemsize = 2*3; // position, normal
    //size_t dsize = elemsize*nvertices;
    
    Ogre::IndexData *idata = new Ogre::IndexData();
    Ogre::VertexData *vdata = new Ogre::VertexData();

    // Quads
    unsigned short *faces = new unsigned short[mCount*6];
    for(uint16 x=0; x<uint16(mCount); x++)
    {
        faces[x*6+0] = x*4+0;
        faces[x*6+1] = x*4+1;
        faces[x*6+2] = x*4+2;
        faces[x*6+3] = x*4+0;
        faces[x*6+4] = x*4+2;
        faces[x*6+5] = x*4+3;
    }
    // Setup buffers
    vdata->vertexStart = 0;
    vdata->vertexCount = nvertices;
    
    VertexDeclaration* decl = vdata->vertexDeclaration;
    VertexBufferBinding* bind = vdata->vertexBufferBinding;

    size_t offset = 0;
    decl->addElement(0, offset, VET_FLOAT3, VES_POSITION);
    offset += VertexElement::getTypeSize(VET_FLOAT3);

    vbuf = 
    HardwareBufferManager::getSingleton().createVertexBuffer(
        offset, nvertices, HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY);

    bind->setBinding(0, vbuf);

    //vbuf->writeData(0, vbuf->getSizeInBytes(), vertices, true);
    
    HardwareIndexBufferSharedPtr ibuf = HardwareBufferManager::getSingleton().
        createIndexBuffer(
            HardwareIndexBuffer::IT_16BIT, 
            mCount*6, 
            HardwareBuffer::HBU_STATIC_WRITE_ONLY);

    idata->indexBuffer = ibuf;
    idata->indexCount = mCount*6;
    idata->indexStart = 0;
    ibuf->writeData(0, ibuf->getSizeInBytes(), faces, true);

    // Delete temporary buffers
    delete [] faces;
    
    // Now make the render operation
    mRenderOp.operationType = Ogre::RenderOperation::OT_TRIANGLE_LIST;
    mRenderOp.indexData = idata;
    mRenderOp.vertexData = vdata;
    mRenderOp.useIndexes = true;
}
コード例 #5
0
ファイル: FollowCamera.cpp プロジェクト: martinkg/stuntrally
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;
}
コード例 #6
0
ファイル: OgreCamera.cpp プロジェクト: carriercomm/gamekit
    //-----------------------------------------------------------------------
    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();

    }
コード例 #7
0
    /// Default shadow camera setup implementation
    void DefaultShadowCameraSetup::getShadowCamera (const SceneManager *sm, const Camera *cam, 
        const Viewport *vp, const Light *light, Camera *texCam, size_t iteration) const
    {
        Vector3 pos, dir;

        // reset custom view / projection matrix in case already set
        texCam->setCustomViewMatrix(false);
        texCam->setCustomProjectionMatrix(false);
        texCam->setNearClipDistance(light->_deriveShadowNearClipDistance(cam));
        texCam->setFarClipDistance(light->_deriveShadowFarClipDistance(cam));

        // get the shadow frustum's far distance
        Real shadowDist = light->getShadowFarDistance();
        if (!shadowDist)
        {
            // need a shadow distance, make one up
            shadowDist = cam->getNearClipDistance() * 300;
        }
        Real shadowOffset = shadowDist * (sm->getShadowDirLightTextureOffset());

        // Directional lights 
        if (light->getType() == Light::LT_DIRECTIONAL)
        {
            // set up the shadow texture
            // Set ortho projection
            texCam->setProjectionType(PT_ORTHOGRAPHIC);
            // set ortho window so that texture covers far dist
            texCam->setOrthoWindow(shadowDist * 2, shadowDist * 2);

            // 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);

            // Calculate direction, which same as directional light direction
            dir = - light->getDerivedDirection(); // backwards since point down -z
            dir.normalise();

            // Calculate position
            // We want to be in the -ve direction of the light direction
            // far enough to project for the dir light extrusion distance
            pos = target + dir * sm->getShadowDirectionalLightExtrusionDistance();

            // Round local x/y position based on a world-space texel; this helps to reduce
            // jittering caused by the projection moving with the camera
            // Viewport is 2 * near clip distance across (90 degree fov)
            //~ Real worldTexelSize = (texCam->getNearClipDistance() * 20) / vp->getActualWidth();
            //~ pos.x -= fmod(pos.x, worldTexelSize);
            //~ pos.y -= fmod(pos.y, worldTexelSize);
            //~ pos.z -= fmod(pos.z, worldTexelSize);
            Real worldTexelSize = (shadowDist * 2) / texCam->getViewport()->getActualWidth();

             //get texCam orientation

             Vector3 up = Vector3::UNIT_Y;
             // Check it's not coincident with dir
             if (Math::Abs(up.dotProduct(dir)) >= 1.0f)
             {
                // Use camera up
                up = Vector3::UNIT_Z;
             }
             // cross twice to rederive, only direction is unaltered
             Vector3 left = dir.crossProduct(up);
             left.normalise();
             up = dir.crossProduct(left);
             up.normalise();
             // Derive quaternion from axes
             Quaternion q;
             q.FromAxes(left, up, dir);

             //convert world space camera position into light space
             Vector3 lightSpacePos = q.Inverse() * pos;
             
             //snap to nearest texel
             lightSpacePos.x -= fmod(lightSpacePos.x, worldTexelSize);
             lightSpacePos.y -= fmod(lightSpacePos.y, worldTexelSize);

             //convert back to world space
             pos = q * lightSpacePos;
            
        }
        // Spotlight
        else if (light->getType() == Light::LT_SPOTLIGHT)
        {
            // Set perspective projection
            texCam->setProjectionType(PT_PERSPECTIVE);
            // set FOV slightly larger than the spotlight range to ensure coverage
            Radian fovy = light->getSpotlightOuterAngle()*1.2;
            // limit angle
            if (fovy.valueDegrees() > 175)
                fovy = Degree(175);
            texCam->setFOVy(fovy);

            // Calculate position, which same as spotlight position
            pos = light->getDerivedPosition();

            // Calculate direction, which same as spotlight direction
            dir = - light->getDerivedDirection(); // backwards since point down -z
            dir.normalise();
        }
        // Point light
        else
        {
            // Set perspective projection
            texCam->setProjectionType(PT_PERSPECTIVE);
            // Use 120 degree FOV for point light to ensure coverage more area
            texCam->setFOVy(Degree(120));

            // 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);

            // Calculate position, which same as point light position
            pos = light->getDerivedPosition();

            dir = (pos - target); // backwards since point down -z
            dir.normalise();
        }

        // Finally set position
        texCam->setPosition(pos);

        // Calculate orientation based on direction calculated above
        /*
        // Next section (camera oriented shadow map) abandoned
        // Always point in the same direction, if we don't do this then
        // we get 'shadow swimming' as camera rotates
        // As it is, we get swimming on moving but this is less noticeable

        // calculate up vector, we want it aligned with cam direction
        Vector3 up = cam->getDerivedDirection();
        // Check it's not coincident with dir
        if (up.dotProduct(dir) >= 1.0f)
        {
        // Use camera up
        up = cam->getUp();
        }
        */
        Vector3 up = Vector3::UNIT_Y;
        // Check it's not coincident with dir
        if (Math::Abs(up.dotProduct(dir)) >= 1.0f)
        {
            // Use camera up
            up = Vector3::UNIT_Z;
        }
        // cross twice to rederive, only direction is unaltered
        Vector3 left = dir.crossProduct(up);
        left.normalise();
        up = dir.crossProduct(left);
        up.normalise();
        // Derive quaternion from axes
        Quaternion q;
        q.FromAxes(left, up, dir);
        texCam->setOrientation(q);
    }