1/* 2 * MovieQTVRUtils.c -- 3 * 4 * Utilities for QTVR movies, part of QuickTimeTcl. 5 * Several code snippets from Apples AddVRActions example. 6 * 7 * $Id: MovieQTVRUtils.c,v 1.1.1.1 2003/04/04 16:24:24 matben Exp $ 8 */ 9 10#ifdef _WIN32 11# include "QuickTimeTclWin.h" 12#endif 13 14#include "QuickTimeTcl.h" 15 16#define kFloat 1 17#define kBoolean 2 18 19/* 20 * For dispatching timecode commands. 21 */ 22 23static char *allHotSpotCmds[] = { 24 "configure", "setid", "setwiredactions", 25 (char *) NULL 26}; 27 28enum { 29 kHotSpotCmdConfigure = 0L, 30 kHotSpotCmdSetID, 31 kHotSpotCmdSetWiredActions 32}; 33 34/* -enabled treated separately. */ 35static char *allHotSpotSetWiredOptions[] = { 36 "-fov", "-pan", "-tilt", 37 (char *) NULL 38}; 39 40enum { 41 kHotSpotSetWiredFov = 0L, 42 kHotSpotSetWiredPan, 43 kHotSpotSetWiredTilt 44}; 45 46static OSErr ReplaceQTVRMedia( Movie movie, QTVRInstance qtvrInst, 47 QTAtomContainer nodeInfoContainer ); 48static int AddWiredActionsToHotSpot( Tcl_Interp *interp, Movie movie, 49 QTVRInstance qtvrInst, int hotspotID, int objc, Tcl_Obj *CONST objv[] ); 50static int CreateHotSpotActionContainer( Tcl_Interp *interp, 51 QTAtomContainer *actionContainer, int objc, Tcl_Obj *CONST objv[] ); 52static OSErr SetWiredActionsToHotSpot ( Handle theSample, long theHotSpotID, 53 QTAtomContainer actionContainer ); 54static OSErr WriteTheMediaPropertyAtom ( Media theMedia, long propertyID, 55 long thePropertySize, void *theProperty ); 56static OSErr CreateFrameLoadedHotSpotEnabledActionContainer( 57 QTAtomContainer *actionContainer, 58 long hotSpotID, Boolean enabled ); 59static OSErr SetWiredActionsToNode ( Handle theSample, 60 QTAtomContainer actionContainer, UInt32 theActionType ); 61 62 63 64/* 65 *---------------------------------------------------------------------- 66 * 67 * ProcessHotspotSubCmd -- 68 * 69 * Process the "hotspot" subcommand. 70 * 71 * Results: 72 * Normal TCL results 73 * 74 * Side effects: 75 * Depends on the command. 76 * 77 *---------------------------------------------------------------------- 78 */ 79 80int 81ProcessHotspotSubCmd( Tcl_Interp *interp, 82 Movie movie, 83 QTVRInstance qtvrInst, 84 int objc, 85 Tcl_Obj *CONST objv[] ) 86{ 87 int result = TCL_OK; 88 int nodeID; 89 int hotspotID; 90 int newHotspotID; 91 int iarg; 92 int len; 93 int rebuild = false; 94 int cmdIndex; 95 int intValue; 96 short index; 97 OSErr err = noErr; 98 QTAtomContainer nodeInfoContainer = NULL; 99 QTAtomContainer vrWorld = NULL; 100 QTVRHotSpotInfoAtomPtr hotspotInfoAtomPtr; 101 QTAtom hotspotParentAtom = 0; 102 QTAtom hotspotAtom = 0; 103 QTAtom hotspotInfoAtom = 0; 104 QTAtom nameAtom = 0; 105 QTAtomID atomID; 106 Boolean enabled; 107 Tcl_DString ds; 108 109 if (objc < 3) { 110 Tcl_WrongNumArgs( interp, 0, objv, 111 "pathName hotspot command nodeId hotspotId ?args?" ); 112 return TCL_ERROR; 113 } 114 if (GetNodeCount( qtvrInst ) != 1) { 115 Tcl_SetObjResult( interp, Tcl_NewStringObj( 116 "Didn't recognize this as a single node QTVR movie", -1 ) ); 117 return TCL_ERROR; 118 } 119 if (Tcl_GetIntFromObj( interp, objv[1], &nodeID ) != TCL_OK) { 120 Tcl_AddErrorInfo( interp, "\n (processing nodeID value)" ); 121 return TCL_ERROR; 122 } 123 nodeID = kQTVRCurrentNode; 124 nodeID = QTVRGetCurrentNodeID( qtvrInst ); 125 if (Tcl_GetIntFromObj( interp, objv[2], &hotspotID ) != TCL_OK) { 126 Tcl_AddErrorInfo( interp, "\n (processing hotspotID value)" ); 127 return TCL_ERROR; 128 } 129 130 /* 131 * This gets only a *copy* of the atom container maintained internally. 132 * If we make any changes, we need to add it back, see below. 133 */ 134 135 err = QTVRGetNodeInfo( qtvrInst, nodeID, &nodeInfoContainer ); 136 if (err != noErr) { 137 CheckAndSetErrorResult( interp, err ); 138 return TCL_ERROR; 139 } 140 QTLockContainer( nodeInfoContainer ); 141 142 /* 143 * Get hotspot parent atom which is the parent of all hot spot atoms of the node. 144 */ 145 146 hotspotParentAtom = QTFindChildByID( nodeInfoContainer, kParentAtomIsContainer, 147 kQTVRHotSpotParentAtomType, 1, NULL ); 148 if (hotspotParentAtom != 0) { 149 hotspotAtom = QTFindChildByID( nodeInfoContainer, hotspotParentAtom, 150 kQTVRHotSpotAtomType, hotspotID, &index ); 151 if (hotspotAtom != 0) { 152 153 /* 154 * Get the hotspot info atom here. 155 */ 156 157 hotspotInfoAtom = QTFindChildByIndex( nodeInfoContainer, hotspotAtom, 158 kQTVRHotSpotInfoAtomType, 1, &atomID ); 159 if (hotspotInfoAtom != 0) { 160 err = QTGetAtomDataPtr( nodeInfoContainer, hotspotInfoAtom, NULL, 161 (Ptr *) &hotspotInfoAtomPtr ); 162 if (err == noErr) { 163 if (EndianS32_BtoN(hotspotInfoAtomPtr->nameAtomID) != 0) { 164 nameAtom = QTFindChildByID( nodeInfoContainer, hotspotAtom, 165 kQTVRStringAtomType, 166 EndianS32_BtoN(hotspotInfoAtomPtr->nameAtomID), NULL ); 167 } 168 } 169 } 170 } 171 } 172 173 /* 174 * Must be unlocked if we want to change something. 175 */ 176 177 QTUnlockContainer( nodeInfoContainer ); 178 179 if (Tcl_GetIndexFromObj( interp, objv[0], allHotSpotCmds, "hotspot command", 180 TCL_EXACT, &cmdIndex ) != TCL_OK ) { 181 return TCL_ERROR; 182 } 183 184 switch (cmdIndex) { 185 186 case kHotSpotCmdConfigure: { 187 188 /* 189 * Parse configure command options. Starting with iarg 3. 190 */ 191 192 if (objc % 2 == 0) { 193 Tcl_WrongNumArgs( interp, 0, objv, 194 "pathName hotspot configure nodeId hotspotId -key value ?-key value?" ); 195 result = TCL_ERROR; 196 goto error; 197 } 198 for (iarg = 3; iarg < objc; iarg = iarg + 2) { 199 200 if (strcmp(Tcl_GetString( objv[iarg] ), "-enabled") == 0) { 201 if (Tcl_GetBooleanFromObj( interp, objv[iarg+1], &intValue ) 202 != TCL_OK) { 203 Tcl_AddErrorInfo( interp, 204 "\n (processing -enabled option)" ); 205 return TCL_ERROR; 206 } 207 enabled = (Boolean) intValue; 208 err = QTVREnableHotSpot( qtvrInst, kQTVRHotSpotID, hotspotID, enabled ); 209 if (err != noErr) { 210 CheckAndSetErrorResult( interp, err ); 211 result = TCL_ERROR; 212 goto error; 213 } 214 } else if (strcmp(Tcl_GetString( objv[iarg] ), "-name") == 0) { 215 if (nameAtom != 0) { 216 QTVRStringAtomPtr strAtomPtr = NULL; 217 UInt16 size; 218 219 Tcl_UtfToExternalDString( gQTTclTranslationEncoding, 220 Tcl_GetString( objv[iarg+1] ), -1, &ds ); 221 len = Tcl_DStringLength(&ds); 222 len = (len > 250) ? 250 : len; 223 224 size = sizeof(QTVRStringAtom) - 4 + len; 225 strAtomPtr = (QTVRStringAtomPtr) NewPtrClear(size); 226 227 if (strAtomPtr != NULL) { 228 strAtomPtr->stringUsage = EndianU16_NtoB(1); 229 strAtomPtr->stringLength = EndianU16_NtoB(len); 230 BlockMove( Tcl_DStringValue(&ds), strAtomPtr->theString, len ); 231 err = QTSetAtomData( nodeInfoContainer, nameAtom, size, 232 (Ptr) strAtomPtr ); 233 DisposePtr( (Ptr) strAtomPtr ); 234 Tcl_DStringFree( &ds ); 235 if (err != noErr) { 236 CheckAndSetErrorResult( interp, err ); 237 result = TCL_ERROR; 238 goto error; 239 } 240 rebuild = true; 241 } 242 } 243 } else { 244 Tcl_SetObjResult( interp, Tcl_NewStringObj( 245 "Unrecognized option for hotspot configure", -1 ) ); 246 result = TCL_ERROR; 247 goto error; 248 } 249 } 250 break; 251 } 252 253 case kHotSpotCmdSetID: { 254 255 /* 256 * The hotspot id should be changed to a new id. 257 */ 258 259 if (Tcl_GetIntFromObj( interp, objv[3], &newHotspotID ) 260 != TCL_OK) { 261 Tcl_AddErrorInfo( interp, 262 "\n (processing newHotspotID option)" ); 263 return TCL_ERROR; 264 } 265 err = QTSetAtomID( nodeInfoContainer, hotspotAtom, newHotspotID ); 266 if (err != noErr) { 267 CheckAndSetErrorResult( interp, err ); 268 result = TCL_ERROR; 269 goto error; 270 } 271 rebuild = true; 272 break; 273 } 274 275 case kHotSpotCmdSetWiredActions: { 276 277 /* 278 * Set wired actions for the specified hotspot. We rebuild inside. 279 */ 280 281 result = AddWiredActionsToHotSpot( interp, movie, qtvrInst, hotspotID, 282 objc - 3, objv + 3 ); 283 if (result != TCL_OK) { 284 goto error; 285 } 286 break; 287 } 288 } 289 290 if (rebuild) { 291 err = ReplaceQTVRMedia( movie, qtvrInst, nodeInfoContainer ); 292 if (err != noErr) { 293 CheckAndSetErrorResult( interp, err ); 294 result = TCL_ERROR; 295 goto error; 296 } 297 } 298 QTVRUpdate( qtvrInst, kQTVRStatic ); 299 300error: 301 if (nodeInfoContainer != NULL) { 302 QTDisposeAtomContainer( nodeInfoContainer ); 303 } 304 if (vrWorld != NULL) { 305 QTDisposeAtomContainer( vrWorld ); 306 } 307 return result; 308} 309 310/* 311 *---------------------------------------------------------------------- 312 * 313 * AddWiredActionsToHotSpot -- 314 * 315 * 316 * 317 * Results: 318 * Tcl result 319 * 320 * Side effects: 321 * None. 322 * 323 *---------------------------------------------------------------------- 324 */ 325 326static int 327AddWiredActionsToHotSpot( Tcl_Interp *interp, 328 Movie movie, 329 QTVRInstance qtvrInst, 330 int hotspotID, 331 int objc, 332 Tcl_Obj *CONST objv[] ) 333{ 334 Track track = NULL; 335 Media media = NULL; 336 TimeValue trackOffset; 337 TimeValue mediaTime; 338 TimeValue sampleDuration; 339 QTVRSampleDescriptionHandle theQTVRDesc = NULL; 340 Handle sample = NULL; 341 short sampleFlags; 342 QTAtomContainer actionContainer = NULL; 343 Boolean hasActions; 344 Boolean enabled; 345 OSErr err = noErr; 346 int result = TCL_OK; 347 int iarg; 348 int hsObjc = 0; 349 int frameObjc = 0; 350 int booleanInt; 351 Tcl_Obj *hsObjv[10]; 352 Tcl_Obj *frameObjv[10]; 353 354 track = QTVRGetQTVRTrack( movie, 1 ); 355 if (track == NULL) { 356 CheckAndSetErrorResult( interp, noErr ); 357 goto bail; 358 } 359 360 /* Get the first media sample in the QTVR track. 361 * 362 * The QTVR track contains one media sample for each node in the movie; 363 * that sample contains a node information atom container, which contains 364 * general information about the node (such as its type, its ID, its name, 365 * and a list of its hot spots) 366 */ 367 368 media = GetTrackMedia(track); 369 if (media == NULL) { 370 CheckAndSetErrorResult( interp, noErr ); 371 goto bail; 372 } 373 trackOffset = GetTrackOffset( track ); 374 mediaTime = TrackTimeToMediaTime( trackOffset, track ); 375 376 theQTVRDesc = (QTVRSampleDescriptionHandle) NewHandle(4); 377 if (theQTVRDesc == NULL) { 378 CheckAndSetErrorResult( interp, noErr ); 379 goto bail; 380 } 381 sample = NewHandle(0); 382 if (sample == NULL) { 383 CheckAndSetErrorResult( interp, noErr ); 384 goto bail; 385 } 386 err = GetMediaSample( media, sample, 0, NULL, mediaTime, NULL, 387 &sampleDuration, (SampleDescriptionHandle) theQTVRDesc, NULL, 1, 388 NULL, &sampleFlags ); 389 if (err != noErr) { 390 CheckAndSetErrorResult( interp, noErr ); 391 goto bail; 392 } 393 394 /* 395 * Some of the arguments must be stored in a 'frame loaded container', 396 * while the other shall be put in the 'hotspot action container'. 397 */ 398 399 for (iarg = 0; iarg < objc; iarg = iarg + 2) { 400 if ((strcmp(Tcl_GetString( objv[iarg] ), "-enabled") == 0)) { 401 frameObjv[frameObjc] = objv[iarg]; 402 frameObjv[frameObjc+1] = objv[iarg+1]; 403 frameObjc += 2; 404 if (Tcl_GetBooleanFromObj( interp, frameObjv[iarg+1], 405 &booleanInt ) != TCL_OK) { 406 Tcl_AddErrorInfo( interp, 407 "\n (processing -enabled option)" ); 408 result = TCL_ERROR; 409 goto bail; 410 } 411 enabled = (Boolean) booleanInt; 412 } else { 413 hsObjv[hsObjc] = objv[iarg]; 414 hsObjv[hsObjc+1] = objv[iarg+1]; 415 hsObjc += 2; 416 } 417 } 418 419 /* 420 * The enabled action is put in a frame loaded container. 421 */ 422 423 if (frameObjc > 0) { 424 err = CreateFrameLoadedHotSpotEnabledActionContainer( &actionContainer, 425 hotspotID, enabled ); 426 if (err != noErr) { 427 CheckAndSetErrorResult( interp, err ); 428 goto bail; 429 } 430 err = SetWiredActionsToNode ( sample, actionContainer, kQTEventFrameLoaded ); 431 if (err != noErr) { 432 CheckAndSetErrorResult( interp, err ); 433 goto bail; 434 } 435 if (actionContainer != NULL) { 436 QTDisposeAtomContainer(actionContainer); 437 actionContainer = NULL; 438 } 439 } 440 441 /* 442 * Add hot-spot actions. 443 */ 444 445 if (hsObjc > 0) { 446 err = CreateHotSpotActionContainer( interp, &actionContainer, hsObjc, hsObjv ); 447 if (err != noErr) { 448 CheckAndSetErrorResult( interp, err ); 449 goto bail; 450 } 451 err = SetWiredActionsToHotSpot( sample, hotspotID, actionContainer ); 452 if (err != noErr) { 453 CheckAndSetErrorResult( interp, err ); 454 goto bail; 455 } 456 if (actionContainer != NULL) { 457 QTDisposeAtomContainer(actionContainer); 458 actionContainer = NULL; 459 } 460 } 461 462 /* 463 * Replace sample in media. 464 */ 465 466 err = ReplaceQTVRMedia( movie, qtvrInst, (QTAtomContainer) sample ); 467 if (err != noErr) { 468 CheckAndSetErrorResult( interp, err ); 469 goto bail; 470 } 471 472 /* 473 * Set the actions property atom, to enable wired action processing. 474 */ 475 476 hasActions = true; 477 // since sizeof(Boolean) == 1, there is no need to swap bytes here 478 err = WriteTheMediaPropertyAtom( media, kSpriteTrackPropertyHasActions, 479 sizeof(Boolean), &hasActions ); 480 if (err != noErr) { 481 CheckAndSetErrorResult( interp, err ); 482 goto bail; 483 } 484 485bail: 486 if (actionContainer != NULL) { 487 QTDisposeAtomContainer(actionContainer); 488 } 489 if (sample != NULL) { 490 DisposeHandle(sample); 491 } 492 if (theQTVRDesc != NULL) { 493 DisposeHandle((Handle)theQTVRDesc); 494 } 495 if (result != TCL_OK) { 496 CheckAndSetErrorResult( interp, err ); 497 } 498 return result; 499} 500 501/* 502 *---------------------------------------------------------------------- 503 * 504 * ReplaceQTVRMedia -- 505 * 506 * 507 * 508 * Results: 509 * OSErr result 510 * 511 * Side effects: 512 * None. 513 * 514 *---------------------------------------------------------------------- 515 */ 516 517static OSErr 518ReplaceQTVRMedia( Movie movie, 519 QTVRInstance qtvrInst, 520 QTAtomContainer nodeInfoContainer ) // the actual sample (Handle) 521{ 522 OSErr err = noErr; 523 Track track = NULL; 524 Media media = NULL; 525 Handle sample = NULL; 526 short sampleFlags; 527 TimeValue sampleDuration; 528 TimeValue trackOffset; 529 TimeValue mediaTime; 530 TimeValue newMediaTime; 531 TimeValue selectionDuration; 532 Fixed trackEditRate; 533 QTVRSampleDescriptionHandle theQTVRDesc = NULL; 534 535 /* 536 * We need to replace the QTVR track with the newly edited information. 537 * A single node QTVR movie contains only a single media sample, with: 538 * 1) The VR World Atom Container as sample description, and 539 * 2) The Node Info Atom Container as the media sample. 540 * So replace the old with the new. Fails for multi node movies! 541 */ 542 543 track = QTVRGetQTVRTrack( movie, 1 ); 544 if (track == NULL) { 545 goto error; 546 } 547 media = GetTrackMedia( track ); 548 if (media == NULL) { 549 goto error; 550 } 551 trackOffset = GetTrackOffset( track ); 552 mediaTime = TrackTimeToMediaTime( trackOffset, track ); 553 theQTVRDesc = (QTVRSampleDescriptionHandle) NewHandle(4); 554 if (theQTVRDesc == NULL) { 555 goto error; 556 } 557 err = GetMediaSample( media, sample, 0, NULL, mediaTime, NULL, 558 &sampleDuration, (SampleDescriptionHandle) theQTVRDesc, NULL, 1, 559 NULL, &sampleFlags ); 560 if (err != noErr) { 561 goto error; 562 } 563 trackEditRate = GetTrackEditRate( track, trackOffset ); 564 if (GetMoviesError() != noErr) 565 goto error; 566 567 GetTrackNextInterestingTime( track, nextTimeMediaSample | nextTimeEdgeOK, 568 trackOffset, fixed1, NULL, &selectionDuration ); 569 if (GetMoviesError() != noErr) 570 goto error; 571 572 /* Get rid of the old QTVR sample first. */ 573 574 err = DeleteTrackSegment( track, trackOffset, selectionDuration ); 575 if (err != noErr) { 576 goto error; 577 } 578 err = BeginMediaEdits( media ); 579 if (err != noErr) { 580 goto error; 581 } 582 err = AddMediaSample( media, 583 (Handle) nodeInfoContainer, // the actual data 584 0, // no offset in data 585 GetHandleSize((Handle) nodeInfoContainer), // the data size 586 sampleDuration, // duration 587 (SampleDescriptionHandle) theQTVRDesc, 588 1, // one sample 589 sampleFlags, // key frame or not 590 &newMediaTime ); 591 if (err != noErr) { 592 goto error; 593 } 594 err = EndMediaEdits( media ); 595 if (err != noErr) { 596 goto error; 597 } 598 err = InsertMediaIntoTrack( track, // the track 599 trackOffset, // track time, 0 for single node 600 newMediaTime, // media start time in media's time 601 selectionDuration, // media's duration in media's time 602 trackEditRate ); // the media's rate 603 if (err != noErr) { 604 goto error; 605 } 606 607error: 608 if (sample != NULL) { 609 DisposeHandle( sample ); 610 } 611 return(err); 612} 613 614/* 615 *---------------------------------------------------------------------- 616 * 617 * CreateFrameLoadedHotSpotEnabledActionContainer -- 618 * 619 * 620 * 621 * Results: 622 * Standard OSErr result 623 * 624 * Side effects: 625 * 626 *---------------------------------------------------------------------- 627 */ 628 629static OSErr 630CreateFrameLoadedHotSpotEnabledActionContainer( QTAtomContainer *actionContainer, 631 long hotSpotID, Boolean enabled ) 632{ 633 QTAtom actionAtom = 0; 634 QTAtom eventAtom = 0; 635 long action; 636 OSErr err = noErr; 637 638 err = QTNewAtomContainer( actionContainer ); 639 if (err != noErr) { 640 goto bail; 641 } 642 err = QTInsertChild( *actionContainer, kParentAtomIsContainer, kQTEventFrameLoaded, 643 1, 1, 0, NULL, &eventAtom ); 644 if (err != noErr) { 645 goto bail; 646 } 647 err = QTInsertChild( *actionContainer, eventAtom, kAction, 1, 1, 0, NULL, 648 &actionAtom ); 649 if (err != noErr) { 650 goto bail; 651 } 652 action = EndianS32_NtoB( kActionQTVREnableHotSpot ); 653 hotSpotID = EndianS32_NtoB( hotSpotID ); 654 err = QTInsertChild( *actionContainer, actionAtom, kWhichAction, 1, 1, 655 sizeof(long), &action, NULL); 656 if (err != noErr) { 657 goto bail; 658 } 659 err = QTInsertChild( *actionContainer, actionAtom, kActionParameter, 0, 0, 660 sizeof(long), &hotSpotID, NULL ); 661 if (err != noErr) { 662 goto bail; 663 } 664 err = QTInsertChild( *actionContainer, actionAtom, kActionParameter, 0, 0, 665 sizeof(Boolean), &enabled, NULL ); 666 if (err != noErr) { 667 goto bail; 668 } 669 670bail: 671 return(err); 672} 673 674/* 675 *---------------------------------------------------------------------- 676 * 677 * CreateHotSpotActionContainer -- 678 * 679 * Return, through the actionContainer parameter, an atom container 680 * that contains one or many hot spot actions. 681 * 682 * Results: 683 * Standard TCL result 684 * 685 * Side effects: 686 * 687 *---------------------------------------------------------------------- 688 */ 689 690static int 691CreateHotSpotActionContainer( Tcl_Interp *interp, 692 QTAtomContainer *actionContainer, 693 int objc, 694 Tcl_Obj *CONST objv[] ) 695{ 696 QTAtom eventAtom = 0; 697 QTAtom actionAtom = 0; 698 long action; 699 float aFloat; 700 double aDouble; 701 OSErr err = noErr; 702 Tcl_Obj *resultObjPtr; 703 int iarg; 704 int optIndex; 705 706 err = QTNewAtomContainer( actionContainer ); 707 if (err != noErr) { 708 goto error; 709 } 710 err = QTInsertChild( *actionContainer, kParentAtomIsContainer, kQTEventType, 711 kQTEventMouseClick, 1, 0, NULL, &eventAtom ); 712 if (err != noErr) { 713 goto error; 714 } 715 716 /* 717 * Parse configure command options. Starting with iarg 0. 718 */ 719 720 for (iarg = 0; iarg < objc; iarg = iarg + 2) { 721 722 if (Tcl_GetIndexFromObj( interp, objv[iarg], allHotSpotSetWiredOptions, 723 "hotspot setwiredactions option", TCL_EXACT, &optIndex ) != TCL_OK ) { 724 goto error; 725 } 726 if (iarg + 1 == objc) { 727 resultObjPtr = Tcl_GetObjResult( interp ); 728 Tcl_AppendStringsToObj( resultObjPtr, "value for \"", 729 Tcl_GetString(objv[iarg]), "\"missing", (char *) NULL ); 730 goto error; 731 } 732 733 switch (optIndex) { 734 735 case kHotSpotSetWiredFov: { 736 action = EndianS32_NtoB( kActionQTVRSetFieldOfView ); 737 if (Tcl_GetDoubleFromObj( interp, objv[iarg+1], &aDouble ) 738 != TCL_OK) { 739 Tcl_AddErrorInfo( interp, 740 "\n (processing -fov option)" ); 741 goto error; 742 } 743 aFloat = (float) aDouble; 744 ConvertFloatToBigEndian( &aFloat ); 745 break; 746 } 747 748 case kHotSpotSetWiredPan: { 749 action = EndianS32_NtoB( kActionQTVRSetPanAngle ); 750 if (Tcl_GetDoubleFromObj( interp, objv[iarg+1], &aDouble ) 751 != TCL_OK) { 752 Tcl_AddErrorInfo( interp, 753 "\n (processing -pan option)" ); 754 goto error; 755 } 756 aFloat = (float) aDouble; 757 ConvertFloatToBigEndian( &aFloat ); 758 break; 759 } 760 761 case kHotSpotSetWiredTilt: { 762 action = EndianS32_NtoB( kActionQTVRSetTiltAngle ); 763 if (Tcl_GetDoubleFromObj( interp, objv[iarg+1], &aDouble ) 764 != TCL_OK) { 765 Tcl_AddErrorInfo( interp, 766 "\n (processing -tilt option)" ); 767 goto error; 768 } 769 aFloat = (float) aDouble; 770 ConvertFloatToBigEndian( &aFloat ); 771 break; 772 } 773 } 774 775 /* 776 * For each action we need an Action atom to hold the WhichAction, 777 * and ActionParameter atoms. 778 */ 779 780 err = QTInsertChild( *actionContainer, eventAtom, kAction, 0, 781 0, 0, NULL, &actionAtom ); 782 if (err != noErr) { 783 goto error; 784 } 785 err = QTInsertChild( *actionContainer, actionAtom, kWhichAction, 1, 786 1, sizeof(long), &action, NULL ); 787 if (err != noErr) { 788 goto error; 789 } 790 err = QTInsertChild( *actionContainer, actionAtom, kActionParameter, 1, 791 1, sizeof(float), &aFloat, NULL ); 792 if (err != noErr) { 793 goto error; 794 } 795 } 796 return TCL_OK; 797 798error: 799 CheckAndSetErrorResult( interp, err ); 800 return TCL_ERROR; 801} 802 803/* 804 *---------------------------------------------------------------------- 805 * 806 * CreateFrameLoadedActionContainer -- 807 * 808 * Return, through the actionContainer parameter, an atom container 809 * that contains a frame-loaded event action. 810 * 811 * Results: 812 * Standard OSErr result 813 * 814 * Side effects: 815 * 816 *---------------------------------------------------------------------- 817 */ 818 819static OSErr 820CreateFrameLoadedActionContainer( QTAtomContainer *actionContainer ) 821{ 822 QTAtom eventAtom = 0; 823 QTAtom actionAtom = 0; 824 long action; 825 float panAngle; 826 OSErr err = noErr; 827 828 err = QTNewAtomContainer(actionContainer); 829 if (err != noErr) { 830 goto bail; 831 } 832 err = QTInsertChild(*actionContainer, kParentAtomIsContainer, kQTEventFrameLoaded, 833 1, 1, 0, NULL, &eventAtom); 834 if (err != noErr) { 835 goto bail; 836 } 837 err = QTInsertChild(*actionContainer, eventAtom, kAction, 1, 1, 838 0, NULL, &actionAtom); 839 if (err != noErr) { 840 goto bail; 841 } 842 action = EndianS32_NtoB(kActionQTVRSetPanAngle); 843 err = QTInsertChild(*actionContainer, actionAtom, kWhichAction, 1, 844 1, sizeof(long), &action, NULL); 845 if (err != noErr) { 846 goto bail; 847 } 848 panAngle = 180.0; 849 ConvertFloatToBigEndian(&panAngle); 850 err = QTInsertChild(*actionContainer, actionAtom, kActionParameter, 1, 851 1, sizeof(float), &panAngle, NULL); 852 if (err != noErr) { 853 goto bail; 854 } 855 856bail: 857 return(err); 858} 859 860/* 861 *---------------------------------------------------------------------- 862 * 863 * CreateIdleActionContainer -- 864 * 865 * Return, through the actionContainer parameter, an atom container 866 * that contains an idle event action. 867 * 868 * Results: 869 * Standard OSErr result 870 * 871 * Side effects: 872 * 873 *---------------------------------------------------------------------- 874 */ 875 876static OSErr 877CreateIdleActionContainer (QTAtomContainer *actionContainer) 878{ 879 QTAtom eventAtom = 0; 880 QTAtom actionAtom = 0; 881 long action; 882 float panAngle; 883 UInt32 flags; 884 OSErr err = noErr; 885 886 err = QTNewAtomContainer(actionContainer); 887 if (err != noErr) 888 goto bail; 889 890 err = QTInsertChild(*actionContainer, kParentAtomIsContainer, kQTEventIdle, 891 1, 1, 0, NULL, &eventAtom); 892 if (err != noErr) 893 goto bail; 894 895 err = QTInsertChild(*actionContainer, eventAtom, kAction, 1, 896 1, 0, NULL, &actionAtom); 897 if (err != noErr) 898 goto bail; 899 900 action = EndianS32_NtoB(kActionQTVRSetPanAngle); 901 err = QTInsertChild(*actionContainer, actionAtom, kWhichAction, 1, 902 1, sizeof(long), &action, NULL); 903 if (err != noErr) 904 goto bail; 905 906 panAngle = 10.0; 907 ConvertFloatToBigEndian(&panAngle); 908 err = QTInsertChild(*actionContainer, actionAtom, kActionParameter, 909 1, 1, sizeof(float), &panAngle, NULL); 910 if (err != noErr) 911 goto bail; 912 913 flags = EndianU32_NtoB(kActionFlagActionIsDelta | kActionFlagParameterWrapsAround); 914 err = QTInsertChild(*actionContainer, actionAtom, kActionFlags, 1, 915 1, sizeof(UInt32), &flags, NULL); 916 if (err != noErr) 917 goto bail; 918 919bail: 920 return(err); 921} 922 923/* 924 *---------------------------------------------------------------------- 925 * 926 * SetWiredActionsToNode -- 927 * 928 * Set the specified actions to be a node action of the specified 929 * type. If actionContainer is NULL, remove any existing action of 930 * that type from theSample. The theSample parameter is assumed to 931 * be a node information atom container; any actions that are 932 * global to the node should be inserted at the root level of this 933 * atom container; in addition, the container type should be the 934 * same as the event type and should have an atom ID of 1. 935 * 936 * Results: 937 * Standard OSErr result 938 * 939 * Side effects: 940 * 941 *---------------------------------------------------------------------- 942 */ 943 944static OSErr 945SetWiredActionsToNode ( Handle theSample, QTAtomContainer actionContainer, 946 UInt32 theActionType ) 947{ 948 QTAtom eventAtom = 0; 949 QTAtom targetAtom = 0; 950 OSErr err = noErr; 951 952 /* Look for a frame-loaded action atom in the specified actions atom container. */ 953 if (actionContainer != NULL) { 954 eventAtom = QTFindChildByID(actionContainer, kParentAtomIsContainer, 955 theActionType, 1, NULL); 956 } 957 958 /* Look for a frame-loaded action atom in the node information atom container. */ 959 targetAtom = QTFindChildByID(theSample, kParentAtomIsContainer, 960 theActionType, 1, NULL); 961 if (targetAtom != 0) { 962 963 /* 964 * If there is already a frame-loaded event atom in the node information 965 * atom container, then either replace it with the one we were passed or 966 * remove it. 967 */ 968 969 if (actionContainer != NULL) { 970 err = QTReplaceAtom(theSample, targetAtom, actionContainer, eventAtom); 971 } else { 972 err = QTRemoveAtom(theSample, targetAtom); 973 } 974 } else { 975 976 /* 977 * There is no frame-loaded event atom in the node information atom container, 978 * so add in the one we were passed. 979 */ 980 981 if (actionContainer != NULL) { 982 err = QTInsertChildren(theSample, kParentAtomIsContainer, actionContainer); 983 } 984 } 985 986 return(err); 987} 988 989/* 990 *---------------------------------------------------------------------- 991 * 992 * SetWiredActionsToHotSpot -- 993 * 994 * Set the specified actions to be a hot-spot action. If 995 * actionContainer is NULL, remove any existing hot-spot actions 996 * for the specified hot spot from theSample. 997 * 998 * Results: 999 * Standard OSErr result 1000 * 1001 * Side effects: 1002 * 1003 *---------------------------------------------------------------------- 1004 */ 1005 1006static OSErr 1007SetWiredActionsToHotSpot ( Handle theSample, long theHotSpotID, 1008 QTAtomContainer actionContainer ) 1009{ 1010 QTAtom hotSpotParentAtom = 0; 1011 QTAtom hotSpotAtom = 0; 1012 short count, 1013 index; 1014 OSErr err = noErr; 1015 1016 hotSpotParentAtom = QTFindChildByIndex(theSample, kParentAtomIsContainer, 1017 kQTVRHotSpotParentAtomType, 1, NULL); 1018 if (hotSpotParentAtom == 0) { 1019 goto bail; 1020 } 1021 hotSpotAtom = QTFindChildByID(theSample, hotSpotParentAtom, 1022 kQTVRHotSpotAtomType, theHotSpotID, NULL); 1023 if (hotSpotAtom == 0) { 1024 goto bail; 1025 } 1026 1027 /* See how many events are already associated with the specified hot spot. */ 1028 count = QTCountChildrenOfType(theSample, hotSpotAtom, kQTEventType); 1029 1030 for (index = count; index > 0; index--) { 1031 QTAtom targetAtom = 0; 1032 1033 /* Remove all the existing events. */ 1034 targetAtom = QTFindChildByIndex(theSample, hotSpotAtom, kQTEventType, 1035 index, NULL); 1036 if (targetAtom != 0) { 1037 err = QTRemoveAtom(theSample, targetAtom); 1038 if (err != noErr) { 1039 goto bail; 1040 } 1041 } 1042 } 1043 1044 if (actionContainer) { 1045 err = QTInsertChildren(theSample, hotSpotAtom, actionContainer); 1046 if (err != noErr) { 1047 goto bail; 1048 } 1049 } 1050 1051bail: 1052 return(err); 1053} 1054 1055/* 1056 *---------------------------------------------------------------------- 1057 * 1058 * WriteTheMediaPropertyAtom -- 1059 * 1060 * Add a media property action to the specified media. 1061 * We assume that the data passed to us through the theProperty 1062 * parameter is big-endian. 1063 * 1064 * Results: 1065 * Standard OSErr result 1066 * 1067 * Side effects: 1068 * 1069 *---------------------------------------------------------------------- 1070 */ 1071 1072static OSErr 1073WriteTheMediaPropertyAtom ( Media theMedia, long propertyID, 1074 long thePropertySize, void *theProperty ) 1075{ 1076 QTAtomContainer propertyAtom = NULL; 1077 QTAtom atom = 0; 1078 OSErr err = noErr; 1079 1080 /* Get the current media property atom. */ 1081 err = GetMediaPropertyAtom(theMedia, &propertyAtom); 1082 if (err != noErr) 1083 goto bail; 1084 1085 /* If there isn't one yet, then create one. */ 1086 if (propertyAtom == NULL) { 1087 err = QTNewAtomContainer(&propertyAtom); 1088 if (err != noErr) 1089 goto bail; 1090 } 1091 1092 /* 1093 * See if there is an existing atom of the specified type; if not, 1094 * then create one. 1095 */ 1096 1097 atom = QTFindChildByID(propertyAtom, kParentAtomIsContainer, 1098 propertyID, 1, NULL); 1099 if (atom == 0) { 1100 err = QTInsertChild(propertyAtom, kParentAtomIsContainer, 1101 propertyID, 1, 0, 0, NULL, &atom); 1102 if ((err != noErr) || (atom == 0)) 1103 goto bail; 1104 } 1105 1106 /* Set the data of the specified atom to the data passed in. */ 1107 err = QTSetAtomData(propertyAtom, atom, thePropertySize, (Ptr)theProperty); 1108 if (err != noErr) 1109 goto bail; 1110 1111 /* Write the new atom data out to the media property atom. */ 1112 err = SetMediaPropertyAtom(theMedia, propertyAtom); 1113 1114bail: 1115 if (propertyAtom != NULL) { 1116 err = QTDisposeAtomContainer(propertyAtom); 1117 } 1118 return(err); 1119} 1120 1121/* 1122 *---------------------------------------------------------------------- 1123 * 1124 * PanoramaGetInfoNode -- 1125 * 1126 * Gets all info from this panorama node, and fills the result 1127 * object. 1128 * 1129 * Results: 1130 * Tcl result. 1131 * 1132 * Side effects: 1133 * Fills the result object with data. 1134 * 1135 *---------------------------------------------------------------------- 1136 */ 1137 1138int 1139PanoramaGetInfoNode( Tcl_Interp *interp, 1140 Movie movie, 1141 QTVRInstance qtvrInst, 1142 UInt32 nodeID, 1143 Tcl_Obj **resObj ) 1144{ 1145 OSErr err = noErr; 1146 QTAtomContainer nodeInfoContainer = NULL; 1147 QTVRNodeHeaderAtomPtr nodeHeaderPtr; 1148 QTVRStringAtomPtr stringAtomPtr; 1149 QTAtom headerAtom = 0; 1150 QTAtom hotspotParentAtom = 0; 1151 QTAtom hotspotAtom = 0; 1152 QTAtom hotspotInfoAtom = 0; 1153 QTAtom nameAtom = 0; 1154 QTAtomID atomHotSpotID; 1155 QTAtomID atomID; 1156 OSType nodeType; 1157 Track panoTrack = NULL; 1158 Tcl_Obj *listObjPtr; 1159 Tcl_Obj *hotspotObjPtr; 1160 Tcl_Obj *rectListObjPtr; 1161 Tcl_DString ds; 1162 char tmpstr[255]; 1163 short index; 1164 int len; 1165 UInt32 currentNodeID; 1166 1167 *resObj = Tcl_NewListObj( 0, (Tcl_Obj **) NULL ); 1168 listObjPtr = Tcl_NewListObj( 0, (Tcl_Obj **) NULL ); 1169 1170 currentNodeID = QTVRGetCurrentNodeID( qtvrInst ); 1171 Tcl_ListObjAppendElement( interp, *resObj, Tcl_NewStringObj("-nodeid", -1) ); 1172 Tcl_ListObjAppendElement( interp, *resObj, Tcl_NewIntObj(currentNodeID) ); 1173 nodeType = QTVRGetNodeType( qtvrInst, nodeID ); 1174 Tcl_ListObjAppendElement( interp, *resObj, Tcl_NewStringObj("-nodetype", -1) ); 1175 if (nodeType == kQTVRPanoramaType) { 1176 Tcl_ListObjAppendElement( interp, *resObj, Tcl_NewStringObj("panorama", -1) ); 1177 } else if (nodeType == kQTVRObjectType) { 1178 Tcl_ListObjAppendElement( interp, *resObj, Tcl_NewStringObj("object", -1) ); 1179 } else { 1180 Tcl_ListObjAppendElement( interp, *resObj, Tcl_NewStringObj("unknown", -1) ); 1181 } 1182 1183 /* 1184 * Start by getting the panorama sample structure. 1185 */ 1186 1187 panoTrack = GetMovieIndTrackType( movie, 1, FOUR_CHAR_CODE('pano'), 1188 movieTrackMediaType ); 1189 if (panoTrack != NULL) { 1190 Media media = NULL; 1191 Handle dataOut = NULL; 1192 Ptr dataPtr = NULL; 1193 long size; 1194 TimeValue sampleTime; 1195 TimeValue durationPerSample; 1196 SampleDescriptionHandle descHandle = NULL; 1197 long descIndex; 1198 long numOfSamples; 1199 QTAtomContainer containerHandle = NULL; 1200 QTAtom sampleAtom = 0; 1201 QTVRPanoSampleAtom *samplePtr; 1202 1203 media = GetTrackMedia( panoTrack ); 1204 if (media != NULL) { 1205 dataOut = NewHandle(4); 1206 err = GetMediaSample( media, 1207 dataOut, 1208 0, 1209 &size, 1210 0, // media time 1211 &sampleTime, 1212 &durationPerSample, 1213 descHandle, 1214 &descIndex, 1215 0, 1216 &numOfSamples, 1217 NULL ); 1218 if (err != noErr) { 1219 CheckAndSetErrorResult( interp, err ); 1220 return TCL_ERROR; 1221 } 1222 HLock( dataOut ); 1223 containerHandle = (QTAtomContainer) dataOut; 1224 QTLockContainer( containerHandle ); 1225 1226 sampleAtom = QTFindChildByID( containerHandle, kParentAtomIsContainer, 1227 kQTVRPanoSampleDataAtomType, 1, NULL ); 1228 if (sampleAtom != 0) { 1229 err = QTGetAtomDataPtr( containerHandle, sampleAtom, NULL, (Ptr *) &samplePtr ); 1230 if (err == noErr) { 1231 ConvertBigEndianFloatToNative( &(samplePtr->minPan) ); 1232 ConvertBigEndianFloatToNative( &(samplePtr->maxPan) ); 1233 ConvertBigEndianFloatToNative( &(samplePtr->minTilt) ); 1234 ConvertBigEndianFloatToNative( &(samplePtr->maxTilt) ); 1235 ConvertBigEndianFloatToNative( &(samplePtr->minFieldOfView) ); 1236 ConvertBigEndianFloatToNative( &(samplePtr->maxFieldOfView) ); 1237 ConvertBigEndianFloatToNative( &(samplePtr->defaultPan) ); 1238 ConvertBigEndianFloatToNative( &(samplePtr->defaultTilt) ); 1239 ConvertBigEndianFloatToNative( &(samplePtr->defaultFieldOfView) ); 1240 1241 Tcl_ListObjAppendElement( interp, *resObj, 1242 Tcl_NewStringObj("-majorversion", -1) ); 1243 Tcl_ListObjAppendElement( interp, *resObj, 1244 Tcl_NewIntObj(EndianU16_BtoN(samplePtr->majorVersion)) ); 1245 Tcl_ListObjAppendElement( interp, *resObj, 1246 Tcl_NewStringObj("-minorversion", -1) ); 1247 Tcl_ListObjAppendElement( interp, *resObj, 1248 Tcl_NewIntObj(EndianU16_BtoN(samplePtr->minorVersion)) ); 1249 Tcl_ListObjAppendElement( interp, *resObj, 1250 Tcl_NewStringObj("-imagereftrackindex", -1) ); 1251 Tcl_ListObjAppendElement( interp, *resObj, 1252 Tcl_NewIntObj(EndianU32_BtoN(samplePtr->imageRefTrackIndex)) ); 1253 Tcl_ListObjAppendElement( interp, *resObj, 1254 Tcl_NewStringObj("-hotspotreftrackindex", -1) ); 1255 Tcl_ListObjAppendElement( interp, *resObj, 1256 Tcl_NewIntObj(EndianU32_BtoN(samplePtr->hotSpotRefTrackIndex)) ); 1257 Tcl_ListObjAppendElement( interp, *resObj, 1258 Tcl_NewStringObj("-minpan", -1) ); 1259 Tcl_ListObjAppendElement( interp, *resObj, 1260 Tcl_NewDoubleObj(samplePtr->minPan) ); 1261 Tcl_ListObjAppendElement( interp, *resObj, 1262 Tcl_NewStringObj("-maxpan", -1) ); 1263 Tcl_ListObjAppendElement( interp, *resObj, 1264 Tcl_NewDoubleObj(samplePtr->maxPan) ); 1265 Tcl_ListObjAppendElement( interp, *resObj, 1266 Tcl_NewStringObj("-mintilt", -1) ); 1267 Tcl_ListObjAppendElement( interp, *resObj, 1268 Tcl_NewDoubleObj(samplePtr->minTilt) ); 1269 Tcl_ListObjAppendElement( interp, *resObj, 1270 Tcl_NewStringObj("-maxtilt", -1) ); 1271 Tcl_ListObjAppendElement( interp, *resObj, 1272 Tcl_NewDoubleObj(samplePtr->maxTilt) ); 1273 Tcl_ListObjAppendElement( interp, *resObj, 1274 Tcl_NewStringObj("-minfov", -1) ); 1275 Tcl_ListObjAppendElement( interp, *resObj, 1276 Tcl_NewDoubleObj(samplePtr->minFieldOfView) ); 1277 Tcl_ListObjAppendElement( interp, *resObj, 1278 Tcl_NewStringObj("-maxfov", -1) ); 1279 Tcl_ListObjAppendElement( interp, *resObj, 1280 Tcl_NewDoubleObj(samplePtr->maxFieldOfView) ); 1281 Tcl_ListObjAppendElement( interp, *resObj, 1282 Tcl_NewStringObj("-defaultpan", -1) ); 1283 Tcl_ListObjAppendElement( interp, *resObj, 1284 Tcl_NewDoubleObj(samplePtr->defaultPan) ); 1285 Tcl_ListObjAppendElement( interp, *resObj, 1286 Tcl_NewStringObj("-defaulttilt", -1) ); 1287 Tcl_ListObjAppendElement( interp, *resObj, 1288 Tcl_NewDoubleObj(samplePtr->defaultTilt) ); 1289 Tcl_ListObjAppendElement( interp, *resObj, 1290 Tcl_NewStringObj("-defaultfov", -1) ); 1291 Tcl_ListObjAppendElement( interp, *resObj, 1292 Tcl_NewDoubleObj(samplePtr->defaultFieldOfView) ); 1293 Tcl_ListObjAppendElement( interp, *resObj, 1294 Tcl_NewStringObj("-imagesizex", -1) ); 1295 Tcl_ListObjAppendElement( interp, *resObj, 1296 Tcl_NewIntObj(EndianU32_BtoN(samplePtr->imageSizeX)) ); 1297 Tcl_ListObjAppendElement( interp, *resObj, 1298 Tcl_NewStringObj("-imagesizey", -1) ); 1299 Tcl_ListObjAppendElement( interp, *resObj, 1300 Tcl_NewIntObj(EndianU32_BtoN(samplePtr->imageSizeY)) ); 1301 Tcl_ListObjAppendElement( interp, *resObj, 1302 Tcl_NewStringObj("-imagenumframesx", -1) ); 1303 Tcl_ListObjAppendElement( interp, *resObj, 1304 Tcl_NewIntObj(EndianU16_BtoN(samplePtr->imageNumFramesX)) ); 1305 Tcl_ListObjAppendElement( interp, *resObj, 1306 Tcl_NewStringObj("-imagenumframesy", -1) ); 1307 Tcl_ListObjAppendElement( interp, *resObj, 1308 Tcl_NewIntObj(EndianU16_BtoN(samplePtr->imageNumFramesY)) ); 1309 Tcl_ListObjAppendElement( interp, *resObj, 1310 Tcl_NewStringObj("-hotspotsizex", -1) ); 1311 Tcl_ListObjAppendElement( interp, *resObj, 1312 Tcl_NewIntObj(EndianU32_BtoN(samplePtr->hotSpotSizeX)) ); 1313 Tcl_ListObjAppendElement( interp, *resObj, 1314 Tcl_NewStringObj("-hotspotsizey", -1) ); 1315 Tcl_ListObjAppendElement( interp, *resObj, 1316 Tcl_NewIntObj(EndianU32_BtoN(samplePtr->hotSpotSizeY)) ); 1317 Tcl_ListObjAppendElement( interp, *resObj, 1318 Tcl_NewStringObj("-hotspotnumframesx", -1) ); 1319 Tcl_ListObjAppendElement( interp, *resObj, 1320 Tcl_NewIntObj(EndianU16_BtoN(samplePtr->hotSpotNumFramesX)) ); 1321 Tcl_ListObjAppendElement( interp, *resObj, 1322 Tcl_NewStringObj("-hotspotnumframesy", -1) ); 1323 Tcl_ListObjAppendElement( interp, *resObj, 1324 Tcl_NewIntObj(EndianU16_BtoN(samplePtr->hotSpotNumFramesY)) ); 1325 } 1326 } 1327 DisposeHandle( dataOut ); 1328 DisposeHandle( (Handle) descHandle ); 1329 } 1330 } 1331 1332 /* 1333 * Investigate what's in the node information atom container. 1334 */ 1335 1336 err = QTVRGetNodeInfo( qtvrInst, nodeID, &nodeInfoContainer ); 1337 if (err != noErr) { 1338 CheckAndSetErrorResult( interp, err ); 1339 return TCL_ERROR; 1340 } 1341 headerAtom = QTFindChildByID( nodeInfoContainer, kParentAtomIsContainer, 1342 kQTVRNodeHeaderAtomType, 1, NULL ); 1343 if (headerAtom == 0) { 1344 CheckAndSetErrorResult( interp, err ); 1345 return TCL_ERROR; 1346 } 1347 QTLockContainer( nodeInfoContainer ); 1348 err = QTGetAtomDataPtr( nodeInfoContainer, headerAtom, NULL, (Ptr *) &nodeHeaderPtr ); 1349 1350 /* 1351 * Find any name atom. 1352 */ 1353 1354 if ((err == noErr) && (EndianS32_BtoN(nodeHeaderPtr->nameAtomID) != 0)) { 1355 nameAtom = QTFindChildByID( nodeInfoContainer, kParentAtomIsContainer, 1356 kQTVRStringAtomType, EndianS32_BtoN(nodeHeaderPtr->nameAtomID), NULL ); 1357 if (nameAtom != 0) { 1358 err = QTGetAtomDataPtr( nodeInfoContainer, nameAtom, NULL, 1359 (Ptr *) &stringAtomPtr ); 1360 if (err == noErr) { 1361 len = EndianU16_BtoN(stringAtomPtr->stringLength); 1362 len = (len > 250) ? 250 : len; 1363 memset( tmpstr, 0, 255 ); 1364 memcpy( tmpstr, &stringAtomPtr->theString, len ); 1365 Tcl_ListObjAppendElement( interp, listObjPtr, 1366 Tcl_NewStringObj("-name", -1) ); 1367 Tcl_ExternalToUtfDString( gQTTclTranslationEncoding, tmpstr, -1, &ds ); 1368 Tcl_ListObjAppendElement( interp, listObjPtr, 1369 Tcl_NewStringObj(Tcl_DStringValue(&ds), -1) ); 1370 Tcl_DStringFree(&ds); 1371 } 1372 } 1373 } 1374 1375 /* 1376 * Get hotspot parent atom which is the parent of all hot spot atoms of the node. 1377 */ 1378 1379 hotspotParentAtom = QTFindChildByID( nodeInfoContainer, kParentAtomIsContainer, 1380 kQTVRHotSpotParentAtomType, 1, NULL ); 1381 if (hotspotParentAtom != 0) { 1382 short numHotspots = 0; 1383 QTVRHotSpotInfoAtomPtr hotspotAtomPtr; 1384 1385 numHotspots = QTCountChildrenOfType( nodeInfoContainer, hotspotParentAtom, 1386 kQTVRHotSpotAtomType ); 1387 Tcl_ListObjAppendElement( interp, *resObj, 1388 Tcl_NewStringObj("-hotspots", -1) ); 1389 1390 /* 1391 * Loop over all hotspots. 1392 */ 1393 1394 for (index = 1; index <= numHotspots; index++) { 1395 hotspotAtom = 0; 1396 hotspotAtom = QTFindChildByIndex( nodeInfoContainer, hotspotParentAtom, 1397 kQTVRHotSpotAtomType, index, &atomHotSpotID ); 1398 if (hotspotAtom != 0) { 1399 hotspotObjPtr = Tcl_NewListObj( 0, (Tcl_Obj **) NULL ); 1400 Tcl_ListObjAppendElement( interp, hotspotObjPtr, 1401 Tcl_NewStringObj("-hotspotid", -1) ); 1402 Tcl_ListObjAppendElement( interp, hotspotObjPtr, 1403 Tcl_NewIntObj(atomHotSpotID) ); 1404 1405 /* 1406 * Get the hotspot info atom here. 1407 */ 1408 1409 hotspotInfoAtom = 0; 1410 hotspotInfoAtom = QTFindChildByIndex( nodeInfoContainer, hotspotAtom, 1411 kQTVRHotSpotInfoAtomType, 1, &atomID ); 1412 if (hotspotInfoAtom != 0) { 1413 err = QTGetAtomDataPtr( nodeInfoContainer, hotspotInfoAtom, NULL, 1414 (Ptr *) &hotspotAtomPtr ); 1415 if (err == noErr) { 1416 if (EndianS32_BtoN(hotspotAtomPtr->nameAtomID) != 0) { 1417 1418 nameAtom = QTFindChildByID( nodeInfoContainer, hotspotAtom, 1419 kQTVRStringAtomType, 1420 EndianS32_BtoN(hotspotAtomPtr->nameAtomID), NULL ); 1421 if (nameAtom != 0) { 1422 err = QTGetAtomDataPtr( nodeInfoContainer, nameAtom, NULL, 1423 (Ptr *) &stringAtomPtr ); 1424 if (err == noErr) { 1425 len = EndianU16_BtoN(stringAtomPtr->stringLength); 1426 len = (len > 250) ? 250 : len; 1427 memset( tmpstr, 0, 255 ); 1428 memcpy( tmpstr, &stringAtomPtr->theString, len ); 1429 Tcl_ListObjAppendElement( interp, hotspotObjPtr, 1430 Tcl_NewStringObj("-name", -1) ); 1431 Tcl_ExternalToUtfDString( gQTTclTranslationEncoding, tmpstr, -1, &ds ); 1432 Tcl_ListObjAppendElement( interp, hotspotObjPtr, 1433 Tcl_NewStringObj(Tcl_DStringValue(&ds), -1) ); 1434 Tcl_DStringFree(&ds); 1435 } 1436 } 1437 } 1438 if (EndianS32_BtoN(hotspotAtomPtr->commentAtomID) != 0) { 1439 1440 nameAtom = QTFindChildByID( nodeInfoContainer, hotspotAtom, 1441 kQTVRStringAtomType, 1442 EndianS32_BtoN(hotspotAtomPtr->commentAtomID), NULL ); 1443 if (nameAtom != 0) { 1444 err = QTGetAtomDataPtr( nodeInfoContainer, nameAtom, NULL, 1445 (Ptr *) &stringAtomPtr ); 1446 if (err == noErr) { 1447 len = EndianU16_BtoN(stringAtomPtr->stringLength); 1448 len = (len > 250) ? 250 : len; 1449 memset( tmpstr, 0, 255 ); 1450 memcpy( tmpstr, &stringAtomPtr->theString, len ); 1451 Tcl_ListObjAppendElement( interp, hotspotObjPtr, 1452 Tcl_NewStringObj("-comment", -1) ); 1453 Tcl_ExternalToUtfDString( gQTTclTranslationEncoding, tmpstr, -1, &ds ); 1454 Tcl_ListObjAppendElement( interp, hotspotObjPtr, 1455 Tcl_NewStringObj(Tcl_DStringValue(&ds), -1) ); 1456 Tcl_DStringFree(&ds); 1457 } 1458 } 1459 } 1460 ConvertBigEndianFloatToNative( &(hotspotAtomPtr->bestPan) ); 1461 ConvertBigEndianFloatToNative( &(hotspotAtomPtr->bestTilt) ); 1462 ConvertBigEndianFloatToNative( &(hotspotAtomPtr->bestFOV) ); 1463 1464 Tcl_ListObjAppendElement( interp, hotspotObjPtr, 1465 Tcl_NewStringObj("-hotspottype", -1) ); 1466 if (EndianU32_BtoN(hotspotAtomPtr->hotSpotType) == kQTVRHotSpotLinkType) { 1467 Tcl_ListObjAppendElement( interp, hotspotObjPtr, 1468 Tcl_NewStringObj("link", -1) ); 1469 } else if (EndianU32_BtoN(hotspotAtomPtr->hotSpotType) == kQTVRHotSpotURLType) { 1470 Tcl_ListObjAppendElement( interp, hotspotObjPtr, 1471 Tcl_NewStringObj("url", -1) ); 1472 } else if (EndianU32_BtoN(hotspotAtomPtr->hotSpotType) == kQTVRHotSpotUndefinedType) { 1473 Tcl_ListObjAppendElement( interp, hotspotObjPtr, 1474 Tcl_NewStringObj("undefined", -1) ); 1475 } else { 1476 Tcl_ListObjAppendElement( interp, hotspotObjPtr, 1477 Tcl_NewStringObj("undefined", -1) ); 1478 } 1479 Tcl_ListObjAppendElement( interp, hotspotObjPtr, 1480 Tcl_NewStringObj("-bestpan", -1) ); 1481 Tcl_ListObjAppendElement( interp, hotspotObjPtr, 1482 Tcl_NewDoubleObj(hotspotAtomPtr->bestPan) ); 1483 Tcl_ListObjAppendElement( interp, hotspotObjPtr, 1484 Tcl_NewStringObj("-besttilt", -1) ); 1485 Tcl_ListObjAppendElement( interp, hotspotObjPtr, 1486 Tcl_NewDoubleObj(hotspotAtomPtr->bestTilt) ); 1487 Tcl_ListObjAppendElement( interp, hotspotObjPtr, 1488 Tcl_NewStringObj("-bestfov", -1) ); 1489 Tcl_ListObjAppendElement( interp, hotspotObjPtr, 1490 Tcl_NewDoubleObj(hotspotAtomPtr->bestFOV) ); 1491 Tcl_ListObjAppendElement( interp, hotspotObjPtr, 1492 Tcl_NewStringObj("-bounds", -1) ); 1493 rectListObjPtr = Tcl_NewListObj( 0, (Tcl_Obj **) NULL ); 1494 Tcl_ListObjAppendElement( interp, rectListObjPtr, 1495 Tcl_NewIntObj(EndianS16_BtoN(hotspotAtomPtr->hotSpotRect.left)) ); 1496 Tcl_ListObjAppendElement( interp, rectListObjPtr, 1497 Tcl_NewIntObj(EndianS16_BtoN(hotspotAtomPtr->hotSpotRect.top )) ); 1498 Tcl_ListObjAppendElement( interp, rectListObjPtr, 1499 Tcl_NewIntObj(EndianS16_BtoN(hotspotAtomPtr->hotSpotRect.right)) ); 1500 Tcl_ListObjAppendElement( interp, rectListObjPtr, 1501 Tcl_NewIntObj(EndianS16_BtoN(hotspotAtomPtr->hotSpotRect.bottom)) ); 1502 1503 Tcl_ListObjAppendElement( interp, hotspotObjPtr, rectListObjPtr ); 1504 } 1505 Tcl_ListObjAppendElement( interp, listObjPtr, hotspotObjPtr ); 1506 } 1507 } 1508 } 1509 } 1510 if (nodeInfoContainer) { 1511 QTUnlockContainer( nodeInfoContainer ); 1512 QTDisposeAtomContainer( nodeInfoContainer ); 1513 } 1514 Tcl_ListObjAppendElement( interp, *resObj, listObjPtr ); 1515 1516 return TCL_OK; 1517} 1518 1519/* 1520 *---------------------------------------------------------------------- 1521 * 1522 * GetNodeCount, SetPanoramaByDegrees, ZoomInOrOutPanorama -- 1523 * 1524 * A collection of utilities for pano movies. 1525 * 1526 * Results: 1527 * Tcl result, Boolean, None. 1528 * 1529 * Side effects: 1530 * None. 1531 * 1532 *---------------------------------------------------------------------- 1533 */ 1534 1535int 1536GetNodeCount( QTVRInstance qtvrInst ) 1537{ 1538 QTAtomContainer vrWorld = NULL; 1539 QTAtom nodeParentAtom; 1540 int numNodes = 0; 1541 OSErr err = noErr; 1542 1543 err = QTVRGetVRWorld( qtvrInst, &vrWorld ); 1544 if (err != noErr) { 1545 return numNodes; 1546 } 1547 nodeParentAtom = QTFindChildByIndex( vrWorld, kParentAtomIsContainer, 1548 kQTVRNodeParentAtomType, 1, NULL ); 1549 if (nodeParentAtom != 0) { 1550 numNodes = QTCountChildrenOfType( vrWorld, nodeParentAtom, kQTVRNodeIDAtomType ); 1551 } 1552 QTDisposeAtomContainer( vrWorld ); 1553 return numNodes; 1554} 1555 1556Boolean 1557SetPanoramaByDegrees( QTVRInstance qtvrInst, long direction, float angle ) 1558{ 1559 Boolean moved; 1560 1561 switch (direction) { 1562 case kDirUp: 1563 QTVRSetTiltAngle( qtvrInst, angle ); 1564 break; 1565 case kDirDown: 1566 QTVRSetTiltAngle( qtvrInst, -angle ); 1567 break; 1568 case kDirLeft: 1569 QTVRSetPanAngle( qtvrInst, angle ); 1570 break; 1571 case kDirRight: 1572 QTVRSetPanAngle( qtvrInst, -angle ); 1573 break; 1574 default: 1575 break; 1576 } 1577 1578 /* Update the image on the screen. */ 1579 QTVRUpdate( qtvrInst, kQTVRStatic ); 1580 1581 switch (direction) { 1582 case kDirUp: 1583 case kDirDown: 1584 moved = (angle != QTVRGetTiltAngle( qtvrInst )); 1585 break; 1586 case kDirLeft: 1587 case kDirRight: 1588 moved = (angle != QTVRGetPanAngle( qtvrInst )); 1589 break; 1590 default: 1591 break; 1592 } 1593 return moved; 1594} 1595 1596void 1597ZoomInOrOutPanorama( QTVRInstance qtvrInst, long direction, float fov ) 1598{ 1599 float oldFov; 1600 1601 // we just set it directly, later could use a factor instead. 1602 oldFov = QTVRGetFieldOfView( qtvrInst ); 1603 switch (direction) { 1604 case kDirIn: 1605 1606 break; 1607 case kDirOut: 1608 1609 break; 1610 default: 1611 break; 1612 } 1613 QTVRSetFieldOfView( qtvrInst, fov ); 1614 QTVRUpdate( qtvrInst, kQTVRStatic ); 1615} 1616 1617/*---------------------------------------------------------------------------*/