//////////
//
//	File:		QT3DTween.c
//
//	Contains:	QuickDraw 3D tweening support for QuickTime movies.
//
//	Written by:	Tim Monroe
//				based largely on 3DTween code by Scott Kuechle (Apple Developer Technical Support),
//				which was based on the 3DMF2AnimatedCameraMovie sample code in the QuickTime 2.5 SDK.
//
//	Copyright:	© 1997-1998 by Apple Computer, Inc., all rights reserved.
//
//	Change History (most recent first):
//
//	   <7>	 	03/17/00	rtm		made changes to get things running under CarbonLib
//	   <6>	 	09/30/98	rtm		tweaked call to AddMovieResource to create single-fork movies
//	   <5>	 	05/29/98	rtm		changed ID of kTween3dInitialCondition atom to 1; this makes
//									the rotation tweening work correctly now
//	   <4>	 	12/05/97	rtm		added rotation tween entry support; revised menus accordingly
//	   <3>	 	12/04/97	rtm		finished Windows version
//	   <2>	 	11/26/97	rtm		finished Mac version
//	   <1>	 	11/24/97	rtm		first file; revised to personal coding style
//	   
//
// This sample shows how to create a QuickTime movie that "animates" a 3D object. It does this
// by storing tweening information in a special tween media track. The same techniques can be used
// to dynamically alter other QuickTime movie tracks (such as sound tracks) by adding tween tracks
// to a movie.
// 
// 
// NOTES:
//
// *** (1) ***
// For complete information on creating tween media tracks, see the chapter "Tween Media Handler Components"
// in the QuickTime 2.5 or 3.0 Developers Guide.
//
//
// TO DO:
//
//////////

#include <Fonts.h>
#include <FixMath.h>
#include <Sound.h>

#include "QT3DTween.h"

// global variables
// these variables determine what kind of tween data we add to the movie

Boolean				gAddCameraTweenData = false;		// do we add camera tween data?
Boolean				gAddRotationTweenData = false;		// do we add rotation tween data?


//////////
//
// QT3DTween_Get3DMFFileAliasAndSize
// Have the user select a 3DMF file; return an alias to, and the size of, that file.
//
//////////

OSErr QT3DTween_Get3DMFFileAliasAndSize (Handle *theAlias, long *theSize)
{
	OSType 						myTypeList[] = {kQTFileType3DMF};
	short						myNumTypes = 1;
	FSSpec						myFSSpec;
	short						myRefNum = 0;
	AliasHandle					myAlias = NULL;
	QTFrameFileFilterUPP		myFileFilterUPP = NULL;
	OSErr						myErr = userCanceledErr;
	
#if TARGET_OS_MAC
	myNumTypes = 0;
#endif

	myFileFilterUPP = QTFrame_GetFileFilterUPP((ProcPtr)QT3DTween_FilterFiles);

	myErr = QTFrame_GetOneFileWithPreview(myNumTypes, (QTFrameTypeListPtr)myTypeList, &myFSSpec, myFileFilterUPP);
	if (myErr != noErr)
		goto bail;

	// determine the size of the data in the 3DMF file
	myErr = FSpOpenDF(&myFSSpec, fsRdPerm, &myRefNum);
	if (myErr != noErr)
		goto bail;
		
	myErr = GetEOF(myRefNum, theSize);
	if (myErr != noErr)
		goto bail;
		
	// make an alias to the 3DMF file (QuickTime calls this a "data reference")
	myErr = NewAliasMinimal(&myFSSpec, &myAlias);
		
bail:
	if (myFileFilterUPP != NULL)
		DisposeNavObjectFilterUPP(myFileFilterUPP);

	if (myRefNum != 0)
		myErr = FSClose(myRefNum);

	*theAlias = (Handle)myAlias;
	
	return(myErr);
}


//////////
//
// QT3DTween_GetFinalCameraData
// Fill in a TQ3CameraData structure with the ending camera data.
//
// This data will eventually be written to the movie file as sample data,
// so we need to make sure the data is returned in big-endian format.
//
//////////

