bool FlyingCamera::onSimCameraQuery(SimCameraQuery *query)
{
   SimObjectTransformQuery tquery;

   query->cameraInfo.fov       = g_rDefaultFOV;
   query->cameraInfo.nearPlane = DEFAULT_NEAR_PLANE;
   query->cameraInfo.farPlane  = getFarPlane();

   if (objFollow && objFollow->processQuery(&tquery))
   {
      Point3F  objPos = tquery.tmat.p;
      Vector3F x, y, z;
      RMat3F   rmat(EulerF(rotation.x - M_PI / 2, rotation.y, -rotation.z));

      tquery.tmat.p   += m_mul(Vector3F(0.0f, rDistance, 0.0f), rmat, &y);
      tquery.tmat.p.z += 2.0f;

      y.neg();
      y.normalize();
      m_cross(y, Vector3F(0.0f, 0.0f, 1.0f), &x);
      x.normalize();
      m_cross(x, y, &z);

      tquery.tmat.setRow(0, x);
      tquery.tmat.setRow(1, y);
      tquery.tmat.setRow(2, z);

      // Set our position
      findLOSPosition(tquery.tmat, objPos);
   }

   query->cameraInfo.tmat = getTransform();
   return (true);
}
bool ITR3DMImport::isLine(const Point3F& k,const Point3F& j,const Point3F& l)
{
   Point3F kj = k,lj = l,pv;
   kj -= j;
   lj -= j;
   m_cross( kj, lj, &pv );
   return isEqual(pv.len(),.0f,TPlaneF::NormalPrecision);
}
// assumes dirY is normalized
void fxRenderImage::faceDirection(Point3F & dirY)
{
	// if we rotate about an axis, use a different routine...
	if (useRotationAxis)
	{
		faceDirection(dirY,rotationAxis);
		return;
	}

	Point3F dirX,dirZ;

	if (fabs(dirY.z) < 0.95)
	{
		// dirY is not near vector (0,0,1), so we can
		// use it as the pivot vector
		m_cross(dirY, Point3F(0,0,1), &dirX);
		dirX.normalize();
		m_cross(dirX, dirY, &dirZ);
	}
	else
	{
		// dirY is near vector (0,0,1), so use
		// pivot Point3F(1,0,0) instead
		m_cross(Point3F(1,0,0), dirY, &dirZ);
		dirZ.normalize();
		m_cross(dirY, dirZ, &dirX);
	}

	transform.setRow(0,dirX);
	transform.setRow(1,dirY);
	transform.setRow(2,dirZ);
	transform.flags |=  TMat3F::Matrix_HasRotation;
	transform.flags &= ~TMat3F::Matrix_HasScale;

   if (useAxisSpin == true) {
      RMat3F tempRot(EulerF(0, axisSpin, 0));
      TMat3F tempOutput;
      m_mul(tempRot, transform, &tempOutput);
      transform = tempOutput;
   }
}
void ITR3DMImport::splitX(Poly* poly,PolyList* polyList)
{
   Vector<Point3F> points;
   Point3F iPoint;

   int v1 = poly->vertexList.size() - 1;
   for (int v2 = 0; v2 < poly->vertexList.size(); v2++) {
      Poly::Vertex* p1 = &poly->vertexList[v1];
      Poly::Vertex* p2 = &poly->vertexList[v2];

      if (p1->texture.x <= float(splitDist - 1) && p2->texture.x > float(splitDist - 1) ||
          p2->texture.x <= float(splitDist - 1) && p1->texture.x > float(splitDist - 1)) {
         Point3F vec;
         vec  = p2->point;
         vec -= p1->point;
         vec *= (float(splitDist - 1) - p1->texture.x) / (p2->texture.x - p1->texture.x);
         vec += p1->point;
         points.push_back(vec);
      }
      if (p1->texture.x < float(splitDist - 1))
         iPoint = p1->point;
      v1 = v2;
   }

   // Build plane and split the poly
   if (points.size() > 1) {
      Point3F vec = points[0];
      vec -= points[1];
      Point3F normal;
      m_cross(vec,poly->plane,&normal);
      TPlaneF plane(points[0],normal);
      if (plane.whichSide(iPoint) != TPlaneF::Inside)
         plane.neg();

      Poly tmp;
      Poly* npoly = new Poly;
      poly->split(plane,&tmp,npoly);
      poly->vertexList    = tmp.vertexList;

      npoly->plane = poly->plane;
      npoly->textureOffset = poly->textureOffset;
      npoly->material = poly->material;
      npoly->volumeMask = poly->volumeMask;
      npoly->textureScaleShift = poly->textureScaleShift;
      npoly->applyAmbient = poly->applyAmbient;
      polyList->push_back(npoly);

		// Normalize after the previous information copy, 
		// normalize alters some of the poly's fields.
      normalizeTexture(poly);
      normalizeTexture(npoly);
   }
}
// assumes dirY and dirZ are normalized
void fxRenderImage::faceDirection(Point3F & _dirY, Point3F & dirZ)
{
	// dirZ is the fixed axis we rotate about, dirY can change some
	Point3F dirX, dirY = _dirY;

	m_cross(dirY, dirZ, &dirX);
	float xlenSq = m_dot(dirX,dirX);
	if (IsEqual(xlenSq,0.0f))
	{
		// hard-luck case -- dirY and dirZ are parallel
		// find any old normalized dirX perp. to dirZ
		if (fabs(dirY.z) < 0.95)
		{
			// dirY is not near vector (0,0,1), so we can
			// use it as the pivot vector
			m_cross(dirY, Point3F(0,0,1), &dirX);
			dirX.normalize();
		}
		else
		{
			// dirY is near vector (0,0,1), so use
			// pivot Point3F(1,0,0) instead
			m_cross(dirY, Point3F(1,0,0), &dirX);
			dirX.normalize();
		}
	}
	else
		dirX *= m_invsqrtf(xlenSq);
	m_cross(dirZ, dirX, &dirY);

	transform.setRow(0,dirX);
	transform.setRow(1,dirY);
	transform.setRow(2,dirZ);
	transform.flags |=  TMat3F::Matrix_HasRotation;
	transform.flags &= ~TMat3F::Matrix_HasScale;
}
void m_rotate(float ax,float ay,float az,float phi,matrix &m)
{
  matrix m1;
  vector a;

  if (ax==0 && ay==0 && az==0) {m_identity(m); return;}

  v3_make(ax, ay, az, a);
  v3_normalize(a, a);

  m_identity(m);
  m_mults(m, (float)cos(phi), m);
  m_diadic3(a, a, m1);
  m_mults(m1, (float)(1-cos(phi)), m1);
  m_add(m, m1, m);

  m_cross(a, m1);
  m_mults(m1, (float)sin(phi), m1);
  m_add(m, m1, m);
  m[3][3] = 1.0;

}
void PlanetRenderImage::render(TSRenderContext &rc)
{
   // A simple planet culling scheme would be to dot the line of sight
   // with the vector from the camera to the planet.  This would eliminate
   // the length test of v below (after m_cross((Point3F)plane, vpNormal, &v))
   GFXSurface *gfxSurface = rc.getSurface();

   gfxSurface->setHazeSource(GFX_HAZE_NONE);
   gfxSurface->setShadeSource(GFX_SHADE_CONSTANT);
   gfxSurface->setAlphaSource(GFX_ALPHA_NONE);
	gfxSurface->setFillMode(GFX_FILL_TEXTURE);
	gfxSurface->setTransparency(FALSE);
   gfxSurface->setTexturePerspective(FALSE);

   gfxSurface->setConstantShade(1.0f);

   int textureHeight;
   gfxSurface->setTextureMap(texture);
   textureHeight = texture->height;
   
   TSCamera *camera = rc.getCamera();
  
   TS::PointArray *pointArray = rc.getPointArray();
   pointArray->reset();
   pointArray->useIntensities(false);
   pointArray->useTextures(textCoord);
   pointArray->useTextures(true);
	pointArray->setVisibility( TS::ClipMask );

   // find out how high the bitmap is at 100% as projected onto the viewport,
   // texel:pixel will be 1:1 at 640x480
   //const RectF &worldVP  = camera->getWorldViewport();
   //const float h = textureHeight*((worldVP.upperL.y - worldVP.lowerR.y)/480.0f);
   //const float sz = 0.5*distance*(h/camera->getNearDist());

   // find the position of the planet
   Point3F displacement = camera->getTCW().p;
   //displacement.z *= -(distance - visibleDistance)/visibleDistance;
   displacement.z = -displacement.z*(distance/(visibleDistance*1.5f));
   Point3F pos = position;
   pos += displacement;
 
   // find the normal to the view plane in world coords
   Point3F v0(0.0f, 1.0f, 0.0f), vpNormal;
   m_mul(v0, (RMat3F)camera->getTCW(), &vpNormal);
   vpNormal.normalize();

   // construct the plane that the camera, planet pos & celestial NP all
   // lie on
   PlaneF plane(pos, camera->getTCW().p, 
      Point3F(displacement.x, displacement.y, displacement.z + distance));

   // the cross product of the VP normal and the normal to the plane just
   // constructed is the up vector for the planet
   Point3F v;
   m_cross((Point3F)plane, vpNormal, &v);
   if (IsEqual(v.len(), 0.0f))
      // planet is directly to the right or left of camera
      return;
   v.normalize();
   
   // cross the up with the normal and we get the right vector
   Point3F u;
   m_cross(vpNormal, v, &u);
   u *= size;
   v *= size;

   TS::VertexIndexPair V[6];
   Point3F ul = pos;
   ul -= u; ul += v;
   V[0].fVertexIndex   = pointArray->addPoint(ul);
   V[0].fTextureIndex  = 0;
   Point3F ur = pos;
   ur += u; ur += v;
   V[1].fVertexIndex   = pointArray->addPoint(ur);
   V[1].fTextureIndex  = 1;
   Point3F lr = pos;
   lr += u; lr -= v;
   V[2].fVertexIndex   = pointArray->addPoint(lr);
   V[2].fTextureIndex  = 2;
   Point3F ll = pos;
   ll -= u; ll -=v;
   V[3].fVertexIndex   = pointArray->addPoint(ll);
   V[3].fTextureIndex  = 3;
	if (gfxSurface->getCaps() & GFX_DEVCAP_SUPPORTS_CONST_ALPHA)
	   gfxSurface->setZTest(GFX_NO_ZTEST);
   pointArray->drawPoly(4, V, 0);
	if (gfxSurface->getCaps() & GFX_DEVCAP_SUPPORTS_CONST_ALPHA)
	   gfxSurface->setZTest(GFX_ZTEST_AND_WRITE);
   if(lensFlare) {
      TS::TransformedVertex vx;
      camera->transformProject(pos, &vx);
      bool vis = vx.fStatus & TS::TransformedVertex::Projected;
      lensFlare->setSunPos(vis, vx.fPoint, pos);
   }
}
bool CelAnimMesh::collideTube( int frameIndex, const Point3F & a, const Point3F &b, float radius,
                                       CollisionSurface & cs, float minTime) const
{
   minTime;

   // hitTime holds the current smallest...
   float hitTime = cs.time;
   int hitFace = -1;
   Point3F hitPoint;

   AssertFatal( fFrames.size() > 0, "Shape must have at least one frame." );
   AssertFatal( frameIndex >= 0 && frameIndex < fFrames.size(),
                "TS::CelAnimMesh: frame index out of range" );
                
   // get the frame struct:
   const Frame *frm = &fFrames[frameIndex];
   int fv = frm->fFirstVert;
   const Point3F *pScale = &frm->fScale;
   const Point3F *pOrigin = &frm->fOrigin;
   
   Point3F tubeVect;
   tubeVect.x = b.x - a.x;
   tubeVect.y = b.y - a.y;
   tubeVect.z = b.z - a.z;
   float tubeLen = tubeVect.len();
   float invTubeLen = 1.0f/tubeLen;

   // tubeVect will hold unit length vector pointing down tube
   tubeVect *= invTubeLen;
   float vectDotA = m_dot(tubeVect,a);

   // inverse radius squared for edgeInTube routine
   float invRadius2 = 1.0f / (radius*radius);

   int i;
   workVerts.setSize(fnVertsPerFrame);
   workRs.setSize(fnVertsPerFrame);
   workTs.setSize(fnVertsPerFrame);
   bool gotNormal;
   for (i=0;i<fFaces.size();i++)
   {
      const Face & theFace = fFaces[i];
      int idx1 = theFace.fVIP[0].fVertexIndex;
      int idx2 = theFace.fVIP[1].fVertexIndex;
      int idx3 = theFace.fVIP[2].fVertexIndex;

      Point3F &v1=workVerts[idx1];
      Point3F &v2=workVerts[idx2];
      Point3F &v3=workVerts[idx3];

      Point3F &R1=workRs[idx1];
      Point3F &R2=workRs[idx2];
      Point3F &R3=workRs[idx3];
      
      float &t1=workTs[idx1];
      float &t2=workTs[idx2];
      float &t3=workTs[idx3];

      if (!(v1Recycled&faceReuseFlags[i]))
      {
         fVerts[idx1+fv].getPoint(v1,*pScale,*pOrigin);
         // distance of vertex down the tube
         t1 = m_dot(v1,tubeVect) - vectDotA;
         // projection of vertex onto tube cross-section (centered on origin)
         R1.x = v1.x - a.x - t1 * tubeVect.x;
         R1.y = v1.y - a.y - t1 * tubeVect.y;
         R1.z = v1.z - a.z - t1 * tubeVect.z;
      }

      if (!(v2Recycled&faceReuseFlags[i]))
      {
         fVerts[idx2+fv].getPoint(v2,*pScale,*pOrigin);
         // distance of vertex down the tube
         t2 = m_dot(v2,tubeVect) - vectDotA;
         // projection of vertex onto tube cross-section (centered on origin)
         R2.x = v2.x - a.x - t2 * tubeVect.x;
         R2.y = v2.y - a.y - t2 * tubeVect.y;
         R2.z = v2.z - a.z - t2 * tubeVect.z;
      }

      if (!(v3Recycled&faceReuseFlags[i]))
      {
         fVerts[idx3+fv].getPoint(v3,*pScale,*pOrigin);
         // distance of vertex down the tube
         t3 = m_dot(v3,tubeVect) - vectDotA;
         // projection of vertex onto tube cross-section (centered on origin)
         R3.x = v3.x - a.x - t3 * tubeVect.x;
         R3.y = v3.y - a.y - t3 * tubeVect.y;
         R3.z = v3.z - a.z - t3 * tubeVect.z;
      }

      bool gotHit=false;
      if (!(e1Recycled&faceReuseFlags[i]))
      {
         if (t1<=t2)
            gotHit = edgeInTube(R1,R2,t1,t2,radius,invRadius2,invTubeLen,hitTime,hitPoint);
         else
            gotHit = edgeInTube(R2,R1,t2,t1,radius,invRadius2,invTubeLen,hitTime,hitPoint);
      }

      if (!(e2Recycled&faceReuseFlags[i]))
      {
         if (t2<=t3)                                          
            gotHit |= edgeInTube(R2,R3,t2,t3,radius,invRadius2,invTubeLen,hitTime,hitPoint);
         else                                                 
            gotHit |= edgeInTube(R3,R2,t3,t2,radius,invRadius2,invTubeLen,hitTime,hitPoint);
      }

      if (!(e3Recycled&faceReuseFlags[i]))
      {
         if (t3<=t1)
            gotHit |= edgeInTube(R3,R1,t3,t1,radius,invRadius2,invTubeLen,hitTime,hitPoint);
         else
            gotHit |= edgeInTube(R1,R3,t1,t3,radius,invRadius2,invTubeLen,hitTime,hitPoint);
      }

      if (gotHit)
      {
         hitPoint.x += tubeLen * hitTime * tubeVect.x + a.x;
         hitPoint.y += tubeLen * hitTime * tubeVect.y + a.y;
         hitPoint.z += tubeLen * hitTime * tubeVect.z + a.z;
         hitFace=i;
         gotNormal=false;
      }

      // now check if tube goes through center of face w/o hitting any edges
      if (m_pointInTriangle(Point3F(0.0f,0.0f,0.0f),tubeVect,R1,R2,R3))
      {
         // build the normal
         Point3F normal;
         m_normal(v1,v2,v3,normal);
         // now we need to find hitTime
         float d = m_dot(normal,v3); // distance of plane from origin
         float denom = m_dot(normal,tubeVect) * tubeLen;
         if (denom>=0.0f) // back face, we can ignore
            continue;
         float absT = d - m_dot(normal,a);
         if (absT<=hitTime*denom) // denom is neg.
            continue;

         // ok, a real collision, set ci variables...
         hitTime=absT/denom;
         hitFace=i;
         cs.normal=normal;
         gotNormal=true;
         hitPoint.x = a.x + hitTime * tubeLen * tubeVect.x;
         hitPoint.y = a.y + hitTime * tubeLen * tubeVect.y;
         hitPoint.z = a.z + hitTime * tubeLen * tubeVect.z;
      }
   }

   if (hitFace>=0)
   {
      const Face & theFace = fFaces[hitFace];
      if (!gotNormal)
      {
         Point3F &v1=workVerts[theFace.fVIP[0].fVertexIndex];
         Point3F &v2=workVerts[theFace.fVIP[1].fVertexIndex];
         Point3F &v3=workVerts[theFace.fVIP[2].fVertexIndex];

         // build the normal
         Point3F v13,v23;
         v13.x = v1.x-v3.x;
         v13.y = v1.y-v3.y;
         v13.z = v1.z-v3.z;
         v23.x = v2.x-v3.x;
         v23.y = v2.y-v3.y;
         v23.z = v2.z-v3.z;
         m_cross(v23,v13,&cs.normal);
      }
      cs.material=theFace.fMaterial;
      cs.surface=hitFace;
      cs.time=hitTime;
      cs.position=hitPoint;
      // cs.distance ??
      return true;
   }
   return false;
}
// decide where to place and then add to the manager
void SimExplosionCloud::lightFuse(SimExplosion * explosion, Point3F & cameraPos, bool igniteNow)
{
	// decide when to detonate
	if (!igniteNow)
		explosion->setDetonationTime(g_expRand.getFloat(0,detonationMax));

	Point3F pos;
   Point3F vec(0, 0, 0);
	Point3F worldPos,worldVec;

	switch (form)
	{
		case Sphere :
		{
			// try to make sure explosions no closer together than 1m in z
			int maxZ = (int) radius;
			if (maxZ)
			{
				if (topOnly)
					vec.z = g_expRand.getInt(0,maxZ);
				else
					vec.z = g_expRand.getInt(-maxZ,maxZ);
			}
			// don't break, fall through to circle
		}
		case Circle :
		{
			float angle = g_expRand.getFloat(0,float(M_2PI));
			float circleRad = m_sqrtf(radius*radius - vec.z *vec.z);
			vec.x  = circleRad * m_cos(angle);
			vec.y  = circleRad * m_sin(angle);
			pos = center;
			pos += vec;
			break;
		}
		case Screen:
		{
			if (radius>0.1f) // if radius==0, skip some work
			{
				vec=cameraPos; 
				vec -= center; 
				vec.normalize(); 
				Point3F dirX,dirZ;
				if (fabs(vec.z) < 0.95)
				{
				   // vec is not near vector (0,0,1), so we can
   				// use it as the pivot vector
   				m_cross(vec, Point3F(0,0,1), &dirX);
					dirX.normalize();
   				m_cross(dirX, vec, &dirZ);
				}
				else
				{
				   // dirY is near vector (0,0,1), so use
   				// pivot Point3F(1,0,0) instead
   				m_cross(Point3F(1,0,0), vec, &dirZ);
   				dirZ.normalize();
   				m_cross(vec, dirZ, &dirX);
				}
				dirX *= g_expRand.getFloat(-radius,radius);
				dirZ *= g_expRand.getFloat(-radius,radius);
				vec *= radius;
				vec += dirX;
				vec += dirZ;
			}
			pos = center;
			pos += vec;
			break;
		}
		case Box :
		{
			int side;
			if (topOnly)
				side = g_expRand.getInt(1,5);
			else
				side = g_expRand.getInt(0,5);
			pos = box.fMax;
			switch (side)
			{
				case 0:
					pos.z = box.fMin.z;
				case 1:
					pos.x = g_expRand.getFloat(box.fMin.x,box.fMax.x);
					pos.y = g_expRand.getFloat(box.fMin.y,box.fMax.y);
					break;
				case 2:
					pos.y = box.fMin.y;
				case 3:
					pos.x = g_expRand.getFloat(box.fMin.x,box.fMax.x);
					pos.z = g_expRand.getFloat(box.fMin.z,box.fMax.z);
					break;
				case 4:
					pos.x = box.fMin.x;
				case 5:
					pos.y = g_expRand.getFloat(box.fMin.y,box.fMax.y);
					pos.z = g_expRand.getFloat(box.fMin.z,box.fMax.z);
					break;
			}
			switch (side)
			{
				case 0:
					vec.set(0,0,box.fMin.z-box.fMax.z);
					break;
				case 1:
					vec.set(0,0,box.fMax.z-box.fMin.z);
					break;
				case 2:
					vec.set(0,box.fMin.y-box.fMax.y,0);
					break;
				case 3:
					vec.set(0,box.fMax.y-box.fMin.y,0);
					break;
				case 4:
					vec.set(box.fMin.x-box.fMax.x,0,0);
					break;
				case 5:
					vec.set(box.fMax.x-box.fMin.x,0,0);
					break;
			}
			break;
		}
	}

	// now put into world coords
	if (hasTransform)
	{
		m_mul(pos,transform,&worldPos);
		m_mul(vec,(RMat3F&)transform,&worldVec);
	}
	else
	{
		worldPos=pos;
		worldVec=vec;
	}

	Point3F camAxis = worldPos;
	camAxis -= cameraPos;
	if (m_dot(camAxis,worldVec)>0.0f)
	{
		worldPos -= worldVec;
		worldPos -= worldVec;
	}
	explosion->setPosition(worldPos);
	explosion->setAxis(worldVec); // move away from the building... ;->
	explosion->setSound(false);
	manager->addObject(explosion);
}