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