void QT3DTween_GetFinalCameraData (TQ3CameraData *theCameraData)
{
	TQ3Point3D		myCameraFrom 	= {0.0, 0.0, 40.0};
	TQ3Point3D		myCameraTo 		= {0.0, 0.0, 0.0};
	TQ3Vector3D		myCameraUp 		= {0.0, 1.0, 0.0};
	
	// set the camera data
	theCameraData->placement.cameraLocation		= myCameraFrom;
	theCameraData->placement.pointOfInterest	= myCameraTo;
	theCameraData->placement.upVector			= myCameraUp;
	
	// convert to proper endian format
	QT3DTween_ConvertFloatToBigEndian(&theCameraData->placement.cameraLocation.x);
	QT3DTween_ConvertFloatToBigEndian(&theCameraData->placement.cameraLocation.y);
	QT3DTween_ConvertFloatToBigEndian(&theCameraData->placement.cameraLocation.z);
	
	QT3DTween_ConvertFloatToBigEndian(&theCameraData->placement.pointOfInterest.x);
	QT3DTween_ConvertFloatToBigEndian(&theCameraData->placement.pointOfInterest.y);
	QT3DTween_ConvertFloatToBigEndian(&theCameraData->placement.pointOfInterest.z);
	
	QT3DTween_ConvertFloatToBigEndian(&theCameraData->placement.upVector.x);
	QT3DTween_ConvertFloatToBigEndian(&theCameraData->placement.upVector.y);
	QT3DTween_ConvertFloatToBigEndian(&theCameraData->placement.upVector.z);
	
	theCameraData->range.yon = 1000.0;
	theCameraData->range.hither = 0.001;
	
	// convert to proper endian format
	QT3DTween_ConvertFloatToBigEndian(&theCameraData->range.yon);
	QT3DTween_ConvertFloatToBigEndian(&theCameraData->range.hither);
	
	theCameraData->viewPort.origin.x = -1.0;
	theCameraData->viewPort.origin.y = 1.0;
	theCameraData->viewPort.width = 2.0;
	theCameraData->viewPort.height = 2.0;
	
	// convert to proper endian format
	QT3DTween_ConvertFloatToBigEndian(&theCameraData->viewPort.origin.x);
	QT3DTween_ConvertFloatToBigEndian(&theCameraData->viewPort.origin.y);
	QT3DTween_ConvertFloatToBigEndian(&theCameraData->viewPort.width);
	QT3DTween_ConvertFloatToBigEndian(&theCameraData->viewPort.height);
}


//////////
//
// QT3DTween_GetStartCameraData
// Fill in a TQ3CameraData structure with the starting tween data.
//
// This data will eventually be written to the movie file as sample data,
// so we need to make sure the data is returned in big-endian format.
//
//////////

void QT3DTween_GetStartCameraData (TQ3CameraData *theCameraData)
{
	TQ3Point3D		myCameraFrom 	= {0.0, 0.0, 40.0};
	
	// reset the original camera location
	theCameraData->placement.cameraLocation = myCameraFrom;

	// set the initial placement of camera
	theCameraData->placement.cameraLocation.x *= k3DPlacementFactor;
	theCameraData->placement.cameraLocation.y *= k3DPlacementFactor;
	theCameraData->placement.cameraLocation.z *= k3DPlacementFactor;
	
	// convert to proper endian format
	QT3DTween_ConvertFloatToBigEndian(&theCameraData->placement.cameraLocation.x);
	QT3DTween_ConvertFloatToBigEndian(&theCameraData->placement.cameraLocation.y);
	QT3DTween_ConvertFloatToBigEndian(&theCameraData->placement.cameraLocation.z);
}


//////////
//
// QT3DTween_CreateTweenMovie
// Create a movie containing a 3D track and a tween track.
//
// To create a tween movie, we need to create a movie containing two tracks, a 3D data track
// and a tween media track. The 3D data track is straightforward: it contains a media sample
// that references a 3DMF file.
//
// The tween media track contains one or more tween media samples. A tween media sample is a
// QTAtomContainer that contains one or more kTweenEntry atoms. Each atom defines a separate
// tweening operation.
//
// Once we've created the 3D track and the tween track, we need to link the two tracks by in-
// serting a reference from the tween track to the 3D track. We also need to update the input
// map of the 3D track, to indicate how it should interpret the data received from the tween
// track. An input map is a QTAtomContainer that contains a kTrackModifierInput atom, which in 
// turn contains two leaf atoms, one atom of type kTrackModifierType and another atom of type
// kInputMapSubInputID.
//
//////////

