Beispiel #1
0
void pvCamera_Fit(DocumentPtr theDocument)
{	
	TQ3Point3D 		from, to;
	TQ3BoundingBox	viewBBox;
	float			fieldOfView, hither, yon;

	if (!theDocument)
		return;

	if (!theDocument->fModel)
		return;

	pvBBox_Get(theDocument, &viewBBox);
	pvBBoxCenter(&viewBBox, &to);
	

	{
		TQ3Vector3D viewVector;
		TQ3Vector3D	normViewVector;
		TQ3Vector3D	eyeToFrontClip;
		TQ3Vector3D	eyeToBackClip;
 		TQ3Vector3D	diagonalVector;
		float		viewDistance;
		float 		maxDimension;

		Q3Point3D_Subtract(&viewBBox.max,
						   &viewBBox.min,
						   &diagonalVector);
		maxDimension = Q3Vector3D_Length(&diagonalVector);
		if (maxDimension == 0.0F)
			maxDimension = 1.0F;

		maxDimension *= 8.0F / 7.0F;
	
		from.x = to.x;
		from.y = to.y;
		from.z = to.z + (2 * maxDimension);		

		Q3Point3D_Subtract(&to, &from, &viewVector);
		viewDistance = Q3Vector3D_Length(&viewVector);
		Q3Vector3D_Normalize(&viewVector, &normViewVector);
		
		maxDimension /= 2.0F;
		
		Q3Vector3D_Scale(&normViewVector, 
						viewDistance - maxDimension,
						&eyeToFrontClip);
						
		Q3Vector3D_Scale(&normViewVector, 
						viewDistance + maxDimension,
						&eyeToBackClip);
		
		hither 	= Q3Vector3D_Length(&eyeToFrontClip);
		yon 	= Q3Vector3D_Length(&eyeToBackClip);
		
		fieldOfView = Q3Math_RadiansToDegrees(1.25 * ErMath_Atan(maxDimension/hither));
	}

	{
		TQ3ViewAngleAspectCameraData 	data;
		TQ3Vector3D 	up 	= { 0.0F, 1.0F, 0.0F };

		data.cameraData.placement.cameraLocation 	= from;
		data.cameraData.placement.pointOfInterest 	= to;
		data.cameraData.placement.upVector 			= up;
	
		data.cameraData.range.hither = hither;
		data.cameraData.range.yon 	 = yon;
	
		data.cameraData.viewPort.origin.x = -1.0F;
		data.cameraData.viewPort.origin.y =  1.0F;
		data.cameraData.viewPort.width  = 2.0F;
		data.cameraData.viewPort.height = 2.0F;

		data.fov = Q3Math_DegreesToRadians(fieldOfView);
		
		{
			float w = (float)(theDocument->fWidth);
			float h = (float)(theDocument->fHeight);

			data.aspectRatioXToY = w/h;
		}
		
		if (theDocument->fView)
		{
			TQ3CameraObject camera;
			
			Q3View_GetCamera(theDocument->fView, &camera);
			if (camera) {
				Q3ViewAngleAspectCamera_SetData(camera, &data);
				Q3Object_Dispose(camera);
			}
			else {
				camera  = Q3ViewAngleAspectCamera_New (&data);
				if (camera) {
					Q3View_SetCamera (theDocument->fView, camera);
					Q3Object_Dispose(camera);
				}
			}
		}
	}
}
Beispiel #2
0
//=============================================================================
//      e3geom_disk_cache_new : Disk cache new method.
//-----------------------------------------------------------------------------
static TQ3Object
e3geom_disk_cache_new(TQ3ViewObject theView, TQ3GeometryObject theGeom, const TQ3DiskData *geomData)
{	float						theAngle, deltaAngle, cosAngle, sinAngle;
	TQ3Uns32					numSides, numPoints, numTriangles, n;
	TQ3Boolean					isPartAngleRange, hasHoleInCenter;
	float						startAngle, endAngle, angleRange;
	float						uMin, uMax, vMin, vMax;
	TQ3TriMeshAttributeData		vertexAttributes[2];
	TQ3Vector3D					surfaceNormalVector;
	TQ3SubdivisionStyleData		subdivisionData;
	TQ3TriMeshTriangleData		*theTriangles;
	TQ3TriMeshData				triMeshData;
	TQ3Vector3D					*theNormals;
	float						crossLength;
	TQ3GeometryObject			theTriMesh;
	TQ3Point3D					*thePoints;
	TQ3Status					qd3dStatus;
	TQ3GroupObject				theGroup;
	TQ3Param2D					*theUVs;



	// Get the UV limits and make sure they are valid.
	// These are for specifying partial disks, and have little to do
	// with surface UV coordinates.
	uMin  = E3Num_Clamp(geomData->uMin, 0.0f, 1.0f);
	uMax  = E3Num_Clamp(geomData->uMax, 0.0f, 1.0f);
	vMin  = E3Num_Clamp(geomData->vMin, 0.0f, 1.0f);
	vMax  = E3Num_Clamp(geomData->vMax, 0.0f, 1.0f);
	// It is possible for uMin to be greater than uMax, so that
	// we can specify which way to wrap around the circle.
	// But it doesn't make sense for vMin to be greater than vMax.
	if (vMin > vMax)
		E3Float_Swap( vMin, vMax );
	hasHoleInCenter = (vMin > kQ3RealZero)? kQ3True : kQ3False;
	
	
	
	// Turn the u limits into an angle range in radians.
	startAngle = uMin * kQ32Pi;
	endAngle = uMax * kQ32Pi;
	if (startAngle > endAngle)
		startAngle -= kQ32Pi;
	angleRange = endAngle - startAngle;
	isPartAngleRange = E3Float_Abs( angleRange - kQ32Pi ) > kQ3RealZero?
		kQ3True : kQ3False;



	// Get the subdivision style, to figure out how many sides we should have.
	numSides   = 10;
	qd3dStatus = Q3View_GetSubdivisionStyleState(theView, &subdivisionData);
	if (qd3dStatus == kQ3Success)
		{
		switch (subdivisionData.method) {
			case kQ3SubdivisionMethodConstant:
				// For a disk, parameter c1 is the number of sides and c2 is unused
				numSides = (TQ3Uns32) subdivisionData.c1;
				break;
			
			case kQ3SubdivisionMethodWorldSpace:
				// keep the length of any side less than or equal to c1
				{
					TQ3Matrix4x4	localToWorld;
					TQ3Vector3D		bigRadius, workVec;
					
					// Find the longer of the two radius vectors.
					bigRadius = geomData->majorRadius;
					if (Q3Vector3D_LengthSquared( &geomData->majorRadius ) <
						Q3Vector3D_LengthSquared( &geomData->minorRadius ) )
					{
						bigRadius = geomData->minorRadius;
					}

					// divide the circumference by c1
					Q3View_GetLocalToWorldMatrixState( theView, &localToWorld );
					Q3Vector3D_Transform( &bigRadius, &localToWorld, &workVec );
					numSides = (TQ3Uns32) ((kQ32Pi * Q3Vector3D_Length(&workVec))
							/ subdivisionData.c1);
				}
				break;

			case kQ3SubdivisionMethodScreenSpace:
				// Not implemented
				break;
			
			default:
				Q3_ASSERT(!"Unknown subdivision method");
				break;
			}
		}
	numSides  = E3Num_Clamp(numSides, 3, 256);
	
	
	
	// For a solid disk, we need one triangle for each side, but if there is a hole
	// in the center, we need two triangles for each side.
	// If the disk is not on the full angle range, we need extra points for the
	// boundary.
	if (hasHoleInCenter)
	{
		numTriangles = numSides * 2;
		numPoints = numSides * 2;
		if (isPartAngleRange == kQ3True)
			numPoints += 2;
	}
	else	// solid
	{
		numTriangles = numSides;
		numPoints = numSides + 1;	// + 1 for center
		if (isPartAngleRange == kQ3True)
			numPoints += 1;
	}



	// Allocate the memory we need for the TriMesh data
	thePoints    = (TQ3Point3D *)             Q3Memory_Allocate(static_cast<TQ3Uns32>(numPoints * sizeof(TQ3Point3D)));
	theNormals   = (TQ3Vector3D *)            Q3Memory_Allocate(static_cast<TQ3Uns32>(numPoints * sizeof(TQ3Vector3D)));
	theUVs       = (TQ3Param2D  *)            Q3Memory_Allocate(static_cast<TQ3Uns32>(numPoints * sizeof(TQ3Param2D)));
	theTriangles = (TQ3TriMeshTriangleData *) Q3Memory_Allocate(static_cast<TQ3Uns32>(numTriangles * sizeof(TQ3TriMeshTriangleData)));

	if (thePoints == NULL || theNormals == NULL || theUVs == NULL || theTriangles == NULL)
		{
		Q3Memory_Free(&thePoints);
		Q3Memory_Free(&theNormals);
		Q3Memory_Free(&theUVs);
		Q3Memory_Free(&theTriangles);
		
		return(NULL);
		}



	// Define the sides, as a cosine/sine combination of major and minor radius vectors
	deltaAngle = angleRange / (float) numSides;
	for (n = 0, theAngle = startAngle; n < numSides; ++n, theAngle += deltaAngle)
		{
		// Figure out where we are
		cosAngle = (float) cos(theAngle);
		sinAngle = (float) sin(theAngle);


		// Set up the points
		if (hasHoleInCenter)
			{
			e3geom_disk_calc_point( geomData, sinAngle, cosAngle, vMax, &thePoints[2*n] );
			e3geom_disk_calc_point( geomData, sinAngle, cosAngle, vMin, &thePoints[2*n+1] );
			}
		else
			{
			e3geom_disk_calc_point( geomData, sinAngle, cosAngle, vMax, &thePoints[n] );
			}


		// Set up the surface UV coordinates
		if (hasHoleInCenter)
			{
			theUVs[2*n].u = (vMax * cosAngle + 1.0f) / 2.0f;
			theUVs[2*n].v = (vMax * sinAngle + 1.0f) / 2.0f;
			theUVs[2*n+1].u = (vMin * cosAngle + 1.0f) / 2.0f;
			theUVs[2*n+1].v = (vMin * sinAngle + 1.0f) / 2.0f;
			}
		else
			{
			theUVs[n].u = (vMax * cosAngle + 1.0f) / 2.0f;
			theUVs[n].v = (vMax * sinAngle + 1.0f) / 2.0f;
			}
		

		// Set up the triangles
		if (hasHoleInCenter)
			{
			if (isPartAngleRange)
				{
				theTriangles[2*n].pointIndices[0] = 2*n + 1;
				theTriangles[2*n].pointIndices[1] = 2*n;
				theTriangles[2*n].pointIndices[2] = 2*n + 2;
				
				theTriangles[2*n+1].pointIndices[0] = 2*n + 1;
				theTriangles[2*n+1].pointIndices[1] = 2*n + 2;
				theTriangles[2*n+1].pointIndices[2] = 2*n + 3;
				}
			else
				{
				theTriangles[2*n].pointIndices[0] = 2*n + 1;
				theTriangles[2*n].pointIndices[1] = 2*n;
				theTriangles[2*n].pointIndices[2] = (2*n + 2) % (2*numSides);
				
				theTriangles[2*n+1].pointIndices[0] = 2*n + 1;
				theTriangles[2*n+1].pointIndices[1] = (2*n + 2) % (2*numSides);
				theTriangles[2*n+1].pointIndices[2] = (2*n + 3) % (2*numSides);
				}
			}
		else
			{
			if (isPartAngleRange)
				{
				theTriangles[n].pointIndices[0] = numSides + 1;
				theTriangles[n].pointIndices[1] = n;
				theTriangles[n].pointIndices[2] = n + 1;
				}
			else
				{
				theTriangles[n].pointIndices[0] = numSides;
				theTriangles[n].pointIndices[1] = n;
				theTriangles[n].pointIndices[2] = (n + 1) % numSides;
				}
			}
		}



	// Finish with center and/or boundary
	if (isPartAngleRange)
		{
		cosAngle = (float) cos(theAngle);
		sinAngle = (float) sin(theAngle);
		if (hasHoleInCenter)
			{
			e3geom_disk_calc_point( geomData, sinAngle, cosAngle, vMax,
				&thePoints[2*numSides] );
			e3geom_disk_calc_point( geomData, sinAngle, cosAngle, vMin,
				&thePoints[2*numSides+1] );
			theUVs[2*numSides].u = (vMax * cosAngle + 1.0f) / 2.0f;
			theUVs[2*numSides].v = (vMax * sinAngle + 1.0f) / 2.0f;
			theUVs[2*numSides+1].u = (vMin * cosAngle + 1.0f) / 2.0f;
			theUVs[2*numSides+1].v = (vMin * sinAngle + 1.0f) / 2.0f;
			}
		else
			{
			e3geom_disk_calc_point( geomData, sinAngle, cosAngle, vMax,
				&thePoints[numSides] );
			thePoints[numSides+1]  = geomData->origin;
			theUVs[numSides].u = (vMax * cosAngle + 1.0f) / 2.0f;
			theUVs[numSides].v = (vMax * sinAngle + 1.0f) / 2.0f;
			theUVs[numSides+1].u   = 0.5f;
			theUVs[numSides+1].v   = 0.5f;
			}
		}
	else
		{
		if (hasHoleInCenter == kQ3False)
			{
			thePoints[numSides]  = geomData->origin;
			theUVs[numSides].u   = 0.5f;
			theUVs[numSides].v   = 0.5f;
			}
		}



	// Surface normal is the cross product of the majorRadius and minorRadius
	Q3Vector3D_Cross(&geomData->majorRadius, &geomData->minorRadius, &surfaceNormalVector);
	crossLength = Q3Vector3D_Length( &surfaceNormalVector );
	if (crossLength <= kQ3RealZero)
	{
		surfaceNormalVector.x = 1.0f;	// arbitrary
		E3ErrorManager_PostError( kQ3ErrorDegenerateGeometry, kQ3False );
	}
	else
	{
		Q3Vector3D_Scale( &surfaceNormalVector, 1.0f/crossLength, &surfaceNormalVector );
	}

	for (n = 0; n < numPoints; ++n)
		theNormals[ n ] = surfaceNormalVector;



	// Initialise the vertex attributes
	vertexAttributes[0].attributeType     = kQ3AttributeTypeNormal;
	vertexAttributes[0].data              = theNormals;
	vertexAttributes[0].attributeUseArray = NULL;

	vertexAttributes[1].attributeType     = kQ3AttributeTypeSurfaceUV;
	vertexAttributes[1].data              = theUVs;
	vertexAttributes[1].attributeUseArray = NULL;



	// Initialise the TriMesh data
	triMeshData.numPoints                 = numPoints;
	triMeshData.points                    = thePoints;
	triMeshData.numTriangles              = numTriangles;
	triMeshData.triangles                 = theTriangles;
	triMeshData.numTriangleAttributeTypes = 0;
	triMeshData.triangleAttributeTypes    = NULL;
	triMeshData.numEdges                  = 0;
	triMeshData.edges                     = NULL;
	triMeshData.numEdgeAttributeTypes     = 0;
	triMeshData.edgeAttributeTypes        = NULL;
	triMeshData.numVertexAttributeTypes   = 2;
	triMeshData.vertexAttributeTypes      = vertexAttributes;
	triMeshData.triMeshAttributeSet       = geomData->diskAttributeSet;

	Q3BoundingBox_SetFromPoints3D(&triMeshData.bBox, triMeshData.points, triMeshData.numPoints, sizeof(TQ3Point3D));



	// Create the TriMesh
	theTriMesh = Q3TriMesh_New(&triMeshData);
	theGroup   = E3TriMesh_BuildOrientationGroup(theTriMesh, kQ3OrientationStyleCounterClockwise);



	// Clean up
	Q3Memory_Free(&thePoints);
	Q3Memory_Free(&theNormals);
	Q3Memory_Free(&theUVs);
	Q3Memory_Free(&theTriangles);



	// Return the cached geometry
	return(theGroup);
}