Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2398 lines
72 KiB

  1. /******************************Module*Header*******************************\
  2. * Module Name: sstext3d.c
  3. *
  4. * Core code for text3D screen saver
  5. *
  6. * Created: 12-24-94 -by- Marc Fortier [marcfo]
  7. *
  8. * Copyright (c) 1994 Microsoft Corporation
  9. *
  10. \**************************************************************************/
  11. #include <stdio.h>
  12. #include <string.h>
  13. #include <stdlib.h>
  14. #include <math.h>
  15. #include <fcntl.h>
  16. #include <io.h>
  17. #include <sys/types.h>
  18. #include <sys/timeb.h>
  19. #include <time.h>
  20. #include <windows.h>
  21. #include <scrnsave.h>
  22. #include <GL/gl.h>
  23. #include <GL/glu.h>
  24. #include <GL/glaux.h>
  25. //#define SS_DEBUG 1
  26. #include "sscommon.h"
  27. #include "sstext3d.h"
  28. #define FMAX_CHORDAL_DEVIATION 0.008f
  29. #define FMIN_DEPTH 0.15f
  30. #define FMAX_DEPTH 0.6f
  31. #define FMIN_VIEW_ANGLE 90.0f
  32. #define FMAX_VIEW_ANGLE 130.0f
  33. #define FMIN_RANDOM_ANGLE 45.0f
  34. #define FMAX_RANDOM_ANGLE 89.0f
  35. #define FMIN_SEESAW_ANGLE 63.0f
  36. #define FMAX_SEESAW_ANGLE 88.0f
  37. #define FMIN_WOBBLE_ANGLE 30.0f
  38. #define FMAX_WOBBLE_ANGLE 55.0f
  39. #define FMIN_WOBBLE_ANGLE2 40.0f
  40. #define FMAX_WOBBLE_ANGLE2 80.0f
  41. #define MIN_ROT_STEP 1
  42. #define MAX_ROT_STEP 20
  43. #define FMAX_ZOOM 5.0f
  44. // globals
  45. static FLOAT gfMinCycleTime = 10.0f;
  46. static POINTFLOAT gTrig[360]; // pre-calculated table of sines and cosines
  47. static POINTFLOAT gSawTooth[360]; // sawtooth table
  48. static POINTFLOAT gInvTrig[360]; // pseudo-inverse trig table
  49. static POINT gTrigDif[360]; // table for converting trig->invtrig
  50. static POINT gInvTrigDif[360]; // table for converting invtrig->trig
  51. AttrContext gac;
  52. // Default texture resource
  53. TEX_RES gTexRes = { TEX_BMP, IDB_DEFTEX };
  54. typedef struct _LIST *PLIST;
  55. typedef struct _LIST {
  56. PLIST pnext;
  57. PLIST plistComplete;
  58. LPTSTR pszStr;
  59. } LIST;
  60. PLIST gplistComplete = NULL;
  61. PLIST gplist = NULL;
  62. static void DeleteNameList();
  63. void text3d_Init( void *data );
  64. void text3d_Reset(void *data );
  65. void text3d_Draw(void *data );
  66. void text3d_Reshape(int width, int height, void *data );
  67. void text3d_Finish( void *data );
  68. static void CalcViewParams( AttrContext *pac );
  69. static BOOL InitFont( AttrContext *pac );
  70. static void InitLighting( AttrContext *pac );
  71. static void InitTexture( AttrContext *pac );
  72. static void InitMaterials( AttrContext *pac );
  73. static void InitView( AttrContext *pac );
  74. static FLOAT MapValue( FLOAT fInVal,
  75. FLOAT fIn1, FLOAT fIn2,
  76. FLOAT fOut1, FLOAT fOut2 );
  77. static int MapValueI( int inVal, int in1, int in2, int out1, int out2 );
  78. static FLOAT CalcChordalDeviation( HDC hdc, AttrContext *pac );
  79. static void (*BoundingBoxProc)( AttrContext *pac);
  80. static void CalcBoundingBox( AttrContext *pac );
  81. static void CalcBoundingBoxFromSphere( AttrContext *pac );
  82. static void CalcBoundingBoxFromSpherePlus( AttrContext *pac, FLOAT zmax );
  83. static void CalcBoundingBoxGeneric( AttrContext *pac );
  84. static void CalcBoundingBoxFromExtents( AttrContext *pac, POINT3D *box );
  85. static void CalcBoundingExtent( FLOAT rot, FLOAT x, FLOAT y,
  86. POINTFLOAT *extent );
  87. static void SortBoxes( POINT3D *box, FLOAT *boxvp, int numBox );
  88. static void (*GetNextRotProc)( AttrContext *pac );
  89. static void GetNextRotNone( AttrContext *pac );
  90. static void GetNextRotRandom( AttrContext *pac );
  91. static void GetNextRotWobble( AttrContext *pac );
  92. static void InitTrigTable();
  93. static void text3d_UpdateTime( AttrContext *pac, BOOL bCheckBounds );
  94. static void text3d_UpdateString( AttrContext *pac, BOOL bCheckBounds );
  95. static BOOL VerifyString( AttrContext *pac );
  96. static BOOL CheckKeyStrings( LPTSTR testString, PSZ psz );
  97. static void ConvertStringAsciiToUnicode( PSZ psz, PWSTR pwstr, int len );
  98. static void InvertBitsA( char *s, int len );
  99. static void ReadNameList();
  100. static PSZ ReadStringFileA( char *file );
  101. static void CreateRandomList();
  102. static void ResetRotationLimits( AttrContext *pac, int *reset );
  103. static int FrameCalibration( AttrContext *pac, struct _timeb *pBaseTime, int framesPerCycle,
  104. int nCycle );
  105. static void SetTransitionPoints( AttrContext *pac, int framesPerCycle,
  106. int *trans1, int *trans2, FLOAT *zTrans );
  107. static void
  108. AdjustRotationStep( AttrContext *pac, int *reset, POINTFLOAT *oldTrig );
  109. /******************************Public*Routine******************************\
  110. * SetFloaterInfo
  111. *
  112. * Set the size and motion of the floating window
  113. *
  114. * ss_SetWindowAspectRatio may be called after this, to finely crop the
  115. * window to the text being displayed. But we can't call it here, since this
  116. * function is called by common when creating the floating window, before the
  117. * text string size has been determined.
  118. \**************************************************************************/
  119. static void
  120. SetFloaterInfo( ISIZE *pParentSize, CHILD_INFO *pChild )
  121. {
  122. float sizeFact;
  123. float sizeScale;
  124. int size;
  125. ISIZE *pChildSize = &pChild->size;
  126. MOTION_INFO *pMotion = &pChild->motionInfo;
  127. AttrContext *pac = &gac;
  128. sizeScale = (float)pac->uSize / 100.0f; // range 0..1
  129. sizeFact = 0.25f + (0.5f * sizeScale); // range 25-75%
  130. size = (int) (sizeFact *
  131. ( ((float)(pParentSize->width + pParentSize->height)) / 2.0f ));
  132. SS_CLAMP_TO_RANGE2( size, 0, pParentSize->width );
  133. SS_CLAMP_TO_RANGE2( size, 0, pParentSize->height );
  134. pChildSize->width = pChildSize->height = size;
  135. pMotion->posInc.x = .01f * (float) size;
  136. if( pMotion->posInc.x < 1.0f )
  137. pMotion->posInc.x = 1.0f;
  138. pMotion->posInc.y = pMotion->posInc.x;
  139. pMotion->posIncVary.x = .4f * pMotion->posInc.x;
  140. pMotion->posIncVary.y = pMotion->posIncVary.x;
  141. }
  142. /******************************Public*Routine******************************\
  143. * Init
  144. *
  145. * Initialize - called on first entry into ss.
  146. * Called BEFORE gl is initialized!
  147. * Just do basic stuff here, like set up callbacks, verify dialog stuff, etc.
  148. *
  149. * Fills global SSContext structure with required data, and returns ptr
  150. * to it.
  151. *
  152. \**************************************************************************/
  153. SSContext *
  154. ss_Init( void )
  155. {
  156. // validate some initial dialog settings
  157. getIniSettings(); // also called on dialog init
  158. // must verify textures here, before GL floater windows are created
  159. if( gac.surfStyle == SURFSTYLE_TEX ) {
  160. ss_DisableTextureErrorMsgs();
  161. ss_VerifyTextureFile( &gac.texFile );
  162. }
  163. // set Init callback
  164. ss_InitFunc( text3d_Init );
  165. // set data ptr to be sent with callbacks
  166. ss_DataPtr( &gac );
  167. // set configuration info to return
  168. gac.ssc.bFloater = TRUE;
  169. gac.ssc.floaterInfo.bMotion = TRUE;
  170. gac.ssc.floaterInfo.ChildSizeFunc = SetFloaterInfo;
  171. gac.ssc.bDoubleBuf = TRUE;
  172. gac.ssc.depthType = SS_DEPTH16;
  173. return &gac.ssc;
  174. }
  175. /******************************Public*Routine******************************\
  176. * text3d_Init
  177. *
  178. * Initializes OpenGL state for text3d screen saver
  179. *
  180. \**************************************************************************/
  181. void
  182. text3d_Init( void *data )
  183. {
  184. AttrContext *pac = (AttrContext *) data;
  185. // Set any callbacks that require GL
  186. ss_UpdateFunc( text3d_Draw );
  187. ss_ReshapeFunc( text3d_Reshape );
  188. ss_FinishFunc( text3d_Finish );
  189. #ifdef SS_DEBUG
  190. glClearColor( 0.2f, 0.2f, 0.2f, 0.0f );
  191. #else
  192. glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  193. #endif
  194. glDepthFunc(GL_LEQUAL);
  195. glEnable(GL_DEPTH_TEST);
  196. // this sequence must be maintained
  197. InitLighting( pac );
  198. InitFont( pac );
  199. InitView( pac );
  200. InitTexture( pac );
  201. InitMaterials( pac );
  202. }
  203. /**************************************************************************\
  204. * InitLighting
  205. *
  206. * Initialize lighting, and back face culling.
  207. *
  208. \**************************************************************************/
  209. static void
  210. InitLighting( AttrContext *pac )
  211. {
  212. float ambient1[] = {0.2f, 0.2f, 0.2f, 1.0f};
  213. float ambient2[] = {0.1f, 0.1f, 0.1f, 1.0f};
  214. float diffuse1[] = {0.7f, 0.7f, 0.7f, 1.0f};
  215. float diffuse2[] = {0.7f, 0.7f, 0.7f, 1.0f};
  216. float position1[] = {0.0f, 50.0f, 150.0f, 0.0f};
  217. float position2[] = {25.0f, 150.0f, 50.0f, 0.0f};
  218. float lmodel_ambient[] = {1.0f, 1.0f, 1.0f, 1.0f};
  219. glLightfv(GL_LIGHT0, GL_AMBIENT, ambient1);
  220. glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse1);
  221. glLightfv(GL_LIGHT0, GL_POSITION, position1);
  222. glEnable(GL_LIGHT0);
  223. glLightfv(GL_LIGHT1, GL_AMBIENT, ambient2);
  224. glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse2);
  225. glLightfv(GL_LIGHT1, GL_POSITION, position2);
  226. glEnable(GL_LIGHT1);
  227. glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
  228. glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
  229. glCullFace( GL_BACK );
  230. glEnable(GL_CULL_FACE);
  231. glEnable(GL_LIGHTING);
  232. }
  233. /**************************************************************************\
  234. * TestFont
  235. *
  236. * Test that GetOutlineTextMetrics works. If not, wglUseFontOutlines will fail.
  237. *
  238. * If the font tests bad, delete it and select in the previous one.
  239. *
  240. \**************************************************************************/
  241. static BOOL
  242. TestFont( HFONT hfont )
  243. {
  244. OUTLINETEXTMETRIC otm;
  245. HFONT hfontOld;
  246. HDC hdc = wglGetCurrentDC();
  247. hfontOld = SelectObject(hdc, hfont);
  248. if( GetOutlineTextMetrics( hdc, sizeof(otm), &otm) <= 0 ) {
  249. SS_DBGPRINT( "sstext3d Init: GetOutlineTextMetrics failure\n" );
  250. SelectObject(hdc, hfontOld);
  251. DeleteObject( hfont );
  252. return FALSE;
  253. }
  254. return TRUE;
  255. }
  256. /**************************************************************************\
  257. * CreateFont
  258. *
  259. * Create a true type font and test it
  260. *
  261. \**************************************************************************/
  262. static HFONT
  263. text3d_CreateFont( LOGFONT *plf )
  264. {
  265. HFONT hfont;
  266. // Create font from LOGFONT data
  267. hfont = CreateFontIndirect(plf);
  268. if( hfont ) {
  269. // Test the font
  270. if( ! TestFont( hfont ) )
  271. hfont = (HFONT) 0;
  272. }
  273. return hfont;
  274. }
  275. /**************************************************************************\
  276. * InitFont
  277. *
  278. *
  279. \**************************************************************************/
  280. static BOOL
  281. InitFont( AttrContext *pac )
  282. {
  283. LOGFONT lf;
  284. HFONT hfont;
  285. int type;
  286. float fChordalDeviation;
  287. HDC hdc = wglGetCurrentDC();
  288. // Set up the LOGFONT structure
  289. memset(&lf, 0, sizeof(LOGFONT));
  290. lstrcpy( lf.lfFaceName, pac->szFontName );
  291. lf.lfWeight = (pac->bBold) ? FW_BOLD : FW_NORMAL;
  292. lf.lfItalic = (pac->bItalic) ? (BYTE) 1 : 0;
  293. lf.lfHeight = 0; // shouldn't matter
  294. lf.lfCharSet = pac->charSet;
  295. lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;
  296. // Create the font
  297. if( ! (hfont = text3d_CreateFont( &lf )) ) {
  298. // Couldn't create a true type font with supplied data
  299. SS_DBGPRINT( "initial text3d_CreateFont failed: \n" );
  300. }
  301. if( !hfont && ss_fOnWin95() ) {
  302. // !!! Bug on win95: the font mapper didn't give us anything useful
  303. // For some reason GetOutlineTextMetrics fails for some fonts (Symbol)
  304. // when using lfHeight = 0 (default height value).
  305. lf.lfHeight = -10;
  306. if( ! (hfont = text3d_CreateFont( &lf )) ) {
  307. SS_DBGPRINT( "text3d_CreateFont with lfHeight != 0 failed: \n" );
  308. }
  309. }
  310. if( hfont == NULL ) {
  311. /* The requested font cannot be loaded. Try to get the system to
  312. * load any TrueType font
  313. */
  314. hfont = CreateFont( 100, 100, 0, 0, lf.lfWeight, lf.lfItalic,
  315. 0, 0, 0, OUT_TT_ONLY_PRECIS, CLIP_DEFAULT_PRECIS,
  316. DEFAULT_QUALITY, DEFAULT_PITCH, NULL );
  317. // If hfont is still null, nothing will be displayed.
  318. if( !hfont || !TestFont(hfont) ) {
  319. SS_DBGPRINT( "text3d_InitFont failure\n" );
  320. return FALSE;
  321. }
  322. }
  323. // We have a valid font
  324. SelectObject(hdc, hfont);
  325. // Set extrusion, chordal deviation, and font type
  326. #ifdef _PPC_
  327. // !!! Work around for PPC compiler bug
  328. // calculate chordalDeviation from input attribute fTesselFact
  329. fChordalDeviation = CalcChordalDeviation( hdc, pac );
  330. pac->fDepth = ss_fRand( FMIN_DEPTH, FMAX_DEPTH );
  331. #else
  332. pac->fDepth = ss_fRand( FMIN_DEPTH, FMAX_DEPTH );
  333. // calculate chordalDeviation from input attribute fTesselFact
  334. fChordalDeviation = CalcChordalDeviation( hdc, pac );
  335. #endif
  336. type = pac->surfStyle == SURFSTYLE_WIREFRAME ? WGL_FONT_LINES :
  337. WGL_FONT_POLYGONS;
  338. // Create a wgl font context
  339. if( !(pac->pWglFontC =
  340. CreateWglFontContext( hdc, type, pac->fDepth, fChordalDeviation )) )
  341. return FALSE;
  342. // intialize the text that will be displayed
  343. if( pac->demoType == DEMO_CLOCK ) {
  344. text3d_UpdateTime( pac, FALSE ); // sets pac->textXXX params as well
  345. } else if( pac->demoType == DEMO_STRING ) {
  346. if( !VerifyString( pac ) ) {
  347. ConvertStringToList( pac->szText, pac->usText, pac->pWglFontC );
  348. pac->textLen = GetStringExtent( pac->szText, &pac->pfTextExtent,
  349. &pac->pfTextOrigin,
  350. pac->pWglFontC );
  351. }
  352. }
  353. return SUCCESS;
  354. }
  355. /**************************************************************************\
  356. * InitView
  357. *
  358. *
  359. \**************************************************************************/
  360. static void
  361. InitView( AttrContext *pac )
  362. {
  363. int numRots=0, axis;
  364. FLOAT *p3dRotMax = (FLOAT *) &pac->p3dRotMax;
  365. FLOAT *p3dRotMin = (FLOAT *) &pac->p3dRotMin;
  366. int *ip3dRotStep = (int *) &pac->ip3dRotStep;
  367. POINT3D p3d_zero = {0.0f, 0.0f, 0.0f};
  368. int stepRange = 2; // default step range
  369. int reset[NUM_AXIS] = {1, 1, 1};
  370. // text is either xmajor or ymajor
  371. pac->bXMajor = pac->pfTextExtent.x >= pac->pfTextExtent.y ? TRUE : FALSE;
  372. /* At this point, the initial string extents will have been
  373. * calculated, and we can use this to determine rotational
  374. * characteristics
  375. */
  376. // default proc to get next rotation
  377. GetNextRotProc = GetNextRotRandom;
  378. /* convert the slider speed values to rotation steps, with
  379. * a steeper slope at the beginning of the scale
  380. */
  381. pac->iRotStep = MapValueI( pac->iSpeed,
  382. MIN_SLIDER, MAX_SLIDER, // slider range
  383. MIN_ROT_STEP, MAX_ROT_STEP ); // step range
  384. // initialize rotation min/max to 0
  385. *( (POINT3D*)p3dRotMin ) = p3d_zero;
  386. *( (POINT3D*)p3dRotMax ) = p3d_zero;
  387. pac->p3dRot = p3d_zero;
  388. /* Set the MAXIMUM rotation limits. This is required initially, in
  389. * order to set the bounding box
  390. */
  391. switch( pac->rotStyle ) {
  392. case ROTSTYLE_NONE:
  393. GetNextRotProc = GetNextRotNone;
  394. break;
  395. case ROTSTYLE_SEESAW:
  396. // rotate minor axis
  397. if( pac->demoType == DEMO_VSTRING )
  398. // always rotate around y-axis
  399. axis = Y_AXIS;
  400. else
  401. axis = pac->bXMajor ? Y_AXIS : X_AXIS;
  402. p3dRotMin[axis] = FMIN_SEESAW_ANGLE;
  403. p3dRotMax[axis] = FMAX_SEESAW_ANGLE;
  404. break;
  405. case ROTSTYLE_WOBBLE:
  406. GetNextRotProc = GetNextRotWobble;
  407. if( pac->demoType == DEMO_VSTRING ) {
  408. axis = Y_AXIS;
  409. }
  410. else {
  411. stepRange = 1;
  412. axis = pac->bXMajor ? Y_AXIS : X_AXIS;
  413. }
  414. p3dRotMin[Z_AXIS] = FMAX_WOBBLE_ANGLE;
  415. p3dRotMax[Z_AXIS] = FMAX_WOBBLE_ANGLE;
  416. p3dRotMin[axis] = FMIN_WOBBLE_ANGLE2;
  417. p3dRotMax[axis] = FMAX_WOBBLE_ANGLE2;
  418. break;
  419. case ROTSTYLE_RANDOM:
  420. // adjust stepRange based on speed
  421. stepRange = MapValueI( pac->iSpeed,
  422. MIN_SLIDER, (MAX_SLIDER-MIN_SLIDER)/2,
  423. 2, 6 ); // step range
  424. for( axis = X_AXIS; axis < NUM_AXIS; axis++ ) {
  425. p3dRotMin[axis] = FMIN_RANDOM_ANGLE;
  426. p3dRotMax[axis] = FMAX_RANDOM_ANGLE;
  427. }
  428. break;
  429. }
  430. // set min and max steps
  431. pac->iRotMinStep = pac->iRotStep >= (MIN_ROT_STEP + stepRange) ?
  432. pac->iRotStep - stepRange : MIN_ROT_STEP;
  433. pac->iRotMaxStep = pac->iRotStep + stepRange; // don't limit upper end
  434. for( axis = X_AXIS; axis < NUM_AXIS; axis++ ) {
  435. ip3dRotStep[axis] = p3dRotMax[axis] != 0.0f ?
  436. ss_iRand2( pac->iRotMinStep, pac->iRotMaxStep ) : 0;
  437. }
  438. // initialize the step iteration
  439. pac->ip3dRoti.x = pac->ip3dRoti.y = pac->ip3dRoti.z = 0;
  440. // initialize the trig table, for fast rotation calculations
  441. InitTrigTable();
  442. // set the current rotation limits
  443. pac->p3dRotLimit = *( (POINT3D *)p3dRotMax );
  444. ResetRotationLimits( pac, reset );
  445. // set view angle
  446. pac->fFovy = ss_fRand( FMIN_VIEW_ANGLE, FMAX_VIEW_ANGLE );
  447. for( axis = X_AXIS; axis < NUM_AXIS; axis++ ) {
  448. if( p3dRotMax[axis] != 0.0f )
  449. numRots++;
  450. }
  451. // set BoundingBoxProc dependent on which axis are being rotated
  452. if( numRots <= 1 )
  453. BoundingBoxProc = CalcBoundingBox;
  454. else
  455. BoundingBoxProc = CalcBoundingBoxGeneric;
  456. (*BoundingBoxProc)( pac );
  457. if( pac->p3dBoundingBox.y == 0.0f )
  458. pac->p3dBoundingBox.y = 1.0f;
  459. }
  460. /**************************************************************************\
  461. * InitMaterials
  462. *
  463. *
  464. \**************************************************************************/
  465. static void
  466. InitMaterials( AttrContext *pac )
  467. {
  468. if( pac->bTexture ) {
  469. ss_InitTexMaterials();
  470. pac->bMaterialCycle = FALSE;
  471. pac->pMat = ss_RandomTexMaterial( TRUE );
  472. } else {
  473. ss_InitTeaMaterials();
  474. pac->bMaterialCycle = TRUE;
  475. pac->pMat = ss_RandomTeaMaterial( TRUE );
  476. }
  477. }
  478. /**************************************************************************\
  479. * InitTexture
  480. *
  481. * History
  482. * Apr. 28, 95 : [marcfo]
  483. * - Changed texture quality from default to high.
  484. *
  485. \**************************************************************************/
  486. static void
  487. InitTexture( AttrContext *pac )
  488. {
  489. if( pac->surfStyle != SURFSTYLE_TEX )
  490. return;
  491. // No choice for texture quality in dialog - set to HIGH
  492. pac->texQual = TEXQUAL_HIGH;
  493. // Try to load the texture file or default texture resource
  494. if( ss_LoadTextureFile( &pac->texFile, &pac->texture ) ||
  495. ss_LoadTextureResource( &gTexRes, &pac->texture ) )
  496. {
  497. pac->bTexture = 1;
  498. glEnable(GL_TEXTURE_2D);
  499. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  500. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  501. glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  502. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  503. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  504. ss_SetTexture( &pac->texture );
  505. // set auto texture coord generation
  506. ss_InitAutoTexture( NULL );
  507. }
  508. else { // couldn't open .bmp file
  509. pac->bTexture = 0;
  510. }
  511. }
  512. /******************************Public*Routine******************************\
  513. * text3d_Finish
  514. *
  515. * Handles any cleanup on program termination
  516. *
  517. \**************************************************************************/
  518. void
  519. text3d_Finish( void *data )
  520. {
  521. AttrContext *pac = (AttrContext *) data;
  522. if( pac )
  523. DeleteWglFontContext( pac->pWglFontC );
  524. // delete any name list
  525. DeleteNameList();
  526. }
  527. /**************************************************************************\
  528. * text3d_Reshape
  529. *
  530. * - called on resize, expose
  531. * - always called on app startup
  532. *
  533. \**************************************************************************/
  534. void
  535. text3d_Reshape(int width, int height, void *data )
  536. {
  537. AttrContext *pac = (AttrContext *) data;
  538. //mf
  539. #if 0
  540. glViewport( 0, 0, width, height );
  541. #endif
  542. // calculate new aspect ratio
  543. pac->fAspect = height == 0 ? 1.0f : (FLOAT) width / (FLOAT) height;
  544. CalcViewParams( pac );
  545. ss_SetWindowAspectRatio( pac->p3dBoundingBox.x / pac->p3dBoundingBox.y );
  546. }
  547. /**************************************************************************\
  548. * CalcViewParams
  549. *
  550. * Calculate viewing parameters, based on window size, bounding box, etc.
  551. *
  552. \**************************************************************************/
  553. static void
  554. CalcViewParams( AttrContext *pac )
  555. {
  556. GLdouble zNear, zFar;
  557. FLOAT aspectBound, viewDist;
  558. FLOAT vHeight;
  559. FLOAT fovy;
  560. // calculate viewing distance so that front of bounding box within view
  561. aspectBound = pac->p3dBoundingBox.x / pac->p3dBoundingBox.y;
  562. // this is distance to FRONT of bounding box:
  563. viewDist = pac->p3dBoundingBox.y /
  564. ( (FLOAT) tan( deg_to_rad(pac->fFovy/2.0f) ) );
  565. // NOTE: these are half-widths and heights
  566. if( aspectBound <= pac->fAspect ) {
  567. // we are bound by the window's height
  568. fovy = pac->fFovy;
  569. } else {
  570. // we are bound by window's width
  571. // adjust fovy, so fovx remains the same
  572. vHeight = pac->p3dBoundingBox.x / pac->fAspect;
  573. fovy = rad_to_deg( 2.0f * (FLOAT) atan( vHeight / viewDist ) );
  574. }
  575. /* Could just use rotation sphere dimensions here, but for now
  576. * set clipping planes 10% beyond bounding box.
  577. */
  578. zNear = 0.9f * viewDist;
  579. zFar = 1.1f * (viewDist + 2.0f*pac->p3dBoundingBox.z);
  580. if( pac->demoType == DEMO_VSTRING )
  581. zFar *= FMAX_ZOOM;
  582. glMatrixMode(GL_PROJECTION);
  583. glLoadIdentity();
  584. gluPerspective( fovy, pac->fAspect, zNear, zFar );
  585. // set viewing distance so that front of bounding box within view
  586. viewDist *= 1.01f; // pull back 1% further to be sure not off by a pixel..
  587. pac->fZtrans = -(viewDist + pac->p3dBoundingBox.z);
  588. glMatrixMode(GL_MODELVIEW);
  589. glLoadIdentity();
  590. glTranslatef( 0.0f, 0.0f, pac->fZtrans );
  591. }
  592. // number of calibration cycles
  593. #define MAX_CALIBRATE 2
  594. /**************************************************************************\
  595. * text3d_Draw
  596. *
  597. * Draw a frame.
  598. *
  599. \**************************************************************************/
  600. void
  601. text3d_Draw( void *data )
  602. {
  603. AttrContext *pac = (AttrContext *) data;
  604. POINT3D *rot = &pac->p3dRot;
  605. static BOOL bCalibrated = FALSE;
  606. static int nCycle = 0; // cycle count
  607. static int frameCount = 0, maxCount;
  608. static int matTransCount, matTransCount2 = 0;
  609. static MATERIAL transMat, transMatInc;
  610. static FLOAT zTrans, zTransInc;
  611. static MATERIAL *pNewMat;
  612. static struct _timeb baseTime;
  613. static BOOL bInit = FALSE;
  614. static int reset[NUM_AXIS] = {1,1,1};
  615. if( !bInit ) {
  616. // Do first time init stuff
  617. // Start the calibration timer
  618. _ftime( &baseTime );
  619. // set default transition points, until calibration done
  620. maxCount = 60;
  621. SetTransitionPoints( pac, maxCount, &matTransCount,
  622. &matTransCount2, &zTrans );
  623. bInit = TRUE;
  624. }
  625. // take action based on frameCount
  626. if( frameCount >= matTransCount ) {
  627. // we are in the transition zone
  628. if( frameCount == matTransCount ) {
  629. // first transition point...
  630. // select new material
  631. if( pac->bTexture )
  632. pNewMat = ss_RandomTexMaterial( FALSE );
  633. else
  634. pNewMat = ss_RandomTeaMaterial( FALSE );
  635. // set material transition, zTrans transition
  636. if( pac->demoType == DEMO_VSTRING ) {
  637. // transition current material to black
  638. ss_CreateMaterialGradient( &transMatInc,
  639. pac->pMat,
  640. &ss_BlackMat,
  641. matTransCount2 - matTransCount );
  642. zTransInc = ((FMAX_ZOOM-1) * pac->fZtrans) /
  643. (matTransCount2 - matTransCount);
  644. } else {
  645. ss_CreateMaterialGradient( &transMatInc,
  646. pac->pMat,
  647. pNewMat,
  648. maxCount - matTransCount );
  649. }
  650. // initialize transition values to current settings
  651. zTrans = pac->fZtrans;
  652. transMat = *(pac->pMat);
  653. // begin transition on NEXT frame.
  654. } else {
  655. // past first transition...
  656. if( matTransCount2 && (frameCount == (matTransCount2+1)) ) {
  657. // optional second transition point...(only for vstrings)
  658. // transition from black to new material
  659. ss_CreateMaterialGradient( &transMatInc,
  660. &ss_BlackMat,
  661. pNewMat,
  662. maxCount - matTransCount2 );
  663. // init transition material to black
  664. transMat = ss_BlackMat;
  665. /* At this point, screen is black, so we can change strings
  666. * and resize the floater without any problems.
  667. */
  668. if( pac->demoType == DEMO_VSTRING )
  669. text3d_UpdateString( pac, TRUE ); // can cause resize
  670. // set zTrans to furthest distance
  671. zTrans = (FMAX_ZOOM * pac->fZtrans);
  672. zTransInc = (pac->fZtrans - zTrans) /
  673. (maxCount - matTransCount2);
  674. // change this while string invisible
  675. ResetRotationLimits( pac, reset );
  676. }
  677. // set the transition material (updates transMat each time)
  678. ss_TransitionMaterial( &transMat, &transMatInc );
  679. zTrans += zTransInc;
  680. if( frameCount >= maxCount ) {
  681. // End of cycle
  682. nCycle++; // 1-based
  683. // Calibrate on MAX_CALIBRATE cycles
  684. if( !bCalibrated && (nCycle >= MAX_CALIBRATE) ) {
  685. maxCount = FrameCalibration( pac, &baseTime, maxCount, nCycle );
  686. SetTransitionPoints( pac, maxCount, &matTransCount,
  687. &matTransCount2, &zTrans );
  688. bCalibrated = TRUE;
  689. }
  690. // set, reset stuff
  691. pac->pMat = pNewMat;
  692. ss_SetMaterial( pNewMat );
  693. zTrans = pac->fZtrans;
  694. frameCount = 0;
  695. }
  696. }
  697. }
  698. if( pac->demoType == DEMO_CLOCK ) {
  699. // have to update the draw string with current time
  700. text3d_UpdateTime( pac, TRUE );
  701. }
  702. // ok, the string's setup - draw it
  703. glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
  704. glMatrixMode(GL_MODELVIEW);
  705. glLoadIdentity();
  706. if( pac->demoType == DEMO_VSTRING )
  707. // use zooming zTrans
  708. glTranslatef( 0.0f, 0.0f, zTrans );
  709. else
  710. // use fixed zTrans
  711. glTranslatef( 0.0f, 0.0f, pac->fZtrans );
  712. /*
  713. * GetNextRotProc provides sinusoidal rotations
  714. */
  715. (*GetNextRotProc)( pac ); // sets pac->p3dRot, or rot
  716. if( pac->p3dRotMax.z != 0.0f ) {
  717. glRotatef( rot->z, 0.0f, 0.0f, 1.0f );
  718. }
  719. if( pac->p3dRotMax.y != 0.0f ) {
  720. glRotatef( rot->y, 0.0f, 1.0f, 0.0f );
  721. }
  722. if( pac->p3dRotMax.x != 0.0f ) {
  723. glRotatef( rot->x, 1.0f, 0.0f, 0.0f );
  724. }
  725. glTranslatef( -pac->pfTextOrigin.x - pac->pfTextExtent.x/2.0f,
  726. -pac->pfTextOrigin.y + pac->pfTextExtent.y/2.0f,
  727. pac->fDepth / 2.0f );
  728. DrawString( pac->usText, pac->textLen, pac->pWglFontC );
  729. glFlush();
  730. frameCount++;
  731. }
  732. /**************************************************************************\
  733. * SetTransitionPoints
  734. *
  735. * Calculate draw transition points, as frame count values.
  736. *
  737. * If doing variable string (VSTRING), first transition point is where we
  738. * start fading to black, and 2nd is from black to next material. Also
  739. * transition the z translation distance (zTrans).
  740. * Note that trans2 indicates the frame number where the image should be
  741. * black. The actual transitioning may occur at trans2+1 (see text3d_draw
  742. * above).
  743. *
  744. * For all other cases, set one transition point for fade to next material.
  745. *
  746. \**************************************************************************/
  747. static void
  748. SetTransitionPoints( AttrContext *pac, int framesPerCycle, int *trans1,
  749. int *trans2, FLOAT *zTrans )
  750. {
  751. *trans1 = (int) (0.5f * (FLOAT) framesPerCycle + 0.5f);
  752. if( pac->demoType == DEMO_VSTRING ) {
  753. *trans2 = *trans1 +
  754. (int) (0.5f * (FLOAT) (framesPerCycle - *trans1) + 0.5f);
  755. *zTrans = pac->fZtrans;
  756. }
  757. }
  758. /**************************************************************************\
  759. * text3d_UpdateTime
  760. *
  761. * Put new time string into the attribute context
  762. *
  763. \**************************************************************************/
  764. static void
  765. text3d_UpdateTime( AttrContext *pac, BOOL bCheckBounds )
  766. {
  767. int oldLen;
  768. POINTFLOAT textExtent, textOrigin;
  769. POINTFLOAT textLowerRight, currentLowerRight;
  770. LPTSTR pszLastTime = pac->szText;
  771. static TCHAR szNewTime[TEXT_BUF_SIZE] = {0};
  772. GetLocalTime( &(pac->stTime) );
  773. GetTimeFormat( GetUserDefaultLCID(), // locale id
  774. 0, // flags
  775. &(pac->stTime), // time struct
  776. NULL, // format string
  777. szNewTime, // buffer
  778. TEXT_BUF_SIZE ); // buffer size
  779. // Compare new time string with last one
  780. if( !lstrcmp( pszLastTime, szNewTime ) )
  781. // time string has not changed, return
  782. return;
  783. // translate the new time string into display lists in pac->usText
  784. ConvertStringToList( szNewTime, pac->usText, pac->pWglFontC );
  785. lstrcpy( pac->szText, szNewTime );
  786. // Check extents of new string
  787. // save current values
  788. oldLen = pac->textLen;
  789. textExtent = pac->pfTextExtent;
  790. textOrigin = pac->pfTextOrigin;
  791. pac->textLen = GetStringExtent( pac->szText,
  792. &textExtent,
  793. &textOrigin,
  794. pac->pWglFontC );
  795. if( !bCheckBounds ) {
  796. // just set new extents and return
  797. pac->pfTextExtent = textExtent;
  798. pac->pfTextOrigin = textOrigin;
  799. return;
  800. }
  801. /* only update bounding box if new extents are larger, or the number
  802. * of chars changes
  803. */
  804. bCheckBounds = FALSE;
  805. if( pac->textLen != oldLen ) {
  806. // recalculate everything
  807. bCheckBounds = TRUE;
  808. pac->pfTextExtent = textExtent;
  809. pac->pfTextOrigin = textOrigin;
  810. }
  811. else {
  812. // accumulate maximum bounding box in pac
  813. // calc current lower right limits
  814. textLowerRight.x = textOrigin.x + textExtent.x;
  815. textLowerRight.y = textOrigin.y - textExtent.y;
  816. currentLowerRight.x = pac->pfTextOrigin.x + pac->pfTextExtent.x;
  817. currentLowerRight.y = pac->pfTextOrigin.y - pac->pfTextExtent.y;
  818. // if new text extents extend beyond current, update
  819. if( textOrigin.x < pac->pfTextOrigin.x ) {
  820. pac->pfTextOrigin.x = textOrigin.x;
  821. bCheckBounds = TRUE;
  822. }
  823. if( textOrigin.y > pac->pfTextOrigin.y ) {
  824. pac->pfTextOrigin.y = textOrigin.y;
  825. bCheckBounds = TRUE;
  826. }
  827. if( textLowerRight.x > currentLowerRight.x ) {
  828. pac->pfTextExtent.x = textLowerRight.x - pac->pfTextOrigin.x;
  829. bCheckBounds = TRUE;
  830. }
  831. if( textLowerRight.y < currentLowerRight.y ) {
  832. pac->pfTextExtent.y = pac->pfTextOrigin.y - textLowerRight.y;
  833. bCheckBounds = TRUE;
  834. }
  835. }
  836. if( bCheckBounds ) {
  837. // string size has changed - recalc box and view params
  838. (*BoundingBoxProc)( pac );
  839. CalcViewParams( pac );
  840. }
  841. }
  842. /**************************************************************************\
  843. * text3d_UpdateString
  844. *
  845. * Select new string to display.
  846. * If bCheckBounds, calculate new bounds as well.
  847. *
  848. \**************************************************************************/
  849. static void
  850. text3d_UpdateString( AttrContext *pac, BOOL bCheckBounds )
  851. {
  852. static int index = 0;
  853. // get next string to display
  854. if( gplist == NULL )
  855. CreateRandomList();
  856. lstrcpy( pac->szText, gplist->pszStr );
  857. ConvertStringToList( pac->szText, pac->usText, pac->pWglFontC );
  858. gplist = gplist->pnext;
  859. // get new extents
  860. pac->textLen = GetStringExtent( pac->szText,
  861. &pac->pfTextExtent,
  862. &pac->pfTextOrigin,
  863. pac->pWglFontC );
  864. if( !bCheckBounds )
  865. return;
  866. // calculate bounding box
  867. (*BoundingBoxProc)( pac );
  868. if( pac->p3dBoundingBox.y == 0.0f )
  869. // avoid /0
  870. pac->p3dBoundingBox.y = 1.0f;
  871. // Make window's aspect ratio dependent on bounding box
  872. /* mf: could clear buffer here, so don't get incorrect results on
  873. * synchronous resize, but not necessary since we're fading
  874. */
  875. ss_SetWindowAspectRatio( pac->p3dBoundingBox.x / pac->p3dBoundingBox.y );
  876. CalcViewParams( pac );
  877. // move window to new random position
  878. ss_RandomWindowPos();
  879. }
  880. /**************************************************************************\
  881. * GetNextRotWobble
  882. *
  883. * Calculate next rotation.
  884. * - 'step' controls amount of rotation
  885. * - rotation values are scaled from -1 to 1 (trig values), and inscribe
  886. * circle with r=1 in the zy plane for the ends of the string
  887. * - steps for both minor and major rotation axis remain in sync
  888. *
  889. * History
  890. * Apr. 28, 95 : [marcfo]
  891. * - Call ResetRotationLimits() when axis rotation is 0
  892. *
  893. \**************************************************************************/
  894. static void
  895. GetNextRotWobble( AttrContext *pac )
  896. {
  897. int *step = (int *) &pac->ip3dRoti; // use step->x
  898. int *rotStep = (int *) &pac->ip3dRotStep.z;
  899. FLOAT *rotMax = (FLOAT *) &pac->p3dRotMax;
  900. POINTFLOAT *pTrig = pac->pTrig;
  901. static int resetPoint[NUM_AXIS] = {90,90,0}; // 0 amplitude points
  902. int reset[NUM_AXIS] = {0}; // which axis to be reset
  903. int axis;
  904. pac->p3dRot.z = pac->p3dRotLimit.z * pTrig[ *step ].y; // sin
  905. pac->p3dRot.y = pac->p3dRotLimit.y * pTrig[ *step ].x; // cos
  906. pac->p3dRot.x = pac->p3dRotLimit.x * pTrig[ *step ].x; // cos
  907. // check for 0 amplitude point for non-vstrings
  908. for( axis = X_AXIS; axis < NUM_AXIS; axis++ ) {
  909. if( rotMax[axis] != 0.0f ) {
  910. if( (pac->demoType != DEMO_VSTRING) &&
  911. (*step == resetPoint[axis]) )
  912. {
  913. reset[axis] = 1;
  914. ResetRotationLimits( pac, reset );
  915. reset[axis] = 0;
  916. }
  917. }
  918. }
  919. // increment step
  920. if( (*step += *rotStep) >= 360 ) {
  921. // make the step variable
  922. *rotStep = ss_iRand2( pac->iRotMinStep, pac->iRotMaxStep );
  923. // start step at variable index
  924. *step = ss_iRand( *rotStep );
  925. }
  926. }
  927. /**************************************************************************\
  928. * GetNextRotRandom
  929. *
  930. * Same as above, but steps for each axis are not kept in sync
  931. *
  932. * History :
  933. * Apr. 28, 95 : [marcfo]
  934. * - Call ResetRotationLimits() when axis rotation is 0
  935. *
  936. \**************************************************************************/
  937. static void
  938. GetNextRotRandom( AttrContext *pac )
  939. {
  940. int *step = (int *) &pac->ip3dRoti;
  941. int *rotStep = (int *) &pac->ip3dRotStep;
  942. FLOAT *rotMax = (FLOAT *) &pac->p3dRotMax;
  943. POINTFLOAT *pTrig = pac->pTrig;
  944. static int resetPoint[NUM_AXIS] = {90,90,0}; // 0 amplitude points
  945. int reset[NUM_AXIS] = {0}; // which axis to be reset
  946. int axis;
  947. // set new rotation
  948. pac->p3dRot.z = pac->p3dRotLimit.z * pTrig[ step[Z_AXIS] ].y; // sin
  949. pac->p3dRot.y = pac->p3dRotLimit.y * pTrig[ step[Y_AXIS] ].x; // cos
  950. pac->p3dRot.x = pac->p3dRotLimit.x * pTrig[ step[X_AXIS] ].x; // cos
  951. // for each rotation axis...
  952. for( axis = X_AXIS; axis < NUM_AXIS; axis++ ) {
  953. if( rotMax[axis] != 0.0f ) {
  954. // check for 0 amplitude point for non-vstrings
  955. if( (pac->demoType != DEMO_VSTRING) &&
  956. (step[axis] == resetPoint[axis]) ) {
  957. reset[axis] = 1;
  958. ResetRotationLimits( pac, reset );
  959. reset[axis] = 0;
  960. }
  961. // increment rotation step and check for end of cycle
  962. if( (step[axis] += rotStep[axis]) >= 360 ) {
  963. // make the step variable
  964. rotStep[axis] = ss_iRand2( pac->iRotMinStep, pac->iRotMaxStep );
  965. // start step at variable index
  966. step[axis] = ss_iRand( rotStep[axis] );
  967. }
  968. }
  969. }
  970. }
  971. /**************************************************************************\
  972. * GetNextRotNone
  973. *
  974. * Null rot proc
  975. *
  976. \**************************************************************************/
  977. static void
  978. GetNextRotNone( AttrContext *pac )
  979. {
  980. }
  981. /**************************************************************************\
  982. * ResetRotationLimits
  983. *
  984. * Reset the maximum axis rotations. So there won't be too much of a 'jump'
  985. * when altering the rotation, this routine should only be called when the
  986. * rotation of the specified axis is at zero amplitude.
  987. *
  988. * Also, change the rotation table if applicable. This affects how the
  989. * rotation 'steps' around the axis.
  990. *
  991. * History :
  992. * Apr. 28, 95 : [marcfo]
  993. * - Make rotation limits randomnly more extreme
  994. * - If trig table switched, call AdjustRotationStep(), to reset the steps
  995. * of non-zero axis rotations so that text string doesn't 'jump'
  996. *
  997. \**************************************************************************/
  998. static void
  999. ResetRotationLimits( AttrContext *pac, int *reset )
  1000. {
  1001. FLOAT *p3dRot = (FLOAT *) &pac->p3dRot; // current rotation
  1002. FLOAT *p3dRotL = (FLOAT *) &pac->p3dRotLimit; // new rot limit
  1003. FLOAT *p3dRotMin = (FLOAT *) &pac->p3dRotMin; // max rotation
  1004. FLOAT *p3dRotMax = (FLOAT *) &pac->p3dRotMax; // max rotation
  1005. POINT3D p3dOldRotL = pac->p3dRotLimit; // save last rot limit
  1006. POINTFLOAT *oldTrig;
  1007. int i;
  1008. // change rotation limits
  1009. for( i = 0; i < NUM_AXIS; i++ ) {
  1010. if( p3dRotMax[i] && reset[i] ) {
  1011. p3dRotL[i] = ss_fRand( p3dRotMin[i], p3dRotMax[i] );
  1012. // grossly modify amplitute sometimes for random
  1013. if( pac->rotStyle == ROTSTYLE_RANDOM ) {
  1014. if( ss_iRand(10) == 2 )
  1015. p3dRotL[i] = ss_fRand( 0.0f, 10.0f );
  1016. else if( ss_iRand(10) == 2 )
  1017. p3dRotL[i] = ss_fRand( 90.0f, 135.0f );
  1018. }
  1019. }
  1020. }
  1021. // change rotation table
  1022. // use i to set a frequency for choosing gInvTrig table
  1023. i = 10;
  1024. oldTrig = pac->pTrig;
  1025. switch( pac->rotStyle ) {
  1026. case ROTSTYLE_RANDOM:
  1027. if( pac->demoType == DEMO_VSTRING )
  1028. i = 7;
  1029. // fall thru...
  1030. case ROTSTYLE_SEESAW:
  1031. // Use InvTrig table every now and then
  1032. if( ss_iRand(i) == 2 ) {
  1033. pac->pTrig = gInvTrig;
  1034. }
  1035. else
  1036. pac->pTrig = gTrig;
  1037. break;
  1038. default:
  1039. // Always use regular trig table
  1040. pac->pTrig = gTrig;
  1041. }
  1042. // if trig table changed, need to adjust steps of non-zero axis rotations
  1043. // (otherwise get 'twitch' in rotation)
  1044. if( pac->pTrig != oldTrig ) {
  1045. // only deal with axis which didn't have amplitudes modified
  1046. for( i = 0; i < NUM_AXIS; i++ )
  1047. reset[i] = ! reset[i];
  1048. AdjustRotationStep( pac, reset, oldTrig );
  1049. }
  1050. }
  1051. /**************************************************************************\
  1052. * AdjustRotationStep
  1053. *
  1054. * If trig table is changed in ResetRotationLimits, then axis with non-zero
  1055. * rotations will appear to jump. This routine modifies the current step
  1056. * so this will not be apparent.
  1057. *
  1058. * History :
  1059. * Apr. 28, 95 : [marcfo]
  1060. * - Wrote it
  1061. *
  1062. \**************************************************************************/
  1063. static void
  1064. AdjustRotationStep( AttrContext *pac, int *reset, POINTFLOAT *oldTrig )
  1065. {
  1066. int *step = (int *) &pac->ip3dRoti;
  1067. FLOAT *p3dRotMax = (FLOAT *) &pac->p3dRotMax;
  1068. int axis;
  1069. POINT *trigDif;
  1070. if( pac->demoType == DEMO_VSTRING )
  1071. // for now doesn't matter, string is invisible at this point
  1072. return;
  1073. // choose diff table to use for modifying step
  1074. trigDif = (oldTrig == gTrig) ? gTrigDif : gInvTrigDif;
  1075. for( axis = 0; axis < NUM_AXIS; axis++ ) {
  1076. if( p3dRotMax[axis] && reset[axis] ) {
  1077. if( axis != Z_AXIS )
  1078. step[axis] += trigDif[ step[axis] ].x;
  1079. else
  1080. step[axis] += trigDif[ step[axis] ].y;
  1081. // check for wrap or out of bounds
  1082. if( (step[axis] >= 360) || (step[axis] < 0) )
  1083. step[axis] = 0;
  1084. }
  1085. }
  1086. }
  1087. /**************************************************************************\
  1088. * FindInvStep
  1089. *
  1090. * Finds step in invTrig table with same value as trig table at i
  1091. *
  1092. * History :
  1093. * Apr. 28, 95 : [marcfo]
  1094. * - Wrote it
  1095. *
  1096. \**************************************************************************/
  1097. static int
  1098. FindInvStep( int i )
  1099. {
  1100. FLOAT val, diff, minDiff;
  1101. int invStep = i;
  1102. val = gTrig[i].y;
  1103. invStep = i;
  1104. minDiff = val - gInvTrig[i].y;
  1105. while( ++i <= 90 ) {
  1106. diff = val - gInvTrig[i].y;
  1107. if( (FLOAT) fabs(diff) < minDiff ) {
  1108. minDiff = (FLOAT) fabs(diff);
  1109. invStep = i;
  1110. }
  1111. if( diff < 0.0f )
  1112. break;
  1113. }
  1114. return invStep;
  1115. }
  1116. /**************************************************************************\
  1117. * FindStep
  1118. *
  1119. * Finds step in trig table with same value as invTrig table at i
  1120. *
  1121. * History :
  1122. * Apr. 28, 95 : [marcfo]
  1123. * - Wrote it
  1124. *
  1125. \**************************************************************************/
  1126. static int
  1127. FindStep( int i )
  1128. {
  1129. FLOAT val, diff, minDiff;
  1130. int step = i;
  1131. val = gInvTrig[i].y;
  1132. step = i;
  1133. minDiff = gTrig[i].y - val;
  1134. while( --i >= 0 ) {
  1135. diff = val - gTrig[i].y;
  1136. if( (FLOAT) fabs(diff) < minDiff ) {
  1137. minDiff = (FLOAT) fabs(diff);
  1138. step = i;
  1139. }
  1140. if( diff > 0.0f )
  1141. break;
  1142. }
  1143. return step;
  1144. }
  1145. /**************************************************************************\
  1146. * InitTrigTable
  1147. *
  1148. * Initialize trig look-up tables
  1149. *
  1150. * History :
  1151. * Apr. 28, 95 : [marcfo]
  1152. * - Calculate 'diff' tables for smooth transitions between trig and
  1153. * invTrig tables
  1154. *
  1155. \**************************************************************************/
  1156. static void
  1157. InitTrigTable()
  1158. {
  1159. int i;
  1160. static int num = 360;
  1161. FLOAT inc = (2.0f*PI)/((FLOAT)num); // 360 degree range
  1162. FLOAT angle = 0.0f;
  1163. int newStep;
  1164. // calc standard trig table
  1165. for( i = 0; i < num; i ++ ) {
  1166. gTrig[i].x = (FLOAT) cos(angle);
  1167. gTrig[i].y = (FLOAT) sin(angle);
  1168. angle += inc;
  1169. }
  1170. // Calc sawtooth and pseudo-inverse trig table, as well as a diff
  1171. // table to convert between trig and invTrig.
  1172. // do y, or sin values first
  1173. for( i = 0; i <= 90; i ++ ) {
  1174. gSawTooth[i].y = (int) i / 90.0f;
  1175. gInvTrig[i].y = 2*gSawTooth[i].y - gTrig[i].y;
  1176. }
  1177. // Create tables to convert trig steps to invTrig steps, and vice-versa
  1178. for( i = 0; i <= 90; i ++ ) {
  1179. newStep = FindInvStep( i );
  1180. gTrigDif[i].y = newStep - i;
  1181. newStep = FindStep( i );
  1182. gInvTrigDif[i].y = newStep - i; // -
  1183. }
  1184. // reflect 0-90 to get 90-180
  1185. for( i = 1; i <= 90; i ++ ) {
  1186. gSawTooth[90+i].y = gSawTooth[90-i].y;
  1187. gInvTrig[90+i].y = gInvTrig[90-i].y;
  1188. gTrigDif[90+i].y = -gTrigDif[90-i].y;
  1189. gInvTrigDif[90+i].y = -gInvTrigDif[90-i].y;
  1190. }
  1191. // invert 0-180 to get 180-360
  1192. for( i = 1; i < 180; i ++ ) {
  1193. gSawTooth[180+i].y = -gSawTooth[i].y;
  1194. gInvTrig[180+i].y = -gInvTrig[i].y;
  1195. gTrigDif[180+i].y = gTrigDif[i].y;
  1196. gInvTrigDif[180+i].y = gInvTrigDif[i].y;
  1197. }
  1198. // calc x, or cos, by phase-shifting y
  1199. for( i = 0; i < 270; i ++ ) {
  1200. gSawTooth[i].x = gSawTooth[i+90].y;
  1201. gInvTrig[i].x = gInvTrig[i+90].y;
  1202. gTrigDif[i].x = gTrigDif[i+90].y;
  1203. gInvTrigDif[i].x = gInvTrigDif[i+90].y;
  1204. }
  1205. for( i = 0; i < 90; i ++ ) {
  1206. gSawTooth[i+270].x = gSawTooth[i].y;
  1207. gInvTrig[i+270].x = gInvTrig[i].y;
  1208. gTrigDif[i+270].x = gTrigDif[i].y;
  1209. gInvTrigDif[i+270].x = gInvTrigDif[i].y;
  1210. }
  1211. }
  1212. /**************************************************************************\
  1213. * CalcChordalDeviation
  1214. *
  1215. *
  1216. \**************************************************************************/
  1217. static FLOAT
  1218. CalcChordalDeviation( HDC hdc, AttrContext *pac )
  1219. {
  1220. OUTLINETEXTMETRIC otm;
  1221. FLOAT cd, mincd; // chordal deviations
  1222. // Query font metrics
  1223. if( GetOutlineTextMetrics( hdc, sizeof(otm), &otm) <= 0 )
  1224. // cmd failed, or buffer size=0
  1225. return 1.0f;
  1226. // minimum chordal deviation is limited by design space
  1227. mincd = 1.0f / (FLOAT) otm.otmEMSquare;
  1228. // now map fTesselFact to chordalDeviation
  1229. cd = MapValue( pac->fTesselFact,
  1230. 0.0f, 1.0f, // fTesselFact range
  1231. FMAX_CHORDAL_DEVIATION, mincd ); // chordalDeviation range
  1232. if( pac->fTesselFact == 0.0f )
  1233. // make sure get lowest resolution
  1234. cd = 1.0f;
  1235. return cd;
  1236. }
  1237. /**************************************************************************\
  1238. * CalcBoundingBox
  1239. *
  1240. \**************************************************************************/
  1241. static void
  1242. CalcBoundingBox( AttrContext *pac )
  1243. {
  1244. POINT3D box[3]; // for each axis rotation
  1245. FLOAT viewAngle, critAngle, critAngleC, rectAngle;
  1246. FLOAT r, rot, x, y, z, xmax, ymax, zCrit;
  1247. FLOAT viewDist, viewDistO, xAngle[3], angle;
  1248. FLOAT boxvpo[3]; // viewpoint to origin distance along z for the boxes
  1249. int n = 0;
  1250. POINTFLOAT extent;
  1251. POINT3D pt;
  1252. /* One thing to remember here is that box[n].z is constrained to be
  1253. * the near clipping plane. The boxe's x and y represent the frustum
  1254. * cross-section at that point.
  1255. */
  1256. viewAngle = deg_to_rad( pac->fFovy ) / 2.0f;
  1257. // x,y,z represent half-extents
  1258. x = pac->pfTextExtent.x/2.0f;
  1259. y = pac->pfTextExtent.y/2.0f;
  1260. z = pac->fDepth/2.0f;
  1261. // initialize box[0] with current extents
  1262. box[0].x = x;
  1263. box[0].y = y;
  1264. box[0].z = z;
  1265. boxvpo[0] = 0.0f;
  1266. // handle rotation around x-axis
  1267. if( pac->p3dRotMax.x != 0.0f ) {
  1268. box[n].x = x;
  1269. // need to determine y and z
  1270. rot = deg_to_rad( pac->p3dRotMax.x );
  1271. r = (FLOAT) sqrt( y*y + z*z );
  1272. // calc incursion along z
  1273. rectAngle = (z == 0.0f) ? PI_OVER_2 : (FLOAT) atan( y/z );
  1274. if( rot >= rectAngle ) {
  1275. // easy, use maximum possible extent
  1276. box[n].z = r;
  1277. } else {
  1278. // rotate lower right corner of box by rot to get extent
  1279. box[n].z = z * (FLOAT) cos( rot ) + y * (FLOAT) sin( rot );
  1280. }
  1281. /* figure out critical angle, where rotated rectangle would
  1282. * be perpendicular to viewing frustum. This indicates the max.
  1283. * y-incursion into the frustum.
  1284. */
  1285. critAngle = PI_OVER_2 - viewAngle;
  1286. ymax = r * (FLOAT) sin(critAngle);
  1287. if( y > z ) {
  1288. rectAngle = PI_OVER_2 - rectAngle;
  1289. critAngleC = PI_OVER_2 - critAngle;
  1290. } else
  1291. critAngleC = critAngle;
  1292. if( (rectAngle + rot) >= critAngleC ) {
  1293. // no view reduction possible in y, use max view
  1294. // need to calc y at box.z
  1295. viewDistO = r / (FLOAT) cos( critAngle );
  1296. boxvpo[n] = viewDistO;
  1297. box[n].y = (viewDistO - box[n].z) *
  1298. (FLOAT) tan( PI_OVER_2 - critAngle);
  1299. } else {
  1300. // we can sonic reduce it
  1301. if( y > z )
  1302. rot = -rot;
  1303. // rotate front-top point by rot to get ymax, z
  1304. ymax = z * (FLOAT) sin( rot ) + y * (FLOAT) cos( rot );
  1305. zCrit = z * (FLOAT) cos( rot ) - y * (FLOAT) sin( rot );
  1306. // not usin viewDistO properly here...
  1307. viewDistO = ymax * (FLOAT) tan( viewAngle);
  1308. boxvpo[n] = viewDistO + zCrit;
  1309. viewDist = boxvpo[n] - box[n].z;
  1310. box[n].y = viewDist * (FLOAT) tan( viewAngle );
  1311. }
  1312. n++;
  1313. }
  1314. if( pac->p3dRotMax.y != 0.0f ) {
  1315. box[n].y = y;
  1316. // need to determine x and z
  1317. rot = deg_to_rad( pac->p3dRotMax.y );
  1318. r = (FLOAT) sqrt( x*x + z*z );
  1319. rectAngle = (z == 0.0f) ? PI_OVER_2 : (FLOAT) atan( x/z );
  1320. // calc incursion along z
  1321. if( rot >= rectAngle ) {
  1322. // easy, use maximum possible extent
  1323. box[n].z = r;
  1324. } else {
  1325. // rotate lower right corner of box by rot to get extent
  1326. box[n].z = z * (FLOAT) cos( rot ) + x * (FLOAT) sin( rot );
  1327. }
  1328. // view distance to largest z
  1329. viewDist = y / (FLOAT) tan(viewAngle);
  1330. // make viewDist represent distance to origin
  1331. viewDistO = viewDist + box[n].z;
  1332. boxvpo[n] = viewDistO;
  1333. // now minimize angle between viewpoint and rotated rect
  1334. if( viewDistO > r ) {
  1335. /* calc crit angle where view is maximized (line from viewpoint
  1336. * tangent to rotation circle)
  1337. * critAngle is between z-axis and radial line
  1338. */
  1339. critAngle = (FLOAT) acos( r / viewDistO );
  1340. // critAngleC is for Comparing
  1341. if( x > z ) {
  1342. rectAngle = PI_OVER_2 - rectAngle;
  1343. critAngleC = PI_OVER_2 - critAngle;
  1344. } else
  1345. critAngleC = critAngle;
  1346. }
  1347. if( (viewDistO > r) && // vp OUTSIDE circle
  1348. ((rectAngle + rot) >= critAngleC) ) {
  1349. /* no view reduction possible in x, use x along the max-view line
  1350. */
  1351. box[n].x = viewDist * (FLOAT) tan( PI_OVER_2 - critAngle );
  1352. } else {
  1353. // we can sonic reduce it
  1354. if( x > z )
  1355. rot = -rot;
  1356. // rotate front-top point by rot to get x,z
  1357. // pt.z not needed
  1358. //pt.z = z * (FLOAT) cos( rot ) - x * (FLOAT) sin( rot );
  1359. pt.x = z * (FLOAT) sin( rot ) + x * (FLOAT) cos( rot );
  1360. box[n].x = pt.x;
  1361. }
  1362. n++;
  1363. }
  1364. if( pac->p3dRotMax.z != 0.0f ) {
  1365. CalcBoundingExtent( deg_to_rad(pac->p3dRotMax.z),
  1366. x, y,
  1367. (POINTFLOAT *) &box[n] );
  1368. box[n].z = z;
  1369. // calc viewing distance from front of box
  1370. viewDist = box[n].y / (FLOAT) tan(viewAngle);
  1371. // calc view distance to origin;
  1372. boxvpo[n] = box[n].z + viewDist;
  1373. n++;
  1374. }
  1375. /* XXX!: this is currently only being used for case of one axis rotation
  1376. * There were clipping problems using it for more axis'
  1377. */
  1378. /* Now we've got 3 rectangles in x-y plane at various depths, and
  1379. * need to pick the shortest viewing distance that will encompass
  1380. * all of them. Or, don't actually have to pick the view distance
  1381. * yet, since might want to wait for the viewport size in Reshape before
  1382. * we do this - but in that case need to pick the rectangle that 'sticks
  1383. * out the most', so it can be used as the bounding box.
  1384. */
  1385. /* The box with the furthest viewpoint will work for y.
  1386. * But then have to check
  1387. * this against the x's of the others. If any stick out of the frustum,
  1388. * then it will have to be made larger in x. By making x larger, we
  1389. * do not affect fovy
  1390. */
  1391. SortBoxes( box, boxvpo, n ); // put largest viewpoint box in box[0]
  1392. // figure view dist to first box
  1393. // (could maintain these as they are calculated)
  1394. viewDist = boxvpo[0] - box[0].z;
  1395. // compare x angles of boxes
  1396. switch( n ) {
  1397. FLOAT den;
  1398. case 3:
  1399. den = viewDist + (box[0].z - box[2].z);
  1400. xAngle[2] = den == 0.0f ? PI_OVER_2 : (FLOAT)atan( box[2].x / den);
  1401. case 2:
  1402. den = viewDist + (box[0].z - box[1].z);
  1403. xAngle[1] = den == 0.0f ? PI_OVER_2 : (FLOAT)atan( box[1].x / den);
  1404. case 1:
  1405. xAngle[0] = viewDist == 0.0f ? PI_OVER_2 :
  1406. (FLOAT)atan( box[0].x / viewDist );
  1407. }
  1408. // here, just call Sort again, with list of xAngles
  1409. SortBoxes( box, xAngle, n ); // put largest xangle box in box[0]
  1410. // now box[0] should contain half extents of the Bounding box
  1411. pac->p3dBoundingBox = box[0];
  1412. }
  1413. /**************************************************************************\
  1414. * CalcBoundingBoxFromSphere
  1415. *
  1416. * Calculates the bounding box from a sphere with r = diagonal of the box
  1417. *
  1418. \**************************************************************************/
  1419. static void
  1420. CalcBoundingBoxFromSphere( AttrContext *pac )
  1421. {
  1422. FLOAT x, y, z, r;
  1423. FLOAT viewAngle, viewDist, viewDistO;
  1424. POINT3D box;
  1425. // x,y,z represent half-extents
  1426. x = pac->pfTextExtent.x/2.0f;
  1427. y = pac->pfTextExtent.y/2.0f;
  1428. z = pac->fDepth/2.0f;
  1429. r = (FLOAT) sqrt( x*x + y*y +z*z );
  1430. box.z = r;
  1431. viewAngle = deg_to_rad( pac->fFovy ) / 2.0f;
  1432. viewDistO = r / (FLOAT) sin( viewAngle );
  1433. viewDist = viewDistO - r;
  1434. box.y = viewDist * (FLOAT) tan( viewAngle );
  1435. box.x = box.y;
  1436. pac->p3dBoundingBox = box;
  1437. }
  1438. /**************************************************************************\
  1439. *
  1440. * CalcBoundingBoxFromSpherePlus
  1441. *
  1442. * Same as above, but tries to optimize for case when z exent is small
  1443. *
  1444. \**************************************************************************/
  1445. static void
  1446. CalcBoundingBoxFromSpherePlus( AttrContext *pac, FLOAT zmax )
  1447. {
  1448. FLOAT x, y, z, r;
  1449. FLOAT viewAngle, viewDist, viewDistO;
  1450. POINT3D box;
  1451. // x,y,z represent half-extents
  1452. x = pac->pfTextExtent.x/2.0f;
  1453. y = pac->pfTextExtent.y/2.0f;
  1454. z = pac->fDepth/2.0f;
  1455. r = (FLOAT) sqrt( x*x + y*y +z*z );
  1456. viewAngle = deg_to_rad( pac->fFovy ) / 2.0f;
  1457. if( zmax < r ) {
  1458. // we can get closer !
  1459. box.z = zmax;
  1460. viewDistO = r / (FLOAT) sin( viewAngle );
  1461. viewDist = viewDistO - zmax;
  1462. // we want to move the clipping plane closer by (r-zmax)
  1463. if( (r-zmax) > viewDist ) {
  1464. #ifdef SS_DEBUG
  1465. glClearColor( 1.0f, 0.0f, 0.0f, 0.0f );
  1466. #endif
  1467. // we are moving the vp inside the sphere
  1468. box.y = (FLOAT) sqrt( r*r - box.z*box.z );
  1469. box.x = box.y;
  1470. } else {
  1471. FLOAT zt; // z-point where view frustum tangent to sphere
  1472. // vp outside sphere: can only optimize if zmax < ztangent
  1473. zt = r * (FLOAT) cos( PI_OVER_2 - viewAngle);
  1474. if( zmax < zt ) {
  1475. #ifdef SS_DEBUG
  1476. // GREEN ZONE !!!
  1477. glClearColor( 0.0f, 1.0f, 0.0f, 0.0f );
  1478. #endif
  1479. box.y = (FLOAT) sqrt( r*r - zmax*zmax );
  1480. } else
  1481. // this is the same as below, but with better clipping
  1482. box.y = (viewDist + (r-zmax)) * (FLOAT) tan( viewAngle );
  1483. box.x = box.y;
  1484. }
  1485. } else {
  1486. box.z = r;
  1487. viewDistO = r / (FLOAT) sin( viewAngle );
  1488. viewDist = viewDistO - r;
  1489. box.y = viewDist * (FLOAT) tan( viewAngle );
  1490. box.x = box.y;
  1491. }
  1492. pac->p3dBoundingBox = box;
  1493. }
  1494. /**************************************************************************\
  1495. * CalcBoundingBoxFromExtents
  1496. *
  1497. * Calculate bounding box for text, assuming text centered at origin, and
  1498. * using maximum possible spin angles.
  1499. *
  1500. * Rotation around any one axis will affect bounding areas in the other
  1501. * 2 directions (e.g. z-rotation affects x and y bounding values).
  1502. *
  1503. * We need to find the maxima of the rotated 2d area, while staying within
  1504. * the max spin angles.
  1505. *
  1506. \**************************************************************************/
  1507. static void
  1508. CalcBoundingBoxFromExtents( AttrContext *pac, POINT3D *box )
  1509. {
  1510. POINTFLOAT extent;
  1511. box->x = pac->pfTextExtent.x / 2.0f;
  1512. box->y = pac->pfTextExtent.y / 2.0f;
  1513. box->z = pac->fDepth / 2.0f;
  1514. // split the 3d problem into 3 2d problems in 'x-y' plane
  1515. if( pac->p3dRotMax.x != 0.0f ) {
  1516. CalcBoundingExtent( deg_to_rad(pac->p3dRotMax.x),
  1517. box->z, box->y, &extent );
  1518. box->z = max( box->z, extent.x );
  1519. box->y = max( box->y, extent.y );
  1520. }
  1521. if( pac->p3dRotMax.y != 0.0f ) {
  1522. CalcBoundingExtent( deg_to_rad(pac->p3dRotMax.y),
  1523. box->x, box->z, &extent );
  1524. box->x = max( box->x, extent.x );
  1525. box->z = max( box->z, extent.y );
  1526. }
  1527. if( pac->p3dRotMax.z != 0.0f ) {
  1528. CalcBoundingExtent( deg_to_rad(pac->p3dRotMax.z),
  1529. box->x, box->y, &extent );
  1530. box->x = max( box->x, extent.x );
  1531. box->y = max( box->y, extent.y );
  1532. }
  1533. }
  1534. /**************************************************************************\
  1535. * CalcBoundingBoxGeneric
  1536. *
  1537. * Combines the bounding sphere with the bounding extents
  1538. * Each of these alone will guarantee no clipping. But we can
  1539. * optimize by combining them.
  1540. *
  1541. \**************************************************************************/
  1542. static void
  1543. CalcBoundingBoxGeneric( AttrContext *pac )
  1544. {
  1545. POINT3D extentBox;
  1546. FLOAT x, y, z, r, d, zt, fovx;
  1547. FLOAT viewAngle, viewDist, viewDistO;
  1548. BOOL xIn, yIn;
  1549. // x,y,z represent half-extents
  1550. x = pac->pfTextExtent.x/2.0f;
  1551. y = pac->pfTextExtent.y/2.0f;
  1552. z = pac->fDepth/2.0f;
  1553. // get the max extent box
  1554. /*!!! wait, this alone doesn't guarantee no clipping? It only
  1555. * checks each axis-rotation separately, without combining them. This
  1556. * is no better than calling old CalcBoundingBox ... ?? Well, I
  1557. * can't prove why theoretically, but it works
  1558. */
  1559. CalcBoundingBoxFromExtents( pac, &extentBox );
  1560. // determine whether x and y extents inside/outside bounding sphere
  1561. r = (FLOAT) sqrt( x*x + y*y +z*z );
  1562. // check y
  1563. d = (FLOAT) sqrt( extentBox.y*extentBox.y + extentBox.z*extentBox.z );
  1564. yIn = d <= r ? TRUE : FALSE;
  1565. // check x
  1566. d = (FLOAT) sqrt( extentBox.x*extentBox.x + extentBox.z*extentBox.z );
  1567. xIn = d <= r ? TRUE : FALSE;
  1568. // handle easy cases
  1569. if( yIn && xIn ) {
  1570. pac->p3dBoundingBox = extentBox;
  1571. return;
  1572. }
  1573. if( !yIn && !xIn ) {
  1574. CalcBoundingBoxFromSpherePlus( pac, extentBox.z );
  1575. return;
  1576. }
  1577. // harder cases
  1578. viewAngle = deg_to_rad( pac->fFovy ) / 2.0f;
  1579. if( yIn ) {
  1580. // figure out x
  1581. viewDist = extentBox.y / (FLOAT) tan(viewAngle);
  1582. /* viewDist can be inside or outside of the sphere
  1583. * If inside - no optimization possible
  1584. * If outside, can draw line from viewpoint tangent to sphere,
  1585. * and use this point for x
  1586. */
  1587. viewDistO = extentBox.z + viewDist;
  1588. if( viewDistO <= r ) {
  1589. // vp inside sphere
  1590. // set x to the point where z intersects sphere
  1591. // this becomes a Pythagorous theorem problem:
  1592. extentBox.x = (FLOAT) sqrt( r*r - extentBox.z*extentBox.z );
  1593. } else {
  1594. // vp outside sphere
  1595. /* - figure out zt, where line tangent to circle for viewAngle
  1596. */
  1597. fovx = (FLOAT) asin( r / viewDistO );
  1598. zt = r * (FLOAT) acos( PI_OVER_2 - viewAngle);
  1599. if( extentBox.z < zt ) {
  1600. // use x where extentBox.z intersects sphere
  1601. extentBox.x = (FLOAT) sqrt( r*r - extentBox.z*extentBox.z );
  1602. } else {
  1603. // use x at tangent point
  1604. extentBox.x = (FLOAT) sqrt( r*r - zt*zt );
  1605. }
  1606. }
  1607. } else {// y out, x in
  1608. // XXX!
  1609. // have to figure out whether vp inside/outside of sphere.
  1610. /* !We can cheat a bit here. It IS possible, with view angles > 90,
  1611. * that the vp be inside sphere. But since we always use 90 for
  1612. * this app, it is safe to assume vp > r (Fix later for general case)
  1613. */
  1614. // XXX: wait, if y out, isn't vp always outside sphere ?
  1615. /* So we solve it this way:
  1616. * - figure out line tangent to circle for viewAngle
  1617. * - y will be where this line intersects the z=extentBox.z line
  1618. */
  1619. viewDistO = r / (FLOAT) sin( viewAngle );
  1620. extentBox.y = (viewDistO - extentBox.z) * (FLOAT) tan( viewAngle );
  1621. // I guess don't have to do anything with x ?
  1622. }
  1623. pac->p3dBoundingBox = extentBox;
  1624. }
  1625. /**************************************************************************\
  1626. *
  1627. * Calculate the extents in x and y from rotating a rectangle in a 2d plane
  1628. *
  1629. \**************************************************************************/
  1630. static void
  1631. CalcBoundingExtent( FLOAT rot, FLOAT x, FLOAT y, POINTFLOAT *extent )
  1632. {
  1633. FLOAT r, angleCrit;
  1634. r = (FLOAT) sqrt( x*x + y*y );
  1635. angleCrit = (x == 0.0f) ? PI_OVER_2 : (FLOAT) atan( y/x );
  1636. // calc incursion in x
  1637. if( rot >= angleCrit ) {
  1638. // easy, use maximum possible extent
  1639. extent->x = r;
  1640. } else {
  1641. // rotate lower right corner of box by rot to get extent
  1642. extent->x = x * (FLOAT) cos( rot ) + y * (FLOAT) sin( rot );
  1643. }
  1644. // calc incursion in y
  1645. angleCrit = PI/2.0f - angleCrit;
  1646. if( rot >= angleCrit ) {
  1647. // easy, use maximum possible extent
  1648. extent->y = r;
  1649. } else {
  1650. // rotate upper right corner of box by rot to get extent
  1651. extent->y = x * (FLOAT) sin( rot ) + y * (FLOAT) cos( rot );
  1652. }
  1653. }
  1654. /**************************************************************************\
  1655. *
  1656. * Sorts in descending order, based on values in val array (bubble sort)
  1657. *
  1658. \**************************************************************************/
  1659. static void
  1660. SortBoxes( POINT3D *box, FLOAT *val, int numBox )
  1661. {
  1662. int i, j, t;
  1663. POINT3D temp;
  1664. j = numBox;
  1665. while( j ) {
  1666. t = 0;
  1667. for( i = 0; i < j-1; i++ ) {
  1668. if( val[i] < val[i+1] ) {
  1669. // swap'em
  1670. temp = box[i];
  1671. box[i] = box[i+1];
  1672. box[i+1] = temp;
  1673. t = i;
  1674. }
  1675. }
  1676. j = t;
  1677. }
  1678. }
  1679. #define FILE_BUF_SIZE 180
  1680. /**************************************************************************\
  1681. * VerifyString
  1682. *
  1683. * Validate the string
  1684. *
  1685. * Has hard-coded ascii routines
  1686. *
  1687. \**************************************************************************/
  1688. static BOOL
  1689. VerifyString( AttrContext *pac )
  1690. {
  1691. HMODULE ghmodule;
  1692. HRSRC hr;
  1693. HGLOBAL hg;
  1694. PSZ psz, pszFile = NULL;
  1695. CHAR szSectName[30], szFileName[FILE_BUF_SIZE], szFname[30];
  1696. BOOL bMatch = FALSE;
  1697. // Check for string file in registry
  1698. if (LoadStringA(hMainInstance, IDS_SAVERNAME, szSectName, 30) &&
  1699. LoadStringA(hMainInstance, IDS_INIFILE, szFname, 30))
  1700. {
  1701. if( GetPrivateProfileStringA(szSectName, "magic", NULL,
  1702. szFileName, FILE_BUF_SIZE, szFname) )
  1703. pszFile = ReadStringFileA( szFileName );
  1704. }
  1705. // Check for key strings
  1706. if( pszFile )
  1707. bMatch = CheckKeyStrings( pac->szText, pszFile );
  1708. if( !bMatch ) {
  1709. if( (ghmodule = GetModuleHandle(NULL)) &&
  1710. (hr = FindResource(ghmodule, MAKEINTRESOURCE(1),
  1711. MAKEINTRESOURCE(99))) &&
  1712. (hg = LoadResource(ghmodule, hr)) &&
  1713. (psz = (PSZ)LockResource(hg)) )
  1714. bMatch = CheckKeyStrings( pac->szText, psz );
  1715. }
  1716. if( bMatch ) {
  1717. // put first string in pac->szText
  1718. pac->demoType = DEMO_VSTRING;
  1719. // for now, initialize strings here
  1720. text3d_UpdateString( pac, FALSE );
  1721. // adjust cycle time based on rotStyle
  1722. switch( pac->rotStyle ) {
  1723. case ROTSTYLE_NONE:
  1724. gfMinCycleTime = 4.0f;
  1725. break;
  1726. case ROTSTYLE_SEESAW:
  1727. gfMinCycleTime = 8.0f;
  1728. break;
  1729. case ROTSTYLE_RANDOM:
  1730. gfMinCycleTime = 10.0f;
  1731. break;
  1732. default:
  1733. gfMinCycleTime = 9.0f;
  1734. }
  1735. }
  1736. if( pszFile )
  1737. free( pszFile ); // allocated by ReadStringFile
  1738. return bMatch;
  1739. }
  1740. /**************************************************************************\
  1741. * CheckKeyStrings
  1742. *
  1743. * Test for match between string and any keystrings
  1744. * 'string' is user-inputted, and limited to TEXT_LIMIT chars.
  1745. *
  1746. \**************************************************************************/
  1747. static BOOL
  1748. CheckKeyStrings( LPTSTR string, PSZ psz )
  1749. {
  1750. int i;
  1751. TCHAR szKey[TEXT_LIMIT+1], testString[TEXT_LIMIT+1] = {0};
  1752. BOOL bMatch = FALSE;
  1753. int nMatch = 0;
  1754. int len;
  1755. // make copy of test string and convert to upper case
  1756. lstrcpy( testString, string );
  1757. #ifdef UNICODE
  1758. _wcsupr( testString );
  1759. #else
  1760. _strupr( testString );
  1761. #endif
  1762. while( psz[0] != '\n' ) { // iterate keyword/data sets
  1763. while( psz[0] != '\n' ) { // iterate keywords
  1764. len = strlen( psz ); // ! could be > TEXT_LIMIT if from file
  1765. // invert keyword bits and convert to uppercase
  1766. #ifdef UNICODE
  1767. // convert ascii keyword to unicode in szKey (inverts at same time)
  1768. ConvertStringAsciiToUnicode( psz, szKey,
  1769. len > TEXT_LIMIT ? TEXT_LIMIT : len );
  1770. _wcsupr( szKey );
  1771. #else
  1772. // just copy keyword to szKey, without going over TEXT_LIMIT
  1773. strncpy( szKey, psz, TEXT_LIMIT );
  1774. InvertBitsA( szKey, len > TEXT_LIMIT ? TEXT_LIMIT : len );
  1775. szKey[TEXT_LIMIT] = '\0'; // in case len > TEXT_LIMIT
  1776. _strupr( szKey );
  1777. #endif
  1778. if( !lstrcmp( szKey, testString ) ) {
  1779. // keyword match !
  1780. bMatch = TRUE;
  1781. nMatch++;
  1782. }
  1783. psz += len + 1; // skip over NULL as well
  1784. }
  1785. psz++; // skip over '\n' at end of keywords
  1786. if( bMatch )
  1787. ReadNameList( psz );
  1788. // skip over data to get to next keyword group
  1789. while( *psz != '\n' )
  1790. psz++;
  1791. psz++; // skip over '\n' at end of data
  1792. bMatch = FALSE; // keep searching for keyword matches
  1793. }
  1794. return nMatch;
  1795. }
  1796. /**************************************************************************\
  1797. * Various functions to process vstrings
  1798. *
  1799. \**************************************************************************/
  1800. static void
  1801. InvertBitsA( char *s, int len )
  1802. {
  1803. while( len-- ) {
  1804. *s++ = ~(*s);
  1805. }
  1806. }
  1807. static PSZ
  1808. ReadStringFileA( LPSTR szFile )
  1809. {
  1810. char lineBuf[180];
  1811. PSZ buf, pBuf;
  1812. int size, length, fdi;
  1813. char *ps;
  1814. char ctrl_n = '\n';
  1815. FILE *fIn;
  1816. BOOL bKey;
  1817. // create buffer to hold entire file
  1818. // mf: ! must be better way of getting file length!
  1819. fdi = _open(szFile, O_RDONLY | O_BINARY);
  1820. if( fdi < 0 )
  1821. return NULL;
  1822. size = _filelength( fdi );
  1823. _close(fdi);
  1824. buf= (char *) malloc( size );
  1825. if( !buf)
  1826. return NULL;
  1827. // open file for ascii text reading
  1828. fIn = fopen( szFile, "r" );
  1829. if( !fIn )
  1830. return NULL;
  1831. // Read in keyword/data sequences
  1832. bKey = TRUE; // so '\n' not appended to file when hit first keyword
  1833. pBuf = buf;
  1834. while( fgets( lineBuf, 180, fIn) ) {
  1835. ps = lineBuf;
  1836. if( *ps == '-' ) {
  1837. // keyword
  1838. if( !bKey ) {
  1839. // first key in group, append '\n' to data
  1840. *pBuf++ = ctrl_n;
  1841. }
  1842. bKey = TRUE;
  1843. ps++; // skip '-'
  1844. } else {
  1845. // data
  1846. if( bKey ) {
  1847. // first data in group, append '\n' to keywords
  1848. *pBuf++ = ctrl_n;
  1849. }
  1850. bKey = FALSE;
  1851. }
  1852. length = strlen( ps );
  1853. InvertBitsA( ps, length );
  1854. *(ps+length-1) = '\0'; // convert '\n' to null
  1855. lstrcpyA( pBuf, ps );
  1856. pBuf += length;
  1857. }
  1858. fclose( fIn );
  1859. // put 2 '\n' at end, for end condition
  1860. *pBuf++ = ctrl_n;
  1861. *pBuf++ = ctrl_n;
  1862. return( buf );
  1863. }
  1864. static void
  1865. CreateRandomList()
  1866. {
  1867. PLIST plist = gplistComplete;
  1868. PLIST *pplist;
  1869. int i = 0;
  1870. int n;
  1871. while (plist != NULL) {
  1872. n = ss_iRand( i+1 );
  1873. pplist = &gplist;
  1874. while (n > 0) {
  1875. pplist = &((*pplist)->pnext);
  1876. n--;
  1877. }
  1878. plist->pnext = *pplist;
  1879. *pplist = plist;
  1880. plist = plist->plistComplete;
  1881. i++;
  1882. }
  1883. }
  1884. static void
  1885. AddName(
  1886. LPTSTR pszStr)
  1887. {
  1888. PLIST plist = (PLIST)LocalAlloc(LPTR, sizeof(LIST));
  1889. if( !plist )
  1890. return;
  1891. plist->pszStr = pszStr;
  1892. plist->pnext = NULL;
  1893. plist->plistComplete = gplistComplete;
  1894. gplistComplete = plist;
  1895. }
  1896. static void
  1897. ReadNameList( PSZ psz )
  1898. {
  1899. int length;
  1900. int i;
  1901. LPTSTR pszNew;
  1902. while (psz[0] != '\n') {
  1903. length = 0;
  1904. while (psz[length] != 0) {
  1905. length++;
  1906. }
  1907. length;
  1908. pszNew = (LPTSTR)LocalAlloc( LPTR, (length + 1)*sizeof(TCHAR) );
  1909. if( !pszNew )
  1910. return;
  1911. #ifdef UNICODE
  1912. ConvertStringAsciiToUnicode( psz, (PWSTR) pszNew, length );
  1913. #else
  1914. strncpy( pszNew, psz, length );
  1915. InvertBitsA( pszNew, length );
  1916. #endif
  1917. AddName(pszNew);
  1918. psz += length + 1;
  1919. }
  1920. }
  1921. static void
  1922. DeleteNameList()
  1923. {
  1924. PLIST plist = gplistComplete, plistLast;
  1925. while( plist != NULL ) {
  1926. LocalFree( plist->pszStr );
  1927. plistLast = plist;
  1928. plist = plist->plistComplete;
  1929. LocalFree( plistLast );
  1930. }
  1931. }
  1932. /**************************************************************************\
  1933. * ConvertStringAsciiToUnicorn
  1934. *
  1935. \**************************************************************************/
  1936. static void
  1937. ConvertStringAsciiToUnicode( PSZ psz, PWSTR pwstr, int len )
  1938. {
  1939. while( len-- )
  1940. *pwstr++ = ~(*psz++) & 0xFF;
  1941. *pwstr = 0; // null terminate
  1942. }
  1943. /**************************************************************************\
  1944. * FrameCalibration
  1945. *
  1946. * Adjusts the number of frames in a cycle to conform to desired cycle time
  1947. *
  1948. \**************************************************************************/
  1949. static int
  1950. FrameCalibration( AttrContext *pac, struct _timeb *pBaseTime, int framesPerCycle, int nCycle )
  1951. {
  1952. struct _timeb thisTime;
  1953. FLOAT cycleTime;
  1954. _ftime( &thisTime );
  1955. cycleTime = thisTime.time - pBaseTime->time +
  1956. (thisTime.millitm - pBaseTime->millitm)/1000.0f;
  1957. cycleTime /= (FLOAT) nCycle;
  1958. if( cycleTime < gfMinCycleTime ) {
  1959. // need to add more frames to cycle
  1960. if( cycleTime == 0.0f ) // very unlikely
  1961. framesPerCycle = 800;
  1962. else
  1963. framesPerCycle = (int)( (FLOAT)framesPerCycle *
  1964. (gfMinCycleTime/cycleTime) );
  1965. } else {
  1966. // for vstrings, subtract frames from cycle
  1967. if( pac->demoType == DEMO_VSTRING ) {
  1968. framesPerCycle = (int)( (FLOAT)framesPerCycle *
  1969. (gfMinCycleTime/cycleTime) );
  1970. }
  1971. }
  1972. #define MIN_FRAMES 16
  1973. // make sure it's not too small
  1974. if( framesPerCycle < MIN_FRAMES )
  1975. framesPerCycle = MIN_FRAMES;
  1976. return framesPerCycle;
  1977. }
  1978. /**************************************************************************\
  1979. * MapValue
  1980. *
  1981. * Maps the value along an input range, to a proportional one along an
  1982. * output range. Each range must be monotonically increasing or decreasing.
  1983. *
  1984. * NO boundary conditions checked - responsibility of caller.
  1985. *
  1986. \**************************************************************************/
  1987. FLOAT
  1988. MapValue( FLOAT fInVal,
  1989. FLOAT fIn1, FLOAT fIn2, // input range
  1990. FLOAT fOut1, FLOAT fOut2 ) // output range
  1991. {
  1992. FLOAT fDist, fOutVal;
  1993. // how far along the input range is fInVal?, in %
  1994. fDist = (fInVal - fIn1) / (fIn2 - fIn1);
  1995. // use this distance to interpolate into output range
  1996. fOutVal = fDist * (fOut2 - fOut1) + fOut1;
  1997. return fOutVal;
  1998. }
  1999. /**************************************************************************\
  2000. * MapValueI
  2001. *
  2002. * Similar to above, but maps integer values
  2003. *
  2004. * Currently, only works for increasing ranges
  2005. *
  2006. * History
  2007. * Apr. 28, 95 : [marcfo]
  2008. * - Added early return for boundary conditions
  2009. *
  2010. \**************************************************************************/
  2011. int
  2012. MapValueI( int inVal,
  2013. int in1, int in2, // input range
  2014. int out1, int out2 ) // output range
  2015. {
  2016. int inDiv;
  2017. int outVal;
  2018. FLOAT fScale, fComp;
  2019. if( inVal >= in2 )
  2020. return out2;
  2021. if( inVal <= in1 )
  2022. return out1;
  2023. inDiv = abs(in2 - in1) + 1;
  2024. fScale = (FLOAT) (inDiv-1) / (FLOAT) inDiv;
  2025. fComp = 1.0f + (1.0f / inDiv);
  2026. outVal = (int) MapValue( (FLOAT) inVal * fComp,
  2027. (FLOAT) in1, (FLOAT) in2 + 0.999f,
  2028. (FLOAT) out1, (FLOAT) out2 + 0.999f );
  2029. return outVal;
  2030. }