OSErr QT3DTween_CreateTweenMovie (void)
{
	Handle						myHandle = NULL;
	ThreeDeeDescriptionHandle	mySampleDesc = NULL;
	short						myResRefNum = 0;
	short						myResID = movieInDataForkResID;
	Movie						myMovie = NULL;
	Track						my3DTrack, myTweenTrack;
	Media						my3DMedia, myTweenMedia;
	FSSpec						myFile;
	Boolean						myIsSelected = false;
	Boolean						myIsReplacing = false;	
	StringPtr 					myPrompt = QTUtils_ConvertCToPascalString(k3DTweenSavePrompt);
	StringPtr 					myFileName = QTUtils_ConvertCToPascalString(k3DTweenSaveMovieFileName);
	QTAtomContainer				mySample = NULL;
	QTAtomContainer				myInputMap = NULL;
	QTAtom						myAtom = 0;
	QTAtom						myInputAtom = 0;
	TQ3CameraData				myCameraData;
	TQ3RotateTransformData		myRotateData;
	long						mySize;
	long						myRefIndex;
	long						myFlags = createMovieFileDeleteCurFile | createMovieFileDontCreateResFile;
	OSErr						myErr = noErr;
	
	//////////
	//
	// create the 3D track and media, which contains a reference to the model (in 3DMF format)
	//
	//////////
	
	// get an alias to a 3DMF file, as well as the size of the data in that file
	myErr = QT3DTween_Get3DMFFileAliasAndSize(&myHandle, &mySize);
	if (myErr != noErr)
		goto bail;

	// prompt user for the destination file name
	QTFrame_PutFile(myPrompt, myFileName, &myFile, &myIsSelected, &myIsReplacing);
	if (!myIsSelected)
		goto bail;
		
	// create a movie file for the destination movie
	myErr = CreateMovieFile(&myFile, FOUR_CHAR_CODE('TVOD'), smCurrentScript, myFlags, &myResRefNum, &myMovie);
	if (myErr != noErr)
		goto bail;

	// create the 3D track and media
	my3DTrack = NewMovieTrack(myMovie, k3DMovieWidth, k3DMovieHeight, kNoVolume);
	my3DMedia = NewTrackMedia(my3DTrack, ThreeDeeMediaType, k3DTimeScale, myHandle, rAliasType);
	
	// create the sample description
	mySampleDesc = (ThreeDeeDescriptionHandle)NewHandleClear(sizeof(ThreeDeeDescription));
	if (mySampleDesc == NULL)
		goto bail;
	
	// fill in the fields of the sample description;
	// by zeroing out the structure when we created it, we get default values for all the fields;
	// (if you have non-zero values in any fields following the dataRefIndex field, make sure you
	// convert your data to big-endian format)
	(**mySampleDesc).descSize = sizeof(ThreeDeeDescription);
		
	// add the 3DMF data reference as a sample to the media
	myErr = AddMediaSampleReference(my3DMedia, 0, (unsigned long)mySize, k3DDuration, (SampleDescriptionHandle)mySampleDesc, 1, 0, NULL);
	if (myErr != noErr)
		goto bail;
	
	// insert the 3D media into the track
	myErr = InsertMediaIntoTrack(my3DTrack, 0, 0, GetMediaDuration(my3DMedia), fixed1);
	if (myErr != noErr)
		goto bail;
		
	// dispose of some things we no longer need
	DisposeHandle(myHandle);
	myHandle = NULL;
	
	DisposeHandle((Handle)mySampleDesc);
	mySampleDesc = NULL;
	
	//////////
	//
	// create the tween track and media, and a sample to contain the tween data
	//
	// NOTE: we can add as many tween entries to the tween sample as we like;
	// for present purposes, we'll illustrate adding (1) some camera tweening data and
	// (2) some object rotation tweening data.
	//
	//////////

	// create the tween track and media
	myTweenTrack = NewMovieTrack(myMovie, 0, 0, kNoVolume);
	myTweenMedia = NewTrackMedia(myTweenTrack, TweenMediaType, k3DTimeScale, NULL, 0);

	// create a new sample; this sample will hold the tween data
	myErr = QTNewAtomContainer(&mySample);
	if (myErr != noErr)
		goto bail;
		
	if (gAddCameraTweenData) {
		// add some camera tweening data to the tween media sample

		// get the camera data for the final frame
		QT3DTween_GetFinalCameraData(&myCameraData);
		
		// make a tween entry for that data;
		// a tween entry is a parent atom that contains leaf atoms describing the tweening operation
		myErr = QT3DTween_AddTweenEntryToSample(mySample, k3DCameraTweenID, kTweenType3dCameraData, &myCameraData, sizeof(myCameraData));
		if (myErr != noErr)
			goto bail;
		
		// get the camera data for the start frame
		QT3DTween_GetStartCameraData(&myCameraData);
		
		// add the initial tween data to the sample
		myErr = QT3DTween_SetTweenEntryInitialConditions(mySample, k3DCameraTweenID, &myCameraData, sizeof(myCameraData));
			
		//////////
		//
		// if you want to add other atoms to the tween entry, you can do it here;
		// for instance, you can use the following lines to make the duration of the tweening operation
		// different from the duration of the sample:
		//
		//	{
		//		TimeValue		myDuration;
		//
		//		myDuration = 1000;
		//		myErr = QT3DTween_SetTweenEntryDuration(mySample, k3DCameraTweenID, myDuration);
		//		if (myErr != noErr)
		//			goto bail;
		//	}
		//
		//////////
	
	} // if (gAddCameraTweenData)
	
	if (gAddRotationTweenData) {
		// add some object rotation data to the tween media sample
		
		// get the rotation data for the final frame
		myRotateData.axis = (TQ3Axis)EndianU32_NtoB(kQ3AxisY);
		myRotateData.radians = 2 * 3.14159;
		QT3DTween_ConvertFloatToBigEndian(&myRotateData.radians);
		myErr = QT3DTween_AddTweenEntryToSample(mySample, k3DRotationTweenID, kTweenType3dRotate, &myRotateData, sizeof(myRotateData));
		if (myErr != noErr)
			goto bail;

		// get the rotation data for the start frame
		myRotateData.axis = (TQ3Axis)EndianU32_NtoB(kQ3AxisY);
		myRotateData.radians = 0.0;
		QT3DTween_ConvertFloatToBigEndian(&myRotateData.radians);
		myErr = QT3DTween_SetTweenEntryInitialConditions(mySample, k3DRotationTweenID, &myRotateData, sizeof(myRotateData));
		if (myErr != noErr)
			goto bail;
			
	} // if (gAddRotationTweenData)
		
	// create the sample description
	mySampleDesc = (ThreeDeeDescriptionHandle)NewHandleClear(sizeof(SampleDescription));
	if (mySampleDesc == NULL)
		goto bail;
	
	(**mySampleDesc).descSize = sizeof(SampleDescription);
		
	// add the tween sample to the media
	BeginMediaEdits(myTweenMedia);
		
	myErr = AddMediaSample(myTweenMedia, mySample, 0, GetHandleSize(mySample), k3DDuration, (SampleDescriptionHandle)mySampleDesc, 1, 0, NULL);
	if (myErr != noErr)
		goto bail;
		
	EndMediaEdits(myTweenMedia);
	
	// add the media to the track
	InsertMediaIntoTrack(myTweenTrack, 0, 0, GetMediaDuration(myTweenMedia), fixed1);
	
	// dispose of some things we no longer need
	QTDisposeAtomContainer(mySample);
	mySample = NULL;
	
	DisposeHandle((Handle)mySampleDesc);
	mySampleDesc = NULL;

	//////////
	//
	// create a link between the 3D track and the tween track, and update the 3D track's input map
	//
	//////////
	
	// first, create a new atom container
	myErr = QTNewAtomContainer(&myInputMap);
	if (myErr != noErr)
		goto bail;
	
	// for *each* tween entry in the tween media sample, 	
	// create a link between the 3D track and the tween track, and add an entry to the input map
	
	if (gAddCameraTweenData) {
		myErr = AddTrackReference(my3DTrack, myTweenTrack, kTrackModifierReference, &myRefIndex);
		if (myErr != noErr)
			goto bail;
			
		myErr = QT3DTween_AddTweenEntryToInputMap(myInputMap, myRefIndex, k3DCameraTweenID, kTrackModifierCameraData, NULL);
		if (myErr != noErr)
			goto bail;
	}
	
	if (gAddRotationTweenData) {
		myErr = AddTrackReference(my3DTrack, myTweenTrack, kTrackModifierReference, &myRefIndex);
		if (myErr != noErr)
			goto bail;
			
		myErr = QT3DTween_AddTweenEntryToInputMap(myInputMap, myRefIndex, k3DRotationTweenID, kTrackModifierType3d4x4Matrix, NULL);
		if (myErr != noErr)
			goto bail;
	}

    // attach the input map to the 3D media handler
    myErr = SetMediaInputMap(my3DMedia, myInputMap);
	if (myErr != noErr)
		goto bail;
	
    // dispose of the input map
    QTDisposeAtomContainer(myInputMap);
	myInputMap = NULL;
		
	//////////
	//
	// finish up
	//
	//////////
	
	// add the movie resource to the movie file
	myErr = AddMovieResource(myMovie, myResRefNum, &myResID, NULL);

bail:
	free(myPrompt);
	free(myFileName);

	if (mySample != NULL)
		QTDisposeAtomContainer(mySample);

	if (myInputMap != NULL)
		QTDisposeAtomContainer(myInputMap);

	if (mySampleDesc != NULL)
		DisposeHandle((Handle)mySampleDesc);
	
	if (myResRefNum != 0)
		CloseMovieFile(myResRefNum);

	if (myHandle != NULL)
		DisposeHandle(myHandle);

	if (myMovie != NULL)
		DisposeMovie(myMovie);
	
	return(myErr);
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Tween utilities.
//
// Use these functions add tween entries to samples or to add attributes to tween entries.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////
//
// QT3DTween_AddTweenEntryToSample
// Add a tween entry to the specified sample.
//
// A tween entry defines a set of values for a single tweening operation. A tween entry is a parent atom
// whose children define the tween data type, the tween data, and additional attributes of the operation.
//
// The data specified in the theData parameter is assumed to be in big-endian format.
//
//////////

OSErr QT3DTween_AddTweenEntryToSample (QTAtomContainer theSample, QTAtomID theID, QTAtomType theType, void *theData, long theDataSize)
{
	QTAtom				myAtom;
	OSErr				myErr = noErr;
	
	// create an entry for this tween in the sample
	myErr = QTInsertChild(theSample, kParentAtomIsContainer, kTweenEntry, theID, 0, 0, NULL, &myAtom);
	if (myErr != noErr)
		goto bail;
	
	// set the type of this tween entry
	// a kTweenEntry atom can contain only one kTweenType atom, whose ID and index must be 1
	theType = EndianU32_NtoB(theType);
	myErr = QTInsertChild(theSample, myAtom, kTweenType, 1, 0, sizeof(theType), &theType, NULL);
	if (myErr != noErr)
		goto bail;
	
	// set the data for this tween entry
	myErr = QTInsertChild(theSample, myAtom, kTweenData, 1, 0, theDataSize, theData, NULL);
	
bail:
	return(myErr);
}


//////////
//
// QT3DTween_AddTweenEntryToInputMap
// Add a tween entry to the specified input map.
//
//////////

OSErr QT3DTween_AddTweenEntryToInputMap (QTAtomContainer theInputMap, long theRefIndex, long theID, OSType theType, char *theName)
{
	QTAtom				myInputAtom;
	OSErr				myErr = noErr;
	
	// add an entry to the input map
	myErr = QTInsertChild(theInputMap, kParentAtomIsContainer, kTrackModifierInput, theRefIndex, 0, 0, NULL, &myInputAtom);
	if (myErr != noErr)
		goto bail;
	
	// add two child atoms to the parent atom;
	// these atoms define the type of the modifier input and the ID of the tween entry atom
	theType = EndianU32_NtoB(theType);
	myErr = QTInsertChild(theInputMap, myInputAtom, kTrackModifierType, 1, 0, sizeof(OSType), &theType, NULL);
	if (myErr != noErr)
		goto bail;
	
	theID = EndianU32_NtoB(theID);
	myErr = QTInsertChild(theInputMap, myInputAtom, kInputMapSubInputID, 1, 0, sizeof(long), &theID, NULL);
	if (myErr != noErr)
		goto bail;
		
	// set the name of the input atom
	if (theName != NULL) {
		long		myLength = 1;
		Ptr			myPtr = theName;
		UInt16		*myShort;
		
		// determine the length of the name string
		while (*myPtr++)
			myLength++;
		
		// convert the name string to the proper endian format
		myPtr = theName;
		while (*myPtr) {
			myShort = (UInt16 *)myPtr;
			*myPtr = EndianU16_NtoB(*myShort);
			myPtr = myPtr + 2;	// point to next word
		}
		
		myErr = QTInsertChild(theInputMap, myInputAtom, kTrackModifierInputName, 1, 0, myLength, theName, NULL);
	}
	
bail:
	return(myErr);
}


//////////
//
// QT3DTween_SetTweenEntryInitialConditions
// Set the initial conditions of the specified tween entry.
//
//////////

OSErr QT3DTween_SetTweenEntryInitialConditions (QTAtomContainer theSample, QTAtomID theID, void *theData, long theDataSize)
{
	QTAtom				myAtom;
	OSErr				myErr = noErr;
	
	// find the tween entry with the specified ID
	myAtom = QTFindChildByID(theSample, kParentAtomIsContainer, kTweenEntry, theID, NULL);
	if (myAtom == 0)
		return(paramErr);
	
	// add in the initial conditions data;
	// a kTweenEntry atom can contain only one kTween3dInitialCondition atom, whose ID and index must be 1
	myErr = QTInsertChild(theSample, myAtom, kTween3dInitialCondition, 1, 0, theDataSize, theData, NULL);
		
	return(myErr);
}


//////////
//
// QT3DTween_SetTweenEntryDuration
// Set the duration of the specified tween entry.
//
//////////

OSErr QT3DTween_SetTweenEntryDuration (QTAtomContainer theSample, QTAtomID theID, TimeValue theDuration)
{
	QTAtom				myAtom;
	OSErr				myErr = noErr;
	
	// find the tween entry with the specified ID
	myAtom = QTFindChildByID(theSample, kParentAtomIsContainer, kTweenEntry, theID, NULL);
	if (myAtom == 0)
		return(paramErr);
	
	// add in the duration
	theDuration = EndianS32_NtoB(theDuration);
	// a kTweenEntry atom can contain only one kTweenDuration atom, whose ID and index must be 1
	myErr = QTInsertChild(theSample, myAtom, kTweenDuration, 1, 0, sizeof(TimeValue), &theDuration, NULL);
		
	return(myErr);
}


//////////
//
// QT3DTween_SetTweenEntryStartOffset
// Set the starting offset of the specified tween entry.
//
//////////

OSErr QT3DTween_SetTweenEntryStartOffset (QTAtomContainer theSample, QTAtomID theID, TimeValue theOffset)
{
	QTAtom				myAtom;
	OSErr				myErr = noErr;
	
	// find the tween entry with the specified ID
	myAtom = QTFindChildByID(theSample, kParentAtomIsContainer, kTweenEntry, theID, NULL);
	if (myAtom == 0)
		return(paramErr);
	
	// add in the starting offset
	// a kTweenEntry atom can contain only one kTweenStartOffset atom, whose ID and index must be 1
	theOffset = EndianS32_NtoB(theOffset);
	myErr = QTInsertChild(theSample, myAtom, kTweenStartOffset, 1, 0, sizeof(TimeValue), &theOffset, NULL);
		
	return(myErr);
}


//////////
//
// QT3DTween_ConvertFloatToBigEndian
// Convert the specified floating-point number to big-endian format.
//
//////////

void QT3DTween_ConvertFloatToBigEndian (float *theFloat)
{
	unsigned long		*myLongPtr;
	
	myLongPtr = (unsigned long *)theFloat;
	*myLongPtr = EndianU32_NtoB(*myLongPtr);
}


//////////
//
// QT3DTween_FilterFiles
// Filter files for a file-opening dialog box.
//
//////////

#if TARGET_OS_MAC
PASCAL_RTN Boolean QT3DTween_FilterFiles (AEDesc *theItem, void *theInfo, void *theCallBackUD, NavFilterModes theFilterMode)
{
#pragma unused(theCallBackUD, theFilterMode)
	NavFileOrFolderInfo		*myInfo = (NavFileOrFolderInfo *)theInfo;
	
	if (theItem->descriptorType == typeFSS) {
		if (!myInfo->isFolder) {
			OSType			myType = myInfo->fileAndFolder.fileInfo.finderInfo.fdType;
			
			if (myType == kQTFileType3DMF)
				return(true);
			
			// if we got to here, it's a file we cannot open
			return(false);		
		}
	}
	
	// if we got to here, it's a folder or non-HFS object
	return(true);
}
#endif
#if TARGET_OS_WIN32
PASCAL_RTN Boolean QT3DTween_FilterFiles (CInfoPBPtr thePBPtr)
{
#pragma unused(thePBPtr)
	return(false);
}
#endif
